diff mbox series

[C++] PR c++/86429 - constexpr variable in lambda.

Message ID 20190326160210.17304-1-jason@redhat.com
State New
Headers show
Series [C++] PR c++/86429 - constexpr variable in lambda. | expand

Commit Message

Jason Merrill March 26, 2019, 4:02 p.m. UTC
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.

Tested x86_64-pc-linux-gnu, applying to trunk.

	PR c++/82643
	PR c++/87327
	* constexpr.c (cxx_eval_constant_expression): In a lambda function,
	try evaluating the captured variable directly.
---
 gcc/cp/constexpr.c                            | 25 +++++++++++++++++--
 .../g++.dg/cpp1y/lambda-generic-const10.C     | 24 ++++++++++++++++++
 .../g++.dg/cpp1y/lambda-generic-const9.C      | 16 ++++++++++++
 .../g++.dg/cpp1z/constexpr-lambda24.C         | 23 +++++++++++++++++
 gcc/cp/ChangeLog                              |  8 ++++++
 5 files changed, 94 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-const9.C
 create mode 100644 gcc/testsuite/g++.dg/cpp1z/constexpr-lambda24.C


base-commit: 7237dce709ba3deef79bd146a009cf0e727476d8
diff mbox series

Patch

diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index e92ec55317b..c00d642fcfe 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -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
index 00000000000..e0080b3d4f6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const10.C
@@ -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
index 00000000000..491c7c322c3
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-const9.C
@@ -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
index 00000000000..2edb24e41ac
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda24.C
@@ -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();
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 80d4ae3f1b9..550b7541d9f 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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