c++: placeholder type constraint on structured binding [PR99899]

Message ID 20210405153105.1348342-1-ppalka@redhat.com
State New
Headers show
Series
  • c++: placeholder type constraint on structured binding [PR99899]
Related show

Commit Message

Patrick Palka via Gcc-patches April 5, 2021, 3:31 p.m.
In this PR, we're crashing because the constraint handling inside
do_auto_deduction doesn't expect to see an adc_decomp_type context.
This patch fixes this by treating adc_decomp_type like adc_variable_type
and adc_return_type during the constraint handling.

Meanwhile, I noticed we weren't checking constraints at all when binding
an array via a structured binding, since do_auto_deduction would exit
early and bypass the constraint check.  This patch fixes this by
replacing the early exit with an appropriate setup of the 'targs'
vector.

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

gcc/cp/ChangeLog:

	PR c++/99899
	* pt.c (do_auto_deduction): Don't exit early when deducing the
	array type of a structured binding.  Also handle adc_decomp_type
	during constraint checking.

gcc/testsuite/ChangeLog:

	PR c++/99899
	* g++.dg/cpp2a/concepts-placeholder7.C: New test.
---
 gcc/cp/pt.c                                   | 22 +++++++-----
 .../g++.dg/cpp2a/concepts-placeholder7.C      | 34 +++++++++++++++++++
 2 files changed, 48 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C

-- 
2.31.1.189.g2e36527f23

Comments

Patrick Palka via Gcc-patches April 5, 2021, 4:31 p.m. | #1
On Mon, 5 Apr 2021, Patrick Palka wrote:

> In this PR, we're crashing because the constraint handling inside

> do_auto_deduction doesn't expect to see an adc_decomp_type context.

> This patch fixes this by treating adc_decomp_type like adc_variable_type

> and adc_return_type during the constraint handling.

> 

> Meanwhile, I noticed we weren't checking constraints at all when binding

> an array via a structured binding, since do_auto_deduction would exit

> early and bypass the constraint check.  This patch fixes this by

> replacing the early exit with an appropriate setup of the 'targs'

> vector.

> 

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

> trunk?

> 

> gcc/cp/ChangeLog:

> 

> 	PR c++/99899

> 	* pt.c (do_auto_deduction): Don't exit early when deducing the

> 	array type of a structured binding.  Also handle adc_decomp_type

> 	during constraint checking.

> 

> gcc/testsuite/ChangeLog:

> 

> 	PR c++/99899

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

> ---

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

>  .../g++.dg/cpp2a/concepts-placeholder7.C      | 34 +++++++++++++++++++

>  2 files changed, 48 insertions(+), 8 deletions(-)

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

> 

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

> index 1d19a59dd62..0f9f5858038 100644

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

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

> @@ -29438,8 +29438,6 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>                     tsubst_flags_t complain, auto_deduction_context context,

>  		   tree outer_targs, int flags)

>  {

> -  tree targs;

> -

>    if (init == error_mark_node)

>      return error_mark_node;

>  

> @@ -29503,14 +29501,19 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>    else

>      init = resolve_nondeduced_context (init, complain);

>  

> +  tree targs;

>    if (context == adc_decomp_type

>        && auto_node == type

>        && init != error_mark_node

>        && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)

> -    /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers

> -       and initializer has array type, deduce cv-qualified array type.  */

> -    return cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),

> -					 complain);

> +    {

> +      /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers

> +	 and initializer has array type, deduce cv-qualified array type.  */

> +      targs = make_tree_vec (1);

> +      TREE_VEC_ELT (targs, 0)

> +	= cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),

> +					complain);


On second thought, I think we can get away with using just TREE_TYPE (init)
here and leaving the propagation of type qualifiers to tsubst.  This has
the effect of making us reject the testcase placeholder8.C below, which
is consistent with the non-array case.  IIUC, the type qualifiers on a
constrained 'auto' shouldn't be relevant during constraint checking.

I'm testing the following:

-- >8 --

gcc/cp/ChangeLog:

	PR c++/99899
	* pt.c (do_auto_deduction): Don't exit early when deducing the
	array type of a structured binding.  Also handle adc_decomp_type
	during constraint checking.

gcc/testsuite/ChangeLog:

	PR c++/99899
	* g++.dg/cpp2a/concepts-placeholder7.C: New test.
	* g++.dg/cpp2a/concepts-placeholder8.C: New test.
---
 gcc/cp/pt.c                                   | 20 +++++++-----
 .../g++.dg/cpp2a/concepts-placeholder7.C      | 32 +++++++++++++++++++
 .../g++.dg/cpp2a/concepts-placeholder8.C      | 10 ++++++
 3 files changed, 54 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C
 create mode 100644 gcc/testsuite/g++.dg/cpp2a/concepts-placeholder8.C

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 1d19a59dd62..fd1e4db42cf 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -29438,8 +29438,6 @@ do_auto_deduction (tree type, tree init, tree auto_node,
                    tsubst_flags_t complain, auto_deduction_context context,
 		   tree outer_targs, int flags)
 {
-  tree targs;
-
   if (init == error_mark_node)
     return error_mark_node;
 
@@ -29503,14 +29501,17 @@ do_auto_deduction (tree type, tree init, tree auto_node,
   else
     init = resolve_nondeduced_context (init, complain);
 
+  tree targs;
   if (context == adc_decomp_type
       && auto_node == type
       && init != error_mark_node
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
-    /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers
-       and initializer has array type, deduce cv-qualified array type.  */
-    return cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),
-					 complain);
+    {
+      /* [dcl.struct.bind]/1 - if decomposition declaration has no ref-qualifiers
+	 and initializer has array type, deduce cv-qualified array type.  */
+      targs = make_tree_vec (1);
+      TREE_VEC_ELT (targs, 0) = TREE_TYPE (init);
+    }
   else if (AUTO_IS_DECLTYPE (auto_node))
     {
       tree stripped_init = tree_strip_any_location_wrapper (init);
@@ -29596,7 +29597,8 @@ do_auto_deduction (tree type, tree init, tree auto_node,
       if (processing_template_decl)
 	{
 	  gcc_checking_assert (context == adc_variable_type
-			       || context == adc_return_type);
+			       || context == adc_return_type
+			       || context == adc_decomp_type);
 	  gcc_checking_assert (!type_dependent_expression_p (init));
 	  /* If the constraint is dependent, we need to wait until
 	     instantiation time to resolve the placeholder.  */
@@ -29604,7 +29606,9 @@ do_auto_deduction (tree type, tree init, tree auto_node,
 	    return type;
 	}
 
-      if ((context == adc_return_type || context == adc_variable_type)
+      if ((context == adc_return_type
+	   || context == adc_variable_type
+	   || context == adc_decomp_type)
 	  && current_function_decl
 	  && DECL_TEMPLATE_INFO (current_function_decl))
 	outer_targs = DECL_TI_ARGS (current_function_decl);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C
new file mode 100644
index 00000000000..93219ff4f05
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C
@@ -0,0 +1,32 @@
+// PR c++/99899
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C1 = sizeof(T) > sizeof(int[1]);
+
+template <class>
+void f() {
+  int x[] = {1,2};
+  int y[] = {3};
+  C1 auto [a,b] = x;
+  C1 auto [c] = y; // { dg-error "constraints" }
+}
+
+template <class T>
+void g() {
+  T x[] = {1,2};
+  T y[] = {3};
+  C1 auto [a,b] = x;
+  C1 auto [c] = y; // { dg-error "constraints" }
+}
+template void g<int>();
+
+
+template <class... Ts> concept C2 = sizeof...(Ts) > 1;
+
+struct S { int a, b; } s;
+
+template <class T>
+void h() {
+  const C2<T> auto& [a, b] = s;
+}
+template void h<int>();
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder8.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder8.C
new file mode 100644
index 00000000000..e78659203ba
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder8.C
@@ -0,0 +1,10 @@
+// { dg-do compile { target c++20 } }
+
+template <class T> concept is_const = __is_same(T, const T);
+
+void f() {
+  int x[] = {1,2};
+  const int y[] = {3};
+  const is_const auto [a,b] = x; // { dg-error "constraints" }
+  const is_const auto [c] = y;
+}
-- 
2.31.1.189.g2e36527f23
Patrick Palka via Gcc-patches April 5, 2021, 6:23 p.m. | #2
On 4/5/21 12:31 PM, Patrick Palka wrote:
> On Mon, 5 Apr 2021, Patrick Palka wrote:

> 

>> In this PR, we're crashing because the constraint handling inside

>> do_auto_deduction doesn't expect to see an adc_decomp_type context.

>> This patch fixes this by treating adc_decomp_type like adc_variable_type

>> and adc_return_type during the constraint handling.

>>

>> Meanwhile, I noticed we weren't checking constraints at all when binding

>> an array via a structured binding, since do_auto_deduction would exit

>> early and bypass the constraint check.  This patch fixes this by

>> replacing the early exit with an appropriate setup of the 'targs'

>> vector.

>>

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

>> trunk?


OK.

>> gcc/cp/ChangeLog:

>>

>> 	PR c++/99899

>> 	* pt.c (do_auto_deduction): Don't exit early when deducing the

>> 	array type of a structured binding.  Also handle adc_decomp_type

>> 	during constraint checking.

>>

>> gcc/testsuite/ChangeLog:

>>

>> 	PR c++/99899

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

>> ---

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

>>   .../g++.dg/cpp2a/concepts-placeholder7.C      | 34 +++++++++++++++++++

>>   2 files changed, 48 insertions(+), 8 deletions(-)

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

>>

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

>> index 1d19a59dd62..0f9f5858038 100644

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

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

>> @@ -29438,8 +29438,6 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>>                      tsubst_flags_t complain, auto_deduction_context context,

>>   		   tree outer_targs, int flags)

>>   {

>> -  tree targs;

>> -

>>     if (init == error_mark_node)

>>       return error_mark_node;

>>   

>> @@ -29503,14 +29501,19 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>>     else

>>       init = resolve_nondeduced_context (init, complain);

>>   

>> +  tree targs;

>>     if (context == adc_decomp_type

>>         && auto_node == type

>>         && init != error_mark_node

>>         && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)

>> -    /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers

>> -       and initializer has array type, deduce cv-qualified array type.  */

>> -    return cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),

>> -					 complain);

>> +    {

>> +      /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers

>> +	 and initializer has array type, deduce cv-qualified array type.  */

>> +      targs = make_tree_vec (1);

>> +      TREE_VEC_ELT (targs, 0)

>> +	= cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),

>> +					complain);

> 

> On second thought, I think we can get away with using just TREE_TYPE (init)

> here and leaving the propagation of type qualifiers to tsubst.  This has

> the effect of making us reject the testcase placeholder8.C below, which

> is consistent with the non-array case.  IIUC, the type qualifiers on a

> constrained 'auto' shouldn't be relevant during constraint checking.

> 

> I'm testing the following:

> 

> -- >8 --

> 

> gcc/cp/ChangeLog:

> 

> 	PR c++/99899

> 	* pt.c (do_auto_deduction): Don't exit early when deducing the

> 	array type of a structured binding.  Also handle adc_decomp_type

> 	during constraint checking.

> 

> gcc/testsuite/ChangeLog:

> 

> 	PR c++/99899

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

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

> ---

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

>   .../g++.dg/cpp2a/concepts-placeholder7.C      | 32 +++++++++++++++++++

>   .../g++.dg/cpp2a/concepts-placeholder8.C      | 10 ++++++

>   3 files changed, 54 insertions(+), 8 deletions(-)

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

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

> 

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

> index 1d19a59dd62..fd1e4db42cf 100644

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

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

> @@ -29438,8 +29438,6 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>                      tsubst_flags_t complain, auto_deduction_context context,

>   		   tree outer_targs, int flags)

>   {

> -  tree targs;

> -

>     if (init == error_mark_node)

>       return error_mark_node;

>   

> @@ -29503,14 +29501,17 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>     else

>       init = resolve_nondeduced_context (init, complain);

>   

> +  tree targs;

>     if (context == adc_decomp_type

>         && auto_node == type

>         && init != error_mark_node

>         && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)

> -    /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers

> -       and initializer has array type, deduce cv-qualified array type.  */

> -    return cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),

> -					 complain);

> +    {

> +      /* [dcl.struct.bind]/1 - if decomposition declaration has no ref-qualifiers

> +	 and initializer has array type, deduce cv-qualified array type.  */

> +      targs = make_tree_vec (1);

> +      TREE_VEC_ELT (targs, 0) = TREE_TYPE (init);

> +    }

>     else if (AUTO_IS_DECLTYPE (auto_node))

>       {

>         tree stripped_init = tree_strip_any_location_wrapper (init);

> @@ -29596,7 +29597,8 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>         if (processing_template_decl)

>   	{

>   	  gcc_checking_assert (context == adc_variable_type

> -			       || context == adc_return_type);

> +			       || context == adc_return_type

> +			       || context == adc_decomp_type);

>   	  gcc_checking_assert (!type_dependent_expression_p (init));

>   	  /* If the constraint is dependent, we need to wait until

>   	     instantiation time to resolve the placeholder.  */

> @@ -29604,7 +29606,9 @@ do_auto_deduction (tree type, tree init, tree auto_node,

>   	    return type;

>   	}

>   

> -      if ((context == adc_return_type || context == adc_variable_type)

> +      if ((context == adc_return_type

> +	   || context == adc_variable_type

> +	   || context == adc_decomp_type)

>   	  && current_function_decl

>   	  && DECL_TEMPLATE_INFO (current_function_decl))

>   	outer_targs = DECL_TI_ARGS (current_function_decl);

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

> new file mode 100644

> index 00000000000..93219ff4f05

> --- /dev/null

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

> @@ -0,0 +1,32 @@

> +// PR c++/99899

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

> +

> +template <class T> concept C1 = sizeof(T) > sizeof(int[1]);

> +

> +template <class>

> +void f() {

> +  int x[] = {1,2};

> +  int y[] = {3};

> +  C1 auto [a,b] = x;

> +  C1 auto [c] = y; // { dg-error "constraints" }

> +}

> +

> +template <class T>

> +void g() {

> +  T x[] = {1,2};

> +  T y[] = {3};

> +  C1 auto [a,b] = x;

> +  C1 auto [c] = y; // { dg-error "constraints" }

> +}

> +template void g<int>();

> +

> +

> +template <class... Ts> concept C2 = sizeof...(Ts) > 1;

> +

> +struct S { int a, b; } s;

> +

> +template <class T>

> +void h() {

> +  const C2<T> auto& [a, b] = s;

> +}

> +template void h<int>();

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

> new file mode 100644

> index 00000000000..e78659203ba

> --- /dev/null

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

> @@ -0,0 +1,10 @@

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

> +

> +template <class T> concept is_const = __is_same(T, const T);

> +

> +void f() {

> +  int x[] = {1,2};

> +  const int y[] = {3};

> +  const is_const auto [a,b] = x; // { dg-error "constraints" }

> +  const is_const auto [c] = y;

> +}

>

Patch

diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 1d19a59dd62..0f9f5858038 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -29438,8 +29438,6 @@  do_auto_deduction (tree type, tree init, tree auto_node,
                    tsubst_flags_t complain, auto_deduction_context context,
 		   tree outer_targs, int flags)
 {
-  tree targs;
-
   if (init == error_mark_node)
     return error_mark_node;
 
@@ -29503,14 +29501,19 @@  do_auto_deduction (tree type, tree init, tree auto_node,
   else
     init = resolve_nondeduced_context (init, complain);
 
+  tree targs;
   if (context == adc_decomp_type
       && auto_node == type
       && init != error_mark_node
       && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE)
-    /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers
-       and initializer has array type, deduce cv-qualified array type.  */
-    return cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),
-					 complain);
+    {
+      /* [dcl.decomp]/1 - if decomposition declaration has no ref-qualifiers
+	 and initializer has array type, deduce cv-qualified array type.  */
+      targs = make_tree_vec (1);
+      TREE_VEC_ELT (targs, 0)
+	= cp_build_qualified_type_real (TREE_TYPE (init), TYPE_QUALS (type),
+					complain);
+    }
   else if (AUTO_IS_DECLTYPE (auto_node))
     {
       tree stripped_init = tree_strip_any_location_wrapper (init);
@@ -29596,7 +29599,8 @@  do_auto_deduction (tree type, tree init, tree auto_node,
       if (processing_template_decl)
 	{
 	  gcc_checking_assert (context == adc_variable_type
-			       || context == adc_return_type);
+			       || context == adc_return_type
+			       || context == adc_decomp_type);
 	  gcc_checking_assert (!type_dependent_expression_p (init));
 	  /* If the constraint is dependent, we need to wait until
 	     instantiation time to resolve the placeholder.  */
@@ -29604,7 +29608,9 @@  do_auto_deduction (tree type, tree init, tree auto_node,
 	    return type;
 	}
 
-      if ((context == adc_return_type || context == adc_variable_type)
+      if ((context == adc_return_type
+	   || context == adc_variable_type
+	   || context == adc_decomp_type)
 	  && current_function_decl
 	  && DECL_TEMPLATE_INFO (current_function_decl))
 	outer_targs = DECL_TI_ARGS (current_function_decl);
diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C
new file mode 100644
index 00000000000..db352a01e83
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/concepts-placeholder7.C
@@ -0,0 +1,34 @@ 
+// PR c++/99899
+// { dg-do compile { target c++20 } }
+
+template <class T> concept C1 = sizeof(T) > sizeof(int[1]);
+
+template <class>
+void f() {
+  int x[] = {1,2};
+  int y[] = {3};
+  C1 auto [a,b] = x;
+  C1 auto [c] = y; // { dg-error "constraints" }
+}
+
+template <class T>
+void g() {
+  T x[] = {1,2};
+  T y[] = {3};
+  C1 auto [a,b] = x;
+  C1 auto [c] = y; // { dg-error "constraints" }
+}
+
+template void g<int>();
+
+
+template <class... Ts> concept C2 = sizeof...(Ts) > 1;
+
+struct S { int a, b; } s;
+
+template <class T>
+void h() {
+  const C2<T> auto& [a, b] = s;
+}
+
+template void h<int>();