PR c++/86429 - constexpr variable in lambda.
authorJason Merrill <jason@redhat.com>
Tue, 26 Mar 2019 16:02:19 +0000 (12:02 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Tue, 26 Mar 2019 16:02:19 +0000 (12:02 -0400)
When we refer to a captured variable from a constant-expression context
inside a lambda, the closure (like any function parameter) is not constant
because we aren't in a call, so we don't have an argument.  So the capture
is non-constant.  But if the captured variable is constant, we might be able
to use it directly in constexpr evaluation.

PR c++/82643
PR c++/87327
* constexpr.c (cxx_eval_constant_expression): In a lambda function,
try evaluating the captured variable directly.

From-SVN: r269951

gcc/cp/ChangeLog
gcc/cp/constexpr.c
gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1y/lambda-generic-const9.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp1z/constexpr-lambda24.C [new file with mode: 0644]

index 80d4ae3f1b940f0716046f82c203a576ac9ad43d..550b7541d9fc4fb6b2331ad806e19d19ad05549f 100644 (file)
@@ -1,3 +1,11 @@
+2019-03-26  Jason Merrill  <jason@redhat.com>
+
+       PR c++/86429 - constexpr variable in lambda.
+       PR c++/82643
+       PR c++/87327
+       * constexpr.c (cxx_eval_constant_expression): In a lambda function,
+       try evaluating the captured variable directly.
+
 2019-03-26  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/89796
index e92ec55317b6fcfbfb69429e70622806662d85a1..c00d642fcfeda2167d250ca834cec8fae394a2f1 100644 (file)
@@ -4442,8 +4442,29 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
 
     case VAR_DECL:
       if (DECL_HAS_VALUE_EXPR_P (t))
-       return cxx_eval_constant_expression (ctx, DECL_VALUE_EXPR (t),
-                                            lval, non_constant_p, overflow_p);
+       {
+         if (is_normal_capture_proxy (t)
+             && current_function_decl == DECL_CONTEXT (t))
+           {
+             /* Function parms aren't constexpr within the function
+                definition, so don't try to look at the closure.  But if the
+                captured variable is constant, try to evaluate it directly. */
+             r = DECL_CAPTURED_VARIABLE (t);
+             tree type = TREE_TYPE (t);
+             if (TYPE_REF_P (type) != TYPE_REF_P (TREE_TYPE (r)))
+               {
+                 /* Adjust r to match the reference-ness of t.  */
+                 if (TYPE_REF_P (type))
+                   r = build_address (r);
+                 else
+                   r = convert_from_reference (r);
+               }
+           }
+         else
+           r = DECL_VALUE_EXPR (t);
+         return cxx_eval_constant_expression (ctx, r, lval, non_constant_p,
+                                              overflow_p);
+       }
       /* fall through */
     case CONST_DECL:
       /* We used to not check lval for CONST_DECL, but darwin.c uses
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C
new file mode 100644 (file)
index 0000000..e0080b3
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/82643
+// { dg-do compile { target c++14 } }
+
+int main()
+{
+  struct A {
+    constexpr int operator()() const { return 42; }
+  };
+
+  auto f = A();
+  constexpr auto x = f(); //ok, call constexpr const non-static method
+
+  [](auto const &f) {
+    constexpr auto x = f(); /*ok*/
+  }(f);
+
+  [&]() {
+    constexpr auto x = f(); //ko, __closure is not a constant expression
+  };
+
+  [=]() {
+    constexpr auto x = f(); //same ko, __closure is not a constant expression
+  };
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const9.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const9.C
new file mode 100644 (file)
index 0000000..491c7c3
--- /dev/null
@@ -0,0 +1,16 @@
+// PR c++/86429
+// { dg-do compile { target c++14 } }
+
+struct A
+{
+  int i;
+  constexpr int f(const int&) const { return i; }
+};
+
+void g()
+{
+  constexpr A a = { 42 };
+  [&](auto x) {
+    constexpr auto y = a.f(x);
+  }(24);
+}
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda24.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda24.C
new file mode 100644 (file)
index 0000000..2edb24e
--- /dev/null
@@ -0,0 +1,23 @@
+// PR c++/87327
+// { dg-do compile { target c++17 } }
+
+template <int N>
+struct Foo {
+    constexpr auto size() const {
+        return N;
+    }
+};
+
+constexpr int foo() {
+    constexpr auto a = Foo<5>{};
+
+    [&] {
+        Foo<a.size()> it = {};
+
+        return it;
+    }();
+
+    return 42;
+}
+
+constexpr int i = foo();