From f968ef9b8df2bc2287e5e7e87299e5a2a44e8c94 Mon Sep 17 00:00:00 2001 From: Jakub Jelinek Date: Sat, 2 Nov 2019 00:28:20 +0100 Subject: [PATCH] PR c++/88335 - Implement P1073R3: Immediate functions PR c++/88335 - Implement P1073R3: Immediate functions c-family/ * c-common.h (enum rid): Add RID_CONSTEVAL. * c-common.c (c_common_reswords): Add consteval. cp/ * cp-tree.h (struct lang_decl_fn): Add immediate_fn_p bit. (DECL_IMMEDIATE_FUNCTION_P, SET_DECL_IMMEDIATE_FUNCTION_P): Define. (enum cp_decl_spec): Add ds_consteval. (fold_non_dependent_expr): Add another tree argument defaulted to NULL_TREE. * name-lookup.h (struct cp_binding_level): Add immediate_fn_ctx_p member. * parser.c (cp_keyword_starts_decl_specifier_p): Adjust comments for C++11 and C++20 specifiers. Handle RID_CONSTEVAL. (CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): Adjust comment. (CP_PARSER_FLAGS_CONSTEVAL): New. (cp_parser_skip_balanced_tokens): New forward declaration. (cp_parser_lambda_declarator_opt): Handle ds_consteval. Set current_binding_level->immediate_fn_ctx_p before parsing parameter list if decl-specifier-seq contains consteval specifier. (cp_parser_decl_specifier_seq): Handle RID_CONSTEVAL. (cp_parser_explicit_instantiation): Diagnose explicit instantiation with consteval specifier. (cp_parser_init_declarator): For consteval or into flags CP_PARSER_FLAGS_CONSTEVAL. (cp_parser_direct_declarator): If CP_PARSER_FLAGS_CONSTEVAL, set current_binding_level->immediate_fn_ctx_p in the sk_function_parms scope. (set_and_check_decl_spec_loc): Add consteval entry, formatting fix. * call.c (build_addr_func): For direct calls to immediate functions use build_address rather than decay_conversion. (build_over_call): Evaluate immediate function invocations. * error.c (dump_function_decl): Handle DECL_IMMEDIATE_FUNCTION_P. * semantics.c (expand_or_defer_fn_1): Use tentative linkage and don't call mark_needed for immediate functions. * typeck.c (cxx_sizeof_or_alignof_expr): Likewise. Formatting fix. (cp_build_addr_expr_1): Reject taking address of immediate function outside of immediate function. * decl.c (validate_constexpr_redeclaration): Diagnose consteval vs. non-consteval or vice versa redeclaration. Use SET_DECL_IMMEDIATE_FUNCTION_P if new_decl is immediate function. (check_tag_decl): Use %qs with keyword string to simplify translation. Handle ds_consteval. (start_decl): Adjust diagnostics for static or thread_local variables in immediate functions. (grokfndecl): Call sorry_at on virtual consteval. Use %qs with keyword to string to simplify translation. Diagnose consteval main. Use SET_DECL_IMMEDIATE_FUNCTION_P for consteval. (grokdeclarator): Handle consteval. Use %qs with keyword strings to simplify translation. Use separate ifs instead of chained else if for invalid specifiers. For constinit clear constinit_p rather than constexpr_p. * constexpr.c (find_immediate_fndecl): New function. (cxx_eval_outermost_constant_expr): Allow consteval calls returning void. Diagnose returning address of immediate function from consteval evaluation. (fold_non_dependent_expr_template): Add OBJECT argument, pass it through to cxx_eval_outermost_constant_expr. (fold_non_dependent_expr): Add OBJECT argument, pass it through to fold_non_dependent_expr_template. (fold_non_dependent_init): Adjust fold_non_dependent_expr_template caller. * method.c (defaulted_late_check): Adjust diagnostics for consteval. * lambda.c (maybe_add_lambda_conv_op): Copy over DECL_DECLARED_CONSTEXPR_P and DECL_IMMEDIATE_FUNCTION_P bits from callop to both artificial functions. * init.c (build_value_init): Don't do further processing if build_special_member_call returned a TREE_CONSTANT. Formatting fix. testsuite/ * g++.dg/cpp2a/consteval1.C: New test. * g++.dg/cpp2a/consteval2.C: New test. * g++.dg/cpp2a/consteval3.C: New test. * g++.dg/cpp2a/consteval4.C: New test. * g++.dg/cpp2a/consteval5.C: New test. * g++.dg/cpp2a/consteval6.C: New test. * g++.dg/cpp2a/consteval7.C: New test. * g++.dg/cpp2a/consteval8.C: New test. * g++.dg/cpp2a/consteval9.C: New test. * g++.dg/cpp2a/consteval10.C: New test. * g++.dg/cpp2a/consteval11.C: New test. * g++.dg/cpp2a/consteval12.C: New test. * g++.dg/cpp2a/consteval13.C: New test. * g++.dg/cpp2a/consteval14.C: New test. * g++.dg/ext/consteval1.C: New test. From-SVN: r277733 --- gcc/c-family/ChangeLog | 6 + gcc/c-family/c-common.c | 1 + gcc/c-family/c-common.h | 2 +- gcc/cp/ChangeLog | 65 ++++++++ gcc/cp/call.c | 71 +++++++++ gcc/cp/constexpr.c | 70 ++++++++- gcc/cp/cp-tree.h | 15 +- gcc/cp/decl.c | 182 ++++++++++++++++++----- gcc/cp/error.c | 4 +- gcc/cp/init.c | 12 +- gcc/cp/lambda.c | 6 + gcc/cp/method.c | 5 +- gcc/cp/name-lookup.h | 5 +- gcc/cp/parser.c | 59 +++++++- gcc/cp/semantics.c | 3 +- gcc/cp/typeck.c | 10 ++ gcc/testsuite/ChangeLog | 17 +++ gcc/testsuite/g++.dg/cpp2a/consteval1.C | 37 +++++ gcc/testsuite/g++.dg/cpp2a/consteval10.C | 3 + gcc/testsuite/g++.dg/cpp2a/consteval11.C | 140 +++++++++++++++++ gcc/testsuite/g++.dg/cpp2a/consteval12.C | 15 ++ gcc/testsuite/g++.dg/cpp2a/consteval13.C | 17 +++ gcc/testsuite/g++.dg/cpp2a/consteval14.C | 30 ++++ gcc/testsuite/g++.dg/cpp2a/consteval2.C | 17 +++ gcc/testsuite/g++.dg/cpp2a/consteval3.C | 63 ++++++++ gcc/testsuite/g++.dg/cpp2a/consteval4.C | 29 ++++ gcc/testsuite/g++.dg/cpp2a/consteval5.C | 42 ++++++ gcc/testsuite/g++.dg/cpp2a/consteval6.C | 26 ++++ gcc/testsuite/g++.dg/cpp2a/consteval7.C | 13 ++ gcc/testsuite/g++.dg/cpp2a/consteval8.C | 29 ++++ gcc/testsuite/g++.dg/cpp2a/consteval9.C | 31 ++++ gcc/testsuite/g++.dg/ext/consteval1.C | 6 + 32 files changed, 967 insertions(+), 64 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval1.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval10.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval11.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval12.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval13.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval14.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval2.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval3.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval4.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval5.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval6.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval7.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval8.C create mode 100644 gcc/testsuite/g++.dg/cpp2a/consteval9.C create mode 100644 gcc/testsuite/g++.dg/ext/consteval1.C diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 372f452dc2d..9bc219049a3 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,9 @@ +2019-11-02 Jakub Jelinek + + PR c++/88335 - Implement P1073R3: Immediate functions + * c-common.h (enum rid): Add RID_CONSTEVAL. + * c-common.c (c_common_reswords): Add consteval. + 2019-10-30 Nathan Sidwell * c-cppbuiltin.c (c_cpp_builtins): Add 'L' suffix to feature diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 79c047c4730..743ef04918d 100644 --- a/gcc/c-family/c-common.c +++ b/gcc/c-family/c-common.c @@ -459,6 +459,7 @@ const struct c_common_resword c_common_reswords[] = { "char32_t", RID_CHAR32, D_CXXONLY | D_CXX11 | D_CXXWARN }, { "class", RID_CLASS, D_CXX_OBJC | D_CXXWARN }, { "const", RID_CONST, 0 }, + { "consteval", RID_CONSTEVAL, D_CXXONLY | D_CXX20 | D_CXXWARN }, { "constexpr", RID_CONSTEXPR, D_CXXONLY | D_CXX11 | D_CXXWARN }, { "constinit", RID_CONSTINIT, D_CXXONLY | D_CXX20 | D_CXXWARN }, { "const_cast", RID_CONSTCAST, D_CXXONLY | D_CXXWARN }, diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 42426ef6e1d..5a24a7eb2f7 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -181,7 +181,7 @@ enum rid RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT, /* C++20 */ - RID_CONSTINIT, + RID_CONSTINIT, RID_CONSTEVAL, /* char8_t */ RID_CHAR8, diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 50da698107d..06a4e31578f 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,70 @@ 2019-11-02 Jakub Jelinek + PR c++/88335 - Implement P1073R3: Immediate functions + * cp-tree.h (struct lang_decl_fn): Add immediate_fn_p bit. + (DECL_IMMEDIATE_FUNCTION_P, SET_DECL_IMMEDIATE_FUNCTION_P): Define. + (enum cp_decl_spec): Add ds_consteval. + (fold_non_dependent_expr): Add another tree argument defaulted to + NULL_TREE. + * name-lookup.h (struct cp_binding_level): Add immediate_fn_ctx_p + member. + * parser.c (cp_keyword_starts_decl_specifier_p): Adjust comments + for C++11 and C++20 specifiers. Handle RID_CONSTEVAL. + (CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR): Adjust comment. + (CP_PARSER_FLAGS_CONSTEVAL): New. + (cp_parser_skip_balanced_tokens): New forward declaration. + (cp_parser_lambda_declarator_opt): Handle ds_consteval. Set + current_binding_level->immediate_fn_ctx_p before parsing parameter + list if decl-specifier-seq contains consteval specifier. + (cp_parser_decl_specifier_seq): Handle RID_CONSTEVAL. + (cp_parser_explicit_instantiation): Diagnose explicit instantiation + with consteval specifier. + (cp_parser_init_declarator): For consteval or into flags + CP_PARSER_FLAGS_CONSTEVAL. + (cp_parser_direct_declarator): If CP_PARSER_FLAGS_CONSTEVAL, set + current_binding_level->immediate_fn_ctx_p in the sk_function_parms + scope. + (set_and_check_decl_spec_loc): Add consteval entry, formatting fix. + * call.c (build_addr_func): For direct calls to immediate functions + use build_address rather than decay_conversion. + (build_over_call): Evaluate immediate function invocations. + * error.c (dump_function_decl): Handle DECL_IMMEDIATE_FUNCTION_P. + * semantics.c (expand_or_defer_fn_1): Use tentative linkage and don't + call mark_needed for immediate functions. + * typeck.c (cxx_sizeof_or_alignof_expr): Likewise. Formatting fix. + (cp_build_addr_expr_1): Reject taking address of immediate function + outside of immediate function. + * decl.c (validate_constexpr_redeclaration): Diagnose consteval + vs. non-consteval or vice versa redeclaration. Use + SET_DECL_IMMEDIATE_FUNCTION_P if new_decl is immediate function. + (check_tag_decl): Use %qs with keyword string to simplify translation. + Handle ds_consteval. + (start_decl): Adjust diagnostics for static or thread_local variables + in immediate functions. + (grokfndecl): Call sorry_at on virtual consteval. Use %qs with keyword + to string to simplify translation. Diagnose consteval main. Use + SET_DECL_IMMEDIATE_FUNCTION_P for consteval. + (grokdeclarator): Handle consteval. Use %qs with keyword strings to + simplify translation. Use separate ifs instead of chained else if + for invalid specifiers. For constinit clear constinit_p rather than + constexpr_p. + * constexpr.c (find_immediate_fndecl): New function. + (cxx_eval_outermost_constant_expr): Allow consteval calls returning + void. Diagnose returning address of immediate function from consteval + evaluation. + (fold_non_dependent_expr_template): Add OBJECT argument, pass it + through to cxx_eval_outermost_constant_expr. + (fold_non_dependent_expr): Add OBJECT argument, pass it through to + fold_non_dependent_expr_template. + (fold_non_dependent_init): Adjust fold_non_dependent_expr_template + caller. + * method.c (defaulted_late_check): Adjust diagnostics for consteval. + * lambda.c (maybe_add_lambda_conv_op): Copy over + DECL_DECLARED_CONSTEXPR_P and DECL_IMMEDIATE_FUNCTION_P bits from + callop to both artificial functions. + * init.c (build_value_init): Don't do further processing if + build_special_member_call returned a TREE_CONSTANT. Formatting fix. + PR c++/91369 - Implement P0784R7: constexpr new * cp-tree.h (CALL_FROM_NEW_OR_DELETE_P): Define. * init.c (build_new_1, build_vec_delete_1, build_delete): Set diff --git a/gcc/cp/call.c b/gcc/cp/call.c index b0c6370107d..3dac31a37b9 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -289,6 +289,9 @@ build_addr_func (tree function, tsubst_flags_t complain) } function = build_address (function); } + else if (TREE_CODE (function) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (function)) + function = build_address (function); else function = decay_conversion (function, complain, /*reject_builtin=*/false); @@ -8145,6 +8148,40 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) addr, nargs, argarray); if (TREE_THIS_VOLATILE (fn) && cfun) current_function_returns_abnormally = 1; + if (TREE_CODE (fn) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (fn) + && (current_function_decl == NULL_TREE + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) + && (current_binding_level->kind != sk_function_parms + || !current_binding_level->immediate_fn_ctx_p)) + { + tree obj_arg = NULL_TREE, exprimm = expr; + if (DECL_CONSTRUCTOR_P (fn)) + obj_arg = first_arg; + if (obj_arg + && is_dummy_object (obj_arg) + && !type_dependent_expression_p (obj_arg)) + { + exprimm = build_cplus_new (DECL_CONTEXT (fn), expr, complain); + obj_arg = NULL_TREE; + } + /* Look through *(const T *)&obj. */ + else if (obj_arg && TREE_CODE (obj_arg) == INDIRECT_REF) + { + tree addr = TREE_OPERAND (obj_arg, 0); + STRIP_NOPS (addr); + if (TREE_CODE (addr) == ADDR_EXPR) + { + tree typeo = TREE_TYPE (obj_arg); + tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); + if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) + obj_arg = TREE_OPERAND (addr, 0); + } + } + fold_non_dependent_expr (exprimm, complain, + /*manifestly_const_eval=*/true, + obj_arg); + } return convert_from_reference (expr); } @@ -8744,6 +8781,40 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain) if (TREE_CODE (c) == CALL_EXPR) TREE_NO_WARNING (c) = 1; } + if (TREE_CODE (fn) == ADDR_EXPR) + { + tree fndecl = STRIP_TEMPLATE (TREE_OPERAND (fn, 0)); + if (TREE_CODE (fndecl) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (fndecl) + && (current_function_decl == NULL_TREE + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl)) + && (current_binding_level->kind != sk_function_parms + || !current_binding_level->immediate_fn_ctx_p)) + { + tree obj_arg = NULL_TREE; + if (DECL_CONSTRUCTOR_P (fndecl)) + obj_arg = cand->first_arg ? cand->first_arg : (*args)[0]; + if (obj_arg && is_dummy_object (obj_arg)) + { + call = build_cplus_new (DECL_CONTEXT (fndecl), call, complain); + obj_arg = NULL_TREE; + } + /* Look through *(const T *)&obj. */ + else if (obj_arg && TREE_CODE (obj_arg) == INDIRECT_REF) + { + tree addr = TREE_OPERAND (obj_arg, 0); + STRIP_NOPS (addr); + if (TREE_CODE (addr) == ADDR_EXPR) + { + tree typeo = TREE_TYPE (obj_arg); + tree typei = TREE_TYPE (TREE_OPERAND (addr, 0)); + if (same_type_ignoring_top_level_qualifiers_p (typeo, typei)) + obj_arg = TREE_OPERAND (addr, 0); + } + } + call = cxx_constant_value (call, obj_arg); + } + } return call; } diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c index 4baaac06252..84ed7accd7d 100644 --- a/gcc/cp/constexpr.c +++ b/gcc/cp/constexpr.c @@ -5739,6 +5739,16 @@ find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/) return NULL_TREE; } +/* Find immediate function decls in *TP if any. */ + +static tree +find_immediate_fndecl (tree *tp, int */*walk_subtrees*/, void */*data*/) +{ + if (TREE_CODE (*tp) == FUNCTION_DECL && DECL_IMMEDIATE_FUNCTION_P (*tp)) + return *tp; + return NULL_TREE; +} + /* ALLOW_NON_CONSTANT is false if T is required to be a constant expression. STRICT has the same sense as for constant_value_1: true if we only allow conforming C++ constant expressions, or false if we want a constant value @@ -5767,13 +5777,38 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, tree type = initialized_type (t); tree r = t; + bool is_consteval = false; if (VOID_TYPE_P (type)) { if (constexpr_dtor) /* Used for destructors of array elements. */ type = TREE_TYPE (object); else - return t; + { + if (cxx_dialect < cxx2a) + return t; + if (TREE_CODE (t) != CALL_EXPR && TREE_CODE (t) != AGGR_INIT_EXPR) + return t; + /* Calls to immediate functions returning void need to be + evaluated. */ + tree fndecl = cp_get_callee_fndecl_nofold (t); + if (fndecl == NULL_TREE || !DECL_IMMEDIATE_FUNCTION_P (fndecl)) + return t; + else + is_consteval = true; + } + } + else if (cxx_dialect >= cxx2a + && (TREE_CODE (t) == CALL_EXPR + || TREE_CODE (t) == AGGR_INIT_EXPR + || TREE_CODE (t) == TARGET_EXPR)) + { + tree x = t; + if (TREE_CODE (x) == TARGET_EXPR) + x = TARGET_EXPR_INITIAL (x); + tree fndecl = cp_get_callee_fndecl_nofold (x); + if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl)) + is_consteval = true; } if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)) { @@ -5874,6 +5909,25 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, } } + /* Check that immediate invocation does not return an expression referencing + any immediate function decls. They need to be allowed while parsing + immediate functions, but can't leak outside of them. */ + if (is_consteval + && t != r + && (current_function_decl == NULL_TREE + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) + if (tree immediate_fndecl + = cp_walk_tree_without_duplicates (&r, find_immediate_fndecl, + NULL)) + { + if (!allow_non_constant && !non_constant_p) + error_at (cp_expr_loc_or_input_loc (t), + "immediate evaluation returns address of immediate " + "function %qD", immediate_fndecl); + r = t; + non_constant_p = true; + } + /* Technically we should check this for all subexpressions, but that runs into problems with our internal representation of pointer subtraction and the 5.19 rules are still in flux. */ @@ -6114,7 +6168,8 @@ clear_cv_and_fold_caches (bool sat /*= true*/) static tree fold_non_dependent_expr_template (tree t, tsubst_flags_t complain, - bool manifestly_const_eval) + bool manifestly_const_eval, + tree object) { gcc_assert (processing_template_decl); @@ -6135,7 +6190,7 @@ fold_non_dependent_expr_template (tree t, tsubst_flags_t complain, tree r = cxx_eval_outermost_constant_expr (t, true, true, manifestly_const_eval, - false, NULL_TREE); + false, object); /* cp_tree_equal looks through NOPs, so allow them. */ gcc_checking_assert (r == t || CONVERT_EXPR_P (t) @@ -6171,16 +6226,17 @@ fold_non_dependent_expr_template (tree t, tsubst_flags_t complain, tree fold_non_dependent_expr (tree t, tsubst_flags_t complain /* = tf_warning_or_error */, - bool manifestly_const_eval /* = false */) + bool manifestly_const_eval /* = false */, + tree object /* = NULL_TREE */) { if (t == NULL_TREE) return NULL_TREE; if (processing_template_decl) return fold_non_dependent_expr_template (t, complain, - manifestly_const_eval); + manifestly_const_eval, object); - return maybe_constant_value (t, NULL_TREE, manifestly_const_eval); + return maybe_constant_value (t, object, manifestly_const_eval); } @@ -6197,7 +6253,7 @@ fold_non_dependent_init (tree t, if (processing_template_decl) { t = fold_non_dependent_expr_template (t, complain, - manifestly_const_eval); + manifestly_const_eval, NULL_TREE); /* maybe_constant_init does this stripping, so do it here too. */ if (TREE_CODE (t) == TARGET_EXPR) { diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 6c9731f24db..eb856394377 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -2697,7 +2697,8 @@ struct GTY(()) lang_decl_fn { unsigned hidden_friend_p : 1; unsigned omp_declare_reduction_p : 1; unsigned has_dependent_explicit_spec_p : 1; - unsigned spare : 12; + unsigned immediate_fn_p : 1; + unsigned spare : 11; /* 32-bits padding on 64-bit host. */ @@ -3211,6 +3212,15 @@ struct GTY(()) lang_decl { #define DECL_DECLARED_CONSTEXPR_P(DECL) \ DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL))) +/* True if FNDECL is an immediate function. */ +#define DECL_IMMEDIATE_FUNCTION_P(NODE) \ + (DECL_LANG_SPECIFIC (FUNCTION_DECL_CHECK (STRIP_TEMPLATE (NODE))) \ + ? LANG_DECL_FN_CHECK (NODE)->immediate_fn_p \ + : false) +#define SET_DECL_IMMEDIATE_FUNCTION_P(NODE) \ + (retrofit_lang_decl (FUNCTION_DECL_CHECK (NODE)), \ + LANG_DECL_FN_CHECK (NODE)->immediate_fn_p = true) + // True if NODE was declared as 'concept'. The flag implies that the // declaration is constexpr, that the declaration cannot be specialized or // refined, and that the result type must be convertible to bool. @@ -5879,6 +5889,7 @@ enum cp_decl_spec { ds_constexpr, ds_complex, ds_constinit, + ds_consteval, ds_thread, ds_type_spec, ds_redefined_builtin_type_spec, @@ -7824,7 +7835,7 @@ extern tree maybe_constant_value (tree, tree = NULL_TREE, bool = false); extern tree maybe_constant_init (tree, tree = NULL_TREE, bool = false); extern tree fold_non_dependent_expr (tree, tsubst_flags_t = tf_warning_or_error, - bool = false); + bool = false, tree = NULL_TREE); extern tree fold_non_dependent_init (tree, tsubst_flags_t = tf_warning_or_error, bool = false); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 72acc8f0224..8f22f230954 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -1225,7 +1225,13 @@ validate_constexpr_redeclaration (tree old_decl, tree new_decl) return true; if (DECL_DECLARED_CONSTEXPR_P (old_decl) == DECL_DECLARED_CONSTEXPR_P (new_decl)) - return true; + { + if (TREE_CODE (old_decl) != FUNCTION_DECL) + return true; + if (DECL_IMMEDIATE_FUNCTION_P (old_decl) + == DECL_IMMEDIATE_FUNCTION_P (new_decl)) + return true; + } if (TREE_CODE (old_decl) == FUNCTION_DECL) { if (fndecl_built_in_p (old_decl)) @@ -1233,6 +1239,8 @@ validate_constexpr_redeclaration (tree old_decl, tree new_decl) /* Hide a built-in declaration. */ DECL_DECLARED_CONSTEXPR_P (old_decl) = DECL_DECLARED_CONSTEXPR_P (new_decl); + if (DECL_IMMEDIATE_FUNCTION_P (new_decl)) + SET_DECL_IMMEDIATE_FUNCTION_P (old_decl); return true; } /* 7.1.5 [dcl.constexpr] @@ -1242,9 +1250,14 @@ validate_constexpr_redeclaration (tree old_decl, tree new_decl) && DECL_TEMPLATE_SPECIALIZATION (new_decl)) return true; + const char *kind = "constexpr"; + if (DECL_IMMEDIATE_FUNCTION_P (old_decl) + || DECL_IMMEDIATE_FUNCTION_P (new_decl)) + kind = "consteval"; error_at (DECL_SOURCE_LOCATION (new_decl), - "redeclaration %qD differs in % " - "from previous declaration", new_decl); + "redeclaration %qD differs in %qs " + "from previous declaration", new_decl, + kind); inform (DECL_SOURCE_LOCATION (old_decl), "previous declaration %qD", old_decl); return false; @@ -5024,12 +5037,15 @@ check_tag_decl (cp_decl_specifier_seq *declspecs, else if (saw_typedef) warning_at (declspecs->locations[ds_typedef], 0, "% was ignored in this declaration"); - else if (decl_spec_seq_has_spec_p (declspecs, ds_constexpr)) + else if (decl_spec_seq_has_spec_p (declspecs, ds_constexpr)) error_at (declspecs->locations[ds_constexpr], - "% cannot be used for type declarations"); - else if (decl_spec_seq_has_spec_p (declspecs, ds_constinit)) + "%qs cannot be used for type declarations", "constexpr"); + else if (decl_spec_seq_has_spec_p (declspecs, ds_constinit)) error_at (declspecs->locations[ds_constinit], - "% cannot be used for type declarations"); + "%qs cannot be used for type declarations", "constinit"); + else if (decl_spec_seq_has_spec_p (declspecs, ds_consteval)) + error_at (declspecs->locations[ds_consteval], + "%qs cannot be used for type declarations", "consteval"); } if (declspecs->attributes && warn_attributes && declared_type) @@ -5387,11 +5403,14 @@ start_decl (const cp_declarator *declarator, bool ok = false; if (CP_DECL_THREAD_LOCAL_P (decl)) error_at (DECL_SOURCE_LOCATION (decl), - "%qD declared % in % function", - decl); + "%qD declared % in %qs function", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); else if (TREE_STATIC (decl)) error_at (DECL_SOURCE_LOCATION (decl), - "%qD declared % in % function", decl); + "%qD declared % in %qs function", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); else ok = true; if (!ok) @@ -9342,6 +9361,15 @@ grokfndecl (tree ctype, } } + /* FIXME: For now. */ + if (virtualp && (inlinep & 8) != 0) + { + sorry_at (DECL_SOURCE_LOCATION (decl), + "% % method %qD not supported yet", + decl); + inlinep &= ~8; + } + /* If this decl has namespace scope, set that up. */ if (in_namespace) set_decl_namespace (decl, in_namespace, friendp); @@ -9389,7 +9417,10 @@ grokfndecl (tree ctype, "cannot declare %<::main%> to be inline"); if (inlinep & 2) error_at (declspecs->locations[ds_constexpr], - "cannot declare %<::main%> to be %"); + "cannot declare %<::main%> to be %qs", "constexpr"); + if (inlinep & 8) + error_at (declspecs->locations[ds_consteval], + "cannot declare %<::main%> to be %qs", "consteval"); if (!publicp) error_at (location, "cannot declare %<::main%> to be static"); inlinep = 0; @@ -9428,6 +9459,11 @@ grokfndecl (tree ctype, } if (inlinep & 2) DECL_DECLARED_CONSTEXPR_P (decl) = true; + else if (inlinep & 8) + { + DECL_DECLARED_CONSTEXPR_P (decl) = true; + SET_DECL_IMMEDIATE_FUNCTION_P (decl); + } // If the concept declaration specifier was found, check // that the declaration satisfies the necessary requirements. @@ -10786,6 +10822,7 @@ grokdeclarator (const cp_declarator *declarator, bool typedef_p = decl_spec_seq_has_spec_p (declspecs, ds_typedef); bool constexpr_p = decl_spec_seq_has_spec_p (declspecs, ds_constexpr); bool constinit_p = decl_spec_seq_has_spec_p (declspecs, ds_constinit); + bool consteval_p = decl_spec_seq_has_spec_p (declspecs, ds_consteval); bool late_return_type_p = false; bool array_parameter_p = false; tree reqs = NULL_TREE; @@ -11058,17 +11095,31 @@ grokdeclarator (const cp_declarator *declarator, if (name == NULL) name = decl_context == PARM ? "parameter" : "type name"; + if (consteval_p && constexpr_p) + { + error_at (declspecs->locations[ds_consteval], + "both %qs and %qs specified", "constexpr", "consteval"); + return error_mark_node; + } + if (concept_p && typedef_p) { error_at (declspecs->locations[ds_concept], - "% cannot appear in a typedef declaration"); + "%qs cannot appear in a typedef declaration", "concept"); return error_mark_node; } if (constexpr_p && typedef_p) { error_at (declspecs->locations[ds_constexpr], - "% cannot appear in a typedef declaration"); + "%qs cannot appear in a typedef declaration", "constexpr"); + return error_mark_node; + } + + if (consteval_p && typedef_p) + { + error_at (declspecs->locations[ds_consteval], + "%qs cannot appear in a typedef declaration", "consteval"); return error_mark_node; } @@ -11474,21 +11525,31 @@ grokdeclarator (const cp_declarator *declarator, /* Function parameters cannot be concept. */ if (concept_p) - error_at (declspecs->locations[ds_concept], - "a parameter cannot be declared %"); + { + error_at (declspecs->locations[ds_concept], + "a parameter cannot be declared %qs", "concept"); + concept_p = 0; + constexpr_p = 0; + } /* Function parameters cannot be constexpr. If we saw one, moan and pretend it wasn't there. */ else if (constexpr_p) { error_at (declspecs->locations[ds_constexpr], - "a parameter cannot be declared %"); + "a parameter cannot be declared %qs", "constexpr"); constexpr_p = 0; } - else if (constinit_p) + if (constinit_p) { error_at (declspecs->locations[ds_constinit], - "a parameter cannot be declared %"); - constexpr_p = 0; + "a parameter cannot be declared %qs", "constinit"); + constinit_p = 0; + } + if (consteval_p) + { + error_at (declspecs->locations[ds_consteval], + "a parameter cannot be declared %qs", "consteval"); + consteval_p = 0; } } @@ -11511,9 +11572,12 @@ grokdeclarator (const cp_declarator *declarator, if (typedef_p) error_at (declspecs->locations[ds_typedef], "structured binding declaration cannot be %qs", "typedef"); - if (constexpr_p) + if (constexpr_p && !concept_p) error_at (declspecs->locations[ds_constexpr], "structured " "binding declaration cannot be %qs", "constexpr"); + if (consteval_p) + error_at (declspecs->locations[ds_consteval], "structured " + "binding declaration cannot be %qs", "consteval"); if (thread_p && cxx_dialect < cxx2a) pedwarn (declspecs->locations[ds_thread], 0, "structured binding declaration can be %qs only in " @@ -11573,6 +11637,7 @@ grokdeclarator (const cp_declarator *declarator, inlinep = 0; typedef_p = 0; constexpr_p = 0; + consteval_p = 0; concept_p = 0; if (storage_class != sc_static) { @@ -12967,7 +13032,7 @@ grokdeclarator (const cp_declarator *declarator, if (concept_p) { error_at (declspecs->locations[ds_concept], - "a destructor cannot be %"); + "a destructor cannot be %qs", "concept"); return error_mark_node; } if (constexpr_p && cxx_dialect < cxx2a) @@ -12977,6 +13042,12 @@ grokdeclarator (const cp_declarator *declarator, " with %<-std=c++2a%> or %<-std=gnu++2a%>"); return error_mark_node; } + if (consteval_p) + { + error_at (declspecs->locations[ds_consteval], + "a destructor cannot be %qs", "consteval"); + return error_mark_node; + } } else if (sfk == sfk_constructor && friendp && !ctype) { @@ -12998,6 +13069,14 @@ grokdeclarator (const cp_declarator *declarator, "a concept cannot be a member function"); concept_p = false; } + else if (consteval_p + && identifier_p (unqualified_id) + && IDENTIFIER_NEWDEL_OP_P (unqualified_id)) + { + error_at (declspecs->locations[ds_consteval], + "%qD cannot be %qs", unqualified_id, "consteval"); + consteval_p = false; + } if (TREE_CODE (unqualified_id) == TEMPLATE_ID_EXPR) { @@ -13028,7 +13107,8 @@ grokdeclarator (const cp_declarator *declarator, reqs, virtualp, flags, memfn_quals, rqual, raises, friendp ? -1 : 0, friendp, publicp, - inlinep | (2 * constexpr_p) | (4 * concept_p), + inlinep | (2 * constexpr_p) | (4 * concept_p) + | (8 * consteval_p), initialized == SD_DELETED, sfk, funcdef_flag, late_return_type_p, template_count, in_namespace, @@ -13130,8 +13210,8 @@ grokdeclarator (const cp_declarator *declarator, set_linkage_for_static_data_member (decl); if (concept_p) error_at (declspecs->locations[ds_concept], - "static data member %qE declared %", - unqualified_id); + "static data member %qE declared %qs", + unqualified_id, "concept"); else if (constexpr_p && !initialized) { error_at (DECL_SOURCE_LOCATION (decl), @@ -13139,6 +13219,10 @@ grokdeclarator (const cp_declarator *declarator, "have an initializer", decl); constexpr_p = false; } + if (consteval_p) + error_at (declspecs->locations[ds_consteval], + "static data member %qE declared %qs", + unqualified_id, "consteval"); if (inlinep) mark_inline_variable (decl, declspecs->locations[ds_inline]); @@ -13163,23 +13247,34 @@ grokdeclarator (const cp_declarator *declarator, else { if (concept_p) - error_at (declspecs->locations[ds_concept], - "non-static data member %qE declared %", - unqualified_id); - else if (constexpr_p) + { + error_at (declspecs->locations[ds_concept], + "non-static data member %qE declared %qs", + unqualified_id, "concept"); + concept_p = false; + constexpr_p = false; + } + else if (constexpr_p) { error_at (declspecs->locations[ds_constexpr], - "non-static data member %qE declared " - "%", unqualified_id); + "non-static data member %qE declared %qs", + unqualified_id, "constexpr"); constexpr_p = false; } - else if (constinit_p) + if (constinit_p) { error_at (declspecs->locations[ds_constinit], - "non-static data member %qE declared " - "%", unqualified_id); + "non-static data member %qE declared %qs", + unqualified_id, "constinit"); constinit_p = false; } + if (consteval_p) + { + error_at (declspecs->locations[ds_consteval], + "non-static data member %qE declared %qs", + unqualified_id, "consteval"); + consteval_p = false; + } decl = build_decl (id_loc, FIELD_DECL, unqualified_id, type); DECL_NONADDRESSABLE_P (decl) = bitfield; if (bitfield && !unqualified_id) @@ -13285,6 +13380,14 @@ grokdeclarator (const cp_declarator *declarator, sfk = sfk_none; } } + if (consteval_p + && identifier_p (unqualified_id) + && IDENTIFIER_NEWDEL_OP_P (unqualified_id)) + { + error_at (declspecs->locations[ds_consteval], + "%qD cannot be %qs", unqualified_id, "consteval"); + consteval_p = false; + } /* Record whether the function is public. */ publicp = (ctype != NULL_TREE @@ -13295,7 +13398,8 @@ grokdeclarator (const cp_declarator *declarator, reqs, virtualp, flags, memfn_quals, rqual, raises, 1, friendp, publicp, - inlinep | (2 * constexpr_p) | (4 * concept_p), + inlinep | (2 * constexpr_p) | (4 * concept_p) + | (8 * consteval_p), initialized == SD_DELETED, sfk, funcdef_flag, @@ -13388,6 +13492,12 @@ grokdeclarator (const cp_declarator *declarator, "is not a definition", decl); constexpr_p = false; } + if (consteval_p) + { + error_at (DECL_SOURCE_LOCATION (decl), + "a variable cannot be declared %"); + consteval_p = false; + } if (inlinep) mark_inline_variable (decl, declspecs->locations[ds_inline]); @@ -16696,7 +16806,7 @@ finish_function (bool inline_p) invoke_plugin_callbacks (PLUGIN_PRE_GENERICIZE, fndecl); /* Perform delayed folding before NRV transformation. */ - if (!processing_template_decl) + if (!processing_template_decl && !DECL_IMMEDIATE_FUNCTION_P (fndecl)) cp_fold_function (fndecl); /* Set up the named return value optimization, if we can. Candidate @@ -16813,7 +16923,7 @@ finish_function (bool inline_p) do_warn_unused_parameter (fndecl); /* Genericize before inlining. */ - if (!processing_template_decl) + if (!processing_template_decl && !DECL_IMMEDIATE_FUNCTION_P (fndecl)) cp_genericize (fndecl); /* We're leaving the context of this function, so zap cfun. It's still in diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 83b8b12e4bd..d104a4d574c 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -1652,7 +1652,9 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags) { if (DECL_DECLARED_CONCEPT_P (t)) pp_cxx_ws_string (pp, "concept"); - else + else if (DECL_IMMEDIATE_FUNCTION_P (t)) + pp_cxx_ws_string (pp, "consteval"); + else pp_cxx_ws_string (pp, "constexpr"); } } diff --git a/gcc/cp/init.c b/gcc/cp/init.c index f86cf551039..eda827250da 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -348,14 +348,12 @@ build_value_init (tree type, tsubst_flags_t complain) gcc_assert (!processing_template_decl || (SCALAR_TYPE_P (type) || TREE_CODE (type) == ARRAY_TYPE)); - if (CLASS_TYPE_P (type) - && type_build_ctor_call (type)) + if (CLASS_TYPE_P (type) && type_build_ctor_call (type)) { - tree ctor = - build_special_member_call (NULL_TREE, complete_ctor_identifier, - NULL, type, LOOKUP_NORMAL, - complain); - if (ctor == error_mark_node) + tree ctor + = build_special_member_call (NULL_TREE, complete_ctor_identifier, + NULL, type, LOOKUP_NORMAL, complain); + if (ctor == error_mark_node || TREE_CONSTANT (ctor)) return ctor; tree fn = NULL_TREE; if (TREE_CODE (ctor) == CALL_EXPR) diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c index d621beca2eb..c582aa5b105 100644 --- a/gcc/cp/lambda.c +++ b/gcc/cp/lambda.c @@ -1194,6 +1194,9 @@ maybe_add_lambda_conv_op (tree type) DECL_ARTIFICIAL (fn) = 1; DECL_NOT_REALLY_EXTERN (fn) = 1; DECL_DECLARED_INLINE_P (fn) = 1; + DECL_DECLARED_CONSTEXPR_P (fn) = DECL_DECLARED_CONSTEXPR_P (callop); + if (DECL_IMMEDIATE_FUNCTION_P (callop)) + SET_DECL_IMMEDIATE_FUNCTION_P (fn); DECL_ARGUMENTS (fn) = build_this_parm (fn, fntype, TYPE_QUAL_CONST); if (nested_def) @@ -1226,6 +1229,9 @@ maybe_add_lambda_conv_op (tree type) DECL_NOT_REALLY_EXTERN (fn) = 1; DECL_DECLARED_INLINE_P (fn) = 1; DECL_STATIC_FUNCTION_P (fn) = 1; + DECL_DECLARED_CONSTEXPR_P (fn) = DECL_DECLARED_CONSTEXPR_P (callop); + if (DECL_IMMEDIATE_FUNCTION_P (callop)) + SET_DECL_IMMEDIATE_FUNCTION_P (fn); DECL_ARGUMENTS (fn) = fn_args; for (tree arg = fn_args; arg; arg = DECL_CHAIN (arg)) { diff --git a/gcc/cp/method.c b/gcc/cp/method.c index b613e5df871..09e9c73cda5 100644 --- a/gcc/cp/method.c +++ b/gcc/cp/method.c @@ -2228,8 +2228,9 @@ defaulted_late_check (tree fn) if (!CLASSTYPE_TEMPLATE_INSTANTIATION (ctx)) { error ("explicitly defaulted function %q+D cannot be declared " - "%qs because the implicit declaration is not %qs:", - fn, "constexpr", "constexpr"); + "%qs because the implicit declaration is not %qs:", fn, + DECL_IMMEDIATE_FUNCTION_P (fn) ? "consteval" : "constexpr", + "constexpr"); explain_implicit_non_constexpr (fn); } DECL_DECLARED_CONSTEXPR_P (fn) = false; diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index b44687e96bb..0b7bf83c4f4 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -233,7 +233,10 @@ struct GTY(()) cp_binding_level { 'this_entity'. */ unsigned defining_class_p : 1; - /* 23 bits left to fill a 32-bit word. */ + /* true for SK_FUNCTION_PARMS of immediate functions. */ + unsigned immediate_fn_ctx_p : 1; + + /* 22 bits left to fill a 32-bit word. */ }; /* The binding level currently in effect. */ diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index f1664e66087..516c14b5610 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -998,11 +998,13 @@ cp_keyword_starts_decl_specifier_p (enum rid keyword) /* GNU extensions. */ case RID_ATTRIBUTE: case RID_TYPEOF: - /* C++0x extensions. */ + /* C++11 extensions. */ case RID_DECLTYPE: case RID_UNDERLYING_TYPE: case RID_CONSTEXPR: + /* C++20 extensions. */ case RID_CONSTINIT: + case RID_CONSTEVAL: return true; default: @@ -1807,12 +1809,15 @@ enum /* When parsing a decl-specifier-seq, only allow type-specifier or constexpr. */ CP_PARSER_FLAGS_ONLY_TYPE_OR_CONSTEXPR = 0x8, - /* When parsing a decl-specifier-seq, only allow mutable or constexpr. */ + /* When parsing a decl-specifier-seq, only allow mutable, constexpr or + for C++2A consteval. */ CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR = 0x10, /* When parsing a decl-specifier-seq, allow missing typename. */ CP_PARSER_FLAGS_TYPENAME_OPTIONAL = 0x20, /* When parsing of the noexcept-specifier should be delayed. */ - CP_PARSER_FLAGS_DELAY_NOEXCEPT = 0x40 + CP_PARSER_FLAGS_DELAY_NOEXCEPT = 0x40, + /* When parsing a consteval declarator. */ + CP_PARSER_FLAGS_CONSTEVAL = 0x80 }; /* This type is used for parameters and variables which hold @@ -2671,6 +2676,7 @@ static bool cp_parser_init_statement_p (cp_parser *); static bool cp_parser_skip_to_closing_square_bracket (cp_parser *); +static size_t cp_parser_skip_balanced_tokens (cp_parser *, size_t); // -------------------------------------------------------------------------- // // Unevaluated Operand Guard @@ -10903,11 +10909,31 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) opening parenthesis if present. */ if (cp_lexer_next_token_is (parser->lexer, CPP_OPEN_PAREN)) { + bool is_consteval = false; + /* For C++20, before parsing the parameter list check if there is + a consteval specifier in the corresponding decl-specifier-seq. */ + if (cxx_dialect >= cxx2a) + { + for (size_t n = cp_parser_skip_balanced_tokens (parser, 1); + cp_lexer_nth_token_is (parser->lexer, n, CPP_KEYWORD); n++) + { + if (cp_lexer_peek_nth_token (parser->lexer, n)->keyword + == RID_CONSTEVAL) + { + is_consteval = true; + break; + } + } + } + matching_parens parens; parens.consume_open (parser); begin_scope (sk_function_parms, /*entity=*/NULL_TREE); + if (is_consteval) + current_binding_level->immediate_fn_ctx_p = true; + /* Parse parameters. */ param_list = cp_parser_parameter_declaration_clause @@ -10992,6 +11018,9 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr) "lambda only available with %<-std=c++17%> or " "%<-std=gnu++17%>"); } + if (lambda_specs.locations[ds_consteval]) + return_type_specs.locations[ds_consteval] + = lambda_specs.locations[ds_consteval]; p = obstack_alloc (&declarator_obstack, 0); @@ -14052,6 +14081,11 @@ cp_parser_decl_specifier_seq (cp_parser* parser, cp_lexer_consume_token (parser->lexer); break; + case RID_CONSTEVAL: + ds = ds_consteval; + cp_lexer_consume_token (parser->lexer); + break; + case RID_CONCEPT: ds = ds_concept; cp_lexer_consume_token (parser->lexer); @@ -14169,7 +14203,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser, if (found_decl_spec && (flags & CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR) && token->keyword != RID_MUTABLE - && token->keyword != RID_CONSTEXPR) + && token->keyword != RID_CONSTEXPR + && token->keyword != RID_CONSTEVAL) error_at (token->location, "%qD invalid in lambda", ridpointers[token->keyword]); @@ -17310,6 +17345,10 @@ cp_parser_explicit_instantiation (cp_parser* parser) permerror (decl_specifiers.locations[ds_constexpr], "explicit instantiation shall not use" " % specifier"); + if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_consteval)) + permerror (decl_specifiers.locations[ds_consteval], + "explicit instantiation shall not use" + " % specifier"); decl = grokdeclarator (declarator, &decl_specifiers, NORMAL, 0, &decl_specifiers.attributes); @@ -20295,6 +20334,9 @@ cp_parser_init_declarator (cp_parser* parser, bool saved_default_arg_ok_p = parser->default_arg_ok_p; location_t tmp_init_loc = UNKNOWN_LOCATION; + if (decl_spec_seq_has_spec_p (decl_specifiers, ds_consteval)) + flags |= CP_PARSER_FLAGS_CONSTEVAL; + /* Gather the attributes that were provided with the decl-specifiers. */ prefix_attributes = decl_specifiers->attributes; @@ -20939,6 +20981,10 @@ cp_parser_direct_declarator (cp_parser* parser, begin_scope (sk_function_parms, NULL_TREE); + /* Signal we are in the immediate function context. */ + if (flags & CP_PARSER_FLAGS_CONSTEVAL) + current_binding_level->immediate_fn_ctx_p = true; + /* Parse the parameter-declaration-clause. */ params = cp_parser_parameter_declaration_clause (parser, flags); @@ -29960,9 +30006,10 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs, "friend", "typedef", "using", - "constexpr", + "constexpr", "__complex", - "constinit" + "constinit", + "consteval" }; gcc_rich_location richloc (location); richloc.add_fixit_remove (); diff --git a/gcc/cp/semantics.c b/gcc/cp/semantics.c index 59def3170ab..8293c0721bd 100644 --- a/gcc/cp/semantics.c +++ b/gcc/cp/semantics.c @@ -4428,7 +4428,7 @@ expand_or_defer_fn_1 (tree fn) if (DECL_INTERFACE_KNOWN (fn)) /* We've already made a decision as to how this function will be handled. */; - else if (!at_eof) + else if (!at_eof || DECL_IMMEDIATE_FUNCTION_P (fn)) tentative_decl_linkage (fn); else import_export_decl (fn); @@ -4439,6 +4439,7 @@ expand_or_defer_fn_1 (tree fn) be emitted; there may be callers in other DLLs. */ if (DECL_DECLARED_INLINE_P (fn) && !DECL_REALLY_EXTERN (fn) + && !DECL_IMMEDIATE_FUNCTION_P (fn) && (flag_keep_inline_functions || (flag_keep_inline_dllexport && lookup_attribute ("dllexport", DECL_ATTRIBUTES (fn))))) diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c index 03c39b3e3b9..15529c66b1e 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6176,6 +6176,16 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) if (processing_template_decl || TREE_CODE (arg) != COMPONENT_REF) { tree stripped_arg = tree_strip_any_location_wrapper (arg); + if (TREE_CODE (stripped_arg) == FUNCTION_DECL + && DECL_IMMEDIATE_FUNCTION_P (stripped_arg) + && (current_function_decl == NULL_TREE + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) + { + if (complain & tf_error) + error ("taking address of an immediate function %qD", + stripped_arg); + return error_mark_node; + } if (TREE_CODE (stripped_arg) == FUNCTION_DECL && !mark_used (stripped_arg, complain) && !(complain & tf_error)) return error_mark_node; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index c5293582e9a..5473b252e73 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,22 @@ 2019-11-02 Jakub Jelinek + PR c++/88335 - Implement P1073R3: Immediate functions + * g++.dg/cpp2a/consteval1.C: New test. + * g++.dg/cpp2a/consteval2.C: New test. + * g++.dg/cpp2a/consteval3.C: New test. + * g++.dg/cpp2a/consteval4.C: New test. + * g++.dg/cpp2a/consteval5.C: New test. + * g++.dg/cpp2a/consteval6.C: New test. + * g++.dg/cpp2a/consteval7.C: New test. + * g++.dg/cpp2a/consteval8.C: New test. + * g++.dg/cpp2a/consteval9.C: New test. + * g++.dg/cpp2a/consteval10.C: New test. + * g++.dg/cpp2a/consteval11.C: New test. + * g++.dg/cpp2a/consteval12.C: New test. + * g++.dg/cpp2a/consteval13.C: New test. + * g++.dg/cpp2a/consteval14.C: New test. + * g++.dg/ext/consteval1.C: New test. + PR c++/91369 - Implement P0784R7: constexpr new * g++.dg/cpp2a/constexpr-new6.C: New test. * g++.dg/cpp2a/constexpr-new7.C: New test. diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval1.C b/gcc/testsuite/g++.dg/cpp2a/consteval1.C new file mode 100644 index 00000000000..fa00b07e272 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval1.C @@ -0,0 +1,37 @@ +// { dg-do run } +// { dg-options "-std=c++2a" } + +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + return __builtin_is_constant_evaluated (); + } +} + +extern "C" void abort (); +constexpr int f0 (int n) { return n; } +consteval int f1 (int n) { return f0 (n) * n; } +consteval int f2 (int n) { return f1 (n); } +consteval bool f3 () { return std::is_constant_evaluated (); } +struct S { constexpr S (int x) : s (x) {} consteval int m1 (int n) const; int s; }; +consteval int +S::m1 (int n) const +{ + n += s; + return n; +} + +constexpr int a = 2; +int b = f1 (a); +int c = f2 (f1 (a)); +bool d = f3 (); +constexpr S e = 41; +int f = e.m1 (1); + +int +main () +{ + if (b != 4 || c != 16 || !d || f != 42) + abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval10.C b/gcc/testsuite/g++.dg/cpp2a/consteval10.C new file mode 100644 index 00000000000..6785f60df58 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval10.C @@ -0,0 +1,3 @@ +// { dg-do compile } + +consteval int bar (void) { return 0; } // { dg-error "'consteval' does not name a type" "" { target c++17_down } } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval11.C b/gcc/testsuite/g++.dg/cpp2a/consteval11.C new file mode 100644 index 00000000000..2f68ec0f892 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval11.C @@ -0,0 +1,140 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +constexpr int a = bar (1); +constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } +constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } +const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } +const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } +int f = bar (1); +int g = bar (6); // { dg-message "in 'constexpr' expansion of" } +int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } + +void +foo () +{ + constexpr int a = bar (1); + constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; // { dg-message "in 'constexpr' expansion of" } + const int d = bar (4); // { dg-message "in 'constexpr' expansion of" } + const int e = 0 ? bar (5) : 1; // { dg-message "in 'constexpr' expansion of" } + int f = bar (1); + int g = bar (6); // { dg-message "in 'constexpr' expansion of" } + int h = 0 ? bar (7) : 1; // { dg-message "in 'constexpr' expansion of" } + h += 0 ? bar (8) : 1; // { dg-message "in 'constexpr' expansion of" } + if (0) + bar (9); // { dg-message "in 'constexpr' expansion of" } + else + bar (10); // { dg-message "in 'constexpr' expansion of" } + if (1) + bar (11); // { dg-message "in 'constexpr' expansion of" } + else + bar (12); // { dg-message "in 'constexpr' expansion of" } + if constexpr (0) + bar (13); // { dg-message "in 'constexpr' expansion of" } + else + bar (14); // { dg-message "in 'constexpr' expansion of" } + if constexpr (1) + bar (15); // { dg-message "in 'constexpr' expansion of" } + else + bar (16); // { dg-message "in 'constexpr' expansion of" } +} + +consteval int +baz () +{ + constexpr int a = bar (1); + constexpr int b = bar (2); // { dg-message "in 'constexpr' expansion of" } + constexpr int c = 0 ? bar (3) : 1; + const int d = bar (4); + const int e = 0 ? bar (5) : 1; + int f = bar (1); + int g = bar (6); + int h = 0 ? bar (7) : 1; + h += 0 ? bar (8) : 1; + if (0) + bar (9); + else + bar (10); + if (1) + bar (11); + else + bar (12); + if constexpr (0) + bar (13); + else + bar (14); + if constexpr (1) + bar (15); + else + bar (16); + return 0; +} + +template +void +qux () +{ + if (0) + bar (2); // { dg-message "in 'constexpr' expansion of" } + else + bar (3); // { dg-message "in 'constexpr' expansion of" } + if (1) + bar (4); // { dg-message "in 'constexpr' expansion of" } + else + bar (5); // { dg-message "in 'constexpr' expansion of" } + if constexpr (0) + bar (6); // { dg-message "in 'constexpr' expansion of" } + else + bar (7); // { dg-message "in 'constexpr' expansion of" } + if constexpr (1) + bar (8); // { dg-message "in 'constexpr' expansion of" } + else + bar (9); // { dg-message "in 'constexpr' expansion of" } + if (0) + bar ((T) 2); + else + bar ((T) 3); + if (1) + bar ((T) 4); + else + bar ((T) 5); + if constexpr (0) + bar ((T) 6); + else + bar ((T) 7); + if constexpr (1) + bar ((T) 8); + else + bar ((T) 9); +} + +template +void +quux () +{ + if (0) + bar ((T) 2); // { dg-message "in 'constexpr' expansion of" } + else + bar ((T) 3); // { dg-message "in 'constexpr' expansion of" } + if (1) + bar ((T) 4); // { dg-message "in 'constexpr' expansion of" } + else + bar ((T) 5); // { dg-message "in 'constexpr' expansion of" } + if constexpr (0) + bar ((T) 6); + else + bar ((T) 7); // { dg-message "in 'constexpr' expansion of" } + if constexpr (1) + bar ((T) 8); // { dg-message "in 'constexpr' expansion of" } + else + bar ((T) 9); +} + +void +corge () +{ + quux (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval12.C b/gcc/testsuite/g++.dg/cpp2a/consteval12.C new file mode 100644 index 00000000000..0e97cf2136a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval12.C @@ -0,0 +1,15 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +consteval int bar () { return 42; } +consteval int baz () { return 1; } +typedef int (*fnptr) (); +consteval fnptr quux () { return bar; } + +void +foo () +{ + auto qux = [] (fnptr a = quux ()) consteval { return a (); }; + constexpr auto e = qux (); + static_assert (e == 42); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval13.C b/gcc/testsuite/g++.dg/cpp2a/consteval13.C new file mode 100644 index 00000000000..8db7e6611c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval13.C @@ -0,0 +1,17 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +consteval int bar () { return 42; } +consteval int baz () { return 1; } +typedef int (*fnptr) (); +consteval fnptr quux () { return bar; } + +void +foo () +{ + auto qux = [] (fnptr a = quux ()) consteval { return a (); }; + constexpr auto c = qux (baz); // { dg-error "taking address of an immediate function" } + constexpr auto d = qux (bar); // { dg-error "taking address of an immediate function" } + static_assert (c == 1); + static_assert (d == 42); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval14.C b/gcc/testsuite/g++.dg/cpp2a/consteval14.C new file mode 100644 index 00000000000..54ab927705c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval14.C @@ -0,0 +1,30 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct S { consteval S () : a (1), b (2) { a++; b++; } consteval S (int x) : a (x), b (x) { a++; b--; } int a, b; }; +S c; + +template +int +foo () +{ + S a; + a.b++; + c = a; + S b = 12; + c.a += b.a; + c.b += b.b; + S e[2]; + S f[2] = { 1, 2 }; + const S g; + c.a += g.a; + c.b += g.b; + const S h = 12; + c.a += h.a; + c.b += h.b; + const S i[2]; + const S j[2] = { 1, 2 }; + return S ().a + e[1].a + f[0].b + i[0].a + j[1].b; +} + +int x = foo <2> (); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval2.C b/gcc/testsuite/g++.dg/cpp2a/consteval2.C new file mode 100644 index 00000000000..8975002ad13 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval2.C @@ -0,0 +1,17 @@ +// { dg-do run } +// { dg-options "-std=c++2a" } + +extern "C" void abort (); +consteval int foo () { return 42; } +consteval auto bar () { return foo; } +consteval int baz (int (*fn) () = bar ()) { return fn (); } +constexpr int a = baz (); +static_assert (a == 42); +int b = baz (); + +int +main () +{ + if (b != 42) + abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval3.C b/gcc/testsuite/g++.dg/cpp2a/consteval3.C new file mode 100644 index 00000000000..4214092131f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval3.C @@ -0,0 +1,63 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct S { S () : a (0), b (1) {} int a, b; }; +int f1 (); // { dg-message "previous declaration 'int f1\\(\\)'" } +consteval int f1 (); // { dg-error "redeclaration 'consteval int f1\\(\\)' differs in 'consteval' from previous declaration" } +consteval int f2 (); // { dg-message "previous declaration 'consteval int f2\\(\\)'" } +int f2 (); // { dg-error "redeclaration 'int f2\\(\\)' differs in 'consteval' from previous declaration" } +constexpr int f3 (); // { dg-message "previous declaration 'constexpr int f3\\(\\)'" } +consteval int f3 (); // { dg-error "redeclaration 'consteval int f3\\(\\)' differs in 'consteval' from previous declaration" } +consteval int f4 (); // { dg-message "previous declaration 'consteval int f4\\(\\)'" } +constexpr int f4 (); // { dg-error "redeclaration 'constexpr int f4\\(\\)' differs in 'consteval' from previous declaration" } +typedef consteval int cint; // { dg-error "'consteval' cannot appear in a typedef declaration" } +consteval struct T { int i; }; // { dg-error "'consteval' cannot be used for type declarations" } +consteval int a = 5; // { dg-error "a variable cannot be declared 'consteval'" } +consteval auto [ b, c ] = S (); // { dg-error "structured binding declaration cannot be 'consteval'" } +int f5 (consteval int x) { return x; } // { dg-error "a parameter cannot be declared 'consteval'" } +consteval int f6 (int x) { return x; } +int d = 6; // { dg-message "'int d' is not const" } +int e = f6 (d); // { dg-error "the value of 'd' is not usable in a constant expression" } +constexpr int f7 (int x) { return f6 (x); } // { dg-error "'x' is not a constant expression" } +constexpr int f = f7 (5); // { dg-error "" } + // { dg-message "in 'constexpr' expansion of" "" { target *-*-* } .-1 } +using fnptr = int (int); +fnptr *g = f6; // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } +int f8 (fnptr *); +int h = f8 (f6); // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } +consteval constexpr int f9 () { return 0; } // { dg-error "both 'constexpr' and 'consteval' specified" } +constexpr consteval int f10 () { return 0; } // { dg-error "both 'constexpr' and 'consteval' specified" } +consteval consteval int f11 () { return 0; } // { dg-error "duplicate 'consteval'" } +struct U { consteval ~U () {} }; // { dg-error "a destructor cannot be 'consteval'" } +struct V { consteval int v = 5; }; // { dg-error "non-static data member 'v' declared 'consteval'" } +struct W { consteval static int w; }; // { dg-error "static data member 'w' declared 'consteval'" } +int i = sizeof (&f6); // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } +using j = decltype (&f6); // { dg-error "taking address of an immediate function 'consteval int f6\\(int\\)'" } +int k = sizeof (f6 (d)); // { dg-error "the value of 'd' is not usable in a constant expression" } +using l = decltype (f6 (d)); // { dg-error "the value of 'd' is not usable in a constant expression" } +bool m = noexcept (f6 (d)); // { dg-error "the value of 'd' is not usable in a constant expression" } +namespace std { +using size_t = decltype (sizeof (0)); +} +consteval void* operator new (std::size_t); // { dg-error "'operator new' cannot be 'consteval'" } +consteval void operator delete (void *, std::size_t) noexcept; // { dg-error "'operator delete' cannot be 'consteval'" } +consteval void operator delete[] (void *) noexcept; // { dg-error "'operator delete \\\[\\\]' cannot be 'consteval'" } +struct X { + static consteval void* operator new (std::size_t); // { dg-error "'operator new' cannot be 'consteval'" } + static consteval void operator delete (void *, std::size_t) noexcept; // { dg-error "'operator delete' cannot be 'consteval'" } + consteval static void operator delete[] (void *) noexcept; // { dg-error "'operator delete \\\[\\\]' cannot be 'consteval'" } +}; +consteval int main () { return 0; } // { dg-error "cannot declare '::main' to be 'consteval'" } +struct A { A (); int a; }; // { dg-message "defaulted constructor calls non-'constexpr' 'A::A\\(\\)'" } +struct B { constexpr B () : b (0) {} int b; }; +struct C { A a; consteval C () = default; }; // { dg-error "explicitly defaulted function 'consteval C::C\\(\\)' cannot be declared 'consteval' because the implicit declaration is not 'constexpr'" } +struct D { B b; consteval D () = default; }; +template consteval T f12 (T x) { return x; } +template consteval float f12 (float x); // { dg-error "explicit instantiation shall not use 'consteval' specifier" } +consteval int +f13 (int x) +{ + static int a = 5; // { dg-error "'a' declared 'static' in 'consteval' function" } + thread_local int b = 6; // { dg-error "'b' declared 'thread_local' in 'consteval' function" } + return x; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval4.C b/gcc/testsuite/g++.dg/cpp2a/consteval4.C new file mode 100644 index 00000000000..3e55c2b5c83 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval4.C @@ -0,0 +1,29 @@ +// { dg-do run } +// { dg-options "-std=c++2a" } + +extern "C" void abort (); +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + return __builtin_is_constant_evaluated (); + } +} + +int +main () +{ + constexpr int a = 5; + auto b = [] (int n) consteval { return n + a + std::is_constant_evaluated (); }; + int c = b (4); + if (c != 10) + abort (); + auto d = [] () consteval { return a + std::is_constant_evaluated (); }; + int e = d (); + if (e != 6) + abort (); + constexpr int f = d (); + if (f != 6) + abort (); + static_assert (d () == 6); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval5.C b/gcc/testsuite/g++.dg/cpp2a/consteval5.C new file mode 100644 index 00000000000..600ce40149b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval5.C @@ -0,0 +1,42 @@ +// { dg-do run } +// { dg-options "-std=c++2a" } + +namespace std { + constexpr inline bool + is_constant_evaluated () noexcept + { + return __builtin_is_constant_evaluated (); + } +} + +extern "C" void abort (); +template +constexpr int f0 (int n) { return n + N; } +template +consteval int f1 (int n) { return f0 (n) * n + N; } +template +consteval int f2 (int n) { return f1 (n); } +template +consteval bool f3 () { return std::is_constant_evaluated () + N; } +struct S { constexpr S (int x) : s (x) {} template consteval int m1 (int n) const; int s; }; +template +consteval int +S::m1 (int n) const +{ + n += s + N; + return n; +} + +constexpr int a = 2; +int b = f1<0> (a); +int c = f2<0> (f1<0> (a)); +bool d = f3<0> (); +constexpr S e = 41; +int f = e.m1<0> (1); + +int +main () +{ + if (b != 4 || c != 16 || !d || f != 42) + abort (); +} diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval6.C b/gcc/testsuite/g++.dg/cpp2a/consteval6.C new file mode 100644 index 00000000000..72d5f79762e --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval6.C @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct A { + constexpr A () {} + A (A const&) = delete; // { dg-message "declared here" } +}; + +template +constexpr void +foo () +{ + T t; + T u = t; +} + +template +consteval void +bar () +{ + T t; + T u = t; // { dg-error "use of deleted function" } +} + +using B = decltype (foo ()); +using C = decltype (bar ()); // { dg-message "required from here" } diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval7.C b/gcc/testsuite/g++.dg/cpp2a/consteval7.C new file mode 100644 index 00000000000..10e4ea41c9b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval7.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +consteval int foo () { return 42; } +consteval auto bar () { return foo; } +constexpr auto a = bar (); // { dg-error "immediate evaluation returns address of immediate function 'consteval int foo\\(\\)'" } +struct S { int b; int (*c) (); }; +consteval S baz () { return { 5, foo }; } +consteval int qux () { S s = baz (); return s.b + s.c (); } +consteval int quux () { constexpr S s = baz (); return s.b + s.c (); } +constexpr auto d = baz (); // { dg-error "immediate evaluation returns address of immediate function 'consteval int foo\\(\\)'" } +constexpr auto e = qux (); +constexpr auto f = quux (); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval8.C b/gcc/testsuite/g++.dg/cpp2a/consteval8.C new file mode 100644 index 00000000000..105737d8f76 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval8.C @@ -0,0 +1,29 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct S { consteval S () : a (1), b (2) { a++; b++; } consteval S (int x) : a (x), b (x) { a++; b--; } int a, b; }; +S c; +S d = 25; + +int +foo () +{ + S a; + a.b++; + c = a; + S b = 12; + c.a += b.a; + c.b += b.b; + S e[2]; + S f[2] = { 1, 2 }; + return S ().a + e[1].a + f[0].b; +} + +constexpr S g; +constexpr S h = 42; +constexpr S i[2]; +constexpr S j[2] = { 3, 4 }; +static_assert (g.a == 2 && g.b == 3); +static_assert (h.a == 43 && h.b == 41); +static_assert (i[0].a == 2 && i[0].b == 3 && i[1].a == 2 && i[1].b == 3); +static_assert (j[0].a == 4 && j[0].b == 2 && j[1].a == 5 && j[1].b == 3); diff --git a/gcc/testsuite/g++.dg/cpp2a/consteval9.C b/gcc/testsuite/g++.dg/cpp2a/consteval9.C new file mode 100644 index 00000000000..489286a12d2 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/consteval9.C @@ -0,0 +1,31 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +consteval int bar (int i) { if (i != 1) throw 1; return 0; } // { dg-error "is not a constant expression" } + +template +void foo () +{ + int a = bar (N); +} + +template +void qux () +{ + int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } +} + +template +void quux () +{ + int a = bar (5); // { dg-message "in 'constexpr' expansion of 'bar\\(5\\)'" } +} + +void +baz () +{ + foo<1> (); + qux<2> (); +} + +int a = bar (2); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } diff --git a/gcc/testsuite/g++.dg/ext/consteval1.C b/gcc/testsuite/g++.dg/ext/consteval1.C new file mode 100644 index 00000000000..928e0f8ab17 --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/consteval1.C @@ -0,0 +1,6 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +consteval int foo (int x) { return x; } +int d = 6; // { dg-message "'int d' is not const" } +bool e = __builtin_has_attribute (foo (d), packed); // { dg-error "the value of 'd' is not usable in a constant expression" } -- 2.30.2