]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
PR c++/84810 - constraints on lambdas
authorJeff Chapman II <jchapman@lock3software.com>
Thu, 31 Oct 2019 02:31:48 +0000 (22:31 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 31 Oct 2019 02:31:48 +0000 (22:31 -0400)
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

gcc/cp/lambda.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/testsuite/g++.dg/cpp2a/concepts-lambda2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-lambda3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-lambda4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/concepts-pr84810.C [new file with mode: 0644]

index f128ed800f68c1783776945b6a349650c68bdc5b..d621beca2ebb2817270c971f9d25f0ced71af03d 100644 (file)
@@ -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
index e29e99f418be40fc00677a6079bc6410ddda05fa..f1664e66087fce28a21def7c0b45feb54109f01e 100644 (file)
@@ -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,
index 414140ade6c661a4e45488a444e02117b0541afc..419e8b8fcd76af59124a705f83db4cf4fef61235 100644 (file)
@@ -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 (file)
index 0000000..ffad95c
--- /dev/null
@@ -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 (file)
index 0000000..7e668ff
--- /dev/null
@@ -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 (file)
index 0000000..dfb5f3b
--- /dev/null
@@ -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 (file)
index 0000000..b330e4b
--- /dev/null
@@ -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;
+}