FWIW, here's my WIP patch to fix the lambda in type alias case "properly". I've gotten stuck with trying to work out how to set LAMBDA_EXPR_EXTRA_CONTEXT on the uninstantiated declaration; any thoughts or suggestions here?
I also found that the hunk - /* Substituting the type might have recursively instantiated this - same alias (c++/86171). */ - if (use_spec_table && gen_tmpl && DECL_ALIAS_TEMPLATE_P (gen_tmpl) - && (spec = retrieve_specialization (gen_tmpl, argvec, hash))) - { - r = spec; - break; - } is no longer needed for the original testcase and doesn't appear to be used anymore, but I imagine we might need something similar in a different way to handle this testcase (which errors since GCC14 but succeeds before then): template <class> struct A; template <class T> using B = decltype([]() -> A<T>::X { return 0; }); template <class T> struct A { typedef int X; typedef B<T> U; }; B<short> b; I wasn't able to find an exact dup for this so I've created a new issue for it (PR117530). -- >8 -- This adds mangling support for lambdas with a mangling context of an alias template, and gives that context when instantiating such a lambda. This only currently works for class-scope alias templates, however, due to the if (LAMBDA_EXPR_EXTRA_SCOPE (t)) record_lambda_scope (r); condition in 'tsubst_lambda_scope'. For namespace-scope alias templates, we can't easily add the mangling context: we can't build the TYPE_DECL to record against until after we've parsed the type (and already recorded lambda scope), as `start_decl` relies on the type being passed in correctly, and setting the mangling scope after parsing is too late because e.g. 'template_class_depth' (called from grokfndecl when building the lambda functions while parsing the type) relies on the LAMBDA_EXPR_EXTRA_SCOPE already being properly set. This will also likely matter for 'determine_visibility'. I'm not sure what a good way to break this recursive dependency is. PR c++/116568 gcc/cp/ChangeLog: * mangle.cc (maybe_template_info): Support getting template info of alias templates. (canonicalize_for_substitution): Don't canonicalise aliases. (decl_mangling_context): Don't treat aliases as lambda closure types. (write_unqualified_name): Likewise. * pt.cc (tsubst_decl): Start lambda scope for alias templates. (instantiate_template): No longer need to special case alias templates here. gcc/testsuite/ChangeLog: * g++.dg/abi/lambda-ctx4.C: Adjust mangling, include namespace scope alias templates (XFAILed for now). Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com> --- gcc/cp/mangle.cc | 13 ++++++++----- gcc/cp/pt.cc | 23 ++++++++++------------- gcc/testsuite/g++.dg/abi/lambda-ctx4.C | 21 ++++++++++++++------- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 42fcdc34353..a6d1830397c 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -293,7 +293,7 @@ abi_check (int ver) static tree maybe_template_info (const tree decl) { - if (TREE_CODE (decl) == TYPE_DECL) + if (TREE_CODE (decl) == TYPE_DECL && !TYPE_DECL_ALIAS_P (decl)) { /* TYPE_DECLs are handled specially. Look at its type to decide if this is a template instantiation. */ @@ -306,7 +306,7 @@ maybe_template_info (const tree decl) { /* Check if the template is a primary template. */ if (DECL_LANG_SPECIFIC (decl) != NULL - && VAR_OR_FUNCTION_DECL_P (decl) + && (VAR_OR_FUNCTION_DECL_P (decl) || TREE_CODE (decl) == TYPE_DECL) && DECL_TEMPLATE_INFO (decl) && PRIMARY_TEMPLATE_P (DECL_TI_TEMPLATE (decl))) return DECL_TEMPLATE_INFO (decl); @@ -403,8 +403,8 @@ write_exception_spec (tree spec) static inline tree canonicalize_for_substitution (tree node) { - /* For a TYPE_DECL, use the type instead. */ - if (TREE_CODE (node) == TYPE_DECL) + /* For a non-alias TYPE_DECL, use the type instead. */ + if (TREE_CODE (node) == TYPE_DECL && !TYPE_DECL_ALIAS_P (node)) node = TREE_TYPE (node); if (TYPE_P (node) && TYPE_CANONICAL (node) != node @@ -1045,6 +1045,7 @@ decl_mangling_context (tree decl) decl = DECL_TEMPLATE_RESULT (decl); if (TREE_CODE (decl) == TYPE_DECL + && !TYPE_DECL_ALIAS_P (decl) && LAMBDA_TYPE_P (TREE_TYPE (decl))) { tree extra = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (decl)); @@ -1589,7 +1590,9 @@ write_unqualified_name (tree decl) if (TREE_CODE (decl) == TYPE_DECL && TYPE_UNNAMED_P (type)) write_unnamed_type_name (type); - else if (TREE_CODE (decl) == TYPE_DECL && LAMBDA_TYPE_P (type)) + else if (TREE_CODE (decl) == TYPE_DECL + && !TYPE_DECL_ALIAS_P (decl) + && LAMBDA_TYPE_P (type)) write_closure_type_name (type); else write_source_name (DECL_NAME (decl)); diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 06d13fda872..686f7246057 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -15627,6 +15627,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, } /* Create a new node for the specialization we need. */ + r = copy_decl (t); if (type == NULL_TREE) { if (is_typedef_decl (t)) @@ -15640,19 +15641,17 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, tsubst_flags_t tcomplain = complain; if (VAR_P (t)) tcomplain |= tf_tst_ok; + bool is_alias + = (is_typedef_decl (t) + && alias_template_specialization_p (TREE_TYPE (t), nt_opaque)); + if (is_alias) + start_lambda_scope (r); type = tsubst (type, args, tcomplain, in_decl); - /* Substituting the type might have recursively instantiated this - same alias (c++/86171). */ - if (use_spec_table && gen_tmpl && DECL_ALIAS_TEMPLATE_P (gen_tmpl) - && (spec = retrieve_specialization (gen_tmpl, argvec, hash))) - { - r = spec; - break; - } + if (is_alias) + finish_lambda_scope (); } if (type == error_mark_node && !(complain & tf_error)) RETURN (error_mark_node); - r = copy_decl (t); if (VAR_P (r)) { DECL_INITIALIZED_P (r) = 0; @@ -22281,8 +22280,7 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain) ctx = tsubst_entering_scope (DECL_CONTEXT (gen_tmpl), targ_ptr, complain, gen_tmpl); push_nested_class (ctx); - if (!DECL_ALIAS_TEMPLATE_P (gen_tmpl)) - start_lambda_scope (TYPE_NAME (ctx)); + start_lambda_scope (TYPE_NAME (ctx)); } tree pattern = DECL_TEMPLATE_RESULT (gen_tmpl); @@ -22313,8 +22311,7 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain) fndecl = tsubst_decl (pattern, targ_ptr, complain, /*use_spec_table=*/false); if (DECL_CLASS_SCOPE_P (gen_tmpl)) { - if (!DECL_ALIAS_TEMPLATE_P (gen_tmpl)) - finish_lambda_scope (); + finish_lambda_scope (); pop_nested_class (); } pop_from_top_level (); diff --git a/gcc/testsuite/g++.dg/abi/lambda-ctx4.C b/gcc/testsuite/g++.dg/abi/lambda-ctx4.C index d6544a84652..809cace8574 100644 --- a/gcc/testsuite/g++.dg/abi/lambda-ctx4.C +++ b/gcc/testsuite/g++.dg/abi/lambda-ctx4.C @@ -1,5 +1,4 @@ // { dg-do compile { target c++20 } } -// { dg-additional-options "-fkeep-inline-functions" } struct S { template <int I> @@ -9,14 +8,22 @@ struct S { S::T<0> a; S::T<1> b; +template <int I> +using L = decltype([]{ return I; }); + +L<0> c; +L<1> d; + int main() { a(); b(); + c(); + d(); } -// Currently we don't implement any special mangling rules for template aliases -// (though we probably should; an alias template is a definable item by -// [basic.def.odr] p1.5 and as such contained lambdas in different TUs should have -// the same type, see [basic.def.odr] p15.6) -// { scan_assembler {_ZNK1SUlvE_clEv:} } -// { scan_assembler {_ZNK1SUlvE0_clEv:} } +// { dg-final { scan-assembler {_ZNK1S1TILi0EEUlvE_clEv:} } } +// { dg-final { scan-assembler {_ZNK1S1TILi1EEUlvE_clEv:} } } + +// namespace-scope aliases don't have LAMBDA_EXPR_EXTRA_CONTEXT properly set +// { dg-final { scan-assembler {_ZNK1LILi0EEUlvE_clEv:} { xfail *-*-* } } } +// { dg-final { scan-assembler {_ZNK1LILi1EEUlvE_clEv:} { xfail *-*-* } } } -- 2.47.0