On Wed, 16 Apr 2025, Jason Merrill wrote: > Tested x86_64-pc-linux-gnu, applying to trunk. > > -- 8< -- > > If we already gave an error while parsing a function, we don't also need to > try to explain what's wrong with it when we later try to use it in a > constant-expression. In the new testcase explain_invalid_constexpr_fn > couldn't find anything still in the function to complain about, so it said > because: followed by nothing. > > We still try to constant-evaluate it to reduce error cascades, but we > shouldn't complain if it doesn't work very well. > > This flag is similar to CLASSTYPE_ERRONEOUS that I added a while back. > > PR c++/113360 > > gcc/cp/ChangeLog: > > * cp-tree.h (struct language_function): Add erroneous bit. > * constexpr.cc (explain_invalid_constexpr_fn): Return if set. > (cxx_eval_call_expression): Quiet if set. > * parser.cc (cp_parser_function_definition_after_declarator) > * pt.cc (instantiate_body): Set it. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp23/constexpr-nonlit18.C: Remove redundant message. > * g++.dg/cpp1y/constexpr-diag2.C: New test. > * g++.dg/cpp1y/pr63996.C: Adjust expected errors. > * g++.dg/template/explicit-args6.C: Likewise. > * g++.dg/cpp0x/constexpr-ice21.C: Likewise. > --- > gcc/cp/cp-tree.h | 2 ++ > gcc/cp/constexpr.cc | 18 +++++++++++++----- > gcc/cp/parser.cc | 5 +++++ > gcc/cp/pt.cc | 5 +++++ > gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C | 2 +- > gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C | 12 ++++++++++++ > gcc/testsuite/g++.dg/cpp1y/pr63996.C | 3 +-- > .../g++.dg/cpp23/constexpr-nonlit18.C | 2 +- > gcc/testsuite/g++.dg/template/explicit-args6.C | 8 +++++--- > 9 files changed, 45 insertions(+), 12 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 55f986e25c1..7798efba3db 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -2206,6 +2206,8 @@ struct GTY(()) language_function { > > BOOL_BITFIELD invalid_constexpr : 1; > BOOL_BITFIELD throwing_cleanup : 1; > + /* True if we gave any errors in this function. */ > + BOOL_BITFIELD erroneous : 1; > > hash_table<named_label_hash> *x_named_labels; > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index 4346b29abc6..d647a09269d 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -1048,6 +1048,12 @@ explain_invalid_constexpr_fn (tree fun) > { > static hash_set<tree> *diagnosed; > tree body; > + > + /* Don't try to explain a function we already complained about. */ > + if (function *f = DECL_STRUCT_FUNCTION (fun)) > + if (f->language->erroneous) > + return; > + > /* In C++23, a function marked 'constexpr' may not actually be a constant > expression. We haven't diagnosed the problem yet: -Winvalid-constexpr > wasn't enabled. The function was called, so diagnose why it cannot be > @@ -3079,6 +3085,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, > tree t, > } > > constexpr_ctx new_ctx = *ctx; > + ctx = &new_ctx; > if (DECL_CONSTRUCTOR_P (fun) && !ctx->object > && TREE_CODE (t) == AGGR_INIT_EXPR) > { > @@ -3088,16 +3095,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, > tree t, > tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), > NULL); > CONSTRUCTOR_NO_CLEARING (ctor) = true; > ctx->global->put_value (new_ctx.object, ctor); > - ctx = &new_ctx; > } > /* An immediate invocation is manifestly constant evaluated including the > arguments of the call, so use mce_true even for the argument > evaluation. */ > if (DECL_IMMEDIATE_FUNCTION_P (fun)) > - { > - new_ctx.manifestly_const_eval = mce_true; > - ctx = &new_ctx; > - } > + new_ctx.manifestly_const_eval = mce_true; > > /* We used to shortcut trivial constructor/op= here, but nowadays > we can only get a trivial function here with -fno-elide-constructors. > */ > @@ -3185,6 +3188,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, > tree t, > } > } > > + /* Don't complain about problems evaluating an ill-formed function. */ > + if (function *f = DECL_STRUCT_FUNCTION (fun)) > + if (f->language->erroneous) > + new_ctx.quiet = true; > + > int depth_ok = push_cx_call_context (t); > > /* Remember the object we are constructing or destructing. */ > diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc > index 812a7c5ae7d..3628cfefa07 100644 > --- a/gcc/cp/parser.cc > +++ b/gcc/cp/parser.cc > @@ -33634,6 +33634,8 @@ cp_parser_function_definition_after_declarator > (cp_parser* parser, > = parser->num_template_parameter_lists; > parser->num_template_parameter_lists = 0; > > + int errs = errorcount + sorrycount; > + > /* If the next token is `try', `__transaction_atomic', or > `__transaction_relaxed`, then we are looking at either > function-try-block > or function-transaction-block. Note that all of these include the > @@ -33653,6 +33655,9 @@ cp_parser_function_definition_after_declarator > (cp_parser* parser, > fn = finish_function (inline_p); > check_module_decl_linkage (fn); > > + if ((errorcount + sorrycount) > errs) > + DECL_STRUCT_FUNCTION (fn)->language->erroneous = true; > +
IIUC this means for template<class T> struct A { using type = T::type; }; void f() { A<int> a; } void g() { [] { A<char> a; }; } we'll mark A<int>, f, g and the lambda as erroneous, which doesn't seem ideal (since e.g. A<int> might still be "usable enough" despite its one member being erroneous), but it's probably the best we can do without making the mechanism a lot more complex. > if (modules_p () > && !inline_p > && TYPE_P (DECL_CONTEXT (fn)) > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index 51433e7c4ec..a71705fd085 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -27758,6 +27758,11 @@ instantiate_body (tree pattern, tree args, tree d, > bool nested_p) > > if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)) > cp_check_omp_declare_reduction (d); > + > + if (int errs = errorcount + sorrycount) > + if (errs > current_tinst_level->errors) > + if (function *f = DECL_STRUCT_FUNCTION (d)) > + f->language->erroneous = true; > } > > /* We're not deferring instantiation any more. */ > diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C > b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C > index 46273654f24..dcc404489be 100644 > --- a/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C > +++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-ice21.C > @@ -3,7 +3,7 @@ > > struct NoMut1 { int a, b; }; > struct NoMut3 : virtual NoMut1 { > - constexpr NoMut3(int a, int b) // { dg-error "virtual base" "" { target > c++23 } } > + constexpr NoMut3(int a, int b) > : NoMut1{a, b} > {} // { dg-error "virtual base" } > }; > diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C > b/gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C > new file mode 100644 > index 00000000000..93f3f10898e > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-diag2.C > @@ -0,0 +1,12 @@ > +// PR c++/113360 > +// { dg-do compile { target c++14 } } > + > +constexpr bool init_list() // { dg-bogus "because" } > +{ > + int total{}; > + for (int x : {1, 2, 3}) // { dg-error "initializer list" } > + total += x; > + return total == 6; > +} > + > +static_assert(init_list(), ""); // { dg-error "constant" } > diff --git a/gcc/testsuite/g++.dg/cpp1y/pr63996.C > b/gcc/testsuite/g++.dg/cpp1y/pr63996.C > index 8eee2e0af30..347c86cc63c 100644 > --- a/gcc/testsuite/g++.dg/cpp1y/pr63996.C > +++ b/gcc/testsuite/g++.dg/cpp1y/pr63996.C > @@ -1,5 +1,4 @@ > // { dg-do compile { target c++14 } } > -// { dg-additional-options "-Wno-return-type" } > > constexpr int > foo (int i) > @@ -8,4 +7,4 @@ foo (int i) > if (i == 23) return 0; > } > > -constexpr int j = foo (1); // { dg-error "flows off the end|in .constexpr. > expansion of" } > +constexpr int j = foo (1); > diff --git a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C > b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C > index 8e230ef3bc3..f8918148cda 100644 > --- a/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C > +++ b/gcc/testsuite/g++.dg/cpp23/constexpr-nonlit18.C > @@ -24,7 +24,7 @@ f3 () > } > > constexpr int > -f4 () // { dg-message "declared here" > "" { target c++20_down } } > +f4 () > { // { dg-message "is not usable as a > 'constexpr' function because:" "" { target c++23 } .-1 } > static const int a = f1 (1); // { dg-error "'a' defined > 'static' in 'constexpr' function only available with" "" { target c++20_down > } } > return 0; // { dg-error "'a' defined 'static' in > 'constexpr' context" "" { target c++23 } .-1 } > diff --git a/gcc/testsuite/g++.dg/template/explicit-args6.C > b/gcc/testsuite/g++.dg/template/explicit-args6.C > index 18663d7bcf9..0d9718c38f6 100644 > --- a/gcc/testsuite/g++.dg/template/explicit-args6.C > +++ b/gcc/testsuite/g++.dg/template/explicit-args6.C > @@ -24,10 +24,12 @@ frob() > > // narrowing check, reject negative values > return unsigned{N}; // { dg-prune-output "narrowing" } > -} // { dg-prune-output "flows off the end" } > -// { dg-prune-output "not a return-statement" } > +} > > -template<int N> void get_n(tuple& t) { get<frob<N>()>(t); } // { dg-error "" > } > +// This complains about calling frob only in C++11 because > +// maybe_save_constexpr_fundef fails; in later standards it succeeds, > +// and the evaluation failure is silent due to the earlier errors. > +template<int N> void get_n(tuple& t) { get<frob<N>()>(t); } // { dg-error "" > "" { target c++11_only } } > > int main() > { > > base-commit: dbffeadf7f682625c7ac8c251ee62c02c90f32df > -- > 2.49.0 > >