c++: empty captured var as template argument [PR107437]

Message ID 20221216164526.224772-1-ppalka@redhat.com
State New
Headers
Series c++: empty captured var as template argument [PR107437] |

Commit Message

Patrick Palka Dec. 16, 2022, 4:45 p.m. UTC
  Here we're rejecting the use of the captured 't' (of empty type) as a
template argument ultimately because convert_nontype_argument checks
potentiality using is_constant_expression which returns false for
captured variables since want_rval=false.  But in this case an
lvalue-to-rvalue conversion of the argument is implied, so I believe we
should be checking is_rvalue_constant_expression.

This patch defines an rvalue version of is_nondep_const_expr and uses
it in convert_nontype_argument when the target type is a non-reference.

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
Alternatively, since 'non_dep' in convert_nontype_argument controls only
whether we should call instantiate_non_dependent_expr, it seems weird to
me that we care about potentiality at all.  So I experimented with using
instantiation_dependent_expression_p here instead, and that seemed to
work well and even fixed the dg-ice'd test cpp1z/constexpr-lambda26.C.
In r12-7564-gec0f53a3a542e7 we made a similar change to decltype.

	PR c++/107437

gcc/cp/ChangeLog:

	* constexpr.cc (is_nondependent_rvalue_constant_expression):
	Define.
	* cp-tree.h (is_nondependent_rvalue_constant_expression):
	Declare.
	* pt.cc (convert_nontype_argument): Use it instead of the
	non-rvalue version when converting to a non-reference type.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1y/lambda-generic-107437.C: New test.
---
 gcc/cp/constexpr.cc                           | 10 +++++++++
 gcc/cp/cp-tree.h                              |  1 +
 gcc/cp/pt.cc                                  |  4 +++-
 .../g++.dg/cpp1y/lambda-generic-107437.C      | 22 +++++++++++++++++++
 4 files changed, 36 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
  

Comments

Jason Merrill Dec. 16, 2022, 5:08 p.m. UTC | #1
On 12/16/22 11:45, Patrick Palka wrote:
> Here we're rejecting the use of the captured 't' (of empty type) as a
> template argument ultimately because convert_nontype_argument checks
> potentiality using is_constant_expression which returns false for
> captured variables since want_rval=false.  But in this case an
> lvalue-to-rvalue conversion of the argument is implied, so I believe we
> should be checking is_rvalue_constant_expression.
> 
> This patch defines an rvalue version of is_nondep_const_expr and uses
> it in convert_nontype_argument when the target type is a non-reference.
> 
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> Alternatively, since 'non_dep' in convert_nontype_argument controls only
> whether we should call instantiate_non_dependent_expr, it seems weird to
> me that we care about potentiality at all.  So I experimented with using
> instantiation_dependent_expression_p here instead, and that seemed to
> work well and even fixed the dg-ice'd test cpp1z/constexpr-lambda26.C.
> In r12-7564-gec0f53a3a542e7 we made a similar change to decltype.

This approach sounds good to me.

> 	PR c++/107437
> 
> gcc/cp/ChangeLog:
> 
> 	* constexpr.cc (is_nondependent_rvalue_constant_expression):
> 	Define.
> 	* cp-tree.h (is_nondependent_rvalue_constant_expression):
> 	Declare.
> 	* pt.cc (convert_nontype_argument): Use it instead of the
> 	non-rvalue version when converting to a non-reference type.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp1y/lambda-generic-107437.C: New test.
> ---
>   gcc/cp/constexpr.cc                           | 10 +++++++++
>   gcc/cp/cp-tree.h                              |  1 +
>   gcc/cp/pt.cc                                  |  4 +++-
>   .../g++.dg/cpp1y/lambda-generic-107437.C      | 22 +++++++++++++++++++
>   4 files changed, 36 insertions(+), 1 deletion(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
> 
> diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
> index e43d92864f5..77e0e261cca 100644
> --- a/gcc/cp/constexpr.cc
> +++ b/gcc/cp/constexpr.cc
> @@ -10139,6 +10139,16 @@ is_nondependent_constant_expression (tree t)
>   	  && !instantiation_dependent_expression_p (t));
>   }
>   
> +/* As above, but expect an rvalue.  */
> +
> +bool
> +is_nondependent_rvalue_constant_expression (tree t)
> +{
> +  return (!type_unknown_p (t)
> +	  && is_rvalue_constant_expression (t)
> +	  && !instantiation_dependent_expression_p (t));
> +}
> +
>   /* Returns true if T is a potential static initializer expression that is not
>      instantiation-dependent.  */
>   
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index 0d6c234b3b0..2d4c5cc3ebd 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -8471,6 +8471,7 @@ extern bool potential_constant_expression       (tree);
>   extern bool is_constant_expression (tree);
>   extern bool is_rvalue_constant_expression (tree);
>   extern bool is_nondependent_constant_expression (tree);
> +extern bool is_nondependent_rvalue_constant_expression (tree);
>   extern bool is_nondependent_static_init_expression (tree);
>   extern bool is_static_init_expression    (tree);
>   extern bool is_std_allocator (tree);
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index bc566ab702b..87caea5f202 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -7318,7 +7318,9 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
>         && has_value_dependent_address (expr))
>       /* If we want the address and it's value-dependent, don't fold.  */;
>     else if (processing_template_decl
> -	   && is_nondependent_constant_expression (expr))
> +	   && (TYPE_REF_P (type)
> +	       ? is_nondependent_constant_expression (expr)
> +	       : is_nondependent_rvalue_constant_expression (expr)))
>       non_dep = true;
>     if (error_operand_p (expr))
>       return error_mark_node;
> diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
> new file mode 100644
> index 00000000000..934f863659d
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
> @@ -0,0 +1,22 @@
> +// PR c++/107437
> +// { dg-do compile { target c++11 } }
> +
> +struct integral_constant {
> +  constexpr operator int() const { return 42; }
> +};
> +
> +template<int N>
> +struct A {
> +  static constexpr int value = N;
> +};
> +
> +template<class T>
> +void f(T t) {
> +  [=](auto) {
> +    A<t> a; // { dg-bogus "constant" }
> +    return a.value;
> +  }(0);
> +}
> +
> +template void f(integral_constant);
> +
  
Patrick Palka Dec. 16, 2022, 7:03 p.m. UTC | #2
On Fri, 16 Dec 2022, Jason Merrill wrote:

> On 12/16/22 11:45, Patrick Palka wrote:
> > Here we're rejecting the use of the captured 't' (of empty type) as a
> > template argument ultimately because convert_nontype_argument checks
> > potentiality using is_constant_expression which returns false for
> > captured variables since want_rval=false.  But in this case an
> > lvalue-to-rvalue conversion of the argument is implied, so I believe we
> > should be checking is_rvalue_constant_expression.
> > 
> > This patch defines an rvalue version of is_nondep_const_expr and uses
> > it in convert_nontype_argument when the target type is a non-reference.
> > 
> > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
> > Alternatively, since 'non_dep' in convert_nontype_argument controls only
> > whether we should call instantiate_non_dependent_expr, it seems weird to
> > me that we care about potentiality at all.  So I experimented with using
> > instantiation_dependent_expression_p here instead, and that seemed to
> > work well and even fixed the dg-ice'd test cpp1z/constexpr-lambda26.C.
> > In r12-7564-gec0f53a3a542e7 we made a similar change to decltype.
> 
> This approach sounds good to me.

Like so?  Bootstrapped and regtested on x86_64-pc-linux-gnu.

-- >8 --

Subject: [PATCH] c++: constantness of non-dependent NTTP argument [PR107437]

Here we're rejecting the use of the captured 't' (of empty type) as a
template argument ultimately because convert_nontype_argument checks
constantness using is_constant_expression which returns false for
captured variables since want_rval=false.  But in this case an
lvalue-to-rvalue conversion of the argument is implied, so I believe we
should be using is_rvalue_constant_expression instead.

However, it doesn't seem necessary to consider constantness at all
in convert_nontype_argument when determining whether to instantiate
a non-dependent argument.  So this patch gets rid of the problematic
constantness test altogether, which is sufficient to fix the testcase
(as well as the similar dg-ice'd testcase from PR87765).  Note that in
r12-7564-gec0f53a3a542e7 we made a similar change to finish_decltype_type.

	PR c++/107437
	PR c++/87765

gcc/cp/ChangeLog:

	* pt.cc (convert_nontype_argument): Relax is_nondep_const_expr
	test to !inst_dep_expr_p.

gcc/testsuite/ChangeLog:

	* g++.dg/cpp1y/lambda-generic-107437.C: New test.
	* g++.dg/cpp1z/constexpr-lambda26.C: Remove dg-ice.
---
 gcc/cp/pt.cc                                  |  2 +-
 .../g++.dg/cpp1y/lambda-generic-107437.C      | 21 +++++++++++++++++++
 .../g++.dg/cpp1z/constexpr-lambda26.C         |  1 -
 3 files changed, 22 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C

diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bc566ab702b..2516cca590e 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -7318,7 +7318,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
       && has_value_dependent_address (expr))
     /* If we want the address and it's value-dependent, don't fold.  */;
   else if (processing_template_decl
-	   && is_nondependent_constant_expression (expr))
+	   && !instantiation_dependent_expression_p (expr))
     non_dep = true;
   if (error_operand_p (expr))
     return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
new file mode 100644
index 00000000000..f9b4e0187e2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
@@ -0,0 +1,21 @@
+// PR c++/107437
+// { dg-do compile { target c++14 } }
+
+struct integral_constant {
+  constexpr operator int() const { return 42; }
+};
+
+template<int N>
+struct A {
+  static constexpr int value = N;
+};
+
+template<class T>
+void f(T t) {
+  [=](auto) {
+    A<t> a; // { dg-bogus "constant" }
+    return a.value;
+  }(0);
+}
+
+template void f(integral_constant);
diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C
index 0cdb400d21c..e66cd1dee64 100644
--- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C
+++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C
@@ -1,7 +1,6 @@
 // PR c++/87765
 // { dg-do compile { target c++17 } }
 // { dg-additional-options "-fchecking" }
-// { dg-ice "cxx_eval_constant_expression" }
 
 template <int N>
 using foo = int;
  
Jason Merrill Dec. 17, 2022, 12:14 a.m. UTC | #3
On 12/16/22 14:03, Patrick Palka wrote:
> On Fri, 16 Dec 2022, Jason Merrill wrote:
> 
>> On 12/16/22 11:45, Patrick Palka wrote:
>>> Here we're rejecting the use of the captured 't' (of empty type) as a
>>> template argument ultimately because convert_nontype_argument checks
>>> potentiality using is_constant_expression which returns false for
>>> captured variables since want_rval=false.  But in this case an
>>> lvalue-to-rvalue conversion of the argument is implied, so I believe we
>>> should be checking is_rvalue_constant_expression.
>>>
>>> This patch defines an rvalue version of is_nondep_const_expr and uses
>>> it in convert_nontype_argument when the target type is a non-reference.
>>>
>>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look right?
>>> Alternatively, since 'non_dep' in convert_nontype_argument controls only
>>> whether we should call instantiate_non_dependent_expr, it seems weird to
>>> me that we care about potentiality at all.  So I experimented with using
>>> instantiation_dependent_expression_p here instead, and that seemed to
>>> work well and even fixed the dg-ice'd test cpp1z/constexpr-lambda26.C.
>>> In r12-7564-gec0f53a3a542e7 we made a similar change to decltype.
>>
>> This approach sounds good to me.
> 
> Like so?  Bootstrapped and regtested on x86_64-pc-linux-gnu.

OK.

> -- >8 --
> 
> Subject: [PATCH] c++: constantness of non-dependent NTTP argument [PR107437]
> 
> Here we're rejecting the use of the captured 't' (of empty type) as a
> template argument ultimately because convert_nontype_argument checks
> constantness using is_constant_expression which returns false for
> captured variables since want_rval=false.  But in this case an
> lvalue-to-rvalue conversion of the argument is implied, so I believe we
> should be using is_rvalue_constant_expression instead.
> 
> However, it doesn't seem necessary to consider constantness at all
> in convert_nontype_argument when determining whether to instantiate
> a non-dependent argument.  So this patch gets rid of the problematic
> constantness test altogether, which is sufficient to fix the testcase
> (as well as the similar dg-ice'd testcase from PR87765).  Note that in
> r12-7564-gec0f53a3a542e7 we made a similar change to finish_decltype_type.
> 
> 	PR c++/107437
> 	PR c++/87765
> 
> gcc/cp/ChangeLog:
> 
> 	* pt.cc (convert_nontype_argument): Relax is_nondep_const_expr
> 	test to !inst_dep_expr_p.
> 
> gcc/testsuite/ChangeLog:
> 
> 	* g++.dg/cpp1y/lambda-generic-107437.C: New test.
> 	* g++.dg/cpp1z/constexpr-lambda26.C: Remove dg-ice.
> ---
>   gcc/cp/pt.cc                                  |  2 +-
>   .../g++.dg/cpp1y/lambda-generic-107437.C      | 21 +++++++++++++++++++
>   .../g++.dg/cpp1z/constexpr-lambda26.C         |  1 -
>   3 files changed, 22 insertions(+), 2 deletions(-)
>   create mode 100644 gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
> 
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index bc566ab702b..2516cca590e 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -7318,7 +7318,7 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
>         && has_value_dependent_address (expr))
>       /* If we want the address and it's value-dependent, don't fold.  */;
>     else if (processing_template_decl
> -	   && is_nondependent_constant_expression (expr))
> +	   && !instantiation_dependent_expression_p (expr))
>       non_dep = true;
>     if (error_operand_p (expr))
>       return error_mark_node;
> diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
> new file mode 100644
> index 00000000000..f9b4e0187e2
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
> @@ -0,0 +1,21 @@
> +// PR c++/107437
> +// { dg-do compile { target c++14 } }
> +
> +struct integral_constant {
> +  constexpr operator int() const { return 42; }
> +};
> +
> +template<int N>
> +struct A {
> +  static constexpr int value = N;
> +};
> +
> +template<class T>
> +void f(T t) {
> +  [=](auto) {
> +    A<t> a; // { dg-bogus "constant" }
> +    return a.value;
> +  }(0);
> +}
> +
> +template void f(integral_constant);
> diff --git a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C
> index 0cdb400d21c..e66cd1dee64 100644
> --- a/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C
> +++ b/gcc/testsuite/g++.dg/cpp1z/constexpr-lambda26.C
> @@ -1,7 +1,6 @@
>   // PR c++/87765
>   // { dg-do compile { target c++17 } }
>   // { dg-additional-options "-fchecking" }
> -// { dg-ice "cxx_eval_constant_expression" }
>   
>   template <int N>
>   using foo = int;
  

Patch

diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index e43d92864f5..77e0e261cca 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -10139,6 +10139,16 @@  is_nondependent_constant_expression (tree t)
 	  && !instantiation_dependent_expression_p (t));
 }
 
+/* As above, but expect an rvalue.  */
+
+bool
+is_nondependent_rvalue_constant_expression (tree t)
+{
+  return (!type_unknown_p (t)
+	  && is_rvalue_constant_expression (t)
+	  && !instantiation_dependent_expression_p (t));
+}
+
 /* Returns true if T is a potential static initializer expression that is not
    instantiation-dependent.  */
 
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 0d6c234b3b0..2d4c5cc3ebd 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8471,6 +8471,7 @@  extern bool potential_constant_expression       (tree);
 extern bool is_constant_expression (tree);
 extern bool is_rvalue_constant_expression (tree);
 extern bool is_nondependent_constant_expression (tree);
+extern bool is_nondependent_rvalue_constant_expression (tree);
 extern bool is_nondependent_static_init_expression (tree);
 extern bool is_static_init_expression    (tree);
 extern bool is_std_allocator (tree);
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index bc566ab702b..87caea5f202 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -7318,7 +7318,9 @@  convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
       && has_value_dependent_address (expr))
     /* If we want the address and it's value-dependent, don't fold.  */;
   else if (processing_template_decl
-	   && is_nondependent_constant_expression (expr))
+	   && (TYPE_REF_P (type)
+	       ? is_nondependent_constant_expression (expr)
+	       : is_nondependent_rvalue_constant_expression (expr)))
     non_dep = true;
   if (error_operand_p (expr))
     return error_mark_node;
diff --git a/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
new file mode 100644
index 00000000000..934f863659d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/lambda-generic-107437.C
@@ -0,0 +1,22 @@ 
+// PR c++/107437
+// { dg-do compile { target c++11 } }
+
+struct integral_constant {
+  constexpr operator int() const { return 42; }
+};
+
+template<int N>
+struct A {
+  static constexpr int value = N;
+};
+
+template<class T>
+void f(T t) {
+  [=](auto) {
+    A<t> a; // { dg-bogus "constant" }
+    return a.value;
+  }(0);
+}
+
+template void f(integral_constant);
+