c++: constrained lambda inside template [PR92633]

Message ID 20200601164718.1522132-1-ppalka@redhat.com
State New
Headers show
Series
  • c++: constrained lambda inside template [PR92633]
Related show

Commit Message

Kees Cook via Gcc-patches June 1, 2020, 4:47 p.m.
When regenerating a constrained lambda during instantiation of an
enclosing template, we are forgetting to substitute into the lambda's
constraints.  Fix this by substituting through the constraints during
tsubst_lambda_expr.

Passes 'make check-c++', and also tested by building the testsuites of
cmcstl2 and range-v3.  Does this look OK to commit to master and to the
10 branch after a full bootstrap and regtest?

gcc/cp/ChangeLog:

	PR c++/92633
	PR c++/92838
	* pt.c (tsubst_function_decl): Don't do set_constraints when
	regenerating a lambda.
	(tsubst_lambda_expr): Substitute into the lambda's constraints
	and do set_constraints here.

gcc/testsuite/ChangeLog:

	PR c++/92633
	PR c++/92838
	* g++.dg/cpp2a/concepts-lambda11.C: New test.
	* g++.dg/cpp2a/concepts-lambda12.C: New test.
---
 gcc/cp/pt.c                                    | 16 +++++++++++++++-
 gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C | 17 +++++++++++++++++
 gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C | 15 +++++++++++++++
 3 files changed, 47 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C

-- 
2.27.0.rc1.5.gae92ac8ae3

Comments

Kees Cook via Gcc-patches June 1, 2020, 5:43 p.m. | #1
On 6/1/20 12:47 PM, Patrick Palka wrote:
> When regenerating a constrained lambda during instantiation of an

> enclosing template, we are forgetting to substitute into the lambda's

> constraints.  Fix this by substituting through the constraints during

> tsubst_lambda_expr.

> 

> Passes 'make check-c++', and also tested by building the testsuites of

> cmcstl2 and range-v3.  Does this look OK to commit to master and to the

> 10 branch after a full bootstrap and regtest?


OK for both.

> gcc/cp/ChangeLog:

> 

> 	PR c++/92633

> 	PR c++/92838

> 	* pt.c (tsubst_function_decl): Don't do set_constraints when

> 	regenerating a lambda.

> 	(tsubst_lambda_expr): Substitute into the lambda's constraints

> 	and do set_constraints here.

> 

> gcc/testsuite/ChangeLog:

> 

> 	PR c++/92633

> 	PR c++/92838

> 	* g++.dg/cpp2a/concepts-lambda11.C: New test.

> 	* g++.dg/cpp2a/concepts-lambda12.C: New test.

> ---

>   gcc/cp/pt.c                                    | 16 +++++++++++++++-

>   gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C | 17 +++++++++++++++++

>   gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C | 15 +++++++++++++++

>   3 files changed, 47 insertions(+), 1 deletion(-)

>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C

>   create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C

> 

> diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c

> index df647af7b46..907ca879c73 100644

> --- a/gcc/cp/pt.c

> +++ b/gcc/cp/pt.c

> @@ -13854,7 +13854,10 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,

>        don't substitute through the constraints; that's only done when

>        they are checked.  */

>     if (tree ci = get_constraints (t))

> -    set_constraints (r, ci);

> +    /* Unless we're regenerating a lambda, in which case we'll set the

> +       lambda's constraints in tsubst_lambda_expr.  */

> +    if (!lambda_fntype)

> +      set_constraints (r, ci);

>   

>     if (DECL_FRIEND_P (t) && DECL_FRIEND_CONTEXT (t))

>       SET_DECL_FRIEND_CONTEXT (r,

> @@ -19029,6 +19032,17 @@ tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)

>   	  finish_member_declaration (fn);

>   	}

>   

> +      if (tree ci = get_constraints (oldfn))

> +	{

> +	  /* Substitute into the lambda's constraints.  */

> +	  if (oldtmpl)

> +	    ++processing_template_decl;

> +	  ci = tsubst_constraint_info (ci, args, complain, in_decl);

> +	  if (oldtmpl)

> +	    --processing_template_decl;

> +	  set_constraints (fn, ci);

> +	}

> +

>         /* Let finish_function set this.  */

>         DECL_DECLARED_CONSTEXPR_P (fn) = false;

>   

> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C

> new file mode 100644

> index 00000000000..dd9cd4e2344

> --- /dev/null

> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C

> @@ -0,0 +1,17 @@

> +// PR c++/92838

> +// { dg-do compile { target c++20 } }

> +

> +template<int N>

> +auto foo()

> +{

> +  [] () requires (N != 0) { }(); // { dg-error "no match" }

> +  [] () requires (N == 0) { }();

> +

> +  [] <int M=1> () requires (N == M) { }(); // { dg-error "no match" }

> +  [] <int M=1> () requires (N != M) { }();

> +}

> +

> +void bar()

> +{

> +  foo<0>();

> +}

> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C

> new file mode 100644

> index 00000000000..2bc9fd0bb25

> --- /dev/null

> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C

> @@ -0,0 +1,15 @@

> +// PR c++/92633

> +// { dg-do compile { target c++20 } }

> +

> +template<class A, class B>

> +concept different_than = !__is_same_as(A, B);

> +

> +template<class B>

> +auto diff(B) {

> +    return [](different_than<B> auto a) {};

> +}

> +

> +int main() {

> +    diff(42)("");

> +    diff(42)(42); // { dg-error "no match" }

> +}

>

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index df647af7b46..907ca879c73 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -13854,7 +13854,10 @@  tsubst_function_decl (tree t, tree args, tsubst_flags_t complain,
      don't substitute through the constraints; that's only done when
      they are checked.  */
   if (tree ci = get_constraints (t))
-    set_constraints (r, ci);
+    /* Unless we're regenerating a lambda, in which case we'll set the
+       lambda's constraints in tsubst_lambda_expr.  */
+    if (!lambda_fntype)
+      set_constraints (r, ci);
 
   if (DECL_FRIEND_P (t) && DECL_FRIEND_CONTEXT (t))
     SET_DECL_FRIEND_CONTEXT (r,
@@ -19029,6 +19032,17 @@  tsubst_lambda_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 	  finish_member_declaration (fn);
 	}
 
+      if (tree ci = get_constraints (oldfn))
+	{
+	  /* Substitute into the lambda's constraints.  */
+	  if (oldtmpl)
+	    ++processing_template_decl;
+	  ci = tsubst_constraint_info (ci, args, complain, in_decl);
+	  if (oldtmpl)
+	    --processing_template_decl;
+	  set_constraints (fn, ci);
+	}
+
       /* Let finish_function set this.  */
       DECL_DECLARED_CONSTEXPR_P (fn) = false;
 
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C
new file mode 100644
index 00000000000..dd9cd4e2344
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda11.C
@@ -0,0 +1,17 @@ 
+// PR c++/92838
+// { dg-do compile { target c++20 } }
+
+template<int N>
+auto foo()
+{
+  [] () requires (N != 0) { }(); // { dg-error "no match" }
+  [] () requires (N == 0) { }();
+
+  [] <int M=1> () requires (N == M) { }(); // { dg-error "no match" }
+  [] <int M=1> () requires (N != M) { }();
+}
+
+void bar()
+{
+  foo<0>();
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C
new file mode 100644
index 00000000000..2bc9fd0bb25
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-lambda12.C
@@ -0,0 +1,15 @@ 
+// PR c++/92633
+// { dg-do compile { target c++20 } }
+
+template<class A, class B>
+concept different_than = !__is_same_as(A, B);
+
+template<class B>
+auto diff(B) {
+    return [](different_than<B> auto a) {};
+}
+
+int main() {
+    diff(42)("");
+    diff(42)(42); // { dg-error "no match" }
+}