* [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).