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;
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 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 ());
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 (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. */
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,
#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) \
#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) \
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;
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);
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);
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;
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);
/* 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;
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,
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
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;
{
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))
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);
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;
--- /dev/null
+// 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" }
+}
--- /dev/null
+// { 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>();