diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index 6f2a862c..1aab70a6 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -1293,11 +1293,13 @@ function_decl_is_less_than(const function_decl&f, const function_decl &s);
 
 bool
 types_have_similar_structure(const type_base_sptr& first,
-			     const type_base_sptr& second);
+			     const type_base_sptr& second,
+			     bool indirect_type = false);
 
 bool
 types_have_similar_structure(const type_base* first,
-			     const type_base* second);
+			     const type_base* second,
+			     bool indirect_type = false);
 
 } // end namespace ir
 
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index 2853fe6c..5eacafc0 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -13573,10 +13573,10 @@ reference_type_def::reference_type_def(const type_base_sptr	pointed_to,
       decl_base_sptr pto = dynamic_pointer_cast<decl_base>(pointed_to);
       string name;
       if (pto)
-        {
-          set_visibility(pto->get_visibility());
-          name = string(pto->get_name()) + "&";
-        }
+	{
+	  set_visibility(pto->get_visibility());
+	  name = string(pto->get_name()) + "&";
+	}
       else
 	name = string(get_type_name(is_function_type(pointed_to),
 				    /*qualified_name=*/true)) + "&";
@@ -22563,33 +22563,44 @@ function_decl_is_less_than(const function_decl &f, const function_decl &s)
 /// Test if two types have similar structures, even though they are
 /// (or can be) different.
 ///
-/// Two indirect types (pointers, references, qualified, typedefs)
-/// have similar structure if their underlying types are of the same
-/// kind and have the same name.  In this indirect types case, the
-/// size of the underlying type does not matter.
+/// const and volatile qualifiers are completely ignored.
+///
+/// typedef are resolved to their definitions; their names are ignored.
+///
+/// Two indirect types (pointers or references) have similar structure
+/// if their underlying types are of the same kind and have the same
+/// name.  In the indirect types case, the size of the underlying type
+/// does not matter.
 ///
 /// Two direct types (i.e, non indirect) have a similar structure if
 /// they have the same kind, name and size.  Two class types have
-/// similar structure if they have the same name, size, and if their
-/// data members have similar types.
+/// similar structure if they have the same name, size, and if the
+/// types of their data members have similar types.
 ///
 /// @param first the first type to consider.
 ///
 /// @param second the second type to consider.
 ///
+/// @param indirect_type whether to do an indirect comparison
+///
 /// @return true iff @p first and @p second have similar structures.
 bool
 types_have_similar_structure(const type_base_sptr& first,
-			     const type_base_sptr& second)
-{return types_have_similar_structure(first.get(), second.get());}
+			     const type_base_sptr& second,
+			     bool indirect_type)
+{return types_have_similar_structure(first.get(), second.get(), indirect_type);}
 
 /// Test if two types have similar structures, even though they are
 /// (or can be) different.
 ///
-/// Two indirect types (pointers, references, qualified, typedefs)
-/// have similar structure if their underlying types are of the same
-/// kind and have the same name.  In this indirect types case, the
-/// size of the underlying type does not matter.
+/// const and volatile qualifiers are completely ignored.
+///
+/// typedef are resolved to their definitions; their names are ignored.
+///
+/// Two indirect types (pointers or references) have similar structure
+/// if their underlying types are of the same kind and have the same
+/// name.  In the indirect types case, the size of the underlying type
+/// does not matter.
 ///
 /// Two direct types (i.e, non indirect) have a similar structure if
 /// they have the same kind, name and size.  Two class types have
@@ -22600,9 +22611,13 @@ types_have_similar_structure(const type_base_sptr& first,
 ///
 /// @param second the second type to consider.
 ///
+/// @param indirect_type whether to do an indirect comparison
+///
 /// @return true iff @p first and @p second have similar structures.
 bool
-types_have_similar_structure(const type_base* first, const type_base* second)
+types_have_similar_structure(const type_base* first,
+			     const type_base* second,
+			     bool indirect_type)
 {
   if (!!first != !!second)
     return false;
@@ -22610,33 +22625,38 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (!first)
     return false;
 
-  if (is_typedef(first) || is_qualified_type(first))
-    first = peel_qualified_or_typedef_type(first);
-
-  if (is_typedef(second) || is_qualified_type(second))
-    second = peel_qualified_or_typedef_type(second);
+  // Treat typedefs purely as type aliases and ignore CV-qualifiers.
+  first = peel_qualified_or_typedef_type(first);
+  second = peel_qualified_or_typedef_type(second);
 
-  bool was_indirect_type = (is_pointer_type(first)
-			    || is_pointer_type(second)
-			    || is_reference_type(first)
-			    || is_reference_type(second));
+  // This check simplifies all the dynamic casting below.
+  if (typeid(*first) != typeid(*second))
+    return false;
 
-  if (was_indirect_type)
+  // Peel off matching pointers.
+  if (const pointer_type_def* ty1 = is_pointer_type(first))
     {
-      first = peel_typedef_pointer_or_reference_type(first);
-      second = peel_typedef_pointer_or_reference_type(second);
+      const pointer_type_def* ty2 = is_pointer_type(second);
+      return types_have_similar_structure(ty1->get_pointed_to_type(),
+					  ty2->get_pointed_to_type(),
+					  true);
     }
 
-  if (typeid(*first) != typeid(*second))
-    return false;
+  // Peel off matching references.
+  if (const reference_type_def* ty1 = is_reference_type(first))
+    {
+      const reference_type_def* ty2 = is_reference_type(second);
+      if (ty1->is_lvalue() != ty2->is_lvalue())
+	return false;
+      return types_have_similar_structure(ty1->get_pointed_to_type(),
+					  ty2->get_pointed_to_type(),
+					  true);
+    }
 
   if (const type_decl* ty1 = is_type_decl(first))
     {
       const type_decl* ty2 = is_type_decl(second);
-      if (ty2 == 0)
-	return false;
-
-      if (!was_indirect_type)
+      if (!indirect_type)
 	if (ty1->get_size_in_bits() != ty2->get_size_in_bits())
 	  return false;
 
@@ -22646,10 +22666,7 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (const enum_type_decl* ty1 = is_enum_type(first))
     {
       const enum_type_decl* ty2 = is_enum_type(second);
-      if (ty2 == 0)
-	return false;
-
-      if (!was_indirect_type)
+      if (!indirect_type)
 	if (ty1->get_size_in_bits() != ty2->get_size_in_bits())
 	  return false;
 
@@ -22660,16 +22677,13 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (const class_decl* ty1 = is_class_type(first))
     {
       const class_decl* ty2 = is_class_type(second);
-      if (ty2 == 0)
-	return false;
-
       if (!ty1->get_is_anonymous() && !ty2->get_is_anonymous()
 	  && ty1->get_name() != ty2->get_name())
 	return false;
 
-      if (!was_indirect_type)
+      if (!indirect_type)
 	{
-	  if (!was_indirect_type)
+	  if (!indirect_type)
 	    if ((ty1->get_size_in_bits() != ty2->get_size_in_bits())
 		|| (ty1->get_non_static_data_members().size()
 		    != ty2->get_non_static_data_members().size()))
@@ -22696,14 +22710,11 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (const union_decl* ty1 = is_union_type(first))
     {
       const union_decl* ty2 = is_union_type(second);
-      if (ty2 == 0)
-	return false;
-
       if (!ty1->get_is_anonymous() && !ty2->get_is_anonymous()
 	  && ty1->get_name() != ty2->get_name())
 	return false;
 
-      if (!was_indirect_type)
+      if (!indirect_type)
 	return ty1->get_size_in_bits() == ty2->get_size_in_bits();
 
       return true;
@@ -22712,13 +22723,13 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (const array_type_def* ty1 = is_array_type(first))
     {
       const array_type_def* ty2 = is_array_type(second);
-
-      if (!was_indirect_type)
-	if (ty1->get_size_in_bits() != ty2->get_size_in_bits()
-	    || ty1->get_dimension_count() != ty2->get_dimension_count()
-	    || !types_have_similar_structure(ty1->get_element_type(),
-					     ty2->get_element_type()))
-	  return false;
+      // TODO: Handle uint32_t[10] vs uint64_t[5] better.
+      if (ty1->get_size_in_bits() != ty2->get_size_in_bits()
+	  || ty1->get_dimension_count() != ty2->get_dimension_count()
+	  || !types_have_similar_structure(ty1->get_element_type(),
+					   ty2->get_element_type(),
+					   true))
+	return false;
 
       return true;
     }
@@ -22726,14 +22737,12 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (const array_type_def::subrange_type *ty1 = is_subrange_type(first))
     {
       const array_type_def::subrange_type *ty2 = is_subrange_type(second);
-      if (!ty2)
-	return false;
-
       if (ty1->get_upper_bound() != ty2->get_upper_bound()
 	  || ty1->get_lower_bound() != ty2->get_lower_bound()
 	  || ty1->get_language() != ty2->get_language()
 	  || !types_have_similar_structure(ty1->get_underlying_type(),
-					  ty2->get_underlying_type()))
+					   ty2->get_underlying_type(),
+					   indirect_type))
 	return false;
 
       return true;
@@ -22742,9 +22751,6 @@ types_have_similar_structure(const type_base* first, const type_base* second)
   if (const function_type* ty1 = is_function_type(first))
     {
       const function_type* ty2 = is_function_type(second);
-      if (!ty2)
-	return false;
-
       if (!types_have_similar_structure(ty1->get_return_type(),
 					ty2->get_return_type()))
 	return false;
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index cf7792f1..6d4e2faa 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -130,6 +130,11 @@ test-abidiff-exit/test-leaf3-v0.o \
 test-abidiff-exit/test-leaf3-v1.c \
 test-abidiff-exit/test-leaf3-v1.o \
 test-abidiff-exit/test-leaf3-report.txt \
+test-abidiff-exit/test-leaf-peeling-v0.cc \
+test-abidiff-exit/test-leaf-peeling-v0.o \
+test-abidiff-exit/test-leaf-peeling-v1.cc \
+test-abidiff-exit/test-leaf-peeling-v1.o \
+test-abidiff-exit/test-leaf-peeling-report.txt \
 \
 test-diff-dwarf/test0-v0.cc		\
 test-diff-dwarf/test0-v0.o			\
diff --git a/tests/data/test-abidiff-exit/test-leaf-peeling-v0.cc b/tests/data/test-abidiff-exit/test-leaf-peeling-v0.cc
new file mode 100644
index 00000000..b927d15e
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-leaf-peeling-v0.cc
@@ -0,0 +1,24 @@
+struct foo {
+  int z;
+};
+
+struct ops1 {
+  int * x;
+};
+
+struct ops2 {
+  foo y[10];
+};
+
+struct ops3 {
+  void (*spong)(int & wibble);
+};
+
+void register_ops1(ops1*) {
+}
+
+void register_ops2(ops2*) {
+}
+
+void register_ops3(ops3*) {
+}
diff --git a/tests/data/test-abidiff-exit/test-leaf-peeling-v0.o b/tests/data/test-abidiff-exit/test-leaf-peeling-v0.o
new file mode 100644
index 0000000000000000000000000000000000000000..a556a63560122a5e926ab708bb13f0fd40714638
GIT binary patch
diff --git a/tests/data/test-abidiff-exit/test-leaf-peeling-v1.cc b/tests/data/test-abidiff-exit/test-leaf-peeling-v1.cc
new file mode 100644
index 00000000..7c88df9f
--- /dev/null
+++ b/tests/data/test-abidiff-exit/test-leaf-peeling-v1.cc
@@ -0,0 +1,24 @@
+struct foo {
+  long z;
+};
+
+struct ops1 {
+  int ** x;
+};
+
+struct ops2 {
+  foo y[10];
+};
+
+struct ops3 {
+  void (*spong)(int && wibble);
+};
+
+void register_ops1(ops1*) {
+}
+
+void register_ops2(ops2*) {
+}
+
+void register_ops3(ops3*) {
+}
diff --git a/tests/data/test-abidiff-exit/test-leaf-peeling-v1.o b/tests/data/test-abidiff-exit/test-leaf-peeling-v1.o
new file mode 100644
index 0000000000000000000000000000000000000000..e71ade4431f80a78ee73292d60808a2ee5404640
GIT binary patch
diff --git a/tests/test-abidiff-exit.cc b/tests/test-abidiff-exit.cc
index 6ecbfd24..ebac7a00 100644
--- a/tests/test-abidiff-exit.cc
+++ b/tests/test-abidiff-exit.cc
@@ -158,6 +158,15 @@ InOutSpec in_out_specs[] =
     "data/test-abidiff-exit/test-leaf3-report.txt",
     "output/test-abidiff-exit/test-leaf3-report.txt"
   },
+  {
+    "data/test-abidiff-exit/test-leaf-peeling-v0.o",
+    "data/test-abidiff-exit/test-leaf-peeling-v1.o",
+    "",
+    "--leaf-changes-only",
+    abigail::tools_utils::ABIDIFF_ABI_CHANGE,
+    "data/test-abidiff-exit/test-leaf-peeling-report.txt",
+    "output/test-abidiff-exit/test-leaf-peeling-report.txt"
+  },
   {0, 0, 0 ,0,  abigail::tools_utils::ABIDIFF_OK, 0, 0}
 };
 
