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