diff options
-rw-r--r-- | gcc/cp/lambda.c | 6 | ||||
-rw-r--r-- | gcc/cp/parser.c | 21 | ||||
-rw-r--r-- | gcc/cp/pt.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-lambda2.C | 153 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-lambda3.C | 64 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-lambda4.C | 14 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp2a/concepts-pr84810.C | 13 |
7 files changed, 270 insertions, 3 deletions
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index f128ed8..d621bec 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1046,6 +1046,12 @@ maybe_add_lambda_conv_op (tree type) return; } + /* Non-generic non-capturing lambdas only have a conversion function to + pointer to function when the trailing requires-clause's constraints are + satisfied. */ + if (!generic_lambda_p && !constraints_satisfied_p (callop)) + return; + /* Non-template conversion operators are defined directly with build_call_a and using DIRECT_ARGVEC for arguments (including 'this'). Templates are deferred and the CALL is built in-place. In the case of a deduced return diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index e29e99f..f1664e6 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -10835,11 +10835,13 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr) lambda-declarator: < template-parameter-list [opt] > + requires-clause [opt] ( parameter-declaration-clause [opt] ) attribute-specifier [opt] decl-specifier-seq [opt] exception-specification [opt] lambda-return-type-clause [opt] + requires-clause [opt] LAMBDA_EXPR is the current representation of the lambda expression. */ @@ -10858,6 +10860,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) tree template_param_list = NULL_TREE; tree tx_qual = NULL_TREE; tree return_type = NULL_TREE; + tree trailing_requires_clause = NULL_TREE; cp_decl_specifier_seq lambda_specs; clear_decl_specs (&lambda_specs); @@ -10877,9 +10880,20 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) cp_lexer_consume_token (parser->lexer); template_param_list = cp_parser_template_parameter_list (parser); - cp_parser_skip_to_end_of_template_parameter_list (parser); + /* We may have a constrained generic lambda; parse the requires-clause + immediately after the template-parameter-list and combine with any + shorthand constraints present. */ + tree dreqs = cp_parser_requires_clause_opt (parser); + if (flag_concepts) + { + tree reqs = get_shorthand_constraints (current_template_parms); + if (dreqs) + reqs = combine_constraint_expressions (reqs, dreqs); + TEMPLATE_PARMS_CONSTRAINTS (current_template_parms) = reqs; + } + /* We just processed one more parameter list. */ ++parser->num_template_parameter_lists; } @@ -10943,6 +10957,9 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) if (cp_next_tokens_can_be_gnu_attribute_p (parser)) gnu_attrs = cp_parser_gnu_attributes_opt (parser); + /* Parse optional trailing requires clause. */ + trailing_requires_clause = cp_parser_requires_clause_opt (parser); + /* The function parameters must be in scope all the way until after the trailing-return-type in case of decltype. */ pop_bindings_and_leave_scope (); @@ -10989,7 +11006,7 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) tx_qual, exception_spec, return_type, - /*requires_clause*/NULL_TREE); + trailing_requires_clause); declarator->std_attributes = std_attrs; fco = grokmethod (&return_type_specs, diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 414140a..419e8b8 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -383,7 +383,7 @@ template_class_depth (tree type) if (DECL_P (type)) type = CP_DECL_CONTEXT (type); - else if (LAMBDA_TYPE_P (type)) + else if (LAMBDA_TYPE_P (type) && LAMBDA_TYPE_EXTRA_SCOPE (type)) type = LAMBDA_TYPE_EXTRA_SCOPE (type); else type = CP_TYPE_CONTEXT (type); diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda2.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda2.C new file mode 100644 index 0000000..ffad95c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda2.C @@ -0,0 +1,153 @@ +// { dg-do compile { target c++2a } } + +template<typename T> +concept False = false; + +template<typename T> +concept C1 = __is_same_as(T, int) + || __is_same_as(T, long long) + || __is_same_as(T, char); + +template<typename T> +concept IsNotLarge = !__is_same_as(T, long long); + +template<typename T> +concept IsNotTiny = !__is_same_as(T, char); + +template<IsNotLarge T> +struct Foo +{ + static constexpr auto a = [](auto n) { return n; }; + template<IsNotTiny S> + auto b() + { + return [](False auto n) { return n; }; + } +}; + +template<IsNotTiny T> +struct Bar +{ + static constexpr auto a = + []<IsNotTiny R>(R t) { return t; }('a'); // { dg-error "no match" } + char b = + []<IsNotTiny R>(R t) { return t; }('b'); // { dg-error "no match" } + + static constexpr auto a2 = + [](char t) requires false { return t; }('a'); // { dg-error "no match" } + char b2 = + [](char t) requires false { return t; }('b'); // { dg-error "no match" } +}; + +template<IsNotLarge S> +S c = + []<IsNotTiny R>(R t) { return t; }('c'); // { dg-error "no match" } +template<IsNotLarge S> +S c2 = + [](char t) requires false { return t; }('c'); // { dg-error "no match" } + +Bar<long long> bar; + +void test0() +{ + auto bar_a = bar.a; + auto bar_b = bar.b; + auto c = ::c<char>; + auto bar_a2 = bar.a2; + auto bar_b2 = bar.b2; + auto c2 = ::c2<char>; + + auto g0 = []<False T>(T t) { return t; }; + auto g1 = []<typename T> requires False<T> (T t) { return t; }; + auto g2 = []<typename T>(T t) requires False<decltype(t)> { return t; }; + auto g3 = [](int t) requires False<decltype(t)> { return t; }; + auto g4 = [](False auto t) { return t; }; + auto g5 = [](auto t) requires False<decltype(t)> { return t; }; + auto g6 = [](int t) requires False<int> { return t; }; + auto g7 = [](int t) requires false { return t; }; + g0(0); // { dg-error "no match" } + g1(0); // { dg-error "no match" } + g2(0); // { dg-error "no match" } + g3(0); // { dg-error "no match" } + g4(0); // { dg-error "no match" } + g5(0); // { dg-error "no match" } + g6(0); // { dg-error "no match" } + g7(0); // { dg-error "no match" } +} + +void test1() +{ + int var{-1}; + auto g0 = [&]<False T>(T t) { return t; }; + auto g1 = [&]<typename T> requires False<T> (T t) { return t; }; + auto g2 = [&]<typename T>(T t) requires False<decltype(t)> { return t; }; + auto g3 = [&](int t) requires False<decltype(t)> { return t; }; + auto g4 = [&](False auto t) { return t; }; + auto g5 = [&](auto t) requires False<decltype(t)> { return t; }; + auto g6 = [&](int t) requires False<int> { return t; }; + auto g7 = [&](int t) requires false { return t; }; + g0(0); // { dg-error "no match" } + g1(0); // { dg-error "no match" } + g2(0); // { dg-error "no match" } + g3(0); // { dg-error "no match" } + g4(0); // { dg-error "no match" } + g5(0); // { dg-error "no match" } + g6(0); // { dg-error "no match" } + g7(0); // { dg-error "no match" } +} + +void test2() +{ + auto x = []<IsNotTiny T>(auto a, T t, auto b) + requires IsNotTiny<decltype(a)> && IsNotLarge<decltype(b)> + { return a + t + (T)b; }; + x(5LL, 2LL, 1); + + x('0', 2LL, 1LL); // { dg-error "no match" } + x(5LL, '0', 1LL); // { dg-error "no match" } + x(5LL, 2LL, 1LL); // { dg-error "no match" } +} + +void test3() +{ + auto x = []<IsNotTiny T>(IsNotTiny auto a, T t, IsNotLarge auto b) + { return a + t + (T)b; }; + x(5LL, 2LL, 1); + + x('0', 2LL, 1LL); // { dg-error "no match" } + x(5LL, '0', 1LL); // { dg-error "no match" } + x(5LL, 2LL, 1LL); // { dg-error "no match" } +} + +void test4() +{ + auto g = []<C1 T> requires IsNotTiny<T>(T t) -> T + requires IsNotLarge<decltype(t)> { return t; }; + g(5.5); // { dg-error "no match" } + g('a'); // { dg-error "no match" } + g(1LL); // { dg-error "no match" } +} + +void test5() +{ + Foo<int> foo1; + foo1.a(5.5); + foo1.a(1LL); + foo1.b<char>(); // { dg-error "no match" } + foo1.b<long long>()(5); // { dg-error "no match" } + + Foo<double> foo2; + foo2.a(5.5); + foo2.a(1LL); + foo2.b<char>(); // { dg-error "no match" } + foo2.b<long long>()(5); // { dg-error "no match" } +} + +using Func = int(*)(int); + +void test6() +{ + Func f1 = [](int a) requires false { return a; }; // { dg-error "cannot convert" } + Func f2 = [](auto a) requires false { return a; }; // { dg-error "cannot convert" } +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda3.C new file mode 100644 index 0000000..7e668ff --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda3.C @@ -0,0 +1,64 @@ +// { dg-do run { target c++2a } } + +template<typename T> +concept C1 = __is_same_as(T, int) + || __is_same_as(T, long long) + || __is_same_as(T, char); + +template<typename T> +concept IsNotLarge = !__is_same_as(T, long long); + +template<typename T> +concept IsNotTiny = !__is_same_as(T, char); + +template<IsNotLarge T> +struct Foo +{ + static constexpr auto a = [](auto n) { return n; }; + template<IsNotTiny S> + auto b() + { + return [](auto n) { return n; }; + } +}; + +using Func = int(*)(int); + +int main(int, char**) +{ + auto g = []<C1 T> requires IsNotTiny<T>(T t) -> T + requires IsNotLarge<decltype(t)> { return t; }; + g(5); + g.operator()<int>(5.5); + + auto z = []<typename T, int N = 5>(T t) requires (N < 4) { return t; }; + z.operator()<int, 3>(5); + + [](int t) requires true { return t; }(5); + [](C1 auto t) { return t; }(5); + + auto a0 = [](IsNotLarge auto a) { return [](auto b){ return b; }; }; + auto a1 = a0(1); + auto a2 = a1(5LL); + + auto b0 = [](auto a) { return [](IsNotLarge auto b){ return b; }; }; + auto b1 = b0(5LL); + auto b2 = b1(1); + + Foo<int> foo1; + foo1.a(5.5); + foo1.a(1LL); + foo1.b<int>()(5); + foo1.b<long long>()(5); + + Foo<double> foo2; + foo2.a(5.5); + foo2.a(1LL); + foo2.b<int>()(5); + foo2.b<long long>()(5); + + Func m1 = [](int a) -> int requires true { return a; }; + + return 0; +} + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda4.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda4.C new file mode 100644 index 0000000..dfb5f3b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda4.C @@ -0,0 +1,14 @@ +// { dg-do compile { target c++2a } } + +struct a {}; +template <bool> using b = a; + +template <typename> struct c; +template <typename d> + requires requires(d e) { e[0]; } +struct c<d> { + static constexpr bool f = [] { return false; }.operator()(); +}; + +b<c<unsigned[]>::f> b0{}; + diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-pr84810.C b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84810.C new file mode 100644 index 0000000..b330e4b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-pr84810.C @@ -0,0 +1,13 @@ +// { dg-do compile { target c++2a } } +template<class> constexpr bool is_int = false; +template<> constexpr bool is_int<int> = true; + +template <class T> +concept Int = is_int<T>; + +int main() { + auto x = []<Int T>(T t) { return 42; }; + auto y = x(42); + auto z = x(""); // { dg-error "no match" } + return z; +} |