diff options
author | Jakub Jelinek <jakub@redhat.com> | 2019-11-02 00:28:20 +0100 |
---|---|---|
committer | Jakub Jelinek <jakub@gcc.gnu.org> | 2019-11-02 00:28:20 +0100 |
commit | f968ef9b8df2bc2287e5e7e87299e5a2a44e8c94 (patch) | |
tree | 6aba8238b5260a14347acaa810dbae9579df6f8d /gcc/cp | |
parent | 8412b939d1cf375c8e478e39a5ac9d7260e4c23c (diff) | |
download | gcc-f968ef9b8df2bc2287e5e7e87299e5a2a44e8c94.zip gcc-f968ef9b8df2bc2287e5e7e87299e5a2a44e8c94.tar.gz gcc-f968ef9b8df2bc2287e5e7e87299e5a2a44e8c94.tar.bz2 |
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
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 65 | ||||
-rw-r--r-- | gcc/cp/call.c | 71 | ||||
-rw-r--r-- | gcc/cp/constexpr.c | 70 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 15 | ||||
-rw-r--r-- | gcc/cp/decl.c | 182 | ||||
-rw-r--r-- | gcc/cp/error.c | 4 | ||||
-rw-r--r-- | gcc/cp/init.c | 12 | ||||
-rw-r--r-- | gcc/cp/lambda.c | 6 | ||||
-rw-r--r-- | gcc/cp/method.c | 5 | ||||
-rw-r--r-- | gcc/cp/name-lookup.h | 5 | ||||
-rw-r--r-- | gcc/cp/parser.c | 59 | ||||
-rw-r--r-- | gcc/cp/semantics.c | 3 | ||||
-rw-r--r-- | gcc/cp/typeck.c | 10 |
13 files changed, 444 insertions, 63 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 50da698..06a4e31 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,5 +1,70 @@ 2019-11-02 Jakub Jelinek <jakub@redhat.com> + 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 b0c6370..3dac31a 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 4baaac0..84ed7ac 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 6c9731f..eb85639 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 72acc8f..8f22f23 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 %<constexpr%> " - "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, "%<typedef%> 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], - "%<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], - "%<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 %<thread_local%> in %<constexpr%> function", - decl); + "%qD declared %<thread_local%> 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 %<static%> in %<constexpr%> function", decl); + "%qD declared %<static%> 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), + "%<virtual%> %<consteval%> 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 %<constexpr%>"); + "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], - "%<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], - "%<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 %<concept%>"); + { + 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 %<constexpr%>"); + "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 %<constinit%>"); - 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 %<concept%>"); + "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 %<concept%>", - 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 %<concept%>", - 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 " - "%<constexpr%>", 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 " - "%<constinit%>", 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%>"); + 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 83b8b12..d104a4d 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 f86cf55..eda8272 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 d621bec..c582aa5 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 b613e5d..09e9c73 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 b44687e..0b7bf83 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 f1664e6..516c14b 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" " %<constexpr%> specifier"); + if (decl_spec_seq_has_spec_p (&decl_specifiers, ds_consteval)) + permerror (decl_specifiers.locations[ds_consteval], + "explicit instantiation shall not use" + " %<consteval%> 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 59def31..8293c07 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 03c39b3..15529c6 100644 --- a/gcc/cp/typeck.c +++ b/gcc/cp/typeck.c @@ -6177,6 +6177,16 @@ cp_build_addr_expr_1 (tree arg, bool strict_lvalue, tsubst_flags_t complain) { 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; val = build_address (arg); |