On Wed, Dec 01, 2021 at 12:17:47PM -0500, Patrick Palka wrote: > On Wed, Dec 1, 2021 at 10:26 AM Marek Polacek via Gcc-patches > <gcc-patches@gcc.gnu.org> wrote: > > > > Here we ICE on > > > > int f() requires (auto(0)); > > > > in do_auto_deduction when handling the auto: we're in a non-templated > > requires-expression which are parsed under processing_template_decl == 1 > > and empty current_template_parms, so 'current_template_args ()' will > > crash. This code is invalid as per "C++20 CA378: Remove non-templated > > constrained functions", but of course we shouldn't crash. > > FWIW it looks like we can trip over the same bug with valid code: > > static_assert(requires { auto(0); });
Right, I hadn't thought of this at all. So it's not just an ICE-on-invalid! > > Since in the scenario above it's expected that current_template_parms is > > null, I've just added a check, and let grokfndecl issue an error. > > I guess another approch would be to fake up a template parameter list > > before calling do_auto_deduction. > > > > For good measure, I've added several well-formed cases with auto(x) in > > a requires-expression. > > > > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? > > > > PR c++/103408 > > > > gcc/cp/ChangeLog: > > > > * pt.c (do_auto_deduction): Check current_template_parms before > > current_template_args (). > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/cpp23/auto-fncast9.C: New test. > > --- > > gcc/cp/pt.c | 2 +- > > gcc/testsuite/g++.dg/cpp23/auto-fncast9.C | 27 +++++++++++++++++++++++ > > 2 files changed, 28 insertions(+), 1 deletion(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast9.C > > > > diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c > > index f4b9d9673fb..012ca5d06c0 100644 > > --- a/gcc/cp/pt.c > > +++ b/gcc/cp/pt.c > > @@ -30041,7 +30041,7 @@ do_auto_deduction (tree type, tree init, tree > > auto_node, > > (but we still may have used them for constraint checking above). > > */; > > else if (context == adc_unify) > > targs = add_to_template_args (outer_targs, targs); > > - else if (processing_template_decl) > > + else if (processing_template_decl && current_template_parms) > > targs = add_to_template_args (current_template_args (), targs); > > return tsubst (type, targs, complain, NULL_TREE); > > Won't this mean the call to tsubst here will end up lowering the level > of the auto from 2 to 1 rather than replacing it with the actual > deduced type? Yes, exactly -- targs has depth 1 but the level of auto is 2. :( > It also looks like this approach doesn't handle static_assert(requires > { auto(auto(0)); }), probably due to this substitution issue. I guess > we could add a dummy level to 'targs' to work around this.. Thanks for pointing this out. Here's a v2 which uses a dummy level. The description hopefully explains what's going on here... Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk? -- >8 -- Here we ICE on int f() requires (auto(0)); in do_auto_deduction when handling the auto: we're in a non-templated requires-expression which are parsed under processing_template_decl == 1 and empty current_template_parms, so 'current_template_args ()' will crash. This code is invalid as per "C++20 CA378: Remove non-templated constrained functions", but of course we shouldn't crash. However, as Patrick pointed out, this is valid: static_assert(requires { auto(0); }); // #1 static_assert(requires { auto(auto(0)); }); // #2 To make #1 and #2 work, I'm playing games with faking up a dummy template parameter level to current_template_parms, which need elaborating a bit. autos are created with the level processing_template_decl + 1. When we call do_auto_deduction for the inner auto(0) in #2, the auto will therefore have level 2. type_unification_real in do_auto_deduction deduces targs to <int> (TMPL_ARGS_DEPTH = 1), so the return tsubst (type, targs, complain, NULL_TREE); will only reduce the level of the auto to 1. So it remains undeduced, causing the "invalid use of auto" error. If I create the dummy template parameter level, then this at the end of do_auto_deduction else if (processing_template_decl) targs = add_to_template_args (current_template_args (), targs); transforms <int> into <, int>, which has TMPL_ARGS_DEPTH = 2 and so the following tsubst actually replaces the auto with int, and things work. For good measure, I've added several well-formed cases with auto(x) in a requires-expression. PR c++/103408 gcc/cp/ChangeLog: * cp-tree.h (add_dummy_template_parameter_level): Declare. * pt.c (add_dummy_template_parameter_level): New function. (begin_template_parm_list): Use it. * semantics.c (finish_compound_literal): Call add_dummy_template_parameter_level if there are no template parameters. * typeck2.c (build_functional_cast_1): Likewise. gcc/testsuite/ChangeLog: * g++.dg/cpp23/auto-fncast9.C: New test. --- gcc/cp/cp-tree.h | 1 + gcc/cp/pt.c | 15 +++++++--- gcc/cp/semantics.c | 3 ++ gcc/cp/typeck2.c | 3 ++ gcc/testsuite/g++.dg/cpp23/auto-fncast9.C | 35 +++++++++++++++++++++++ 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp23/auto-fncast9.C diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 1ee2c57e83c..ad220980476 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -7174,6 +7174,7 @@ extern tree get_innermost_template_args (tree, int); extern void maybe_begin_member_template_processing (tree); extern void maybe_end_member_template_processing (void); extern tree finish_member_template_decl (tree); +extern void add_dummy_template_parameter_level (); extern void begin_template_parm_list (void); extern bool begin_specialization (void); extern void reset_specialization (void); diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index f4b9d9673fb..f1ef1dbde0f 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -689,6 +689,16 @@ strip_innermost_template_args (tree args, int extra_levels) return new_args; } +/* Add a dummy template parameter level to current_template_parms. */ + +void +add_dummy_template_parameter_level () +{ + current_template_parms = tree_cons (size_int (processing_template_decl), + make_tree_vec (0), + current_template_parms); +} + /* We've got a template header coming up; push to a new level for storing the parms. */ @@ -715,10 +725,7 @@ begin_template_parm_list (void) note_template_header (0); /* Add a dummy parameter level while we process the parameter list. */ - current_template_parms - = tree_cons (size_int (processing_template_decl), - make_tree_vec (0), - current_template_parms); + add_dummy_template_parameter_level (); } /* This routine is called when a specialization is declared. If it is diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index cd1956497f8..7e2179412b2 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -3143,6 +3143,9 @@ finish_compound_literal (tree type, tree compound_literal, pedwarn (input_location, OPT_Wc__23_extensions, "%<auto{x}%> only available with " "%<-std=c++2b%> or %<-std=gnu++2b%>"); + auto ctp = make_temp_override (current_template_parms); + if (processing_template_decl && !current_template_parms) + add_dummy_template_parameter_level (); type = do_auto_deduction (type, compound_literal, type, complain, adc_variable_type); if (type == error_mark_node) diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 3fb651a02ba..af20315c9e4 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -2202,6 +2202,7 @@ build_functional_cast_1 (location_t loc, tree exp, tree parms, if (tree anode = type_uses_auto (type)) { tree init; + auto ctp = make_temp_override (current_template_parms); if (CLASS_PLACEHOLDER_TEMPLATE (anode)) init = parms; /* C++23 auto(x). */ @@ -2213,6 +2214,8 @@ build_functional_cast_1 (location_t loc, tree exp, tree parms, pedwarn (loc, OPT_Wc__23_extensions, "%<auto(x)%> only available with " "%<-std=c++2b%> or %<-std=gnu++2b%>"); + if (processing_template_decl && !current_template_parms) + add_dummy_template_parameter_level (); } else { diff --git a/gcc/testsuite/g++.dg/cpp23/auto-fncast9.C b/gcc/testsuite/g++.dg/cpp23/auto-fncast9.C new file mode 100644 index 00000000000..b92a5f7b320 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp23/auto-fncast9.C @@ -0,0 +1,35 @@ +// PR c++/103408 +// { dg-do compile { target c++23 } } + +int bad1() requires (auto(true)); // { dg-error "constraints on a non-templated function" } + +template<bool B> +int f1() requires (auto(B)); + +template<typename T> +struct S { T t; constexpr operator bool() { return true; } }; + +int bad2() requires (bool(S{1})); // { dg-error "constraints on a non-templated function" } +int bad3() requires (bool(S(1))); // { dg-error "constraints on a non-templated function" } + +template<int N> +int f2() requires (bool(S{N})); + +template<int N> +int f3() requires (bool(S(N))); + +void +g () +{ + f1<true>(); + f2<42>(); + f3<42>(); +} + +static_assert(requires { auto(0); }); +static_assert(requires { auto(auto(0)); }); +static_assert(requires { auto(auto(auto(0))); }); + +static_assert(requires { auto{0}; }); +static_assert(requires { auto{auto{0}}; }); +static_assert(requires { auto{auto{auto{0}}}; }); base-commit: 860c56b5bc356960a4d0445dadc43ceddbe3c7e2 -- 2.33.1