On Mon, 22 Jul 2024, Jason Merrill wrote:

> On 7/19/24 10:30 AM, Patrick Palka wrote:
> > On Thu, 18 Jul 2024, Jason Merrill wrote:
> > 
> > > On 7/18/24 12:45 PM, Patrick Palka wrote:
> > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does thi look
> > > > OK for trunk/14?
> > > > 
> > > > -- >8 --
> > > > 
> > > > As a followup of r15-2047-g7954bb4fcb6fa8, we also need to consider
> > > > dependent attributes when recursing into a non-template alias that names
> > > > a dependent alias template specialization (and so STF_STRIP_DEPENDENT
> > > > is set), otherwise in the first testcase below we undesirably strip B
> > > > all the way to T instead of to A<T>.
> > > > 
> > > > We also need to move the typedef recursion case of strip_typedefs up to
> > > > get checked before the compound type recursion cases.  Otherwise for C
> > > > below (which ultimately aliases T*) we end up stripping it to T* instead
> > > > of to A<T*> because the POINTER_TYPE recursion dominates the typedef
> > > > recursion.  It also means we issue an unexpected extra error in the
> > > > third testcase below.
> > > > 
> > > > Ideally we would also want to consider dependent attributes on
> > > > non-template aliases, so that we accept the second testcase below, but
> > > > making that work correctly would require broader changes to e.g.
> > > > spec_hasher which currently assumes all non-template aliases are
> > > > stripped and hence it'd conflate the dependent specializations A<T>
> > > > and A<B> even if we didn't strip B.
> > > 
> > > Wouldn't that just be a matter of changing structural_comptypes to
> > > consider
> > > dependent attributes as well as dependent specializations?
> > 
> > Pretty much, it seems.  ISTM we should check dependent attributes even
> > when !comparing_dependent_aliases since they affect type identity rather
> > than just SFINAE behavior.
> > 
> > > 
> > > Or better, adding attributes to dependent_alias_template_spec_p (and
> > > changing
> > > its name)?  It seems like other callers would also benefit from that
> > > change.
> > 
> > I ended up adding a new predicate opaque_alias_p separate from
> > dependent_alias_template_spec_p since ISTM we need to call it from
> > there and from alias_template_specialization_p to avoid looking through
> > such aliases.
> 
> Sounds good, but I think let's add the word "dependent" to the name of the new
> function.

Done.

> 
> > So opaque_alias_p checks for type identity of an alias, whereas
> > dependent_alias_template_spec_p more broadly checks for SFINAE identity.
> > 
> > Something like the following (as an incremental patch on top of the
> > previous one, to consider separately for backportings since it's riskier):
> > 
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index 0620c8c023a..4d4a5cef92c 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -6508,6 +6508,19 @@ alias_type_or_template_p (tree t)
> >       || DECL_ALIAS_TEMPLATE_P (t));
> >   }
> >   +/* Return true if substituting into T would yield a different type than
> > +   substituting into its expansion.  */
> 
> Please discuss when to use this vs dependent_alias_template_spec_p (both here
> and there).  Maybe just to say that any place that checks one probably also
> wants to check the other.
> 
> Other places that use d_a_t_s_p and seem to need adjusting:
> dependent_type_p_r, any_dependent_arguments_need_structural_equality_p,
> alias_ctad_tweaks.

For dependent_type_p_r, I think we can get rid of the existing d_a_t_s_p
check if we manually set TYPE_DEPENDENT_P/_VALID to true at parse time
like we do at instantiation time from instantiate_alias_template.

For any_dependent_arguments_need_structural_equality_p, those checks
can't be removed but we can relax nt_transparent to nt_opaque since we
know the template arguments have already gone through strip_typedefs.

For alias_ctad_tweaks I think we can replace the d_a_t_s_p with
template_args_equal which has the desired comparing_dependent_aliases
behavior.

After the above, it seems no relevant user of d_a_t_s_p survives that
passes nt_transparent instead of nt_opaque, and so we can avoid adding
a transparent_typedefs flag to the new predicate.

> 
> > +bool
> > +opaque_alias_p (const_tree t)
> > +{
> > +  return (TYPE_P (t)
> > +     && typedef_variant_p (t)
> > +     && uses_template_parms (DECL_ORIGINAL_TYPE (TYPE_NAME (t)))
> 
> Checking this seems wrong; a vector of dependent size seems opaque even if
> it's a vector of int.

Makes sense, fixed.  I added a testcase variant too.

-- >8 --

Subject: [PATCH 2/1] c++: non-template alias with dependent attributes 
[PR115897]

        PR c++/115897

gcc/cp/ChangeLog:

        * cp-tree.h (dependent_opaque_alias_p): Declare.
        * pt.cc (push_template_decl): Manually mark a dependent opaque
        alias or dependent alias template specialization as dependent,
        and use structural equality for them.
        * pt.cc (dependent_opaque_alias_p): Define.
        (alias_template_specialization_p): Don't look through an
        opaque alias.
        (complex_alias_template_p): Use dependent_opaque_alias_p instead of
        any_dependent_template_arguments_p directly.
        (dependent_alias_template_spec_p): Don't look through an
        opaque alias.
        (get_underlying_template): Use dependent_opaque_alias_p instead of
        any_dependent_template_arguments_p.
        (instantiate_alias_template): Add comment mentioning similar
        behavior in push_template_decl above.
        (dependent_type_p_r): Remove dependent_alias_template_spec_p check.
        (any_template_arguments_need_structural_equality_p): Return true
        for a dependent opaque alias argument.
        (alias_ctad_tweaks): Use template_args_equal instead of same_type_p
        followed by dependent_alias_template_spec_p.
        * tree.cc (strip_typedefs): Don't strip an opaque alias.
        * typeck.cc (structural_comptypes): Compare declaration attributes
        for an opaque alias.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/alias-decl-79.C: Remove xfails.
        * g++.dg/cpp0x/alias-decl-79a.C: New test.
---
 gcc/cp/cp-tree.h                            |  1 +
 gcc/cp/pt.cc                                | 55 +++++++++++++++------
 gcc/cp/tree.cc                              |  7 +--
 gcc/cp/typeck.cc                            | 17 +++++--
 gcc/testsuite/g++.dg/cpp0x/alias-decl-79.C  | 16 +++---
 gcc/testsuite/g++.dg/cpp0x/alias-decl-79a.C | 41 +++++++++++++++
 6 files changed, 106 insertions(+), 31 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-79a.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 7d50aac4b6b..0de181ae84d 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -7619,6 +7619,7 @@ extern tree instantiate_non_dependent_or_null   (tree);
 extern bool variable_template_specialization_p  (tree);
 extern bool alias_type_or_template_p            (tree);
 enum { nt_opaque = false, nt_transparent = true };
+extern bool dependent_opaque_alias_p                   (const_tree);
 extern tree alias_template_specialization_p     (const_tree, bool);
 extern tree dependent_alias_template_spec_p     (const_tree, bool);
 extern tree get_template_parm_object           (tree expr, tree mangle);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index e102e3ea490..4da3bb49ccf 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -6271,6 +6271,18 @@ push_template_decl (tree decl, bool is_friend)
        }
     }
 
+  if (is_typedef_decl (decl)
+      && (dependent_opaque_alias_p (TREE_TYPE (decl))
+         || dependent_alias_template_spec_p (TREE_TYPE (decl), nt_opaque)))
+    {
+      /* Manually mark such aliases as dependent so that dependent_type_p_r
+        doesn't have to check these predicates.  */
+      TYPE_DEPENDENT_P_VALID (TREE_TYPE (decl)) = true;
+      TYPE_DEPENDENT_P (TREE_TYPE (decl)) = true;
+      /* The identity of such aliases is hairy; see structural_comptypes.  */
+      SET_TYPE_STRUCTURAL_EQUALITY (TREE_TYPE (decl));
+    }
+
   if (flag_implicit_templates
       && !is_friend
       && TREE_PUBLIC (decl)
@@ -6513,6 +6525,19 @@ alias_type_or_template_p (tree t)
          || DECL_ALIAS_TEMPLATE_P (t));
 }
 
+/* Return true if substituting into T would yield a different type than
+   substituting into its expansion.  Checking this predicate is usually
+   done in tandem with dependent_alias_template_spec_p.  */
+
+bool
+dependent_opaque_alias_p (const_tree t)
+{
+  return (TYPE_P (t)
+         && typedef_variant_p (t)
+         && any_dependent_type_attributes_p (DECL_ATTRIBUTES
+                                             (TYPE_NAME (t))));
+}
+
 /* If T is a specialization of an alias template, return it; otherwise return
    NULL_TREE.  If TRANSPARENT_TYPEDEFS is true, look through other aliases.  */
 
@@ -6530,7 +6555,7 @@ alias_template_specialization_p (const_tree t,
       if (tree tinfo = TYPE_ALIAS_TEMPLATE_INFO (t))
        if (PRIMARY_TEMPLATE_P (TI_TEMPLATE (tinfo)))
          return CONST_CAST_TREE (t);
-      if (transparent_typedefs)
+      if (transparent_typedefs && !dependent_opaque_alias_p (t))
        return alias_template_specialization_p (DECL_ORIGINAL_TYPE
                                                (TYPE_NAME (t)),
                                                transparent_typedefs);
@@ -6635,8 +6660,7 @@ complex_alias_template_p (const_tree tmpl, tree *seen_out)
     return true;
 
   /* An alias with dependent type attributes is complex.  */
-  if (any_dependent_type_attributes_p (DECL_ATTRIBUTES
-                                      (DECL_TEMPLATE_RESULT (tmpl))))
+  if (dependent_opaque_alias_p (TREE_TYPE (tmpl)))
     return true;
 
   if (!complex_alias_tmpl_info)
@@ -6687,7 +6711,10 @@ complex_alias_template_p (const_tree tmpl, tree 
*seen_out)
 /* If T is a specialization of a complex alias template with a dependent
    argument for an unused template parameter, return it; otherwise return
    NULL_TREE.  If T is a typedef to such a specialization, return the
-   specialization.  */
+   specialization.  Checking this predicate is usually done in tandem
+   with dependent_opaque_alias_p.  Whereas dependent_opaque_alias_p checks
+   type equivalence of an alias vs its expansion, this predicate more
+   broadly checks SFINAE equivalence.  */
 
 tree
 dependent_alias_template_spec_p (const_tree t, bool transparent_typedefs)
@@ -6723,7 +6750,7 @@ dependent_alias_template_spec_p (const_tree t, bool 
transparent_typedefs)
        }
     }
 
-  if (transparent_typedefs)
+  if (transparent_typedefs && !dependent_opaque_alias_p (t))
     {
       tree utype = DECL_ORIGINAL_TYPE (TYPE_NAME (t));
       return dependent_alias_template_spec_p (utype, transparent_typedefs);
@@ -6792,8 +6819,7 @@ get_underlying_template (tree tmpl)
        break;
 
       /* If TMPL adds dependent type attributes, it isn't equivalent.  */
-      if (any_dependent_type_attributes_p (DECL_ATTRIBUTES
-                                          (DECL_TEMPLATE_RESULT (tmpl))))
+      if (dependent_opaque_alias_p (TREE_TYPE (tmpl)))
        break;
 
       /* Alias is equivalent.  Strip it and repeat.  */
@@ -22278,6 +22304,7 @@ instantiate_alias_template (tree tmpl, tree args, 
tsubst_flags_t complain)
 
   if (tree d = dependent_alias_template_spec_p (TREE_TYPE (r), nt_opaque))
     {
+      /* Note this is also done at parse time from push_template_decl.  */
       /* An alias template specialization can be dependent
         even if its underlying type is not.  */
       TYPE_DEPENDENT_P (d) = true;
@@ -27906,11 +27933,6 @@ dependent_type_p_r (tree type)
   if (TREE_CODE (type) == TYPENAME_TYPE)
     return true;
 
-  /* An alias template specialization can be dependent even if the
-     resulting type is not.  */
-  if (dependent_alias_template_spec_p (type, nt_transparent))
-    return true;
-
   /* -- a cv-qualified type where the cv-unqualified type is
        dependent.
      No code is necessary for this bullet; the code below handles
@@ -29018,7 +29040,8 @@ any_template_arguments_need_structural_equality_p (tree 
args)
                return true;
              else if (TYPE_P (arg)
                       && TYPE_STRUCTURAL_EQUALITY_P (arg)
-                      && dependent_alias_template_spec_p (arg, nt_transparent))
+                      && (dependent_alias_template_spec_p (arg, nt_opaque)
+                          || dependent_opaque_alias_p (arg)))
                /* Require structural equality for specializations written
                   in terms of a dependent alias template specialization.  */
                return true;
@@ -30418,9 +30441,9 @@ alias_ctad_tweaks (tree tmpl, tree uguides)
             A with the same template arguments.  */
          ret = TREE_TYPE (TREE_TYPE (fprime));
          if (ctad_kind == alias
-             && (!same_type_p (atype, ret)
-                 /* FIXME this should mean they don't compare as equivalent. */
-                 || dependent_alias_template_spec_p (atype, nt_opaque)))
+             /* Use template_args_equal instead of same_type_p to get the
+                comparing_dependent_aliases behavior.  */
+             && !template_args_equal (atype, ret))
            {
              tree same = finish_trait_expr (loc, CPTK_IS_DEDUCIBLE, tmpl, ret);
              ci = append_constraint (ci, same);
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index f2001ace6db..31ecbb1ac79 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -1613,12 +1613,13 @@ strip_typedefs (tree t, bool *remove_attributes /* = 
NULL */,
          && !user_facing_original_type_p (t))
        return t;
 
+      if (dependent_opaque_alias_p (t))
+       return t;
+
       if (alias_template_specialization_p (t, nt_opaque))
        {
          if (dependent_alias_template_spec_p (t, nt_opaque)
-             && (!(flags & STF_STRIP_DEPENDENT)
-                 || any_dependent_type_attributes_p (DECL_ATTRIBUTES
-                                                     (TYPE_NAME (t)))))
+             && !(flags & STF_STRIP_DEPENDENT))
            /* DR 1558: However, if the template-id is dependent, subsequent
               template argument substitution still applies to the template-id. 
 */
            return t;
diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc
index 8df8b871676..f26b5b2a1f4 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1658,8 +1658,17 @@ structural_comptypes (tree t1, tree t2, int strict)
     return false;
 
  check_alias:
-  if (comparing_dependent_aliases)
-    {
+  if (comparing_dependent_aliases
+      && (typedef_variant_p (t1) || typedef_variant_p (t2)))
+    {
+      tree dep1 = dependent_opaque_alias_p (t1) ? t1 : NULL_TREE;
+      tree dep2 = dependent_opaque_alias_p (t2) ? t2 : NULL_TREE;
+      if ((dep1 || dep2)
+         && (!(dep1 && dep2)
+             || !comp_type_attributes (DECL_ATTRIBUTES (TYPE_NAME (dep1)),
+                                       DECL_ATTRIBUTES (TYPE_NAME (dep2)))))
+       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
@@ -1667,8 +1676,8 @@ structural_comptypes (tree t1, tree t2, int strict)
         time.  And aliases can't be equivalent without being ==, so
         we don't need to look any deeper.  */
       ++processing_template_decl;
-      tree dep1 = dependent_alias_template_spec_p (t1, nt_transparent);
-      tree dep2 = dependent_alias_template_spec_p (t2, nt_transparent);
+      dep1 = dependent_alias_template_spec_p (t1, nt_transparent);
+      dep2 = dependent_alias_template_spec_p (t2, nt_transparent);
       --processing_template_decl;
       if ((dep1 || dep2) && dep1 != dep2)
        return false;
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-79.C 
b/gcc/testsuite/g++.dg/cpp0x/alias-decl-79.C
index e0f07475cc1..58436f907ef 100644
--- a/gcc/testsuite/g++.dg/cpp0x/alias-decl-79.C
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-79.C
@@ -14,22 +14,22 @@ template<class T> struct A;
 template<class T>
 void f() {
   using B [[gnu::vector_size(16)]] = T;
-  static_assert(!is_same<T, B>::value, "");        // { dg-bogus "" "" { xfail 
*-*-* } }
-  static_assert(!is_same<A<T>, A<B>>::value, "");  // { dg-bogus "" "" { xfail 
*-*-* } }
+  static_assert(!is_same<T, B>::value, "");
+  static_assert(!is_same<A<T>, A<B>>::value, "");
 #if __cpp_variable_templates
-  static_assert(!is_same_v<T, B>, "");             // { dg-bogus "" "" { xfail 
c++14 } }
-  static_assert(!is_same_v<A<T>, A<B>>, "");       // { dg-bogus "" "" { xfail 
c++14 } }
+  static_assert(!is_same_v<T, B>, "");
+  static_assert(!is_same_v<A<T>, A<B>>, "");
 #endif
 };
 
 template<class T>
 void g() {
   using C [[gnu::vector_size(16)]] = T*;
-  static_assert(!is_same<T*, C>::value, "");       // { dg-bogus "" "" { xfail 
*-*-* } }
-  static_assert(!is_same<A<T*>, A<C>>::value, ""); // { dg-bogus "" "" { xfail 
*-*-* } }
+  static_assert(!is_same<T*, C>::value, "");
+  static_assert(!is_same<A<T*>, A<C>>::value, "");
 #if __cpp_variable_templates
-  static_assert(!is_same_v<T*, C>, "");            // { dg-bogus "" "" { xfail 
c++14 } }
-  static_assert(!is_same_v<A<T*>, A<C>>, "");      // { dg-bogus "" "" { xfail 
c++14 } }
+  static_assert(!is_same_v<T*, C>, "");
+  static_assert(!is_same_v<A<T*>, A<C>>, "");
 #endif
 };
 
diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-79a.C 
b/gcc/testsuite/g++.dg/cpp0x/alias-decl-79a.C
new file mode 100644
index 00000000000..151b8487e1f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-79a.C
@@ -0,0 +1,41 @@
+// A version of alias-decl-79.C where defining-type-id of B and C
+// are not dependent and instead their vector_size attribute is.
+// PR c++/115897
+// { dg-do compile { target c++11 } }
+
+template<class T, class U>
+struct is_same { static constexpr bool value = __is_same(T, U); };
+
+#if __cpp_variable_templates
+template<class T, class U>
+constexpr bool is_same_v = __is_same(T, U);
+#endif
+
+template<class T> struct A;
+
+template<int N>
+void f() {
+  using T = float;
+  using B [[gnu::vector_size(N * sizeof(float))]] = T;
+  static_assert(!is_same<T, B>::value, "");
+  static_assert(!is_same<A<T>, A<B>>::value, "");
+#if __cpp_variable_templates
+  static_assert(!is_same_v<T, B>, "");
+  static_assert(!is_same_v<A<T>, A<B>>, "");
+#endif
+};
+
+template<int N>
+void g() {
+  using T = float*;
+  using C [[gnu::vector_size(N * sizeof(float*))]] = T;
+  static_assert(!is_same<T*, C>::value, "");
+  static_assert(!is_same<A<T*>, A<C>>::value, "");
+#if __cpp_variable_templates
+  static_assert(!is_same_v<T*, C>, "");
+  static_assert(!is_same_v<A<T*>, A<C>>, "");
+#endif
+};
+
+template void f<4>();
+template void g<4>();
-- 
2.46.0.rc2

Reply via email to