Hi! The following patch implements P1073R3, i.e. consteval, except that virtual consteval is not supported (I think support for that would need to include the consteval virtual methods at the end of the binfo structures after all non-consteval virtual methods, but make sure we don't actually emit those in the actual virtual tables etc.).
Unlike the previous implementation, this doesn't invoke consteval function already during parsing, but later on, so there aren't issues with say consteval constructors or consteval in default arguments. Initially I thought the best spot to do that would be during cp_fold, but that isn't called e.g. on template function bodies before instantiation, or on the expressions from unevaluated contexts, so I had to add a function to walk trees and evaluate consteval function calls found in there. Furthermore, cp_fold isn't called just during cp_fold_function, but also during fold_for_warn etc., so some consteval functions might be evaluated multiple times, which can be a problem that if there are errors, they might be emitted multiple times. If you think it is worth avoiding the tree walk for -std=c++2a for the finish_function of non-templates, perhaps it could be done during cp_genericize too. Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk? 2019-10-15 Jakub Jelinek <ja...@redhat.com> 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. (cxx_eval_consteval): Declare. * 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_has_attribute_expression): Call cxx_eval_consteval. (cp_parser_lambda_declarator_opt): Handle ds_consteval. (cp_parser_decl_specifier_seq): Handle RID_CONSTEVAL. (cp_parser_explicit_instantiation): Diagnose explicit instantiation with consteval specifier. (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. * 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. (finish_decltype_type): Call cxx_eval_consteval. * 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. (check_initializer): Call cxx_eval_consteval. (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. (finish_function): Call cxx_eval_consteval for non-immediate functions. * constexpr.c (struct constexpr_global_ctx): Add in_consteval field, initialize it in the constructor. (cxx_eval_call_expression): When encountering immediate function call outside of in_consteval and outside of immediate function, call cxx_constant_value. (find_immediate_fndecl): New function. (cxx_eval_outermost_constant_expr): Allow consteval calls returning void. Set global_ctx.in_consteval. Diagnose returning address of immediate function from in_consteval evaluation. (fold_non_dependent_expr_template): Add forward declaration. Add allow_non_constant argument and pass it through to cxx_eval_outermost_constant_expr. (cxx_eval_consteval_r, cxx_eval_consteval): New functions. (fold_non_dependent_expr, fold_non_dependent_init): Adjust fold_non_dependent_expr_template callers. * method.c (defaulted_late_check): Adjust diagnostics for consteval. * except.c (finish_noexcept_expr): Call cxx_eval_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. 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/ext/consteval1.C: New test. --- gcc/c-family/c-common.h.jj 2019-10-14 10:26:37.832352583 +0200 +++ gcc/c-family/c-common.h 2019-10-14 17:24:26.407978329 +0200 @@ -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, --- gcc/c-family/c-common.c.jj 2019-10-14 10:26:37.810352919 +0200 +++ gcc/c-family/c-common.c 2019-10-14 17:24:46.361675167 +0200 @@ -459,6 +459,7 @@ const struct c_common_resword c_common_r { "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 }, --- gcc/cp/cp-tree.h.jj 2019-10-10 09:04:50.547692560 +0200 +++ gcc/cp/cp-tree.h 2019-10-15 12:10:51.607055382 +0200 @@ -2693,7 +2693,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. */ @@ -3207,6 +3208,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. @@ -5861,6 +5871,7 @@ enum cp_decl_spec { ds_constexpr, ds_complex, ds_constinit, + ds_consteval, ds_thread, ds_type_spec, ds_redefined_builtin_type_spec, @@ -7799,6 +7810,7 @@ extern tree fold_non_dependent_expr (tr extern tree fold_non_dependent_init (tree, tsubst_flags_t = tf_warning_or_error, bool = false); +extern tree cxx_eval_consteval (tree); extern tree fold_simple (tree); extern bool reduced_constant_expression_p (tree); extern bool is_instantiation_of_constexpr (tree); --- gcc/cp/parser.c.jj 2019-10-14 08:52:03.704579127 +0200 +++ gcc/cp/parser.c 2019-10-15 14:08:06.963289881 +0200 @@ -995,11 +995,13 @@ cp_keyword_starts_decl_specifier_p (enum /* 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: @@ -1827,7 +1829,8 @@ 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, @@ -8580,7 +8583,10 @@ cp_parser_has_attribute_expression (cp_p /* If the type-id production did not work out, then we must be looking at an expression. */ if (!oper || oper == error_mark_node) - oper = cp_parser_assignment_expression (parser); + { + oper = cp_parser_assignment_expression (parser); + oper = cxx_eval_consteval (oper); + } STRIP_ANY_LOCATION_WRAPPER (oper); @@ -10998,6 +11004,9 @@ cp_parser_lambda_declarator_opt (cp_pars "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); @@ -14059,6 +14068,11 @@ cp_parser_decl_specifier_seq (cp_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); @@ -14176,7 +14190,8 @@ cp_parser_decl_specifier_seq (cp_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]); @@ -17315,6 +17330,10 @@ cp_parser_explicit_instantiation (cp_par 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); @@ -29964,9 +29983,10 @@ set_and_check_decl_spec_loc (cp_decl_spe "friend", "typedef", "using", - "constexpr", + "constexpr", "__complex", - "constinit" + "constinit", + "consteval" }; gcc_rich_location richloc (location); richloc.add_fixit_remove (); --- gcc/cp/call.c.jj 2019-10-10 01:33:38.283942013 +0200 +++ gcc/cp/call.c 2019-10-15 10:49:56.837744805 +0200 @@ -289,6 +289,9 @@ build_addr_func (tree function, tsubst_f } 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); --- gcc/cp/error.c.jj 2019-10-10 01:33:38.187943450 +0200 +++ gcc/cp/error.c 2019-10-14 16:52:27.707101444 +0200 @@ -1648,7 +1648,9 @@ dump_function_decl (cxx_pretty_printer * { 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"); } } --- gcc/cp/semantics.c.jj 2019-10-14 10:11:32.396100623 +0200 +++ gcc/cp/semantics.c 2019-10-15 12:26:52.389473220 +0200 @@ -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))))) @@ -9628,6 +9629,8 @@ finish_decltype_type (tree expr, bool id return error_mark_node; } + expr = cxx_eval_consteval (expr); + /* Depending on the resolution of DR 1172, we may later need to distinguish instantiation-dependent but not type-dependent expressions so that, say, A<decltype(sizeof(T))>::U doesn't require 'typename'. */ --- gcc/cp/typeck.c.jj 2019-10-12 10:21:30.256263248 +0200 +++ gcc/cp/typeck.c 2019-10-15 12:28:35.530907909 +0200 @@ -1860,10 +1860,11 @@ cxx_alignof_expr (tree e, tsubst_flags_t tree cxx_sizeof_or_alignof_expr (tree e, enum tree_code op, bool complain) { + e = cxx_eval_consteval (e); if (op == SIZEOF_EXPR) - return cxx_sizeof_expr (e, complain? tf_warning_or_error : tf_none); + return cxx_sizeof_expr (e, complain ? tf_warning_or_error : tf_none); else - return cxx_alignof_expr (e, complain? tf_warning_or_error : tf_none); + return cxx_alignof_expr (e, complain ? tf_warning_or_error : tf_none); } /* Build a representation of an expression 'alignas(E).' Return the @@ -6177,6 +6178,16 @@ cp_build_addr_expr_1 (tree arg, bool str { 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); --- gcc/cp/decl.c.jj 2019-10-15 18:34:22.724179075 +0200 +++ gcc/cp/decl.c 2019-10-15 18:50:35.719370678 +0200 @@ -1225,7 +1225,13 @@ validate_constexpr_redeclaration (tree o 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 o /* 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 o && 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; @@ -5034,12 +5047,15 @@ check_tag_decl (cp_decl_specifier_seq *d 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) @@ -5397,11 +5413,14 @@ start_decl (const cp_declarator *declara 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) @@ -6559,6 +6578,9 @@ check_initializer (tree decl, tree init, /* There is no way to make a variable-sized class type in GNU C++. */ gcc_assert (TREE_CONSTANT (TYPE_SIZE (type))); + if (init && TREE_STATIC (decl)) + init = cxx_eval_consteval (init); + if (init && BRACE_ENCLOSED_INITIALIZER_P (init)) { int init_len = CONSTRUCTOR_NELTS (init); @@ -9154,6 +9176,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); @@ -9201,7 +9232,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; @@ -9240,6 +9274,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. @@ -10604,6 +10643,7 @@ grokdeclarator (const cp_declarator *dec 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; @@ -10876,17 +10916,31 @@ grokdeclarator (const cp_declarator *dec 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; } @@ -11292,21 +11346,31 @@ grokdeclarator (const cp_declarator *dec /* 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; } } @@ -11329,9 +11393,12 @@ grokdeclarator (const cp_declarator *dec 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 " @@ -11391,6 +11458,7 @@ grokdeclarator (const cp_declarator *dec inlinep = 0; typedef_p = 0; constexpr_p = 0; + consteval_p = 0; concept_p = 0; if (storage_class != sc_static) { @@ -12788,7 +12856,7 @@ grokdeclarator (const cp_declarator *dec 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) @@ -12798,6 +12866,12 @@ grokdeclarator (const cp_declarator *dec " 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) { @@ -12819,6 +12893,14 @@ grokdeclarator (const cp_declarator *dec "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) { @@ -12849,7 +12931,8 @@ grokdeclarator (const cp_declarator *dec 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, @@ -12951,8 +13034,8 @@ grokdeclarator (const cp_declarator *dec 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), @@ -12960,6 +13043,10 @@ grokdeclarator (const cp_declarator *dec "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]); @@ -12984,23 +13071,34 @@ grokdeclarator (const cp_declarator *dec 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) @@ -13106,6 +13204,14 @@ grokdeclarator (const cp_declarator *dec 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 @@ -13116,7 +13222,8 @@ grokdeclarator (const cp_declarator *dec 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, @@ -13209,6 +13316,12 @@ grokdeclarator (const cp_declarator *dec "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]); @@ -16469,6 +16582,9 @@ finish_function (bool inline_p) || is_valid_constexpr_fn (fndecl, /*complain*/false)) && potential_constant_expression (DECL_SAVED_TREE (fndecl))); + if (!DECL_IMMEDIATE_FUNCTION_P (fndecl)) + DECL_SAVED_TREE (fndecl) = cxx_eval_consteval (DECL_SAVED_TREE (fndecl)); + /* Save constexpr function body before it gets munged by the NRV transformation. */ maybe_save_function_definition (fndecl); @@ -16478,7 +16594,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 @@ -16595,7 +16711,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 --- gcc/cp/constexpr.c.jj 2019-10-12 10:21:30.258263218 +0200 +++ gcc/cp/constexpr.c 2019-10-15 15:33:18.390730126 +0200 @@ -1022,8 +1022,10 @@ struct constexpr_global_ctx { /* Heap VAR_DECLs created during the evaluation of the outermost constant expression. */ auto_vec<tree, 16> heap_vars; + /* True if evaluating a call to consteval function. */ + bool in_consteval; /* Constructor. */ - constexpr_global_ctx () : constexpr_ops_count (0) {} + constexpr_global_ctx () : constexpr_ops_count (0), in_consteval (false) {} }; /* The constexpr expansion context. CALL is the current function @@ -1663,6 +1665,20 @@ cxx_eval_call_expression (const constexp } } } + else if (DECL_IMMEDIATE_FUNCTION_P (fun) + && !ctx->global->in_consteval + && (current_function_decl == NULL_TREE + || !DECL_IMMEDIATE_FUNCTION_P (current_function_decl))) + { + tree c = cxx_constant_value (t, NULL_TREE); + if (c == t || c == error_mark_node) + { + *non_constant_p = true; + return t; + } + return cxx_eval_constant_expression (ctx, c, lval, non_constant_p, + overflow_p); + } if (TREE_CODE (fun) != FUNCTION_DECL) { if (!ctx->quiet && !*non_constant_p) @@ -5641,6 +5657,17 @@ find_heap_var_refs (tree *tp, int *walk_ 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 @@ -5675,7 +5702,26 @@ cxx_eval_outermost_constant_expr (tree t /* 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 + global_ctx.in_consteval = true; + } + } + else if (cxx_dialect >= cxx2a + && (TREE_CODE (t) == CALL_EXPR || TREE_CODE (t) == AGGR_INIT_EXPR)) + { + tree fndecl = cp_get_callee_fndecl_nofold (t); + if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl)) + global_ctx.in_consteval = true; } if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)) { @@ -5776,6 +5822,25 @@ cxx_eval_outermost_constant_expr (tree t } } + /* 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 (global_ctx.in_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. */ @@ -5865,6 +5930,63 @@ cxx_constant_dtor (tree t, tree decl) cxx_eval_outermost_constant_expr (t, false, true, true, true, decl); } +static tree +fold_non_dependent_expr_template (tree, tsubst_flags_t, bool, bool); + +/* Helper function for cxx_eval_consteval. Find calls to immediate + functions in *TP and evaluate them. */ + +static tree +cxx_eval_consteval_r (tree *tp, int */*walk_subtrees*/, void */*data*/) +{ + if (TREE_CODE (*tp) == CALL_EXPR || TREE_CODE (*tp) == AGGR_INIT_EXPR) + { + tree callee = cp_get_callee (*tp); + if (processing_template_decl + && callee + && TREE_CODE (callee) != FUNCTION_DECL + && (TREE_TYPE (callee) == NULL_TREE + || !INDIRECT_TYPE_P (TREE_TYPE (callee)))) + return NULL_TREE; + callee = cp_get_fndecl_from_callee (callee, false); + if (callee && DECL_IMMEDIATE_FUNCTION_P (callee)) + { + if (processing_template_decl) + fold_non_dependent_expr_template (*tp, tf_warning_or_error, + /*allow_non_constant*/false, + /*manifestly_const_eval=*/ true); + else + { + int saved_errorcount = errorcount; + tree t = cxx_constant_value (*tp, NULL_TREE); + if (t != *tp) + *tp = t; + else + { + /* Error recovery, make sure we don't diagnose it multiple + times. */ + gcc_assert (errorcount > saved_errorcount); + if (VOID_TYPE_P (TREE_TYPE (*tp))) + *tp = void_node; + else + *tp = build_zero_cst (t); + } + } + } + } + return NULL_TREE; +} + +/* Evaluate calls to immediate functions in T. */ + +tree +cxx_eval_consteval (tree t) +{ + if (cxx_dialect >= cxx2a) + cp_walk_tree_without_duplicates (&t, cxx_eval_consteval_r, NULL); + return t; +} + /* Helper routine for fold_simple function. Either return simplified expression T, otherwise NULL_TREE. In contrast to cp_fully_fold, and to maybe_constant_value, we try to fold @@ -6016,6 +6138,7 @@ clear_cv_and_fold_caches (bool sat /*= t static tree fold_non_dependent_expr_template (tree t, tsubst_flags_t complain, + bool allow_non_constant, bool manifestly_const_eval) { gcc_assert (processing_template_decl); @@ -6035,7 +6158,7 @@ fold_non_dependent_expr_template (tree t return t; } - tree r = cxx_eval_outermost_constant_expr (t, true, true, + tree r = cxx_eval_outermost_constant_expr (t, allow_non_constant, true, manifestly_const_eval, false, NULL_TREE); /* cp_tree_equal looks through NOPs, so allow them. */ @@ -6080,6 +6203,7 @@ fold_non_dependent_expr (tree t, if (processing_template_decl) return fold_non_dependent_expr_template (t, complain, + /*allow_non_constant*/true, manifestly_const_eval); return maybe_constant_value (t, NULL_TREE, manifestly_const_eval); @@ -6099,6 +6223,7 @@ fold_non_dependent_init (tree t, if (processing_template_decl) { t = fold_non_dependent_expr_template (t, complain, + /*allow_non_constant*/true, manifestly_const_eval); /* maybe_constant_init does this stripping, so do it here too. */ if (TREE_CODE (t) == TARGET_EXPR) --- gcc/cp/method.c.jj 2019-10-12 10:21:30.257263232 +0200 +++ gcc/cp/method.c 2019-10-14 18:23:24.258313542 +0200 @@ -2256,8 +2256,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; --- gcc/cp/except.c.jj 2019-06-25 08:57:57.706484715 +0200 +++ gcc/cp/except.c 2019-10-15 13:56:59.833413107 +0200 @@ -1191,6 +1191,8 @@ finish_noexcept_expr (tree expr, tsubst_ if (expr == error_mark_node) return error_mark_node; + expr = cxx_eval_consteval (expr); + if (processing_template_decl) return build_min (NOEXCEPT_EXPR, boolean_type_node, expr); --- gcc/cp/lambda.c.jj 2019-10-10 01:33:38.284941998 +0200 +++ gcc/cp/lambda.c 2019-10-15 13:41:52.372183503 +0200 @@ -1189,6 +1189,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) @@ -1221,6 +1224,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)) { --- gcc/testsuite/g++.dg/cpp2a/consteval1.C.jj 2019-10-14 16:52:27.711101384 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval1.C 2019-10-14 16:52:27.711101384 +0200 @@ -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 (); +} --- gcc/testsuite/g++.dg/cpp2a/consteval2.C.jj 2019-10-14 16:52:27.712101369 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval2.C 2019-10-14 16:52:27.712101369 +0200 @@ -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 (); +} --- gcc/testsuite/g++.dg/cpp2a/consteval3.C.jj 2019-10-14 16:52:27.712101369 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval3.C 2019-10-15 16:28:25.253572602 +0200 @@ -0,0 +1,62 @@ +// { 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 "" } +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 <class T> 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; +} --- gcc/testsuite/g++.dg/cpp2a/consteval4.C.jj 2019-10-14 16:52:27.712101369 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval4.C 2019-10-14 16:52:27.712101369 +0200 @@ -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); +} --- gcc/testsuite/g++.dg/cpp2a/consteval5.C.jj 2019-10-14 16:52:27.712101369 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval5.C 2019-10-14 16:52:27.712101369 +0200 @@ -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 <int N> +constexpr int f0 (int n) { return n + N; } +template <int N> +consteval int f1 (int n) { return f0<N> (n) * n + N; } +template <int N> +consteval int f2 (int n) { return f1<N> (n); } +template <int N> +consteval bool f3 () { return std::is_constant_evaluated () + N; } +struct S { constexpr S (int x) : s (x) {} template <int N> consteval int m1 (int n) const; int s; }; +template <int N> +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 (); +} --- gcc/testsuite/g++.dg/cpp2a/consteval6.C.jj 2019-10-14 16:52:27.712101369 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval6.C 2019-10-14 16:52:27.712101369 +0200 @@ -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<typename T> +constexpr void +foo () +{ + T t; + T u = t; +} + +template<typename T> +consteval void +bar () +{ + T t; + T u = t; // { dg-error "use of deleted function" } +} + +using B = decltype (foo<A> ()); +using C = decltype (bar<A> ()); // { dg-message "required from here" } --- gcc/testsuite/g++.dg/cpp2a/consteval7.C.jj 2019-10-14 16:52:27.726101156 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval7.C 2019-10-15 11:32:51.815658571 +0200 @@ -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 (); --- gcc/testsuite/g++.dg/cpp2a/consteval8.C.jj 2019-10-15 10:29:50.229065304 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval8.C 2019-10-15 10:30:33.032415274 +0200 @@ -0,0 +1,14 @@ +// { dg-do compile } +// { dg-options "-std=c++2a" } + +struct S { consteval S () : a (1), b (2) { a++; b++; } int a, b; }; +S c; + +int +foo () +{ + S a; + a.b++; + c = a; + return S ().a; +} --- gcc/testsuite/g++.dg/cpp2a/consteval9.C.jj 2019-10-15 13:09:39.781510537 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval9.C 2019-10-15 13:11:52.785492131 +0200 @@ -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 <int N> +void foo () +{ + int a = bar (N); +} + +template <int N> +void qux () +{ + int a = bar (N); // { dg-message "in 'constexpr' expansion of 'bar\\(2\\)'" } +} + +template <int N> +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\\)'" } --- gcc/testsuite/g++.dg/cpp2a/consteval10.C.jj 2019-10-15 13:12:24.741007195 +0200 +++ gcc/testsuite/g++.dg/cpp2a/consteval10.C 2019-10-15 13:13:29.032031549 +0200 @@ -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 } } --- gcc/testsuite/g++.dg/ext/consteval1.C.jj 2019-10-15 14:06:18.667933180 +0200 +++ gcc/testsuite/g++.dg/ext/consteval1.C 2019-10-15 15:33:18.391730111 +0200 @@ -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" } Jakub