c++: constrained CTAD for nested class template [PR97679]

Message ID 20210407213350.3190828-1-ppalka@redhat.com
State New
Headers show
Series
  • c++: constrained CTAD for nested class template [PR97679]
Related show

Commit Message

Martin Sebor via Gcc-patches April 7, 2021, 9:33 p.m.
In the testcase below, we're crashing during constraint checking of the
implicitly generated deduction guides for the nested class template A::B
because we never substitute the outer template arguments (for A) into
the constraint, neither ahead of time nor as part of satisfaction.

Ideally we'd like to avoid substituting into a constraint ahead of
time, but the "flattening" vector 'tsubst_args' is constructed under the
assumption that all outer template arguments are already substituted in,
and eliminating this assumption to yield a flattening vector that
includes outer (generic) template arguments suitable for substituting
into the constraint would be tricky.  So this patch takes the
approximate approach of substituting the outer arguments into the
constraint ahead of time, so that we could subsequently substitute
'tsubst_args' into the constraint.

NB: I noticed that [over.match.class.deduct]/1, which describes how
guides are formed from constructors of a class template, doesn't mention
that a guide inherits the constraints of the corresponding constructor.
Though [over.match.class.deduct]/2 later suggests that guides do have
constraints: "The associated constraints [of f'] are the conjunction of
the associated constraints of [the guide] g and ..."  So I'm unsure if
we're being conforming by giving guides the constraints of the
corresponding constructor.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
trunk and 10 branch (after 10.3 is released)?

gcc/cp/ChangeLog:

	PR c++/97679
	* pt.c (build_deduction_guide): Substitute outer template
	arguments into the constraints.

gcc/testsuite/ChangeLog:

	PR c++/97679
	* g++.dg/cpp2a/concepts-ctad3.C: New test.
---
 gcc/cp/pt.c                                 | 17 +++++++++++++++--
 gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C | 16 ++++++++++++++++
 2 files changed, 31 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C

-- 
2.31.1.189.g2e36527f23

Comments

Martin Sebor via Gcc-patches April 8, 2021, 3:09 p.m. | #1
On 4/7/21 5:33 PM, Patrick Palka wrote:
> In the testcase below, we're crashing during constraint checking of the

> implicitly generated deduction guides for the nested class template A::B

> because we never substitute the outer template arguments (for A) into

> the constraint, neither ahead of time nor as part of satisfaction.

> 

> Ideally we'd like to avoid substituting into a constraint ahead of

> time, but the "flattening" vector 'tsubst_args' is constructed under the

> assumption that all outer template arguments are already substituted in,

> and eliminating this assumption to yield a flattening vector that

> includes outer (generic) template arguments suitable for substituting

> into the constraint would be tricky.  So this patch takes the

> approximate approach of substituting the outer arguments into the

> constraint ahead of time, so that we could subsequently substitute

> 'tsubst_args' into the constraint.


This seems reasonable, and related to the substitution needed for 
http://wg21.link/p2103#ca104 , though in this case the substitution 
could be avoided.

> NB: I noticed that [over.match.class.deduct]/1, which describes how

> guides are formed from constructors of a class template, doesn't mention

> that a guide inherits the constraints of the corresponding constructor.

> Though [over.match.class.deduct]/2 later suggests that guides do have

> constraints: "The associated constraints [of f'] are the conjunction of

> the associated constraints of [the guide] g and ..."  So I'm unsure if

> we're being conforming by giving guides the constraints of the

> corresponding constructor.


I think propagating the constraints is necessary, or deduction would do 
the wrong thing for constructors that try to exclude problematic arguments.

> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for

> trunk and 10 branch (after 10.3 is released)?


OK.  Please also document outer_args in the comment for the function.

> gcc/cp/ChangeLog:

> 

> 	PR c++/97679

> 	* pt.c (build_deduction_guide): Substitute outer template

> 	arguments into the constraints.

> 

> gcc/testsuite/ChangeLog:

> 

> 	PR c++/97679

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

> ---

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

>   gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C | 16 ++++++++++++++++

>   2 files changed, 31 insertions(+), 2 deletions(-)

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

> 

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

> index 7917a280804..6f44199e8eb 100644

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

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

> @@ -28719,7 +28719,15 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com

>   	  if (fparms == error_mark_node)

>   	    ok = false;

>   	  if (ci)

> -	    ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor);

> +	    {

> +	      if (outer_args)

> +		/* FIXME: We'd like to avoid substituting outer template

> +		   arguments into the constraint ahead of time, but the

> +		   construction of tsubst_args assumes that outer arguments

> +		   are already substituted in.  */

> +		ci = tsubst_constraint_info (ci, outer_args, complain, ctor);

> +	      ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor);

> +	    }

>   

>   	  /* Parms are to have DECL_CHAIN tsubsted, which would be skipped if

>   	     cp_unevaluated_operand.  */

> @@ -28735,7 +28743,12 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com

>   	  fparms = tsubst_arg_types (fparms, targs, NULL_TREE, complain, ctor);

>   	  fargs = tsubst (fargs, targs, complain, ctor);

>   	  if (ci)

> -	    ci = tsubst_constraint_info (ci, targs, complain, ctor);

> +	    {

> +	      if (outer_args)

> +		/* FIXME: As above.  */

> +		ci = tsubst_constraint_info (ci, outer_args, complain, ctor);

> +	      ci = tsubst_constraint_info (ci, targs, complain, ctor);

> +	    }

>   	}

>   

>         --processing_template_decl;

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

> new file mode 100644

> index 00000000000..3546b7461a8

> --- /dev/null

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

> @@ -0,0 +1,16 @@

> +// PR c++/97679

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

> +

> +template <bool V> struct A {

> +  template <class T> struct B {

> +    B(T) requires V;

> +    template <class U> B(T, U) requires V || (__is_same(T, char) && __is_same(U, int));

> +  };

> +};

> +

> +A<true>::B x1(0);

> +A<false>::B x2(0); // { dg-error "deduction|no match" }

> +

> +A<true>::B y1(0, '0');

> +A<false>::B y2(0, '0'); // { dg-error "deduction|no match" }

> +A<false>::B y3('0', 0);

>

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 7917a280804..6f44199e8eb 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -28719,7 +28719,15 @@  build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com
 	  if (fparms == error_mark_node)
 	    ok = false;
 	  if (ci)
-	    ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor);
+	    {
+	      if (outer_args)
+		/* FIXME: We'd like to avoid substituting outer template
+		   arguments into the constraint ahead of time, but the
+		   construction of tsubst_args assumes that outer arguments
+		   are already substituted in.  */
+		ci = tsubst_constraint_info (ci, outer_args, complain, ctor);
+	      ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor);
+	    }
 
 	  /* Parms are to have DECL_CHAIN tsubsted, which would be skipped if
 	     cp_unevaluated_operand.  */
@@ -28735,7 +28743,12 @@  build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com
 	  fparms = tsubst_arg_types (fparms, targs, NULL_TREE, complain, ctor);
 	  fargs = tsubst (fargs, targs, complain, ctor);
 	  if (ci)
-	    ci = tsubst_constraint_info (ci, targs, complain, ctor);
+	    {
+	      if (outer_args)
+		/* FIXME: As above.  */
+		ci = tsubst_constraint_info (ci, outer_args, complain, ctor);
+	      ci = tsubst_constraint_info (ci, targs, complain, ctor);
+	    }
 	}
 
       --processing_template_decl;
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C b/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C
new file mode 100644
index 00000000000..3546b7461a8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-ctad3.C
@@ -0,0 +1,16 @@ 
+// PR c++/97679
+// { dg-do compile { target c++20 } }
+
+template <bool V> struct A {
+  template <class T> struct B {
+    B(T) requires V;
+    template <class U> B(T, U) requires V || (__is_same(T, char) && __is_same(U, int));
+  };
+};
+
+A<true>::B x1(0);
+A<false>::B x2(0); // { dg-error "deduction|no match" }
+
+A<true>::B y1(0, '0');
+A<false>::B y2(0, '0'); // { dg-error "deduction|no match" }
+A<false>::B y3('0', 0);