diff mbox series

[WIP] c++: Fix ABI for lambdas declared in alias templates [PR116568]

Message ID 67318587.170a0220.3d2cbe.bdb7@mx.google.com
State New
Headers show
Series [WIP] c++: Fix ABI for lambdas declared in alias templates [PR116568] | expand

Commit Message

Nathaniel Shead Nov. 11, 2024, 4:18 a.m. UTC
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 <nathanieloshead@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 mbox series

Patch

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 *-*-* } } }