https://gcc.gnu.org/g:dfb63765e994bee74d533c810fdf75d3269530ad

commit r15-3530-gdfb63765e994bee74d533c810fdf75d3269530ad
Author: Patrick Palka <ppa...@redhat.com>
Date:   Sat Sep 7 14:06:37 2024 -0400

    c++: deferring partial substitution into lambda [PR116567]
    
    Here we correctly defer partial substitution into the lambda used as
    a default template argument, but then incorrectly perform the full
    substitution, because add_extra_args adds outer template arguments from
    the full substitution that are not related to the original template
    context of the lambda.  For example, the template depth of the first
    lambda is 1 but add_extra_args return a set of args with 3 levels, with
    the inner level corresponding to the parameters of v1 (good) and the
    outer levels corresponding to those of A and B (bad).
    
    For the cases that we're interested in, add_extra_args can assume that
    the deferred args are a full set of template arguments, and so it
    suffices to just substitute into the deferred args and not do any
    additional merging.
    
    This patch refines add_extra_args accordingly, and additionally
    makes it look for the tf_partial flag instead of for dependent args to
    decide if the deferred substitution is a partial one.  This reveals we
    were neglecting to set tf_partial when substituting into a default
    template argument in a template context.
    
            PR c++/116567
    
    gcc/cp/ChangeLog:
    
            * pt.cc (coerce_template_parms): Set tf_partial when substituting
            into a default template argument in a template context.
            (build_extra_args): Set TREE_STATIC on the deferred args if this
            is a partial substitution.
            (add_extra_args): Check TREE_STATIC instead of dependence of args.
            Adjust merging behavior in that case.
            (tsubst_lammda_expr): Check for tf_partial instead of dependence
            of args when determining whether to defer substitution.
            (tsubst_expr) <case LAMBDA_EXPR>: Remove tf_partial early exit.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/cpp2a/lambda-targ7.C: New test.
    
    Reviewed-by: Jason Merrill <ja...@redhat.com>

Diff:
---
 gcc/cp/pt.cc                              | 35 +++++++++++++-------------
 gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C | 42 +++++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+), 17 deletions(-)

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 72b17fdd8a76..310e5dfff033 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -9326,7 +9326,9 @@ coerce_template_parms (tree parms,
        {
          /* There must be a default arg in this case.  */
          arg = tsubst_template_arg (TREE_PURPOSE (parm), new_args,
-                                    complain, in_decl);
+                                    complain | (processing_template_decl
+                                                ? tf_partial : tf_none),
+                                    in_decl);
          /* The position of the first default template argument,
             is also the number of non-defaulted arguments in NEW_INNER_ARGS.
             Record that.  */
@@ -13569,6 +13571,9 @@ build_extra_args (tree pattern, tree args, 
tsubst_flags_t complain)
   /* Make a copy of the extra arguments so that they won't get changed
      out from under us.  */
   tree extra = preserve_args (copy_template_args (args), /*cow_p=*/false);
+  if (complain & tf_partial)
+    /* Remember whether this is a partial substitution.  */
+    TREE_STATIC (extra) = true;
   if (local_specializations)
     if (tree locals = extract_local_specs (pattern, complain))
       extra = tree_cons (NULL_TREE, extra, locals);
@@ -13581,7 +13586,10 @@ build_extra_args (tree pattern, tree args, 
tsubst_flags_t complain)
 tree
 add_extra_args (tree extra, tree args, tsubst_flags_t complain, tree in_decl)
 {
-  if (extra && TREE_CODE (extra) == TREE_LIST)
+  if (!extra)
+    return args;
+
+  if (TREE_CODE (extra) == TREE_LIST)
     {
       for (tree elt = TREE_CHAIN (extra); elt; elt = TREE_CHAIN (elt))
        {
@@ -13599,13 +13607,13 @@ add_extra_args (tree extra, tree args, tsubst_flags_t 
complain, tree in_decl)
       gcc_assert (!TREE_PURPOSE (extra));
       extra = TREE_VALUE (extra);
     }
-  if (uses_template_parms (extra))
-    {
-      /* This can happen after dependent substitution into a
-        requires-expr or a lambda that uses constexpr if.  */
-      extra = tsubst_template_args (extra, args, complain, in_decl);
-      args = add_outermost_template_args (args, extra);
-    }
+  gcc_checking_assert (TREE_STATIC (extra) == uses_template_parms (extra));
+  if (TREE_STATIC (extra))
+    /* This is a partial substitution into e.g. a requires-expr or lambda-expr
+       inside a default template argument; we expect 'extra' to be a full set
+       of template arguments for the template context, so it suffices to just
+       substitute into them.  */
+    args = tsubst_template_args (extra, args, complain, in_decl);
   else
     args = add_to_template_args (extra, args);
   return args;
@@ -19704,7 +19712,7 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
 
   args = add_extra_args (LAMBDA_EXPR_EXTRA_ARGS (t), args, complain, in_decl);
   if (processing_template_decl
-      && (!in_template_context || any_dependent_template_arguments_p (args)))
+      && (!in_template_context || (complain & tf_partial)))
     {
       /* Defer templated substitution into a lambda-expr if we lost the
         necessary template context.  This may happen for a lambda-expr
@@ -21825,13 +21833,6 @@ tsubst_expr (tree t, tree args, tsubst_flags_t 
complain, tree in_decl)
 
     case LAMBDA_EXPR:
       {
-       if (complain & tf_partial)
-         {
-           /* We don't have a full set of template arguments yet; don't touch
-              the lambda at all.  */
-           gcc_assert (processing_template_decl);
-           return t;
-         }
        tree r = tsubst_lambda_expr (t, args, complain, in_decl);
 
        RETURN (build_lambda_object (r));
diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C 
b/gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C
new file mode 100644
index 000000000000..97b42d6ed0c4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/lambda-targ7.C
@@ -0,0 +1,42 @@
+// PR c++/116567
+// { dg-do compile { target c++20 } }
+
+struct X { int n; };
+
+template<auto N, auto F = []{ return N; }>
+auto v1 = F;
+
+template<auto N, auto F = [](auto y){ return decltype(N){N.n + y}; }>
+auto v1g = F;
+
+template<class T>
+struct A {
+  template<auto N, auto F = []{ return N; }>
+  static inline auto v2 = F;
+
+  template<auto N, auto F = [](auto y){ return decltype(N){N.n + y}; }>
+  static inline auto v2g = F;
+
+  template<class U>
+  struct B {
+    template<auto N, auto F = []{ return N; }>
+    static inline auto v3 = F;
+
+    template<auto N, auto F = [](auto y){ return decltype(N){N.n + y}; }>
+    static inline auto v3g = F;
+
+    template<class V>
+    static void f() {
+      static_assert(v1<X{1}>().n == 1);
+      static_assert(v1g<X{1}>(42).n == 1 + 42);
+      static_assert(v2<X{2}>().n == 2);
+      static_assert(v2g<X{2}>(42).n == 2 + 42);
+      static_assert(v3<X{3}>().n == 3);
+      static_assert(v3g<X{3}>(42).n == 3 + 42);
+    }
+  };
+};
+
+int main() {
+  A<int>::B<int>::f<int>();
+}

Reply via email to