diff options
author | Jeff Chapman II <jchapman@lock3software.com> | 2019-10-30 22:31:48 -0400 |
---|---|---|
committer | Jason Merrill <jason@gcc.gnu.org> | 2019-10-30 22:31:48 -0400 |
commit | e4c431266f9aaf604edfca68e852ae8efe966e8e (patch) | |
tree | 7f8442e216b2d8722a38b040707deedc3c3da7cd /gcc | |
parent | 56e0346dcb882b07199b8b19616b52f9667e356f (diff) | |
download | gcc-e4c431266f9aaf604edfca68e852ae8efe966e8e.zip gcc-e4c431266f9aaf604edfca68e852ae8efe966e8e.tar.gz gcc-e4c431266f9aaf604edfca68e852ae8efe966e8e.tar.bz2 |
PR c++/84810 - constraints on lambdas
Attached is a patch that adds parsing of the optional requires-clause in a
lambda-expression and lambda-declarator. Additionally, shorthand constraints
from the template-parameter-list are now actually applied and constrain the
synthesized operator().
Previously we were not parsing the requires clauses at all and not saving
the shorthand constraints in the place expected by grokfndecl.
The trailing requires-clause is now also used to suppress synthesis of the
conversion to function pointer for non-capturing non-generic lambdas as per
expr.prim.lambda.closure/7.
This includes a fix to template_class_depth. Previously it was computing the
wrong depth for lambdas in the initializer of a static member of a class
template, exhibited by the concepts-lambda4 test which currently fails on
trunk. The bug was causing grokfndecl to use the constraints from the
template class for the lambda.
gcc/cp/
2019-10-30 Jeff Chapman II <jchapman@lock3software.com>
PR c++/84810 - constraints on lambdas
* lambda.c (maybe_add_lambda_conv_op): Do not synthesize
conversion if the call operator does not satisfy its constraints.
* parser.c (cp_parser_lambda_declarator_opt): Parse
requires-clause on generic lambdas; combine with shorthand
constraints. Parse trailing requires-clause and attach to the
synthesized call operator.
* pt.c (template_class_depth): Only inspect
LAMBDA_TYPE_EXTRA_SCOPE if it is present. This fixes an
incorrect depth calculation for lambdas inside the initializer
of a static data member of a template class.
gcc/testsuite/
2019-10-30 Jeff Chapman II <jchapman@lock3software.com>
PR c++/84810 - constraints on lambdas
* g++.dg/cpp2a/concepts-lambda2.C: New test.
* g++.dg/cpp2a/concepts-lambda3.C: Ditto.
* g++.dg/cpp2a/concepts-lambda4.C: Ditto.
* g++.dg/cpp2a/concepts-pr84810.C: Ditto.
From-SVN: r277655
Diffstat (limited to 'gcc')
-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; +} |