aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/cp/lambda.c6
-rw-r--r--gcc/cp/parser.c21
-rw-r--r--gcc/cp/pt.c2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-lambda2.C153
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-lambda3.C64
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-lambda4.C14
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/concepts-pr84810.C13
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;
+}