ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR)
STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates)
MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR)
+ LAMBDA_EXPR_CONST_QUAL_P (in LAMBDA_EXPR)
2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE)
ICS_THIS_FLAG (in _CONV)
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
#define LAMBDA_EXPR_CONSTEVAL_BLOCK_P(NODE) \
TREE_LANG_FLAG_0 (LAMBDA_EXPR_CHECK (NODE))
+/* True if we should add "const" when figuring out the type of an entity
+ in a lambda. This is false in the parameter-declaration-clause of
+ a lambda; after that, it will remain false if the mutable keyword is
+ present. */
+#define LAMBDA_EXPR_CONST_QUAL_P(NODE) \
+ TREE_LANG_FLAG_1 (LAMBDA_EXPR_CHECK (NODE))
+
/* True iff uses of a const variable capture were optimized away. */
#define LAMBDA_EXPR_CAPTURE_OPTIMIZED(NODE) \
TREE_LANG_FLAG_2 (LAMBDA_EXPR_CHECK (NODE))
extern void maybe_show_extern_c_location (void);
extern bool literal_integer_zerop (const_tree);
extern tree attr_chainon (tree, tree);
+extern tree maybe_add_dummy_lambda_op (tree);
+extern void remove_dummy_lambda_op (tree, tree);
/* in pt.cc */
extern tree canonical_type_parameter (tree);
extern void record_lambda_scope_discriminator (tree lambda);
extern void record_lambda_scope_sig_discriminator (tree lambda, tree fn);
extern tree start_lambda_function (tree fn, tree lambda_expr);
+extern void push_capture_proxies (tree, bool = false);
extern void finish_lambda_function (tree body);
extern bool regenerated_lambda_fn_p (tree);
extern tree lambda_regenerating_args (tree);
/* MEMBER is a capture field in a lambda closure class. Now that we're
inside the operator(), build a placeholder var for future lookups and
- debugging. */
+ debugging. But if EARLY_P is true, we do not have the real operator()
+ yet and we have to proceed differently. */
-static tree
-build_capture_proxy (tree member, tree init)
+tree
+build_capture_proxy (tree member, tree init, bool early_p)
{
tree var, object, fn, closure, name, lam, type;
if (name == this_identifier)
{
+ if (early_p)
+ return var;
gcc_assert (LAMBDA_EXPR_THIS_CAPTURE (lam) == member);
LAMBDA_EXPR_THIS_CAPTURE (lam) = var;
}
- if (fn == current_function_decl)
+ if (early_p)
+ {
+ gcc_checking_assert (current_binding_level->kind == sk_lambda);
+ /* insert_capture_proxy below wouldn't push into the lambda scope. */
+ pushdecl (var);
+ }
+ else if (fn == current_function_decl)
insert_capture_proxy (var);
else
vec_safe_push (LAMBDA_EXPR_PENDING_PROXIES (lam), var);
= tree_cons (listmem, initializer, LAMBDA_EXPR_CAPTURE_LIST (lambda));
if (LAMBDA_EXPR_CLOSURE (lambda))
- return build_capture_proxy (member, initializer);
+ return build_capture_proxy (member, initializer, /*early_p=*/false);
/* For explicit captures we haven't started the function yet, so we wait
and build the proxy from cp_parser_lambda_body. */
LAMBDA_CAPTURE_EXPLICIT_P (LAMBDA_EXPR_CAPTURE_LIST (lambda)) = true;
tree type = TYPE_MAIN_VARIANT (TREE_TYPE (object));
gcc_assert (!TYPE_PTR_P (type));
+ tree fn;
if (type != current_class_type
&& current_class_type
&& LAMBDA_TYPE_P (current_class_type)
- && lambda_function (current_class_type)
+ && (fn = lambda_function (current_class_type))
+ /* Even dummy lambdas have an operator() since P2036, but the
+ dummy operator() doesn't have this set. */
+ && DECL_LAMBDA_FUNCTION_P (fn)
&& DERIVED_FROM_P (type, nonlambda_method_basetype()))
return CLASSTYPE_LAMBDA_EXPR (current_class_type);
LAMBDA_EXPR_SCOPE_SIG_DISCRIMINATOR (lambda) = sig->count++;
}
+/* Push the proxies for any explicit captures in LAMBDA_EXPR.
+ If EARLY_P, we do not have the real operator() yet. */
+
+void
+push_capture_proxies (tree lambda_expr, bool early_p)
+{
+ for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap;
+ cap = TREE_CHAIN (cap))
+ build_capture_proxy (TREE_PURPOSE (cap), TREE_VALUE (cap), early_p);
+}
+
tree
start_lambda_function (tree fco, tree lambda_expr)
{
tree body = begin_function_body ();
/* Push the proxies for any explicit captures. */
- for (tree cap = LAMBDA_EXPR_CAPTURE_LIST (lambda_expr); cap;
- cap = TREE_CHAIN (cap))
- build_capture_proxy (TREE_PURPOSE (cap), TREE_VALUE (cap));
+ push_capture_proxies (lambda_expr);
return body;
}
}
/* Don't complain if it's from an enclosing function. */
else if (DECL_CONTEXT (old) == current_function_decl
- && TREE_CODE (decl) != PARM_DECL
- && TREE_CODE (old) == PARM_DECL)
+ && ((TREE_CODE (decl) != PARM_DECL
+ && TREE_CODE (old) == PARM_DECL)
+ /* We should also give an error for
+ [x=1]{ int x; } */
+ || is_capture_proxy (old)))
{
/* Go to where the parms should be and see if we find
them there. */
"template-parameter-scope",
"template-explicit-spec-scope",
"transaction-scope",
- "openmp-scope"
+ "openmp-scope",
+ "lambda-scope"
};
static_assert (ARRAY_SIZE (scope_kind_names) == sk_count,
"must keep names aligned with scope_kind enum");
case sk_transaction:
case sk_omp:
case sk_stmt_expr:
+ case sk_lambda:
scope->keep = keep_next_level_flag;
break;
"template <>", this scope is always empty. */
sk_transaction, /* A synchronized or atomic statement. */
sk_omp, /* An OpenMP structured block. */
+ sk_lambda, /* A lambda scope. */
sk_count /* Number of scope_kind enumerations. */
};
if (cp_parser_start_tentative_firewall (parser))
start = token;
+ /* A lambda scope starts immediately after the lambda-introducer of E
+ and extends to the end of the compound-statement of E. */
+ begin_scope (sk_lambda, NULL_TREE);
+
ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr,
consteval_block_p);
if (ok)
maybe_add_lambda_conv_op (type);
+ /* Leave the lambda scope. */
+ pop_bindings_and_leave_scope ();
finish_struct (type, /*attributes=*/NULL_TREE);
in_expansion_stmt = save_in_expansion_stmt;
clear_decl_specs (&lambda_specs);
/* A lambda op() is const unless explicitly 'mutable'. */
cp_cv_quals quals = TYPE_QUAL_CONST;
+ /* Don't add "const" to entities in the parameter-declaration-clause. */
+ LAMBDA_EXPR_CONST_QUAL_P (lambda_expr) = false;
+
+ /* Inject the captures into the lambda scope as they may be used in the
+ declarator and we have to be able to look them up. */
+ tree dummy_fco = maybe_add_dummy_lambda_op (lambda_expr);
+ push_capture_proxies (lambda_expr, /*early_p=*/true);
/* The template-parameter-list is optional, but must begin with
an opening angle if present. */
}
}
+ /* Now we're done with the parameter-declaration-clause, and should
+ assume "const" unless "mutable" was present. */
+ LAMBDA_EXPR_CONST_QUAL_P (lambda_expr) = quals == TYPE_QUAL_CONST;
+
tx_qual = cp_parser_tx_qualifier_opt (parser);
if (omitted_parms_loc && tx_qual)
{
pop_bindings_and_leave_scope ();
}
+ /* We are about to create the real operator(), so get rid of the old one. */
+ if (dummy_fco)
+ remove_dummy_lambda_op (dummy_fco, lambda_expr);
+
/* Create the function call operator.
Messing with declarators like this is no uglier than building up the
}
}
+/* Create a fake operator() for a lambda. We do this so that we can
+ build_capture_proxy even before start_lambda_function. */
+
+static tree
+make_dummy_lambda_op ()
+{
+ cp_decl_specifier_seq return_type_specs;
+ cp_cv_quals quals = TYPE_UNQUALIFIED;
+
+ clear_decl_specs (&return_type_specs);
+ return_type_specs.type = make_auto ();
+
+ void *p = obstack_alloc (&declarator_obstack, 0);
+
+ cp_declarator *declarator = make_id_declarator (NULL_TREE,
+ call_op_identifier,
+ sfk_none,
+ input_location);
+
+ declarator = make_call_declarator (declarator, void_list_node, quals,
+ VIRT_SPEC_UNSPECIFIED,
+ REF_QUAL_NONE, NULL_TREE,
+ NULL_TREE, NULL_TREE, NULL_TREE,
+ NULL_TREE, UNKNOWN_LOCATION);
+
+ tree fco = grokmethod (&return_type_specs, declarator, NULL_TREE);
+ obstack_free (&declarator_obstack, p);
+
+ return fco;
+}
+
+/* We need to push early capture proxies (for parsing the lambda-declarator),
+ and we may need a dummy operator() to be able to build the proxies.
+ LAMBDA_EXPR is the lambda we are building the captures for. */
+
+tree
+maybe_add_dummy_lambda_op (tree lambda_expr)
+{
+ /* If there are no captures, we don't need this. */
+ if (!LAMBDA_EXPR_CAPTURE_LIST (lambda_expr))
+ return NULL_TREE;
+
+ tree fco = make_dummy_lambda_op ();
+ if (fco != error_mark_node)
+ finish_member_declaration (fco);
+
+ return fco;
+}
+
+/* Remove the dummy operator() DUMMY_FCO we built for parsing the
+ lambda-declarator of LAMBDA_EXPR. */
+
+void
+remove_dummy_lambda_op (tree dummy_fco, tree lambda_expr)
+{
+ tree type = TREE_TYPE (lambda_expr);
+ if (TYPE_FIELDS (type) == dummy_fco)
+ {
+ /* Stitch out the dummy operator(). */
+ TYPE_FIELDS (type) = DECL_CHAIN (TYPE_FIELDS (type));
+ /* And clear the member vector as well. */
+ auto *member_vec = CLASSTYPE_MEMBER_VEC (type);
+ gcc_assert (member_vec->length () == 1);
+ member_vec->truncate (0);
+ }
+ /* Class templates will have the dummy operator() stashed here too. */
+ tree &list = CLASSTYPE_DECL_LIST (type);
+ if (list && TREE_VALUE (list) == dummy_fco)
+ list = TREE_CHAIN (list);
+ /* ??? We can't ggc_free dummy_fco yet. There's still a binding in the
+ closure to it, and the captures have it as their DECL_CONTEXT. */
+}
+
/* Parse the body of a lambda expression, which is simply
compound-statement
tree fntype = static_fn_type (oldfn);
+ begin_scope (sk_lambda, NULL_TREE);
+
+ /* Like in cp_parser_lambda_expression, we need to bring the captures
+ into the lambda scope. */
+ tree ns = decl_namespace_context (type);
+ push_nested_namespace (ns);
+ push_nested_class (type);
+ tree dummy_fco = maybe_add_dummy_lambda_op (r);
+ pop_nested_class ();
+ pop_nested_namespace (ns);
+ push_capture_proxies (r, /*early_p=*/true);
+
tree saved_ctp = current_template_parms;
if (oldtmpl)
{
--processing_template_decl;
}
+ /* We are about to create the real operator(), so get rid of the old one. */
+ if (dummy_fco)
+ remove_dummy_lambda_op (dummy_fco, r);
+
if (fntype == error_mark_node)
r = error_mark_node;
else
enclosing expression. */
cp_evaluated ev;
+ /* Now we're done with the parameter-declaration-clause, and should
+ assume "const" unless "mutable" was present. */
+ LAMBDA_EXPR_CONST_QUAL_P (r) = LAMBDA_EXPR_CONST_QUAL_P (t);
+
bool nested = cfun;
if (nested)
push_function_context ();
}
out:
+ pop_bindings_and_leave_scope ();
finish_struct (type, /*attr*/NULL_TREE);
insert_pending_capture_proxies ();
return build_baselink (conv_path, access_path, fns, /*optype=*/NULL_TREE);
}
+/* Returns true iff we are currently parsing a lambda-declarator. */
+
+static bool
+parsing_lambda_declarator ()
+{
+ cp_binding_level *b = current_binding_level;
+ while (b->kind == sk_template_parms || b->kind == sk_function_parms)
+ b = b->level_chain;
+ return b->kind == sk_lambda;
+}
+
/* Returns true iff DECL is a variable from a function outside
the current one. */
/* Don't get confused by temporaries. */
&& DECL_NAME (decl)
&& (DECL_CONTEXT (decl) != current_function_decl
- || parsing_nsdmi ()));
+ || parsing_nsdmi ()
+ /* Also consider captures as outer vars if we are in
+ decltype in a lambda declarator as in:
+ auto l = [j=0]() -> decltype((j)) { ... }
+ for the sake of finish_decltype_type.
+
+ (Similar issue also affects non-lambdas, but vexing parse
+ makes it more difficult to handle than lambdas.) */
+ || parsing_lambda_declarator ()));
}
/* As above, but also checks that DECL is automatic. */
if (!mark_used (decl, complain))
return error_mark_node;
- if (parsing_nsdmi ())
+ if (parsing_nsdmi () || parsing_lambda_declarator ())
containing_function = NULL_TREE;
if (containing_function && LAMBDA_FUNCTION_P (containing_function))
}
else
{
- if (outer_automatic_var_p (STRIP_REFERENCE_REF (expr))
- && current_function_decl
- && LAMBDA_FUNCTION_P (current_function_decl))
+ tree decl = STRIP_REFERENCE_REF (expr);
+ tree lam = current_lambda_expr ();
+ if (lam && outer_automatic_var_p (decl))
{
/* [expr.prim.id.unqual]/3: If naming the entity from outside of an
unevaluated operand within S would refer to an entity captured by
local variable inside decltype, not just decltype((x)) (PR83167).
And we don't handle nested lambdas properly, where we need to
consider the outer lambdas as well (PR112926). */
- tree decl = STRIP_REFERENCE_REF (expr);
- tree lam = CLASSTYPE_LAMBDA_EXPR (DECL_CONTEXT (current_function_decl));
tree cap = lookup_name (DECL_NAME (decl), LOOK_where::BLOCK,
LOOK_want::HIDDEN_LAMBDA);
if (type && !TYPE_REF_P (type))
{
- tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl));
- if (WILDCARD_TYPE_P (non_reference (obtype)))
- /* We don't know what the eventual obtype quals will be. */
- goto dependent;
- auto direct_type = [](tree t){
- if (INDIRECT_TYPE_P (t))
- return TREE_TYPE (t);
- return t;
- };
- int const quals = cp_type_quals (type)
- | cp_type_quals (direct_type (obtype));
+ int quals;
+ if (current_function_decl
+ && LAMBDA_FUNCTION_P (current_function_decl)
+ && DECL_XOBJ_MEMBER_FUNCTION_P (current_function_decl))
+ {
+ tree obtype = TREE_TYPE (DECL_ARGUMENTS (current_function_decl));
+ if (WILDCARD_TYPE_P (non_reference (obtype)))
+ /* We don't know what the eventual obtype quals will be. */
+ goto dependent;
+ auto direct_type = [](tree t){
+ if (INDIRECT_TYPE_P (t))
+ return TREE_TYPE (t);
+ return t;
+ };
+ quals = (cp_type_quals (type)
+ | cp_type_quals (direct_type (obtype)));
+ }
+ else
+ /* We are in the parameter clause, trailing return type, or
+ the requires clause and have no relevant c_f_decl yet. */
+ quals = (LAMBDA_EXPR_CONST_QUAL_P (lam)
+ ? TYPE_QUAL_CONST : TYPE_UNQUALIFIED);
type = cp_build_qualified_type (type, quals);
type = build_reference_type (type);
}
decltype((x)) y2 = y1; // y2 has type float const&
decltype(r) r1 = y1; // r1 has type float&
decltype((r)) r2 = y2; // r2 has type float const&
- return y2; // { dg-bogus "'float&' to 'const float'" "" { xfail *-*-* } }
+ return y2; // { dg-bogus "'float&' to 'const float'" }
};
[=](decltype((x)) y) {
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+
+template <typename T, typename U>
+constexpr bool is_same = false;
+
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+struct S {
+ void foo () {
+ auto counter1 = [j=0]() mutable -> decltype(j) {
+ return j++;
+ };
+ auto counter2 = [j=0, o=0, k=0, e=0]() mutable -> decltype(j) {
+ return j + o + k + e;
+ };
+ }
+};
+
+// [expr.prim.id.unqual]/3.2
+void
+f ()
+{
+ float x, &r = x;
+
+ [=]() -> decltype((x)) { // lambda returns float const& because this lambda is not mutable and
+ // x is an lvalue
+ decltype(x) y1; // y1 has type float
+ decltype((x)) y2 = y1; // y2 has type float const&
+ decltype(r) r1 = y1; // r1 has type float&
+ decltype((r)) r2 = y2; // r2 has type float const&
+ return y2;
+ };
+
+ [=](decltype((x)) y) {
+ decltype((x)) z = x; // OK, y has type float&, z has type float const&
+ static_assert(is_same<float&, decltype((y))>);
+ static_assert(is_same<const float&, decltype((z))>);
+ };
+
+ [=] {
+ [](decltype((x)) y) { // OK, lambda takes a parameter of type float const&
+ };
+
+ [x=1](decltype((x)) y) {
+ decltype((x)) z = x; // OK, y has type int&, z has type int const&
+ // FIXME We don't handle nested lambdas yet?
+ //static_assert(is_same<int&, decltype((y))>);
+ static_assert(is_same<const int&, decltype((z))>);
+ };
+ };
+
+ [x=1](decltype((x)) y) {
+ decltype((x)) z = x;
+ static_assert(is_same<int&, decltype((y))>);
+ static_assert(is_same<const int&, decltype((z))>);
+ };
+}
+
+void
+ok ()
+{
+ auto counter1 = [j=0]() mutable -> decltype(j) {
+ static_assert(is_same<int&, decltype((j))>);
+ return j++;
+ };
+
+ auto l = [j=0]() -> decltype(j) {
+ static_assert(is_same<const int&, decltype((j))>);
+ return j;
+ };
+
+ int y;
+ [=] -> decltype((y)) {
+ return y;
+ };
+}
+
+void
+foo ()
+{
+ int x = [x](int y[sizeof x]){return sizeof x;}(0);
+}
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+// From LLVM's test/SemaCXX/lambda-capture-type-deduction.cpp
+
+template <typename T, typename U>
+constexpr bool is_same = false;
+
+template <typename T>
+constexpr bool is_same<T, T> = true;
+
+void
+f ()
+{
+ int y;
+
+ static_assert(is_same<const int &,
+ decltype([x = 1] -> decltype((x)) { return x; }())>);
+
+ static_assert(is_same<int &,
+ decltype([x = 1] mutable -> decltype((x)) { return x; }())>);
+
+ static_assert(is_same<const int &,
+ decltype([=] -> decltype((y)) { return y; }())>);
+
+ static_assert(is_same<int &,
+ decltype([=] mutable -> decltype((y)) { return y; }())>);
+
+ // Clang++ rejects this one, though the only difference is the extra (),
+ // and without the () the result is correct, as demonstrated above.
+ static_assert(is_same<const int &,
+ decltype([=]() -> decltype((y)) { return y; }())>);
+
+ static_assert(is_same<int &,
+ decltype([=]() mutable -> decltype((y)) { return y; }())>);
+
+ static_assert(is_same<const int &,
+ decltype([y] -> decltype((y)) { return y; }())>);
+
+ static_assert(is_same<int &,
+ decltype([y] mutable -> decltype((y)) { return y; }())>);
+
+
+ auto ref = [&x = y](
+ decltype([&](decltype(x)) { return 0; }) y) {
+ return x;
+ };
+}
+
+void
+nested ()
+{
+ int x, y, z;
+ (void)[&](
+ decltype([&](
+ decltype([=](
+ decltype([&](
+ decltype([&](decltype(x)) {})) {})) {})) {})){};
+
+ (void)[&](
+ decltype([&](
+ decltype([&](
+ decltype([&](
+ decltype([&](decltype(y)) {})) {})) {})) {})){};
+
+ (void)[=](
+ decltype([=](
+ decltype([=](
+ decltype([=](
+ decltype([&]<decltype(z)> {})) {})) {})) {})){};
+}
+
+void
+test_noexcept ()
+{
+ int y;
+
+ static_assert(noexcept([x = 1] noexcept(is_same<const int &, decltype((x))>) {}()));
+ static_assert(noexcept([x = 1] mutable noexcept(is_same<int &, decltype((x))>) {}()));
+ static_assert(noexcept([y] noexcept(is_same<const int &, decltype((y))>) {}()));
+ static_assert(noexcept([y] mutable noexcept(is_same<int &, decltype((y))>) {}()));
+ static_assert(noexcept([=] noexcept(is_same<const int &, decltype((y))>) {}()));
+ static_assert(noexcept([=] mutable noexcept(is_same<int &, decltype((y))>) {}()));
+ static_assert(noexcept([&] noexcept(is_same<int &, decltype((y))>) {}()));
+ static_assert(noexcept([&] mutable noexcept(is_same<int &, decltype((y))>) {}()));
+}
+
+void
+check_params ()
+{
+ int i = 0;
+ int &j = i;
+
+ [=](decltype((j)) jp, decltype((i)) ip) {
+ static_assert(is_same<const int&, decltype((j))>);
+ static_assert(is_same<const int &, decltype((i))>);
+ static_assert(is_same<int &, decltype((jp))>);
+ static_assert(is_same<int &, decltype((ip))>);
+ };
+
+ [=](decltype((j)) jp, decltype((i)) ip) mutable {
+ static_assert(is_same<int &, decltype((j))>);
+ static_assert(is_same<int &, decltype((i))>);
+ static_assert(is_same<int &, decltype((jp))>);
+ static_assert(is_same<int &, decltype((ip))>);
+ static_assert(is_same<int &, decltype(jp)>);
+ static_assert(is_same<int &, decltype(ip)>);
+ };
+
+ [a = 0](decltype((a)) ap) mutable {
+ static_assert(is_same<int &, decltype((a))>);
+ static_assert(is_same<int, decltype(a)>);
+ static_assert(is_same<int &, decltype(ap)>);
+ decltype(a) x;
+ decltype((a)) y = x;
+ static_assert(is_same<int &, decltype(y)>);
+ };
+
+ [a = 0](decltype((a)) ap) {
+ static_assert(is_same<const int &, decltype((a))>);
+ static_assert(is_same<int, decltype(a)>);
+ static_assert(is_same<int&, decltype((ap))>);
+ decltype(a) x;
+ decltype((a)) y = x;
+ static_assert(is_same<const int &, decltype(y)>);
+ };
+
+ int a;
+ [a](decltype((a)) ap) mutable {
+ static_assert(is_same<int &, decltype((a))>);
+ static_assert(is_same<int, decltype(a)>);
+ static_assert(is_same<int &, decltype(ap)>);
+ decltype(a) x;
+ decltype((a)) y = x;
+ static_assert(is_same<int &, decltype(y)>);
+ };
+
+ [a](decltype((a)) ap) {
+ static_assert(is_same<const int &, decltype((a))>);
+ static_assert(is_same<int, decltype(a)>);
+ static_assert(is_same<int&, decltype((ap))>);
+ decltype(a) x;
+ decltype((a)) y = x;
+ static_assert(is_same<const int &, decltype(y)>);
+ };
+}
+
+template <typename T>
+void
+check_params_tpl ()
+{
+ T i = 0;
+ T &j = i;
+ (void)[=](decltype((j)) jp, decltype((i)) ip) {
+ static_assert(is_same<const int&, decltype((j))>);
+ static_assert(is_same<const int &, decltype((i))>);
+ // In these two, clang++ produces 'const int&'. Why, when it's
+ // the same as in check_params, just not a template?
+ static_assert(is_same<int &, decltype((jp))>);
+ static_assert(is_same<int &, decltype((ip))>);
+ };
+
+ (void)[=](decltype((j)) jp, decltype((i)) ip) mutable {
+ static_assert(is_same<int &, decltype((j))>);
+ static_assert(is_same<int &, decltype((i))>);
+ static_assert(is_same<int &, decltype((jp))>);
+ static_assert(is_same<int &, decltype((ip))>);
+ static_assert(is_same<int &, decltype(jp)>);
+ static_assert(is_same<int &, decltype(ip)>);
+ };
+
+ (void)[a = 0](decltype((a)) ap) mutable {
+ static_assert(is_same<int &, decltype((a))>);
+ static_assert(is_same<int, decltype(a)>);
+ static_assert(is_same<int &, decltype(ap)>);
+ };
+ (void)[a = 0](decltype((a)) ap) {
+ static_assert(is_same<const int &, decltype((a))>);
+ static_assert(is_same<int, decltype(a)>);
+ static_assert(is_same<int&, decltype((ap))>);
+ };
+}
+
+template<typename T>
+void
+test_requires ()
+{
+ int x;
+
+ [x = 1]() requires is_same<const int &, decltype((x))> {} ();
+ [x = 1]() mutable requires is_same<int &, decltype((x))> {}
+ ();
+ [x]() requires is_same<const int &, decltype((x))> {} ();
+ [x]() mutable requires is_same<int &, decltype((x))> {}
+ ();
+ [=]() requires is_same<const int &, decltype((x))> {} ();
+ [=]() mutable requires is_same<int &, decltype((x))> {}
+ ();
+ [&]() requires is_same<int &, decltype((x))> {}
+ ();
+ [&]() mutable requires is_same<int &, decltype((x))> {}
+ ();
+ [&x]() requires is_same<int &, decltype((x))> {}
+ ();
+ [&x]() mutable requires is_same<int &, decltype((x))> {}
+ ();
+
+ [x = 1]() requires is_same<const int &, decltype((x))> {} ();
+ [x = 1]() mutable requires is_same<int &, decltype((x))> {} ();
+}
+
+void
+use ()
+{
+ test_requires<int>();
+ check_params_tpl<int>();
+}
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+template <typename T>
+inline constexpr auto
+equal1 (T &&t)
+{
+ return [t = 3](const auto& obj) -> decltype(obj == t)
+ {
+ return obj == t;
+ };
+}
+
+template <typename T>
+inline constexpr auto
+equal2 (T &&t)
+{
+ return [t = t](const auto& obj) -> decltype(obj == t)
+ {
+ return obj == t;
+ };
+}
+
+template <typename T>
+inline constexpr auto
+equal3 (T &&t)
+{
+ return [t = 4](const auto& obj) -> decltype(obj == t)
+ {
+ return obj == t;
+ };
+}
+
+void
+g ()
+{
+ constexpr auto l1 = equal1 (5);
+ static_assert (l1 (3));
+ constexpr auto l2 = equal2 (3);
+ static_assert (l2 (3));
+ constexpr auto l3 = equal3 (2);
+ static_assert (l3 (4));
+}
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+struct integral_constant {
+ using type = integral_constant;
+};
+template <bool> using __bool_constant = integral_constant;
+template <typename _Fn, typename>
+struct is_invocable : __bool_constant<true> {};
+int forward() { return 42; }
+template <typename...> class tuple;
+struct plus {
+ template <typename _Tp, typename _Up>
+ constexpr auto operator()(_Tp __t, _Up __u) {
+ return __t > __u;
+ }
+};
+constexpr auto equal() {
+ int t = 0;
+ return [t = 3](auto obj) -> decltype(obj == t) { return t; };
+}
+template <typename> struct is_tuple_invocable;
+template <typename... Ts> struct is_tuple_invocable<tuple<Ts...>> {
+ using type = typename is_invocable<Ts...>::type;
+};
+namespace detail {
+template <typename F, typename Tail, typename... T>
+constexpr auto compose(__bool_constant<true>, F f, Tail tail, T... objs) {
+ return f(tail(objs...));
+}
+} // namespace detail
+template <typename F, typename... Fs> constexpr auto compose(F f, Fs... fs) {
+ return [f, tail(fs...)](auto... objs) {
+ auto unitail =
+ typename is_tuple_invocable<tuple<decltype(objs)...>>::type{};
+ return detail::compose(unitail, f, tail, objs...);
+ };
+}
+template <auto> constexpr auto eq = equal();
+static_assert(compose(eq<3>, plus{})(1, 2));
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+struct integral_constant {
+ using type = integral_constant;
+};
+template <bool> using __bool_constant = integral_constant;
+template <typename _Fn, typename>
+struct is_invocable : __bool_constant<true> {};
+int forward() { return 42; }
+template <typename...> class tuple;
+struct plus {
+ template <typename _Tp, typename _Up>
+ constexpr auto operator()(_Tp __t, _Up __u) {
+ return __t > __u;
+ }
+};
+constexpr auto equal() {
+ int t = 0;
+ return [t = 3](auto obj) -> decltype(obj == t) { return t; };
+}
+template <typename> struct is_tuple_invocable;
+template <typename... Ts> struct is_tuple_invocable<tuple<Ts...>> {
+ using type = typename is_invocable<Ts...>::type;
+};
+namespace detail {
+template <typename F, typename Tail, typename... T>
+constexpr auto compose(__bool_constant<true>, F f, Tail tail, T... objs) {
+ return f(tail(objs...));
+}
+} // namespace detail
+template <typename F, typename... Fs>
+constexpr auto compose(F f, Fs... fs) {
+ return [f, tail(fs...)](auto... objs) -> decltype (detail::compose(typename is_tuple_invocable<tuple<decltype(objs)...>>::type{}, f, tail, objs...)) {
+ auto unitail =
+ typename is_tuple_invocable<tuple<decltype(objs)...>>::type{};
+ return detail::compose(unitail, f, tail, objs...);
+ };
+}
+template <auto> constexpr auto eq = equal();
+static_assert(compose(eq<3>, plus{})(1, 2));
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++17 } }
+
+constexpr auto equal() {
+ int t = 0;
+ return [t](auto obj) { return obj; };
+}
+template <typename F>
+constexpr int bar (F) {
+ return 42;
+}
+
+template <typename F>
+constexpr auto compose(F f)
+{
+ return [f=f](int i) -> decltype(bar (f)) {
+ return 42;
+ };
+}
+template <auto> constexpr auto eq = equal();
+static_assert(compose(eq<3>)(1));
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+
+void
+g ()
+{
+ /* It looks like this shouldn't work but [expr.prim.lambda.closure]/6
+ says "Otherwise, it is a non-static member function or member function
+ template that is declared const if and only if the lambda-expression's
+ parameter-declaration-clause is not followed by mutable and the
+ lambda-declarator does not contain an explicit object parameter." */
+ auto counter = [j=0](this auto const& self) -> decltype(j) {
+ return j++;
+ };
+
+ auto counter2 = [j=0](this auto& self) -> decltype(j) {
+ return j++;
+ };
+}
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++14_only } }
+
+template <typename T>
+inline constexpr auto
+equal1 (T &&t)
+{
+ return [t = 3](const auto& obj) -> decltype(obj == t)
+ {
+ return obj == t;
+ };
+}
+
+void
+g ()
+{
+ constexpr auto l1 = equal1 (5); // { dg-error "not literal" }
+ static_assert (l1 (3), ""); // { dg-error "non-constant|non-.constexpr." }
+}
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+// { dg-options "-Wshadow" }
+
+void
+bad ()
+{
+ [x=1](int x){}; // { dg-error "declared as a capture" }
+ // { dg-warning "shadows a lambda capture" "" { target *-*-* } .-1 }
+ [x=1]{ int x; }; // { dg-error "shadows a parameter" }
+
+ auto f = [i = 5] () { int i; return 0; }; // { dg-error "shadows a parameter" }
+ auto f2 = [i = 5] <int N> () { int i; return 0; }; // { dg-error "shadows a parameter" }
+
+ // [expr.prim.lambda.capture]/5
+ int x = 0;
+ auto g = [x](int x) { return 0; }; // { dg-error "declared as a capture" }
+ // { dg-warning "shadows a lambda capture" "" { target *-*-* } .-1 }
+ auto h = [y = 0]<typename y>(y) { return 0; }; // { dg-error "shadows template parameter" }
+
+ auto l2 = [i = 0, j = i]() -> decltype(i) { // { dg-error "not declared in this scope" }
+ return i;
+ };
+}
--- /dev/null
+// P2036R3 - Change scope of lambda trailing-return-type
+// PR c++/102610
+// { dg-do compile { target c++23 } }
+// { dg-options "" }
+
+constexpr char f(auto a) { return 'a'; }
+
+namespace A {
+ int i = 42;
+ template<char X = f([i]{})> void g() { } // { dg-warning "capture of variable .A::i. with non-automatic storage duration" }
+}
+
+namespace B {
+ void call() { A::g(); }
+}
// { dg-do compile }
-// { dg-options "-Wshadow" }
+// { dg-options "-Wshadow -Wpedantic" }
void
foo (int x)
extern int y; // { dg-warning "declaration of 'y' shadows a previous local" }
}
#if __cplusplus >= 201102L
- auto fn = [x] () { extern int x; return 0; }; // { dg-warning "declaration of 'x' shadows a lambda capture" "" { target c++11 } }
+ auto fn = [x] () { extern int x; return 0; }; // { dg-warning "declaration of 'int x' shadows a parameter" "" { target c++11 } }
#endif
}
void f3(int i) {
[=]{
- int j = i; // { dg-message "shadowed declaration" }
- int i; // { dg-warning "shadows a lambda capture" }
+ int j = i; // { dg-message "previously declared here" }
+ int i; // { dg-error "shadows a parameter" }
i = 1;
};
}
template <class T>
void f4(int i) {
[=]{
- int j = i; // { dg-message "shadowed declaration" }
- int i; // { dg-warning "shadows a " }
+ int j = i; // { dg-message "previously declared here" }
+ int i; // { dg-error "shadows a parameter" }
i = 1;
};
}