Message ID | 20230606182953.815966-1-ppalka@redhat.com |
---|---|
State | New |
Headers | show |
Series | c++: unsynthesized defaulted constexpr fn [PR110122] | expand |
On 6/6/23 14:29, Patrick Palka wrote: > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk? > > -- >8 -- > > In the second testcase of PR110122, during regeneration of the generic > lambda with V=Bar{}, substitution followed by coerce_template_parms for > A<V>'s template argument naturally yields a copy of V in terms of Bar's > (implicitly) defaulted copy constructor. > > This however happens inside a template context so although we introduced > a use of the copy constructor, mark_used didn't actually synthesize it, > which causes subsequent constant evaluation of the template argument to > fail with: > > nontype-class58.C: In instantiation of ‘void f() [with Bar V = Bar{Foo()}]’: > nontype-class58.C:22:11: required from here > nontype-class58.C:18:18: error: ‘constexpr Bar::Bar(const Bar&)’ used before its definition > > Conveniently we already make sure to instantiate eligible constexpr > functions before such (manifestly) constant evaluation, as per P0859R0. > So this patch fixes this by making sure to synthesize eligible defaulted > constexpr functions beforehand as well. We probably also want to do this in cxx_eval_call_expression, under > /* We can't defer instantiating the function any longer. */ Jason
On Wed, 7 Jun 2023, Jason Merrill wrote: > On 6/6/23 14:29, Patrick Palka wrote: > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > > trunk? > > > > -- >8 -- > > > > In the second testcase of PR110122, during regeneration of the generic > > lambda with V=Bar{}, substitution followed by coerce_template_parms for > > A<V>'s template argument naturally yields a copy of V in terms of Bar's > > (implicitly) defaulted copy constructor. > > > > This however happens inside a template context so although we introduced > > a use of the copy constructor, mark_used didn't actually synthesize it, > > which causes subsequent constant evaluation of the template argument to > > fail with: > > > > nontype-class58.C: In instantiation of ‘void f() [with Bar V = > > Bar{Foo()}]’: > > nontype-class58.C:22:11: required from here > > nontype-class58.C:18:18: error: ‘constexpr Bar::Bar(const Bar&)’ used > > before its definition > > > > Conveniently we already make sure to instantiate eligible constexpr > > functions before such (manifestly) constant evaluation, as per P0859R0. > > So this patch fixes this by making sure to synthesize eligible defaulted > > constexpr functions beforehand as well. > > We probably also want to do this in cxx_eval_call_expression, under Makes sense, like so? I'm not sure if it's possible to write a test for which this code path makes an observable difference, but I verified the code path is hit a couple of times throughout the testsuite (mainly from fold_non_dependent_expr called from build_non_dependent_expr). Bootstrapped and regtested on x86_64-pc-linux-gnu. -->8 -- PR c++/110122 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_call_expression): Also synthesize eligible defaulted functions. (instantiate_cx_fn_r): Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/nontype-class58.C: New test. --- gcc/cp/constexpr.cc | 14 ++++++++---- gcc/testsuite/g++.dg/cpp2a/nontype-class58.C | 23 ++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class58.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 8f7f0b7d325..9122a5efa65 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -2897,7 +2897,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, /* We can't defer instantiating the function any longer. */ if (!DECL_INITIAL (fun) - && DECL_TEMPLOID_INSTANTIATION (fun) + && (DECL_TEMPLOID_INSTANTIATION (fun) || DECL_DEFAULTED_FN (fun)) && !uid_sensitive_constexpr_evaluation_p ()) { location_t save_loc = input_location; @@ -2905,7 +2905,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, ++function_depth; if (ctx->manifestly_const_eval == mce_true) FNDECL_MANIFESTLY_CONST_EVALUATED (fun) = true; - instantiate_decl (fun, /*defer_ok*/false, /*expl_inst*/false); + if (DECL_TEMPLOID_INSTANTIATION (fun)) + instantiate_decl (fun, /*defer_ok*/false, /*expl_inst*/false); + else + synthesize_method (fun); --function_depth; input_location = save_loc; } @@ -8110,11 +8113,14 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/) && DECL_DECLARED_CONSTEXPR_P (*tp) && !DECL_INITIAL (*tp) && !trivial_fn_p (*tp) - && DECL_TEMPLOID_INSTANTIATION (*tp) + && (DECL_TEMPLOID_INSTANTIATION (*tp) || DECL_DEFAULTED_FN (*tp)) && !uid_sensitive_constexpr_evaluation_p ()) { ++function_depth; - instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); + if (DECL_TEMPLOID_INSTANTIATION (*tp)) + instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); + else + synthesize_method (*tp); --function_depth; } else if (TREE_CODE (*tp) == CALL_EXPR diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C new file mode 100644 index 00000000000..6e40698da2f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C @@ -0,0 +1,23 @@ +// PR c++/110122 +// { dg-do compile { target c++20 } } + +struct Foo { + Foo() = default; + constexpr Foo(const Foo&) { } +}; + +struct Bar { + Foo _; +}; + +template<Bar V> +struct A { }; + +template<Bar V> +void f() { + [](auto){ A<V> d; }(0); // { dg-bogus "used before its definition" } +}; + +int main() { + f<Bar{}>(); +}
On 6/8/23 15:54, Patrick Palka wrote: > On Wed, 7 Jun 2023, Jason Merrill wrote: > >> On 6/6/23 14:29, Patrick Palka wrote: >>> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for >>> trunk? >>> >>> -- >8 -- >>> >>> In the second testcase of PR110122, during regeneration of the generic >>> lambda with V=Bar{}, substitution followed by coerce_template_parms for >>> A<V>'s template argument naturally yields a copy of V in terms of Bar's >>> (implicitly) defaulted copy constructor. >>> >>> This however happens inside a template context so although we introduced >>> a use of the copy constructor, mark_used didn't actually synthesize it, >>> which causes subsequent constant evaluation of the template argument to >>> fail with: >>> >>> nontype-class58.C: In instantiation of ‘void f() [with Bar V = >>> Bar{Foo()}]’: >>> nontype-class58.C:22:11: required from here >>> nontype-class58.C:18:18: error: ‘constexpr Bar::Bar(const Bar&)’ used >>> before its definition >>> >>> Conveniently we already make sure to instantiate eligible constexpr >>> functions before such (manifestly) constant evaluation, as per P0859R0. >>> So this patch fixes this by making sure to synthesize eligible defaulted >>> constexpr functions beforehand as well. >> >> We probably also want to do this in cxx_eval_call_expression, under > > Makes sense, like so? I'm not sure if it's possible to write a test > for which this code path makes an observable difference, but I verified > the code path is hit a couple of times throughout the testsuite (mainly > from fold_non_dependent_expr called from build_non_dependent_expr). > Bootstrapped and regtested on x86_64-pc-linux-gnu. OK. > -->8 -- > > PR c++/110122 > > gcc/cp/ChangeLog: > > * constexpr.cc (cxx_eval_call_expression): Also synthesize > eligible defaulted functions. > (instantiate_cx_fn_r): Likewise. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp2a/nontype-class58.C: New test. > --- > gcc/cp/constexpr.cc | 14 ++++++++---- > gcc/testsuite/g++.dg/cpp2a/nontype-class58.C | 23 ++++++++++++++++++++ > 2 files changed, 33 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/nontype-class58.C > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index 8f7f0b7d325..9122a5efa65 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -2897,7 +2897,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > > /* We can't defer instantiating the function any longer. */ > if (!DECL_INITIAL (fun) > - && DECL_TEMPLOID_INSTANTIATION (fun) > + && (DECL_TEMPLOID_INSTANTIATION (fun) || DECL_DEFAULTED_FN (fun)) > && !uid_sensitive_constexpr_evaluation_p ()) > { > location_t save_loc = input_location; > @@ -2905,7 +2905,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, > ++function_depth; > if (ctx->manifestly_const_eval == mce_true) > FNDECL_MANIFESTLY_CONST_EVALUATED (fun) = true; > - instantiate_decl (fun, /*defer_ok*/false, /*expl_inst*/false); > + if (DECL_TEMPLOID_INSTANTIATION (fun)) > + instantiate_decl (fun, /*defer_ok*/false, /*expl_inst*/false); > + else > + synthesize_method (fun); > --function_depth; > input_location = save_loc; > } > @@ -8110,11 +8113,14 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/) > && DECL_DECLARED_CONSTEXPR_P (*tp) > && !DECL_INITIAL (*tp) > && !trivial_fn_p (*tp) > - && DECL_TEMPLOID_INSTANTIATION (*tp) > + && (DECL_TEMPLOID_INSTANTIATION (*tp) || DECL_DEFAULTED_FN (*tp)) > && !uid_sensitive_constexpr_evaluation_p ()) > { > ++function_depth; > - instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); > + if (DECL_TEMPLOID_INSTANTIATION (*tp)) > + instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); > + else > + synthesize_method (*tp); > --function_depth; > } > else if (TREE_CODE (*tp) == CALL_EXPR > diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C > new file mode 100644 > index 00000000000..6e40698da2f > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C > @@ -0,0 +1,23 @@ > +// PR c++/110122 > +// { dg-do compile { target c++20 } } > + > +struct Foo { > + Foo() = default; > + constexpr Foo(const Foo&) { } > +}; > + > +struct Bar { > + Foo _; > +}; > + > +template<Bar V> > +struct A { }; > + > +template<Bar V> > +void f() { > + [](auto){ A<V> d; }(0); // { dg-bogus "used before its definition" } > +}; > + > +int main() { > + f<Bar{}>(); > +}
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 8f7f0b7d325..a7efebcded1 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -8110,11 +8110,14 @@ instantiate_cx_fn_r (tree *tp, int *walk_subtrees, void */*data*/) && DECL_DECLARED_CONSTEXPR_P (*tp) && !DECL_INITIAL (*tp) && !trivial_fn_p (*tp) - && DECL_TEMPLOID_INSTANTIATION (*tp) + && (DECL_TEMPLOID_INSTANTIATION (*tp) || DECL_DEFAULTED_FN (*tp)) && !uid_sensitive_constexpr_evaluation_p ()) { ++function_depth; - instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); + if (DECL_TEMPLOID_INSTANTIATION (*tp)) + instantiate_decl (*tp, /*defer_ok*/false, /*expl_inst*/false); + else + synthesize_method (*tp); --function_depth; } else if (TREE_CODE (*tp) == CALL_EXPR diff --git a/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C b/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C new file mode 100644 index 00000000000..6e40698da2f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/nontype-class58.C @@ -0,0 +1,23 @@ +// PR c++/110122 +// { dg-do compile { target c++20 } } + +struct Foo { + Foo() = default; + constexpr Foo(const Foo&) { } +}; + +struct Bar { + Foo _; +}; + +template<Bar V> +struct A { }; + +template<Bar V> +void f() { + [](auto){ A<V> d; }(0); // { dg-bogus "used before its definition" } +}; + +int main() { + f<Bar{}>(); +}