c++: check alias match for specializations [PR98116]
authorNathan Sidwell <nathan@acm.org>
Mon, 7 Dec 2020 15:02:58 +0000 (07:02 -0800)
committerNathan Sidwell <nathan@acm.org>
Mon, 7 Dec 2020 16:49:33 +0000 (08:49 -0800)
This fixes the underlying problem my recent (backedout) changes to
array type creation uncovered.  We had paths through
structural_comptypes that ignored alias templates, even when
significant.  This adds the necessary checks.

PR c++/98116
gcc/cp/
* typeck.c (structural_comptypes): Move early outs to comptype.
Always check template-alias match when comparing_specializations.
(comptypes): Do early out checking here.
gcc/testsuite/
* g++.dg/template/pr98116.C: Remove dg-ice.
* g++.dg/template/pr98116-2.C: New.

gcc/cp/typeck.c
gcc/testsuite/g++.dg/template/pr98116-2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/template/pr98116.C

index 267b284ea40fad3c985ed520e7f7eab91ea19fc8..4d499af5ccb91cae326207d946125830f654b020 100644 (file)
@@ -1247,14 +1247,8 @@ cxx_safe_function_type_cast_p (tree t1, tree t2)
 static bool
 structural_comptypes (tree t1, tree t2, int strict)
 {
-  if (t1 == t2)
-    return true;
-
-  /* Suppress errors caused by previously reported errors.  */
-  if (t1 == error_mark_node || t2 == error_mark_node)
-    return false;
-
-  gcc_assert (TYPE_P (t1) && TYPE_P (t2));
+  /* Both should be types that are not obviously the same.  */
+  gcc_checking_assert (t1 != t2 && TYPE_P (t1) && TYPE_P (t2));
 
   if (!comparing_specializations)
     {
@@ -1300,13 +1294,13 @@ structural_comptypes (tree t1, tree t2, int strict)
   /* Allow for two different type nodes which have essentially the same
      definition.  Note that we already checked for equality of the type
      qualifiers (just above).  */
-
   if (TREE_CODE (t1) != ARRAY_TYPE
       && TYPE_MAIN_VARIANT (t1) == TYPE_MAIN_VARIANT (t2))
-    return true;
-
+    goto check_alias;
 
-  /* Compare the types.  Break out if they could be the same.  */
+  /* Compare the types.  Return false on known not-same. Break on not
+     known.   Never return true from this switch -- you'll break
+     specialization comparison.    */
   switch (TREE_CODE (t1))
     {
     case VOID_TYPE:
@@ -1332,7 +1326,11 @@ structural_comptypes (tree t1, tree t2, int strict)
         have identical properties, different TYPE_MAIN_VARIANTs, but
         represent the same type.  The canonical type system keeps
         track of equivalence in this case, so we fall back on it.  */
-      return TYPE_CANONICAL (t1) == TYPE_CANONICAL (t2);
+      if (TYPE_CANONICAL (t1) != TYPE_CANONICAL (t2))
+       return false;
+
+      /* We don't need or want the attribute comparison.  */
+      goto check_alias;
 
     case TEMPLATE_TEMPLATE_PARM:
     case BOUND_TEMPLATE_TEMPLATE_PARM:
@@ -1477,24 +1475,28 @@ structural_comptypes (tree t1, tree t2, int strict)
       return false;
     }
 
-  /* Don't treat an alias template specialization with dependent
-     arguments as equivalent to its underlying type when used as a
-     template argument; we need them to be distinct so that we
-     substitute into the specialization arguments at instantiation
-     time.  And aliases can't be equivalent without being ==, so
-     we don't need to look any deeper.  */
+  /* If we get here, we know that from a target independent POV the
+     types are the same.  Make sure the target attributes are also
+     the same.  */
+  if (!comp_type_attributes (t1, t2))
+    return false;
+
+ check_alias:
   if (comparing_specializations)
     {
+      /* Don't treat an alias template specialization with dependent
+        arguments as equivalent to its underlying type when used as a
+        template argument; we need them to be distinct so that we
+        substitute into the specialization arguments at instantiation
+        time.  And aliases can't be equivalent without being ==, so
+        we don't need to look any deeper.  */
       tree dep1 = dependent_alias_template_spec_p (t1, nt_transparent);
       tree dep2 = dependent_alias_template_spec_p (t2, nt_transparent);
       if ((dep1 || dep2) && dep1 != dep2)
        return false;
     }
 
-  /* If we get here, we know that from a target independent POV the
-     types are the same.  Make sure the target attributes are also
-     the same.  */
-  return comp_type_attributes (t1, t2);
+  return true;
 }
 
 /* Return true if T1 and T2 are related as allowed by STRICT.  STRICT
@@ -1509,6 +1511,13 @@ comptypes (tree t1, tree t2, int strict)
   gcc_checking_assert (TREE_CODE (t1) != TYPE_ARGUMENT_PACK
                       && TREE_CODE (t2) != TYPE_ARGUMENT_PACK);
 
+  if (t1 == t2)
+    return true;
+
+  /* Suppress errors caused by previously reported errors.  */
+  if (t1 == error_mark_node || t2 == error_mark_node)
+    return false;
+
   if (strict == COMPARE_STRICT && comparing_specializations
       && (t1 != TYPE_CANONICAL (t1) || t2 != TYPE_CANONICAL (t2)))
     /* If comparing_specializations, treat dependent aliases as distinct.  */
@@ -1516,12 +1525,6 @@ comptypes (tree t1, tree t2, int strict)
 
   if (strict == COMPARE_STRICT)
     {
-      if (t1 == t2)
-       return true;
-
-      if (t1 == error_mark_node || t2 == error_mark_node)
-       return false;
-
       if (TYPE_STRUCTURAL_EQUALITY_P (t1) || TYPE_STRUCTURAL_EQUALITY_P (t2))
        /* At least one of the types requires structural equality, so
           perform a deep check. */
diff --git a/gcc/testsuite/g++.dg/template/pr98116-2.C b/gcc/testsuite/g++.dg/template/pr98116-2.C
new file mode 100644 (file)
index 0000000..fd12bb1
--- /dev/null
@@ -0,0 +1,34 @@
+// PR 98116, ICE with stripping typedef array type
+// { dg-do compile { target c++11 } }
+// { dg-additional-options {--param=hash-table-verification-limit=10000000 -fchecking=2} }
+
+// We got confused by alias templates that alias the same type.  Their
+// hashes were different (good), but they compared equal (bad)
+
+namespace std {
+typedef int is_convertible;
+template <typename _Tp> using remove_pointer_t = typename _Tp ::type;
+template <bool> struct enable_if;
+template <typename> void declval();
+template <bool _Cond> using enable_if_t = typename enable_if<_Cond>::type;
+template <typename, typename> class Trans_NS___cxx11_basic_string {
+  long _M_string_length;
+};
+} // namespace std
+struct string16_char_traits;
+template class std::Trans_NS___cxx11_basic_string<unsigned short,
+                                                  string16_char_traits>;
+template <typename, typename> using IsLegalDataConversion = std::is_convertible;
+template <typename Container, typename T>
+using ContainerHasConvertibleData = IsLegalDataConversion<
+    std::remove_pointer_t<decltype(std::declval<Container>)>, T>;
+template <typename Array, typename T, long>
+using EnableIfSpanCompatibleArray =
+  std::enable_if_t<!!sizeof (ContainerHasConvertibleData<Array, T>)>;
+template <int Extent> class span {
+  template <long N, EnableIfSpanCompatibleArray<
+                        const std::Trans_NS___cxx11_basic_string<
+                            unsigned short, string16_char_traits>[N],
+                        std::Trans_NS___cxx11_basic_string<short, int>, Extent>>
+  span();
+};
index 874c590f9d238295bc99a72e46c52e7ea776652c..7d54314b26b36dc2281b1b0be54c4e4ac939d357 100644 (file)
@@ -1,10 +1,9 @@
 // PR 98116, ICE with stripping typedef array type
 // { dg-do compile { target c++11 } }
 // { dg-additional-options {--param=hash-table-verification-limit=10000000 -fchecking=2} }
-// { dg-ice "spec_hasher::equal" }
 
-// We get confused by alias templates that alias the same type.
-// { dg-prune-output "hash table checking failed" }
+// We got confused by alias templates that alias the same type.  Their
+// hashes were different (good), but they compared equal (bad)
 
 namespace std {
 struct is_convertible;