From 0c1e0d63fe0ceabbd04384070f3b59f8bf50de09 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 12 Nov 2018 23:49:09 -0500 Subject: [PATCH] Implement P0315R4, Lambdas in unevaluated contexts. When lambdas were added in C++11 they were banned from unevaluated contexts as a way to avoid needing to deal with them in mangling or SFINAE. This proposal avoids that with a more narrow proposal: lambdas never compare as equivalent (so we don't need to mangle them), and substitution failures within a lambda are hard errors. Lambdas appearing in places that types couldn't previously have been declared introduces various complications; in particular, it seems likely to mean types with no linkage being used more broadly, risking ODR violations. I want to follow up this patch with some related diagnostics. * decl2.c (min_vis_expr_r): Handle LAMBDA_EXPR. * mangle.c (write_expression): Handle LAMBDA_EXPR. * parser.c (cp_parser_lambda_expression): Allow lambdas in unevaluated context. Start the tentative firewall sooner. (cp_parser_lambda_body): Use cp_evaluated. * pt.c (iterative_hash_template_arg): Handle LAMBDA_EXPR. (tsubst_function_decl): Substitute a lambda even if it isn't dependent. (tsubst_lambda_expr): Use cp_evaluated. Always complain. (tsubst_copy_and_build) [LAMBDA_EXPR]: Do nothing if tf_partial. * semantics.c (begin_class_definition): Allow in template parm list. * tree.c (strip_typedefs_expr): Pass through LAMBDA_EXPR. (cp_tree_equal): Handle LAMBDA_EXPR. From-SVN: r266056 --- gcc/cp/ChangeLog | 15 ++++++ gcc/cp/decl2.c | 1 + gcc/cp/mangle.c | 10 ++++ gcc/cp/parser.c | 22 +++++--- gcc/cp/pt.c | 28 ++++++++-- gcc/cp/semantics.c | 6 --- gcc/cp/tree.c | 29 +++++----- .../g++.dg/cpp0x/lambda/lambda-ice6.C | 2 +- .../g++.dg/cpp0x/lambda/lambda-sfinae1.C | 9 ++-- .../g++.dg/cpp0x/lambda/lambda-uneval.C | 2 +- .../g++.dg/cpp0x/lambda/lambda-uneval2.C | 5 +- gcc/testsuite/g++.dg/cpp2a/lambda-uneval1.C | 16 ++++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval2.C | 54 +++++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval3.C | 12 +++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval4.C | 8 +++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval5.C | 5 ++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval6.C | 26 +++++++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval7.C | 12 +++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval8.C | 13 +++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.C | 12 +++++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.cc | 3 ++ gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.h | 9 ++++ 22 files changed, 262 insertions(+), 37 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval6.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval7.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval8.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.cc create mode 100644 gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.h diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 5cd147114a0..7e80c4e69ac 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,20 @@ 2018-11-12 Jason Merrill + Implement P0315R4, Lambdas in unevaluated contexts. + * decl2.c (min_vis_expr_r): Handle LAMBDA_EXPR. + * mangle.c (write_expression): Handle LAMBDA_EXPR. + * parser.c (cp_parser_lambda_expression): Allow lambdas in + unevaluated context. Start the tentative firewall sooner. + (cp_parser_lambda_body): Use cp_evaluated. + * pt.c (iterative_hash_template_arg): Handle LAMBDA_EXPR. + (tsubst_function_decl): Substitute a lambda even if it isn't + dependent. + (tsubst_lambda_expr): Use cp_evaluated. Always complain. + (tsubst_copy_and_build) [LAMBDA_EXPR]: Do nothing if tf_partial. + * semantics.c (begin_class_definition): Allow in template parm list. + * tree.c (strip_typedefs_expr): Pass through LAMBDA_EXPR. + (cp_tree_equal): Handle LAMBDA_EXPR. + * pt.c (fn_type_unification): If we have a full set of explicit arguments, go straight to substitution. diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index 74b9f4ee826..04537417129 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -2288,6 +2288,7 @@ min_vis_expr_r (tree *tp, int */*walk_subtrees*/, void *data) case DYNAMIC_CAST_EXPR: case NEW_EXPR: case CONSTRUCTOR: + case LAMBDA_EXPR: tpvis = type_visibility (TREE_TYPE (*tp)); break; diff --git a/gcc/cp/mangle.c b/gcc/cp/mangle.c index b9d8ee20116..64415894bc5 100644 --- a/gcc/cp/mangle.c +++ b/gcc/cp/mangle.c @@ -3139,6 +3139,16 @@ write_expression (tree expr) write_expression (val); write_char ('E'); } + else if (code == LAMBDA_EXPR) + { + /* [temp.over.link] Two lambda-expressions are never considered + equivalent. + + So just use the closure type mangling. */ + write_string ("tl"); + write_type (LAMBDA_EXPR_CLOSURE (expr)); + write_char ('E'); + } else if (dependent_name (expr)) { write_unqualified_id (dependent_name (expr)); diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index 0428f6dda90..db0f0338179 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10175,12 +10175,15 @@ cp_parser_lambda_expression (cp_parser* parser) LAMBDA_EXPR_LOCATION (lambda_expr) = token->location; - if (cp_unevaluated_operand) + if (cxx_dialect >= cxx2a) + /* C++20 allows lambdas in unevaluated context. */; + else if (cp_unevaluated_operand) { if (!token->error_reported) { error_at (LAMBDA_EXPR_LOCATION (lambda_expr), - "lambda-expression in unevaluated context"); + "lambda-expression in unevaluated context" + " only available with -std=c++2a or -std=gnu++2a"); token->error_reported = true; } ok = false; @@ -10189,7 +10192,8 @@ cp_parser_lambda_expression (cp_parser* parser) { if (!token->error_reported) { - error_at (token->location, "lambda-expression in template-argument"); + error_at (token->location, "lambda-expression in template-argument" + " only available with -std=c++2a or -std=gnu++2a"); token->error_reported = true; } ok = false; @@ -10200,6 +10204,8 @@ cp_parser_lambda_expression (cp_parser* parser) push_deferring_access_checks (dk_no_deferred); cp_parser_lambda_introducer (parser, lambda_expr); + if (cp_parser_error_occurred (parser)) + return error_mark_node; type = begin_lambda_type (lambda_expr); if (type == error_mark_node) @@ -10238,6 +10244,9 @@ cp_parser_lambda_expression (cp_parser* parser) /* By virtue of defining a local class, a lambda expression has access to the private variables of enclosing classes. */ + if (cp_parser_start_tentative_firewall (parser)) + start = token; + ok &= cp_parser_lambda_declarator_opt (parser, lambda_expr); if (ok && cp_parser_error_occurred (parser)) @@ -10245,9 +10254,6 @@ cp_parser_lambda_expression (cp_parser* parser) if (ok) { - if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_BRACE) - && cp_parser_start_tentative_firewall (parser)) - start = token; cp_parser_lambda_body (parser, lambda_expr); } else if (cp_parser_require (parser, CPP_OPEN_BRACE, RT_OPEN_BRACE)) @@ -10736,6 +10742,10 @@ cp_parser_lambda_body (cp_parser* parser, tree lambda_expr) bool local_variables_forbidden_p = parser->local_variables_forbidden_p; bool in_function_body = parser->in_function_body; + /* The body of a lambda-expression is not a subexpression of the enclosing + expression. */ + cp_evaluated ev; + if (nested) push_function_context (); else diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f948aef3776..b58ec06a09e 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -1814,10 +1814,11 @@ iterative_hash_template_arg (tree arg, hashval_t val) return iterative_hash_template_arg (TREE_OPERAND (arg, 2), val); case LAMBDA_EXPR: - /* A lambda can't appear in a template arg, but don't crash on - erroneous input. */ - gcc_assert (seen_error ()); - return val; + /* [temp.over.link] Two lambda-expressions are never considered + equivalent. + + So just hash the closure type. */ + return iterative_hash_template_arg (TREE_TYPE (arg), val); case CAST_EXPR: case IMPLICIT_CONV_EXPR: @@ -12842,7 +12843,8 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, if (TREE_CODE (DECL_TI_TEMPLATE (t)) == TEMPLATE_DECL) { /* If T is not dependent, just return it. */ - if (!uses_template_parms (DECL_TI_ARGS (t))) + if (!uses_template_parms (DECL_TI_ARGS (t)) + && !LAMBDA_FUNCTION_P (t)) return t; /* Calculate the most general template of which R is a @@ -17957,6 +17959,10 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) /* Let finish_function set this. */ DECL_DECLARED_CONSTEXPR_P (fn) = false; + /* The body of a lambda-expression is not a subexpression of the + enclosing expression. */ + cp_evaluated ev; + bool nested = cfun; if (nested) push_function_context (); @@ -17992,6 +17998,11 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) current_function_infinite_loop = ol->infinite_loop; } + /* [temp.deduct] A lambda-expression appearing in a function type or a + template parameter is not considered part of the immediate context for + the purposes of template argument deduction. */ + complain = tf_warning_or_error; + tsubst_expr (DECL_SAVED_TREE (oldfn), args, complain, r, /*constexpr*/false); @@ -19285,6 +19296,13 @@ tsubst_copy_and_build (tree t, case LAMBDA_EXPR: { + if (complain & tf_partial) + { + /* We don't have a full set of template arguments yet; don't touch + the lambda at all. */ + gcc_assert (processing_template_decl); + return t; + } tree r = tsubst_lambda_expr (t, args, complain, in_decl); RETURN (build_lambda_object (r)); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 182d360a562..20fd9c47f1f 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -2988,12 +2988,6 @@ begin_class_definition (tree t) if (error_operand_p (t) || error_operand_p (TYPE_MAIN_DECL (t))) return error_mark_node; - if (processing_template_parmlist) - { - error ("definition of %q#T inside template parameter list", t); - return error_mark_node; - } - /* According to the C++ ABI, decimal classes defined in ISO/IEC TR 24733 are passed the same as decimal scalar types. */ if (TREE_CODE (t) == RECORD_TYPE diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 5e21fce54a8..02a9856acbf 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -1834,8 +1834,7 @@ strip_typedefs_expr (tree t, bool *remove_attributes) } case LAMBDA_EXPR: - error ("lambda-expression in a constant expression"); - return error_mark_node; + return t; case STATEMENT_LIST: error ("statement-expression in a constant expression"); @@ -2777,7 +2776,18 @@ no_linkage_check (tree t, bool relaxed_p) { tree r; - /* There's no point in checking linkage on template functions; we + /* Lambda types that don't have mangling scope have no linkage. We + check CLASSTYPE_LAMBDA_EXPR for error_mark_node because + when we get here from pushtag none of the lambda information is + set up yet, so we want to assume that the lambda has linkage and + fix it up later if not. We need to check this even in templates so + that we properly handle a lambda-expression in the signature. */ + if (LAMBDA_TYPE_P (t) + && CLASSTYPE_LAMBDA_EXPR (t) != error_mark_node + && LAMBDA_TYPE_EXTRA_SCOPE (t) == NULL_TREE) + return t; + + /* Otherwise there's no point in checking linkage on template functions; we can't know their complete types. */ if (processing_template_decl) return NULL_TREE; @@ -2787,15 +2797,6 @@ no_linkage_check (tree t, bool relaxed_p) case RECORD_TYPE: if (TYPE_PTRMEMFUNC_P (t)) goto ptrmem; - /* Lambda types that don't have mangling scope have no linkage. We - check CLASSTYPE_LAMBDA_EXPR for error_mark_node because - when we get here from pushtag none of the lambda information is - set up yet, so we want to assume that the lambda has linkage and - fix it up later if not. */ - if (CLASSTYPE_LAMBDA_EXPR (t) - && CLASSTYPE_LAMBDA_EXPR (t) != error_mark_node - && LAMBDA_TYPE_EXTRA_SCOPE (t) == NULL_TREE) - return t; /* Fall through. */ case UNION_TYPE: if (!CLASS_TYPE_P (t)) @@ -3849,6 +3850,10 @@ cp_tree_equal (tree t1, tree t2) DECL_NAME (t2))); return false; + case LAMBDA_EXPR: + /* Two lambda-expressions are never considered equivalent. */ + return false; + default: break; } diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice6.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice6.C index 408af4203ba..67b669f78d7 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice6.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-ice6.C @@ -1,4 +1,4 @@ // PR c++/51464 // { dg-do compile { target c++11 } } -template struct A {}; // { dg-error "lambda" } +template struct A {}; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C index 40abcb99a57..5928894e4b9 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-sfinae1.C @@ -8,7 +8,7 @@ struct AddRvalueReferenceImpl { typedef T type; }; template struct AddRvalueReferenceImpl::type> { typedef T &&type; @@ -19,7 +19,7 @@ struct AddRvalueReference : AddRvalueReferenceImpl { }; namespace ImplHelpers { template - typename AddRvalueReference::type create(void) { } + typename AddRvalueReference::type create(void); } template @@ -27,9 +27,8 @@ struct IsConstructibleImpl { enum { value = 0 }; }; template struct IsConstructibleImpl() ...); - }>::type, Args ...> { + [] { T t( ::ImplHelpers::create() ...); } // { dg-error "" } + >::type, Args ...> { enum { value = 1 }; }; diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval.C index dcea1690dde..1b1f03e4773 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval.C @@ -3,7 +3,7 @@ template struct A { }; -A a; // { dg-error "lambda.*unevaluated context" } +A a; // { dg-error "lambda.*unevaluated context" "" { target c++17_down } } // { dg-prune-output "template argument" } // { dg-prune-output "invalid type" } diff --git a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval2.C b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval2.C index 14cb2987420..f887a7d9e44 100644 --- a/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval2.C +++ b/gcc/testsuite/g++.dg/cpp0x/lambda/lambda-uneval2.C @@ -3,5 +3,8 @@ struct A { - decltype( [](){ return this; }() ) x; // { dg-error "unevaluated" } + decltype( [](){ return this; }() ) x; // { dg-error "unevaluated" "" { target c++17_down } } + // { dg-error "not captured" "" { target c++2a } .-1 } }; + +// { dg-prune-output "declared void" } diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval1.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval1.C new file mode 100644 index 00000000000..8a1703304ab --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval1.C @@ -0,0 +1,16 @@ +// { dg-do compile { target c++2a } } + +typedef decltype([]{}) C; // the closure type has no name for linkage purposes + +// { dg-final { scan-assembler-not "globl\[ \t]*_Z1f" } } +// { dg-final { scan-assembler-not "_Z1f1C" } } +void f(C) {} + +int main() +{ + C c; + c(); + f(c); +} + + diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval2.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval2.C new file mode 100644 index 00000000000..f29a59e26fd --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval2.C @@ -0,0 +1,54 @@ +// { dg-do compile { target c++2a } } + +// ill-formed, no diagnostic required: the two expressions are +// functionally equivalent but not equivalent +template void foo(const char (&s)[([]{}, N)]); +template void foo(const char (&s)[([]{}, N)]); + +// two different declarations because the non-dependent portions are not +// considered equivalent +template void spam(decltype([]{}) (*s)[sizeof(T)]); +template void spam(decltype([]{}) (*s)[sizeof(T)]); + +template +using A = decltype([] { }); +// A and A refer to different closure types + +template +auto f(T) -> decltype([]() { T::invalid; } ()); // { dg-error "invalid" } +void f(...); + +template // { dg-error "invalid" } +void g(T); +void g(...); + +template +auto h(T) -> decltype([x = T::invalid]() { }); +void h(...); + +template +auto i(T) -> decltype([]() -> typename T::invalid { }); +void i(...); + +template +auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t)); +void j(...); + +template struct different {}; +template struct different { typename T::invalid t; }; + +template struct same; +template struct same {}; + +int main() +{ + foo<1>(""); // { dg-error "ambiguous" } + spam(nullptr); // { dg-error "ambiguous" } + different,A>(); + same,A>(); + f(0); // error: invalid expression not part of the immediate context + g(0); // error: invalid expression not part of the immediate context + h(0); // error: invalid expression not part of the immediate context + i(0); // error: invalid expression not part of the immediate context + j(0); // deduction fails on #1, calls #2 +} diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval3.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval3.C new file mode 100644 index 00000000000..3c9b1e1cd45 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval3.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++2a } } + +template void foo(const char (*s)[([]{}, N)]) {} +template void spam(decltype([]{}) (*s)[sizeof(T)]) {} + +int main() +{ + foo<1>(nullptr); + spam(nullptr); +} + +// { dg-final { scan-assembler-not "weak.*_Z" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval4.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval4.C new file mode 100644 index 00000000000..e75a1270c72 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval4.C @@ -0,0 +1,8 @@ +// { dg-do link { target c++2a } } + +template T f(T t) { return t; } +using L = decltype([]{ return f(42); }); +int main() +{ + return L()(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval5.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval5.C new file mode 100644 index 00000000000..7fb05c3ce9c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval5.C @@ -0,0 +1,5 @@ +// { dg-do compile { target c++2a } } + +using L = decltype([]{ }); +void f(L) { } +// { dg-final { scan-assembler-not "globl.*_Z1f" } } diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval6.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval6.C new file mode 100644 index 00000000000..0396f9ff10d --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval6.C @@ -0,0 +1,26 @@ +// { dg-do compile { target c++2a } } + +static decltype([] { }) f(); +static decltype([] { }) f(); // { dg-error "ambiguating" } + +static decltype([] { }) g(); +static decltype(g()) g(); // okay + +static void h(decltype([] { }) *) { } +static void h(decltype([] { }) *) { } +void x1() { h(nullptr); } // { dg-error "ambiguous" } + +using A = decltype([] { }); +static void i(A *); +static void i(A *) { } +void x2() { i(nullptr); } // okay + +template +using B = decltype([] { }); +static void j(B *) { } +static void j(B *) { } +void x3() { j(nullptr); } // { dg-error "ambiguous" } + +template static void k(decltype([]{ return 0; }())); +template static void k(decltype([]{ return 0; }())); // okay +template static void k(int); // okay diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval7.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval7.C new file mode 100644 index 00000000000..41028824ce8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval7.C @@ -0,0 +1,12 @@ +// { dg-do compile { target c++2a } } + +template +struct A { }; + +template +void g(A<[]{return N;}()>) {} + +int main() +{ + g<1>({}); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval8.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval8.C new file mode 100644 index 00000000000..3692154f9be --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval8.C @@ -0,0 +1,13 @@ +// { dg-do compile { target c++2a } } + +template +struct A { + static constexpr auto n = N; +}; + +template +constexpr auto g(A<[]{return N;}> a) { + return a.n(); +} + +static_assert(g<42>({}) == 42); diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.C b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.C new file mode 100644 index 00000000000..e32d447819e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.C @@ -0,0 +1,12 @@ +// { dg-do run { target c++2a } } +// { dg-additional-sources "lambda-uneval9.cc" } + +#include "lambda-uneval9.h" +int foo() { return f(); } +extern int bar(); + +int main() +{ + if (foo() != 1) __builtin_abort(); + if (bar() != 2) __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.cc b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.cc new file mode 100644 index 00000000000..0ff3b126895 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.cc @@ -0,0 +1,3 @@ +#include "lambda-uneval9.h" + +int bar() { return f(); } diff --git a/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.h b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.h new file mode 100644 index 00000000000..0979748fd11 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/lambda-uneval9.h @@ -0,0 +1,9 @@ +// a.h: +template +int counter() { + static int cnt = 0; + return ++cnt; +} +inline int f() { + return counter(); +} -- 2.30.2