diff mbox series

C++ PATCH for c++/87145 - bogus error converting class type in template argument list

Message ID 20190320201213.GA26967@redhat.com
State New
Headers show
Series C++ PATCH for c++/87145 - bogus error converting class type in template argument list | expand

Commit Message

Marek Polacek March 20, 2019, 8:12 p.m. UTC
The fix for 77656 caused us to call convert_nontype_argument even for
value-dependent arguments, to perform the conversion in order to avoid
a bogus warning.

In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
because we're in a template and build_address no longer crashes on CONSTRUCTORs
in a template.

Then when instantiating the function foo we substitute its argument: &{N}.  So
we're in tsubst_copy_and_build/ADDR_EXPR.  The call to
tsubst_non_call_postfix_expression turns {N} into TARGET_EXPR <D.2329, {.val=2}>.
Then build_x_unary_op is supposed to put the ADDR_EXPR back.  It calls
cp_build_addr_expr_strict.  But it's *strict*, so the prvalue of class type
TARGET_EXPR <D.2329, {.val=2}> isn't allowed -> error.

It's _strict since <https://gcc.gnu.org/ml/gcc-patches/2010-09/msg02144.html>,
that seem like a desirable change, and we had a warning for taking the address
of a TARGET_EXPR in build_x_unary_op even before that.

So rather than messing with _strict, let's avoid this scenario altogether.
I checked whether we have a case in the testsuite that results in convert_like
getting a value-dependent CONSTRUCTOR, but found none.

With this patch, we avoid it, and only call convert_nontype_argument after
substitution, at which point maybe_constant_value will be able to evaluate
the conversion to a constant.

This problem doesn't occur when passing Pod{N} as an argument to a function,
or using it as an array dimension; seems we avoid converting the argument if
it's value-dependent.

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-03-20  Marek Polacek  <polacek@redhat.com>

	PR c++/87145 - bogus error converting class type in template arg list.
	* pt.c (convert_template_argument): Don't call convert_nontype_argument
	if it could involve calling a conversion function with a value-dependent
	constructor as its argument.

	* g++.dg/cpp0x/constexpr-conv3.C: New test.
	* g++.dg/cpp0x/constexpr-conv4.C: New test.

Comments

Marek Polacek March 27, 2019, 6:51 p.m. UTC | #1
Ping.

On Wed, Mar 20, 2019 at 04:12:13PM -0400, Marek Polacek wrote:
> The fix for 77656 caused us to call convert_nontype_argument even for
> value-dependent arguments, to perform the conversion in order to avoid
> a bogus warning.
> 
> In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
> in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
> because we're in a template and build_address no longer crashes on CONSTRUCTORs
> in a template.
> 
> Then when instantiating the function foo we substitute its argument: &{N}.  So
> we're in tsubst_copy_and_build/ADDR_EXPR.  The call to
> tsubst_non_call_postfix_expression turns {N} into TARGET_EXPR <D.2329, {.val=2}>.
> Then build_x_unary_op is supposed to put the ADDR_EXPR back.  It calls
> cp_build_addr_expr_strict.  But it's *strict*, so the prvalue of class type
> TARGET_EXPR <D.2329, {.val=2}> isn't allowed -> error.
> 
> It's _strict since <https://gcc.gnu.org/ml/gcc-patches/2010-09/msg02144.html>,
> that seem like a desirable change, and we had a warning for taking the address
> of a TARGET_EXPR in build_x_unary_op even before that.
> 
> So rather than messing with _strict, let's avoid this scenario altogether.
> I checked whether we have a case in the testsuite that results in convert_like
> getting a value-dependent CONSTRUCTOR, but found none.
> 
> With this patch, we avoid it, and only call convert_nontype_argument after
> substitution, at which point maybe_constant_value will be able to evaluate
> the conversion to a constant.
> 
> This problem doesn't occur when passing Pod{N} as an argument to a function,
> or using it as an array dimension; seems we avoid converting the argument if
> it's value-dependent.
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-03-20  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/87145 - bogus error converting class type in template arg list.
> 	* pt.c (convert_template_argument): Don't call convert_nontype_argument
> 	if it could involve calling a conversion function with a value-dependent
> 	constructor as its argument.
> 
> 	* g++.dg/cpp0x/constexpr-conv3.C: New test.
> 	* g++.dg/cpp0x/constexpr-conv4.C: New test.
> 
> diff --git gcc/cp/pt.c gcc/cp/pt.c
> index 0acc16d1b92..6878583d99b 100644
> --- gcc/cp/pt.c
> +++ gcc/cp/pt.c
> @@ -8056,7 +8056,16 @@ convert_template_argument (tree parm,
>  	t = canonicalize_type_argument (t, complain);
>  
>        if (!type_dependent_expression_p (orig_arg)
> -	  && !uses_template_parms (t))
> +	  && !uses_template_parms (t)
> +	  /* This might trigger calling a conversion function with
> +	     a value-dependent argument, which could invoke taking
> +	     the address of a temporary representing the result of
> +	     the conversion.  */
> +	  && !(COMPOUND_LITERAL_P (orig_arg)
> +	       && MAYBE_CLASS_TYPE_P (TREE_TYPE (orig_arg))
> +	       && TYPE_HAS_CONVERSION (TREE_TYPE (orig_arg))
> +	       && INTEGRAL_OR_ENUMERATION_TYPE_P (t)
> +	       && value_dependent_expression_p (orig_arg)))
>  	/* We used to call digest_init here.  However, digest_init
>  	   will report errors, which we don't want when complain
>  	   is zero.  More importantly, digest_init will try too
> @@ -8092,7 +8101,7 @@ convert_template_argument (tree parm,
>  	      && TREE_CODE (TREE_TYPE (innertype)) == FUNCTION_TYPE
>  	      && TREE_OPERAND_LENGTH (inner) > 0
>                && reject_gcc_builtin (TREE_OPERAND (inner, 0)))
> -              return error_mark_node;
> +	    return error_mark_node;
>          }
>  
>        if (TREE_CODE (val) == SCOPE_REF)
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
> new file mode 100644
> index 00000000000..3f47c58cd2a
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
> @@ -0,0 +1,25 @@
> +// PR c++/87145
> +// { dg-do compile { target c++11 } }
> +
> +template<typename T, T t> struct integral_constant {
> +  static constexpr T value = t;
> +};
> +
> +enum class Enum : unsigned {};
> +
> +struct Pod {
> +  unsigned val;
> +
> +  constexpr operator Enum() const {
> +    return static_cast<Enum>(val);
> +  }
> +};
> +
> +template<unsigned N>
> +constexpr void foo() {
> +  using Foo = integral_constant<Enum, Pod{N}>;
> +}
> +
> +int main() {
> +  foo<2>();
> +}
> diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
> new file mode 100644
> index 00000000000..f4e3f00a585
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
> @@ -0,0 +1,25 @@
> +// PR c++/87145
> +// { dg-do compile { target c++11 } }
> +
> +struct S {
> +  int val;
> +
> +  constexpr operator int() const {
> +    return static_cast<int>(val);
> +  }
> +};
> +
> +template<int N>
> +struct F { };
> +
> +template<unsigned N>
> +constexpr void foo() {
> +  F<int{N}> f;
> +  F<S{N}> f2;
> +}
> +
> +int
> +main()
> +{
> +  foo<2>();
> +}

Marek
Jason Merrill March 28, 2019, 7:54 p.m. UTC | #2
On 3/20/19 4:12 PM, Marek Polacek wrote:
> The fix for 77656 caused us to call convert_nontype_argument even for
> value-dependent arguments, to perform the conversion in order to avoid
> a bogus warning.
> 
> In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
> in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
> because we're in a template and build_address no longer crashes on CONSTRUCTORs
> in a template.

Yeah, we shouldn't be preserving lower level codes like this ADDR_EXPR; 
we should probably return an IMPLICIT_CONV_EXPR rather than make the 
call explicit.

Jason
Marek Polacek April 4, 2019, 9:18 p.m. UTC | #3
On Thu, Mar 28, 2019 at 03:54:30PM -0400, Jason Merrill wrote:
> On 3/20/19 4:12 PM, Marek Polacek wrote:
> > The fix for 77656 caused us to call convert_nontype_argument even for
> > value-dependent arguments, to perform the conversion in order to avoid
> > a bogus warning.
> > 
> > In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
> > in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
> > because we're in a template and build_address no longer crashes on CONSTRUCTORs
> > in a template.
> 
> Yeah, we shouldn't be preserving lower level codes like this ADDR_EXPR; we
> should probably return an IMPLICIT_CONV_EXPR rather than make the call
> explicit.

I'm having trouble with this.  Do we want build_converted_constant_expr_internal
to build an IMPLICIT_CONV_EXPR in a template, much like
perform_implicit_conversion?  That seems to break a lot of stuff, and I'm
nervous about that at this stage :/  We could probably handle this specially
in convert_nontype_argument.  Maybe build an IMPLICIT_CONV_EXPR for
value-dependent CONSTRUCTORs?

Marek
Jason Merrill April 5, 2019, 4:12 a.m. UTC | #4
On 4/4/19 5:18 PM, Marek Polacek wrote:
> On Thu, Mar 28, 2019 at 03:54:30PM -0400, Jason Merrill wrote:
>> On 3/20/19 4:12 PM, Marek Polacek wrote:
>>> The fix for 77656 caused us to call convert_nontype_argument even for
>>> value-dependent arguments, to perform the conversion in order to avoid
>>> a bogus warning.
>>>
>>> In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
>>> in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
>>> because we're in a template and build_address no longer crashes on CONSTRUCTORs
>>> in a template.
>>
>> Yeah, we shouldn't be preserving lower level codes like this ADDR_EXPR; we
>> should probably return an IMPLICIT_CONV_EXPR rather than make the call
>> explicit.
> 
> I'm having trouble with this.  Do we want build_converted_constant_expr_internal
> to build an IMPLICIT_CONV_EXPR in a template, much like
> perform_implicit_conversion?  That seems to break a lot of stuff, and I'm
> nervous about that at this stage :/  We could probably handle this specially
> in convert_nontype_argument.  Maybe build an IMPLICIT_CONV_EXPR for
> value-dependent CONSTRUCTORs?

That sounds reasonable.

Jason
Marek Polacek April 5, 2019, 8:50 p.m. UTC | #5
On Fri, Apr 05, 2019 at 12:12:11AM -0400, Jason Merrill wrote:
> On 4/4/19 5:18 PM, Marek Polacek wrote:
> > On Thu, Mar 28, 2019 at 03:54:30PM -0400, Jason Merrill wrote:
> > > On 3/20/19 4:12 PM, Marek Polacek wrote:
> > > > The fix for 77656 caused us to call convert_nontype_argument even for
> > > > value-dependent arguments, to perform the conversion in order to avoid
> > > > a bogus warning.
> > > > 
> > > > In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
> > > > in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
> > > > because we're in a template and build_address no longer crashes on CONSTRUCTORs
> > > > in a template.
> > > 
> > > Yeah, we shouldn't be preserving lower level codes like this ADDR_EXPR; we
> > > should probably return an IMPLICIT_CONV_EXPR rather than make the call
> > > explicit.
> > 
> > I'm having trouble with this.  Do we want build_converted_constant_expr_internal
> > to build an IMPLICIT_CONV_EXPR in a template, much like
> > perform_implicit_conversion?  That seems to break a lot of stuff, and I'm
> > nervous about that at this stage :/  We could probably handle this specially
> > in convert_nontype_argument.  Maybe build an IMPLICIT_CONV_EXPR for
> > value-dependent CONSTRUCTORs?
> 
> That sounds reasonable.

Great, thanks.  Something like this, then?

Bootstrapped/regtested on x86_64-linux, ok for trunk?

2019-04-05  Marek Polacek  <polacek@redhat.com>

	PR c++/87145 - bogus error converting class type in template arg list.
	* pt.c (convert_nontype_argument): Don't call
	build_converted_constant_expr if it could involve calling a conversion
	function with a instantiation-dependent constructor as its argument.

	* g++.dg/cpp0x/constexpr-conv3.C: New test.
	* g++.dg/cpp0x/constexpr-conv4.C: New test.

diff --git gcc/cp/pt.c gcc/cp/pt.c
index 40d954d1e8a..f8001317bda 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -6837,6 +6837,19 @@ convert_nontype_argument (tree type, tree expr, tsubst_flags_t complain)
       else if (INTEGRAL_OR_ENUMERATION_TYPE_P (type)
 	       || cxx_dialect >= cxx17)
 	{
+	  /* Calling build_converted_constant_expr might create a call to
+	     a conversion function with a value-dependent argument, which
+	     could invoke taking the address of a temporary representing
+	     the result of the conversion.  */
+	  if (COMPOUND_LITERAL_P (expr)
+	      && CONSTRUCTOR_IS_DEPENDENT (expr)
+	      && MAYBE_CLASS_TYPE_P (expr_type)
+	      && TYPE_HAS_CONVERSION (expr_type))
+	    {
+	      expr = build1 (IMPLICIT_CONV_EXPR, type, expr);
+	      IMPLICIT_CONV_EXPR_NONTYPE_ARG (expr) = true;
+	      return expr;
+	    }
 	  /* C++17: A template-argument for a non-type template-parameter shall
 	     be a converted constant expression (8.20) of the type of the
 	     template-parameter.  */
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
new file mode 100644
index 00000000000..3f47c58cd2a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
@@ -0,0 +1,25 @@
+// PR c++/87145
+// { dg-do compile { target c++11 } }
+
+template<typename T, T t> struct integral_constant {
+  static constexpr T value = t;
+};
+
+enum class Enum : unsigned {};
+
+struct Pod {
+  unsigned val;
+
+  constexpr operator Enum() const {
+    return static_cast<Enum>(val);
+  }
+};
+
+template<unsigned N>
+constexpr void foo() {
+  using Foo = integral_constant<Enum, Pod{N}>;
+}
+
+int main() {
+  foo<2>();
+}
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
new file mode 100644
index 00000000000..f4e3f00a585
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
@@ -0,0 +1,25 @@
+// PR c++/87145
+// { dg-do compile { target c++11 } }
+
+struct S {
+  int val;
+
+  constexpr operator int() const {
+    return static_cast<int>(val);
+  }
+};
+
+template<int N>
+struct F { };
+
+template<unsigned N>
+constexpr void foo() {
+  F<int{N}> f;
+  F<S{N}> f2;
+}
+
+int
+main()
+{
+  foo<2>();
+}
Jason Merrill April 5, 2019, 9:17 p.m. UTC | #6
On 4/5/19 4:50 PM, Marek Polacek wrote:
> On Fri, Apr 05, 2019 at 12:12:11AM -0400, Jason Merrill wrote:
>> On 4/4/19 5:18 PM, Marek Polacek wrote:
>>> On Thu, Mar 28, 2019 at 03:54:30PM -0400, Jason Merrill wrote:
>>>> On 3/20/19 4:12 PM, Marek Polacek wrote:
>>>>> The fix for 77656 caused us to call convert_nontype_argument even for
>>>>> value-dependent arguments, to perform the conversion in order to avoid
>>>>> a bogus warning.
>>>>>
>>>>> In this case, the argument is Pod{N}.  The call to build_converted_constant_expr
>>>>> in convert_nontype_argument produces Pod::operator Enum(&{N}).  It doesn't crash
>>>>> because we're in a template and build_address no longer crashes on CONSTRUCTORs
>>>>> in a template.
>>>>
>>>> Yeah, we shouldn't be preserving lower level codes like this ADDR_EXPR; we
>>>> should probably return an IMPLICIT_CONV_EXPR rather than make the call
>>>> explicit.
>>>
>>> I'm having trouble with this.  Do we want build_converted_constant_expr_internal
>>> to build an IMPLICIT_CONV_EXPR in a template, much like
>>> perform_implicit_conversion?  That seems to break a lot of stuff, and I'm
>>> nervous about that at this stage :/  We could probably handle this specially
>>> in convert_nontype_argument.  Maybe build an IMPLICIT_CONV_EXPR for
>>> value-dependent CONSTRUCTORs?
>>
>> That sounds reasonable.
> 
> Great, thanks.  Something like this, then?
> 
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
> 
> 2019-04-05  Marek Polacek  <polacek@redhat.com>
> 
> 	PR c++/87145 - bogus error converting class type in template arg list.
> 	* pt.c (convert_nontype_argument): Don't call
> 	build_converted_constant_expr if it could involve calling a conversion
> 	function with a instantiation-dependent constructor as its argument.

OK.

Jason
diff mbox series

Patch

diff --git gcc/cp/pt.c gcc/cp/pt.c
index 0acc16d1b92..6878583d99b 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -8056,7 +8056,16 @@  convert_template_argument (tree parm,
 	t = canonicalize_type_argument (t, complain);
 
       if (!type_dependent_expression_p (orig_arg)
-	  && !uses_template_parms (t))
+	  && !uses_template_parms (t)
+	  /* This might trigger calling a conversion function with
+	     a value-dependent argument, which could invoke taking
+	     the address of a temporary representing the result of
+	     the conversion.  */
+	  && !(COMPOUND_LITERAL_P (orig_arg)
+	       && MAYBE_CLASS_TYPE_P (TREE_TYPE (orig_arg))
+	       && TYPE_HAS_CONVERSION (TREE_TYPE (orig_arg))
+	       && INTEGRAL_OR_ENUMERATION_TYPE_P (t)
+	       && value_dependent_expression_p (orig_arg)))
 	/* We used to call digest_init here.  However, digest_init
 	   will report errors, which we don't want when complain
 	   is zero.  More importantly, digest_init will try too
@@ -8092,7 +8101,7 @@  convert_template_argument (tree parm,
 	      && TREE_CODE (TREE_TYPE (innertype)) == FUNCTION_TYPE
 	      && TREE_OPERAND_LENGTH (inner) > 0
               && reject_gcc_builtin (TREE_OPERAND (inner, 0)))
-              return error_mark_node;
+	    return error_mark_node;
         }
 
       if (TREE_CODE (val) == SCOPE_REF)
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
new file mode 100644
index 00000000000..3f47c58cd2a
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv3.C
@@ -0,0 +1,25 @@ 
+// PR c++/87145
+// { dg-do compile { target c++11 } }
+
+template<typename T, T t> struct integral_constant {
+  static constexpr T value = t;
+};
+
+enum class Enum : unsigned {};
+
+struct Pod {
+  unsigned val;
+
+  constexpr operator Enum() const {
+    return static_cast<Enum>(val);
+  }
+};
+
+template<unsigned N>
+constexpr void foo() {
+  using Foo = integral_constant<Enum, Pod{N}>;
+}
+
+int main() {
+  foo<2>();
+}
diff --git gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
new file mode 100644
index 00000000000..f4e3f00a585
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp0x/constexpr-conv4.C
@@ -0,0 +1,25 @@ 
+// PR c++/87145
+// { dg-do compile { target c++11 } }
+
+struct S {
+  int val;
+
+  constexpr operator int() const {
+    return static_cast<int>(val);
+  }
+};
+
+template<int N>
+struct F { };
+
+template<unsigned N>
+constexpr void foo() {
+  F<int{N}> f;
+  F<S{N}> f2;
+}
+
+int
+main()
+{
+  foo<2>();
+}