public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: Don't substitute into constraints on lambdas [PR99874]
@ 2021-04-07 16:10 Patrick Palka
  2021-04-08 15:25 ` Jason Merrill
  0 siblings, 1 reply; 4+ messages in thread
From: Patrick Palka @ 2021-04-07 16:10 UTC (permalink / raw)
  To: gcc-patches

We currently substitute through a lambda's constraints whenever we
regenerate it via tsubst_lambda_expr.  This is the wrong approach
because it can lead to hard errors due to constraints being evaluated
out of order (as in the testcase concepts-lambda17.C below), and because
it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS
mechanism for delaying substitution into requires-expressions, which is
the cause of this PR.

But in order to avoid substituting through a lambda's constraints during
regeneration, we we need to be able to get at all in-scope template
parameters and the corresponding template arguments during constraint
checking of a lambda's op().  And this information is not easily
available where we need it, it seems.

To that end, the approach that this patch takes is to add two new fields
to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM
(replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS.
The former allows us to obtain the complete set of template parameters
that are in-scope for a lambda's op(), and the latter gives us all outer
template arguments that were used to regenerate the lambda.

LAMBDA_EXPR_REGENERATING_TARGS is not strictly necessary -- in an
earlier version of the patch, I walked LAMBDA_EXPR_EXTRA_SCOPE to build
up this set of outer template arguments on demand, but it's cleaner to
do it this way.  (We'd need to walk LAMBDA_EXPR_EXTRA_SCOPE and not
DECL/TYPE_CONTEXT because the latter skips over variable template
scopes.)

This patch also renames the predicate instantiated_lambda_fn_p to
regenerated_lambda_fn_p, for sake of consistency with the rest of the
patch which uses "regenerated" instead of "instantiated".

Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on
cmcstl2 and range-v3.  Does this look OK for trunk?

gcc/cp/ChangeLog:

	PR c++/99874
	* constraint.cc (get_normalized_constraints_from_decl): Handle
	regenerated lambdas.
	(satisfy_declaration_constraints): Likewise.  Check for
	dependent args later.
	* cp-tree.h (LAMBDA_EXPR_INSTANTIATED): Replace with ...
	(LAMBDA_EXPR_REGENERATED_FROM): ... this.
	(LAMBDA_EXPR_REGENERATING_TARGS): New.
	(tree_lambda_expr::regenerated_from): New data member.
	(tree_lambda_expr::regenerating_targs): New data member.
	(add_to_template_args): Declare.
	(regenerated_lambda_fn_p): Likewise.
	(most_general_lambda): Likewise.
	* lambda.c (build_lambda_expr): Set LAMBDA_EXPR_REGENERATED_FROM
	and LAMBDA_EXPR_REGENERATING_TARGS.
	* pt.c (add_to_template_args): No longer static.
	(tsubst_function_decl): Unconditionally propagate constraints on
	the substituted function decl.
	(instantiated_lambda_fn_p): Rename to ...
	(regenerated_lambda_fn_p): ... this.  Check
	LAMBDA_EXPR_REGENERATED_FROM instead of
	LAMBDA_EXPR_INSTANTIATED.
	(most_general_lambda): Define.
	(enclosing_instantiation_of): Adjust after renaming
	instantiated_lambda_fn_p.
	(tsubst_lambda_expr): Don't substitute or set constraints on
	the regenerated lambda.

gcc/testsuite/ChangeLog:

	PR c++/99874
	* g++.dg/cpp2a/concepts-lambda16.C: New test.
	* g++.dg/cpp2a/concepts-lambda17.C: New test.
---
 gcc/cp/constraint.cc                          | 43 +++++++++++--
 gcc/cp/cp-tree.h                              | 20 ++++--
 gcc/cp/lambda.c                               |  2 +
 gcc/cp/pt.c                                   | 42 ++++++-------
 .../g++.dg/cpp2a/concepts-lambda16.C          | 61 +++++++++++++++++++
 .../g++.dg/cpp2a/concepts-lambda17.C          | 14 +++++
 6 files changed, 150 insertions(+), 32 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 5cf43bd6c18..bd526f669ab 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -886,6 +886,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
      it has the correct template information attached. */
   d = strip_inheriting_ctors (d);
 
+  if (regenerated_lambda_fn_p (d))
+    {
+      /* If this lambda was regenerated, DECL_TEMPLATE_PARMS doesn't contain
+	 all in-scope template parameters, but the lambda from which it was
+	 ultimately regenerated does, so use that instead.  */
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d));
+      lambda = most_general_lambda (lambda);
+      d = lambda_function (lambda);
+    }
+
   if (TREE_CODE (d) == TEMPLATE_DECL)
     {
       tmpl = d;
@@ -3174,13 +3184,27 @@ satisfy_declaration_constraints (tree t, sat_info info)
       args = TI_ARGS (ti);
       if (inh_ctor_targs)
 	args = add_outermost_template_args (args, inh_ctor_targs);
+    }
 
-      /* If any arguments depend on template parameters, we can't
-	 check constraints. Pretend they're satisfied for now.  */
-      if (uses_template_parms (args))
-	return boolean_true_node;
+  if (regenerated_lambda_fn_p (t))
+    {
+      /* The DECL_TI_ARGS of a regenerated lambda contains only the innermost
+	 set of template arguments.  Augment this with the outer template
+	 arguments that were used to regenerate the lambda.  */
+      gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
+      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
+      if (args)
+	args = add_to_template_args (outer_args, args);
+      else
+	args = outer_args;
     }
 
+  /* If any arguments depend on template parameters, we can't
+     check constraints. Pretend they're satisfied for now.  */
+  if (uses_template_parms (args))
+    return boolean_true_node;
+
   /* Get the normalized constraints.  */
   tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
 
@@ -3227,7 +3251,16 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
 
   gcc_assert (TREE_CODE (t) == TEMPLATE_DECL);
 
-  args = add_outermost_template_args (t, args);
+  if (regenerated_lambda_fn_p (t))
+    {
+      /* As in the two-parameter version of this function.  */
+      gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
+      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
+      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
+      args = add_to_template_args (outer_args, args);
+    }
+  else
+    args = add_outermost_template_args (t, args);
 
   /* If any arguments depend on template parameters, we can't
      check constraints. Pretend they're satisfied for now.  */
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a5d9d7ac625..bf9d5add0cf 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -490,7 +490,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE)
       CONSTRUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR)
       OVL_NESTED_P (in OVERLOAD)
-      LAMBDA_EXPR_INSTANTIATED (in LAMBDA_EXPR)
       DECL_MODULE_EXPORT_P (in _DECL)
    4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
       TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
@@ -1434,10 +1433,6 @@ enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
   TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
 
-/* True iff this LAMBDA_EXPR was generated in tsubst_lambda_expr.  */
-#define LAMBDA_EXPR_INSTANTIATED(NODE) \
-  TREE_LANG_FLAG_3 (LAMBDA_EXPR_CHECK (NODE))
-
 /* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit
    capture.  */
 #define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \
@@ -1461,6 +1456,16 @@ enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \
   (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies)
 
+/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE
+   (if NODE was not regenerated via tsubst_lambda_expr).  */
+#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \
+  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from)
+
+/* The full set of template arguments used to regenerate NODE, or NULL_TREE
+   (if NODE was not regenerated via tsubst_lambda_expr).  */
+#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \
+  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs)
+
 /* The closure type of the lambda, which is also the type of the
    LAMBDA_EXPR.  */
 #define LAMBDA_EXPR_CLOSURE(NODE) \
@@ -1472,6 +1477,8 @@ struct GTY (()) tree_lambda_expr
   tree capture_list;
   tree this_capture;
   tree extra_scope;
+  tree regenerated_from;
+  tree regenerating_targs;
   vec<tree, va_gc> *pending_proxies;
   location_t locus;
   enum cp_lambda_default_capture_mode_type default_capture_mode : 8;
@@ -7247,6 +7254,7 @@ extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec);
 extern void add_mergeable_specialization        (bool is_decl, bool is_alias,
 						 spec_entry *,
 						 tree outer, unsigned);
+extern tree add_to_template_args		(tree, tree);
 extern tree add_outermost_template_args		(tree, tree);
 extern tree add_extra_args			(tree, tree);
 extern tree build_extra_args			(tree, tree, tsubst_flags_t);
@@ -7557,6 +7565,8 @@ extern void record_null_lambda_scope		(tree);
 extern void finish_lambda_scope			(void);
 extern tree start_lambda_function		(tree fn, tree lambda_expr);
 extern void finish_lambda_function		(tree body);
+extern bool regenerated_lambda_fn_p		(tree);
+extern tree most_general_lambda			(tree);
 
 /* in tree.c */
 extern int cp_tree_operand_length		(const_tree);
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index b0fd6ecc57e..c0a5ffb427e 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -41,6 +41,8 @@ build_lambda_expr (void)
   LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE;
   LAMBDA_EXPR_CAPTURE_LIST         (lambda) = NULL_TREE;
   LAMBDA_EXPR_THIS_CAPTURE         (lambda) = NULL_TREE;
+  LAMBDA_EXPR_REGENERATED_FROM     (lambda) = NULL_TREE;
+  LAMBDA_EXPR_REGENERATING_TARGS   (lambda) = NULL_TREE;
   LAMBDA_EXPR_PENDING_PROXIES      (lambda) = NULL;
   LAMBDA_EXPR_MUTABLE_P            (lambda) = false;
   return lambda;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index a08d08d2834..7917a280804 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -151,7 +151,6 @@ static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
 static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t,
 					      bool, bool);
 static void tsubst_enum	(tree, tree, tree);
-static tree add_to_template_args (tree, tree);
 static bool check_instantiated_args (tree, tree, tsubst_flags_t);
 static int check_non_deducible_conversion (tree, tree, int, int,
 					   struct conversion **, bool);
@@ -553,7 +552,7 @@ maybe_end_member_template_processing (void)
 /* Return a new template argument vector which contains all of ARGS,
    but has as its innermost set of arguments the EXTRA_ARGS.  */
 
-static tree
+tree
 add_to_template_args (tree args, tree extra_args)
 {
   tree new_args;
@@ -14058,10 +14057,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
      don't substitute through the constraints; that's only done when
      they are checked.  */
   if (tree ci = get_constraints (t))
-    /* Unless we're regenerating a lambda, in which case we'll set the
-       lambda's constraints in tsubst_lambda_expr.  */
-    if (!lambda_fntype)
-      set_constraints (r, ci);
+    set_constraints (r, ci);
 
   if (DECL_FRIEND_CONTEXT (t))
     SET_DECL_FRIEND_CONTEXT (r,
@@ -14347,13 +14343,24 @@ lambda_fn_in_template_p (tree fn)
    which the above is true.  */
 
 bool
-instantiated_lambda_fn_p (tree fn)
+regenerated_lambda_fn_p (tree fn)
 {
   if (!fn || !LAMBDA_FUNCTION_P (fn))
     return false;
   tree closure = DECL_CONTEXT (fn);
   tree lam = CLASSTYPE_LAMBDA_EXPR (closure);
-  return LAMBDA_EXPR_INSTANTIATED (lam);
+  return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE;
+}
+
+/* Return the LAMBDA_EXPR from which T was ultimately regenerated.
+   If T is not a regenerated LAMBDA_EXPR, return T.  */
+
+tree
+most_general_lambda (tree t)
+{
+  while (LAMBDA_EXPR_REGENERATED_FROM (t))
+    t = LAMBDA_EXPR_REGENERATED_FROM (t);
+  return t;
 }
 
 /* We're instantiating a variable from template function TCTX.  Return the
@@ -14369,7 +14376,7 @@ enclosing_instantiation_of (tree otctx)
   int lambda_count = 0;
 
   for (; tctx && (lambda_fn_in_template_p (tctx)
-		  || instantiated_lambda_fn_p (tctx));
+		  || regenerated_lambda_fn_p (tctx));
        tctx = decl_function_context (tctx))
     ++lambda_count;
 
@@ -14389,7 +14396,7 @@ enclosing_instantiation_of (tree otctx)
     {
       tree ofn = fn;
       int flambda_count = 0;
-      for (; fn && instantiated_lambda_fn_p (fn);
+      for (; fn && regenerated_lambda_fn_p (fn);
 	   fn = decl_function_context (fn))
 	++flambda_count;
       if ((fn && DECL_TEMPLATE_INFO (fn))
@@ -19264,7 +19271,9 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r)
     = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t);
   LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t);
-  LAMBDA_EXPR_INSTANTIATED (r) = true;
+  LAMBDA_EXPR_REGENERATED_FROM (r) = t;
+  LAMBDA_EXPR_REGENERATING_TARGS (r)
+    = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args);
 
   gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
 	      && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
@@ -19406,17 +19415,6 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	  finish_member_declaration (fn);
 	}
 
-      if (tree ci = get_constraints (oldfn))
-	{
-	  /* Substitute into the lambda's constraints.  */
-	  if (oldtmpl)
-	    ++processing_template_decl;
-	  ci = tsubst_constraint_info (ci, args, complain, in_decl);
-	  if (oldtmpl)
-	    --processing_template_decl;
-	  set_constraints (fn, ci);
-	}
-
       /* Let finish_function set this.  */
       DECL_DECLARED_CONSTEXPR_P (fn) = false;
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
new file mode 100644
index 00000000000..01fda6efb38
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
@@ -0,0 +1,61 @@
+// PR c++/99874
+// { dg-do compile { target c++20 } }
+
+template <class T>
+struct A {
+  static inline auto a = [] <class U> (U) {
+    return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { };
+  };
+
+  template <class W>
+  static inline auto b = [] <class U> (U) {
+    return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { };
+  };
+
+  static auto f() {
+    return [] <class U> (U) {
+      return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { };
+    };
+  }
+
+  template <class W>
+  static auto g() {
+    return [] <class U> (U) {
+      return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { };
+    };
+  }
+};
+
+template <class T>
+auto a = [] <class U> (U) {
+  return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { };
+};
+
+template <class T>
+auto b = [] <class U> (U) {
+  return [] <class V> (V) {
+    return [] {
+      return [] () requires requires (T t, U u, V v) { t + u + v; } { };
+    };
+  };
+};
+
+int main() {
+  A<int>::a(0)(0);
+  A<int>::a(0)(nullptr); // { dg-error "no match" }
+
+  A<int>::b<int>(0)(0);
+  A<int>::b<int>(0)(nullptr); // { dg-error "no match" }
+
+  A<int>::f()(0)(0);
+  A<int>::f()(0)(nullptr); // { dg-error "no match" }
+
+  A<int>::g<int>()(0)(0);
+  A<int>::g<int>()(0)(nullptr); // { dg-error "no match" }
+
+  a<int>(0)(0);
+  a<int>(0)(nullptr); // { dg-error "no match" }
+
+  b<int>(0)(0)();
+  b<int>(0)(nullptr)()(); // { dg-error "no match" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
new file mode 100644
index 00000000000..32ae1e12174
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
@@ -0,0 +1,14 @@
+// { dg-do compile { target c++20 } }
+
+template<class T>
+struct A { static const bool value = T::value; };
+
+template<class T>
+void f() {
+  // Verify we don't substitute into a lambda's constraints when
+  // regenerating it, which would lead to a hard error here.
+  [] () requires (T::value && A<T>::value) || true { }();
+  [] <class U> (U) requires (U::value && A<T>::value) || true { }(0);
+}
+
+template void f<int>();
-- 
2.31.1.189.g2e36527f23


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] c++: Don't substitute into constraints on lambdas [PR99874]
  2021-04-07 16:10 [PATCH] c++: Don't substitute into constraints on lambdas [PR99874] Patrick Palka
@ 2021-04-08 15:25 ` Jason Merrill
  2021-04-08 21:01   ` Patrick Palka
  0 siblings, 1 reply; 4+ messages in thread
From: Jason Merrill @ 2021-04-08 15:25 UTC (permalink / raw)
  To: Patrick Palka, gcc-patches

On 4/7/21 12:10 PM, Patrick Palka wrote:
> We currently substitute through a lambda's constraints whenever we
> regenerate it via tsubst_lambda_expr.  This is the wrong approach
> because it can lead to hard errors due to constraints being evaluated
> out of order (as in the testcase concepts-lambda17.C below), and because
> it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS
> mechanism for delaying substitution into requires-expressions, which is
> the cause of this PR.
> 
> But in order to avoid substituting through a lambda's constraints during
> regeneration, we we need to be able to get at all in-scope template
> parameters and the corresponding template arguments during constraint
> checking of a lambda's op().  And this information is not easily
> available where we need it, it seems.
> 
> To that end, the approach that this patch takes is to add two new fields
> to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM
> (replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS.
> The former allows us to obtain the complete set of template parameters
> that are in-scope for a lambda's op(), and the latter gives us all outer
> template arguments that were used to regenerate the lambda.

I'm a little surprised you didn't use a TEMPLATE_INFO for these, but 
this way is fine too.

> LAMBDA_EXPR_REGENERATING_TARGS is not strictly necessary -- in an
> earlier version of the patch, I walked LAMBDA_EXPR_EXTRA_SCOPE to build
> up this set of outer template arguments on demand, but it's cleaner to
> do it this way.  (We'd need to walk LAMBDA_EXPR_EXTRA_SCOPE and not
> DECL/TYPE_CONTEXT because the latter skips over variable template
> scopes.)
> 
> This patch also renames the predicate instantiated_lambda_fn_p to
> regenerated_lambda_fn_p, for sake of consistency with the rest of the
> patch which uses "regenerated" instead of "instantiated".
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, and also tested on
> cmcstl2 and range-v3.  Does this look OK for trunk?

OK.

> gcc/cp/ChangeLog:
> 
> 	PR c++/99874
> 	* constraint.cc (get_normalized_constraints_from_decl): Handle
> 	regenerated lambdas.
> 	(satisfy_declaration_constraints): Likewise.  Check for
> 	dependent args later.
> 	* cp-tree.h (LAMBDA_EXPR_INSTANTIATED): Replace with ...
> 	(LAMBDA_EXPR_REGENERATED_FROM): ... this.
> 	(LAMBDA_EXPR_REGENERATING_TARGS): New.
> 	(tree_lambda_expr::regenerated_from): New data member.
> 	(tree_lambda_expr::regenerating_targs): New data member.
> 	(add_to_template_args): Declare.
> 	(regenerated_lambda_fn_p): Likewise.
> 	(most_general_lambda): Likewise.
> 	* lambda.c (build_lambda_expr): Set LAMBDA_EXPR_REGENERATED_FROM
> 	and LAMBDA_EXPR_REGENERATING_TARGS.
> 	* pt.c (add_to_template_args): No longer static.
> 	(tsubst_function_decl): Unconditionally propagate constraints on
> 	the substituted function decl.
> 	(instantiated_lambda_fn_p): Rename to ...
> 	(regenerated_lambda_fn_p): ... this.  Check
> 	LAMBDA_EXPR_REGENERATED_FROM instead of
> 	LAMBDA_EXPR_INSTANTIATED.
> 	(most_general_lambda): Define.
> 	(enclosing_instantiation_of): Adjust after renaming
> 	instantiated_lambda_fn_p.
> 	(tsubst_lambda_expr): Don't substitute or set constraints on
> 	the regenerated lambda.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR c++/99874
> 	* g++.dg/cpp2a/concepts-lambda16.C: New test.
> 	* g++.dg/cpp2a/concepts-lambda17.C: New test.
> ---
>   gcc/cp/constraint.cc                          | 43 +++++++++++--
>   gcc/cp/cp-tree.h                              | 20 ++++--
>   gcc/cp/lambda.c                               |  2 +
>   gcc/cp/pt.c                                   | 42 ++++++-------
>   .../g++.dg/cpp2a/concepts-lambda16.C          | 61 +++++++++++++++++++
>   .../g++.dg/cpp2a/concepts-lambda17.C          | 14 +++++
>   6 files changed, 150 insertions(+), 32 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 5cf43bd6c18..bd526f669ab 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -886,6 +886,16 @@ get_normalized_constraints_from_decl (tree d, bool diag = false)
>        it has the correct template information attached. */
>     d = strip_inheriting_ctors (d);
>   
> +  if (regenerated_lambda_fn_p (d))
> +    {
> +      /* If this lambda was regenerated, DECL_TEMPLATE_PARMS doesn't contain
> +	 all in-scope template parameters, but the lambda from which it was
> +	 ultimately regenerated does, so use that instead.  */
> +      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (d));
> +      lambda = most_general_lambda (lambda);
> +      d = lambda_function (lambda);
> +    }
> +
>     if (TREE_CODE (d) == TEMPLATE_DECL)
>       {
>         tmpl = d;
> @@ -3174,13 +3184,27 @@ satisfy_declaration_constraints (tree t, sat_info info)
>         args = TI_ARGS (ti);
>         if (inh_ctor_targs)
>   	args = add_outermost_template_args (args, inh_ctor_targs);
> +    }
>   
> -      /* If any arguments depend on template parameters, we can't
> -	 check constraints. Pretend they're satisfied for now.  */
> -      if (uses_template_parms (args))
> -	return boolean_true_node;
> +  if (regenerated_lambda_fn_p (t))
> +    {
> +      /* The DECL_TI_ARGS of a regenerated lambda contains only the innermost
> +	 set of template arguments.  Augment this with the outer template
> +	 arguments that were used to regenerate the lambda.  */
> +      gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
> +      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
> +      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
> +      if (args)
> +	args = add_to_template_args (outer_args, args);
> +      else
> +	args = outer_args;
>       }
>   
> +  /* If any arguments depend on template parameters, we can't
> +     check constraints. Pretend they're satisfied for now.  */
> +  if (uses_template_parms (args))
> +    return boolean_true_node;
> +
>     /* Get the normalized constraints.  */
>     tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
>   
> @@ -3227,7 +3251,16 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
>   
>     gcc_assert (TREE_CODE (t) == TEMPLATE_DECL);
>   
> -  args = add_outermost_template_args (t, args);
> +  if (regenerated_lambda_fn_p (t))
> +    {
> +      /* As in the two-parameter version of this function.  */
> +      gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
> +      tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
> +      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
> +      args = add_to_template_args (outer_args, args);
> +    }
> +  else
> +    args = add_outermost_template_args (t, args);
>   
>     /* If any arguments depend on template parameters, we can't
>        check constraints. Pretend they're satisfied for now.  */
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index a5d9d7ac625..bf9d5add0cf 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -490,7 +490,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
>         DECLTYPE_FOR_REF_CAPTURE (in DECLTYPE_TYPE)
>         CONSTRUCTOR_C99_COMPOUND_LITERAL (in CONSTRUCTOR)
>         OVL_NESTED_P (in OVERLOAD)
> -      LAMBDA_EXPR_INSTANTIATED (in LAMBDA_EXPR)
>         DECL_MODULE_EXPORT_P (in _DECL)
>      4: IDENTIFIER_MARKED (IDENTIFIER_NODEs)
>         TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR,
> @@ -1434,10 +1433,6 @@ enum cp_lambda_default_capture_mode_type {
>   #define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
>     TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
>   
> -/* True iff this LAMBDA_EXPR was generated in tsubst_lambda_expr.  */
> -#define LAMBDA_EXPR_INSTANTIATED(NODE) \
> -  TREE_LANG_FLAG_3 (LAMBDA_EXPR_CHECK (NODE))
> -
>   /* True if this TREE_LIST in LAMBDA_EXPR_CAPTURE_LIST is for an explicit
>      capture.  */
>   #define LAMBDA_CAPTURE_EXPLICIT_P(NODE) \
> @@ -1461,6 +1456,16 @@ enum cp_lambda_default_capture_mode_type {
>   #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \
>     (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies)
>   
> +/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE
> +   (if NODE was not regenerated via tsubst_lambda_expr).  */
> +#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \
> +  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from)
> +
> +/* The full set of template arguments used to regenerate NODE, or NULL_TREE
> +   (if NODE was not regenerated via tsubst_lambda_expr).  */
> +#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \
> +  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs)
> +
>   /* The closure type of the lambda, which is also the type of the
>      LAMBDA_EXPR.  */
>   #define LAMBDA_EXPR_CLOSURE(NODE) \
> @@ -1472,6 +1477,8 @@ struct GTY (()) tree_lambda_expr
>     tree capture_list;
>     tree this_capture;
>     tree extra_scope;
> +  tree regenerated_from;
> +  tree regenerating_targs;
>     vec<tree, va_gc> *pending_proxies;
>     location_t locus;
>     enum cp_lambda_default_capture_mode_type default_capture_mode : 8;
> @@ -7247,6 +7254,7 @@ extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec);
>   extern void add_mergeable_specialization        (bool is_decl, bool is_alias,
>   						 spec_entry *,
>   						 tree outer, unsigned);
> +extern tree add_to_template_args		(tree, tree);
>   extern tree add_outermost_template_args		(tree, tree);
>   extern tree add_extra_args			(tree, tree);
>   extern tree build_extra_args			(tree, tree, tsubst_flags_t);
> @@ -7557,6 +7565,8 @@ extern void record_null_lambda_scope		(tree);
>   extern void finish_lambda_scope			(void);
>   extern tree start_lambda_function		(tree fn, tree lambda_expr);
>   extern void finish_lambda_function		(tree body);
> +extern bool regenerated_lambda_fn_p		(tree);
> +extern tree most_general_lambda			(tree);
>   
>   /* in tree.c */
>   extern int cp_tree_operand_length		(const_tree);
> diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
> index b0fd6ecc57e..c0a5ffb427e 100644
> --- a/gcc/cp/lambda.c
> +++ b/gcc/cp/lambda.c
> @@ -41,6 +41,8 @@ build_lambda_expr (void)
>     LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE;
>     LAMBDA_EXPR_CAPTURE_LIST         (lambda) = NULL_TREE;
>     LAMBDA_EXPR_THIS_CAPTURE         (lambda) = NULL_TREE;
> +  LAMBDA_EXPR_REGENERATED_FROM     (lambda) = NULL_TREE;
> +  LAMBDA_EXPR_REGENERATING_TARGS   (lambda) = NULL_TREE;
>     LAMBDA_EXPR_PENDING_PROXIES      (lambda) = NULL;
>     LAMBDA_EXPR_MUTABLE_P            (lambda) = false;
>     return lambda;
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index a08d08d2834..7917a280804 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -151,7 +151,6 @@ static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
>   static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t,
>   					      bool, bool);
>   static void tsubst_enum	(tree, tree, tree);
> -static tree add_to_template_args (tree, tree);
>   static bool check_instantiated_args (tree, tree, tsubst_flags_t);
>   static int check_non_deducible_conversion (tree, tree, int, int,
>   					   struct conversion **, bool);
> @@ -553,7 +552,7 @@ maybe_end_member_template_processing (void)
>   /* Return a new template argument vector which contains all of ARGS,
>      but has as its innermost set of arguments the EXTRA_ARGS.  */
>   
> -static tree
> +tree
>   add_to_template_args (tree args, tree extra_args)
>   {
>     tree new_args;
> @@ -14058,10 +14057,7 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
>        don't substitute through the constraints; that's only done when
>        they are checked.  */
>     if (tree ci = get_constraints (t))
> -    /* Unless we're regenerating a lambda, in which case we'll set the
> -       lambda's constraints in tsubst_lambda_expr.  */
> -    if (!lambda_fntype)
> -      set_constraints (r, ci);
> +    set_constraints (r, ci);
>   
>     if (DECL_FRIEND_CONTEXT (t))
>       SET_DECL_FRIEND_CONTEXT (r,
> @@ -14347,13 +14343,24 @@ lambda_fn_in_template_p (tree fn)
>      which the above is true.  */
>   
>   bool
> -instantiated_lambda_fn_p (tree fn)
> +regenerated_lambda_fn_p (tree fn)
>   {
>     if (!fn || !LAMBDA_FUNCTION_P (fn))
>       return false;
>     tree closure = DECL_CONTEXT (fn);
>     tree lam = CLASSTYPE_LAMBDA_EXPR (closure);
> -  return LAMBDA_EXPR_INSTANTIATED (lam);
> +  return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE;
> +}
> +
> +/* Return the LAMBDA_EXPR from which T was ultimately regenerated.
> +   If T is not a regenerated LAMBDA_EXPR, return T.  */
> +
> +tree
> +most_general_lambda (tree t)
> +{
> +  while (LAMBDA_EXPR_REGENERATED_FROM (t))
> +    t = LAMBDA_EXPR_REGENERATED_FROM (t);
> +  return t;
>   }
>   
>   /* We're instantiating a variable from template function TCTX.  Return the
> @@ -14369,7 +14376,7 @@ enclosing_instantiation_of (tree otctx)
>     int lambda_count = 0;
>   
>     for (; tctx && (lambda_fn_in_template_p (tctx)
> -		  || instantiated_lambda_fn_p (tctx));
> +		  || regenerated_lambda_fn_p (tctx));
>          tctx = decl_function_context (tctx))
>       ++lambda_count;
>   
> @@ -14389,7 +14396,7 @@ enclosing_instantiation_of (tree otctx)
>       {
>         tree ofn = fn;
>         int flambda_count = 0;
> -      for (; fn && instantiated_lambda_fn_p (fn);
> +      for (; fn && regenerated_lambda_fn_p (fn);
>   	   fn = decl_function_context (fn))
>   	++flambda_count;
>         if ((fn && DECL_TEMPLATE_INFO (fn))
> @@ -19264,7 +19271,9 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>     LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r)
>       = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t);
>     LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t);
> -  LAMBDA_EXPR_INSTANTIATED (r) = true;
> +  LAMBDA_EXPR_REGENERATED_FROM (r) = t;
> +  LAMBDA_EXPR_REGENERATING_TARGS (r)
> +    = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args);
>   
>     gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
>   	      && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
> @@ -19406,17 +19415,6 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>   	  finish_member_declaration (fn);
>   	}
>   
> -      if (tree ci = get_constraints (oldfn))
> -	{
> -	  /* Substitute into the lambda's constraints.  */
> -	  if (oldtmpl)
> -	    ++processing_template_decl;
> -	  ci = tsubst_constraint_info (ci, args, complain, in_decl);
> -	  if (oldtmpl)
> -	    --processing_template_decl;
> -	  set_constraints (fn, ci);
> -	}
> -
>         /* Let finish_function set this.  */
>         DECL_DECLARED_CONSTEXPR_P (fn) = false;
>   
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
> new file mode 100644
> index 00000000000..01fda6efb38
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda16.C
> @@ -0,0 +1,61 @@
> +// PR c++/99874
> +// { dg-do compile { target c++20 } }
> +
> +template <class T>
> +struct A {
> +  static inline auto a = [] <class U> (U) {
> +    return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { };
> +  };
> +
> +  template <class W>
> +  static inline auto b = [] <class U> (U) {
> +    return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { };
> +  };
> +
> +  static auto f() {
> +    return [] <class U> (U) {
> +      return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { };
> +    };
> +  }
> +
> +  template <class W>
> +  static auto g() {
> +    return [] <class U> (U) {
> +      return [] <class V> (V) requires requires (T t, U u, V v, W w) { t + u + v + w; } { };
> +    };
> +  }
> +};
> +
> +template <class T>
> +auto a = [] <class U> (U) {
> +  return [] <class V> (V) requires requires (T t, U u, V v) { t + u + v; } { };
> +};
> +
> +template <class T>
> +auto b = [] <class U> (U) {
> +  return [] <class V> (V) {
> +    return [] {
> +      return [] () requires requires (T t, U u, V v) { t + u + v; } { };
> +    };
> +  };
> +};
> +
> +int main() {
> +  A<int>::a(0)(0);
> +  A<int>::a(0)(nullptr); // { dg-error "no match" }
> +
> +  A<int>::b<int>(0)(0);
> +  A<int>::b<int>(0)(nullptr); // { dg-error "no match" }
> +
> +  A<int>::f()(0)(0);
> +  A<int>::f()(0)(nullptr); // { dg-error "no match" }
> +
> +  A<int>::g<int>()(0)(0);
> +  A<int>::g<int>()(0)(nullptr); // { dg-error "no match" }
> +
> +  a<int>(0)(0);
> +  a<int>(0)(nullptr); // { dg-error "no match" }
> +
> +  b<int>(0)(0)();
> +  b<int>(0)(nullptr)()(); // { dg-error "no match" }
> +}
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
> new file mode 100644
> index 00000000000..32ae1e12174
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda17.C
> @@ -0,0 +1,14 @@
> +// { dg-do compile { target c++20 } }
> +
> +template<class T>
> +struct A { static const bool value = T::value; };
> +
> +template<class T>
> +void f() {
> +  // Verify we don't substitute into a lambda's constraints when
> +  // regenerating it, which would lead to a hard error here.
> +  [] () requires (T::value && A<T>::value) || true { }();
> +  [] <class U> (U) requires (U::value && A<T>::value) || true { }(0);
> +}
> +
> +template void f<int>();
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] c++: Don't substitute into constraints on lambdas [PR99874]
  2021-04-08 15:25 ` Jason Merrill
@ 2021-04-08 21:01   ` Patrick Palka
  2021-04-09 14:18     ` Jason Merrill
  0 siblings, 1 reply; 4+ messages in thread
From: Patrick Palka @ 2021-04-08 21:01 UTC (permalink / raw)
  To: Jason Merrill; +Cc: Patrick Palka, gcc-patches

On Thu, 8 Apr 2021, Jason Merrill wrote:

> On 4/7/21 12:10 PM, Patrick Palka wrote:
> > We currently substitute through a lambda's constraints whenever we
> > regenerate it via tsubst_lambda_expr.  This is the wrong approach
> > because it can lead to hard errors due to constraints being evaluated
> > out of order (as in the testcase concepts-lambda17.C below), and because
> > it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS
> > mechanism for delaying substitution into requires-expressions, which is
> > the cause of this PR.
> > 
> > But in order to avoid substituting through a lambda's constraints during
> > regeneration, we we need to be able to get at all in-scope template
> > parameters and the corresponding template arguments during constraint
> > checking of a lambda's op().  And this information is not easily
> > available where we need it, it seems.
> > 
> > To that end, the approach that this patch takes is to add two new fields
> > to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM
> > (replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS.
> > The former allows us to obtain the complete set of template parameters
> > that are in-scope for a lambda's op(), and the latter gives us all outer
> > template arguments that were used to regenerate the lambda.
> 
> I'm a little surprised you didn't use a TEMPLATE_INFO for these, but this way
> is fine too.

Using a TEMPLATE_INFO here works nicely here, I hadn't considered it.
Like so?  Tested on x86_64-pc-linux-gnu.

-- >8 --

Subject: [PATCH] c++: Use a TEMPLATE_INFO to hold regenerated lambda info

A TEMPLATE_INFO is a natural fit for what LAMBDA_EXPR_REGENERATED_FROM
and LAMBDA_EXPR_REGENERATING_TARGS hold, so let's use it instead.

gcc/cp/ChangeLog:

	* cp-tree.h (LAMBDA_EXPR_REGENERATED_FROM)
	(LAMBDA_EXPR_REGENERATING_TARGS): Replace these with ...
	(LAMBDA_EXPR_REGEN_INFO): ... this.
	(tree_lambda_expr::regenerated_from)
	(tree_lambda_expr::regenerating_targs): Replace these with ...
	(tree_lambda_expr::regen_info): ... this.
	* constraint.cc (satisfy_declaration_constraints): Adjust
	accordingly.
	* lambda.c (build_lambda_expr): Likewise.
	* pt.c (regenerated_lambda_fn_p): Likewise.
	(most_general_lambda): Likewise.
	(tsubst_lambda_expr): Likewise.
---
 gcc/cp/constraint.cc |  4 ++--
 gcc/cp/cp-tree.h     | 18 +++++++-----------
 gcc/cp/lambda.c      |  3 +--
 gcc/cp/pt.c          | 15 +++++++++------
 4 files changed, 19 insertions(+), 21 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 0a9d1bfea0f..0ddb2990dd9 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -3193,7 +3193,7 @@ satisfy_declaration_constraints (tree t, sat_info info)
 	 arguments that were used to regenerate the lambda.  */
       gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
       tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
-      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
+      tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda));
       if (args)
 	args = add_to_template_args (outer_args, args);
       else
@@ -3256,7 +3256,7 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
       /* As in the two-parameter version of this function.  */
       gcc_assert (TMPL_ARGS_DEPTH (args) == 1);
       tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
-      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
+      tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda));
       args = add_to_template_args (outer_args, args);
     }
   else
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index bf9d5add0cf..e42b82ae5a4 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -1456,15 +1456,12 @@ enum cp_lambda_default_capture_mode_type {
 #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \
   (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies)
 
-/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE
-   (if NODE was not regenerated via tsubst_lambda_expr).  */
-#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \
-  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from)
-
-/* The full set of template arguments used to regenerate NODE, or NULL_TREE
-   (if NODE was not regenerated via tsubst_lambda_expr).  */
-#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \
-  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs)
+/* If NODE was regenerated via tsubst_lambda_expr, this is a TEMPLATE_INFO
+   whose TI_TEMPLATE is the immediate LAMBDA_EXPR from which NODE was
+   regenerated, and TI_ARGS is the full set of template arguments used
+   to regenerate NODE from the most general lambda.  */
+#define LAMBDA_EXPR_REGEN_INFO(NODE) \
+  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regen_info)
 
 /* The closure type of the lambda, which is also the type of the
    LAMBDA_EXPR.  */
@@ -1477,8 +1474,7 @@ struct GTY (()) tree_lambda_expr
   tree capture_list;
   tree this_capture;
   tree extra_scope;
-  tree regenerated_from;
-  tree regenerating_targs;
+  tree regen_info;
   vec<tree, va_gc> *pending_proxies;
   location_t locus;
   enum cp_lambda_default_capture_mode_type default_capture_mode : 8;
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index c0a5ffb427e..16e2b4c18b4 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -41,8 +41,7 @@ build_lambda_expr (void)
   LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE;
   LAMBDA_EXPR_CAPTURE_LIST         (lambda) = NULL_TREE;
   LAMBDA_EXPR_THIS_CAPTURE         (lambda) = NULL_TREE;
-  LAMBDA_EXPR_REGENERATED_FROM     (lambda) = NULL_TREE;
-  LAMBDA_EXPR_REGENERATING_TARGS   (lambda) = NULL_TREE;
+  LAMBDA_EXPR_REGEN_INFO           (lambda) = NULL_TREE;
   LAMBDA_EXPR_PENDING_PROXIES      (lambda) = NULL;
   LAMBDA_EXPR_MUTABLE_P            (lambda) = false;
   return lambda;
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index daf1b5aeb32..01c807be1bb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -14356,7 +14356,7 @@ regenerated_lambda_fn_p (tree fn)
     return false;
   tree closure = DECL_CONTEXT (fn);
   tree lam = CLASSTYPE_LAMBDA_EXPR (closure);
-  return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE;
+  return LAMBDA_EXPR_REGEN_INFO (lam) != NULL_TREE;
 }
 
 /* Return the LAMBDA_EXPR from which T was ultimately regenerated.
@@ -14365,8 +14365,8 @@ regenerated_lambda_fn_p (tree fn)
 tree
 most_general_lambda (tree t)
 {
-  while (LAMBDA_EXPR_REGENERATED_FROM (t))
-    t = LAMBDA_EXPR_REGENERATED_FROM (t);
+  while (tree ti = LAMBDA_EXPR_REGEN_INFO (t))
+    t = TI_TEMPLATE (ti);
   return t;
 }
 
@@ -19278,9 +19278,12 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
   LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r)
     = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t);
   LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t);
-  LAMBDA_EXPR_REGENERATED_FROM (r) = t;
-  LAMBDA_EXPR_REGENERATING_TARGS (r)
-    = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args);
+  if (tree ti = LAMBDA_EXPR_REGEN_INFO (t))
+    LAMBDA_EXPR_REGEN_INFO (r)
+      = build_template_info (t, add_to_template_args (TI_ARGS (ti), args));
+  else
+    LAMBDA_EXPR_REGEN_INFO (r)
+      = build_template_info (t, args);
 
   gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
 	      && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
-- 
2.31.1.189.g2e36527f23


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH] c++: Don't substitute into constraints on lambdas [PR99874]
  2021-04-08 21:01   ` Patrick Palka
@ 2021-04-09 14:18     ` Jason Merrill
  0 siblings, 0 replies; 4+ messages in thread
From: Jason Merrill @ 2021-04-09 14:18 UTC (permalink / raw)
  To: Patrick Palka; +Cc: gcc-patches

On 4/8/21 5:01 PM, Patrick Palka wrote:
> On Thu, 8 Apr 2021, Jason Merrill wrote:
> 
>> On 4/7/21 12:10 PM, Patrick Palka wrote:
>>> We currently substitute through a lambda's constraints whenever we
>>> regenerate it via tsubst_lambda_expr.  This is the wrong approach
>>> because it can lead to hard errors due to constraints being evaluated
>>> out of order (as in the testcase concepts-lambda17.C below), and because
>>> it doesn't mesh well with the recently added REQUIRES_EXPR_EXTRA_ARGS
>>> mechanism for delaying substitution into requires-expressions, which is
>>> the cause of this PR.
>>>
>>> But in order to avoid substituting through a lambda's constraints during
>>> regeneration, we we need to be able to get at all in-scope template
>>> parameters and the corresponding template arguments during constraint
>>> checking of a lambda's op().  And this information is not easily
>>> available where we need it, it seems.
>>>
>>> To that end, the approach that this patch takes is to add two new fields
>>> to LAMBDA_EXPR (and remove one): LAMBDA_EXPR_REGENERATED_FROM
>>> (replacing LAMBDA_EXPR_INSTANTIATED), and LAMBDA_EXPR_REGENERATING_TARGS.
>>> The former allows us to obtain the complete set of template parameters
>>> that are in-scope for a lambda's op(), and the latter gives us all outer
>>> template arguments that were used to regenerate the lambda.
>>
>> I'm a little surprised you didn't use a TEMPLATE_INFO for these, but this way
>> is fine too.
> 
> Using a TEMPLATE_INFO here works nicely here, I hadn't considered it.
> Like so?  Tested on x86_64-pc-linux-gnu.

OK.

> -- >8 --
> 
> Subject: [PATCH] c++: Use a TEMPLATE_INFO to hold regenerated lambda info
> 
> A TEMPLATE_INFO is a natural fit for what LAMBDA_EXPR_REGENERATED_FROM
> and LAMBDA_EXPR_REGENERATING_TARGS hold, so let's use it instead.
> 
> gcc/cp/ChangeLog:
> 
> 	* cp-tree.h (LAMBDA_EXPR_REGENERATED_FROM)
> 	(LAMBDA_EXPR_REGENERATING_TARGS): Replace these with ...
> 	(LAMBDA_EXPR_REGEN_INFO): ... this.
> 	(tree_lambda_expr::regenerated_from)
> 	(tree_lambda_expr::regenerating_targs): Replace these with ...
> 	(tree_lambda_expr::regen_info): ... this.
> 	* constraint.cc (satisfy_declaration_constraints): Adjust
> 	accordingly.
> 	* lambda.c (build_lambda_expr): Likewise.
> 	* pt.c (regenerated_lambda_fn_p): Likewise.
> 	(most_general_lambda): Likewise.
> 	(tsubst_lambda_expr): Likewise.
> ---
>   gcc/cp/constraint.cc |  4 ++--
>   gcc/cp/cp-tree.h     | 18 +++++++-----------
>   gcc/cp/lambda.c      |  3 +--
>   gcc/cp/pt.c          | 15 +++++++++------
>   4 files changed, 19 insertions(+), 21 deletions(-)
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 0a9d1bfea0f..0ddb2990dd9 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3193,7 +3193,7 @@ satisfy_declaration_constraints (tree t, sat_info info)
>   	 arguments that were used to regenerate the lambda.  */
>         gcc_assert (!args || TMPL_ARGS_DEPTH (args) == 1);
>         tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
> -      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
> +      tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda));
>         if (args)
>   	args = add_to_template_args (outer_args, args);
>         else
> @@ -3256,7 +3256,7 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
>         /* As in the two-parameter version of this function.  */
>         gcc_assert (TMPL_ARGS_DEPTH (args) == 1);
>         tree lambda = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (t));
> -      tree outer_args = LAMBDA_EXPR_REGENERATING_TARGS (lambda);
> +      tree outer_args = TI_ARGS (LAMBDA_EXPR_REGEN_INFO (lambda));
>         args = add_to_template_args (outer_args, args);
>       }
>     else
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index bf9d5add0cf..e42b82ae5a4 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -1456,15 +1456,12 @@ enum cp_lambda_default_capture_mode_type {
>   #define LAMBDA_EXPR_PENDING_PROXIES(NODE) \
>     (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->pending_proxies)
>   
> -/* The immediate LAMBDA_EXPR from which NODE was regenerated, or NULL_TREE
> -   (if NODE was not regenerated via tsubst_lambda_expr).  */
> -#define LAMBDA_EXPR_REGENERATED_FROM(NODE) \
> -  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerated_from)
> -
> -/* The full set of template arguments used to regenerate NODE, or NULL_TREE
> -   (if NODE was not regenerated via tsubst_lambda_expr).  */
> -#define LAMBDA_EXPR_REGENERATING_TARGS(NODE) \
> -  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regenerating_targs)
> +/* If NODE was regenerated via tsubst_lambda_expr, this is a TEMPLATE_INFO
> +   whose TI_TEMPLATE is the immediate LAMBDA_EXPR from which NODE was
> +   regenerated, and TI_ARGS is the full set of template arguments used
> +   to regenerate NODE from the most general lambda.  */
> +#define LAMBDA_EXPR_REGEN_INFO(NODE) \
> +  (((struct tree_lambda_expr *)LAMBDA_EXPR_CHECK (NODE))->regen_info)
>   
>   /* The closure type of the lambda, which is also the type of the
>      LAMBDA_EXPR.  */
> @@ -1477,8 +1474,7 @@ struct GTY (()) tree_lambda_expr
>     tree capture_list;
>     tree this_capture;
>     tree extra_scope;
> -  tree regenerated_from;
> -  tree regenerating_targs;
> +  tree regen_info;
>     vec<tree, va_gc> *pending_proxies;
>     location_t locus;
>     enum cp_lambda_default_capture_mode_type default_capture_mode : 8;
> diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
> index c0a5ffb427e..16e2b4c18b4 100644
> --- a/gcc/cp/lambda.c
> +++ b/gcc/cp/lambda.c
> @@ -41,8 +41,7 @@ build_lambda_expr (void)
>     LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (lambda) = CPLD_NONE;
>     LAMBDA_EXPR_CAPTURE_LIST         (lambda) = NULL_TREE;
>     LAMBDA_EXPR_THIS_CAPTURE         (lambda) = NULL_TREE;
> -  LAMBDA_EXPR_REGENERATED_FROM     (lambda) = NULL_TREE;
> -  LAMBDA_EXPR_REGENERATING_TARGS   (lambda) = NULL_TREE;
> +  LAMBDA_EXPR_REGEN_INFO           (lambda) = NULL_TREE;
>     LAMBDA_EXPR_PENDING_PROXIES      (lambda) = NULL;
>     LAMBDA_EXPR_MUTABLE_P            (lambda) = false;
>     return lambda;
> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
> index daf1b5aeb32..01c807be1bb 100644
> --- a/gcc/cp/pt.c
> +++ b/gcc/cp/pt.c
> @@ -14356,7 +14356,7 @@ regenerated_lambda_fn_p (tree fn)
>       return false;
>     tree closure = DECL_CONTEXT (fn);
>     tree lam = CLASSTYPE_LAMBDA_EXPR (closure);
> -  return LAMBDA_EXPR_REGENERATED_FROM (lam) != NULL_TREE;
> +  return LAMBDA_EXPR_REGEN_INFO (lam) != NULL_TREE;
>   }
>   
>   /* Return the LAMBDA_EXPR from which T was ultimately regenerated.
> @@ -14365,8 +14365,8 @@ regenerated_lambda_fn_p (tree fn)
>   tree
>   most_general_lambda (tree t)
>   {
> -  while (LAMBDA_EXPR_REGENERATED_FROM (t))
> -    t = LAMBDA_EXPR_REGENERATED_FROM (t);
> +  while (tree ti = LAMBDA_EXPR_REGEN_INFO (t))
> +    t = TI_TEMPLATE (ti);
>     return t;
>   }
>   
> @@ -19278,9 +19278,12 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>     LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (r)
>       = LAMBDA_EXPR_DEFAULT_CAPTURE_MODE (t);
>     LAMBDA_EXPR_MUTABLE_P (r) = LAMBDA_EXPR_MUTABLE_P (t);
> -  LAMBDA_EXPR_REGENERATED_FROM (r) = t;
> -  LAMBDA_EXPR_REGENERATING_TARGS (r)
> -    = add_to_template_args (LAMBDA_EXPR_REGENERATING_TARGS (t), args);
> +  if (tree ti = LAMBDA_EXPR_REGEN_INFO (t))
> +    LAMBDA_EXPR_REGEN_INFO (r)
> +      = build_template_info (t, add_to_template_args (TI_ARGS (ti), args));
> +  else
> +    LAMBDA_EXPR_REGEN_INFO (r)
> +      = build_template_info (t, args);
>   
>     gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (t) == NULL_TREE
>   	      && LAMBDA_EXPR_PENDING_PROXIES (t) == NULL);
> 


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2021-04-09 14:18 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-04-07 16:10 [PATCH] c++: Don't substitute into constraints on lambdas [PR99874] Patrick Palka
2021-04-08 15:25 ` Jason Merrill
2021-04-08 21:01   ` Patrick Palka
2021-04-09 14:18     ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).