c++: alias in qualified-id in template arg [PR98570]
authorJason Merrill <jason@redhat.com>
Wed, 27 Jan 2021 22:15:39 +0000 (17:15 -0500)
committerJason Merrill <jason@redhat.com>
Mon, 1 Feb 2021 14:49:42 +0000 (09:49 -0500)
template_args_equal has handled dependent alias specializations for a while,
but in this testcase the actual template argument is a SCOPE_REF, so we
called cp_tree_equal, which doesn't handle aliases specially when we get to
them.

This patch generalizes this by setting a flag so structural_comptypes will
check for template alias equivalence (if we aren't doing partial ordering).
The existing flag, comparing_specializations, was too broad; in particular,
when we're doing decls_match, we want to treat corresponding parameters as
equivalent, so we need to separate that from alias comparison.  So I
introduce the comparing_dependent_aliases flag.

From looking at other uses of comparing_specializations, it seems to me that
the new flag is what modules wants, as well.

The other use of comparing_specializations in structural_comptypes is a hack
to deal with spec_hasher::equal not calling push_to_top_level, which we
also don't want to tie to the alias comparison semantics.

This patch also changes how we get to structural comparison of aliases from
checking TYPE_CANONICAL in comptypes to marking the aliases as getting
structural comparison when they are built, which is more consistent with how
e.g. typename is handled.

As I mention in the comment for comparing_dependent_aliases, I think the
default should be to treat different dependent aliases for the same type as
distinct, only treating them as equal during deduction (particularly partial
ordering).  But that's a matter for the C++ committee, to try in stage 1.

gcc/cp/ChangeLog:

PR c++/98570
* cp-tree.h: Declare it.
* pt.c (comparing_dependent_aliases): New flag.
(template_args_equal, spec_hasher::equal): Set it.
(dependent_alias_template_spec_p): Assert that we don't
get non-types other than error_mark_node.
(instantiate_alias_template): SET_TYPE_STRUCTURAL_EQUALITY
on complex alias specializations.  Set TYPE_DEPENDENT_P here.
(tsubst_decl): Not here.
* module.cc (module_state::read_cluster): Set
comparing_dependent_aliases instead of
comparing_specializations.
* tree.c (cp_tree_equal): Remove comparing_specializations
module handling.
* typeck.c (structural_comptypes): Adjust.
(comptypes): Remove comparing_specializations handling.

gcc/testsuite/ChangeLog:

PR c++/98570
* g++.dg/cpp0x/alias-decl-targ1.C: New test.

gcc/cp/cp-tree.h
gcc/cp/module.cc
gcc/cp/pt.c
gcc/cp/tree.c
gcc/cp/typeck.c
gcc/testsuite/g++.dg/cpp0x/alias-decl-targ1.C [new file with mode: 0644]

index f31319904eb1b1d080188b1216a7beb821e2c12e..aed85d7928715fd9613c455ff78c63f744d526e3 100644 (file)
@@ -5449,11 +5449,14 @@ extern GTY(()) tree integer_two_node;
    function, two inside the body of a function in a local class, etc.)  */
 extern int function_depth;
 
-/* Nonzero if we are inside eq_specializations, which affects
-   comparison of PARM_DECLs in cp_tree_equal and alias specializations
-   in structrual_comptypes.  */
+/* Nonzero if we are inside spec_hasher::equal, which affects
+   comparison of PARM_DECLs in cp_tree_equal.  */
 extern int comparing_specializations;
 
+/* Nonzero if we want different dependent aliases to compare as unequal.
+   FIXME we should always do this except during deduction/ordering.  */
+extern int comparing_dependent_aliases;
+
 /* When comparing specializations permit context _FROM to match _TO.  */
 extern tree map_context_from;
 extern tree map_context_to;
index 2d761452505db80b709954d70cf115b083fcc49b..41ce20115255aae298acc7da0af51de8845cd9e4 100644 (file)
@@ -14801,7 +14801,7 @@ module_state::read_cluster (unsigned snum)
   dump.indent ();
 
   /* We care about structural equality.  */
-  comparing_specializations++;
+  comparing_dependent_aliases++;
 
   /* First seed the imports.  */
   while (tree import = sec.tree_node ())
@@ -14976,7 +14976,7 @@ module_state::read_cluster (unsigned snum)
 #undef cfun
   cfun = old_cfun;
   current_function_decl = old_cfd;
-  comparing_specializations--;
+  comparing_dependent_aliases--;
 
   dump.outdent ();
   dump () && dump ("Read section:%u", snum);
index 9089afb6ae822243963675ce3859511b9a0bb0f2..db0ff73bdeb8a75c9b9b50d1dbbc01ccd0838ee9 100644 (file)
@@ -1709,6 +1709,7 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
 
 /* Restricts tree and type comparisons.  */
 int comparing_specializations;
+int comparing_dependent_aliases;
 
 /* Returns true iff two spec_entry nodes are equivalent.  */
 
@@ -1718,6 +1719,7 @@ spec_hasher::equal (spec_entry *e1, spec_entry *e2)
   int equal;
 
   ++comparing_specializations;
+  ++comparing_dependent_aliases;
   equal = (e1->tmpl == e2->tmpl
           && comp_template_args (e1->args, e2->args));
   if (equal && flag_concepts
@@ -1732,6 +1734,7 @@ spec_hasher::equal (spec_entry *e1, spec_entry *e2)
       tree c2 = e2->spec ? get_constraints (e2->spec) : NULL_TREE;
       equal = equivalent_constraints (c1, c2);
     }
+  --comparing_dependent_aliases;
   --comparing_specializations;
 
   return equal;
@@ -6516,7 +6519,11 @@ complex_alias_template_p (const_tree tmpl)
 tree
 dependent_alias_template_spec_p (const_tree t, bool transparent_typedefs)
 {
-  if (!TYPE_P (t) || !typedef_variant_p (t))
+  if (t == error_mark_node)
+    return NULL_TREE;
+  gcc_assert (TYPE_P (t));
+
+  if (!typedef_variant_p (t))
     return NULL_TREE;
 
   tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t);
@@ -9166,6 +9173,18 @@ template_args_equal (tree ot, tree nt, bool partial_order /* = false */)
   if (class_nttp_const_wrapper_p (ot))
     ot = TREE_OPERAND (ot, 0);
 
+  /* DR 1558: 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.
+
+     During partial ordering, however, we need to treat them normally so we can
+     order uses of the same alias with different cv-qualification (79960).  */
+  auto cso = make_temp_override (comparing_dependent_aliases);
+  if (!partial_order)
+    ++comparing_dependent_aliases;
+
   if (TREE_CODE (nt) == TREE_VEC || TREE_CODE (ot) == TREE_VEC)
     /* For member templates */
     return TREE_CODE (ot) == TREE_CODE (nt) && comp_template_args (ot, nt);
@@ -9183,21 +9202,7 @@ template_args_equal (tree ot, tree nt, bool partial_order /* = false */)
     {
       if (!(TYPE_P (nt) && TYPE_P (ot)))
        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.
-
-         During partial ordering, however, we need to treat them normally so
-         that we can order uses of the same alias with different
-         cv-qualification (79960).  */
-      if (!partial_order
-         && (TYPE_ALIAS_P (nt) || TYPE_ALIAS_P (ot)))
-       return false;
-      else
-       return same_type_p (ot, nt);
+      return same_type_p (ot, nt);
     }
   else
     {
@@ -14903,10 +14908,6 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
          {
            DECL_ORIGINAL_TYPE (r) = NULL_TREE;
            set_underlying_type (r);
-           if (TYPE_DECL_ALIAS_P (r))
-             /* An alias template specialization can be dependent
-                even if its underlying type is not.  */
-             TYPE_DEPENDENT_P_VALID (TREE_TYPE (r)) = false;
          }
 
        layout_decl (r, 0);
@@ -21136,6 +21137,17 @@ instantiate_alias_template (tree tmpl, tree args, tsubst_flags_t complain)
   tree r = instantiate_template (tmpl, args, complain);
   pop_tinst_level ();
 
+  if (tree d = dependent_alias_template_spec_p (TREE_TYPE (r), nt_opaque))
+    {
+      /* An alias template specialization can be dependent
+        even if its underlying type is not.  */
+      TYPE_DEPENDENT_P (d) = true;
+      TYPE_DEPENDENT_P_VALID (d) = true;
+      /* Sometimes a dependent alias spec is equivalent to its expansion,
+        sometimes not.  So always use structural_comptypes.  */
+      SET_TYPE_STRUCTURAL_EQUALITY (d);
+    }
+
   return r;
 }
 
index 7dcb453ba6ae9cd2629ac908a16abe29cfcec5e6..2e5a1f198e82ec926a5e18e5d63ec2b83479a160 100644 (file)
@@ -3847,12 +3847,7 @@ cp_tree_equal (tree t1, tree t2)
         template.  */
 
       if (comparing_specializations
-         && DECL_CONTEXT (t1) != DECL_CONTEXT (t2)
-         /* Module duplicate checking can have t1 = new, t2 =
-            existing, and they should be considered matching at this
-            point.  */
-         && !(DECL_CONTEXT (t1) == map_context_from
-              && DECL_CONTEXT (t2) == map_context_to))
+         && DECL_CONTEXT (t1) != DECL_CONTEXT (t2))
        /* When comparing hash table entries, only an exact match is
           good enough; we don't want to replace 'this' with the
           version from another function.  But be more flexible
index 9322e087345e1e6ff140ff4f90ccd3c362951ad8..a87d5e5f2ac9bea9ee4dfcbc70c9e3840bf40a5d 100644 (file)
@@ -1251,6 +1251,8 @@ structural_comptypes (tree t1, tree t2, int strict)
   /* Both should be types that are not obviously the same.  */
   gcc_checking_assert (t1 != t2 && TYPE_P (t1) && TYPE_P (t2));
 
+  /* Suppress typename resolution under spec_hasher::equal in place of calling
+     push_to_top_level there.  */
   if (!comparing_specializations)
     {
       /* TYPENAME_TYPEs should be resolved if the qualifying scope is the
@@ -1483,7 +1485,7 @@ structural_comptypes (tree t1, tree t2, int strict)
     return false;
 
  check_alias:
-  if (comparing_specializations)
+  if (comparing_dependent_aliases)
     {
       /* Don't treat an alias template specialization with dependent
         arguments as equivalent to its underlying type when used as a
@@ -1519,11 +1521,6 @@ comptypes (tree t1, tree t2, int strict)
   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.  */
-    strict = COMPARE_STRUCTURAL;
-
   if (strict == COMPARE_STRICT)
     {
       if (TYPE_STRUCTURAL_EQUALITY_P (t1) || TYPE_STRUCTURAL_EQUALITY_P (t2))
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-targ1.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-targ1.C
new file mode 100644 (file)
index 0000000..dd97479
--- /dev/null
@@ -0,0 +1,9 @@
+// PR c++/98570
+// { dg-do compile { target c++11 } }
+
+template <int> struct b { enum { c }; };
+template <typename> using i = b<0>;
+
+template <int> struct d { };
+template <typename l> d<i<l>::c> m() { }
+template <typename n> d<i<n*>::c> m() { }