Message ID | 20200316145739.2476758-1-polacek@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: Fix wrong no post-decrement operator error in template [PR94190] | expand |
On 3/16/20 10:57 AM, Marek Polacek wrote: > Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts > something that involves a class in a template, we must be prepared to > handle it. In this test, we have a class S and we're converting it > to long int& using a user-defined conversion since we're performing > -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls > build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before > the convert_like change it got *S::operator long int &(&b) whose type > is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type > is a reference type. So you need to make sure that your IMPLICIT_CONV_EXPR gets convert_from_reference'd at some point. Jason
On Mon, Mar 16, 2020 at 05:12:15PM -0400, Jason Merrill wrote: > On 3/16/20 10:57 AM, Marek Polacek wrote: > > Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts > > something that involves a class in a template, we must be prepared to > > handle it. In this test, we have a class S and we're converting it > > to long int& using a user-defined conversion since we're performing > > -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls > > build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before > > the convert_like change it got *S::operator long int &(&b) whose type > > is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type > > is a reference type. > > So you need to make sure that your IMPLICIT_CONV_EXPR gets > convert_from_reference'd at some point. Perhaps like the following? Bootstrapped/regtested on x86_64-linux, built Boost/cmcstl2. -- >8 -- Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts something that involves a class in a template, we must be prepared to handle it. In this test, we have a class S and we're converting it to long int& using a user-defined conversion since we're performing -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before the convert_like change it got *S::operator long int &(&b) whose type is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type is a reference type. But the !MAYBE_CLASS_TYPE_P switch doesn't handle reference types and so we complain. Fixed by calling convert_from_reference on the result of convert_like. PR c++/94190 - wrong no post-decrement operator error in template. * call.c (build_new_op_1): Use convert_from_reference on the result of convert_like. * g++.dg/conversion/op7.C: New test. --- gcc/cp/call.c | 3 +++ gcc/testsuite/g++.dg/conversion/op7.C | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 gcc/testsuite/g++.dg/conversion/op7.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 1715acc0ec3..d8b28573b95 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -6426,6 +6426,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags, { conv = strip_standard_conversion (conv); arg1 = convert_like (conv, arg1, complain); + arg1 = convert_from_reference (arg1); } if (arg2) @@ -6435,6 +6436,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags, { conv = strip_standard_conversion (conv); arg2 = convert_like (conv, arg2, complain); + arg2 = convert_from_reference (arg2); } } @@ -6445,6 +6447,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags, { conv = strip_standard_conversion (conv); arg3 = convert_like (conv, arg3, complain); + arg3 = convert_from_reference (arg3); } } } diff --git a/gcc/testsuite/g++.dg/conversion/op7.C b/gcc/testsuite/g++.dg/conversion/op7.C new file mode 100644 index 00000000000..c6401d109b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/conversion/op7.C @@ -0,0 +1,22 @@ +// PR c++/94190 - wrong no post-decrement operator error in template. + +struct S { operator long & (); } b; + +template<int> void +foo () +{ + b--; + ++b; + --b; + b++; + !b; + ~b; + +b; + -b; +} + +void +bar () +{ + foo<0> (); +} base-commit: 2691ffe6dbaffb704593dd6220178c28848b3855
On 3/16/20 10:01 PM, Marek Polacek wrote: > On Mon, Mar 16, 2020 at 05:12:15PM -0400, Jason Merrill wrote: >> On 3/16/20 10:57 AM, Marek Polacek wrote: >>> Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts >>> something that involves a class in a template, we must be prepared to >>> handle it. In this test, we have a class S and we're converting it >>> to long int& using a user-defined conversion since we're performing >>> -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls >>> build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before >>> the convert_like change it got *S::operator long int &(&b) whose type >>> is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type >>> is a reference type. >> >> So you need to make sure that your IMPLICIT_CONV_EXPR gets >> convert_from_reference'd at some point. > > Perhaps like the following? > > Bootstrapped/regtested on x86_64-linux, built Boost/cmcstl2. > > -- >8 -- > Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts > something that involves a class in a template, we must be prepared to > handle it. In this test, we have a class S and we're converting it > to long int& using a user-defined conversion since we're performing > -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls > build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before > the convert_like change it got *S::operator long int &(&b) whose type > is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type > is a reference type. But the !MAYBE_CLASS_TYPE_P switch doesn't handle > reference types and so we complain. > > Fixed by calling convert_from_reference on the result of convert_like. > > PR c++/94190 - wrong no post-decrement operator error in template. > * call.c (build_new_op_1): Use convert_from_reference on the result > of convert_like. The result of convert_like should already be dereferenced in this case. I think convert_like should only return a bare reference for ck_ref_bind, where we're explicitly requesting one. > * g++.dg/conversion/op7.C: New test. > --- > gcc/cp/call.c | 3 +++ > gcc/testsuite/g++.dg/conversion/op7.C | 22 ++++++++++++++++++++++ > 2 files changed, 25 insertions(+) > create mode 100644 gcc/testsuite/g++.dg/conversion/op7.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 1715acc0ec3..d8b28573b95 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -6426,6 +6426,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags, > { > conv = strip_standard_conversion (conv); > arg1 = convert_like (conv, arg1, complain); > + arg1 = convert_from_reference (arg1); > } > > if (arg2) > @@ -6435,6 +6436,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags, > { > conv = strip_standard_conversion (conv); > arg2 = convert_like (conv, arg2, complain); > + arg2 = convert_from_reference (arg2); > } > } > > @@ -6445,6 +6447,7 @@ build_new_op_1 (const op_location_t &loc, enum tree_code code, int flags, > { > conv = strip_standard_conversion (conv); > arg3 = convert_like (conv, arg3, complain); > + arg3 = convert_from_reference (arg3); > } > } > } > diff --git a/gcc/testsuite/g++.dg/conversion/op7.C b/gcc/testsuite/g++.dg/conversion/op7.C > new file mode 100644 > index 00000000000..c6401d109b4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/conversion/op7.C > @@ -0,0 +1,22 @@ > +// PR c++/94190 - wrong no post-decrement operator error in template. > + > +struct S { operator long & (); } b; > + > +template<int> void > +foo () > +{ > + b--; > + ++b; > + --b; > + b++; > + !b; > + ~b; > + +b; > + -b; > +} > + > +void > +bar () > +{ > + foo<0> (); > +} > > base-commit: 2691ffe6dbaffb704593dd6220178c28848b3855 >
On Tue, Mar 17, 2020 at 04:05:31PM -0400, Jason Merrill wrote: > On 3/16/20 10:01 PM, Marek Polacek wrote: > > On Mon, Mar 16, 2020 at 05:12:15PM -0400, Jason Merrill wrote: > > > On 3/16/20 10:57 AM, Marek Polacek wrote: > > > > Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts > > > > something that involves a class in a template, we must be prepared to > > > > handle it. In this test, we have a class S and we're converting it > > > > to long int& using a user-defined conversion since we're performing > > > > -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls > > > > build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before > > > > the convert_like change it got *S::operator long int &(&b) whose type > > > > is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type > > > > is a reference type. > > > > > > So you need to make sure that your IMPLICIT_CONV_EXPR gets > > > convert_from_reference'd at some point. > > > > Perhaps like the following? > > > > Bootstrapped/regtested on x86_64-linux, built Boost/cmcstl2. > > > > -- >8 -- > > Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts > > something that involves a class in a template, we must be prepared to > > handle it. In this test, we have a class S and we're converting it > > to long int& using a user-defined conversion since we're performing > > -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls > > build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before > > the convert_like change it got *S::operator long int &(&b) whose type > > is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type > > is a reference type. But the !MAYBE_CLASS_TYPE_P switch doesn't handle > > reference types and so we complain. > > > > Fixed by calling convert_from_reference on the result of convert_like. > > > > PR c++/94190 - wrong no post-decrement operator error in template. > > * call.c (build_new_op_1): Use convert_from_reference on the result > > of convert_like. > > The result of convert_like should already be dereferenced in this case. I > think convert_like should only return a bare reference for ck_ref_bind, > where we're explicitly requesting one. I tried to adjust all the appropriate return ...; in convert_like to return convert_from_reference (...); and then remove unnecessary calls to convert_from_reference after a call to convert_like elsewhere in the codebase, but that fails very badly. But it seems we can use convert_from_reference in convert_like when returning the new IMPLICIT_CONV_EXPR, like this: Bootstrapped/regtested on x86_64-linux, ok for trunk? -- >8 -- Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts something that involves a class in a template, we must be prepared to handle it. In this test, we have a class S and we're converting it to long int& using a user-defined conversion since we're performing -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before the convert_like change it got *S::operator long int &(&b) whose type is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type is a reference type. But the !MAYBE_CLASS_TYPE_P switch doesn't handle reference types and so we complain. Fixed by calling convert_from_reference on the result of convert_like. PR c++/94190 - wrong no post-decrement operator error in template. * call.c (convert_like_real): Use convert_from_reference on the result. * g++.dg/conversion/op7.C: New test. --- gcc/cp/call.c | 5 ++++- gcc/testsuite/g++.dg/conversion/op7.C | 22 ++++++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/conversion/op7.C diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 65a3ea35dee..bae4b2c0f52 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -7389,7 +7389,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, if (processing_template_decl && convs->kind != ck_identity && (CLASS_TYPE_P (totype) || CLASS_TYPE_P (TREE_TYPE (expr)))) - return build1 (IMPLICIT_CONV_EXPR, totype, expr); + { + expr = build1 (IMPLICIT_CONV_EXPR, totype, expr); + return convs->kind == ck_ref_bind ? expr : convert_from_reference (expr); + } switch (convs->kind) { diff --git a/gcc/testsuite/g++.dg/conversion/op7.C b/gcc/testsuite/g++.dg/conversion/op7.C new file mode 100644 index 00000000000..c6401d109b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/conversion/op7.C @@ -0,0 +1,22 @@ +// PR c++/94190 - wrong no post-decrement operator error in template. + +struct S { operator long & (); } b; + +template<int> void +foo () +{ + b--; + ++b; + --b; + b++; + !b; + ~b; + +b; + -b; +} + +void +bar () +{ + foo<0> (); +} base-commit: a3586eeb88414e77c7cccb69362b8d04562536b6
On 3/23/20 11:06 AM, Marek Polacek wrote: > On Tue, Mar 17, 2020 at 04:05:31PM -0400, Jason Merrill wrote: >> On 3/16/20 10:01 PM, Marek Polacek wrote: >>> On Mon, Mar 16, 2020 at 05:12:15PM -0400, Jason Merrill wrote: >>>> On 3/16/20 10:57 AM, Marek Polacek wrote: >>>>> Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts >>>>> something that involves a class in a template, we must be prepared to >>>>> handle it. In this test, we have a class S and we're converting it >>>>> to long int& using a user-defined conversion since we're performing >>>>> -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls >>>>> build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before >>>>> the convert_like change it got *S::operator long int &(&b) whose type >>>>> is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type >>>>> is a reference type. >>>> >>>> So you need to make sure that your IMPLICIT_CONV_EXPR gets >>>> convert_from_reference'd at some point. >>> >>> Perhaps like the following? >>> >>> Bootstrapped/regtested on x86_64-linux, built Boost/cmcstl2. >>> >>> -- >8 -- >>> Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts >>> something that involves a class in a template, we must be prepared to >>> handle it. In this test, we have a class S and we're converting it >>> to long int& using a user-defined conversion since we're performing >>> -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls >>> build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before >>> the convert_like change it got *S::operator long int &(&b) whose type >>> is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type >>> is a reference type. But the !MAYBE_CLASS_TYPE_P switch doesn't handle >>> reference types and so we complain. >>> >>> Fixed by calling convert_from_reference on the result of convert_like. >>> >>> PR c++/94190 - wrong no post-decrement operator error in template. >>> * call.c (build_new_op_1): Use convert_from_reference on the result >>> of convert_like. >> >> The result of convert_like should already be dereferenced in this case. I >> think convert_like should only return a bare reference for ck_ref_bind, >> where we're explicitly requesting one. > > I tried to adjust all the appropriate return ...; in convert_like to return > convert_from_reference (...); and then remove unnecessary calls to > convert_from_reference after a call to convert_like elsewhere in the codebase, > but that fails very badly. > > But it seems we can use convert_from_reference in convert_like when returning > the new IMPLICIT_CONV_EXPR, like this: > > Bootstrapped/regtested on x86_64-linux, ok for trunk? OK. > -- >8 -- > Now that convert_like creates an IMPLICIT_CONV_EXPR when it converts > something that involves a class in a template, we must be prepared to > handle it. In this test, we have a class S and we're converting it > to long int& using a user-defined conversion since we're performing > -- on it. So cp_build_unary_op/POSTDECREMENT_EXPR calls > build_expr_type_conversion which gets the IMPLICIT_CONV_EXPR. Before > the convert_like change it got *S::operator long int &(&b) whose type > is long int but now it gets IMPLICIT_CONV_EXPR<long int&>(b) whose type > is a reference type. But the !MAYBE_CLASS_TYPE_P switch doesn't handle > reference types and so we complain. > > Fixed by calling convert_from_reference on the result of convert_like. > > PR c++/94190 - wrong no post-decrement operator error in template. > * call.c (convert_like_real): Use convert_from_reference on the result. > > * g++.dg/conversion/op7.C: New test. > --- > gcc/cp/call.c | 5 ++++- > gcc/testsuite/g++.dg/conversion/op7.C | 22 ++++++++++++++++++++++ > 2 files changed, 26 insertions(+), 1 deletion(-) > create mode 100644 gcc/testsuite/g++.dg/conversion/op7.C > > diff --git a/gcc/cp/call.c b/gcc/cp/call.c > index 65a3ea35dee..bae4b2c0f52 100644 > --- a/gcc/cp/call.c > +++ b/gcc/cp/call.c > @@ -7389,7 +7389,10 @@ convert_like_real (conversion *convs, tree expr, tree fn, int argnum, > if (processing_template_decl > && convs->kind != ck_identity > && (CLASS_TYPE_P (totype) || CLASS_TYPE_P (TREE_TYPE (expr)))) > - return build1 (IMPLICIT_CONV_EXPR, totype, expr); > + { > + expr = build1 (IMPLICIT_CONV_EXPR, totype, expr); > + return convs->kind == ck_ref_bind ? expr : convert_from_reference (expr); > + } > > switch (convs->kind) > { > diff --git a/gcc/testsuite/g++.dg/conversion/op7.C b/gcc/testsuite/g++.dg/conversion/op7.C > new file mode 100644 > index 00000000000..c6401d109b4 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/conversion/op7.C > @@ -0,0 +1,22 @@ > +// PR c++/94190 - wrong no post-decrement operator error in template. > + > +struct S { operator long & (); } b; > + > +template<int> void > +foo () > +{ > + b--; > + ++b; > + --b; > + b++; > + !b; > + ~b; > + +b; > + -b; > +} > + > +void > +bar () > +{ > + foo<0> (); > +} > > base-commit: a3586eeb88414e77c7cccb69362b8d04562536b6 >
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c index 22a72c08c45..0c285ed4fe9 100644 --- a/gcc/cp/cvt.c +++ b/gcc/cp/cvt.c @@ -1732,7 +1732,7 @@ build_type_conversion (tree xtype, tree expr) tree build_expr_type_conversion (int desires, tree expr, bool complain) { - tree basetype = TREE_TYPE (expr); + tree basetype = non_reference (TREE_TYPE (expr)); tree conv = NULL_TREE; tree winner = NULL_TREE; diff --git a/gcc/testsuite/g++.dg/conversion/op7.C b/gcc/testsuite/g++.dg/conversion/op7.C new file mode 100644 index 00000000000..c6401d109b4 --- /dev/null +++ b/gcc/testsuite/g++.dg/conversion/op7.C @@ -0,0 +1,22 @@ +// PR c++/94190 - wrong no post-decrement operator error in template. + +struct S { operator long & (); } b; + +template<int> void +foo () +{ + b--; + ++b; + --b; + b++; + !b; + ~b; + +b; + -b; +} + +void +bar () +{ + foo<0> (); +}