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; + 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