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. 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): -- >8 -- Subject: [PATCH 2/1] c++: non-template alias with dependent attributes [PR115897] PR c++/115897 gcc/cp/ChangeLog: * cp-tree.h (opaque_alias_p): Declare. * decl.cc (start_decl): Use structural equality for an opaque alias. * pt.cc (opaque_alias_p): Define. (alias_template_specialization_p): Don't look through an opaque alias. (complex_alias_template_p): Use 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 opaque_alias_p instead of any_dependent_template_arguments_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. --- gcc/cp/cp-tree.h | 1 + gcc/cp/decl.cc | 3 ++ gcc/cp/pt.cc | 23 ++++++++---- gcc/cp/tree.cc | 7 ++-- gcc/cp/typeck.cc | 42 ++++++++++++++-------- gcc/testsuite/g++.dg/cpp0x/alias-decl-79.C | 16 ++++----- 6 files changed, 61 insertions(+), 31 deletions(-) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index c6f102564ce..f2a64b63133 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7622,6 +7622,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 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/decl.cc b/gcc/cp/decl.cc index 66e8a973cce..755701fd8aa 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -6144,6 +6144,9 @@ start_decl (const cp_declarator *declarator, if (decl == error_mark_node) return error_mark_node; + if (opaque_alias_p (TREE_TYPE (decl))) + SET_TYPE_STRUCTURAL_EQUALITY (TREE_TYPE (decl)); + if (VAR_P (decl) && DECL_NAMESPACE_SCOPE_P (decl) && !TREE_PUBLIC (decl) && !was_public && !DECL_THIS_STATIC (decl) && !DECL_ARTIFICIAL (decl) 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. */ + +bool +opaque_alias_p (const_tree t) +{ + return (TYPE_P (t) + && typedef_variant_p (t) + && uses_template_parms (DECL_ORIGINAL_TYPE (TYPE_NAME (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. */ @@ -6525,7 +6538,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 && !opaque_alias_p (t)) return alias_template_specialization_p (DECL_ORIGINAL_TYPE (TYPE_NAME (t)), transparent_typedefs); @@ -6630,8 +6643,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 (opaque_alias_p (TREE_TYPE (tmpl))) return true; if (!complex_alias_tmpl_info) @@ -6718,7 +6730,7 @@ dependent_alias_template_spec_p (const_tree t, bool transparent_typedefs) } } - if (transparent_typedefs) + if (transparent_typedefs && !opaque_alias_p (t)) { tree utype = DECL_ORIGINAL_TYPE (TYPE_NAME (t)); return dependent_alias_template_spec_p (utype, transparent_typedefs); @@ -6787,8 +6799,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 (opaque_alias_p (TREE_TYPE (tmpl))) break; /* Alias is equivalent. Strip it and repeat. */ diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 0e6be8de59a..3a2283b4d65 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 (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 15e5577f670..e966bcd9d03 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -1658,20 +1658,34 @@ structural_comptypes (tree t1, tree t2, int strict) return false; check_alias: - if (comparing_dependent_aliases) - { - /* 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. */ - ++processing_template_decl; - tree dep1 = dependent_alias_template_spec_p (t1, nt_transparent); - tree dep2 = dependent_alias_template_spec_p (t2, nt_transparent); - --processing_template_decl; - if ((dep1 || dep2) && dep1 != dep2) - return false; + if (typedef_variant_p (t1) || typedef_variant_p (t2)) + { + tree o1 = opaque_alias_p (t1) ? t1 : NULL_TREE; + tree o2 = opaque_alias_p (t2) ? t2 : NULL_TREE; + if (o1 || o2) + { + if (!o1 || !o2) + return false; + if (!comp_type_attributes (DECL_ATTRIBUTES (TYPE_NAME (o1)), + DECL_ATTRIBUTES (TYPE_NAME (o2)))) + return false; + } + + if (comparing_dependent_aliases) + { + /* 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. */ + ++processing_template_decl; + tree dep1 = dependent_alias_template_spec_p (t1, nt_transparent); + tree dep2 = dependent_alias_template_spec_p (t2, nt_transparent); + --processing_template_decl; + if ((dep1 || dep2) && dep1 != dep2) + return false; + } } return true; 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 }; -- 2.46.0.rc0.106.g1c4a234a1c