diff options
Diffstat (limited to 'gcc/cp')
35 files changed, 7211 insertions, 1997 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index fe8aa4b..2944f22 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,1558 @@ +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + * cp-tree.h (struct lang_type): Add comment before key_method. + Remove lambda_expr. + (CLASSTYPE_KEY_METHOD): Give NULL_TREE if not TYPE_POLYMORPHIC_P. + (SET_CLASSTYPE_KEY_METHOD): Define. + (CLASSTYPE_LAMBDA_EXPR): Give NULL_TREE if TYPE_POLYMORPHIC_P. + Use key_method member instead of lambda_expr. + (SET_CLASSTYPE_LAMBDA_EXPR): Define. + * class.cc (determine_key_method): Use SET_CLASSTYPE_KEY_METHOD + macro. + * decl.cc (xref_tag): Use SET_CLASSTYPE_LAMBDA_EXPR macro. + * lambda.cc (begin_lambda_type): Likewise. + * module.cc (trees_in::read_class_def): Use SET_CLASSTYPE_LAMBDA_EXPR + and SET_CLASSTYPE_KEY_METHOD macros, assert lambda is NULL if + TYPE_POLYMORPHIC_P and otherwise assert key_method is NULL. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/120628 + * parser.cc (cp_parser_elaborated_type_specifier): Use + cp_parser_nth_token_starts_class_definition_p with extra argument 1 + instead of cp_parser_next_token_starts_class_definition_p. + (cp_parser_class_property_specifier_seq_opt): For final conditional + keyword in C++98 check if the token after it isn't + cp_parser_nth_token_starts_class_definition_p nor CPP_NAME and in + that case break without consuming it nor warning. + (cp_parser_class_head): Use + cp_parser_nth_token_starts_class_definition_p with extra argument 1 + instead of cp_parser_next_token_starts_class_definition_p. + (cp_parser_next_token_starts_class_definition_p): Renamed to ... + (cp_parser_nth_token_starts_class_definition_p): ... this. Add N + argument. Use cp_lexer_peek_nth_token instead of cp_lexer_peek_token. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/120569 + * parser.cc (cp_parser_class_property_specifier_seq_opt): New + function. + (cp_parser_class_head): Use it instead of + cp_parser_property_specifier_seq_opt. Don't diagnose + VIRT_SPEC_OVERRIDE here. Formatting fix. + +2025-07-10 Jakub Jelinek <jakub@redhat.com> + + PR c++/117785 + * constexpr.cc: Implement C++26 P3068R5 - constexpr exceptions. + (class constexpr_global_ctx): Add caught_exceptions and + uncaught_exceptions members. + (constexpr_global_ctx::constexpr_global_ctx): Initialize + uncaught_exceptions. + (returns, breaks, continues, switches): Move earlier. + (throws): New function. + (exception_what_str, diagnose_std_terminate, + diagnose_uncaught_exception): New functions. + (enum cxa_builtin): New type. + (cxx_cxa_builtin_fn_p, cxx_eval_cxa_builtin_fn): New functions. + (cxx_eval_builtin_function_call): Add jump_target argument. Call + cxx_eval_cxa_builtin_fn for __builtin_eh_ptr_adjust_ref. Adjust + cxx_eval_constant_expression calls, if it results in jmp_target, + set *jump_target to it and return. + (cxx_bind_parameters_in_call): Add jump_target argument. Pass + it through to cxx_eval_constant_expression. If it sets *jump_target, + break. + (fold_operand): Adjust cxx_eval_constant_expression caller. + (cxx_eval_assert): Likewise. If it set jmp_target, return true. + (cxx_eval_internal_function): Add jump_target argument. Pass it + through to cxx_eval_constant_expression. Return early if *jump_target + after recursing on args. + (cxx_eval_dynamic_cast_fn): Likewise. Don't set reference_p for + C++26 with -fexceptions. + (cxx_eval_thunk_call): Add jump_target argument. Pass it through + to cxx_eval_constant_expression. + (cxx_set_object_constness): Likewise. Don't set TREE_READONLY if + throws (jump_target). + (cxx_eval_call_expression): Add jump_target argument. Pass it + through to cxx_eval_internal_function, cxx_eval_builtin_function_call, + cxx_eval_thunk_call, cxx_eval_dynamic_cast_fn and + cxx_set_object_constness. Pass it through also + cxx_eval_constant_expression on arguments, cxx_bind_parameters_in_call + and cxx_fold_indirect_ref and for those cases return early + if *jump_target. Call cxx_eval_cxa_builtin_fn for cxx_cxa_builtin_fn_p + functions. For cxx_eval_constant_expression on body, pass address of + cleared jmp_target automatic variable, if it throws propagate + to *jump_target and make it non-cacheable. For C++26 don't diagnose + calls to non-constexpr functions before cxx_bind_parameters_in_call + could report some argument throwing an exception. + (cxx_eval_unary_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression and return early + if *jump_target after the call. + (cxx_fold_pointer_plus_expression): Likewise. + (cxx_eval_binary_expression): Likewise and similarly for + cxx_fold_pointer_plus_expression call. + (cxx_eval_conditional_expression): Pass jump_target to + cxx_eval_constant_expression on first operand and return early + if *jump_target after the call. + (cxx_eval_vector_conditional_expression): Add jump_target argument. + Pass it through to cxx_eval_constant_expression for all 3 arguments + and return early if *jump_target after any of those calls. + (get_array_or_vector_nelts): Add jump_target argument. Pass it + through to cxx_eval_constant_expression. + (eval_and_check_array_index): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + each of them if *jump_target. + (cxx_eval_array_reference): Likewise. + (cxx_eval_component_reference): Likewise. + (cxx_eval_bit_field_ref): Likewise. + (cxx_eval_bit_cast): Likewise. Assert CHECKING_P call doesn't + throw or return. + (cxx_eval_logical_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + each of them if *jump_target. + (cxx_eval_bare_aggregate): Likewise. + (cxx_eval_vec_init_1): Add jump_target argument. Pass it through + to cxx_eval_bare_aggregate and recursive call. Pass it through + to get_array_or_vector_nelts and cxx_eval_constant_expression + and return early after it if *jump_target. + (cxx_eval_vec_init): Add jump_target argument. Pass it through + to cxx_eval_constant_expression and cxx_eval_vec_init_1. + (cxx_union_active_member): Add jump_target argument. Pass it + through to cxx_eval_constant_expression and return early after it + if *jump_target. + (cxx_fold_indirect_ref_1): Add jump_target argument. Pass it + through to cxx_union_active_member and recursive calls. + (cxx_eval_indirect_ref): Add jump_target argument. Pass it through + to cxx_fold_indirect_ref_1 calls and to recursive call, in which + case return early after it if *jump_target. + (cxx_fold_indirect_ref): Add jump_target argument. Pass it through + to cxx_fold_indirect_ref and cxx_eval_constant_expression calls and + return early after those if *jump_target. + (cxx_eval_trinary_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + those if *jump_target. + (cxx_eval_store_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression and eval_and_check_array_index + calls and return early after those if *jump_target. + (cxx_eval_increment_expression): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls and return early after + those if *jump_target. + (label_matches): Handle VAR_DECL case. + (cxx_eval_statement_list): Remove local_target variable and + !jump_target handling. Handle throws (jump_target) like returns or + breaks. + (cxx_eval_loop_expr): Remove local_target variable and !jump_target + handling. Pass it through to cxx_eval_constant_expression. Handle + throws (jump_target) like returns. + (cxx_eval_switch_expr): Pass jump_target through to + cxx_eval_constant_expression on cond, return early after it + if *jump_target. + (build_new_constexpr_heap_type): Add jump_target argument. Pass it + through to cxx_eval_constant_expression calls, return early after + those if *jump_target. + (merge_jump_target): New function. + (cxx_eval_constant_expression): Make jump_target argument no longer + defaulted, don't test jump_target for NULL. Pass jump_target + through to recursive calls, cxx_eval_call_expression, + cxx_eval_store_expression, cxx_eval_indirect_ref, + cxx_eval_unary_expression, cxx_eval_binary_expression, + cxx_eval_logical_expression, cxx_eval_array_reference, + cxx_eval_component_reference, cxx_eval_bit_field_ref, + cxx_eval_vector_conditional_expression, cxx_eval_bare_aggregate, + cxx_eval_vec_init, cxx_eval_trinary_expression, cxx_fold_indirect_ref, + build_new_constexpr_heap_type, cxx_eval_increment_expression, + cxx_eval_bit_cast and return earlyu after some of those + if *jump_target as needed. + (cxx_eval_constant_expression) <case TARGET_EXPR>: For C++26 push + also CLEANUP_EH_ONLY cleanups, with NULL_TREE marker after them. + (cxx_eval_constant_expression) <case RETURN_EXPR>: Don't + override *jump_target if throws (jump_target). + (cxx_eval_constant_expression) <case TRY_CATCH_EXPR, case TRY_BLOCK, + case MUST_NOT_THROW_EXPR, case TRY_FINALLY_EXPR, case CLEANUP_STMT>: + Handle C++26 constant expressions. + (cxx_eval_constant_expression) <case CLEANUP_POINT_EXPR>: For C++26 + with throws (jump_target) evaluate the CLEANUP_EH_ONLY cleanups as + well, and if not throws (jump_target) skip those. Set *jump_target + if some of the cleanups threw. + (cxx_eval_constant_expression) <case THROW_EXPR>: Recurse on operand + for C++26. + (cxx_eval_outermost_constant_expr): Diagnose uncaught exceptions both + from main expression and cleanups, diagnose also + break/continue/returns from the main expression. Handle + CLEANUP_EH_ONLY cleanup markers. Don't diagnose mutable poison stuff + if non_constant_p. Use different diagnostics for non-deleted heap + allocations if they were allocated by __cxa_allocate_exception. + (callee_might_throw): New function. + (struct check_for_return_continue_data): Add could_throw field. + (check_for_return_continue): Handle AGGR_INIT_EXPR and CALL_EXPR and + set d->could_throw if they could throw. + (potential_constant_expression_1): For CALL_EXPR allow + cxx_dynamic_cast_fn_p calls. For C++26 set *jump_target to void_node + for calls that could throw. For C++26 if call to non-constexpr call + is seen, try to evaluate arguments first and if they could throw, + don't diagnose call to non-constexpr function nor return false. + Adjust check_for_return_continue_data initializers and + set *jump_target to void_node if data.could_throw_p. For C++26 + recurse on THROW_EXPR argument. Add comment explaining TRY_BLOCK + handling with C++26 exceptions. Handle throws like returns in some + cases. + * cp-tree.h (MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P, + MUST_NOT_THROW_CATCH_P, DECL_EXCEPTION_REFCOUNT): Define. + (DECL_LOCAL_DECL_P): Fix comment typo, VARIABLE_DECL -> VAR_DECL. + (enum cp_built_in_function): Add CP_BUILT_IN_EH_PTR_ADJUST_REF, + (handler_match_for_exception_type): Declare. + * call.cc (handler_match_for_exception_type): New function. + * except.cc (initialize_handler_parm): Set MUST_NOT_THROW_CATCH_P + on newly created MUST_NOT_THROW_EXPR. + (begin_eh_spec_block): Set MUST_NOT_THROW_NOEXCEPT_P. + (wrap_cleanups_r): Set MUST_NOT_THROW_THROW_P. + (build_throw): Add another TARGET_EXPR whose scope spans + until after the __cxa_throw call and copy pointer value from ptr + to it and use it in __cxa_throw argument. + * tree.cc (builtin_valid_in_constant_expr_p): Handle + CP_BUILT_IN_EH_PTR_ADJUST_REF. + * decl.cc (cxx_init_decl_processing): Initialize + __builtin_eh_ptr_adjust_ref FE builtin. + * pt.cc (tsubst_stmt) <case MUST_NOT_THROW_EXPR>: Copy the + MUST_NOT_THROW_NOEXCEPT_P, MUST_NOT_THROW_THROW_P and + MUST_NOT_THROW_CATCH_P flags. + * cp-gimplify.cc (cp_gimplify_expr) <case CALL_EXPR>: Error on + non-folded CP_BUILT_IN_EH_PTR_ADJUST_REF calls. + +2025-07-09 Jason Merrill <jason@redhat.com> + + PR c++/121012 + PR c++/120917 + * parser.cc (cp_parser_lambda_expression): Clear + parser->in_template_argument_list_p. + +2025-07-09 Jason Merrill <jason@redhat.com> + + PR c++/121008 + PR c++/113563 + * semantics.cc (finish_this_expr): Do check current_class_ref for + non-lambda. + +2025-07-09 Marek Polacek <polacek@redhat.com> + + PR c++/119838 + * parser.cc (cp_parser_nested_name_specifier_opt): New global_p + parameter. Look for "template" when global_p is true. + (cp_parser_simple_type_specifier): Pass global_p to + cp_parser_nested_name_specifier_opt. + +2025-07-08 Marek Polacek <polacek@redhat.com> + Andrew Pinski <quic_apinski@quicinc.com> + + PR c++/83469 + PR c++/93809 + * cp-tree.h (UNION_TYPE_P): Define. + (TYPENAME_IS_UNION_P): Define. + * decl.cc (struct typename_info): Add union_p field. + (struct typename_hasher::equal): Compare union_p field. + (build_typename_type): Use ti.union_p for union_type. Set + TYPENAME_IS_UNION_P. + * error.cc (dump_type) <case TYPENAME_TYPE>: Handle + TYPENAME_IS_UNION_P. + * module.cc (trees_out::type_node): Likewise. + * parser.cc (cp_parser_check_class_key): Allow typename key for union + types and allow union keyword for typename types. + * pt.cc (tsubst) <case TYPENAME_TYPE>: Don't conflate unions with + class_type. For TYPENAME_IS_CLASS_P, check NON_UNION_CLASS_TYPE_P + rather than CLASS_TYPE_P. Add TYPENAME_IS_UNION_P handling. + +2025-07-08 Jakub Jelinek <jakub@redhat.com> + + PR c++/117784 + * decl.cc: Implement part of C++26 P2686R4 - constexpr structured + bindings. + (cp_finish_decl): Pedwarn for C++23 and older on constinit on + structured bindings except for static/thread_local where it uses + earlier error. + (grokdeclarator): Pedwarn on constexpr structured bindings for + C++23 and older instead of emitting error always, don't clear + constexpr_p in that case. + * parser.cc (cp_parser_decomposition_declaration): Copy over + DECL_DECLARED_CONSTEXPR_P and DECL_DECLARED_CONSTINIT_P flags. + +2025-07-07 Alfie Richards <alfie.richards@arm.com> + + PR c++/119498 + * decl.cc (duplicate_decls): Change logic to not always exclude FMV + annotated functions in cases of return type non-ambiguation. + +2025-07-07 Jason Merrill <jason@redhat.com> + + PR c++/120917 + * parser.cc (cp_parser_simple_type_specifier): Attach + auto in targ in parameter to -Wabbreviated-auto-in-template-arg. + (cp_parser_placeholder_type_specifier): Diagnose constrained auto in + template arg. + +2025-07-07 Jakub Jelinek <jakub@redhat.com> + + PR c++/84009 + * parser.cc (cp_parser_decomposition_declaration): Pedwarn + on thread_local, __thread or static in decl_specifiers for + for-range-declaration. + (cp_parser_init_declarator): Likewise, and also for extern + or register. + +2025-07-04 Jason Merrill <jason@redhat.com> + + PR c++/120575 + PR c++/116064 + * parser.cc (cp_parser_abort_tentative_parse): Check seen_error + instead of errorcount. + +2025-07-03 Jason Merrill <jason@redhat.com> + + PR c++/120716 + * lambda.cc (finish_lambda_function): Pass cur_stmt_list to + prune_lambda_captures. + +2025-07-03 Jason Merrill <jason@redhat.com> + + PR c++/120748 + * lambda.cc (lambda_expr_this_capture): Don't return a FIELD_DECL. + * parser.cc (cp_parser_primary_expression): Ignore THIS_FORBIDDEN + if cp_unevaluated_operand. + +2025-07-03 Jakub Jelinek <jakub@redhat.com> + + PR c++/120940 + * typeck.cc (cp_build_array_ref): Fix a pasto. + +2025-07-03 Jason Merrill <jason@redhat.com> + + PR c++/120684 + PR c++/118856 + * constexpr.cc (cxx_eval_constant_expression) [TARGET_EXPR]: Clear + the value first if is_complex. + +2025-07-01 Jakub Jelinek <jakub@redhat.com> + + PR c++/120471 + * typeck.cc (cp_build_array_ref) <case COND_EXPR>: If idx is not + INTEGER_CST, don't optimize the case (but cp_default_conversion on + array early if it has ARRAY_TYPE) or use + SAVE_EXPR <op0>, SAVE_EXPR <idx>, SAVE_EXPR <op0> as new op0 depending + on flag_strong_eval_order and whether op1 and op2 are arrays with + invariant address or tree invariant pointers. Formatting fixes. + +2025-06-28 Nathaniel Shead <nathanieloshead@gmail.com> + + * module.cc (trees_out::walking_bit_field_unit): New flag. + (trees_out::trees_out): Initialize it. + (trees_out::core_vals): Set it. + (trees_out::get_merge_kind): Use it, move previous ad-hoc check + into assertion. + +2025-06-28 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120644 + * decl.cc (cp_finish_decl): Also propagate type to partial + templates. + * module.cc (trees_out::decl_value): Add assertion that the + TREE_TYPE of a streamed template decl matches its inner. + (trees_in::is_matching_decl): Clarify function return type + deduction should only occur for non-TEMPLATE_DECL. + * pt.cc (template_for_substitution): Handle partial specs. + +2025-06-27 Marek Polacek <polacek@redhat.com> + + PR c++/120756 + * pt.cc (resolve_nondeduced_context): Pass complain to mark_used. + +2025-06-27 Jakub Jelinek <jakub@redhat.com> + + PR c++/120777 + * constexpr.cc: Implement C++26 P3533R2 - constexpr virtual + inheritance. + (is_valid_constexpr_fn): Don't reject constexpr cdtors in classes + with virtual bases for C++26, adjust error wording. + (cxx_bind_parameters_in_call): Add ORIG_FUN argument, add + values for __in_chrg and __vtt_parm arguments when needed. + (cxx_eval_dynamic_cast_fn): Adjust function comment, HINT -1 + should be possible. For C++26 if obj is cast from POINTER_PLUS_EXPR, + attempt to use cxx_fold_indirect_ref to simplify it and if successful, + build ADDR_EXPR of that. + (cxx_eval_call_expression): Add orig_fun variable, set it to + fun before looking through clones, pass it to + cxx_bind_parameters_in_call. + (reduced_constant_expression_p): Add SZ argument, pass DECL_SIZE + of FIELD_DECL e.index to recursive calls and don't return false + if SZ is non-NULL and there are unfilled fields with bit position + at or above SZ. + (cxx_fold_indirect_ref_1): Handle reading of vtables using + ptrdiff_t dynamic type instead of some pointer type. Set el_sz + to DECL_SIZE_UNIT value rather than TYPE_SIZE_UNIT of + DECL_FIELD_IS_BASE fields in classes with virtual bases. + (cxx_fold_indirect_ref): In canonicalize_obj_off lambda look + through COMPONENT_REFs with DECL_FIELD_IS_BASE in classes with + virtual bases and adjust off correspondingly. Remove assertion that + off is integer_zerop, pass tree_to_uhwi (off) instead of 0 to the + cxx_fold_indirect_ref_1 call. + * cp-tree.h (publicly_virtually_derived_p): Declare. + (reduced_constant_expression_p): Add another tree argument defaulted + to NULL_TREE. + * method.cc (synthesized_method_walk): Don't clear *constexpr_p + if there are virtual bases for C++26. + * class.cc (build_base_path): Compute fixed_type_p and + virtual_access before checks for build_simple_base_path instead of + after that and conditional cp_build_addr_expr. Use build_simple_path + if !virtual_access even when v_binfo is non-NULL. + (layout_virtual_bases): For build_base_field calls use + access_public_node rather than access_private_node if + publicly_virtually_derived_p. + (build_vtbl_initializer): Revert 2018-09-18 and 2018-12-11 changes. + (publicly_virtually_derived_p): New function. + +2025-06-27 Jason Merrill <jason@redhat.com> + + * class.cc (fixed_type_or_null): Handle class-type CALL_EXPR. + * parser.cc (cp_parser_binary_expression): Fix decltype_p handling. + +2025-06-27 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/98735 + PR c++/118904 + * cp-gimplify.cc (source_location_id): Remove. + (fold_builtin_source_location): Use generate_internal_label. + * module.cc (enum tree_tag): Add 'tt_internal_id' enumerator. + (trees_out::tree_value): Adjust assertion, write definitions + of uncontexted VAR_DECLs. + (trees_in::tree_value): Read variable definitions. + (trees_out::tree_node): Write internal labels, adjust assert. + (trees_in::tree_node): Read internal labels. + +2025-06-27 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120040 + * constexpr.cc (cxx_eval_constant_expression): Handle TYPE_NAME + now being a TYPE_DECL rather than just an IDENTIFIER_NODE. + * init.cc (build_new_constexpr_heap_type): Build a TYPE_DECL for + the returned type; mark the type as artificial. + * module.cc (trees_out::type_node): Add some assertions. + +2025-06-27 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/98735 + PR c++/120040 + * module.cc (trees_out::tree_value): Write TYPE_DECLs. + (trees_in::tree_value): Read TYPE_DECLs. + (trees_out::tree_node): Support uncontexted TYPE_DECLs, and + ensure that all parts of a by-value decl are marked for + streaming. + (trees_out::get_merge_kind): Treat members of uncontexted types + as always unique. + +2025-06-27 Nathaniel Shead <nathanieloshead@gmail.com> + + * decl.cc (grokfndecl): Add explanation of how to attach to + global module. + +2025-06-26 David Malcolm <dmalcolm@redhat.com> + + * error.cc (cxx_initialize_diagnostics): Use + diagnostic_context::set_adjust_diagnostic_info_callback. + +2025-06-26 Jakub Jelinek <jakub@redhat.com> + + * cp-trait.def: Implement C++26 P2830R10 - Constexpr Type Ordering. + (TYPE_ORDER): New. + * method.cc (type_order_value): Define. + * cp-tree.h (type_order_value): Declare. + * semantics.cc (trait_expr_value): Use gcc_unreachable also + for CPTK_TYPE_ORDER, adjust comment. + (finish_trait_expr): Handle CPTK_TYPE_ORDER. + * constraint.cc (diagnose_trait_expr): Likewise. + +2025-06-25 Martin Jambor <mjambor@suse.cz> + + * coroutines.h (class cp_coroutine_transform): Remove member + orig_fn_body. + +2025-06-24 Jakub Jelinek <jakub@redhat.com> + + PR c++/120773 + * decl.cc (grokfndecl): Implement C++26 P3618R0 - Allow attaching + main to the global module. Only pedwarn for current_lang_name + other than lang_name_cplusplus and adjust pedwarn wording. + +2025-06-23 Tobias Burnus <tburnus@baylibre.com> + + * parser.cc (OACC_WAIT_CLAUSE_MASK): Ass if clause. + +2025-06-18 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/115908 + PR c++/118074 + PR c++/95615 + * coroutines.cc (coro_frame_refcount_id): New. + (coro_init_identifiers): Initialise coro_frame_refcount_id. + (build_actor_fn): Set up initial_await_resume_called. Handle + decrementing of the frame reference count. Return directly to + the caller if that is non-zero. + (cp_coroutine_transform::wrap_original_function_body): Use a + conditional eh-only cleanup around the initial await expression + to release the body use on exception before initial await + resume. + (cp_coroutine_transform::build_ramp_function): Wrap the called + body in a cleanup that releases a use of the frame when we + return to the ramp. Implement frame, promise and argument copy + destruction via conditional cleanups when the frame use count + is zero. + +2025-06-17 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (struct coroutine_info): Update comments. + (struct coro_aw_data): Remove self_handle and add in + information to create the handle in lowering. + (expand_one_await_expression): Build a temporary coroutine + handle. + (build_actor_fn): Remove reference to the frame copy of the + coroutine handle. + (cp_coroutine_transform::wrap_original_function_body): Remove + reference to the frame copy of the coroutine handle. + +2025-06-17 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (analyze_expression_awaits): Elide assume + attributes containing await expressions, since these have + side effects. Emit a diagnostic that this has been done. + +2025-06-17 Jason Merrill <jason@redhat.com> + + PR c++/120678 + * cp-trait.def (IS_TRIVIALLY_DESTRUCTIBLE): Fix nargs. + +2025-06-17 Jason Merrill <jason@redhat.com> + + * module.cc (module_state::write_diagnostic_classification): New. + (module_state::write_begin): Call it. + (module_state::read_diagnostic_classification): New. + (module_state::read_initial): Call it. + (dk_string, dump_dc_change): New. + +2025-06-17 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (finish_co_await_expr): Do not allow in an + unevaluated context. + (finish_co_yield_expr): Likewise. + +2025-06-17 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/120273 + * coroutines.cc + (cp_coroutine_transform::wrap_original_function_body): Use + function start and end locations when synthesizing code. + (cp_coroutine_transform::cp_coroutine_transform): Set the + function end location. + +2025-06-16 Jason Merrill <jason@redhat.com> + + * constraint.cc (failed_completions_map): New. + (note_failed_type_completion): Rename from + note_failed_type_completion_for_satisfaction. Add + -Wsfinae-incomplete handling. + (failed_completion_location): New. + * class.cc (finish_struct_1): Add -Wsfinae-incomplete warning. + * decl.cc (require_deduced_type): Adjust. + (finish_function): Add -Wsfinae-incomplete warning. + * typeck.cc (complete_type_or_maybe_complain): Adjust. + (cxx_sizeof_or_alignof_type): Call note_failed_type_completion. + * pt.cc (dependent_template_arg_p): No longer static. + * cp-tree.h: Adjust. + +2025-06-16 yxj-github-437 <2457369732@qq.com> + + * parser.cc (cp_parser_asm_operand_list): Check for unexpanded + parameter packs. + +2025-06-14 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (build_co_await): Identify diagnostics + for initial and final await expressions. + (cp_coroutine_transform::wrap_original_function_body): Do + not handle initial and final await expressions here ... + (cp_coroutine_transform::apply_transforms): ... handle them + here and avoid duplicate diagnostics. + * coroutines.h: Declare inital and final await expressions + in the transform class. Save the function closing brace + location. + +2025-06-13 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/116775 + * coroutines.cc (analyze_expression_awaits): When we see + a builtin_constant_p call, and that contains one or more + await expressions, then replace the call with its result + and discard the unevaluated operand. + +2025-06-13 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (build_actor_fn): Set can_throw. + +2025-06-12 Alfie Richards <alfie.richards@arm.com> + + * decl.cc (maybe_version_functions): Change record_function_versions + call to add_function_version. + +2025-06-12 Jakub Jelinek <jakub@redhat.com> + + * cp-tree.h (union lang_type::maybe_objc_info): New type. + (struct lang_type): Use union maybe_objc_info info member + instead of tree objc_info. + * lex.cc (copy_lang_type): Use sizeof (struct lang_type) + just for ObjC++ and otherwise offsetof (struct lang_type, info). + (maybe_add_lang_type_raw): Likewise. + (cxx_make_type): Formatting fix. + +2025-06-09 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/120495 + PR c++/115605 + * pt.cc (lookup_template_class): Honour provided namespace contexts + when looking up class templates. + +2025-06-06 Jason Merrill <jason@redhat.com> + + PR c++/120555 + * decl2.cc (fn_being_defined, fn_template_being_defined): New. + (mark_used): Check fn_template_being_defined. + +2025-06-05 Patrick Palka <ppalka@redhat.com> + + PR c++/120224 + * pt.cc (tsubst_function_decl): Return error_mark_node if + substituting into the formal parameter list failed. + (tsubst_decl) <case PARM_DECL>: Return error_mark_node + upon TREE_TYPE substitution failure, when in a SFINAE + context. Return error_mark_node upon DECL_CHAIN substitution + failure. + +2025-06-05 Patrick Palka <ppalka@redhat.com> + + PR c++/118340 + * constexpr.cc (maybe_constant_value): First try looking up each + operand in the cv_cache and reusing the result. + +2025-06-05 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (analyze_fn_parms): Move from free function.. + (cp_coroutine_transform::analyze_fn_parms):... to method. + (cp_coroutine_transform::apply_transforms): Adjust call to + analyze_fn_parms. + * coroutines.h: Declare analyze_fn_parms. + +2025-06-05 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (expand_one_await_expression): Set the + initial_await_resume_called flag here. + (build_actor_fn): Populate the frame accessor for the + initial_await_resume_called flag. + (cp_coroutine_transform::wrap_original_function_body): Do + not modify the initial_await expression to include the + initial_await_resume_called flag here. + +2025-06-04 Jason Merrill <jason@redhat.com> + + PR c++/120502 + * cp-gimplify.cc (cp_fold_r) [TARGET_EXPR]: Do constexpr evaluation + before genericize. + * constexpr.cc (cxx_eval_store_expression): Add comment. + +2025-06-03 Jason Merrill <jason@redhat.com> + + * name-lookup.h (operator|, operator|=): Define for WMB_Flags. + +2025-06-02 Jason Merrill <jason@redhat.com> + + PR c++/107600 + * method.cc (destructible_expr): Fix refs and arrays of unknown + bound. + +2025-06-02 Jason Merrill <jason@redhat.com> + + PR c++/120506 + * constexpr.cc (cxx_eval_outermost_constant_expr): Always check + CONSTRUCTOR_NO_CLEARING. + +2025-06-02 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (build_actor_fn): Remove an unused + label, guard the frame deallocation correctly, use + simpler APIs to build if and return statements. + +2025-06-02 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/118903 + * constexpr.cc (potential_constant_expression_1): Emit + an error when co_await et. al. are used in constexpr + contexts. + +2025-06-02 Iain Sandoe <iain@sandoe.co.uk> + + * error.cc (dump_expr): Add co_await, co_yield and co_return. + +2025-06-02 Jason Merrill <jason@redhat.com> + + PR c++/107600 + * method.cc (destructible_expr): Handle non-classes. + (constructible_expr): Check for abstract class here... + (is_xible_helper): ...not here. + +2025-06-02 Jason Merrill <jason@redhat.com> + + PR c++/107600 + * semantics.cc (trait_expr_value) [CPTK_HAS_TRIVIAL_DESTRUCTOR]: + Add cp_unevaluated. + +2025-06-02 Sandra Loosemore <sloosemore@baylibre.com> + + * cp-tree.h (maybe_convert_cond): Declare. + * parser.cc (cp_parser_omp_context_selector): Call + maybe_convert_cond and fold_build_cleanup_point_expr on the + expression for OMP_TRAIT_PROPERTY_BOOL_EXPR. + * pt.cc (tsubst_omp_context_selector): Likewise. + * semantics.cc (maybe_convert_cond): Remove static declaration. + +2025-05-30 Jason Merrill <jason@redhat.com> + + PR c++/113563 + * lambda.cc (lambda_capture_field_type): Handle 'this' normally. + (build_capture_proxy): Special-case 'this' by-ref capture more. + (nonlambda_method_basetype): Look through xobj lambdas. + +2025-05-30 Julian Brown <julian@codesourcery.com> + Tobias Burnus <tburnus@baylibre.com> + + * constexpr.cc (reduced_constant_expression_p): Add OMP_DECLARE_MAPPER + case. + (cxx_eval_constant_expression, potential_constant_expression_1): + Likewise. + * cp-gimplify.cc (cxx_omp_finish_mapper_clauses): New function. + * cp-objcp-common.h (LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES, + LANG_HOOKS_OMP_MAPPER_LOOKUP, LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE, + LANG_HOOKS_OMP_MAP_ARRAY_SECTION): Define langhooks. + * cp-tree.h (lang_decl_base): Add omp_declare_mapper_p field. Recount + spare bits comment. + (DECL_OMP_DECLARE_MAPPER_P): New macro. + (omp_mapper_id): Add prototype. + (cp_check_omp_declare_mapper): Add prototype. + (omp_instantiate_mappers): Add prototype. + (cxx_omp_finish_mapper_clauses): Add prototype. + (cxx_omp_mapper_lookup): Add prototype. + (cxx_omp_extract_mapper_directive): Add prototype. + (cxx_omp_map_array_section): Add prototype. + * decl.cc (check_initializer): Add OpenMP declare mapper support. + (cp_finish_decl): Set DECL_INITIAL for OpenMP declare mapper var decls + as appropriate. + * decl2.cc (mark_used): Instantiate OpenMP "declare mapper" magic var + decls. + * error.cc (dump_omp_declare_mapper): New function. + (dump_simple_decl): Use above. + * parser.cc (cp_parser_omp_clause_map): Add KIND parameter. Support + "mapper" modifier. + (cp_parser_omp_all_clauses): Add KIND argument to + cp_parser_omp_clause_map call. + (cp_parser_omp_target): Call omp_instantiate_mappers before + finish_omp_clauses. + (cp_parser_omp_declare_mapper): New function. + (cp_parser_omp_declare): Add "declare mapper" support. + * pt.cc (tsubst_decl): Adjust name of "declare mapper" magic var decls + once we know their type. + (tsubst_omp_clauses): Call omp_instantiate_mappers before + finish_omp_clauses, for target regions. + (tsubst_expr): Support OMP_DECLARE_MAPPER nodes. + (instantiate_decl): Instantiate initialiser (i.e definition) for OpenMP + declare mappers. + * semantics.cc (gimplify.h): Include. + (omp_mapper_id, omp_mapper_lookup, omp_extract_mapper_directive, + cxx_omp_map_array_section, cp_check_omp_declare_mapper): New functions. + (finish_omp_clauses): Delete GOMP_MAP_PUSH_MAPPER_NAME and + GOMP_MAP_POP_MAPPER_NAME artificial clauses. + (omp_target_walk_data): Add MAPPERS field. + (finish_omp_target_clauses_r): Scan for uses of struct/union/class type + variables. + (finish_omp_target_clauses): Create artificial mapper binding clauses + for used structs/unions/classes in offload region. + +2025-05-29 David Malcolm <dmalcolm@redhat.com> + + * error.cc (cxx_format_postprocessor::clone): Update to use + unique_ptr. + (cxx_dump_pretty_printer::cxx_dump_pretty_printer): Likewise. + (cxx_initialize_diagnostics): Likewise. + +2025-05-29 Jason Merrill <jason@redhat.com> + + PR c++/113563 + * lambda.cc (build_capture_proxy): Check pointerness of the + member, not the proxy type. + (lambda_expr_this_capture): Don't assume current_class_ref. + (nonlambda_method_basetype): Likewise. + * semantics.cc (finish_non_static_data_member): Don't assume + TREE_TYPE (object) is set. + (finish_this_expr): Check current_class_type for lambda, + not current_class_ref. + +2025-05-29 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/109283 + * coroutines.cc (find_any_await): Only save the statement + pointer if the caller passes a place for it. + (flatten_await_stmt): When checking that ternary expressions + have been handled, also check that they contain a co_await. + +2025-05-29 Jason Merrill <jason@redhat.com> + + * decl.cc (start_decl): Also set invalid_constexpr + for maybe_constexpr_fn. + * parser.cc (cp_parser_jump_statement): Likewise. + * constexpr.cc (potential_constant_expression_1): Ignore + goto to an artificial label. + +2025-05-29 Sandra Loosemore <sloosemore@baylibre.com> + + * parser.cc (cp_parser_omp_metadirective): Do not call + cp_parser_skip_to_end_of_block_or_statement after an error. + +2025-05-29 Sandra Loosemore <sloosemore@baylibre.com> + + PR c/120180 + * parser.cc (cp_parser_omp_metadirective): Only consume the + token if it is the expected close paren. + +2025-05-29 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (analyze_fn_parms): No longer + create a parameter copy guard var. + * coroutines.h (struct param_info): Remove the + entry for the parameter copy destructor guard. + +2025-05-29 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/120453 + * cp-tree.h (DECL_RAMP_P): New. + * typeck.cc (check_return_expr): Use DECL_RAMP_P instead + of DECL_RAMP_FN. + +2025-05-29 Jason Merrill <jason@redhat.com> + + PR c++/107600 + * cp-trait.def (IS_DESTRUCTIBLE, IS_NOTHROW_DESTRUCTIBLE) + (IS_TRIVIALLY_DESTRUCTIBLE): New. + * constraint.cc (diagnose_trait_expr): Explain them. + * method.cc (destructible_expr): New. + (is_xible_helper): Use it. + * semantics.cc (finish_trait_expr): Handle new traits. + (trait_expr_value): Likewise. Complain about asking + whether a deleted dtor is trivial. + +2025-05-28 Jason Merrill <jason@redhat.com> + + * module.cc (module_state::write_namespaces): Write + using-directives. + (module_state::read_namespaces): And read them. + * name-lookup.cc (add_using_namespace): Add overload. Build a + USING_DECL for modules. + (name_lookup::search_usings, name_lookup::queue_usings) + (using_directives_contain_std_p): Strip the USING_DECL. + * name-lookup.h: Declare it. + * parser.cc (cp_parser_import_declaration): Set MK_EXPORTING + for export import. + +2025-05-27 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc (cp_coroutine_transform::build_ramp_function): + Replace TRUTH_AND_EXPR with TRUTH_ANDIF_EXPR in three places. + +2025-05-26 Tobias Burnus <tburnus@baylibre.com> + + PR c++/120413 + * semantics.cc (finish_omp_target_clauses_r): Handle + BIND_EXPR with empty BIND_EXPR_BLOCK. + +2025-05-26 Jason Merrill <jason@redhat.com> + + * cp-tree.h: Declare tinst_dump_id. + * cp-objcp-common.cc (cp_register_dumps): Set it. + * pt.cc (push_tinst_level_loc): Dump it. + (reopen_tinst_level): Here too. + (tinst_complete_p): New. + (instantiate_pending_templates): Don't reopen_tinst_level for + already-complete instantiations. + +2025-05-26 Jason Merrill <jason@redhat.com> + + * cp-tree.h (class cxx_dump_pretty_printer): New. + * error.cc (cxx_dump_pretty_printer): Ctor/dtor definitions. + +2025-05-25 Jason Merrill <jason@redhat.com> + + * error.cc (dump_template_bindings): Correct skipping of + redundant bindings. + +2025-05-23 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120363 + * decl2.cc (get_tls_init_fn): Set context as global_namespace. + (get_tls_wrapper_fn): Likewise. + +2025-05-23 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120414 + * module.cc (trees_in::tree_node): Allow reading a USING_DECL + when streaming tt_data_member. + +2025-05-23 Jason Merrill <jason@redhat.com> + + * mangle.cc (mangle_decl_string): Don't push_tinst_level. + +2025-05-22 Jason Merrill <jason@redhat.com> + + PR c++/120935 + * cp-gimplify.cc (cp_fold): Check always_inline. + +2025-05-21 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc + (cp_coroutine_transform::build_ramp_function): Replace ramp + cleanup try-catch block with eh-only cleanup statements. + +2025-05-21 Iain Sandoe <iain@sandoe.co.uk> + + * coroutines.cc + (cp_coroutine_transform::build_ramp_function): Use + decltype(auto) to determine the type of the temporary + get_return_object. + +2025-05-21 Iain Sandoe <iain@sandoe.co.uk> + + PR c++/119916 + * coroutines.cc + (cp_coroutine_transform::wrap_original_function_body): Do not + initialise initial_await_resume_called here... + (cp_coroutine_transform::build_ramp_function): ... but here. + When the coroutine is not void, initialize a GRO object from + promise.get_return_object(). Use this as the argument to the + return expression. Use a regular cleanup for the GRO, since + it is ramp-local. + +2025-05-20 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120349 + * module.cc (trees_out::core_bools): Always mark vtables as + DECL_EXTERNAL. + +2025-05-20 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120013 + * module.cc (trees_in::install_entity): Handle re-registering + the inner TYPE_DECL of a partial specialisation. + +2025-05-20 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120350 + * rtti.cc (get_tinfo_decl_direct): Mark TREE_ADDRESSABLE. + +2025-05-16 Ville Voutilainen <ville.voutilainen@gmail.com> + + * cp-gimplify.cc (cp_fold): Do the conversion unconditionally, even for same-type cases. + +2025-05-16 Iain Sandoe <iain@sandoe.co.uk> + + * typeck.cc (check_return_expr): Suppress conversions for NVRO + in coroutine ramp functions. + +2025-05-16 Iain Sandoe <iain@sandoe.co.uk> + + * decl.cc (poplevel): Set BLOCK_OUTER_CURLY_BRACE_P on the + body block for functions with no subblocks. + +2025-05-16 Nathaniel Shead <nathanieloshead@gmail.com> + + * module.cc (importer_interface): Adjust flags. + (get_importer_interface): Rename flags. + (trees_out::core_bools): Clean up special casing. + (trees_out::write_function_def): Rename flag. + +2025-05-15 Patrick Palka <ppalka@redhat.com> + + PR c++/120161 + * pt.cc (unify) <case RECORD_TYPE>: When comparing specializations + of a non-primary template, still perform a type comparison. + +2025-05-15 Jason Merrill <jason@redhat.com> + + * module.cc (trees_out::lang_decl_bools): Stream implicit_constexpr. + (trees_in::lang_decl_bools): Likewise. + (trees_in::is_matching_decl): Check it. + +2025-05-15 Jason Merrill <jason@redhat.com> + + PR c++/99599 + * pt.cc (conversion_may_instantiate_p): Make sure + classes are complete. + +2025-05-14 Ville Voutilainen <ville.voutilainen@gmail.com> + + * cp-gimplify.cc (cp_fold): Remove a remnant comment. + +2025-05-14 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120125 + * module.cc (trees_out::write_function_def): Only set + DECL_NOT_REALLY_EXTERN if the importer might need to emit it. + * optimize.cc (maybe_thunk_body): Don't assume 'fn' has a cgraph + node created. + +2025-05-14 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119864 + * decl2.cc (start_objects): Only use module initialized for + host. + (c_parse_final_cleanups): Don't always create an OMP offload + init function in modules. + +2025-05-14 Nathaniel Shead <nathanieloshead@gmail.com> + + * name-lookup.cc (lookup_imported_hidden_friend): Add back + lazy_load_pendings with comment. + +2025-05-14 Ville Voutilainen <ville.voutilainen@gmail.com> + + * cp-gimplify.cc (cp_fold): Add to_underlying. + +2025-05-14 Owen Avery <powerboat9.gamer@gmail.com> + Jason Merrill <jason@redhat.com> + + * method.cc (synthesized_method_walk): Check whether + -Wvirtual-move-assign is enabled at the location of a base + class's move assignment operator. + +2025-05-12 Jason Merrill <jason@redhat.com> + + PR c++/120012 + * class.cc (check_non_pod_aggregate): Check is_empty_class. + +2025-05-10 Jason Merrill <jason@redhat.com> + + PR c++/120204 + * pt.cc (tsubst_baselink): Always error if lookup fails. + +2025-05-09 Jason Merrill <jason@redhat.com> + + * decl2.cc (determine_visibility): Ignore args for friend templates. + +2025-05-09 Jason Merrill <jason@redhat.com> + + PR c++/99599 + PR c++/120185 + * class.cc (type_has_converting_constructor): Handle null parm. + * pt.cc (fn_type_unification): Skip early non-dep checking if + no concepts. + +2025-05-08 Jason Merrill <jason@redhat.com> + + PR c++/99599 + * cp-tree.h (type_has_converting_constructor): Declare. + * class.cc (type_has_converting_constructor): New. + * pt.cc (conversion_may_instantiate_p): Don't check completeness. + +2025-05-05 Simon Martin <simon@nasilyan.com> + + * cp-tree.h (parsing_default_capturing_generic_lambda_in_template): + Remove obsolete prototype. + +2025-05-02 Jason Merrill <jason@redhat.com> + + PR c++/115207 + * decl.cc (cp_finish_decl): Call layout_decl after CTAD. + +2025-05-02 Jason Merrill <jason@redhat.com> + + PR c++/120012 + * cp-tree.h (struct lang_type): Add non_aggregate_pod. + (CLASSTYPE_NON_AGGREGATE_POD): New. + * class.cc (check_bases_and_members): Set it. + (check_non_pod_aggregate): Diagnose it. + +2025-05-02 Jakub Jelinek <jakub@redhat.com> + + PR c++/117827 + * init.cc (build_vec_init): Push to *cleanup_flags clearing of rval + instead of setting of iterator to maxindex. + +2025-05-01 Patrick Palka <ppalka@redhat.com> + + * constexpr.cc (explain_invalid_constexpr_fn): In the + DECL_CONSTRUCTOR_P branch pass the non-genericized body to + require_potential_constant_expression. + +2025-05-01 Patrick Palka <ppalka@redhat.com> + + PR c++/119034 + PR c++/68942 + * pt.cc (tsubst_expr) <case CALL_EXPR>: Revert PR68942 fix. + * semantics.cc (finish_call_expr): Ensure the callee of an + ADL-enabled call is wrapped in an OVERLOAD. + +2025-05-01 Jason Merrill <jason@redhat.com> + + * Make-lang.in: Don't pass the full path to gperf. + * std-name-hint.h: Regenerate. + +2025-05-01 Jason Merrill <jason@redhat.com> + + PR c++/119162 + * constexpr.cc (find_deleted_heap_var): Remove. + (cxx_eval_call_expression): Don't call it. Don't set TREE_STATIC on + heap vars. + (cxx_eval_outermost_constant_expr): Don't mess with varpool. + +2025-04-30 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/120023 + * module.cc (depset::hash::find_dependencies): Also call + add_deduction_guides when walking one. + +2025-04-30 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119938 + * pt.cc (get_template_parm_object): When !check_init, add assert + that expr really is constant and mark decl as such. + +2025-04-30 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119551 + PR c++/119996 + * module.cc (depset::hash::make_dependency): Also mark inline + variables referencing TU-local values as exposures here. + (depset::hash::finalize_dependencies): Add error message for + inline variables. + +2025-04-30 Patrick Palka <ppalka@redhat.com> + + PR c++/119981 + PR c++/119378 + * pt.cc (tsubst) <case UNBOUND_CLASS_TEMPLATE>: Substitute + into template parameter list first. When substituting the + context, only set processing_template_decl if there's more + than one level of introduced template parameters. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * parser.cc: Include "analyzer/analyzer-language.h". + (ana::cp_translation_unit): New class. + (cp_parser_translation_unit): Add call to + ana::on_finish_translation_unit. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * cxx-pretty-print.cc: Drop include of "make-unique.h". + Replace uses of ::make_unique with std::make_unique. + * error.cc: Likewise. + * name-lookup.cc: Likewise. + * parser.cc: Likewise. + +2025-04-28 David Malcolm <dmalcolm@redhat.com> + + * name-lookup.cc: Include "make-unique.h". + (namespace_hints::convert_candidates_to_name_hint): Use + ::make_unique rather than "new" when making + show_candidate_location and suggest_alternatives. + (namespace_hints::maybe_decorate_with_limit): Likewise when making + namespace_limit_reached. + (suggest_alternatives_for_1): Likewise when making + suggest_missing_option. + (maybe_suggest_missing_std_header): Likewise when making + missing_std_header. + (macro_use_before_def::maybe_make): Use std::unique_ptr. + (macro_use_before_def::macro_use_before_def): Make public. + (lookup_name_fuzzy): Use ::make_unique rather than "new" when + making suggest_missing_header. + * parser.cc: Include "make-unique.h". + (cp_parser_error_1): Use ::make_unique rather than "new" when + making suggest_missing_header. + +2025-04-27 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119939 + * module.cc (trees_out::lang_decl_vals): Also stream + lang->u.fn.context when DECL_UNIQUE_FRIEND_P. + (trees_in::lang_decl_vals): Likewise. + +2025-04-27 H.J. Lu <hjl.tools@gmail.com> + + PR middle-end/112877 + * call.cc (type_passed_as): Remove the + targetm.calls.promote_prototypes call. + (convert_for_arg_passing): Likewise. + * typeck.cc (cxx_safe_arg_type_equiv_p): Likewise. + +2025-04-25 Jason Merrill <jason@redhat.com> + + PR c++/119764 + PR c++/87185 + * lambda.cc (insert_capture_proxy): Handle noexcept lambda. + (prune_lambda_captures): Likewise, in ABI v21. + +2025-04-25 Jason Merrill <jason@redhat.com> + + * cp-tree.h (struct tinst_level): Add had_errors bit. + * pt.cc (push_tinst_level_loc): Clear it. + (pop_tinst_level): Set it. + (reopen_tinst_level): Check it. + (instantiate_pending_templates): Call limit_bad_template_recursion. + +2025-04-24 Jason Merrill <jason@redhat.com> + + PR c++/116954 + * contracts.cc (remove_contract_attributes): Return early if + not enabled. + +2025-04-22 Nathaniel Shead <nathanieloshead@gmail.com> + + * name-lookup.cc (lookup_imported_hidden_friend): Remove + unnecessary lazy_load_pendings. + +2025-04-22 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119863 + * name-lookup.cc (get_mergeable_namespace_binding): Remove + no-longer-used function. + (lookup_imported_hidden_friend): Also look for hidden imported + decls in an attached decl's module. + +2025-04-21 Jason Merrill <jason@redhat.com> + + * constexpr.cc (cxx_eval_outermost_constant_expr): Move + verify_constant later. + +2025-04-21 Jason Merrill <jason@redhat.com> + + PR c++/118775 + * constexpr.cc (cxx_eval_call_expression): Add assert. + (fold_to_constant): Handle processing_template_decl. + * init.cc (build_new_1): Use fold_to_constant. + +2025-04-21 Jason Merrill <jason@redhat.com> + + PR c++/99456 + * constexpr.cc (cxx_eval_constant_expression): Check strict + instead of manifestly_const_eval. + (maybe_constant_init_1): Be strict for static constexpr vars. + +2025-04-19 Jason Merrill <jason@redhat.com> + + * coroutines.cc (coro_build_expr_stmt) + (coro_build_cvt_void_expr_stmt): Remove. + (build_actor_fn): Use finish_expr_stmt. + * semantics.cc (finish_expr_stmt): Avoid wrapping statement in + EXPR_STMT. + (finish_stmt_expr_expr): Add comment. + +2025-04-17 Jason Merrill <jason@redhat.com> + + * constexpr.cc (is_valid_constexpr_fn): Improve diagnostic. + +2025-04-17 Jason Merrill <jason@redhat.com> + + * constexpr.cc (cxx_eval_outermost_constant_expr): Give both + expression and allocation location in allocated storage diagnostics. + +2025-04-17 Jason Merrill <jason@redhat.com> + + * name-lookup.cc (name_lookup::preserve_state): Fix reserve call. + * rtti.cc (get_tinfo_desc): Use vec_safe_grow_cleared. + +2025-04-17 Jason Merrill <jason@redhat.com> + + * semantics.cc (finish_type_pack_element): Add more info + to diagnostics. + +2025-04-17 Jason Merrill <jason@redhat.com> + + * decl.cc (cp_make_fname_decl): Prevent silent failure. + +2025-04-17 Jason Merrill <jason@redhat.com> + + * lex.cc (unqualified_name_lookup_error): Handle 'requires' better. + +2025-04-17 Jason Merrill <jason@redhat.com> + + PR c++/113360 + * cp-tree.h (struct language_function): Add erroneous bit. + * constexpr.cc (explain_invalid_constexpr_fn): Return if set. + (cxx_eval_call_expression): Quiet if set. + * parser.cc (cp_parser_function_definition_after_declarator) + * pt.cc (instantiate_body): Set it. + +2025-04-16 Jason Merrill <jason@redhat.com> + + PR c++/114772 + PR c++/101180 + * pt.cc (apply_late_template_attributes): Also override + target_option_current_node. + +2025-04-16 Jason Merrill <jason@redhat.com> + + PR c++/116954 + * contracts.cc (remove_contract_attributes): Preserve flags + on the attribute list. + +2025-04-15 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119755 + * lambda.cc (prune_lambda_captures): Remove pruned capture from + function's BLOCK_VARS and BIND_EXPR_VARS. + +2025-04-15 Jason Merrill <jason@redhat.com> + + PR c++/111075 + * constexpr.cc (cxx_eval_call_expression): Allow trivial + call from a thunk. + +2025-04-15 Patrick Palka <ppalka@redhat.com> + + PR c++/119807 + PR c++/112288 + * pt.cc (tsubst_friend_function): Skip remapping an + existing specialization if it doesn't match the shape of + the new friend definition. + +2025-04-15 Jason Merrill <jason@redhat.com> + + PR c++/113835 + * constexpr.cc (cxx_eval_outermost_constant_expr): Bail out early + for std::vector(N). + +2025-04-14 Patrick Palka <ppalka@redhat.com> + + PR c++/99214 + * constraint.cc (satisfy_declaration_constraints): Pass the + original ARGS to push_tinst_level. + +2025-04-13 Patrick Palka <ppalka@redhat.com> + + PR c++/115639 + * constexpr.cc (struct constexpr_call): Add NSDMIs to each + field. Replace 'result' data member with 3-element 'results' + array and a 'result' accessor function. Remove + 'manifestly_const_eval' data member. + (constexpr_call_hasher::equal): Adjust after constexpr_call + layout change. + (cxx_eval_call_expression): Likewise. Define some local + variables closer to their first use. Use unknown_type_node + instead of NULL_TREE as the "in progress" result. After + successully evaluating a call with mce_unknown, also cache the + result in the corresponding mce_true and mce_false slots. + +2025-04-13 Nathaniel Shead <nathanieloshead@gmail.com> + + * module.cc (trees_in::is_matching_decl): Don't check for + mismatches when importing a DECL_MAYBE_DELETED function over one + that's already finished. + +2025-04-13 Nathaniel Shead <nathanieloshead@gmail.com> + + * module.cc (trees_in::is_matching_decl): Add custom errors for + different kinds of mismatches. + +2025-04-12 Patrick Palka <ppalka@redhat.com> + + PR c++/116416 + * constexpr.cc (maybe_constant_init_1): Generalize type of + of manifestly_const_eval parameter from bool to mce_value. + (maybe_constant_init): Define 3-parameter version taking a + manifestly_const_eval instead of bool parameter. + (cxx_constant_init): Adjust. + * cp-gimplify.cc (cp_fold_r) <case TARGET_EXPR>: Pass mce_false + to maybe_constant_init during prvalue folding if ff_mce_false is + set. + * cp-tree.h (maybe_constant_init): Declare new overload. + +2025-04-11 Jason Merrill <jason@redhat.com> + + PR c++/114970 + * cp-gimplify.cc (cp_build_init_expr_for_ctor): Suppress warnings on + return_this COMPOUND_EXPR. + +2025-04-10 Jason Merrill <jason@redhat.com> + + PR c++/119345 + * pt.cc (add_extra_args): Also register a specialization + of the captured variable. + +2025-04-10 Patrick Palka <ppalka@redhat.com> + + PR c++/119687 + * pt.cc (alias_ctad_tweaks): Use lkp_range / lkp_iterator + instead of ovl_iterator. + +2025-04-10 Jakub Jelinek <jakub@redhat.com> + + PR translation/119684 + * error.cc (cp_print_error_function): Use G_ instead of _ for + pp_printf arguments. + (function_category): Use G_ instead of _. + (print_instantiation_full_context): Use G_ instead of _ in pp_verbatim + arguments. + (print_location): Likewise. + (print_instantiation_partial_context): Likewise. + (maybe_print_constexpr_context): Likewise. + (print_constrained_decl_info): Use G_() around pp_verbatim argument. + (print_concept_check_info): Likewise. + (print_constraint_context_head): Likewise. + (print_requires_expression_info): Likewise. Merge separate pp_verbatim + "in requirements " and "with " into one with conditional messages. + +2025-04-10 Jason Merrill <jason@redhat.com> + + PR c++/119175 + * mangle.cc (decl_mangling_context): Look through lambda type. + +2025-04-09 Patrick Palka <ppalka@redhat.com> + + PR c++/119574 + * pt.cc (add_extra_args): Remove checking assert. + +2025-04-09 Jason Merrill <jason@redhat.com> + + PR c++/118698 + * constraint.cc (struct norm_info): Add tf_partial. + * pt.cc (any_template_parm_r): Handle LAMBDA_EXPR_EXTRA_ARGS. + +2025-04-08 Jason Merrill <jason@redhat.com> + + PR c++/117530 + * pt.cc (instantiate_template): Check retrieve_specialization after + tsubst. + +2025-04-07 Jason Merrill <jason@redhat.com> + + PR c++/119652 + * constexpr.cc (cxx_eval_outermost_constant_expr): Also don't add a + TARGET_EXPR around AGGR_INIT_EXPR. + +2025-04-06 Patrick Palka <ppalka@redhat.com> + + PR c++/118626 + * pt.cc (maybe_dependent_member_ref): Restrict TYPENAME_TYPE + shortcut to non-typedef TYPE_DECL. + +2025-04-06 Patrick Palka <ppalka@redhat.com> + + PR c++/118626 + * pt.cc (maybe_dependent_member_ref): Substitute and return the + stripped type if we decided to not rewrite it directly. + +2025-04-05 Patrick Palka <ppalka@redhat.com> + + PR c++/118249 + * constexpr.cc (potential_constant_expression_1) + <case INDIRECT_REF>: Remove obsolete *this handling. + +2025-04-05 Jason Merrill <jason@redhat.com> + + PR c++/118629 + * name-lookup.cc (pushdecl_outermost_localscope): Look for an + sk_block. + +2025-04-04 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119564 + * decl.cc (cp_tree_node_structure): Add TU_LOCAL_ENTITY; fix + formatting. + +2025-04-04 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119608 + * module.cc (trees_out::decl_node): Maybe require by-value + walking not just when streaming. + +2025-04-04 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119462 + * module.cc (trees_in::is_matching_decl): Propagate exception + spec and constexpr to DECL_MAYBE_DELETED; clear if appropriate. + +2025-04-04 Jason Merrill <jason@redhat.com> + + PR c++/99546 + PR c++/113925 + PR c++/106976 + PR c++/109961 + PR c++/117336 + * lambda.cc (build_lambda_object): Handle fake + requires-expr processing_template_decl. + * parser.cc (cp_parser_lambda_expression): Likewise. + +2025-04-04 Patrick Palka <ppalka@redhat.com> + + PR c++/117849 + * semantics.cc (finish_id_expression_1): Allow use of constraint + variable outside an unevaluated context. + +2025-04-03 Patrick Palka <ppalka@redhat.com> + + PR c++/119387 + * constexpr.cc (p2280_active_p): New. + (cxx_eval_constant_expression) <case VAR_DECL>: Use it to + restrict P2280 relaxations. + <case PARM_DECL>: Likewise. + +2025-04-03 Jason Merrill <jason@redhat.com> + + * module.cc (module_state::read_cluster) + (post_load_processing): Clear DECL_EXTERNAL if DECL_COMDAT. + +2025-04-03 Jason Merrill <jason@redhat.com> + + * call.cc (add_candidates): Re-lookup ne_fns if we move into + another namespace. + +2025-04-03 Andrew Pinski <quic_apinski@quicinc.com> + Jakub Jelinek <jakub@redhat.com> + + PR c++/119563 + * call.cc (build_list_conv): Fix a typo in loop gathering + summary information from subsubconvs. + +2025-04-02 Sandra Loosemore <sloosemore@baylibre.com> + + PR middle-end/118965 + * parser.cc (c_parser_omp_clause_init_modifiers): Adjust + error message. + (cp_parser_omp_clause_init): Remove code for recognizing clauses + without modifiers. Diagnose missing target/targetsync modifier. + (cp_finish_omp_declare_variant): Diagnose missing target/targetsync + modifier. + +2025-04-01 Nathaniel Shead <nathanieloshead@gmail.com> + + PR c++/119551 + * module.cc (trees_out::write_var_def): Only ignore non-inline + variable initializers. + +2025-04-01 Nathaniel Shead <nathanieloshead@gmail.com> + + * parser.cc (cp_parser_diagnose_invalid_type_name): Replace + fmodules-ts with fmodules. + (cp_parser_template_declaration): Likewise. + +2025-04-01 Marek Polacek <polacek@redhat.com> + + PR c++/119383 + * call.cc (build_over_call): Use force_lvalue to ensure op= returns + an lvalue. + * cp-tree.h (force_lvalue): Declare. + * cvt.cc (force_lvalue): New. + * typeck.cc (cp_build_indirect_ref_1): Revert r15-8011. + 2025-03-31 Jason Merrill <jason@redhat.com> PR c++/119401 diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index 25143c8..dae3c68 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -168,8 +168,8 @@ $(srcdir)/cp/std-name-hint.h: $(srcdir)/cp/std-name-hint.gperf else $(srcdir)/cp/std-name-hint.h: | $(srcdir)/cp/std-name-hint.gperf endif - gperf -o -C -E -k '1,2,7,11,$$' -D -N find -L C++ \ - $(srcdir)/cp/std-name-hint.gperf --output-file $(srcdir)/cp/std-name-hint.h + cd $(srcdir)/cp; gperf -o -C -E -k '1,2,7,11,$$' -D -N find -L C++ \ + std-name-hint.gperf --output-file std-name-hint.h # The std-name-hint.gperf file itself is generated from a general # C++ API description. diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index b1469cb..2629625 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -917,7 +917,7 @@ build_list_conv (tree type, tree ctor, int flags, tsubst_flags_t complain) t->rank = cr_exact; for (j = 0; j < (unsigned) RAW_DATA_LENGTH (val); ++j) { - sub = subsubconvs[i]; + sub = subsubconvs[j]; if (sub->rank > t->rank) t->rank = sub->rank; if (sub->user_conv_p) @@ -1723,6 +1723,56 @@ involves_qualification_conversion_p (tree to, tree from) return false; } +/* Return true if HANDLER is a match for exception object with EXCEPT_TYPE as + per [except.handle]/3. */ + +bool +handler_match_for_exception_type (tree handler, tree except_type) +{ + tree handler_type = HANDLER_TYPE (handler); + if (handler_type == NULL_TREE) + return true; /* ... */ + if (same_type_ignoring_top_level_qualifiers_p (handler_type, except_type)) + return true; + if (CLASS_TYPE_P (except_type) && CLASS_TYPE_P (handler_type)) + { + base_kind b_kind; + tree binfo = lookup_base (except_type, handler_type, ba_check, &b_kind, + tf_none); + if (binfo && binfo != error_mark_node) + return true; + } + if (TYPE_PTR_P (handler_type) || TYPE_PTRDATAMEM_P (handler_type)) + { + if (TREE_CODE (except_type) == NULLPTR_TYPE) + return true; + if ((TYPE_PTR_P (handler_type) && TYPE_PTR_P (except_type)) + || (TYPE_PTRDATAMEM_P (handler_type) + && TYPE_PTRDATAMEM_P (except_type))) + { + conversion *conv + = standard_conversion (handler_type, except_type, NULL_TREE, + /*c_cast_p=*/false, 0, tf_none); + if (conv && !conv->bad_p) + { + for (conversion *t = conv; t; t = next_conversion (t)) + switch (t->kind) + { + case ck_ptr: + case ck_fnptr: + case ck_qual: + case ck_identity: + break; + default: + return false; + } + return true; + } + } + } + return false; +} + /* A reference of the indicated TYPE is being bound directly to the expression represented by the implicit conversion sequence CONV. Return a conversion sequence for this binding. */ @@ -6673,6 +6723,7 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, bool check_list_ctor = false; bool check_converting = false; unification_kind_t strict; + tree ne_context = NULL_TREE; tree ne_fns = NULL_TREE; if (!fns) @@ -6719,6 +6770,7 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, tree ne_name = ovl_op_identifier (false, NE_EXPR); if (DECL_CLASS_SCOPE_P (fn)) { + ne_context = DECL_CONTEXT (fn); ne_fns = lookup_fnfields (TREE_TYPE ((*args)[0]), ne_name, 1, tf_none); if (ne_fns == error_mark_node || ne_fns == NULL_TREE) @@ -6728,8 +6780,9 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, } else { - tree context = decl_namespace_context (fn); - ne_fns = lookup_qualified_name (context, ne_name, LOOK_want::NORMAL, + ne_context = decl_namespace_context (fn); + ne_fns = lookup_qualified_name (ne_context, ne_name, + LOOK_want::NORMAL, /*complain*/false); if (ne_fns == error_mark_node || !is_overloaded_fn (ne_fns)) @@ -6828,8 +6881,26 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args, /* When considering reversed operator==, if there's a corresponding operator!= in the same scope, it's not a rewrite target. */ - if (ne_fns) + if (ne_context) { + if (TREE_CODE (ne_context) == NAMESPACE_DECL) + { + /* With argument-dependent lookup, fns can span multiple + namespaces; make sure we look in the fn's namespace for a + corresponding operator!=. */ + tree fn_ns = decl_namespace_context (fn); + if (fn_ns != ne_context) + { + ne_context = fn_ns; + tree ne_name = ovl_op_identifier (false, NE_EXPR); + ne_fns = lookup_qualified_name (ne_context, ne_name, + LOOK_want::NORMAL, + /*complain*/false); + if (ne_fns == error_mark_node + || !is_overloaded_fn (ne_fns)) + ne_fns = NULL_TREE; + } + } bool found = false; for (lkp_iterator ne (ne_fns); !found && ne; ++ne) if (0 && !ne.using_p () @@ -9686,11 +9757,6 @@ type_passed_as (tree type) /* Pass classes with copy ctors by invisible reference. */ if (TREE_ADDRESSABLE (type)) type = build_reference_type (type); - else if (targetm.calls.promote_prototypes (NULL_TREE) - && INTEGRAL_TYPE_P (type) - && COMPLETE_TYPE_P (type) - && tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node))) - type = integer_type_node; return type; } @@ -9726,11 +9792,6 @@ convert_for_arg_passing (tree type, tree val, tsubst_flags_t complain) /* Pass classes with copy ctors by invisible reference. */ else if (TREE_ADDRESSABLE (type)) val = build1 (ADDR_EXPR, build_reference_type (type), val); - else if (targetm.calls.promote_prototypes (NULL_TREE) - && INTEGRAL_TYPE_P (type) - && COMPLETE_TYPE_P (type) - && tree_int_cst_lt (TYPE_SIZE (type), TYPE_SIZE (integer_type_node))) - val = cp_perform_integral_promotions (val, complain); if (complain & tf_warning) maybe_warn_parm_abi (type, cp_expr_loc_or_input_loc (val)); diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index 2b694b9..151ee2b 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -347,9 +347,18 @@ build_base_path (enum tree_code code, || processing_template_decl || in_template_context); + fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull); + + /* Do we need to look in the vtable for the real offset? */ + virtual_access = (v_binfo && fixed_type_p <= 0); + /* For a non-pointer simple base reference, express it as a COMPONENT_REF without taking its address (and so causing lambda capture, 91933). */ - if (code == PLUS_EXPR && !v_binfo && !want_pointer && !has_empty && !uneval) + if (code == PLUS_EXPR + && !want_pointer + && !has_empty + && !uneval + && !virtual_access) return build_simple_base_path (expr, binfo); if (!want_pointer) @@ -362,7 +371,6 @@ build_base_path (enum tree_code code, expr = mark_rvalue_use (expr); offset = BINFO_OFFSET (binfo); - fixed_type_p = resolves_to_fixed_type_p (expr, &nonnull); target_type = code == PLUS_EXPR ? BINFO_TYPE (binfo) : BINFO_TYPE (d_binfo); /* TARGET_TYPE has been extracted from BINFO, and, is therefore always cv-unqualified. Extract the cv-qualifiers from EXPR so that the @@ -371,9 +379,6 @@ build_base_path (enum tree_code code, (target_type, cp_type_quals (TREE_TYPE (TREE_TYPE (expr)))); ptr_target_type = build_pointer_type (target_type); - /* Do we need to look in the vtable for the real offset? */ - virtual_access = (v_binfo && fixed_type_p <= 0); - /* Don't bother with the calculations inside sizeof; they'll ICE if the source type is incomplete and the pointer value doesn't matter. In a template (even in instantiate_non_dependent_expr), we don't have vtables @@ -5724,6 +5729,50 @@ type_has_user_provided_constructor (tree t) return false; } +/* Returns true iff class T has a constructor that accepts a single argument + and does not have a single parameter of type reference to T. + + This does not exclude explicit constructors because they are still + considered for conversions within { } even though choosing one is + ill-formed. */ + +bool +type_has_converting_constructor (tree t) +{ + if (!CLASS_TYPE_P (t)) + return false; + + if (!TYPE_HAS_USER_CONSTRUCTOR (t)) + return false; + + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (t)); iter; ++iter) + { + tree fn = *iter; + tree parm = FUNCTION_FIRST_USER_PARMTYPE (fn); + if (parm == NULL_TREE) + /* Varargs. */ + return true; + if (parm == void_list_node + || !sufficient_parms_p (TREE_CHAIN (parm))) + /* Can't accept a single argument, so won't be considered for + conversion. */ + continue; + if (TREE_CODE (fn) == TEMPLATE_DECL + || TREE_CHAIN (parm) != void_list_node) + /* Not a simple single parameter. */ + return true; + if (TYPE_MAIN_VARIANT (non_reference (TREE_VALUE (parm))) + != DECL_CONTEXT (fn)) + /* The single parameter has the wrong type. */ + return true; + if (get_constraints (fn)) + /* Constrained. */ + return true; + } + + return false; +} + /* Returns true iff class T has a user-provided or explicit constructor. */ bool @@ -6413,9 +6462,7 @@ check_bases_and_members (tree t) Again, other conditions for being an aggregate are checked elsewhere. */ CLASSTYPE_NON_AGGREGATE (t) - |= ((cxx_dialect < cxx20 - ? type_has_user_provided_or_explicit_constructor (t) - : TYPE_HAS_USER_CONSTRUCTOR (t)) + |= (type_has_user_provided_or_explicit_constructor (t) || TYPE_POLYMORPHIC_P (t)); /* This is the C++98/03 definition of POD; it changed in C++0x, but we retain the old definition internally for ABI reasons. */ @@ -6437,6 +6484,20 @@ check_bases_and_members (tree t) CLASSTYPE_NON_LAYOUT_POD_P (t) = true; } + /* P1008: Prohibit aggregates with user-declared constructors. */ + if (cxx_dialect >= cxx20 && TYPE_HAS_USER_CONSTRUCTOR (t)) + { + CLASSTYPE_NON_AGGREGATE (t) = true; + if (!CLASSTYPE_NON_LAYOUT_POD_P (t)) + { + /* c++/120012: The C++20 aggregate change affected layout. */ + if (!abi_version_at_least (21)) + CLASSTYPE_NON_LAYOUT_POD_P (t) = true; + if (abi_version_crosses (21)) + CLASSTYPE_NON_AGGREGATE_POD (t) = true; + } + } + /* If the only explicitly declared default constructor is user-provided, set TYPE_HAS_COMPLEX_DFLT. */ if (!TYPE_HAS_COMPLEX_DFLT (t) @@ -6698,9 +6759,11 @@ layout_virtual_bases (record_layout_info rli, splay_tree offsets) { /* This virtual base is not a primary base of any class in the hierarchy, so we have to add space for it. */ - next_field = build_base_field (rli, vbase, - access_private_node, - offsets, next_field); + tree access = access_private_node; + if (publicly_virtually_derived_p (BINFO_TYPE (vbase), t)) + access = access_public_node; + next_field = build_base_field (rli, vbase, access, offsets, + next_field); } } } @@ -6809,7 +6872,8 @@ end_of_class (tree t, eoc_mode mode) static void check_non_pod_aggregate (tree field) { - if (!abi_version_crosses (17) || cxx_dialect < cxx14) + if ((!abi_version_crosses (17) || cxx_dialect < cxx14) + && (!abi_version_crosses (21) || cxx_dialect < cxx20)) return; if (TREE_CODE (field) != FIELD_DECL || (!DECL_FIELD_IS_BASE (field) @@ -6822,7 +6886,10 @@ check_non_pod_aggregate (tree field) tree type = TREE_TYPE (field); if (TYPE_IDENTIFIER (type) == as_base_identifier) type = TYPE_CONTEXT (type); - if (!CLASS_TYPE_P (type) || !CLASSTYPE_NON_POD_AGGREGATE (type)) + if (!CLASS_TYPE_P (type) + || is_empty_class (type) + || (!CLASSTYPE_NON_POD_AGGREGATE (type) + && !CLASSTYPE_NON_AGGREGATE_POD (type))) return; tree size = end_of_class (type, (DECL_FIELD_IS_BASE (field) ? eoc_nvsize : eoc_nv_or_dsize)); @@ -6831,13 +6898,31 @@ check_non_pod_aggregate (tree field) { location_t loc = DECL_SOURCE_LOCATION (next); if (DECL_FIELD_IS_BASE (next)) - warning_at (loc, OPT_Wabi,"offset of %qT base class for " - "%<-std=c++14%> and up changes in " - "%<-fabi-version=17%> (GCC 12)", TREE_TYPE (next)); + { + if (abi_version_crosses (17) + && CLASSTYPE_NON_POD_AGGREGATE (type)) + warning_at (loc, OPT_Wabi,"offset of %qT base class for " + "%<-std=c++14%> and up changes in " + "%<-fabi-version=17%> (GCC 12)", TREE_TYPE (next)); + else if (abi_version_crosses (21) + && CLASSTYPE_NON_AGGREGATE_POD (type)) + warning_at (loc, OPT_Wabi,"offset of %qT base class for " + "%<-std=c++20%> and up changes in " + "%<-fabi-version=21%> (GCC 16)", TREE_TYPE (next)); + } else - warning_at (loc, OPT_Wabi, "offset of %qD for " - "%<-std=c++14%> and up changes in " - "%<-fabi-version=17%> (GCC 12)", next); + { + if (abi_version_crosses (17) + && CLASSTYPE_NON_POD_AGGREGATE (type)) + warning_at (loc, OPT_Wabi, "offset of %qD for " + "%<-std=c++14%> and up changes in " + "%<-fabi-version=17%> (GCC 12)", next); + else if (abi_version_crosses (21) + && CLASSTYPE_NON_AGGREGATE_POD (type)) + warning_at (loc, OPT_Wabi, "offset of %qD for " + "%<-std=c++20%> and up changes in " + "%<-fabi-version=21%> (GCC 16)", next); + } } } @@ -7367,7 +7452,7 @@ determine_key_method (tree type) && ! DECL_DECLARED_INLINE_P (method) && ! DECL_PURE_VIRTUAL_P (method)) { - CLASSTYPE_KEY_METHOD (type) = method; + SET_CLASSTYPE_KEY_METHOD (type, method); break; } @@ -7842,6 +7927,17 @@ finish_struct_1 (tree t) return; } + if (location_t fcloc = failed_completion_location (t)) + { + auto_diagnostic_group adg; + if (warning (OPT_Wsfinae_incomplete_, + "defining %qT, which previously failed to be complete " + "in a SFINAE context", t) + && warn_sfinae_incomplete == 1) + inform (fcloc, "here. Use %qs for a diagnostic at that point", + "-Wsfinae-incomplete=2"); + } + /* If this type was previously laid out as a forward reference, make sure we lay it out again. */ TYPE_SIZE (t) = NULL_TREE; @@ -8261,6 +8357,15 @@ fixed_type_or_null (tree instance, int *nonnull, int *cdtorp) *nonnull = 1; return TREE_TYPE (instance); } + if (CLASS_TYPE_P (TREE_TYPE (instance))) + { + /* We missed a build_cplus_new somewhere, likely due to tf_decltype + mishandling. */ + gcc_checking_assert (false); + if (nonnull) + *nonnull = 1; + return TREE_TYPE (instance); + } return NULL_TREE; case SAVE_EXPR: @@ -10531,7 +10636,7 @@ build_vtbl_initializer (tree binfo, int i; if (init == size_zero_node) for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) - CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); else for (i = 0; i < TARGET_VTABLE_USES_DESCRIPTORS; ++i) { @@ -10539,11 +10644,11 @@ build_vtbl_initializer (tree binfo, fn, build_int_cst (NULL_TREE, i)); TREE_CONSTANT (fdesc) = 1; - CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), fdesc); + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, fdesc); } } else - CONSTRUCTOR_APPEND_ELT (*inits, size_int (jx++), init); + CONSTRUCTOR_APPEND_ELT (*inits, NULL_TREE, init); } } @@ -10882,6 +10987,17 @@ publicly_uniquely_derived_p (tree parent, tree type) return base && base != error_mark_node; } +/* TRUE iff TYPE is publicly & virtually derived from PARENT. */ + +bool +publicly_virtually_derived_p (tree parent, tree type) +{ + tree base = lookup_base (type, parent, + ba_ignore_scope | ba_check | ba_require_virtual, + NULL, tf_none); + return base && base != error_mark_node; +} + /* CTX1 and CTX2 are declaration contexts. Return the innermost common class between them, if any. */ diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index 4820bcc..eb19784 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -303,11 +303,20 @@ is_valid_constexpr_fn (tree fun, bool complain) } } } - else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun))) + else if (CLASSTYPE_VBASECLASSES (DECL_CONTEXT (fun)) && cxx_dialect < cxx26) { ret = false; if (complain) - error ("%q#T has virtual base classes", DECL_CONTEXT (fun)); + { + if (DECL_CONSTRUCTOR_P (fun)) + error ("%<constexpr%> constructor in %q#T that has " + "virtual base classes only available with " + "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun)); + else + error ("%<constexpr%> destructor in %q#T that has " + "virtual base classes only available with " + "%<-std=c++2c%> or %<-std=gnu++2c%>", DECL_CONTEXT (fun)); + } } return ret; @@ -1048,6 +1057,12 @@ explain_invalid_constexpr_fn (tree fun) { static hash_set<tree> *diagnosed; tree body; + + /* Don't try to explain a function we already complained about. */ + if (function *f = DECL_STRUCT_FUNCTION (fun)) + if (f->language->erroneous) + return; + /* In C++23, a function marked 'constexpr' may not actually be a constant expression. We haven't diagnosed the problem yet: -Winvalid-constexpr wasn't enabled. The function was called, so diagnose why it cannot be @@ -1097,17 +1112,14 @@ explain_invalid_constexpr_fn (tree fun) body = fd->body; else body = DECL_SAVED_TREE (fun); - body = massage_constexpr_body (fun, body); - require_potential_rvalue_constant_expression (body); + tree massaged = massage_constexpr_body (fun, body); + require_potential_rvalue_constant_expression (massaged); if (DECL_CONSTRUCTOR_P (fun)) { - cx_check_missing_mem_inits (DECL_CONTEXT (fun), body, true); + cx_check_missing_mem_inits (DECL_CONTEXT (fun), massaged, true); if (cxx_dialect > cxx11) - { - /* Also check the body, not just the ctor-initializer. */ - body = DECL_SAVED_TREE (fun); - require_potential_rvalue_constant_expression (body); - } + /* Also check the body, not just the ctor-initializer. */ + require_potential_rvalue_constant_expression (body); } } } @@ -1119,20 +1131,22 @@ explain_invalid_constexpr_fn (tree fun) struct GTY((for_user)) constexpr_call { /* Description of the constexpr function definition. */ - constexpr_fundef *fundef; + constexpr_fundef *fundef = nullptr; /* Parameter bindings environment. A TREE_VEC of arguments. */ - tree bindings; - /* Result of the call. - NULL means the call is being evaluated. + tree bindings = NULL_TREE; + /* Result of the call, indexed by the value of + constexpr_ctx::manifestly_const_eval. + unknown_type_node means the call is being evaluated. error_mark_node means that the evaluation was erroneous or otherwise uncacheable (e.g. because it depends on the caller). Otherwise, the actual value of the call. */ - tree result; + tree results[3] = { NULL_TREE, NULL_TREE, NULL_TREE }; /* The hash of this call; we remember it here to avoid having to recalculate it when expanding the hash table. */ - hashval_t hash; - /* The value of constexpr_ctx::manifestly_const_eval. */ - enum mce_value manifestly_const_eval; + hashval_t hash = 0; + + /* The result slot corresponding to the given mce_value. */ + tree& result (mce_value mce) { return results[1 + int(mce)]; } }; struct constexpr_call_hasher : ggc_ptr_hash<constexpr_call> @@ -1170,6 +1184,10 @@ public: /* Heap VAR_DECLs created during the evaluation of the outermost constant expression. */ auto_vec<tree, 16> heap_vars; + /* Vector of caught exceptions, including exceptions still not active at + the start of a handler (those are immediately followed up by HANDLER_TYPE + until __cxa_begin_catch finishes). */ + auto_vec<tree, 2> caught_exceptions; /* Cleanups that need to be evaluated at the end of CLEANUP_POINT_EXPR. */ vec<tree> *cleanups; /* If non-null, only allow modification of existing values of the variables @@ -1177,10 +1195,13 @@ public: hash_set<tree> *modifiable; /* Number of heap VAR_DECL deallocations. */ unsigned heap_dealloc_count; + /* Number of uncaught exceptions. */ + unsigned uncaught_exceptions; + /* Constructor. */ constexpr_global_ctx () : constexpr_ops_count (0), cleanups (NULL), modifiable (nullptr), - heap_dealloc_count (0) {} + heap_dealloc_count (0), uncaught_exceptions (0) {} bool is_outside_lifetime (tree t) { @@ -1294,6 +1315,64 @@ struct constexpr_ctx { mce_value manifestly_const_eval; }; +/* Predicates for the meaning of *jump_target. */ + +static bool +returns (tree *jump_target) +{ + return *jump_target && TREE_CODE (*jump_target) == RETURN_EXPR; +} + +static bool +breaks (tree *jump_target) +{ + return (*jump_target + && ((TREE_CODE (*jump_target) == LABEL_DECL + && LABEL_DECL_BREAK (*jump_target)) + || TREE_CODE (*jump_target) == BREAK_STMT + || TREE_CODE (*jump_target) == EXIT_EXPR)); +} + +static bool +continues (tree *jump_target) +{ + return (*jump_target + && ((TREE_CODE (*jump_target) == LABEL_DECL + && LABEL_DECL_CONTINUE (*jump_target)) + || TREE_CODE (*jump_target) == CONTINUE_STMT)); +} + +static bool +switches (tree *jump_target) +{ + return *jump_target && TREE_CODE (*jump_target) == INTEGER_CST; +} + +static bool +throws (tree *jump_target) +{ + /* void_node is for use in potential_constant_expression_1, otherwise + it should an artificial VAR_DECL created by constant evaluation + of __cxa_allocate_exception (). */ + return (*jump_target && (VAR_P (*jump_target) || *jump_target == void_node)); +} + +/* True if the constexpr relaxations afforded by P2280R4 for unknown + references and objects are in effect. */ + +static bool +p2280_active_p (const constexpr_ctx *ctx) +{ + if (ctx->manifestly_const_eval != mce_true) + /* Disable these relaxations during speculative constexpr folding, + as it can significantly increase compile time/memory use + (PR119387). */ + return false; + + /* P2280R4 was accepted as a DR against C++11. */ + return cxx_dialect >= cxx11; +} + /* Remove T from the global values map, checking for attempts to destroy a value that has already finished its lifetime. */ @@ -1411,8 +1490,6 @@ constexpr_call_hasher::equal (constexpr_call *lhs, constexpr_call *rhs) return true; if (lhs->hash != rhs->hash) return false; - if (lhs->manifestly_const_eval != rhs->manifestly_const_eval) - return false; if (!constexpr_fundef_hasher::equal (lhs->fundef, rhs->fundef)) return false; return cp_tree_equal (lhs->bindings, rhs->bindings); @@ -1515,13 +1592,671 @@ enum value_cat { }; static tree cxx_eval_constant_expression (const constexpr_ctx *, tree, - value_cat, bool *, bool *, tree * = NULL); + value_cat, bool *, bool *, tree *); static tree cxx_eval_bare_aggregate (const constexpr_ctx *, tree, - value_cat, bool *, bool *); + value_cat, bool *, bool *, tree *); static tree cxx_fold_indirect_ref (const constexpr_ctx *, location_t, tree, tree, - bool * = NULL); + bool *, tree *); static tree find_heap_var_refs (tree *, int *, void *); -static tree find_deleted_heap_var (tree *, int *, void *); + +/* For exception object EXC if it has class type and usable what () method + which returns cv char * return the xmalloced string literal which it returns + if possible, otherwise return NULL. */ + +static char * +exception_what_str (const constexpr_ctx *ctx, tree exc) +{ + tree type = strip_array_types (TREE_TYPE (exc)); + if (!CLASS_TYPE_P (type)) + return NULL; + tree std_exception = lookup_qualified_name (std_node, "exception", + LOOK_want::NORMAL, false); + if (TREE_CODE (std_exception) != TYPE_DECL) + return NULL; + if (!CLASS_TYPE_P (TREE_TYPE (std_exception))) + return NULL; + base_kind b_kind; + tree binfo = lookup_base (type, TREE_TYPE (std_exception), ba_check, &b_kind, + tf_none); + if (binfo == NULL_TREE || binfo == error_mark_node) + return NULL; + if (type != TREE_TYPE (exc)) + exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL); + tree call + = finish_class_member_access_expr (exc, get_identifier ("what"), false, + tf_none); + if (call == error_mark_node) + return NULL; + releasing_vec what_args; + call = finish_call_expr (call, &what_args, false, false, tf_none); + if (call == error_mark_node) + return NULL; + if (TREE_CODE (TREE_TYPE (call)) != POINTER_TYPE + || !INTEGRAL_TYPE_P (TREE_TYPE (TREE_TYPE (call))) + || !COMPLETE_TYPE_P (TREE_TYPE (TREE_TYPE (call))) + || !tree_int_cst_equal (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (call))), + TYPE_SIZE_UNIT (char_type_node)) + || TYPE_PRECISION (TREE_TYPE (TREE_TYPE (call))) != BITS_PER_UNIT) + return NULL; + if (!potential_constant_expression (call)) + return NULL; + bool non_constant_p = false, overflow_p = false; + tree jmp_target = NULL; + tree ptr = cxx_eval_constant_expression (ctx, call, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) || non_constant_p) + return NULL; + if (reduced_constant_expression_p (ptr)) + if (const char *msg = c_getstr (ptr)) + return xstrdup (msg); + auto_vec <char, 32> v; + for (unsigned i = 0; i < INT_MAX; ++i) + { + tree t = call; + if (i) + t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ptr), ptr, size_int (i)); + t = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (t)), t); + non_constant_p = false; + overflow_p = false; + jmp_target = NULL; + tree t2 = cxx_eval_constant_expression (ctx, t, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) + || non_constant_p + || !tree_fits_shwi_p (t2)) + return NULL; + char c = tree_to_shwi (t2); + v.safe_push (c); + if (c == '\0') + break; + } + return xstrdup (v.address ()); +} + +/* Diagnose constant expression evaluation encountering call to + std::terminate due to exception EXC. */ + +static void +diagnose_std_terminate (location_t loc, const constexpr_ctx *ctx, tree exc) +{ + tree type = strip_array_types (TREE_TYPE (exc)); + if (char *str = exception_what_str (ctx, exc)) + { + error_at (loc, "%qs called after throwing an exception of type %qT; " + "%<what()%>: %qs", "std::terminate", type, str); + free (str); + } + else + { + if (type != TREE_TYPE (exc)) + exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL); + bool non_constant_p = false, overflow_p = false; + tree jmp_target = NULL; + tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + gcc_assert (!throws (&jmp_target) && !non_constant_p); + if (reduced_constant_expression_p (val)) + error_at (loc, "%qs called after throwing an exception %qE", + "std::terminate", val); + else + error_at (loc, "%qs called after throwing an exception of type %qT", + "std::terminate", type); + } +} + +/* Diagnose constant expression evaluation encountering call to + uncaught exception EXC. */ + +static void +diagnose_uncaught_exception (location_t loc, const constexpr_ctx *ctx, tree exc) +{ + tree type = strip_array_types (TREE_TYPE (exc)); + if (char *str = exception_what_str (ctx, exc)) + { + error_at (loc, "uncaught exception of type %qT; %<what()%>: %qs", type, str); + free (str); + } + else + { + if (type != TREE_TYPE (exc)) + exc = build4 (ARRAY_REF, type, exc, size_zero_node, NULL, NULL); + bool non_constant_p = false, overflow_p = false; + tree jmp_target = NULL; + tree val = cxx_eval_constant_expression (ctx, exc, vc_prvalue, + &non_constant_p, &overflow_p, + &jmp_target); + gcc_assert (!throws (&jmp_target) && !non_constant_p); + if (reduced_constant_expression_p (val)) + error_at (loc, "uncaught exception %qE", val); + else + error_at (loc, "uncaught exception of type %qT", type); + } +} + +/* Kinds of __cxa_* functions (and a few other EH related ones) we handle as + magic constexpr functions for C++26. */ + +enum cxa_builtin { + CXA_NONE = 0, + CXA_ALLOCATE_EXCEPTION = 1, + CXA_FREE_EXCEPTION = 2, + CXA_THROW = 3, + CXA_BEGIN_CATCH = 4, + CXA_END_CATCH = 5, + CXA_RETHROW = 6, + CXA_GET_EXCEPTION_PTR = 7, + CXA_BAD_CAST = 8, + CXA_BAD_TYPEID = 9, + CXA_THROW_BAD_ARRAY_NEW_LENGTH = 10, + STD_UNCAUGHT_EXCEPTIONS = 11, + STD_CURRENT_EXCEPTION = 12, + STD_RETHROW_EXCEPTION = 13, + BUILTIN_EH_PTR_ADJUST_REF = 14 +}; + +/* Return cxa_builtin if FNDECL is a __cxa_* function handled as + magic constexpr function for C++26. Return CXA_NONE otherwise. */ + +static enum cxa_builtin +cxx_cxa_builtin_fn_p (tree fndecl) +{ + if (cxx_dialect < cxx26) + return CXA_NONE; + if (DECL_LANGUAGE (fndecl) != lang_c) + { + if (!decl_in_std_namespace_p (fndecl)) + return CXA_NONE; + if (id_equal (DECL_NAME (fndecl), "uncaught_exceptions")) + return STD_UNCAUGHT_EXCEPTIONS; + if (id_equal (DECL_NAME (fndecl), "current_exception")) + return STD_CURRENT_EXCEPTION; + if (id_equal (DECL_NAME (fndecl), "rethrow_exception")) + return STD_RETHROW_EXCEPTION; + return CXA_NONE; + } + if (!startswith (IDENTIFIER_POINTER (DECL_NAME (fndecl)), "__cxa_")) + return CXA_NONE; + if (id_equal (DECL_NAME (fndecl), "__cxa_allocate_exception")) + return CXA_ALLOCATE_EXCEPTION; + if (id_equal (DECL_NAME (fndecl), "__cxa_free_exception")) + return CXA_FREE_EXCEPTION; + if (id_equal (DECL_NAME (fndecl), "__cxa_throw")) + return CXA_THROW; + if (id_equal (DECL_NAME (fndecl), "__cxa_begin_catch")) + return CXA_BEGIN_CATCH; + if (id_equal (DECL_NAME (fndecl), "__cxa_end_catch")) + return CXA_END_CATCH; + if (id_equal (DECL_NAME (fndecl), "__cxa_rethrow")) + return CXA_RETHROW; + if (id_equal (DECL_NAME (fndecl), "__cxa_get_exception_ptr")) + return CXA_GET_EXCEPTION_PTR; + if (id_equal (DECL_NAME (fndecl), "__cxa_bad_cast")) + return CXA_BAD_CAST; + if (id_equal (DECL_NAME (fndecl), "__cxa_bad_typeid")) + return CXA_BAD_TYPEID; + if (id_equal (DECL_NAME (fndecl), "__cxa_throw_bad_array_new_length")) + return CXA_THROW_BAD_ARRAY_NEW_LENGTH; + return CXA_NONE; +} + +/* Helper function for cxx_eval_cxa_builtin_fn. + Check if ARG is a valid first argument of __cxa_throw or + __cxa_free_exception or __builtin_eh_ptr_adjust_ref. Return NULL_TREE if + not, otherwise return the artificial __cxa_allocate_exception allocated + VAR_DECL. FREE_EXC is true for __cxa_free_exception, false otherwise. */ + +static tree +cxa_check_throw_arg (tree arg, bool free_exc) +{ + STRIP_NOPS (arg); + if (TREE_CODE (arg) != ADDR_EXPR) + return NULL_TREE; + arg = TREE_OPERAND (arg, 0); + if (!VAR_P (arg) + || !DECL_ARTIFICIAL (arg) + || ((!free_exc || DECL_NAME (arg) != heap_uninit_identifier) + && DECL_NAME (arg) != heap_identifier) + || !DECL_LANG_SPECIFIC (arg)) + return NULL_TREE; + return arg; +} + +/* Helper function for cxx_eval_cxa_builtin_fn. + "Allocate" on the constexpr heap an exception object of TYPE + with REFCOUNT. */ + +static tree +cxa_allocate_exception (location_t loc, const constexpr_ctx *ctx, tree type, + tree refcount) +{ + tree var = build_decl (loc, VAR_DECL, heap_uninit_identifier, type); + DECL_ARTIFICIAL (var) = 1; + retrofit_lang_decl (var); + DECL_EXCEPTION_REFCOUNT (var) = refcount; + ctx->global->heap_vars.safe_push (var); + return var; +} + +/* Evaluate various __cxa_* calls as magic constexpr builtins for + C++26 constexpr exception support (P3068R5). */ + +static tree +cxx_eval_cxa_builtin_fn (const constexpr_ctx *ctx, tree call, + enum cxa_builtin kind, tree fndecl, + bool *non_constant_p, bool *overflow_p, + tree *jump_target) +{ + int nargs = call_expr_nargs (call); + location_t loc = cp_expr_loc_or_input_loc (call); + tree args[4], arg; + if (nargs > 4) + { + invalid_nargs: + if (!ctx->quiet) + error_at (loc, "call to %qD function with incorrect" + "number of arguments", fndecl); + *non_constant_p = true; + return call; + } + if ((kind == CXA_BEGIN_CATCH || kind == CXA_GET_EXCEPTION_PTR) + && nargs == 1 + && (arg = CALL_EXPR_ARG (call, 0)) + && TREE_CODE (arg) == CALL_EXPR + && call_expr_nargs (arg) == 1 + && integer_zerop (CALL_EXPR_ARG (arg, 0))) + if (tree fun = get_function_named_in_call (arg)) + if (fndecl_built_in_p (fun, BUILT_IN_EH_POINTER)) + { + if (ctx->global->caught_exceptions.length () < 2) + { + no_caught_exceptions: + if (!ctx->quiet) + error_at (loc, "%qD called with no caught exceptions pending", + fndecl); + *non_constant_p = true; + return call; + } + /* Both __cxa_get_exception_ptr (__builtin_eh_pointer (0)) + and __cxa_begin_catch (__builtin_eh_pointer (0)) calls expect + ctx->global->caught_exceptions vector to end with + __cxa_allocate_exception created artificial VAR_DECL (the + exception object) followed by handler type, pushed by TRY_BLOCK + evaluation. The only difference between the functions is that + __cxa_begin_catch pops the handler type from the vector and keeps + the VAR_DECL last and decreases uncaught_exceptions. The + VAR_DECL after __cxa_begin_catch serves as the current exception + and is then popped in __cxa_end_catch evaluation. */ + tree handler_type = ctx->global->caught_exceptions.last (); + if (handler_type && VAR_P (handler_type)) + goto no_caught_exceptions; + unsigned idx = ctx->global->caught_exceptions.length () - 2; + arg = ctx->global->caught_exceptions[idx]; + gcc_assert (VAR_P (arg)); + if (kind == CXA_BEGIN_CATCH) + { + ctx->global->caught_exceptions.pop (); + --ctx->global->uncaught_exceptions; + } + if (handler_type == NULL_TREE) + /* Used for catch (...). Just return void. */ + return void_node; + else if (POINTER_TYPE_P (handler_type)) + { + /* Used for catch of a pointer. */ + if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE) + arg = build4 (ARRAY_REF, TREE_TYPE (TREE_TYPE (arg)), arg, + size_zero_node, NULL_TREE, NULL_TREE); + arg = cp_convert (handler_type, arg, + ctx->quiet ? tf_none : tf_warning_or_error); + if (arg == error_mark_node) + { + *non_constant_p = true; + return call; + } + } + else + { + /* Used for catch of a non-pointer type. */ + tree exc_type = strip_array_types (TREE_TYPE (arg)); + tree exc_ptr_type = build_pointer_type (exc_type); + arg = build_fold_addr_expr_with_type (arg, exc_ptr_type); + if (CLASS_TYPE_P (handler_type)) + { + tree ptr_type = build_pointer_type (handler_type); + arg = cp_convert (ptr_type, arg, + ctx->quiet ? tf_none + : tf_warning_or_error); + if (arg == error_mark_node) + { + *non_constant_p = true; + return call; + } + } + } + return cxx_eval_constant_expression (ctx, arg, vc_prvalue, + non_constant_p, overflow_p, + jump_target); + } + for (int i = 0; i < nargs; ++i) + { + args[i] = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (call, i), + vc_prvalue, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p) + return call; + if (*jump_target) + return NULL_TREE; + } + switch (kind) + { + case CXA_ALLOCATE_EXCEPTION: + if (nargs != 1) + goto invalid_nargs; + if (!tree_fits_uhwi_p (args[0])) + { + if (!ctx->quiet) + error_at (loc, "cannot allocate exception: size not constant"); + *non_constant_p = true; + return call; + } + else + { + tree type = build_array_type_nelts (char_type_node, + tree_to_uhwi (args[0])); + tree var = cxa_allocate_exception (loc, ctx, type, size_zero_node); + ctx->global->put_value (var, NULL_TREE); + return fold_convert (ptr_type_node, build_address (var)); + } + case CXA_FREE_EXCEPTION: + if (nargs != 1) + goto invalid_nargs; + arg = cxa_check_throw_arg (args[0], true); + if (arg == NULL_TREE) + { + invalid_ptr: + if (!ctx->quiet) + error_at (loc, "first argument to %qD function not result of " + "%<__cxa_allocate_exception%>", fndecl); + *non_constant_p = true; + return call; + } + DECL_NAME (arg) = heap_deleted_identifier; + ctx->global->destroy_value (arg); + ctx->global->heap_dealloc_count++; + return void_node; + case CXA_THROW: + if (nargs != 3) + goto invalid_nargs; + arg = cxa_check_throw_arg (args[0], false); + if (arg == NULL_TREE) + goto invalid_ptr; + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + ++ctx->global->uncaught_exceptions; + *jump_target = arg; + return void_node; + case CXA_BEGIN_CATCH: + case CXA_GET_EXCEPTION_PTR: + goto invalid_nargs; + case CXA_END_CATCH: + if (nargs != 0) + goto invalid_nargs; + if (ctx->global->caught_exceptions.is_empty ()) + { + no_active_exc: + if (!ctx->quiet) + error_at (loc, "%qD called with no caught exceptions active", + fndecl); + *non_constant_p = true; + return call; + } + else + { + arg = ctx->global->caught_exceptions.pop (); + if (arg == NULL_TREE || !VAR_P (arg)) + goto no_active_exc; + free_except: + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (MINUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + if (integer_zerop (DECL_EXCEPTION_REFCOUNT (arg))) + { + if (type_build_dtor_call (TREE_TYPE (arg))) + { + tree cleanup + = cxx_maybe_build_cleanup (arg, (ctx->quiet ? tf_none + : tf_warning_or_error)); + if (cleanup == error_mark_node) + *non_constant_p = true; + tree jmp_target = NULL_TREE; + cxx_eval_constant_expression (ctx, cleanup, vc_discard, + non_constant_p, overflow_p, + &jmp_target); + if (throws (&jmp_target)) + *jump_target = jmp_target; + } + DECL_NAME (arg) = heap_deleted_identifier; + ctx->global->destroy_value (arg); + ctx->global->heap_dealloc_count++; + } + } + return void_node; + case CXA_RETHROW: + if (nargs != 0) + goto invalid_nargs; + unsigned idx; + FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg) + if (arg == NULL_TREE || !VAR_P (arg)) + --idx; + else + break; + if (arg == NULL_TREE) + { + if (!ctx->quiet) + error_at (loc, "%qD called with no caught exceptions active", + fndecl); + *non_constant_p = true; + return call; + } + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node); + ++ctx->global->uncaught_exceptions; + *jump_target = arg; + return void_node; + case CXA_BAD_CAST: + case CXA_BAD_TYPEID: + case CXA_THROW_BAD_ARRAY_NEW_LENGTH: + if (nargs != 0) + goto invalid_nargs; + else + { + tree name; + switch (kind) + { + case CXA_BAD_CAST: + name = get_identifier ("bad_cast"); + break; + case CXA_BAD_TYPEID: + name = get_identifier ("bad_typeid"); + break; + case CXA_THROW_BAD_ARRAY_NEW_LENGTH: + name = get_identifier ("bad_array_new_length"); + break; + default: + gcc_unreachable (); + } + tree decl = lookup_qualified_name (std_node, name); + if (TREE_CODE (decl) != TYPE_DECL + || !CLASS_TYPE_P (TREE_TYPE (decl)) + || !type_build_ctor_call (TREE_TYPE (decl))) + { + if (!ctx->quiet) + error_at (loc, "%qD called without %<std::%D%> being defined", + fndecl, name); + *non_constant_p = true; + return call; + } + tree type = TREE_TYPE (decl); + tree var = cxa_allocate_exception (loc, ctx, type, size_one_node); + tree ctor + = build_special_member_call (var, complete_ctor_identifier, + NULL, type, LOOKUP_NORMAL, + ctx->quiet ? tf_none + : tf_warning_or_error); + if (ctor == error_mark_node) + { + *non_constant_p = true; + return call; + } + if (TREE_CONSTANT (ctor)) + ctx->global->put_value (var, ctor); + else + { + ctx->global->put_value (var, NULL_TREE); + cxx_eval_constant_expression (ctx, ctor, vc_discard, + non_constant_p, overflow_p, + jump_target); + if (*non_constant_p) + return call; + if (throws (jump_target)) + return NULL_TREE; + } + ++ctx->global->uncaught_exceptions; + *jump_target = var; + } + return void_node; + case STD_UNCAUGHT_EXCEPTIONS: + if (nargs != 0) + goto invalid_nargs; + /* Similarly to __builtin_is_constant_evaluated (), we don't + want to give a definite answer during mce_unknown evaluation, + because that might prevent evaluation later on when some + exceptions might be uncaught. But unlike that, we don't + want to constant fold it even during cp_fold, because at runtime + std::uncaught_exceptions () might still be non-zero. */ + if (ctx->manifestly_const_eval != mce_true) + { + *non_constant_p = true; + return call; + } + return build_int_cst (integer_type_node, + ctx->global->uncaught_exceptions); + case STD_CURRENT_EXCEPTION: + if (nargs != 0) + goto invalid_nargs; + else + { + tree name = get_identifier ("exception_ptr"); + tree decl = lookup_qualified_name (std_node, name); + tree fld; + if (TREE_CODE (decl) != TYPE_DECL + || !CLASS_TYPE_P (TREE_TYPE (decl)) + || !COMPLETE_TYPE_P (TREE_TYPE (decl)) + || !(fld = next_aggregate_field (TYPE_FIELDS (TREE_TYPE (decl)))) + || DECL_ARTIFICIAL (fld) + || TREE_CODE (TREE_TYPE (fld)) != POINTER_TYPE + || next_aggregate_field (DECL_CHAIN (fld)) + || !tree_int_cst_equal (TYPE_SIZE (TREE_TYPE (decl)), + TYPE_SIZE (TREE_TYPE (fld)))) + { + if (!ctx->quiet) + error_at (loc, "%qD called without supportable %qs", + fndecl, "std::exception_ptr"); + *non_constant_p = true; + return call; + } + FOR_EACH_VEC_ELT_REVERSE (ctx->global->caught_exceptions, idx, arg) + if (arg == NULL_TREE || !VAR_P (arg)) + --idx; + else + break; + /* Similarly to __builtin_is_constant_evaluated (), we don't + want to give a definite answer during mce_unknown evaluation, + because that might prevent evaluation later on when some + exceptions might be current. But unlike that, we don't + want to constant fold it to null even during cp_fold, because + at runtime std::current_exception () might still be non-null. */ + if (ctx->manifestly_const_eval != mce_true && arg == NULL_TREE) + { + *non_constant_p = true; + return call; + } + if (arg == NULL_TREE) + arg = build_zero_cst (TREE_TYPE (fld)); + else + { + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + arg = fold_convert (ptr_type_node, build_address (arg)); + } + return build_constructor_single (TREE_TYPE (decl), fld, arg); + } + case STD_RETHROW_EXCEPTION: + if (nargs != 1) + goto invalid_nargs; + if (TYPE_REF_P (TREE_TYPE (args[0]))) + { + arg = args[0]; + STRIP_NOPS (arg); + if (TREE_CODE (arg) == ADDR_EXPR) + { + args[0] + = cxx_eval_constant_expression (ctx, TREE_OPERAND (arg, 0), + vc_prvalue, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p) + return call; + if (*jump_target) + return NULL_TREE; + } + } + if (TREE_CODE (args[0]) != CONSTRUCTOR + || CONSTRUCTOR_NELTS (args[0]) != 1) + { + invalid_std_rethrow: + if (!ctx->quiet) + error_at (loc, "%qD called with unexpected %qs argument", + fndecl, "std::exception_ptr"); + *non_constant_p = true; + return void_node; + } + arg = cxa_check_throw_arg (CONSTRUCTOR_ELT (args[0], 0)->value, false); + if (arg == NULL_TREE) + goto invalid_std_rethrow; + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), size_one_node); + ++ctx->global->uncaught_exceptions; + *jump_target = arg; + return void_node; + case BUILTIN_EH_PTR_ADJUST_REF: + if (nargs != 2) + goto invalid_nargs; + arg = cxa_check_throw_arg (args[0], false); + if (arg == NULL_TREE) + goto invalid_ptr; + if (integer_onep (args[1])) + DECL_EXCEPTION_REFCOUNT (arg) + = size_binop (PLUS_EXPR, DECL_EXCEPTION_REFCOUNT (arg), + size_one_node); + else if (integer_minus_onep (args[1])) + goto free_except; + else + { + if (!ctx->quiet) + error_at (loc, "%qD called with second argument " + "other than 1 or -1", fndecl); + *non_constant_p = true; + } + return void_node; + default: + gcc_unreachable (); + } +} /* Attempt to evaluate T which represents a call to a builtin function. We assume here that all builtin functions evaluate to scalar types @@ -1530,7 +2265,8 @@ static tree find_deleted_heap_var (tree *, int *, void *); static tree cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { const int nargs = call_expr_nargs (t); tree *args = (tree *) alloca (nargs * sizeof (tree)); @@ -1576,6 +2312,12 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, return fold_builtin_source_location (t); } + if (fndecl_built_in_p (fun, CP_BUILT_IN_EH_PTR_ADJUST_REF, + BUILT_IN_FRONTEND)) + return cxx_eval_cxa_builtin_fn (ctx, t, BUILTIN_EH_PTR_ADJUST_REF, + fun, non_constant_p, overflow_p, + jump_target); + int strops = 0; int strret = 0; if (fndecl_built_in_p (fun, BUILT_IN_NORMAL)) @@ -1650,8 +2392,14 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, || potential_constant_expression (arg)) { bool dummy1 = false, dummy2 = false; + tree jmp_target = NULL_TREE; arg = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, - &dummy1, &dummy2); + &dummy1, &dummy2, &jmp_target); + if (jmp_target) + { + *jump_target = jmp_target; + return NULL_TREE; + } } if (bi_const_p) @@ -1740,7 +2488,8 @@ cxx_eval_builtin_function_call (const constexpr_ctx *ctx, tree t, tree fun, } return cxx_eval_constant_expression (&new_ctx, new_call, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* TEMP is the constant value of a temporary object of type TYPE. Adjust @@ -1854,20 +2603,29 @@ addr_of_non_const_var (tree *tp, int *walk_subtrees, void *data) static tree cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, - bool *non_constant_p, bool *overflow_p, - bool *non_constant_args) + tree orig_fun, bool *non_constant_p, + bool *overflow_p, bool *non_constant_args, + tree *jump_target) { - const int nargs = call_expr_nargs (t); + int nargs = call_expr_nargs (t); tree parms = DECL_ARGUMENTS (fun); - int i; + int i, j = 0; + if (DECL_HAS_IN_CHARGE_PARM_P (fun) && fun != orig_fun) + ++nargs; + if (DECL_HAS_VTT_PARM_P (fun) + && fun != orig_fun + && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun) + || DECL_COMPLETE_DESTRUCTOR_P (orig_fun))) + ++nargs; /* We don't record ellipsis args below. */ int nparms = list_length (parms); int nbinds = nargs < nparms ? nargs : nparms; tree binds = make_tree_vec (nbinds); /* The call is not a constant expression if it involves the cdtor for a type - with virtual bases. */ - if (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun)) + with virtual bases before C++26. */ + if (cxx_dialect < cxx26 + && (DECL_HAS_IN_CHARGE_PARM_P (fun) || DECL_HAS_VTT_PARM_P (fun))) { if (!ctx->quiet) { @@ -1885,7 +2643,30 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, tree type = parms ? TREE_TYPE (parms) : void_type_node; if (parms && DECL_BY_REFERENCE (parms)) type = TREE_TYPE (type); - x = get_nth_callarg (t, i); + if (i == 1 + && j == 0 + && DECL_HAS_IN_CHARGE_PARM_P (fun) + && orig_fun != fun) + { + if (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun) + || DECL_COMPLETE_DESTRUCTOR_P (orig_fun)) + x = boolean_true_node; + else + x = boolean_false_node; + j = -1; + } + else if (i == 2 + && j == -1 + && DECL_HAS_VTT_PARM_P (fun) + && orig_fun != fun + && (DECL_COMPLETE_CONSTRUCTOR_P (orig_fun) + || DECL_COMPLETE_DESTRUCTOR_P (orig_fun))) + { + x = build_zero_cst (type); + j = -2; + } + else + x = get_nth_callarg (t, i + j); /* For member function, the first argument is a pointer to the implied object. For a constructor, it might still be a dummy object, in which case we get the real argument from ctx. */ @@ -1900,14 +2681,16 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, /* Undo convert_for_arg_passing work here. */ x = convert_from_reference (x); arg = cxx_eval_constant_expression (ctx, x, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } else /* Normally we would strip a TARGET_EXPR in an initialization context such as this, but here we do the elision differently: we keep the TARGET_EXPR, and use its CONSTRUCTOR as the value of the parm. */ arg = cxx_eval_constant_expression (ctx, x, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); /* Check we aren't dereferencing a null pointer when calling a non-static member function, which is undefined behaviour. */ if (i == 0 && DECL_OBJECT_MEMBER_FUNCTION_P (fun) @@ -1925,6 +2708,8 @@ cxx_bind_parameters_in_call (const constexpr_ctx *ctx, tree t, tree fun, /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p && ctx->quiet) break; + if (*jump_target) + break; /* Just discard ellipsis args after checking their constantitude. */ if (!parms) continue; @@ -2036,9 +2821,10 @@ fold_operand (tree e, const constexpr_ctx *ctx) if (ctx) { bool new_non_constant_p = false, new_overflow_p = false; + tree jmp_target = NULL_TREE; e = cxx_eval_constant_expression (ctx, e, vc_prvalue, &new_non_constant_p, - &new_overflow_p); + &new_overflow_p, &jmp_target); } else e = fold_non_dependent_expr (e, tf_none, /*manifestly_const_eval=*/true); @@ -2125,7 +2911,7 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, if (*non_constant_p) return true; - tree eval; + tree eval, jmp_target = NULL_TREE; if (!evaluated) { if (!potential_rvalue_constant_expression (arg)) @@ -2138,12 +2924,15 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, modifiable_tracker ms (new_ctx.global); eval = cxx_eval_constant_expression (&new_ctx, arg, vc_prvalue, &new_non_constant_p, - &new_overflow_p); + &new_overflow_p, &jmp_target); } else eval = cxx_eval_constant_expression (ctx, arg, vc_prvalue, non_constant_p, - overflow_p); + overflow_p, &jmp_target); + if (jmp_target) + return true; + if (!*non_constant_p && integer_zerop (eval)) { if (!ctx->quiet) @@ -2175,7 +2964,8 @@ cxx_eval_assert (const constexpr_ctx *ctx, tree arg, const char *msg, static tree cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { enum tree_code opcode = ERROR_MARK; @@ -2208,13 +2998,15 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, case IFN_LAUNDER: return cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); case IFN_VEC_CONVERT: { tree arg = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; if (TREE_CODE (arg) == VECTOR_CST) if (tree r = fold_const_call (CFN_VEC_CONVERT, TREE_TYPE (t), arg)) return r; @@ -2232,10 +3024,13 @@ cxx_eval_internal_function (const constexpr_ctx *ctx, tree t, /* Evaluate constant arguments using OPCODE and return a complex number containing the result and the overflow bit. */ tree arg0 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); tree arg1 = cxx_eval_constant_expression (ctx, CALL_EXPR_ARG (t, 1), lval, - non_constant_p, overflow_p); - + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (TREE_CODE (arg0) == INTEGER_CST && TREE_CODE (arg1) == INTEGER_CST) { location_t loc = cp_expr_loc_or_input_loc (t); @@ -2504,14 +3299,12 @@ get_component_with_type (tree path, tree type, tree stop) dst_ptr + src2dst == src_ptr -1: unspecified relationship -2: src_type is not a public base of dst_type - -3: src_type is a multiple public non-virtual base of dst_type - - Since literal types can't have virtual bases, we only expect hint >=0, - -2, or -3. */ + -3: src_type is a multiple public non-virtual base of dst_type */ static tree cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { /* T will be something like __dynamic_cast ((B*) b, &_ZTI1B, &_ZTI1D, 8) @@ -2530,19 +3323,42 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, /* TYPE can only be either T* or T&. We can't know which of these it is by looking at TYPE, but OBJ will be "(T*) x" in the first case, - and something like "(T*)(T&)(T*) x" in the second case. */ - bool reference_p = false; + and something like "(T*)(T&)(T*) x" in the second case. + This is true for the reference cases in C++ < 26 or when exceptions + aren't enabled, in that case we should diagnose errors. For C++26 + with exceptions we should silently evaluate to null pointer and + let the callers call __cxa_bad_cast () later to throw an exception. */ + bool fail_for_non_constant_p = false; while (CONVERT_EXPR_P (obj) || TREE_CODE (obj) == SAVE_EXPR) { - reference_p |= TYPE_REF_P (TREE_TYPE (obj)); + if (cxx_dialect < cxx26 || !flag_exceptions) + fail_for_non_constant_p |= TYPE_REF_P (TREE_TYPE (obj)); obj = TREE_OPERAND (obj, 0); } /* Evaluate the object so that we know its dynamic type. */ obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); if (*non_constant_p) return call; + if (*jump_target) + return NULL_TREE; + + /* For dynamic_cast from classes with virtual bases we can get something + like (virt_base *)(&d + 16) as OBJ. Try to convert that into + d.D.1234 using cxx_fold_indirect_ref. */ + if (cxx_dialect >= cxx26 && CONVERT_EXPR_P (obj)) + { + tree objo = obj; + STRIP_NOPS (objo); + if (TREE_CODE (objo) == POINTER_PLUS_EXPR) + { + objo = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (TREE_TYPE (obj)), + obj, NULL, jump_target); + if (objo) + obj = build_fold_addr_expr (objo); + } + } /* We expect OBJ to be in form of &d.D.2102 when HINT == 0, but when HINT is > 0, it can also be something like @@ -2554,7 +3370,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, ? TREE_OPERAND (obj, 1) : obj)) if (TREE_CODE (t) != FIELD_DECL || !DECL_FIELD_IS_BASE (t)) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2576,9 +3392,12 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, constructor or destructor's class. */ tree vtable = build_vfield_ref (obj, objtype); vtable = cxx_eval_constant_expression (ctx, vtable, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return call; + if (*jump_target) + return NULL_TREE; /* With -fsanitize=vptr, we initialize all vtable pointers to null, so it's possible that we got a null pointer now. */ if (integer_zerop (vtable)) @@ -2610,7 +3429,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, /* If not accessible, give an error. */ if (t == error_mark_node) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2643,7 +3462,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, obj = get_component_with_type (obj, mdtype, NULL_TREE); if (obj == error_mark_node) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2665,7 +3484,7 @@ cxx_eval_dynamic_cast_fn (const constexpr_ctx *ctx, tree call, tree binfo = lookup_base (mdtype, type, ba_check, &b_kind, tf_none); if (!binfo || binfo == error_mark_node) { - if (reference_p) + if (fail_for_non_constant_p) { if (!ctx->quiet) { @@ -2761,7 +3580,7 @@ replace_decl (tree *tp, tree decl, tree replacement) static tree cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree function = THUNK_TARGET (thunk_fndecl); @@ -2804,7 +3623,8 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, new_call, offset); return cxx_eval_constant_expression (ctx, new_call, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* If OBJECT is of const class type, evaluate it to a CONSTRUCTOR and set @@ -2814,7 +3634,7 @@ cxx_eval_thunk_call (const constexpr_ctx *ctx, tree t, tree thunk_fndecl, static void cxx_set_object_constness (const constexpr_ctx *ctx, tree object, bool readonly_p, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { if (CLASS_TYPE_P (TREE_TYPE (object)) && CP_TYPE_CONST_P (TREE_TYPE (object))) @@ -2822,8 +3642,11 @@ cxx_set_object_constness (const constexpr_ctx *ctx, tree object, /* Subobjects might not be stored in ctx->global->values but we can get its CONSTRUCTOR by evaluating *this. */ tree e = cxx_eval_constant_expression (ctx, object, vc_prvalue, - non_constant_p, overflow_p); - if (TREE_CODE (e) == CONSTRUCTOR && !*non_constant_p) + non_constant_p, overflow_p, + jump_target); + if (!*non_constant_p + && !throws (jump_target) + && TREE_CODE (e) == CONSTRUCTOR) TREE_READONLY (e) = readonly_p; } } @@ -2835,23 +3658,25 @@ cxx_set_object_constness (const constexpr_ctx *ctx, tree object, static tree cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { location_t loc = cp_expr_loc_or_input_loc (t); tree fun = get_function_named_in_call (t); - constexpr_call new_call - = { NULL, NULL, NULL, 0, ctx->manifestly_const_eval }; - int depth_ok; if (fun == NULL_TREE) return cxx_eval_internal_function (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (TREE_CODE (fun) != FUNCTION_DECL) { /* Might be a constexpr function pointer. */ fun = cxx_eval_constant_expression (ctx, fun, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; STRIP_NOPS (fun); if (TREE_CODE (fun) == ADDR_EXPR) fun = TREE_OPERAND (fun, 0); @@ -2894,6 +3719,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; return t; } + tree orig_fun = fun; if (DECL_CLONED_FUNCTION_P (fun) && !DECL_DELETING_DESTRUCTOR_P (fun)) fun = DECL_CLONED_FUNCTION (fun); @@ -2902,9 +3728,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, if (fndecl_built_in_p (fun)) return cxx_eval_builtin_function_call (ctx, t, fun, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); if (DECL_THUNK_P (fun)) - return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p); + return cxx_eval_thunk_call (ctx, t, fun, lval, non_constant_p, overflow_p, + jump_target); + bool non_constexpr_call = false; if (!maybe_constexpr_fn (fun)) { if (TREE_CODE (t) == CALL_EXPR @@ -2919,7 +3748,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { tree arg = CALL_EXPR_ARG (t, i); arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Deleting a non-constant pointer has a better error message below. */ if (new_op_p || i != 0) @@ -2930,12 +3762,11 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, gcc_assert (arg0); if (new_op_p) { - /* FIXME: We should not get here; the VERIFY_CONSTANT above - should have already caught it. But currently a conversion - from pointer type to arithmetic type is only considered - non-constant for CONVERT_EXPRs, not NOP_EXPRs. */ if (!tree_fits_uhwi_p (arg0)) { + /* We should not get here; the VERIFY_CONSTANT above + should have already caught it. */ + gcc_checking_assert (false); if (!ctx->quiet) error_at (loc, "cannot allocate array: size not constant"); *non_constant_p = true; @@ -2950,14 +3781,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, : heap_uninit_identifier, type); DECL_ARTIFICIAL (var) = 1; - TREE_STATIC (var) = 1; - // Temporarily register the artificial var in varpool, - // so that comparisons of its address against NULL are folded - // through nonzero_address even with - // -fno-delete-null-pointer-checks or that comparison of - // addresses of different heap artificial vars is folded too. - // See PR98988 and PR99031. - varpool_node::finalize_decl (var); ctx->global->heap_vars.safe_push (var); ctx->global->put_value (var, NULL_TREE); return fold_convert (ptr_type_node, build_address (var)); @@ -3043,7 +3866,10 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, { tree arg = CALL_EXPR_ARG (t, i); arg = cxx_eval_constant_expression (ctx, arg, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (i == 1) arg1 = arg; else @@ -3053,19 +3879,35 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, return arg1; } else if (cxx_dynamic_cast_fn_p (fun)) - return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p); + return cxx_eval_dynamic_cast_fn (ctx, t, non_constant_p, overflow_p, + jump_target); + else if (enum cxa_builtin kind = cxx_cxa_builtin_fn_p (fun)) + return cxx_eval_cxa_builtin_fn (ctx, t, kind, fun, + non_constant_p, overflow_p, + jump_target); - if (!ctx->quiet) + /* Calls to non-constexpr functions can be diagnosed right away + before C++26, though in C++26 evaluation of the arguments might + throw and if caught it could be still constant expression. + So for C++26 this is diagnosed only after + cxx_bind_parameters_in_call. */ + if (cxx_dialect >= cxx26) + non_constexpr_call = true; + else { - if (!lambda_static_thunk_p (fun)) - error_at (loc, "call to non-%<constexpr%> function %qD", fun); - explain_invalid_constexpr_fn (fun); + if (!ctx->quiet) + { + if (!lambda_static_thunk_p (fun)) + error_at (loc, "call to non-%<constexpr%> function %qD", fun); + explain_invalid_constexpr_fn (fun); + } + *non_constant_p = true; + return t; } - *non_constant_p = true; - return t; } constexpr_ctx new_ctx = *ctx; + ctx = &new_ctx; if (DECL_CONSTRUCTOR_P (fun) && !ctx->object && TREE_CODE (t) == AGGR_INIT_EXPR) { @@ -3075,30 +3917,30 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, tree ctor = new_ctx.ctor = build_constructor (DECL_CONTEXT (fun), NULL); CONSTRUCTOR_NO_CLEARING (ctor) = true; ctx->global->put_value (new_ctx.object, ctor); - ctx = &new_ctx; } /* An immediate invocation is manifestly constant evaluated including the arguments of the call, so use mce_true even for the argument evaluation. */ if (DECL_IMMEDIATE_FUNCTION_P (fun)) - { - new_ctx.manifestly_const_eval = mce_true; - new_call.manifestly_const_eval = mce_true; - ctx = &new_ctx; - } + new_ctx.manifestly_const_eval = mce_true; /* We used to shortcut trivial constructor/op= here, but nowadays we can only get a trivial function here with -fno-elide-constructors. */ gcc_checking_assert (!trivial_fn_p (fun) || !flag_elide_constructors + /* Or it's a call from maybe_thunk_body (111075). */ + || (TREE_CODE (t) == CALL_EXPR ? CALL_FROM_THUNK_P (t) + : AGGR_INIT_FROM_THUNK_P (t)) /* We don't elide constructors when processing a noexcept-expression. */ || cp_noexcept_operand); bool non_constant_args = false; + constexpr_call new_call; new_call.bindings - = cxx_bind_parameters_in_call (ctx, t, fun, non_constant_p, - overflow_p, &non_constant_args); + = cxx_bind_parameters_in_call (ctx, t, fun, orig_fun, non_constant_p, + overflow_p, &non_constant_args, + jump_target); /* We build up the bindings list before we know whether we already have this call cached. If we don't end up saving these bindings, ggc_free them when @@ -3112,8 +3954,21 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, void preserve () { bindings = NULL; } } fb (new_call.bindings); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; + if (non_constexpr_call) + { + if (!ctx->quiet) + { + if (!lambda_static_thunk_p (fun)) + error_at (loc, "call to non-%<constexpr%> function %qD", fun); + explain_invalid_constexpr_fn (fun); + } + *non_constant_p = true; + return t; + } /* We can't defer instantiating the function any longer. */ if (!DECL_INITIAL (fun) @@ -3169,7 +4024,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, } } - depth_ok = push_cx_call_context (t); + /* Don't complain about problems evaluating an ill-formed function. */ + if (function *f = DECL_STRUCT_FUNCTION (fun)) + if (f->language->erroneous) + new_ctx.quiet = true; + + int depth_ok = push_cx_call_context (t); /* Remember the object we are constructing or destructing. */ tree new_obj = NULL_TREE; @@ -3181,7 +4041,9 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, new_obj = TREE_VEC_ELT (new_call.bindings, 0); bool empty_base = false; new_obj = cxx_fold_indirect_ref (ctx, loc, DECL_CONTEXT (fun), new_obj, - &empty_base); + &empty_base, jump_target); + if (*jump_target) + return NULL_TREE; /* If we're initializing an empty class, don't set constness, because cxx_fold_indirect_ref will return the wrong object to set constness of. */ @@ -3211,8 +4073,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, new_call.hash = constexpr_fundef_hasher::hash (new_call.fundef); new_call.hash = iterative_hash_template_arg (new_call.bindings, new_call.hash); - new_call.hash - = iterative_hash_object (ctx->manifestly_const_eval, new_call.hash); /* If we have seen this call before, we are done. */ maybe_initialize_constexpr_call_table (); @@ -3230,22 +4090,23 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, the slot can move during evaluation of the body. */ *slot = entry = ggc_alloc<constexpr_call> (); *entry = new_call; + entry->result (ctx->manifestly_const_eval) = unknown_type_node; fb.preserve (); } } - /* Calls that are in progress have their result set to NULL, so that we - can detect circular dependencies. Now that we only cache up to - constexpr_cache_depth this won't catch circular dependencies that + /* Calls that are in progress have their result set to unknown_type_node, + so that we can detect circular dependencies. Now that we only cache + up to constexpr_cache_depth this won't catch circular dependencies that start deeper, but they'll hit the recursion or ops limit. */ - else if (entry->result == NULL) + else if (entry->result (ctx->manifestly_const_eval) == unknown_type_node) { if (!ctx->quiet) error ("call has circular dependency"); *non_constant_p = true; - entry->result = result = error_mark_node; + entry->result (ctx->manifestly_const_eval) = result = error_mark_node; } else - result = entry->result; + result = entry->result (ctx->manifestly_const_eval); } if (!depth_ok) @@ -3331,7 +4192,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, semantics are no longer in effect; see [class.dtor]p5. */ if (new_obj && DECL_DESTRUCTOR_P (fun)) cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* If this is a constructor, we are beginning the lifetime of the object we are initializing. */ @@ -3345,16 +4206,25 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, build_constructor (TREE_TYPE (new_obj), NULL)); cxx_eval_constant_expression (ctx, activate, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); ggc_free (activate); + if (*jump_target) + return NULL_TREE; } - tree jump_target = NULL_TREE; + tree jmp_target = NULL_TREE; cxx_eval_constant_expression (&call_ctx, body, vc_discard, non_constant_p, overflow_p, - &jump_target); + &jmp_target); - if (DECL_CONSTRUCTOR_P (fun)) + if (!*non_constant_p && throws (&jmp_target)) + { + result = NULL_TREE; + cacheable = false; + *jump_target = jmp_target; + } + else if (DECL_CONSTRUCTOR_P (fun)) /* This can be null for a subobject constructor call, in which case what we care about is the initialization side-effects rather than the value. We could get at the @@ -3382,7 +4252,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, marking the CONSTRUCTOR TREE_READONLY. */ if (new_obj && DECL_CONSTRUCTOR_P (fun)) cxx_set_object_constness (ctx, new_obj, /*readonly_p=*/true, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* Remove the parms/result from the values map. */ destroy_value_checked (ctx, res, non_constant_p); @@ -3425,11 +4295,6 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, cacheable = false; break; } - /* And don't cache a ref to a deleted heap variable (119162). */ - if (cacheable - && (cp_walk_tree_without_duplicates - (&result, find_deleted_heap_var, NULL))) - cacheable = false; } /* Rewrite all occurrences of the function's RESULT_DECL with the @@ -3447,7 +4312,13 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, cacheable = false; result = cxx_eval_constant_expression (ctx, result, lval, non_constant_p, - overflow_p); + overflow_p, + jump_target); + if (*jump_target) + { + cacheable = false; + result = NULL_TREE; + } } } @@ -3466,7 +4337,22 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, else if (!result) result = void_node; if (entry) - entry->result = cacheable ? result : error_mark_node; + { + entry->result (ctx->manifestly_const_eval) + = cacheable ? result : error_mark_node; + + if (result != error_mark_node + && ctx->manifestly_const_eval == mce_unknown) + { + /* Evaluation succeeded and was independent of whether we're in a + manifestly constant-evaluated context, so we can also reuse + this result when evaluating this call with a fixed context. */ + if (!entry->result (mce_true)) + entry->result (mce_true) = entry->result (mce_unknown); + if (!entry->result (mce_false)) + entry->result (mce_false) = entry->result (mce_unknown); + } + } } /* The result of a constexpr function must be completely initialized. @@ -3487,11 +4373,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, /* Return true if T is a valid constant initializer. If a CONSTRUCTOR initializes all the members, the CONSTRUCTOR_NO_CLEARING flag will be - cleared. + cleared. If called recursively on a FIELD_DECL's CONSTRUCTOR, SZ + is DECL_SIZE of the FIELD_DECL, otherwise NULL. FIXME speed this up, it's taking 16% of compile time on sieve testcase. */ bool -reduced_constant_expression_p (tree t) +reduced_constant_expression_p (tree t, tree sz /* = NULL_TREE */) { if (t == NULL_TREE) return false; @@ -3502,6 +4389,9 @@ reduced_constant_expression_p (tree t) /* Even if we can't lower this yet, it's constant. */ return true; + case OMP_DECLARE_MAPPER: + return true; + case CONSTRUCTOR: /* And we need to handle PTRMEM_CST wrapped in a CONSTRUCTOR. */ tree field; @@ -3556,7 +4446,12 @@ reduced_constant_expression_p (tree t) { /* If VAL is null, we're in the middle of initializing this element. */ - if (!reduced_constant_expression_p (e.value)) + if (!reduced_constant_expression_p (e.value, + (e.index + && (TREE_CODE (e.index) + == FIELD_DECL)) + ? DECL_SIZE (e.index) + : NULL_TREE)) return false; /* We want to remove initializers for empty fields in a struct to avoid confusing output_constructor. */ @@ -3576,7 +4471,16 @@ reduced_constant_expression_p (tree t) /* There could be a non-empty field at the end. */ for (; field; field = next_subobject_field (DECL_CHAIN (field))) if (!is_really_empty_class (TREE_TYPE (field), /*ignore_vptr*/false)) - return false; + { + /* Ignore FIELD_DECLs with bit positions beyond DECL_SIZE of + the parent FIELD_DECL (if any) for classes with virtual + bases. */ + if (cxx_dialect >= cxx26 + && sz + && tree_int_cst_le (sz, bit_position (field))) + break; + return false; + } ok: if (CONSTRUCTOR_NO_CLEARING (t)) /* All the fields are initialized. */ @@ -3772,12 +4676,16 @@ cxx_eval_check_shift_p (location_t loc, const constexpr_ctx *ctx, static tree cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, bool /*lval*/, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r; tree orig_arg = TREE_OPERAND (t, 0); tree arg = cxx_eval_constant_expression (ctx, orig_arg, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg); location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); @@ -3801,7 +4709,7 @@ cxx_eval_unary_expression (const constexpr_ctx *ctx, tree t, static tree cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, tree lhs, tree rhs, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { STRIP_NOPS (lhs); if (TREE_CODE (lhs) != ADDR_EXPR) @@ -3823,9 +4731,12 @@ cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, t = fold_convert_loc (loc, ssizetype, TREE_OPERAND (lhs, 1)); tree nelts = array_type_nelts_top (TREE_TYPE (TREE_OPERAND (lhs, 0))); nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return NULL_TREE; + if (*jump_target) + return NULL_TREE; /* Don't fold an out-of-bound access. */ if (!tree_int_cst_le (t, nelts)) return NULL_TREE; @@ -3845,7 +4756,8 @@ cxx_fold_pointer_plus_expression (const constexpr_ctx *ctx, tree t, t = cp_build_addr_expr (t, tf_warning_or_error); t = cp_fold_convert (orig_type, t); return cxx_eval_constant_expression (ctx, t, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } return NULL_TREE; @@ -3889,22 +4801,29 @@ cxx_maybe_fold_addr_pointer_plus (tree t) static tree cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r = NULL_TREE; tree orig_lhs = TREE_OPERAND (t, 0); tree orig_rhs = TREE_OPERAND (t, 1); tree lhs, rhs; lhs = cxx_eval_constant_expression (ctx, orig_lhs, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); /* Don't VERIFY_CONSTANT here, it's unnecessary and will break pointer subtraction. */ if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; rhs = cxx_eval_constant_expression (ctx, orig_rhs, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; location_t loc = EXPR_LOCATION (t); enum tree_code code = TREE_CODE (t); @@ -3960,13 +4879,17 @@ cxx_eval_binary_expression (const constexpr_ctx *ctx, tree t, return t; } else if (code == POINTER_PLUS_EXPR) - r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p, - overflow_p); + { + r = cxx_fold_pointer_plus_expression (ctx, t, lhs, rhs, non_constant_p, + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; + } else if (code == SPACESHIP_EXPR) { r = genericize_spaceship (loc, type, lhs, rhs); return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } if (r == NULL_TREE) @@ -4025,7 +4948,10 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, { tree val = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (val); if (TREE_CODE (t) == IF_STMT && IF_STMT_CONSTEVAL_P (t)) { @@ -4086,19 +5012,29 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t, static tree cxx_eval_vector_conditional_expression (const constexpr_ctx *ctx, tree t, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree arg1 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg1); tree arg2 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg2); tree arg3 = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 2), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (arg3); location_t loc = EXPR_LOCATION (t); tree type = TREE_TYPE (t); @@ -4486,7 +5422,8 @@ diag_array_subscript (location_t loc, const constexpr_ctx *ctx, tree array, tree static tree get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree nelts; if (TREE_CODE (type) == ARRAY_TYPE) @@ -4503,7 +5440,8 @@ get_array_or_vector_nelts (const constexpr_ctx *ctx, tree type, /* For VLAs, the number of elements won't be an integer constant. */ nelts = cxx_eval_constant_expression (ctx, nelts, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); return nelts; } @@ -4534,13 +5472,17 @@ extract_string_elt (tree string, unsigned chars_per_elt, unsigned index) static tree eval_and_check_array_index (const constexpr_ctx *ctx, tree t, bool allow_one_past, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { location_t loc = cp_expr_loc_or_input_loc (t); tree ary = TREE_OPERAND (t, 0); t = TREE_OPERAND (t, 1); tree index = cxx_eval_constant_expression (ctx, t, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (index); if (!tree_fits_shwi_p (index) @@ -4552,7 +5494,9 @@ eval_and_check_array_index (const constexpr_ctx *ctx, } tree nelts = get_array_or_vector_nelts (ctx, TREE_TYPE (ary), non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (nelts); if (allow_one_past ? !tree_int_cst_le (index, nelts) @@ -4572,14 +5516,18 @@ eval_and_check_array_index (const constexpr_ctx *ctx, static tree cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree oldary = TREE_OPERAND (t, 0); tree ary = cxx_eval_constant_expression (ctx, oldary, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; if (!lval && TREE_CODE (ary) == VIEW_CONVERT_EXPR && VECTOR_TYPE_P (TREE_TYPE (TREE_OPERAND (ary, 0))) @@ -4589,9 +5537,12 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, tree oldidx = TREE_OPERAND (t, 1); tree index = eval_and_check_array_index (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; if (lval && ary == oldary && index == oldidx) return t; @@ -4709,7 +5660,7 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, ctx = &new_ctx; } t = cxx_eval_constant_expression (ctx, val, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); if (new_ctor && t != ctx->ctor) free_constructor (ctx->ctor); return t; @@ -4721,7 +5672,8 @@ cxx_eval_array_reference (const constexpr_ctx *ctx, tree t, static tree cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { unsigned HOST_WIDE_INT i; tree field; @@ -4730,9 +5682,12 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, tree orig_whole = TREE_OPERAND (t, 0); tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; if (INDIRECT_REF_P (whole) && integer_zerop (TREE_OPERAND (whole, 0))) { @@ -4841,7 +5796,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, value = build_value_init (TREE_TYPE (t), tf_warning_or_error); return cxx_eval_constant_expression (ctx, value, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* Subroutine of cxx_eval_constant_expression. @@ -4851,7 +5807,8 @@ cxx_eval_component_reference (const constexpr_ctx *ctx, tree t, static tree cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree orig_whole = TREE_OPERAND (t, 0); tree retval, fldval, utype, mask; @@ -4859,10 +5816,13 @@ cxx_eval_bit_field_ref (const constexpr_ctx *ctx, tree t, HOST_WIDE_INT istart, isize; tree whole = cxx_eval_constant_expression (ctx, orig_whole, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); tree start, field, value; unsigned HOST_WIDE_INT i; + if (*jump_target) + return NULL_TREE; if (whole == orig_whole) return t; /* Don't VERIFY_CONSTANT here; we only want to check that we got a @@ -5143,7 +6103,7 @@ clear_uchar_or_std_byte_in_mask (location_t loc, tree t, unsigned char *mask) static tree cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, - bool *overflow_p) + bool *overflow_p, tree *jump_target) { if (check_bit_cast_type (ctx, EXPR_LOCATION (t), TREE_TYPE (t), TREE_TYPE (t)) @@ -5157,9 +6117,12 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, } tree op = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) return t; + if (*jump_target) + return NULL_TREE; location_t loc = EXPR_LOCATION (t); if (BITS_PER_UNIT != 8 || CHAR_BIT != 8) @@ -5237,8 +6200,9 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, if (CHECKING_P) { tree e = cxx_eval_bare_aggregate (ctx, r, vc_prvalue, - non_constant_p, overflow_p); - gcc_checking_assert (e == r); + non_constant_p, overflow_p, + jump_target); + gcc_checking_assert (e == r && !*jump_target); r = e; } } @@ -5279,19 +6243,24 @@ cxx_eval_bit_cast (const constexpr_ctx *ctx, tree t, bool *non_constant_p, static tree cxx_eval_logical_expression (const constexpr_ctx *ctx, tree t, tree bailout_value, tree continue_value, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree r; tree lhs = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (lhs); if (tree_int_cst_equal (lhs, bailout_value)) return lhs; gcc_assert (tree_int_cst_equal (lhs, continue_value)); r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (r); return r; } @@ -5448,7 +6417,8 @@ verify_ctor_sanity (const constexpr_ctx *ctx, tree type) static tree cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { vec<constructor_elt, va_gc> *v = CONSTRUCTOR_ELTS (t); bool changed = false; @@ -5501,7 +6471,10 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, get_or_insert_ctor_field (ctx->ctor, index); tree elt = cxx_eval_constant_expression (&new_ctx, value, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT here. */ if (ctx->quiet && *non_constant_p) break; @@ -5591,7 +6564,8 @@ cxx_eval_bare_aggregate (const constexpr_ctx *ctx, tree t, static tree cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, bool value_init, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree elttype = TREE_TYPE (atype); verify_ctor_sanity (ctx, atype); @@ -5602,7 +6576,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, if (init && TREE_CODE (init) == CONSTRUCTOR) return cxx_eval_bare_aggregate (ctx, init, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); /* For the default constructor, build up a call to the default constructor of the element type. We only need to handle class types @@ -5639,7 +6613,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, } tree nelts = get_array_or_vector_nelts (ctx, atype, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; unsigned HOST_WIDE_INT max = tree_to_uhwi (nelts); for (i = 0; i < max; ++i) { @@ -5665,9 +6641,9 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, } else eltinit = cp_build_array_ref (input_location, init, idx, complain); - eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, value_init, - lval, - non_constant_p, overflow_p); + eltinit = cxx_eval_vec_init_1 (&new_ctx, elttype, eltinit, + value_init, lval, non_constant_p, + overflow_p, jump_target); } else if (pre_init) { @@ -5681,7 +6657,8 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, /* Clarify what object is being initialized (118285). */ eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit); eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); reuse = i == 0; } else @@ -5697,8 +6674,11 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, /* Clarify what object is being initialized (118285). */ eltinit = build2 (INIT_EXPR, elttype, new_ctx.object, eltinit); eltinit = cxx_eval_constant_expression (&new_ctx, eltinit, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } + if (*jump_target) + return NULL_TREE; if (*non_constant_p) break; if (no_slot) @@ -5748,7 +6728,7 @@ cxx_eval_vec_init_1 (const constexpr_ctx *ctx, tree atype, tree init, static tree cxx_eval_vec_init (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, tree *jump_target) { tree atype = TREE_TYPE (t); tree init = VEC_INIT_EXPR_INIT (t); @@ -5780,10 +6760,10 @@ cxx_eval_vec_init (const constexpr_ctx *ctx, tree t, } init = expand_vec_init_expr (ctx->object, t, complain); return cxx_eval_constant_expression (ctx, init, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } tree r = cxx_eval_vec_init_1 (ctx, atype, init, value_init, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, jump_target); if (*non_constant_p) return t; else @@ -5812,14 +6792,16 @@ same_type_ignoring_tlq_and_bounds_p (tree type1, tree type2) otherwise return NULL_TREE. */ static tree -cxx_union_active_member (const constexpr_ctx *ctx, tree t) +cxx_union_active_member (const constexpr_ctx *ctx, tree t, tree *jump_target) { constexpr_ctx new_ctx = *ctx; new_ctx.quiet = true; bool non_constant_p = false, overflow_p = false; tree ctor = cxx_eval_constant_expression (&new_ctx, t, vc_prvalue, &non_constant_p, - &overflow_p); + &overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; if (TREE_CODE (ctor) == CONSTRUCTOR && CONSTRUCTOR_NELTS (ctor) == 1 && CONSTRUCTOR_ELT (ctor, 0)->index @@ -5832,12 +6814,28 @@ cxx_union_active_member (const constexpr_ctx *ctx, tree t) static tree cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, - tree op, unsigned HOST_WIDE_INT off, bool *empty_base) + tree op, unsigned HOST_WIDE_INT off, bool *empty_base, + tree *jump_target) { tree optype = TREE_TYPE (op); unsigned HOST_WIDE_INT const_nunits; if (off == 0 && similar_type_p (optype, type)) return op; + else if (cxx_dialect >= cxx26 + && VAR_P (op) + && DECL_VTABLE_OR_VTT_P (op) + && same_type_ignoring_top_level_qualifiers_p (type, + ptrdiff_type_node) + && POINTER_TYPE_P (strip_array_types (optype))) + { + /* We often read some virtual table elements using ptrdiff_t rather + than pointer type. */ + if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, + strip_array_types (optype), + op, off, empty_base, + jump_target)) + return fold_convert (type, ret); + } else if (TREE_CODE (optype) == COMPLEX_TYPE && similar_type_p (type, TREE_TYPE (optype))) { @@ -5881,7 +6879,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, op = build4_loc (loc, ARRAY_REF, TREE_TYPE (optype), op, index, NULL_TREE, NULL_TREE); return cxx_fold_indirect_ref_1 (ctx, loc, type, op, rem, - empty_base); + empty_base, jump_target); } } /* ((foo *)&struct_with_foo_field)[x] => COMPONENT_REF */ @@ -5890,7 +6888,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, { if (TREE_CODE (optype) == UNION_TYPE) /* For unions prefer the currently active member. */ - if (tree field = cxx_union_active_member (ctx, op)) + if (tree field = cxx_union_active_member (ctx, op, jump_target)) { unsigned HOST_WIDE_INT el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); @@ -5899,7 +6897,8 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field, NULL_TREE); if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop, - off, empty_base)) + off, empty_base, + jump_target)) return ret; } } @@ -5931,15 +6930,21 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, if (!tree_fits_uhwi_p (pos)) continue; unsigned HOST_WIDE_INT upos = tree_to_uhwi (pos); - unsigned HOST_WIDE_INT el_sz - = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); + unsigned HOST_WIDE_INT el_sz; + if (DECL_FIELD_IS_BASE (field) + && CLASS_TYPE_P (optype) + && CLASSTYPE_VBASECLASSES (optype)) + el_sz = tree_to_uhwi (DECL_SIZE_UNIT (field)); + else + el_sz = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (field))); if (upos <= off && off < upos + el_sz) { tree cop = build3 (COMPONENT_REF, TREE_TYPE (field), op, field, NULL_TREE); if (tree ret = cxx_fold_indirect_ref_1 (ctx, loc, type, cop, off - upos, - empty_base)) + empty_base, + jump_target)) return ret; } } @@ -5959,7 +6964,7 @@ cxx_fold_indirect_ref_1 (const constexpr_ctx *ctx, location_t loc, tree type, static tree cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, - tree op0, bool *empty_base /* = NULL*/) + tree op0, bool *empty_base, tree *jump_target) { tree sub = op0; tree subtype; @@ -5983,6 +6988,25 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, offset positive, so that cxx_fold_indirect_ref_1 can identify more folding opportunities. */ auto canonicalize_obj_off = [] (tree& obj, tree& off) { + if (cxx_dialect >= cxx26) + { + /* For C++26, we need to fold *(B *)(&x.D.1234 + 32) used + to access virtual base members. */ + tree nobj = obj; + while (TREE_CODE (nobj) == COMPONENT_REF + && DECL_FIELD_IS_BASE (TREE_OPERAND (nobj, 1))) + nobj = TREE_OPERAND (nobj, 0); + if (nobj != obj + && CLASS_TYPE_P (TREE_TYPE (nobj)) + && CLASSTYPE_VBASECLASSES (TREE_TYPE (nobj))) + while (obj != nobj) + { + tree field = TREE_OPERAND (obj, 1); + tree pos = byte_position (field); + off = int_const_binop (PLUS_EXPR, off, pos); + obj = TREE_OPERAND (obj, 0); + } + } while (TREE_CODE (obj) == COMPONENT_REF /* We need to preserve union member accesses so that we can later properly diagnose accessing the wrong member. */ @@ -6021,8 +7045,9 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, { tree off = integer_zero_node; canonicalize_obj_off (op, off); - gcc_assert (integer_zerop (off)); - return cxx_fold_indirect_ref_1 (ctx, loc, type, op, 0, empty_base); + return cxx_fold_indirect_ref_1 (ctx, loc, type, op, + tree_to_uhwi (off), empty_base, + jump_target); } } else if (TREE_CODE (sub) == POINTER_PLUS_EXPR @@ -6037,7 +7062,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree obj = TREE_OPERAND (op00, 0); canonicalize_obj_off (obj, off); return cxx_fold_indirect_ref_1 (ctx, loc, type, obj, - tree_to_uhwi (off), empty_base); + tree_to_uhwi (off), empty_base, + jump_target); } } /* *(foo *)fooarrptr => (*fooarrptr)[0] */ @@ -6047,7 +7073,10 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, tree type_domain; tree min_val = size_zero_node; tree newsub - = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL); + = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (subtype), sub, NULL, + jump_target); + if (*jump_target) + return NULL_TREE; if (newsub) sub = newsub; else @@ -6065,7 +7094,8 @@ cxx_fold_indirect_ref (const constexpr_ctx *ctx, location_t loc, tree type, static tree cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { tree orig_op0 = TREE_OPERAND (t, 0); bool empty_base = false; @@ -6083,13 +7113,17 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, /* First try to simplify it directly. */ tree r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), - orig_op0, &empty_base); + orig_op0, &empty_base, jump_target); + if (*jump_target) + return NULL_TREE; if (!r) { /* If that didn't work, evaluate the operand first. */ tree op0 = cxx_eval_constant_expression (ctx, orig_op0, vc_prvalue, non_constant_p, - overflow_p); + overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -6103,7 +7137,9 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, } r = cxx_fold_indirect_ref (ctx, EXPR_LOCATION (t), TREE_TYPE (t), op0, - &empty_base); + &empty_base, jump_target); + if (*jump_target) + return NULL_TREE; if (r == NULL_TREE) { /* We couldn't fold to a constant value. Make sure it's not @@ -6133,7 +7169,10 @@ cxx_eval_indirect_ref (const constexpr_ctx *ctx, tree t, } r = cxx_eval_constant_expression (ctx, r, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; @@ -6243,7 +7282,8 @@ non_const_var_error (location_t loc, tree r, bool fundef_p) static tree cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { int i; tree args[3]; @@ -6253,7 +7293,10 @@ cxx_eval_trinary_expression (const constexpr_ctx *ctx, tree t, { args[i] = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, i), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (args[i]); } @@ -6375,7 +7418,8 @@ modifying_const_object_p (tree_code code, tree obj, bool mutable_p) static tree cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { constexpr_ctx new_ctx = *ctx; @@ -6383,7 +7427,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (TREE_CLOBBER_P (init) && CLOBBER_KIND (init) < CLOBBER_OBJECT_END) - /* Only handle clobbers ending the lifetime of objects. */ + /* Only handle clobbers ending the lifetime of objects. + ??? We should probably set CONSTRUCTOR_NO_CLEARING. */ return void_node; /* First we figure out where we're storing to. */ @@ -6400,7 +7445,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (!SCALAR_TYPE_P (type)) new_ctx.ctor = new_ctx.object = NULL_TREE; init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6412,8 +7460,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, as a whole; otherwise, only evaluate the innermost piece to avoid building up unnecessary *_REFs. */ target = cxx_eval_constant_expression (ctx, target, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); evaluated = true; + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6439,7 +7490,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (TREE_CODE (probe) == ARRAY_REF) { elt = eval_and_check_array_index (ctx, probe, false, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6496,8 +7550,11 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, break; } probe = cxx_eval_constant_expression (ctx, probe, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); evaluated = true; + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; } @@ -6841,7 +7898,10 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, if (tree tinit = TARGET_EXPR_INITIAL (init)) init = tinit; init = cxx_eval_constant_expression (&new_ctx, init, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* The hash table might have moved since the get earlier, and the initializer might have mutated the underlying CONSTRUCTORs, so we must recompute VALP. */ @@ -6967,7 +8027,8 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t, static tree cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, value_cat lval, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { enum tree_code code = TREE_CODE (t); tree type = TREE_TYPE (t); @@ -6981,12 +8042,18 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, /* The operand as an lvalue. */ op = cxx_eval_constant_expression (ctx, op, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* The operand as an rvalue. */ tree val = cxx_eval_constant_expression (ctx, op, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT if this might be dealing with a pointer to a local array in a constexpr function. */ bool ptr = INDIRECT_TYPE_P (TREE_TYPE (val)); @@ -7025,8 +8092,11 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, tree store = build2_loc (cp_expr_loc_or_loc (t, input_location), MODIFY_EXPR, type, op, mod); mod = cxx_eval_constant_expression (ctx, store, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); ggc_free (store); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; @@ -7040,42 +8110,6 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t, return val; } -/* Predicates for the meaning of *jump_target. */ - -static bool -returns (tree *jump_target) -{ - return *jump_target - && TREE_CODE (*jump_target) == RETURN_EXPR; -} - -static bool -breaks (tree *jump_target) -{ - return *jump_target - && ((TREE_CODE (*jump_target) == LABEL_DECL - && LABEL_DECL_BREAK (*jump_target)) - || TREE_CODE (*jump_target) == BREAK_STMT - || TREE_CODE (*jump_target) == EXIT_EXPR); -} - -static bool -continues (tree *jump_target) -{ - return *jump_target - && ((TREE_CODE (*jump_target) == LABEL_DECL - && LABEL_DECL_CONTINUE (*jump_target)) - || TREE_CODE (*jump_target) == CONTINUE_STMT); - -} - -static bool -switches (tree *jump_target) -{ - return *jump_target - && TREE_CODE (*jump_target) == INTEGER_CST; -} - /* Subroutine of cxx_eval_statement_list. Determine whether the statement STMT matches *jump_target. If we're looking for a case label and we see the default label, note it in ctx->css_state. */ @@ -7123,6 +8157,11 @@ label_matches (const constexpr_ctx *ctx, tree *jump_target, tree stmt) breaks (jump_target) or continues (jump_target). */ break; + case VAR_DECL: + /* Uncaught exception. This is handled by TRY_BLOCK evaluation + and other places by testing throws (jump_target). */ + break; + default: gcc_unreachable (); } @@ -7137,15 +8176,9 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, tree *jump_target) { - tree local_target; /* In a statement-expression we want to return the last value. For empty statement expression return void_node. */ tree r = void_node; - if (!jump_target) - { - local_target = NULL_TREE; - jump_target = &local_target; - } for (tree_stmt_iterator i = tsi_start (t); !tsi_end_p (i); ++i) { tree stmt = *i; @@ -7173,18 +8206,11 @@ cxx_eval_statement_list (const constexpr_ctx *ctx, tree t, jump_target); if (*non_constant_p) break; - if (returns (jump_target) || breaks (jump_target)) + if (returns (jump_target) + || breaks (jump_target) + || throws (jump_target)) break; } - if (*jump_target && jump_target == &local_target) - { - /* We aren't communicating the jump to our caller, so give up. We don't - need to support evaluation of jumps out of statement-exprs. */ - if (!ctx->quiet) - error_at (cp_expr_loc_or_input_loc (r), - "statement is not a constant expression"); - *non_constant_p = true; - } return r; } @@ -7196,13 +8222,6 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, bool *non_constant_p, bool *overflow_p, tree *jump_target) { - tree local_target; - if (!jump_target) - { - local_target = NULL_TREE; - jump_target = &local_target; - } - tree body, cond = NULL_TREE, expr = NULL_TREE; tree cond_prep = NULL_TREE, cond_cleanup = NULL_TREE; unsigned cond_cleanup_depth = 0; @@ -7258,7 +8277,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, tree c; FOR_EACH_VEC_ELT_REVERSE (cleanups, i, c) cxx_eval_constant_expression (ctx, c, vc_discard, non_constant_p, - overflow_p); + overflow_p, jump_target); } if (cond_prep) for (tree decl = BIND_EXPR_VARS (cond_prep); @@ -7353,7 +8372,8 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, if (*non_constant_p || returns (jump_target) || breaks (jump_target) - || continues (jump_target)) + || continues (jump_target) + || throws (jump_target)) { depth = 1; break; @@ -7400,6 +8420,7 @@ cxx_eval_loop_expr (const constexpr_ctx *ctx, tree t, && !breaks (jump_target) && !continues (jump_target) && (!switches (jump_target) || count == 0) + && !throws (jump_target) && !*non_constant_p); cleanup_cond (); @@ -7418,7 +8439,10 @@ cxx_eval_switch_expr (const constexpr_ctx *ctx, tree t, tree cond = TREE_CODE (t) == SWITCH_STMT ? SWITCH_STMT_COND (t) : SWITCH_COND (t); cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (cond); if (TREE_CODE (cond) != INTEGER_CST) { @@ -7551,7 +8575,8 @@ maybe_warn_about_constant_value (location_t loc, tree decl) static tree build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type, tree cookie_size, tree full_size, tree arg_size, - bool *non_constant_p, bool *overflow_p) + bool *non_constant_p, bool *overflow_p, + tree *jump_target) { gcc_assert (cookie_size == NULL_TREE || tree_fits_uhwi_p (cookie_size)); gcc_assert (tree_fits_uhwi_p (full_size)); @@ -7587,13 +8612,17 @@ build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type, if (integer_zerop (op0)) arg_size = cxx_eval_constant_expression (ctx, op1, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); else if (integer_zerop (op1)) arg_size = cxx_eval_constant_expression (ctx, op0, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); else arg_size = NULL_TREE; + if (*jump_target) + return NULL_TREE; } else arg_size = NULL_TREE; @@ -7614,6 +8643,38 @@ build_new_constexpr_heap_type (const constexpr_ctx *ctx, tree elt_type, return build_new_constexpr_heap_type (elt_type, cookie_size, itype2); } +/* Handle the case when a cleanup of some expression throws. JMP_TARGET + indicates whether the cleanup threw or not, *JUMP_TARGET indicates whether + the expression which needed the cleanup threw. If both threw, diagnose + it and return NULL, otherwise return R. If only the cleanup threw, set + *JUMP_TARGET to the exception object from the cleanup. */ + +static tree +merge_jump_target (location_t loc, const constexpr_ctx *ctx, tree r, + bool *non_constant_p, tree *jump_target, tree jmp_target) +{ + if (!throws (&jmp_target)) + return r; + if (throws (jump_target)) + { + /* [except.throw]/9 - If the exception handling mechanism + handling an uncaught exception directly invokes a function + that exits via an exception, the function std::terminate is + invoked. */ + if (!ctx->quiet) + { + auto_diagnostic_group d; + diagnose_std_terminate (loc, ctx, *jump_target); + inform (loc, "destructor exited with an exception"); + } + *non_constant_p = true; + *jump_target = NULL_TREE; + return NULL_TREE; + } + *jump_target = jmp_target; + return r; +} + /* Attempt to reduce the expression T to a constant value. On failure, issue diagnostic and return error_mark_node. */ /* FIXME unify with c_fully_fold */ @@ -7623,9 +8684,9 @@ static tree cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, value_cat lval, bool *non_constant_p, bool *overflow_p, - tree *jump_target /* = NULL */) + tree *jump_target) { - if (jump_target && *jump_target) + if (*jump_target) { /* If we are jumping, ignore all statements/expressions except those that could have LABEL_EXPR or CASE_LABEL_EXPR in their bodies. */ @@ -7749,7 +8810,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = convert_from_reference (r); } return cxx_eval_constant_expression (ctx, r, lval, non_constant_p, - overflow_p); + overflow_p, jump_target); } /* fall through */ case CONST_DECL: @@ -7792,7 +8853,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = TARGET_EXPR_INITIAL (r); if (DECL_P (r) /* P2280 allows references to unknown. */ - && !(VAR_P (t) && TYPE_REF_P (TREE_TYPE (t)))) + && !(p2280_active_p (ctx) && VAR_P (t) && TYPE_REF_P (TREE_TYPE (t)))) { if (!ctx->quiet) non_const_var_error (loc, r, /*fundef_p*/false); @@ -7811,6 +8872,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case LABEL_EXPR: case CASE_LABEL_EXPR: case PREDICT_EXPR: + case OMP_DECLARE_MAPPER: return t; case PARM_DECL: @@ -7826,7 +8888,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = v; if (TREE_ADDRESSABLE (TREE_TYPE (t))) r = cxx_eval_constant_expression (ctx, r, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; } else if (lval) /* Defer in case this is only used for its type. */; @@ -7844,9 +8909,9 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = build_constructor (TREE_TYPE (t), NULL); TREE_CONSTANT (r) = true; } - else if (TYPE_REF_P (TREE_TYPE (t))) + else if (p2280_active_p (ctx) && TYPE_REF_P (TREE_TYPE (t))) /* P2280 allows references to unknown... */; - else if (is_this_parameter (t)) + else if (p2280_active_p (ctx) && is_this_parameter (t)) /* ...as well as the this pointer. */; else { @@ -7859,7 +8924,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case CALL_EXPR: case AGGR_INIT_EXPR: r = cxx_eval_call_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case DECL_EXPR: @@ -7923,7 +8988,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (tree init = DECL_INITIAL (r)) { init = cxx_eval_constant_expression (ctx, init, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't share a CONSTRUCTOR that might be changed. */ init = unshare_constructor (init); /* Remember that a constant object's constructor has already @@ -7982,14 +9050,23 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ctx->global->put_value (new_ctx.object, new_ctx.ctor); ctx = &new_ctx; } + + /* If the initializer is complex, evaluate it to initialize slot. */ + bool is_complex = target_expr_needs_replace (t); + if (is_complex) + /* In case no initialization actually happens, clear out any + void_node from a previous evaluation. */ + ctx->global->put_value (slot, NULL_TREE); + /* Pass vc_prvalue because this indicates initialization of a temporary. */ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); if (*non_constant_p) break; - /* If the initializer is complex, evaluate it to initialize slot. */ - bool is_complex = target_expr_needs_replace (t); + if (*jump_target) + return NULL_TREE; if (!is_complex) { r = unshare_constructor (r); @@ -7997,8 +9074,15 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = adjust_temp_type (type, r); ctx->global->put_value (slot, r); } - if (TARGET_EXPR_CLEANUP (t) && !CLEANUP_EH_ONLY (t)) - ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t)); + if (TARGET_EXPR_CLEANUP (t) + && (!CLEANUP_EH_ONLY (t) || cxx_dialect >= cxx26)) + { + ctx->global->cleanups->safe_push (TARGET_EXPR_CLEANUP (t)); + /* Mark CLEANUP_EH_ONLY cleanups by pushing NULL_TREE after + them. */ + if (CLEANUP_EH_ONLY (t)) + ctx->global->cleanups->safe_push (NULL_TREE); + } if (ctx->save_exprs) ctx->save_exprs->safe_push (slot); if (lval) @@ -8012,33 +9096,28 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MODIFY_EXPR: gcc_assert (jump_target == NULL || *jump_target == NULL_TREE); r = cxx_eval_store_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case SCOPE_REF: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case RETURN_EXPR: if (TREE_OPERAND (t, 0) != NULL_TREE) r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); - /* FALLTHRU */ + non_constant_p, overflow_p, + jump_target); + if (!throws (jump_target)) + *jump_target = t; + break; case BREAK_STMT: case CONTINUE_STMT: - if (jump_target) - *jump_target = t; - else - { - /* Can happen with ({ return true; }) && false; passed to - maybe_constant_value. There is nothing to jump over in this - case, and the bug will be diagnosed later. */ - gcc_assert (ctx->quiet); - *non_constant_p = true; - } + *jump_target = t; break; case SAVE_EXPR: @@ -8047,9 +9126,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, r = v; else { - r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_prvalue, - non_constant_p, overflow_p); - if (*non_constant_p) + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), + vc_prvalue, non_constant_p, + overflow_p, jump_target); + if (*non_constant_p || *jump_target) break; ctx->global->put_value (t, r); if (ctx->save_exprs) @@ -8057,16 +9137,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, } break; - case TRY_CATCH_EXPR: - if (TREE_OPERAND (t, 0) == NULL_TREE) - { - r = void_node; - break; - } - /* FALLTHRU */ case NON_LVALUE_EXPR: - case TRY_BLOCK: - case MUST_NOT_THROW_EXPR: case EXPR_STMT: case EH_SPEC_BLOCK: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), @@ -8075,6 +9146,42 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, jump_target); break; + case TRY_BLOCK: + r = cxx_eval_constant_expression (ctx, TRY_STMTS (t), lval, + non_constant_p, overflow_p, + jump_target); + if (!*non_constant_p && throws (jump_target)) + if (tree h = TRY_HANDLERS (t)) + { + tree type = strip_array_types (TREE_TYPE (*jump_target)); + if (TREE_CODE (h) == STATEMENT_LIST) + { + for (tree stmt : tsi_range (h)) + if (TREE_CODE (stmt) == HANDLER + && handler_match_for_exception_type (stmt, type)) + { + h = stmt; + break; + } + if (TREE_CODE (h) == STATEMENT_LIST) + h = NULL_TREE; + } + else if (TREE_CODE (h) != HANDLER + || !handler_match_for_exception_type (h, type)) + h = NULL_TREE; + if (h) + { + gcc_assert (VAR_P (*jump_target)); + ctx->global->caught_exceptions.safe_push (*jump_target); + ctx->global->caught_exceptions.safe_push (HANDLER_TYPE (h)); + *jump_target = NULL_TREE; + r = cxx_eval_constant_expression (ctx, HANDLER_BODY (h), + vc_discard, non_constant_p, + overflow_p, jump_target); + } + } + break; + case CLEANUP_POINT_EXPR: { auto_vec<tree, 2> cleanups; @@ -8092,47 +9199,132 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ctx->global->cleanups = prev_cleanups; unsigned int i; - tree cleanup; + tree cleanup, jmp_target = NULL_TREE; + bool eh = throws (jump_target); /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) - cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard, - non_constant_p, overflow_p); + if (cleanup == NULL_TREE) + { + /* NULL_TREE cleanup is a marker that before it is + CLEANUP_EH_ONLY cleanup. Skip the cleanup before it + if the body didn't throw. */ + if (!eh) + --i; + } + else + cxx_eval_constant_expression (&new_ctx, cleanup, vc_discard, + non_constant_p, overflow_p, + &jmp_target); /* Forget SAVE_EXPRs and TARGET_EXPRs created by this full-expression. */ for (tree save_expr : save_exprs) destroy_value_checked (ctx, save_expr, non_constant_p); + if (throws (&jmp_target)) + *jump_target = jmp_target; } break; + case MUST_NOT_THROW_EXPR: + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), + lval, + non_constant_p, overflow_p, + jump_target); + if (throws (jump_target)) + { + /* [except.handle]/7 - If the search for a handler exits the + function body of a function with a non-throwing exception + specification, the function std::terminate is invoked. */ + if (!ctx->quiet) + { + auto_diagnostic_group d; + diagnose_std_terminate (loc, ctx, *jump_target); + if (MUST_NOT_THROW_NOEXCEPT_P (t) + && ctx->call + && ctx->call->fundef) + inform (loc, "uncaught exception exited from %<noexcept%> " + "function %qD", + ctx->call->fundef->decl); + else if (MUST_NOT_THROW_THROW_P (t)) + inform (loc, "destructor exited with an exception after " + "initializing the exception object"); + else if (MUST_NOT_THROW_CATCH_P (t)) + inform (loc, "constructor exited with another exception while " + "entering handler"); + } + *non_constant_p = true; + *jump_target = NULL_TREE; + r = NULL_TREE; + } + break; + + case TRY_CATCH_EXPR: + if (TREE_OPERAND (t, 0) == NULL_TREE) + { + r = void_node; + break; + } + r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, + non_constant_p, overflow_p, + jump_target); + if (!*non_constant_p && throws (jump_target)) + { + tree jmp_target = NULL_TREE; + cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard, + non_constant_p, overflow_p, + &jmp_target); + r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target, + jmp_target); + } + break; + case TRY_FINALLY_EXPR: r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, non_constant_p, overflow_p, jump_target); if (!*non_constant_p) - /* Also evaluate the cleanup. */ - cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard, - non_constant_p, overflow_p); + { + tree jmp_target = NULL_TREE; + /* Also evaluate the cleanup. */ + if (TREE_CODE (TREE_OPERAND (t, 1)) == EH_ELSE_EXPR + && throws (jump_target)) + cxx_eval_constant_expression (ctx, + TREE_OPERAND (TREE_OPERAND (t, 1), + 1), vc_discard, + non_constant_p, overflow_p, + &jmp_target); + else + cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 1), vc_discard, + non_constant_p, overflow_p, + &jmp_target); + r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target, + jmp_target); + } break; case EH_ELSE_EXPR: /* Evaluate any cleanup that applies to non-EH exits. */ cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), vc_discard, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); - /* We do not have constexpr exceptions yet, so skip the EH path. */ + /* The EH path is handled in TRY_FINALLY_EXPR handling above. */ break; case CLEANUP_STMT: r = cxx_eval_constant_expression (ctx, CLEANUP_BODY (t), lval, non_constant_p, overflow_p, jump_target); - if (!CLEANUP_EH_ONLY (t) && !*non_constant_p) + if ((!CLEANUP_EH_ONLY (t) || throws (jump_target)) && !*non_constant_p) { iloc_sentinel ils (loc); + tree jmp_target = NULL_TREE; /* Also evaluate the cleanup. */ cxx_eval_constant_expression (ctx, CLEANUP_EXPR (t), vc_discard, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + &jmp_target); + r = merge_jump_target (loc, ctx, r, non_constant_p, jump_target, + jmp_target); } break; @@ -8142,14 +9334,18 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case MEM_REF: case INDIRECT_REF: r = cxx_eval_indirect_ref (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case ADDR_EXPR: { tree oldop = TREE_OPERAND (t, 0); tree op = cxx_eval_constant_expression (ctx, oldop, vc_glvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; /* Don't VERIFY_CONSTANT here. */ if (*non_constant_p) return t; @@ -8169,7 +9365,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (lval) { r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (r == error_mark_node) ; else if (r == TREE_OPERAND (t, 0) || lval == vc_discard) @@ -8190,7 +9389,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case FIXED_CONVERT_EXPR: case VEC_DUPLICATE_EXPR: r = cxx_eval_unary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case SIZEOF_EXPR: @@ -8228,6 +9428,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, cxx_eval_constant_expression (ctx, op0, vc_discard, non_constant_p, overflow_p, jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; op1 = TREE_OPERAND (t, 1); @@ -8280,7 +9482,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case RANGE_EXPR: case COMPLEX_EXPR: r = cxx_eval_binary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; /* fold can introduce non-IF versions of these; still treat them as @@ -8289,19 +9492,22 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case TRUTH_ANDIF_EXPR: r = cxx_eval_logical_expression (ctx, t, boolean_false_node, boolean_true_node, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case TRUTH_OR_EXPR: case TRUTH_ORIF_EXPR: r = cxx_eval_logical_expression (ctx, t, boolean_true_node, boolean_false_node, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case ARRAY_REF: r = cxx_eval_array_reference (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case COMPONENT_REF: @@ -8316,17 +9522,19 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return t; } r = cxx_eval_component_reference (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case BIT_FIELD_REF: r = cxx_eval_bit_field_ref (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case COND_EXPR: case IF_STMT: - if (jump_target && *jump_target) + if (*jump_target) { tree orig_jump = *jump_target; tree arg = ((TREE_CODE (t) != IF_STMT || TREE_OPERAND (t, 1)) @@ -8364,7 +9572,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, break; case VEC_COND_EXPR: r = cxx_eval_vector_conditional_expression (ctx, t, non_constant_p, - overflow_p); + overflow_p, jump_target); break; case CONSTRUCTOR: @@ -8376,7 +9584,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return t; } r = cxx_eval_bare_aggregate (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case VEC_INIT_EXPR: @@ -8386,12 +9594,13 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, or xvalue of the same type, meaning direct-initialization from the corresponding member. */ r = cxx_eval_vec_init (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, jump_target); break; case VEC_PERM_EXPR: r = cxx_eval_trinary_expression (ctx, t, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case PAREN_EXPR: @@ -8399,7 +9608,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, /* A PAREN_EXPR resulting from __builtin_assoc_barrier has no effect in constant expressions since it's unaffected by -fassociative-math. */ r = cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); break; case NOP_EXPR: @@ -8423,7 +9633,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, ? vc_discard : tcode == VIEW_CONVERT_EXPR ? lval : vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; tree type = TREE_TYPE (t); @@ -8434,7 +9647,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (TREE_CODE (t) == CONVERT_EXPR && ARITHMETIC_TYPE_P (type) && INDIRECT_TYPE_P (TREE_TYPE (op)) - && ctx->manifestly_const_eval == mce_true) + && ctx->strict) { if (!ctx->quiet) error_at (loc, @@ -8480,7 +9693,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, { if (integer_zerop (sop)) return build_int_cst (type, 0); - r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop); + r = cxx_fold_indirect_ref (ctx, loc, TREE_TYPE (type), sop, + NULL, jump_target); + if (*jump_target) + return NULL_TREE; if (r) { r = build1 (ADDR_EXPR, type, r); @@ -8586,7 +9802,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, tree cookie_size = NULL_TREE; tree arg_size = NULL_TREE; if (TREE_CODE (elt_type) == RECORD_TYPE - && TYPE_NAME (elt_type) == heap_identifier) + && TYPE_IDENTIFIER (elt_type) == heap_identifier) { tree fld1 = TYPE_FIELDS (elt_type); tree fld2 = DECL_CHAIN (fld1); @@ -8607,10 +9823,14 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, if (cxx_replaceable_global_alloc_fn (fun) && IDENTIFIER_NEW_OP_P (DECL_NAME (fun))) arg_size = CALL_EXPR_ARG (oldop, 0); - TREE_TYPE (var) + tree new_type = build_new_constexpr_heap_type (ctx, elt_type, cookie_size, var_size, arg_size, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; + TREE_TYPE (var) = new_type; TREE_TYPE (TREE_OPERAND (op, 0)) = build_pointer_type (TREE_TYPE (var)); } @@ -8649,7 +9869,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, tree op = cxx_eval_constant_expression (ctx, oldop, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; if (*non_constant_p) return t; r = fold_convert (TREE_TYPE (t), op); @@ -8686,14 +9909,20 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case PREDECREMENT_EXPR: case POSTDECREMENT_EXPR: return cxx_eval_increment_expression (ctx, t, - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); + case THROW_EXPR: + if (cxx_dialect >= cxx26) + return cxx_eval_constant_expression (ctx, TREE_OPERAND (t, 0), lval, + non_constant_p, overflow_p, + jump_target); + /* FALLTHROUGH */ case LAMBDA_EXPR: case NEW_EXPR: case VEC_NEW_EXPR: case DELETE_EXPR: case VEC_DELETE_EXPR: - case THROW_EXPR: case MODOP_EXPR: /* GCC internal stuff. */ case VA_ARG_EXPR: @@ -8707,7 +9936,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, case OBJ_TYPE_REF: /* Virtual function lookup. We don't need to do anything fancy. */ return cxx_eval_constant_expression (ctx, OBJ_TYPE_REF_EXPR (t), - lval, non_constant_p, overflow_p); + lval, non_constant_p, overflow_p, + jump_target); case PLACEHOLDER_EXPR: /* Use of the value or address of the current object. */ @@ -8717,7 +9947,8 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, return ctor; else return cxx_eval_constant_expression (ctx, ctor, lval, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); } /* A placeholder without a referent. We can get here when checking whether NSDMIs are noexcept, or in massage_init_elt; @@ -8730,7 +9961,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, { tree cond = TREE_OPERAND (t, 0); cond = cxx_eval_constant_expression (ctx, cond, vc_prvalue, - non_constant_p, overflow_p); + non_constant_p, overflow_p, + jump_target); + if (*jump_target) + return NULL_TREE; VERIFY_CONSTANT (cond); if (integer_nonzerop (cond)) *jump_target = t; @@ -8842,7 +10076,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t, *non_constant_p = true; return t; } - r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p); + r = cxx_eval_bit_cast (ctx, t, non_constant_p, overflow_p, jump_target); break; case OMP_PARALLEL: @@ -8981,20 +10215,6 @@ find_heap_var_refs (tree *tp, int *walk_subtrees, void */*data*/) return NULL_TREE; } -/* Look for deleted heap variables in the expression *TP. */ - -static tree -find_deleted_heap_var (tree *tp, int *walk_subtrees, void */*data*/) -{ - if (VAR_P (*tp) - && DECL_NAME (*tp) == heap_deleted_identifier) - return *tp; - - if (TYPE_P (*tp)) - *walk_subtrees = 0; - return NULL_TREE; -} - /* Find immediate function decls in *TP if any. */ static tree @@ -9100,6 +10320,15 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, tree fndecl = cp_get_callee_fndecl_nofold (x); if (fndecl && DECL_IMMEDIATE_FUNCTION_P (fndecl)) is_consteval = true; + /* Don't try to evaluate a std::vector constructor taking an integer, it + will fail in the 'if (heap_var)' block below after doing all the work + (c++/113835). This will need adjustment if P3554 is accepted. Note + that evaluation of e.g. the vector default constructor can succeed, so + we don't shortcut all vector constructors. */ + if (fndecl && DECL_CONSTRUCTOR_P (fndecl) && allow_non_constant + && is_std_class (type, "vector") && call_expr_nargs (x) > 1 + && TREE_CODE (TREE_TYPE (get_nth_callarg (x, 1))) == INTEGER_TYPE) + return t; } if (AGGREGATE_TYPE_P (type) || VECTOR_TYPE_P (type)) { @@ -9166,30 +10395,67 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (manifestly_const_eval == mce_true) instantiate_constexpr_fns (r); + tree jmp_target = NULL_TREE; r = cxx_eval_constant_expression (&ctx, r, vc_prvalue, - &non_constant_p, &overflow_p); + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) && !non_constant_p) + { + if (!ctx.quiet) + diagnose_uncaught_exception (input_location, &ctx, jmp_target); + non_constant_p = true; + jmp_target = NULL_TREE; + r = t; + } + else if (!non_constant_p && jmp_target) + { + non_constant_p = true; + if (!ctx.quiet) + { + if (breaks (&jmp_target)) + error ("%<break%> outside of a loop or %<switch%>"); + else if (continues (&jmp_target)) + error ("%<continue%> outside of a loop"); + else if (returns (&jmp_target)) + error ("%<return%> in a statement expression"); + else + gcc_unreachable (); + } + r = t; + } /* If we got a non-simple TARGET_EXPR, the initializer was a sequence of statements, and the result ought to be stored in ctx.ctor. */ if (r == void_node && !constexpr_dtor && ctx.ctor) r = ctx.ctor; - if (!constexpr_dtor) - verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); - else - DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true; - unsigned int i; tree cleanup; + jmp_target = NULL_TREE; /* Evaluate the cleanups. */ FOR_EACH_VEC_ELT_REVERSE (cleanups, i, cleanup) - cxx_eval_constant_expression (&ctx, cleanup, vc_discard, - &non_constant_p, &overflow_p); + if (cleanup == NULL_TREE) + /* NULL_TREE cleanup is a marker that before it is + CLEANUP_EH_ONLY cleanup. Skip the cleanup before it. */ + --i; + else + cxx_eval_constant_expression (&ctx, cleanup, vc_discard, + &non_constant_p, &overflow_p, + &jmp_target); + if (throws (&jmp_target) && !non_constant_p) + { + if (!ctx.quiet) + diagnose_uncaught_exception (input_location, &ctx, jmp_target); + non_constant_p = true; + r = t; + } /* Mutable logic is a bit tricky: we want to allow initialization of constexpr variables with mutable members, but we can't copy those members to another constexpr variable. */ - if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_MUTABLE_POISON (r)) + if (!non_constant_p + && TREE_CODE (r) == CONSTRUCTOR + && CONSTRUCTOR_MUTABLE_POISON (r)) { if (!allow_non_constant) error ("%qE is not a constant expression because it refers to " @@ -9197,15 +10463,6 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, non_constant_p = true; } - if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r)) - { - if (!allow_non_constant) - error ("%qE is not a constant expression because it refers to " - "an incompletely initialized variable", t); - TREE_CONSTANT (r) = false; - non_constant_p = true; - } - if (!non_constant_p && cxx_dialect >= cxx20 && !global_ctx.heap_vars.is_empty ()) { @@ -9215,9 +10472,16 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (heap_var) { if (!allow_non_constant && !non_constant_p) - error_at (DECL_SOURCE_LOCATION (heap_var), - "%qE is not a constant expression because it refers to " - "a result of %<operator new%>", t); + { + if (DECL_LANG_SPECIFIC (heap_var)) + error ("%qE is not a constant expression because it refers to " + "exception object allocated with " + "%<__cxa_allocate_exception%>", t); + else + error ("%qE is not a constant expression because it refers to " + "a result of %<operator new%>", t); + inform (DECL_SOURCE_LOCATION (heap_var), "allocated here"); + } r = t; non_constant_p = true; } @@ -9226,13 +10490,14 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (DECL_NAME (heap_var) != heap_deleted_identifier) { if (!allow_non_constant && !non_constant_p) - error_at (DECL_SOURCE_LOCATION (heap_var), - "%qE is not a constant expression because allocated " - "storage has not been deallocated", t); + { + error ("%qE is not a constant expression because allocated " + "storage has not been deallocated", t); + inform (DECL_SOURCE_LOCATION (heap_var), "allocated here"); + } r = t; non_constant_p = true; } - varpool_node::get (heap_var)->remove (); } } @@ -9258,6 +10523,20 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, non_constant_p = true; } + if (!non_constant_p && !constexpr_dtor) + verify_constant (r, allow_non_constant, &non_constant_p, &overflow_p); + + /* After verify_constant because reduced_constant_expression_p can unset + CONSTRUCTOR_NO_CLEARING. */ + if (TREE_CODE (r) == CONSTRUCTOR && CONSTRUCTOR_NO_CLEARING (r)) + { + if (!allow_non_constant) + error ("%qE is not a constant expression because it refers to " + "an incompletely initialized variable", t); + TREE_CONSTANT (r) = false; + non_constant_p = true; + } + if (non_constant_p) /* If we saw something bad, go back to our argument. The wrapping below is only for the cases of TREE_CONSTANT argument or overflow. */ @@ -9274,13 +10553,17 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (non_constant_p && !allow_non_constant) return error_mark_node; - else if (constexpr_dtor) - return r; else if (non_constant_p && TREE_CONSTANT (r)) r = mark_non_constant (r); else if (non_constant_p) return t; + if (constexpr_dtor) + { + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (object) = true; + return r; + } + /* Check we are not trying to return the wrong type. */ if (!same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (r))) { @@ -9300,7 +10583,8 @@ cxx_eval_outermost_constant_expr (tree t, bool allow_non_constant, if (TREE_CODE (t) == TARGET_EXPR && TARGET_EXPR_INITIAL (t) == r) return t; - else if (TREE_CODE (t) == CONSTRUCTOR || TREE_CODE (t) == CALL_EXPR) + else if (TREE_CODE (t) == CONSTRUCTOR || TREE_CODE (t) == CALL_EXPR + || TREE_CODE (t) == AGGR_INIT_EXPR) /* Don't add a TARGET_EXPR if our argument didn't have one. */; else if (TREE_CODE (t) == TARGET_EXPR && TARGET_EXPR_CLEANUP (t)) r = get_target_expr (r); @@ -9431,6 +10715,9 @@ fold_simple (tree t) tree fold_to_constant (tree t) { + if (processing_template_decl) + return t; + tree r = fold (t); if (CONSTANT_CLASS_P (r) && !TREE_OVERFLOW (r)) return r; @@ -9450,8 +10737,35 @@ tree maybe_constant_value (tree t, tree decl /* = NULL_TREE */, mce_value manifestly_const_eval /* = mce_unknown */) { + tree orig_t = t; tree r; + if (EXPR_P (t) && manifestly_const_eval == mce_unknown) + { + /* Look up each operand in the cv_cache first to see if we've already + reduced it, and reuse that result to avoid quadratic behavior if + we're called when building up a large expression. */ + int n = cp_tree_operand_length (t); + tree *ops = XALLOCAVEC (tree, n); + bool rebuild = false; + for (int i = 0; i < n; ++i) + { + ops[i] = TREE_OPERAND (t, i); + if (tree *cached = hash_map_safe_get (cv_cache, ops[i])) + if (*cached != ops[i]) + { + ops[i] = *cached; + rebuild = true; + } + } + if (rebuild) + { + t = copy_node (t); + for (int i = 0; i < n; ++i) + TREE_OPERAND (t, i) = ops[i]; + } + } + if (!is_nondependent_constant_expression (t)) { if (TREE_OVERFLOW_P (t) @@ -9469,6 +10783,10 @@ maybe_constant_value (tree t, tree decl /* = NULL_TREE */, return fold_to_constant (t); if (manifestly_const_eval != mce_unknown) + /* TODO: Extend the cache to be mce_value aware. And if we have a + previously cached mce_unknown result that's TREE_CONSTANT, it means + the reduced value is independent of mce_value and so we should + be able to reuse it in the mce_true/false case. */ return cxx_eval_outermost_constant_expr (t, true, true, manifestly_const_eval, false, decl); @@ -9498,7 +10816,7 @@ maybe_constant_value (tree t, tree decl /* = NULL_TREE */, || (TREE_CONSTANT (t) && !TREE_CONSTANT (r)) || !cp_tree_equal (r, t)); if (!c.evaluation_restricted_p ()) - cv_cache->put (t, r); + cv_cache->put (orig_t, r); return r; } @@ -9662,7 +10980,7 @@ fold_non_dependent_init (tree t, static tree maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant, - bool manifestly_const_eval) + mce_value manifestly_const_eval) { if (!t) return t; @@ -9688,17 +11006,27 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant, { /* [basic.start.static] allows constant-initialization of variables with static or thread storage duration even if it isn't required, but we - shouldn't bend the rules the same way for automatic variables. */ + shouldn't bend the rules the same way for automatic variables. + + But still enforce the requirements of constexpr/constinit. + [dcl.constinit] "If a variable declared with the constinit specifier + has dynamic initialization, the program is ill-formed, even if the + implementation would perform that initialization as a static + initialization." */ bool is_static = (decl && DECL_P (decl) && (TREE_STATIC (decl) || DECL_EXTERNAL (decl))); + bool strict = (!is_static + || (decl && DECL_P (decl) + && (DECL_DECLARED_CONSTEXPR_P (decl) + || DECL_DECLARED_CONSTINIT_P (decl)))); if (is_static) - manifestly_const_eval = true; + manifestly_const_eval = mce_true; - if (cp_unevaluated_operand && !manifestly_const_eval) + if (cp_unevaluated_operand && manifestly_const_eval != mce_true) return fold_to_constant (t); - t = cxx_eval_outermost_constant_expr (t, allow_non_constant, !is_static, - mce_value (manifestly_const_eval), + t = cxx_eval_outermost_constant_expr (t, allow_non_constant, strict, + manifestly_const_eval, false, decl); } if (TREE_CODE (t) == TARGET_EXPR) @@ -9715,6 +11043,12 @@ maybe_constant_init_1 (tree t, tree decl, bool allow_non_constant, tree maybe_constant_init (tree t, tree decl, bool manifestly_const_eval) { + return maybe_constant_init_1 (t, decl, true, mce_value (manifestly_const_eval)); +} + +tree +maybe_constant_init (tree t, tree decl, mce_value manifestly_const_eval) +{ return maybe_constant_init_1 (t, decl, true, manifestly_const_eval); } @@ -9723,7 +11057,25 @@ maybe_constant_init (tree t, tree decl, bool manifestly_const_eval) tree cxx_constant_init (tree t, tree decl) { - return maybe_constant_init_1 (t, decl, false, true); + return maybe_constant_init_1 (t, decl, false, mce_true); +} + +/* Return true if CALL_EXPR T might throw during constant evaluation. */ + +static bool +callee_might_throw (tree t) +{ + if (cxx_dialect < cxx26 || !flag_exceptions) + return false; + tree callee = cp_get_callee (t); + if (callee == NULL_TREE) + return false; + tree callee_fn = cp_get_fndecl_from_callee (callee, false); + return (!flag_enforce_eh_specs + || type_dependent_expression_p (callee) + || !POINTER_TYPE_P (TREE_TYPE (callee)) + || (!type_noexcept_p (TREE_TYPE (TREE_TYPE (callee))) + && (callee_fn == NULL_TREE || !TREE_NOTHROW (callee_fn)))); } #if 0 @@ -9758,11 +11110,13 @@ struct check_for_return_continue_data { hash_set<tree> *pset; tree continue_stmt; tree break_stmt; + bool could_throw; }; /* Helper function for potential_constant_expression_1 SWITCH_STMT handling, called through cp_walk_tree. Return the first RETURN_EXPR found, or note - the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. */ + the first CONTINUE_STMT and/or BREAK_STMT if RETURN_EXPR is not found. + For C++26 also note presence of possibly throwing calls. */ static tree check_for_return_continue (tree *tp, int *walk_subtrees, void *data) { @@ -9847,6 +11201,13 @@ check_for_return_continue (tree *tp, int *walk_subtrees, void *data) case CONSTRUCTOR: break; + case AGGR_INIT_EXPR: + case CALL_EXPR: + /* In C++26 a function could throw. */ + if (callee_might_throw (t)) + d->could_throw = true; + break; + default: if (!EXPR_P (t)) *walk_subtrees = 0; @@ -10052,8 +11413,27 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, || TREE_CODE (t) != CALL_EXPR || current_function_decl == NULL_TREE || !is_std_construct_at (current_function_decl)) - && !cxx_dynamic_cast_fn_p (fun)) + && !cxx_dynamic_cast_fn_p (fun) + && !cxx_cxa_builtin_fn_p (fun)) { + /* In C++26 evaluation of the function arguments might + throw and in that case it is irrelevant whether + fun is constexpr or not. */ + if (cxx_dialect >= cxx26) + for (; i < nargs; ++i) + { + tree x = get_nth_callarg (t, i); + bool rv = processing_template_decl ? any : rval; + bool sub_now = false; + if (!potential_constant_expression_1 (x, rv, strict, + sub_now, + fundef_p, + flags, + jump_target)) + return false; + if (throws (jump_target)) + return true; + } if ((flags & tf_error) && constexpr_error (loc, fundef_p, "call to non-%<constexpr%> " @@ -10098,7 +11478,12 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, sub_now, fundef_p, flags, jump_target)) return false; + if (throws (jump_target)) + return true; } + /* In C++26 a function could throw. */ + if (*jump_target == NULL_TREE && callee_might_throw (t)) + *jump_target = void_node; return true; } @@ -10263,22 +11648,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, return true; case INDIRECT_REF: - { - tree x = TREE_OPERAND (t, 0); - STRIP_NOPS (x); - if (is_this_parameter (x) && !is_capture_proxy (x)) - { - if (now || !var_in_maybe_constexpr_fn (x)) - { - if (flags & tf_error) - constexpr_error (loc, fundef_p, "use of %<this%> in a " - "constant expression"); - return false; - } - return true; - } - return RECUR (x, rval); - } + return RECUR (TREE_OPERAND (t, 0), rval); case STATEMENT_LIST: for (tree stmt : tsi_range (t)) @@ -10336,11 +11706,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, a return. */ hash_set<tree> pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, false }; if (tree ret_expr = cp_walk_tree (&FOR_BODY (t), check_for_return_continue, &data, &pset)) *jump_target = ret_expr; + if (data.could_throw) + *jump_target = void_node; return true; } } @@ -10380,11 +11752,13 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, a return. */ hash_set<tree> pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, false }; if (tree ret_expr = cp_walk_tree (&WHILE_BODY (t), check_for_return_continue, &data, &pset)) *jump_target = ret_expr; + if (data.could_throw) + *jump_target = void_node; return true; } if (!RECUR (WHILE_BODY (t), any)) @@ -10408,7 +11782,7 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, { hash_set<tree> pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, false }; if (tree ret_expr = cp_walk_tree (&SWITCH_STMT_BODY (t), check_for_return_continue, &data, &pset)) @@ -10417,6 +11791,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, else if (data.continue_stmt) /* The switch can't return, but might continue. */ *jump_target = data.continue_stmt; + if (data.could_throw) + *jump_target = void_node; } return true; @@ -10446,7 +11822,6 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case DYNAMIC_CAST_EXPR: case PSEUDO_DTOR_EXPR: - case THROW_EXPR: case OMP_PARALLEL: case OMP_TASK: case OMP_FOR: @@ -10497,6 +11872,16 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, "expression", t); return false; + case OMP_DECLARE_MAPPER: + /* This can be used to initialize VAR_DECLs: it's treated as a magic + constant. */ + return true; + + case THROW_EXPR: + if (cxx_dialect < cxx26) + goto fail; + return RECUR (TREE_OPERAND (t, 0), rval); + case ASM_EXPR: if (flags & tf_error) inline_asm_in_constexpr_error (loc, fundef_p); @@ -10625,6 +12010,22 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case CLEANUP_POINT_EXPR: case MUST_NOT_THROW_EXPR: case TRY_CATCH_EXPR: + /* Even for C++26 handle TRY_BLOCK conservatively, if we detect the + body could throw, even with catch (...) among handlers we'd need + to analyze them in detail if they couldn't rethrow it. More + importantly though, throws (jump_target) is just conservative, + and there could be e.g. + try + { + possibly_throwing_fn (args); + break; + } + catch (...) + { + } + or continue or return instead of break. So, clearing *jump_target + because we see catch (...) handler might mean we missed break + etc. */ case TRY_BLOCK: case EH_SPEC_BLOCK: case EXPR_STMT: @@ -10866,9 +12267,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, want_rval, strict, now, fundef_p, tf_none, &this_jump_target)) { - if (returns (&this_jump_target)) + if (returns (&this_jump_target) || throws (&this_jump_target)) *jump_target = this_jump_target; - else if (!returns (jump_target)) + else if (!returns (jump_target) && !throws (jump_target)) { if (breaks (&this_jump_target) || continues (&this_jump_target)) @@ -10880,7 +12281,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, couldn't return, break or continue. */ hash_set<tree> pset; check_for_return_continue_data data = { &pset, NULL_TREE, - NULL_TREE }; + NULL_TREE, + false }; if (tree ret_expr = cp_walk_tree (&TREE_OPERAND (t, 2), check_for_return_continue, &data, @@ -10893,6 +12295,8 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, else if (data.break_stmt) *jump_target = data.break_stmt; } + if (data.could_throw) + *jump_target = void_node; } } return true; @@ -10944,6 +12348,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, *jump_target = *target; return true; } + if (DECL_ARTIFICIAL (*target)) + /* The user didn't write this goto, this isn't the problem. */ + return true; if (flags & tf_error) constexpr_error (loc, fundef_p, "%<goto%> is not a constant " "expression"); @@ -10977,6 +12384,9 @@ potential_constant_expression_1 (tree t, bool want_rval, bool strict, bool now, case CO_AWAIT_EXPR: case CO_YIELD_EXPR: case CO_RETURN_EXPR: + if (flags & tf_error) + constexpr_error (cp_expr_loc_or_loc (t, input_location), fundef_p, + "%qE is not a constant expression", t); return false; /* Assume a TU-local entity is not constant, we'll error later when diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index a9caba8..c8eef24 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -354,7 +354,7 @@ struct norm_info : subst_info /* Construct a top-level context for DECL. */ norm_info (tree in_decl, bool diag) - : subst_info (tf_warning_or_error, in_decl), + : subst_info (tf_warning_or_error|tf_partial, in_decl), generate_diagnostics (diag) { if (in_decl) @@ -1836,7 +1836,7 @@ tsubst_parameter_mapping (tree map, tree args, tsubst_flags_t complain, tree in_ static bool satisfying_constraint; /* A vector of incomplete types (and of declarations with undeduced return type), - appended to by note_failed_type_completion_for_satisfaction. The + appended to by note_failed_type_completion. The satisfaction caches use this in order to keep track of "potentially unstable" satisfaction results. @@ -1845,21 +1845,69 @@ static bool satisfying_constraint; static GTY((deletable)) vec<tree, va_gc> *failed_type_completions; +/* A map of where types were found to be incomplete in SFINAE context, for + warning if they are later completed. */ + +static GTY((cache)) hash_map<tree, location_t, decl_location_traits> *failed_completions_map; + /* Called whenever a type completion (or return type deduction) failure occurs that definitely affects the meaning of the program, by e.g. inducing substitution failure. */ void -note_failed_type_completion_for_satisfaction (tree t) +note_failed_type_completion (tree t, tsubst_flags_t complain) { + if (dependent_template_arg_p (t)) + return; + + gcc_checking_assert ((TYPE_P (t) && !COMPLETE_TYPE_P (t)) + || (DECL_P (t) && undeduced_auto_decl (t))); + if (satisfying_constraint) + vec_safe_push (failed_type_completions, t); + + if (TYPE_P (t)) { - gcc_checking_assert ((TYPE_P (t) && !COMPLETE_TYPE_P (t)) - || (DECL_P (t) && undeduced_auto_decl (t))); - vec_safe_push (failed_type_completions, t); + if (!CLASS_TYPE_P (t)) + return; + t = TYPE_MAIN_DECL (t); + } + if (!(complain & tf_error) + && warning_enabled_at (DECL_SOURCE_LOCATION (t), + OPT_Wsfinae_incomplete_)) + { + if (warn_sfinae_incomplete > 1) + { + if (TREE_CODE (t) == TYPE_DECL) + warning (OPT_Wsfinae_incomplete_, + "failed to complete %qT in SFINAE context", TREE_TYPE (t)); + else + warning (OPT_Wsfinae_incomplete_, + "failed to deduce %qD in SFINAE context", t); + } + if (!failed_completions_map) + failed_completions_map + = hash_map<tree, location_t, decl_location_traits>::create_ggc (); + failed_completions_map->put (t, input_location); } } +/* If T was previously found to be incomplete in SFINAE context, return the + location where that happened, otherwise UNKNOWN_LOCATION. */ + +location_t +failed_completion_location (tree t) +{ + if (failed_completions_map) + { + if (TYPE_P (t)) + t = TYPE_MAIN_DECL (t); + if (location_t *p = failed_completions_map->get (t)) + return *p; + } + return UNKNOWN_LOCATION; +} + /* Returns true if the range [BEGIN, END) of elements within the failed_type_completions vector contains a complete type (or a declaration with a non-placeholder return type). */ @@ -2704,6 +2752,8 @@ satisfy_declaration_constraints (tree t, sat_info info) static tree satisfy_declaration_constraints (tree t, tree args, sat_info info) { + tree orig_args = args; + /* Update the declaration for diagnostics. */ info.in_decl = t; @@ -2732,7 +2782,7 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info) tree result = boolean_true_node; if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ())) { - if (!push_tinst_level (t, args)) + if (!push_tinst_level (t, orig_args)) return result; tree pattern = DECL_TEMPLATE_RESULT (t); push_to_top_level (); @@ -3098,6 +3148,9 @@ diagnose_trait_expr (tree expr, tree args) case CPTK_IS_CONVERTIBLE: inform (loc, " %qT is not convertible from %qE", t2, t1); break; + case CPTK_IS_DESTRUCTIBLE: + inform (loc, " %qT is not destructible", t1); + break; case CPTK_IS_EMPTY: inform (loc, " %qT is not an empty class", t1); break; @@ -3143,6 +3196,9 @@ diagnose_trait_expr (tree expr, tree args) case CPTK_IS_NOTHROW_CONVERTIBLE: inform (loc, " %qT is not nothrow convertible from %qE", t2, t1); break; + case CPTK_IS_NOTHROW_DESTRUCTIBLE: + inform (loc, " %qT is not nothrow destructible", t1); + break; case CPTK_IS_NOTHROW_INVOCABLE: if (!t2) inform (loc, " %qT is not nothrow invocable", t1); @@ -3192,6 +3248,9 @@ diagnose_trait_expr (tree expr, tree args) case CPTK_IS_TRIVIALLY_COPYABLE: inform (loc, " %qT is not trivially copyable", t1); break; + case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: + inform (loc, " %qT is not trivially destructible", t1); + break; case CPTK_IS_UNBOUNDED_ARRAY: inform (loc, " %qT is not an unbounded array", t1); break; @@ -3207,6 +3266,9 @@ diagnose_trait_expr (tree expr, tree args) case CPTK_RANK: inform (loc, " %qT cannot yield a rank", t1); break; + case CPTK_TYPE_ORDER: + inform (loc, " %qT and %qT cannot be ordered", t1, t2); + break; case CPTK_REF_CONSTRUCTS_FROM_TEMPORARY: inform (loc, " %qT is not a reference that binds to a temporary " "object of type %qT (direct-initialization)", t1, t2); diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index f2b126c..d0cfd2e 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -860,10 +860,17 @@ cp_contract_assertion_p (const_tree attr) void remove_contract_attributes (tree fndecl) { + if (!flag_contracts) + return; + tree list = NULL_TREE; for (tree p = DECL_ATTRIBUTES (fndecl); p; p = TREE_CHAIN (p)) if (!cxx_contract_attribute_p (p)) - list = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p), list); + { + tree nl = copy_node (p); + TREE_CHAIN (nl) = list; + list = nl; + } DECL_ATTRIBUTES (fndecl) = nreverse (list); } diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc index b92d09f..52cc186 100644 --- a/gcc/cp/coroutines.cc +++ b/gcc/cp/coroutines.cc @@ -191,8 +191,87 @@ static bool coro_promise_type_found_p (tree, location_t); just syntactic sugar for a co_await). We defer the analysis and transformation until template expansion is - complete so that we have complete types at that time. */ + complete so that we have complete types at that time. + --------------------------------------------------------------------------- + + Coroutine state, and responsibility for its release. + + As noted above, a coroutine has some state that persists across suspensions. + + The state has two components: + * State that is specified by the standard and persists for the entire + life of the coroutine. + * Local state that is constructed/destructed as scopes in the original + function body are entered/exited. The destruction of local state is + always the responsibility of the body code. + + The persistent state (and the overall storage for the state) must be + managed in two places: + * The ramp function (which allocates and builds this - and can, in some + cases, be responsible for destroying it) + * The re-written function body which can destroy it when that body + completes its final suspend - or when the handle.destroy () is called. + + In all cases the ramp holds responsibility for constructing the standard- + mandated persistent state. + + There are four ways in which the ramp might be re-entered after starting + the function body: + A The body could suspend (one might expect that to be the 'normal' case + for most coroutines). + B The body might complete either synchronously or via continuations. + C An exception might be thrown during the setup of the initial await + expression, before the initial awaiter resumes. + D An exception might be processed by promise.unhandled_exception () and + that, in turn, might re-throw it (or throw something else). In this + case, the coroutine is considered suspended at the final suspension + point. + + Until the ramp return value has been constructed, the ramp is considered + to have a use of the state. + + To manage these interacting conditions we allocate a reference counter + for the frame state. This is initialised to 1 by the ramp as part of its + startup (note that failures/exceptions in the startup code are handled + locally to the ramp). + + When the body returns (either normally, or by exception) the ramp releases + its use. + + Once the rewritten coroutine body is started, the body is considered to + have a use of the frame. This use (potentially) needs to be released if + an exception is thrown from the body. We implement this using an eh-only + cleanup around the initial await and function body. If we have the case + D above, then we do not release the use. + + In case: + + A, typically the ramp would be re-entered with the body holding a use, + and therefore the ramp should not destroy the state. + + B, both the body and ramp will have released their uses, and the ramp + should destroy the state. + + C, we must arrange for the body to release its use, because we require + the ramp to cleanup in this circumstance. + + D is an outlier, since the responsibility for destruction of the state + now rests with the user's code (via a handle.destroy() call). + + NOTE: In the case that the body has never suspended before such an + exception occurs, the only reasonable way for the user code to obtain the + necessary handle is if unhandled_exception() throws the handle or some + object that contains the handle. That is outside of the designs here - + if the user code might need this corner-case, then such provision will + have to be made. + + In the ramp, we implement destruction for the persistent frame state by + means of cleanups. These are run conditionally when the reference count + is 0 signalling that both the body and the ramp have completed. + + In the body, once we pass the final suspend, then we test the use and + delete the state if the use is 0. */ /* The state that we collect during parsing (and template expansion) for a coroutine. */ @@ -206,11 +285,10 @@ struct GTY((for_user)) coroutine_info tree traits_type; /* The cached traits type for this function. */ tree handle_type; /* The cached coroutine handle for this function. */ tree self_h_proxy; /* A handle instance that is used as the proxy for the - one that will eventually be allocated in the coroutine - frame. */ + one that will eventually be built in lowering. */ tree promise_proxy; /* Likewise, a proxy promise instance. */ - tree from_address; /* handle_type from_address function. */ - tree return_void; /* The expression for p.return_void() if it exists. */ + tree from_address; /* handle_type from_address() function. */ + tree return_void; /* The expression for p.return_void(), if it exists. */ location_t first_coro_keyword; /* The location of the keyword that made this function into a coroutine. */ @@ -348,6 +426,7 @@ static GTY(()) tree coro_resume_index_id; static GTY(()) tree coro_self_handle_id; static GTY(()) tree coro_actor_continue_id; static GTY(()) tree coro_frame_i_a_r_c_id; +static GTY(()) tree coro_frame_refcount_id; /* Create the identifiers used by the coroutines library interfaces and the implementation frame state. */ @@ -385,6 +464,7 @@ coro_init_identifiers () coro_resume_index_id = get_identifier ("_Coro_resume_index"); coro_self_handle_id = get_identifier ("_Coro_self_handle"); coro_actor_continue_id = get_identifier ("_Coro_actor_continue"); + coro_frame_refcount_id = get_identifier ("_Coro_frame_refcount"); } /* Trees we only need to set up once. */ @@ -1277,8 +1357,14 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind, if (TREE_CODE (o_type) != RECORD_TYPE) { - error_at (loc, "awaitable type %qT is not a structure", - o_type); + if (suspend_kind == FINAL_SUSPEND_POINT) + error_at (loc, "%qs awaitable type %qT is not a structure", + "final_suspend()", o_type); + else if (suspend_kind == INITIAL_SUSPEND_POINT) + error_at (loc, "%qs awaitable type %qT is not a structure", + "initial_suspend()", o_type); + else + error_at (loc, "awaitable type %qT is not a structure", o_type); return error_mark_node; } @@ -1461,6 +1547,12 @@ finish_co_await_expr (location_t kw, tree expr) if (!expr || error_operand_p (expr)) return error_mark_node; + if (cp_unevaluated_operand) + { + error_at (kw, "%qs cannot be used in an unevaluated context","co_await"); + return error_mark_node; + } + if (!coro_common_keyword_context_valid_p (current_function_decl, kw, "co_await")) return error_mark_node; @@ -1541,6 +1633,12 @@ finish_co_yield_expr (location_t kw, tree expr) if (!expr || error_operand_p (expr)) return error_mark_node; + if (cp_unevaluated_operand) + { + error_at (kw, "%qs cannot be used in an unevaluated context","co_yield"); + return error_mark_node; + } + /* Check the general requirements and simple syntax errors. */ if (!coro_common_keyword_context_valid_p (current_function_decl, kw, "co_yield")) @@ -1852,21 +1950,6 @@ coro_build_frame_access_expr (tree coro_ref, tree member_id, bool preserve_ref, return expr; } -/* Helpers to build EXPR_STMT and void-cast EXPR_STMT, common ops. */ - -static tree -coro_build_expr_stmt (tree expr, location_t loc) -{ - return maybe_cleanup_point_expr_void (build_stmt (loc, EXPR_STMT, expr)); -} - -static tree -coro_build_cvt_void_expr_stmt (tree expr, location_t loc) -{ - tree t = build1 (CONVERT_EXPR, void_type_node, expr); - return coro_build_expr_stmt (t, loc); -} - /* Helpers to build an artificial var, with location LOC, NAME and TYPE, in CTX, and with initializer INIT. */ @@ -1979,12 +2062,13 @@ struct coro_aw_data tree coro_fp; /* Frame pointer var. */ tree resume_idx; /* This is the index var in the frame. */ tree i_a_r_c; /* initial suspend await_resume() was called if true. */ - tree self_h; /* This is a handle to the current coro (frame var). */ tree cleanup; /* This is where to go once we complete local destroy. */ tree cororet; /* This is where to go if we suspend. */ tree corocont; /* This is where to go if we continue. */ tree dispatch; /* This is where we go if we restart the dispatch. */ tree conthand; /* This is the handle for a continuation. */ + tree handle_type; /* Handle type for this coroutine... */ + tree hfa_m; /* ... and handle.from_address() for this. */ unsigned index; /* This is our current resume index. */ }; @@ -2042,8 +2126,10 @@ expand_one_await_expression (tree *expr, tree *await_expr, void *d) tree awaiter_calls = TREE_OPERAND (saved_co_await, 3); tree source = TREE_OPERAND (saved_co_await, 4); - bool is_final = (source - && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT); + bool is_final + = (source && TREE_INT_CST_LOW (source) == (int) FINAL_SUSPEND_POINT); + bool is_initial + = (source && TREE_INT_CST_LOW (source) == (int) INITIAL_SUSPEND_POINT); /* Build labels for the destinations of the control flow when we are resuming or destroying. */ @@ -2100,6 +2186,18 @@ expand_one_await_expression (tree *expr, tree *await_expr, void *d) tree suspend = TREE_VEC_ELT (awaiter_calls, 1); /* await_suspend(). */ tree susp_type = TREE_TYPE (suspend); + tree susp_call = suspend; + if (TREE_CODE (suspend) == TARGET_EXPR) + susp_call = TARGET_EXPR_INITIAL (suspend); + gcc_checking_assert (TREE_CODE (susp_call) == CALL_EXPR); + tree dummy_ch = build_dummy_object (data->handle_type); + r = fold_convert (build_pointer_type (void_type_node), data->coro_fp); + vec<tree, va_gc> *args = make_tree_vector_single (r); + tree hfa = cp_fold_rvalue ( + build_new_method_call (dummy_ch, data->hfa_m, &args, NULL_TREE, + LOOKUP_NORMAL, NULL, tf_warning_or_error)); + release_tree_vector (args); + CALL_EXPR_ARG (susp_call, call_expr_nargs (susp_call) - 1) = hfa; bool is_cont = false; /* NOTE: final suspend can't resume; the "resume" label in that case @@ -2171,6 +2269,13 @@ expand_one_await_expression (tree *expr, tree *await_expr, void *d) /* Resume point. */ add_stmt (build_stmt (loc, LABEL_EXPR, resume_label)); + if (is_initial && data->i_a_r_c) + { + r = cp_build_modify_expr (loc, data->i_a_r_c, NOP_EXPR, boolean_true_node, + tf_warning_or_error); + finish_expr_stmt (r); + } + /* This will produce the value (if one is provided) from the co_await expression. */ tree resume_call = TREE_VEC_ELT (awaiter_calls, 2); /* await_resume(). */ @@ -2412,6 +2517,11 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, bool spf = start_preparsed_function (actor, NULL_TREE, SF_PRE_PARSED); gcc_checking_assert (spf); gcc_checking_assert (cfun && current_function_decl && TREE_STATIC (actor)); + if (flag_exceptions) + /* We, unconditionally, add a try/catch and rethrow. + TODO: Determine if the combination of initial suspend and the original + body cannot throw, and elide these additions. */ + cp_function_chain->can_throw = true; tree stmt = begin_function_body (); tree actor_bind = build3 (BIND_EXPR, void_type_node, NULL, NULL, NULL); @@ -2558,8 +2668,8 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /* Finish the resume dispatcher. */ finish_switch_stmt (dispatcher); - finish_else_clause (lsb_if); + finish_else_clause (lsb_if); finish_if_stmt (lsb_if); /* If we reach here then we've hit UB. */ @@ -2569,22 +2679,16 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /* Now we start building the rewritten function body. */ add_stmt (build_stmt (loc, LABEL_EXPR, actor_begin_label)); - /* actor's coroutine 'self handle'. */ - tree ash = coro_build_frame_access_expr (actor_frame, coro_self_handle_id, - false, tf_warning_or_error); - /* So construct the self-handle from the frame address. */ - tree hfa_m = get_coroutine_from_address (orig); - /* Should have been set earlier by coro_promise_type_found_p. */ - gcc_assert (hfa_m); - - tree r = build1 (CONVERT_EXPR, build_pointer_type (void_type_node), actor_fp); - vec<tree, va_gc> *args = make_tree_vector_single (r); - tree hfa = build_new_method_call (ash, hfa_m, &args, NULL_TREE, LOOKUP_NORMAL, - NULL, tf_warning_or_error); - r = cp_build_init_expr (ash, hfa); - r = coro_build_cvt_void_expr_stmt (r, loc); - add_stmt (r); - release_tree_vector (args); + tree i_a_r_c = NULL_TREE; + if (flag_exceptions) + { + i_a_r_c + = coro_build_frame_access_expr (actor_frame, coro_frame_i_a_r_c_id, + false, tf_warning_or_error); + tree m = cp_build_modify_expr (loc, i_a_r_c, NOP_EXPR, + boolean_false_node, tf_warning_or_error); + finish_expr_stmt (m); + } /* Now we know the real promise, and enough about the frame layout to decide where to put things. */ @@ -2599,69 +2703,74 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, /* Add in our function body with the co_returns rewritten to final form. */ add_stmt (fnbody); - /* now do the tail of the function. */ + /* We are done with the frame, but if the ramp still has a hold on it + we should not cleanup. So decrement the refcount and then return to + the ramp if it is > 0. */ + tree coro_frame_refcount + = coro_build_frame_access_expr (actor_frame, coro_frame_refcount_id, + false, tf_warning_or_error); + tree released = build2_loc (loc, MINUS_EXPR, short_unsigned_type_node, + coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 1)); + tree r = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, released, + tf_warning_or_error); + finish_expr_stmt (r); + tree cond = build2_loc (loc, NE_EXPR, short_unsigned_type_node, + coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 0)); + tree ramp_cu_if = begin_if_stmt (); + finish_if_stmt_cond (cond, ramp_cu_if); + finish_return_stmt (NULL_TREE); + finish_then_clause (ramp_cu_if); + finish_if_stmt (ramp_cu_if); + + /* Otherwise, do the tail of the function; first cleanups. */ r = build_stmt (loc, LABEL_EXPR, del_promise_label); add_stmt (r); - /* Destructors for the things we built explicitly. */ + /* Destructors for the things we built explicitly. + promise... */ if (tree c = cxx_maybe_build_cleanup (promise_proxy, tf_warning_or_error)) - add_stmt (c); - - tree del_frame_label - = create_named_label_with_ctx (loc, "coro.delete.frame", actor); - r = build_stmt (loc, LABEL_EXPR, del_frame_label); - add_stmt (r); - - /* Here deallocate the frame (if we allocated it), which we will have at - present. */ - tree fnf2_x - = coro_build_frame_access_expr (actor_frame, coro_frame_needs_free_id, - false, tf_warning_or_error); + finish_expr_stmt (c); - tree need_free_if = begin_if_stmt (); - fnf2_x = build1 (CONVERT_EXPR, integer_type_node, fnf2_x); - tree cmp = build2 (NE_EXPR, integer_type_node, fnf2_x, integer_zero_node); - finish_if_stmt_cond (cmp, need_free_if); + /* Argument copies ... */ while (!param_dtor_list->is_empty ()) { tree parm_id = param_dtor_list->pop (); tree a = coro_build_frame_access_expr (actor_frame, parm_id, false, tf_warning_or_error); if (tree dtor = cxx_maybe_build_cleanup (a, tf_warning_or_error)) - add_stmt (dtor); + finish_expr_stmt (dtor); } + /* Here deallocate the frame (if we allocated it), which we will have at + present. */ + tree fnf2_x + = coro_build_frame_access_expr (actor_frame, coro_frame_needs_free_id, + false, tf_warning_or_error); + tree need_free_if = begin_if_stmt (); + finish_if_stmt_cond (fnf2_x, need_free_if); + /* Build the frame DTOR. */ tree del_coro_fr = build_coroutine_frame_delete_expr (actor_fp, frame_size, promise_type, loc); finish_expr_stmt (del_coro_fr); finish_then_clause (need_free_if); - tree scope = IF_SCOPE (need_free_if); - IF_SCOPE (need_free_if) = NULL; - r = do_poplevel (scope); - add_stmt (r); + finish_if_stmt (need_free_if); - /* done. */ - r = build_stmt (loc, RETURN_EXPR, NULL); - suppress_warning (r); /* We don't want a warning about this. */ - r = maybe_cleanup_point_expr_void (r); - add_stmt (r); + /* Done. */ + finish_return_stmt (NULL_TREE); /* This is the suspend return point. */ - r = build_stmt (loc, LABEL_EXPR, ret_label); - add_stmt (r); + add_stmt (build_stmt (loc, LABEL_EXPR, ret_label)); - r = build_stmt (loc, RETURN_EXPR, NULL); - suppress_warning (r); /* We don't want a warning about this. */ - r = maybe_cleanup_point_expr_void (r); - add_stmt (r); + finish_return_stmt (NULL_TREE); /* This is the 'continuation' return point. For such a case we have a coro handle (from the await_suspend() call) and we want handle.resume() to execute as a tailcall allowing arbitrary chaining of coroutines. */ - r = build_stmt (loc, LABEL_EXPR, continue_label); - add_stmt (r); + add_stmt (build_stmt (loc, LABEL_EXPR, continue_label)); /* Should have been set earlier by the coro_initialized code. */ gcc_assert (void_coro_handle_address); @@ -2678,19 +2787,25 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody, we must tail call them. However, some targets do not support indirect tail calls to arbitrary callees. See PR94359. */ CALL_EXPR_TAILCALL (resume) = true; - resume = coro_build_cvt_void_expr_stmt (resume, loc); - add_stmt (resume); + finish_expr_stmt (resume); r = build_stmt (loc, RETURN_EXPR, NULL); gcc_checking_assert (maybe_cleanup_point_expr_void (r) == r); add_stmt (r); + /* How to construct the handle for this coroutine from the frame address. */ + tree hfa_m = get_coroutine_from_address (orig); + /* Should have been set earlier by coro_promise_type_found_p. */ + gcc_assert (hfa_m); + tree handle_type = TREE_TYPE (get_coroutine_self_handle_proxy (orig)); + /* We've now rewritten the tree and added the initial and final co_awaits. Now pass over the tree and expand the co_awaits. */ - coro_aw_data data = {actor, actor_fp, resume_idx_var, NULL_TREE, - ash, del_promise_label, ret_label, - continue_label, restart_dispatch_label, continuation, 2}; + coro_aw_data data = {actor, actor_fp, resume_idx_var, i_a_r_c, + del_promise_label, ret_label, + continue_label, restart_dispatch_label, continuation, + handle_type, hfa_m, 2}; cp_walk_tree (&actor_body, await_statement_expander, &data, NULL); BIND_EXPR_BODY (actor_bind) = pop_stmt_list (actor_body); @@ -2882,8 +2997,8 @@ find_any_await (tree *stmt, int *dosub, void *d) if (TREE_CODE (*stmt) == CO_AWAIT_EXPR) { *dosub = 0; /* We don't need to consider this any further. */ - tree **p = (tree **) d; - *p = stmt; + if (d) + *(tree **)d = stmt; return *stmt; } return NULL_TREE; @@ -3133,7 +3248,9 @@ flatten_await_stmt (var_nest_node *n, hash_set<tree> *promoted, bool already_present = promoted->add (var); gcc_checking_assert (!already_present); tree inner = TARGET_EXPR_INITIAL (init); - gcc_checking_assert (TREE_CODE (inner) != COND_EXPR); + gcc_checking_assert + (TREE_CODE (inner) != COND_EXPR + || !cp_walk_tree (&inner, find_any_await, nullptr, nullptr)); init = cp_build_modify_expr (input_location, var, INIT_EXPR, init, tf_warning_or_error); /* Simplify for the case that we have an init containing the temp @@ -3438,7 +3555,8 @@ maybe_promote_temps (tree *stmt, void *d) return cp_walk_tree (stmt, register_awaits, d, &visited); } -/* Lightweight callback to determine two key factors: +/* Relatively lightweight callback to do initial assessment: + 0) Rewrite some await expressions. 1) If the statement/expression contains any await expressions. 2) If the statement/expression potentially requires a re-write to handle TRUTH_{AND,OR}IF_EXPRs since, in most cases, they will need expansion @@ -3455,6 +3573,39 @@ analyze_expression_awaits (tree *stmt, int *do_subtree, void *d) switch (TREE_CODE (*stmt)) { default: return NULL_TREE; + case CALL_EXPR: + { + tree fn = cp_get_callee_fndecl_nofold (*stmt); + /* Special-cases where we want to re-write await expressions to some + other value before they are otherwise processed. */ + if (fn && DECL_IS_BUILTIN_CONSTANT_P (fn)) + { + gcc_checking_assert (call_expr_nargs (*stmt) == 1); + tree expr = CALL_EXPR_ARG (*stmt, 0); + if (cp_walk_tree (&expr, find_any_await, nullptr, NULL)) + { + if (TREE_CONSTANT (maybe_constant_value (expr))) + *stmt = integer_one_node; + else + *stmt = integer_zero_node; + } + *do_subtree = 0; + } + else if (!fn && CALL_EXPR_IFN (*stmt) == IFN_ASSUME) + { + tree expr = CALL_EXPR_ARG (*stmt, 0); + if (TREE_SIDE_EFFECTS (expr)) + { + location_t loc_e = cp_expr_location (expr); + location_t loc_s = cp_expr_location (*stmt); + location_t loc_n = make_location (loc_e, loc_s, loc_s); + warning_at (loc_n, OPT_Wattributes,"assumption ignored" + " because it contains an await-expression"); + *stmt = build_empty_stmt (loc_n); + } + } + } + break; case CO_YIELD_EXPR: /* co_yield is syntactic sugar, re-write it to co_await. */ *stmt = TREE_OPERAND (*stmt, 1); @@ -4045,12 +4196,14 @@ rewrite_param_uses (tree *stmt, int *do_subtree ATTRIBUTE_UNUSED, void *d) } /* Build up a set of info that determines how each param copy will be - handled. */ + handled. We store this in a hash map so that we can access it from + a tree walk callback that re-writes the original parameters to their + copies. */ -static void -analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses) +void +cp_coroutine_transform::analyze_fn_parms () { - if (!DECL_ARGUMENTS (orig)) + if (!DECL_ARGUMENTS (orig_fn_decl)) return; /* Build a hash map with an entry for each param. @@ -4060,19 +4213,19 @@ analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses) Then a tree list of the uses. The second two entries start out empty - and only get populated when we see uses. */ - bool lambda_p = LAMBDA_FUNCTION_P (orig); + bool lambda_p = LAMBDA_FUNCTION_P (orig_fn_decl); /* Count the param copies from 1 as per the std. */ unsigned parm_num = 1; - for (tree arg = DECL_ARGUMENTS (orig); arg != NULL; + for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; ++parm_num, arg = DECL_CHAIN (arg)) { bool existed; - param_info &parm = param_uses->get_or_insert (arg, &existed); + param_info &parm = param_uses.get_or_insert (arg, &existed); gcc_checking_assert (!existed); parm.body_uses = NULL; tree actual_type = TREE_TYPE (arg); - actual_type = complete_type_or_else (actual_type, orig); + actual_type = complete_type_or_else (actual_type, orig_fn_decl); if (actual_type == NULL_TREE) actual_type = error_mark_node; parm.orig_type = actual_type; @@ -4106,17 +4259,7 @@ analyze_fn_parms (tree orig, hash_map<tree, param_info> *param_uses) } parm.field_id = name; if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type)) - { - char *buf = xasprintf ("_Coro_q%u_%s_live", parm_num, - DECL_NAME (arg) ? IDENTIFIER_POINTER (name) - : "__unnamed"); - parm.guard_var - = coro_build_artificial_var (UNKNOWN_LOCATION, get_identifier (buf), - boolean_type_node, orig, - boolean_false_node); - free (buf); - parm.trivial_dtor = false; - } + parm.trivial_dtor = false; else parm.trivial_dtor = true; } @@ -4307,8 +4450,7 @@ cp_coroutine_transform::wrap_original_function_body () { /* Avoid the code here attaching a location that makes the debugger jump. */ iloc_sentinel stable_input_loc (fn_start); - location_t loc = UNKNOWN_LOCATION; - input_location = loc; + location_t loc = fn_start; /* This will be our new outer scope. */ tree update_body @@ -4356,7 +4498,6 @@ cp_coroutine_transform::wrap_original_function_body () /* Wrap the function body in a try {} catch (...) {} block, if exceptions are enabled. */ tree var_list = NULL_TREE; - tree initial_await = build_init_or_final_await (fn_start, false); /* [stmt.return.coroutine] / 3 If p.return_void() is a valid expression, flowing off the end of a @@ -4395,16 +4536,6 @@ cp_coroutine_transform::wrap_original_function_body () var_list = promise; add_decl_expr (promise); - /* We need a handle to this coroutine, which is passed to every - await_suspend(). This was created on demand when parsing we now link it - into our scope. */ - var = get_coroutine_self_handle_proxy (orig_fn_decl); - DECL_CONTEXT (var) = orig_fn_decl; - DECL_SOURCE_LOCATION (var) = loc; - DECL_CHAIN (var) = var_list; - var_list = var; - add_decl_expr (var); - /* If we have function parms, then these will be copied to the coroutine frame as per [dcl.fct.def.coroutine] / 13. Here, we create a local (proxy) variable for each parm, since the original @@ -4448,27 +4579,44 @@ cp_coroutine_transform::wrap_original_function_body () var_list = resume_idx_var; add_decl_expr (resume_idx_var); + tree coro_frame_refcount + = coro_build_artificial_var (loc, coro_frame_refcount_id, + short_unsigned_type_node, orig_fn_decl, + NULL_TREE); + DECL_CHAIN (coro_frame_refcount) = var_list; + var_list = coro_frame_refcount; + add_decl_expr (coro_frame_refcount); + /* If the coroutine has a frame that needs to be freed, this will be set by the ramp. */ - var = coro_build_artificial_var (fn_start, coro_frame_needs_free_id, + var = coro_build_artificial_var (loc, coro_frame_needs_free_id, boolean_type_node, orig_fn_decl, NULL_TREE); DECL_CHAIN (var) = var_list; var_list = var; add_decl_expr (var); + /* We consider that the body has a use of the frame once we start to process + the initial suspend expression. (the use might be relinquished if we + encounter an exception before the body is finished). */ + tree body_use + = build2_loc (loc, PLUS_EXPR, short_unsigned_type_node, coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 1)); + body_use = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, body_use, + tf_warning_or_error); + finish_expr_stmt (body_use); if (flag_exceptions) { /* Build promise.unhandled_exception(); */ tree ueh = coro_build_promise_expression (orig_fn_decl, promise, coro_unhandled_exception_identifier, - fn_start, NULL, /*musthave=*/true); + loc, NULL, /*musthave=*/true); /* Create and initialize the initial-await-resume-called variable per [dcl.fct.def.coroutine] / 5.3. */ tree i_a_r_c = coro_build_artificial_var (loc, coro_frame_i_a_r_c_id, boolean_type_node, orig_fn_decl, - boolean_false_node); + NULL_TREE); DECL_CHAIN (i_a_r_c) = var_list; var_list = i_a_r_c; add_decl_expr (i_a_r_c); @@ -4476,34 +4624,28 @@ cp_coroutine_transform::wrap_original_function_body () tree tcb = build_stmt (loc, TRY_BLOCK, NULL_TREE, NULL_TREE); add_stmt (tcb); TRY_STMTS (tcb) = push_stmt_list (); - if (initial_await != error_mark_node) - { - /* Build a compound expression that sets the - initial-await-resume-called variable true and then calls the - initial suspend expression await resume. - In the case that the user decides to make the initial await - await_resume() return a value, we need to discard it and, it is - a reference type, look past the indirection. */ - if (INDIRECT_REF_P (initial_await)) - initial_await = TREE_OPERAND (initial_await, 0); - /* In the case that the initial_await returns a target expression - we might need to look through that to update the await expr. */ - tree iaw = initial_await; - if (TREE_CODE (iaw) == TARGET_EXPR) - iaw = TARGET_EXPR_INITIAL (iaw); - gcc_checking_assert (TREE_CODE (iaw) == CO_AWAIT_EXPR); - tree vec = TREE_OPERAND (iaw, 3); - tree aw_r = TREE_VEC_ELT (vec, 2); - aw_r = convert_to_void (aw_r, ICV_STATEMENT, tf_warning_or_error); - tree update = build2 (MODIFY_EXPR, boolean_type_node, i_a_r_c, - boolean_true_node); - aw_r = cp_build_compound_expr (update, aw_r, tf_warning_or_error); - TREE_VEC_ELT (vec, 2) = aw_r; - } + /* We need a new scope to handle the cleanup for the ramp use that is + needed for exceptions. */ + tree except_scope = begin_compound_stmt (0); + current_binding_level->artificial = 1; + tree release + = build2_loc (loc, MINUS_EXPR, short_unsigned_type_node, + coro_frame_refcount, build_int_cst (short_unsigned_type_node, 1)); + release = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, + release, tf_warning_or_error); + /* Once we pass the initial await resume, the cleanup rules on exception + change so that the responsibility lies with the caller. */ + release = build3 (COND_EXPR, void_type_node, i_a_r_c, + build_empty_stmt (loc), release); + push_cleanup (NULL_TREE, release, /*ehonly*/true); /* Add the initial await to the start of the user-authored function. */ finish_expr_stmt (initial_await); + /* End the scope that handles the remove of frame-use on exception. */ + finish_compound_stmt (except_scope); + /* Append the original function body. */ add_stmt (coroutine_body); + if (return_void) add_stmt (return_void); TRY_STMTS (tcb) = pop_stmt_list (TRY_STMTS (tcb)); @@ -4548,9 +4690,9 @@ cp_coroutine_transform::wrap_original_function_body () tree ueh_meth = lookup_promise_method (orig_fn_decl, coro_unhandled_exception_identifier, - fn_start, /*musthave=*/false); + loc, /*musthave=*/false); if (!ueh_meth || ueh_meth == error_mark_node) - warning_at (fn_start, 0, "no member named %qE in %qT", + warning_at (loc, 0, "no member named %qE in %qT", coro_unhandled_exception_identifier, get_coroutine_promise_type (orig_fn_decl)); } @@ -4563,6 +4705,10 @@ cp_coroutine_transform::wrap_original_function_body () add_stmt (return_void); } + /* We are now doing actions associated with the end of the function, so + point to the closing brace. */ + input_location = loc = fn_end; + /* co_return branches to the final_suspend label, so declare that now. */ fs_label = create_named_label_with_ctx (loc, "final.suspend", NULL_TREE); @@ -4574,7 +4720,8 @@ cp_coroutine_transform::wrap_original_function_body () zero_resume = build2_loc (loc, MODIFY_EXPR, act_des_fn_ptr_type, resume_fn_ptr, zero_resume); finish_expr_stmt (zero_resume); - finish_expr_stmt (build_init_or_final_await (fn_start, true)); + finish_expr_stmt (final_await); + BIND_EXPR_BODY (update_body) = pop_stmt_list (BIND_EXPR_BODY (update_body)); BIND_EXPR_VARS (update_body) = nreverse (var_list); BLOCK_VARS (top_block) = BIND_EXPR_VARS (update_body); @@ -4883,47 +5030,6 @@ cp_coroutine_transform::build_ramp_function () coro_fp = pushdecl (coro_fp); add_decl_expr (coro_fp); - tree coro_promise_live = NULL_TREE; - tree coro_gro_live = NULL_TREE; - if (flag_exceptions) - { - /* Signal that we need to clean up the promise object on exception. */ - coro_promise_live - = coro_build_and_push_artificial_var (loc, "_Coro_promise_live", - boolean_type_node, orig_fn_decl, - boolean_false_node); - - /* When the get-return-object is in the RETURN slot, we need to arrange - for cleanup on exception. */ - coro_gro_live - = coro_build_and_push_artificial_var (loc, "_Coro_gro_live", - boolean_type_node, orig_fn_decl, - boolean_false_node); - - /* To signal that we need to cleanup copied function args. */ - if (DECL_ARGUMENTS (orig_fn_decl)) - for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; - arg = DECL_CHAIN (arg)) - { - param_info *parm_i = param_uses.get (arg); - if (parm_i->trivial_dtor) - continue; - parm_i->guard_var = pushdecl (parm_i->guard_var); - add_decl_expr (parm_i->guard_var); - } - } - - /* deref the frame pointer, to use in member access code. */ - tree deref_fp - = cp_build_indirect_ref (loc, coro_fp, RO_UNARY_STAR, - tf_warning_or_error); - tree frame_needs_free - = coro_build_and_push_artificial_var_with_dve (loc, - coro_frame_needs_free_id, - boolean_type_node, - orig_fn_decl, NULL_TREE, - deref_fp); - /* Build the frame. */ /* The CO_FRAME internal function is a mechanism to allow the middle end @@ -4967,37 +5073,35 @@ cp_coroutine_transform::build_ramp_function () finish_if_stmt (if_stmt); } + /* Dereference the frame pointer, to use in member access code. */ + tree deref_fp + = cp_build_indirect_ref (loc, coro_fp, RO_UNARY_STAR, tf_warning_or_error); + /* For now, once allocation has succeeded we always assume that this needs destruction, there's no impl. for frame allocation elision. */ - r = cp_build_init_expr (frame_needs_free, boolean_true_node); - finish_expr_stmt (r); - - /* Set up the promise. */ - tree p - = coro_build_and_push_artificial_var_with_dve (loc, coro_promise_id, - promise_type, orig_fn_decl, - NULL_TREE, deref_fp); + tree frame_needs_free + = coro_build_and_push_artificial_var_with_dve (loc, + coro_frame_needs_free_id, + boolean_type_node, + orig_fn_decl, + boolean_true_node, + deref_fp); + /* Although it appears to be unused here the frame entry is needed and we + just set it true. */ + TREE_USED (frame_needs_free) = true; - /* Up to now any exception thrown will propagate directly to the caller. - This is OK since the only source of such exceptions would be in allocation - of the coroutine frame, and therefore the ramp will not have initialized - any further state. From here, we will track state that needs explicit - destruction in the case that promise or g.r.o setup fails or an exception - is thrown from the initial suspend expression. */ - tree ramp_try_block = NULL_TREE; - tree ramp_try_stmts = NULL_TREE; - tree iarc_x = NULL_TREE; - if (flag_exceptions) - { - iarc_x - = coro_build_and_push_artificial_var_with_dve (loc, - coro_frame_i_a_r_c_id, - boolean_type_node, - orig_fn_decl, NULL_TREE, - deref_fp); - ramp_try_block = begin_try_block (); - ramp_try_stmts = begin_compound_stmt (BCS_TRY_BLOCK); - } + tree coro_frame_refcount + = coro_build_and_push_artificial_var_with_dve (loc, coro_frame_refcount_id, + short_unsigned_type_node, + orig_fn_decl, NULL_TREE, + deref_fp); + /* Cleanup if both the ramp and the body have finished. */ + tree cond + = build2_loc (loc, EQ_EXPR, short_unsigned_type_node, coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 0)); + r = build3 (COND_EXPR, void_type_node, cond, delete_frame_call, + build_empty_stmt (loc)); + push_cleanup (coro_fp, r, /*eh_only*/false); /* Put the resumer and destroyer functions in. */ @@ -5069,24 +5173,34 @@ cp_coroutine_transform::build_ramp_function () tf_warning_or_error); } finish_expr_stmt (r); + + /* Arrange for parm copies to be cleaned up when an exception is + thrown before initial await resume. */ if (!parm.trivial_dtor) { - param_dtor_list.safe_push (parm.field_id); - /* Cleanup this frame copy on exception. */ parm.fr_copy_dtor = cxx_maybe_build_cleanup (fld_idx, tf_warning_or_error); - if (flag_exceptions) + if (parm.fr_copy_dtor && parm.fr_copy_dtor != error_mark_node) { - /* This var is now live. */ - r = build_modify_expr (loc, parm.guard_var, - boolean_type_node, INIT_EXPR, loc, - boolean_true_node, boolean_type_node); - finish_expr_stmt (r); + param_dtor_list.safe_push (parm.field_id); + cond + = build2_loc (loc, EQ_EXPR, short_unsigned_type_node, + coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 0)); + r = build3_loc (loc, COND_EXPR, void_type_node, cond, + parm.fr_copy_dtor, build_empty_stmt (loc)); + push_cleanup (fld_idx, r, /*eh_only*/false); } } } } + /* Set up the promise. */ + tree p + = coro_build_and_push_artificial_var_with_dve (loc, coro_promise_id, + promise_type, orig_fn_decl, + NULL_TREE, deref_fp); + if (type_build_ctor_call (promise_type)) { /* Construct the promise object [dcl.fct.def.coroutine] / 5.7. @@ -5120,11 +5234,16 @@ cp_coroutine_transform::build_ramp_function () finish_expr_stmt (r); } - tree promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error);; - if (flag_exceptions && promise_dtor) + tree promise_dtor = cxx_maybe_build_cleanup (p, tf_warning_or_error); + /* If the promise is live, then run its dtor if that's available. */ + if (promise_dtor && promise_dtor != error_mark_node) { - r = cp_build_init_expr (coro_promise_live, boolean_true_node); - finish_expr_stmt (r); + cond = build2_loc (loc, EQ_EXPR, short_unsigned_type_node, + coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 0)); + r = build3 (COND_EXPR, void_type_node, cond, promise_dtor, + build_empty_stmt (loc)); + push_cleanup (p, r, /*eh_only*/false); } tree get_ro @@ -5139,8 +5258,11 @@ cp_coroutine_transform::build_ramp_function () /* Check for a bad get return object type. [dcl.fct.def.coroutine] / 7 requires: The expression promise.get_return_object() is used to initialize the - returned reference or prvalue result object ... */ - tree gro_type = TREE_TYPE (get_ro); + returned reference or prvalue result object ... + When we use a local to hold this, it is decltype(auto). */ + tree gro_type + = finish_decltype_type (get_ro, /*id_expression_or_member_access_p*/false, + tf_warning_or_error); if (VOID_TYPE_P (gro_type) && !void_ramp_p) { error_at (fn_start, "no viable conversion from %<void%> provided by" @@ -5153,169 +5275,62 @@ cp_coroutine_transform::build_ramp_function () (loc, coro_resume_index_id, short_unsigned_type_node, orig_fn_decl, build_zero_cst (short_unsigned_type_node), deref_fp); - if (flag_exceptions && iarc_x) - { - r = cp_build_init_expr (iarc_x, boolean_false_node); - finish_expr_stmt (r); - } - - /* Used for return objects in the RESULT slot. */ - tree ret_val_dtor = NULL_TREE; - tree retval = NULL_TREE; + /* We must manage the cleanups ourselves, with the exception of the g_r_o, + because the responsibility for them changes after the initial suspend. + However, any use of cxx_maybe_build_cleanup () in preceding code can + set the throwing_cleanup flag. */ + cp_function_chain->throwing_cleanup = false; /* [dcl.fct.def.coroutine] / 7 The expression promise.get_return_object() is used to initialize the glvalue result or prvalue result object of a call to a coroutine. */ - /* We must manage the cleanups ourselves, because the responsibility for - them changes after the initial suspend. However, any use of - cxx_maybe_build_cleanup () can set the throwing_cleanup flag. */ - cp_function_chain->throwing_cleanup = false; + tree coro_gro = NULL_TREE; if (void_ramp_p) /* We still want to call the method, even if the result is unused. */ - r = get_ro; + finish_expr_stmt (get_ro); else { - /* The initial section of finish_return_expr (). */ - bool no_warning; - bool dangling; - /* Without a relevant location, bad conversions in check_return_expr - result in unusable diagnostics, since there is not even a mention - of the relevant function. Here we carry out the first part of - finish_return_expr(). */ - input_location = fn_start; - r = check_return_expr (get_ro, &no_warning, &dangling); - input_location = UNKNOWN_LOCATION; - gcc_checking_assert (!dangling); - /* Check for bad things. */ - if (!r || r == error_mark_node) - return false; - if (!aggregate_value_p (fn_return_type, orig_fn_decl) - && TREE_CODE (r) == INIT_EXPR) - { - /* If fn_return_type doesn't need to be returned in memory, normally - gimplify_return_expr redirects the INIT_EXPR to a temporary. But - r isn't wrapped in the RETURN_EXPR, so we need to do the - redirection here as well. See PR118874. */ - tree temp = create_temporary_var (fn_return_type); - add_decl_expr (temp); - retval = copy_node (r); - TREE_OPERAND (r, 0) = temp; - TREE_OPERAND (retval, 1) = temp; - } - else - retval = DECL_RESULT (orig_fn_decl); - } - - finish_expr_stmt (r); + /* Per CWG2563, we keep the result of promise.get_return_object () in + a temp which is then used to intialize the return object, including + NVRO. */ - if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (fn_return_type)) - /* If some part of the initalization code (prior to the await_resume - of the initial suspend expression), then we need to clean up the - return value. */ - ret_val_dtor = cxx_maybe_build_cleanup (DECL_RESULT (orig_fn_decl), - tf_warning_or_error); + /* Temporary var to hold the g_r_o across the function body. */ + coro_gro + = coro_build_and_push_artificial_var (loc, "_Coro_gro", gro_type, + orig_fn_decl, NULL_TREE); - /* If we have a live g.r.o in the return slot, then signal this for exception - cleanup. */ - if (flag_exceptions && ret_val_dtor) - { - r = cp_build_init_expr (coro_gro_live, boolean_true_node); + r = cp_build_init_expr (coro_gro, STRIP_REFERENCE_REF (get_ro)); finish_expr_stmt (r); + tree coro_gro_cleanup + = cxx_maybe_build_cleanup (coro_gro, tf_warning_or_error); + if (coro_gro_cleanup) + push_cleanup (coro_gro, coro_gro_cleanup, /*eh_only*/false); } - /* Start the coroutine body. */ + /* Start the coroutine body, we now have a use of the frame... */ + r = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, + build_int_cst (short_unsigned_type_node, 1), + tf_warning_or_error); + finish_expr_stmt (r); + /* ... but when we finish we want to release that, and we want to do that + before any of the other cleanups run. */ + tree released + = build2_loc (loc, MINUS_EXPR, short_unsigned_type_node, coro_frame_refcount, + build_int_cst (short_unsigned_type_node, 1)); + released = cp_build_modify_expr (loc, coro_frame_refcount, NOP_EXPR, released, + tf_warning_or_error); + push_cleanup (NULL_TREE, released, /*eh_only*/false); + r = build_call_expr_loc (fn_start, resumer, 1, coro_fp); finish_expr_stmt (r); /* The ramp is done, we just need the return statement, which we build from the return object we constructed before we called the actor. */ - r = retval; - - /* The reminder of finish_return_expr (). */ - r = build_stmt (loc, RETURN_EXPR, r); - r = maybe_cleanup_point_expr_void (r); - r = add_stmt (r); + r = void_ramp_p ? NULL_TREE : convert_from_reference (coro_gro); + finish_return_stmt (r); - if (flag_exceptions) - { - finish_compound_stmt (ramp_try_stmts); - finish_try_block (ramp_try_block); - tree handler = begin_handler (); - finish_handler_parms (NULL_TREE, handler); /* catch (...) */ - - /* If we have a live G.R.O in the return slot, then run its DTOR. */ - if (ret_val_dtor && ret_val_dtor != error_mark_node) - { - tree gro_d_if = begin_if_stmt (); - finish_if_stmt_cond (coro_gro_live, gro_d_if); - finish_expr_stmt (ret_val_dtor); - finish_then_clause (gro_d_if); - finish_if_stmt (gro_d_if); - } - - /* Before initial resume is called, the responsibility for cleanup on - exception falls to the ramp. After that, the coroutine body code - should do the cleanup. This is signalled by the flag - 'initial_await_resume_called'. */ - - tree not_iarc - = build1_loc (loc, TRUTH_NOT_EXPR, boolean_type_node, iarc_x); - tree cleanup_if = begin_if_stmt (); - finish_if_stmt_cond (not_iarc, cleanup_if); - /* If the promise is live, then run its dtor if that's available. */ - if (promise_dtor && promise_dtor != error_mark_node) - { - tree promise_d_if = begin_if_stmt (); - finish_if_stmt_cond (coro_promise_live, promise_d_if); - finish_expr_stmt (promise_dtor); - finish_then_clause (promise_d_if); - finish_if_stmt (promise_d_if); - } - - /* Clean up any frame copies of parms with non-trivial dtors. - Do this in reverse order from their creation. */ - auto_vec<param_info *> worklist; - if (DECL_ARGUMENTS (orig_fn_decl)) - for (tree arg = DECL_ARGUMENTS (orig_fn_decl); arg != NULL; - arg = DECL_CHAIN (arg)) - { - param_info *parm_i = param_uses.get (arg); - if (parm_i->trivial_dtor) - continue; - worklist.safe_push (parm_i); - } - while (!worklist.is_empty ()) - { - param_info *parm_i = worklist.pop (); - if (parm_i->fr_copy_dtor && parm_i->fr_copy_dtor != error_mark_node) - { - tree dtor_if = begin_if_stmt (); - finish_if_stmt_cond (parm_i->guard_var, dtor_if); - finish_expr_stmt (parm_i->fr_copy_dtor); - finish_then_clause (dtor_if); - finish_if_stmt (dtor_if); - } - } - - /* No delete the frame if required. */ - tree fnf_if = begin_if_stmt (); - finish_if_stmt_cond (frame_needs_free, fnf_if); - finish_expr_stmt (delete_frame_call); - finish_then_clause (fnf_if); - finish_if_stmt (fnf_if); - - /* Finished cleanups conditional on "initial resume is not called". */ - finish_then_clause (cleanup_if); - finish_if_stmt (cleanup_if); - - tree rethrow = build_throw (loc, NULL_TREE, tf_warning_or_error); - suppress_warning (rethrow); - finish_expr_stmt (rethrow); - finish_handler (handler); - finish_handler_sequence (ramp_try_block); - } finish_compound_stmt (ramp_fnbody); return true; } @@ -5346,9 +5361,10 @@ cp_coroutine_transform::cp_coroutine_transform (tree _orig_fn, bool _inl) } /* We don't have the locus of the opening brace - it's filled in later (and - there doesn't really seem to be any easy way to get at it). - The closing brace is assumed to be input_location. */ + there doesn't really seem to be any easy way to get at it). */ fn_start = DECL_SOURCE_LOCATION (orig_fn_decl); + /* The closing brace is assumed to be input_location. */ + fn_end = input_location; /* Build types we need. */ tree fr_name = get_fn_local_identifier (orig_fn_decl, "Frame"); @@ -5389,7 +5405,6 @@ cp_coroutine_transform::~cp_coroutine_transform () bool _Coro_frame_needs_free; free the coro frame mem if set. bool _Coro_i_a_r_c; [dcl.fct.def.coroutine] / 5.3 short _Coro_resume_index; - handle_type _Coro_self_handle; parameter copies (were required). local variables saved (including awaitables) (maybe) trailing space. @@ -5416,7 +5431,7 @@ cp_coroutine_transform::apply_transforms () /* Collect information on the original function params and their use in the function body. */ - analyze_fn_parms (orig_fn_decl, ¶m_uses); + analyze_fn_parms (); /* Declare the actor and destroyer functions, the following code needs to see these. */ @@ -5427,6 +5442,16 @@ cp_coroutine_transform::apply_transforms () = coro_build_actor_or_destroy_function (orig_fn_decl, act_des_fn_type, frame_ptr_type, false); + /* Avoid repeating diagnostics about promise or awaiter fails. */ + if (!seen_error ()) + { + iloc_sentinel stable_input_loc (fn_start); + initial_await = build_init_or_final_await (fn_start, false); + input_location = fn_end; + if (initial_await && initial_await != error_mark_node) + final_await = build_init_or_final_await (fn_end, true); + } + /* Transform the function body as per [dcl.fct.def.coroutine] / 5. */ wrap_original_function_body (); diff --git a/gcc/cp/coroutines.h b/gcc/cp/coroutines.h index d13bea0..fcc4645 100644 --- a/gcc/cp/coroutines.h +++ b/gcc/cp/coroutines.h @@ -9,7 +9,6 @@ struct param_info vec<tree *> *body_uses; /* Worklist of uses, void if there are none. */ tree frame_type; /* The type used to represent this parm in the frame. */ tree orig_type; /* The original type of the parm (not as passed). */ - tree guard_var; /* If we need a DTOR on exception, this bool guards it. */ tree fr_copy_dtor; /* If we need a DTOR on exception, this is it. */ bool by_ref; /* Was passed by reference. */ bool pt_ref; /* Was a pointer to object. */ @@ -101,8 +100,8 @@ public: private: tree orig_fn_decl; /* The original function decl. */ - tree orig_fn_body = NULL_TREE; /* The original function body. */ location_t fn_start = UNKNOWN_LOCATION; + location_t fn_end = UNKNOWN_LOCATION; tree resumer = error_mark_node; tree destroyer = error_mark_node; tree coroutine_body = NULL_TREE; @@ -127,6 +126,10 @@ private: bool inline_p = false; bool valid_coroutine = false; + tree initial_await = error_mark_node; + tree final_await = error_mark_node; + + void analyze_fn_parms (); void wrap_original_function_body (); bool build_ramp_function (); }; diff --git a/gcc/cp/cp-gimplify.cc b/gcc/cp/cp-gimplify.cc index 550cea29..882a943 100644 --- a/gcc/cp/cp-gimplify.cc +++ b/gcc/cp/cp-gimplify.cc @@ -889,6 +889,12 @@ cp_gimplify_expr (tree *expr_p, gimple_seq *pre_p, gimple_seq *post_p) (EXPR_LOCATION (*expr_p), call_expr_nargs (*expr_p), &CALL_EXPR_ARG (*expr_p, 0)); break; + case CP_BUILT_IN_EH_PTR_ADJUST_REF: + error_at (EXPR_LOCATION (*expr_p), + "%qs used outside of constant expressions", + "__builtin_eh_ptr_adjust_ref"); + *expr_p = void_node; + break; default: break; } @@ -1199,8 +1205,11 @@ cp_build_init_expr_for_ctor (tree call, tree init) tree s = build_fold_indirect_ref_loc (loc, a); init = cp_build_init_expr (s, init); if (return_this) - init = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (call), init, - fold_convert_loc (loc, TREE_TYPE (call), a)); + { + init = build2_loc (loc, COMPOUND_EXPR, TREE_TYPE (call), init, + fold_convert_loc (loc, TREE_TYPE (call), a)); + suppress_warning (init); + } return init; } @@ -1470,6 +1479,19 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) break; case TARGET_EXPR: + if (!flag_no_inline) + if (tree &init = TARGET_EXPR_INITIAL (stmt)) + { + tree folded = maybe_constant_init (init, TARGET_EXPR_SLOT (stmt), + (data->flags & ff_mce_false + ? mce_false : mce_unknown)); + if (folded != init && TREE_CONSTANT (folded)) + init = folded; + } + + /* This needs to happen between the constexpr evaluation (which wants + pre-generic trees) and fold (which wants the cp_genericize_init + transformations). */ if (data->flags & ff_genericize) cp_genericize_target_expr (stmt_p); @@ -1478,12 +1500,6 @@ cp_fold_r (tree *stmt_p, int *walk_subtrees, void *data_) cp_walk_tree (&init, cp_fold_r, data, NULL); cp_walk_tree (&TARGET_EXPR_CLEANUP (stmt), cp_fold_r, data, NULL); *walk_subtrees = 0; - if (!flag_no_inline) - { - tree folded = maybe_constant_init (init, TARGET_EXPR_SLOT (stmt)); - if (folded != init && TREE_CONSTANT (folded)) - init = folded; - } /* Folding might replace e.g. a COND_EXPR with a TARGET_EXPR; in that case, strip it in favor of this one. */ if (TREE_CODE (init) == TARGET_EXPR) @@ -2804,6 +2820,12 @@ cxx_omp_finish_clause (tree c, gimple_seq *, bool /* openacc */) } } +tree +cxx_omp_finish_mapper_clauses (tree clauses) +{ + return finish_omp_clauses (clauses, C_ORT_OMP); +} + /* Return true if DECL's DECL_VALUE_EXPR (if any) should be disregarded in OpenMP construct, because it is going to be remapped during OpenMP lowering. SHARED is true if DECL @@ -3338,19 +3360,13 @@ cp_fold (tree x, fold_flags_t flags) || id_equal (DECL_NAME (callee), "addressof") /* This addressof equivalent is used heavily in libstdc++. */ || id_equal (DECL_NAME (callee), "__addressof") + || id_equal (DECL_NAME (callee), "to_underlying") || id_equal (DECL_NAME (callee), "as_const"))) { r = CALL_EXPR_ARG (x, 0); - /* Check that the return and argument types are sane before - folding. */ - if (INDIRECT_TYPE_P (TREE_TYPE (x)) - && INDIRECT_TYPE_P (TREE_TYPE (r))) - { - if (!same_type_p (TREE_TYPE (x), TREE_TYPE (r))) - r = build_nop (TREE_TYPE (x), r); - x = cp_fold (r, flags); - break; - } + r = build_nop (TREE_TYPE (x), r); + x = cp_fold (r, flags); + break; } int sv = optimize, nw = sv; @@ -3442,7 +3458,9 @@ cp_fold (tree x, fold_flags_t flags) Do constexpr expansion of expressions where the call itself is not constant, but the call followed by an INDIRECT_REF is. */ if (callee && DECL_DECLARED_CONSTEXPR_P (callee) - && !flag_no_inline) + && (!flag_no_inline + || lookup_attribute ("always_inline", + DECL_ATTRIBUTES (callee)))) { mce_value manifestly_const_eval = mce_unknown; if (flags & ff_mce_false) @@ -3875,7 +3893,6 @@ struct source_location_table_entry_hash static GTY(()) hash_table <source_location_table_entry_hash> *source_location_table; -static GTY(()) unsigned int source_location_id; /* Fold the __builtin_source_location () call T. */ @@ -3908,9 +3925,7 @@ fold_builtin_source_location (const_tree t) var = entryp->var; else { - char tmp_name[32]; - ASM_GENERATE_INTERNAL_LABEL (tmp_name, "Lsrc_loc", source_location_id++); - var = build_decl (loc, VAR_DECL, get_identifier (tmp_name), + var = build_decl (loc, VAR_DECL, generate_internal_label ("Lsrc_loc"), source_location_impl); TREE_STATIC (var) = 1; TREE_PUBLIC (var) = 0; diff --git a/gcc/cp/cp-objcp-common.cc b/gcc/cp/cp-objcp-common.cc index 8336d0b..7665b94 100644 --- a/gcc/cp/cp-objcp-common.cc +++ b/gcc/cp/cp-objcp-common.cc @@ -614,6 +614,8 @@ cp_register_dumps (gcc::dump_manager *dumps) (".raw", "lang-raw", "lang-raw", DK_lang, OPTGROUP_NONE, false); coro_dump_id = dumps->dump_register (".coro", "lang-coro", "lang-coro", DK_lang, OPTGROUP_NONE, false); + tinst_dump_id = dumps->dump_register + (".tinst", "lang-tinst", "lang-tinst", DK_lang, OPTGROUP_NONE, false); } void diff --git a/gcc/cp/cp-objcp-common.h b/gcc/cp/cp-objcp-common.h index 13fb80c..ff35428 100644 --- a/gcc/cp/cp-objcp-common.h +++ b/gcc/cp/cp-objcp-common.h @@ -190,6 +190,15 @@ static const scoped_attribute_specs *const cp_objcp_attribute_table[] = #define LANG_HOOKS_OMP_CLAUSE_DTOR cxx_omp_clause_dtor #undef LANG_HOOKS_OMP_FINISH_CLAUSE #define LANG_HOOKS_OMP_FINISH_CLAUSE cxx_omp_finish_clause +#undef LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES +#define LANG_HOOKS_OMP_FINISH_MAPPER_CLAUSES cxx_omp_finish_mapper_clauses +#undef LANG_HOOKS_OMP_MAPPER_LOOKUP +#define LANG_HOOKS_OMP_MAPPER_LOOKUP cxx_omp_mapper_lookup +#undef LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE +#define LANG_HOOKS_OMP_EXTRACT_MAPPER_DIRECTIVE \ + cxx_omp_extract_mapper_directive +#undef LANG_HOOKS_OMP_MAP_ARRAY_SECTION +#define LANG_HOOKS_OMP_MAP_ARRAY_SECTION cxx_omp_map_array_section #undef LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE #define LANG_HOOKS_OMP_PRIVATIZE_BY_REFERENCE cxx_omp_privatize_by_reference #undef LANG_HOOKS_OMP_DISREGARD_VALUE_EXPR diff --git a/gcc/cp/cp-trait.def b/gcc/cp/cp-trait.def index 6aaca13..e71b28c 100644 --- a/gcc/cp/cp-trait.def +++ b/gcc/cp/cp-trait.def @@ -71,6 +71,7 @@ DEFTRAIT_EXPR (IS_CLASS, "__is_class", 1) DEFTRAIT_EXPR (IS_CONST, "__is_const", 1) DEFTRAIT_EXPR (IS_CONSTRUCTIBLE, "__is_constructible", -1) DEFTRAIT_EXPR (IS_CONVERTIBLE, "__is_convertible", 2) +DEFTRAIT_EXPR (IS_DESTRUCTIBLE, "__is_destructible", 1) DEFTRAIT_EXPR (IS_EMPTY, "__is_empty", 1) DEFTRAIT_EXPR (IS_ENUM, "__is_enum", 1) DEFTRAIT_EXPR (IS_FINAL, "__is_final", 1) @@ -84,6 +85,7 @@ DEFTRAIT_EXPR (IS_MEMBER_POINTER, "__is_member_pointer", 1) DEFTRAIT_EXPR (IS_NOTHROW_ASSIGNABLE, "__is_nothrow_assignable", 2) DEFTRAIT_EXPR (IS_NOTHROW_CONSTRUCTIBLE, "__is_nothrow_constructible", -1) DEFTRAIT_EXPR (IS_NOTHROW_CONVERTIBLE, "__is_nothrow_convertible", 2) +DEFTRAIT_EXPR (IS_NOTHROW_DESTRUCTIBLE, "__is_nothrow_destructible", 1) DEFTRAIT_EXPR (IS_NOTHROW_INVOCABLE, "__is_nothrow_invocable", -1) DEFTRAIT_EXPR (IS_OBJECT, "__is_object", 1) DEFTRAIT_EXPR (IS_POINTER_INTERCONVERTIBLE_BASE_OF, "__is_pointer_interconvertible_base_of", 2) @@ -98,6 +100,7 @@ DEFTRAIT_EXPR (IS_TRIVIAL, "__is_trivial", 1) DEFTRAIT_EXPR (IS_TRIVIALLY_ASSIGNABLE, "__is_trivially_assignable", 2) DEFTRAIT_EXPR (IS_TRIVIALLY_CONSTRUCTIBLE, "__is_trivially_constructible", -1) DEFTRAIT_EXPR (IS_TRIVIALLY_COPYABLE, "__is_trivially_copyable", 1) +DEFTRAIT_EXPR (IS_TRIVIALLY_DESTRUCTIBLE, "__is_trivially_destructible", 1) DEFTRAIT_EXPR (IS_UNBOUNDED_ARRAY, "__is_unbounded_array", 1) DEFTRAIT_EXPR (IS_UNION, "__is_union", 1) DEFTRAIT_EXPR (IS_VIRTUAL_BASE_OF, "__builtin_is_virtual_base_of", 2) @@ -111,6 +114,7 @@ DEFTRAIT_TYPE (REMOVE_CVREF, "__remove_cvref", 1) DEFTRAIT_TYPE (REMOVE_EXTENT, "__remove_extent", 1) DEFTRAIT_TYPE (REMOVE_POINTER, "__remove_pointer", 1) DEFTRAIT_TYPE (REMOVE_REFERENCE, "__remove_reference", 1) +DEFTRAIT_EXPR (TYPE_ORDER, "__builtin_type_order", 2) DEFTRAIT_TYPE (TYPE_PACK_ELEMENT, "__type_pack_element", -1) DEFTRAIT_TYPE (UNDERLYING_TYPE, "__underlying_type", 1) diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 927f51b..9081628 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -452,6 +452,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) RETURN_EXPR_LOCAL_ADDR_P (in RETURN_EXPR) PACK_INDEX_PARENTHESIZED_P (in PACK_INDEX_*) + MUST_NOT_THROW_NOEXCEPT_P (in MUST_NOT_THROW_EXPR) 1: IDENTIFIER_KIND_BIT_1 (in IDENTIFIER_NODE) TI_PENDING_TEMPLATE_FLAG. TEMPLATE_PARMS_FOR_INLINE. @@ -472,6 +473,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; BIND_EXPR_VEC_DTOR (in BIND_EXPR) ATOMIC_CONSTR_EXPR_FROM_CONCEPT_P (in ATOMIC_CONSTR) STATIC_INIT_DECOMP_BASE_P (in the TREE_LIST for {static,tls}_aggregates) + MUST_NOT_THROW_THROW_P (in MUST_NOT_THROW_EXPR) 2: IDENTIFIER_KIND_BIT_2 (in IDENTIFIER_NODE) ICS_THIS_FLAG (in _CONV) DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL) @@ -493,6 +495,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) STATIC_INIT_DECOMP_NONBASE_P (in the TREE_LIST for {static,tls}_aggregates) + MUST_NOT_THROW_CATCH_P (in MUST_NOT_THROW_EXPR) 3: IMPLICIT_RVALUE_P (in NON_LVALUE_EXPR or STATIC_CAST_EXPR) ICS_BAD_FLAG (in _CONV) FN_TRY_BLOCK_P (in TRY_BLOCK) @@ -506,6 +509,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX]; LAMBDA_EXPR_STATIC_P (in LAMBDA_EXPR) TARGET_EXPR_ELIDING_P (in TARGET_EXPR) contract_semantic (in ASSERTION_, PRECONDITION_, POSTCONDITION_STMT) + TYPENAME_IS_UNION_P (in TYPENAME_TYPE) 4: IDENTIFIER_MARKED (IDENTIFIER_NODEs) TREE_HAS_CONSTRUCTOR (in INDIRECT_REF, SAVE_EXPR, CONSTRUCTOR, CALL_EXPR, or FIELD_DECL). @@ -2206,6 +2210,8 @@ struct GTY(()) language_function { BOOL_BITFIELD invalid_constexpr : 1; BOOL_BITFIELD throwing_cleanup : 1; + /* True if we gave any errors in this function. */ + BOOL_BITFIELD erroneous : 1; hash_table<named_label_hash> *x_named_labels; @@ -2352,6 +2358,10 @@ enum languages { lang_c, lang_cplusplus }; #define NON_UNION_CLASS_TYPE_P(T) \ (TREE_CODE (T) == RECORD_TYPE && TYPE_LANG_FLAG_5 (T)) +/* Nonzero if T is a class type and is a union. */ +#define UNION_TYPE_P(T) \ + (TREE_CODE (T) == UNION_TYPE && TYPE_LANG_FLAG_5 (T)) + /* Keep these checks in ascending code order. */ #define RECORD_OR_UNION_CODE_P(T) \ ((T) == RECORD_TYPE || (T) == UNION_TYPE) @@ -2489,6 +2499,7 @@ struct GTY(()) lang_type { unsigned unique_obj_representations_set : 1; bool erroneous : 1; bool non_pod_aggregate : 1; + bool non_aggregate_pod : 1; /* When adding a flag here, consider whether or not it ought to apply to a template instance if it applies to the template. If @@ -2497,7 +2508,7 @@ struct GTY(()) lang_type { /* There are some bits left to fill out a 32-bit word. Keep track of this by updating the size of this bitfield whenever you add or remove a flag. */ - unsigned dummy : 3; + unsigned dummy : 2; tree primary_base; vec<tree_pair_s, va_gc> *vcall_indices; @@ -2508,15 +2519,19 @@ struct GTY(()) lang_type { vec<tree, va_gc> *pure_virtuals; tree friend_classes; vec<tree, va_gc> * GTY((reorder ("resort_type_member_vec"))) members; + /* CLASSTYPE_KEY_METHOD for TYPE_POLYMORPHIC_P types, CLASSTYPE_LAMBDA_EXPR + otherwise. */ tree key_method; tree decl_list; tree befriending_classes; - /* In a RECORD_TYPE, information specific to Objective-C++, such - as a list of adopted protocols or a pointer to a corresponding - @interface. See objc/objc-act.h for details. */ - tree objc_info; - /* FIXME reuse another field? */ - tree lambda_expr; + union maybe_objc_info { + /* If not c_dialect_objc, this part is not even allocated. */ + char GTY((tag ("0"))) non_objc; + /* In a RECORD_TYPE, information specific to Objective-C, such + as a list of adopted protocols or a pointer to a corresponding + @interface. See objc/objc-act.h for details. */ + tree GTY((tag ("1"))) objc_info; + } GTY ((desc ("c_dialect_objc ()"))) info; }; /* We used to have a variant type for lang_type. Keep the name of the @@ -2631,7 +2646,13 @@ struct GTY(()) lang_type { /* The member function with which the vtable will be emitted: the first noninline non-pure-virtual member function. NULL_TREE if there is no key function or if this is a class template */ -#define CLASSTYPE_KEY_METHOD(NODE) (LANG_TYPE_CLASS_CHECK (NODE)->key_method) +#define CLASSTYPE_KEY_METHOD(NODE) \ + (TYPE_POLYMORPHIC_P (NODE) \ + ? LANG_TYPE_CLASS_CHECK (NODE)->key_method \ + : NULL_TREE) +#define SET_CLASSTYPE_KEY_METHOD(NODE, VALUE) \ + (gcc_checking_assert (TYPE_POLYMORPHIC_P (NODE)), \ + LANG_TYPE_CLASS_CHECK (NODE)->key_method = (VALUE)) /* Vector of members. During definition, it is unordered and only member functions are present. After completion it is sorted and @@ -2763,7 +2784,12 @@ struct GTY(()) lang_type { /* The associated LAMBDA_EXPR that made this class. */ #define CLASSTYPE_LAMBDA_EXPR(NODE) \ - (LANG_TYPE_CLASS_CHECK (NODE)->lambda_expr) + (TYPE_POLYMORPHIC_P (NODE) \ + ? NULL_TREE \ + : LANG_TYPE_CLASS_CHECK (NODE)->key_method) +#define SET_CLASSTYPE_LAMBDA_EXPR(NODE, VALUE) \ + (gcc_checking_assert (!TYPE_POLYMORPHIC_P (NODE)), \ + LANG_TYPE_CLASS_CHECK (NODE)->key_method = (VALUE)) /* The extra mangling scope for this closure type. */ #define LAMBDA_TYPE_EXTRA_SCOPE(NODE) \ (LAMBDA_EXPR_EXTRA_SCOPE (CLASSTYPE_LAMBDA_EXPR (NODE))) @@ -2824,6 +2850,11 @@ struct GTY(()) lang_type { with a hash_set only filled in when abi_version_crosses (17). */ #define CLASSTYPE_NON_POD_AGGREGATE(NODE) \ (LANG_TYPE_CLASS_CHECK (NODE)->non_pod_aggregate) + +/* True if this class is layout-POD though it's not an aggregate in C++20 and + above (c++/120012). This could also be a hash_set. */ +#define CLASSTYPE_NON_AGGREGATE_POD(NODE) \ + (LANG_TYPE_CLASS_CHECK (NODE)->non_aggregate_pod) /* Additional macros for inheritance information. */ @@ -2967,7 +2998,10 @@ struct GTY(()) lang_decl_base { unsigned module_keyed_decls_p : 1; /* has keys, applies to all decls */ - /* 11 spare bits. */ + /* VAR_DECL being used to represent an OpenMP declared mapper. */ + unsigned omp_declare_mapper_p : 1; + + /* 10 spare bits. */ }; /* True for DECL codes which have template info and access. */ @@ -2996,6 +3030,8 @@ struct GTY(()) lang_decl_min { In a lambda-capture proxy VAR_DECL, this is DECL_CAPTURED_VARIABLE. In a function-scope TREE_STATIC VAR_DECL or IMPLICIT_TYPEDEF_P TYPE_DECL, this is DECL_DISCRIMINATOR. + In constexpr exception artificial VAR_DECL, this is + DECL_EXCEPTION_REFCOUNT. In a DECL_LOCAL_DECL_P decl, this is the namespace decl it aliases. Otherwise, in a class-scope DECL, this is DECL_ACCESS. */ tree access; @@ -4450,6 +4486,23 @@ get_vec_init_expr (tree t) #define MUST_NOT_THROW_COND(NODE) \ TREE_OPERAND (MUST_NOT_THROW_EXPR_CHECK (NODE), 1) +/* Reasons why MUST_NOT_THROW_EXPR has been created. */ + +/* Indicates MUST_NOT_THROW_EXPR has been created to wrap body of + a noexcept function. */ +#define MUST_NOT_THROW_NOEXCEPT_P(NODE) \ + TREE_LANG_FLAG_0 (MUST_NOT_THROW_EXPR_CHECK (NODE)) + +/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of + exception object during throw. */ +#define MUST_NOT_THROW_THROW_P(NODE) \ + TREE_LANG_FLAG_1 (MUST_NOT_THROW_EXPR_CHECK (NODE)) + +/* Indicates MUST_NOT_THROW_EXPR has been created to wrap construction of + handler parameter during catch. */ +#define MUST_NOT_THROW_CATCH_P(NODE) \ + TREE_LANG_FLAG_2 (MUST_NOT_THROW_EXPR_CHECK (NODE)) + /* The TYPE_MAIN_DECL for a class template type is a TYPE_DECL, not a TEMPLATE_DECL. This macro determines whether or not a given class type is really a template type, as opposed to an instantiation or @@ -4470,11 +4523,14 @@ get_vec_init_expr (tree t) #define TYPENAME_IS_ENUM_P(NODE) \ (TREE_LANG_FLAG_0 (TYPENAME_TYPE_CHECK (NODE))) -/* True if a TYPENAME_TYPE was declared as a "class", "struct", or - "union". */ +/* True if a TYPENAME_TYPE was declared as a "class" or "struct". */ #define TYPENAME_IS_CLASS_P(NODE) \ (TREE_LANG_FLAG_1 (TYPENAME_TYPE_CHECK (NODE))) +/* True if a TYPENAME_TYPE was declared as a "union". */ +#define TYPENAME_IS_UNION_P(NODE) \ + (TREE_LANG_FLAG_3 (TYPENAME_TYPE_CHECK (NODE))) + /* True if a TYPENAME_TYPE is in the process of being resolved. */ #define TYPENAME_IS_RESOLVING_P(NODE) \ (TREE_LANG_FLAG_2 (TYPENAME_TYPE_CHECK (NODE))) @@ -4489,7 +4545,7 @@ get_vec_init_expr (tree t) #define TYPE_CONTAINS_VPTR_P(NODE) \ (TYPE_POLYMORPHIC_P (NODE) || CLASSTYPE_VBASECLASSES (NODE)) -/* Nonzero if NODE is a FUNCTION_DECL or VARIABLE_DECL (for a decl +/* Nonzero if NODE is a FUNCTION_DECL or VAR_DECL (for a decl with namespace scope) declared in a local scope. */ #define DECL_LOCAL_DECL_P(NODE) \ DECL_LANG_FLAG_0 (VAR_OR_FUNCTION_DECL_CHECK (NODE)) @@ -4522,6 +4578,11 @@ get_vec_init_expr (tree t) #define DECL_OMP_DECLARE_REDUCTION_P(NODE) \ (LANG_DECL_FN_CHECK (DECL_COMMON_CHECK (NODE))->omp_declare_reduction_p) +/* Nonzero if NODE is an artificial FUNCTION_DECL for + #pragma omp declare mapper. */ +#define DECL_OMP_DECLARE_MAPPER_P(NODE) \ + (DECL_LANG_SPECIFIC (VAR_DECL_CHECK (NODE))->u.base.omp_declare_mapper_p) + /* Nonzero if DECL has been declared threadprivate by #pragma omp threadprivate. */ #define CP_DECL_THREADPRIVATE_P(DECL) \ @@ -5125,6 +5186,10 @@ get_vec_init_expr (tree t) protected_access_node will appear in the DECL_ACCESS for the node. */ #define DECL_ACCESS(NODE) (LANG_DECL_MIN_CHECK (NODE)->access) +/* In artificial VAR_DECL created by cxa_allocate_exception + this is reference count. */ +#define DECL_EXCEPTION_REFCOUNT(NODE) (LANG_DECL_MIN_CHECK (NODE)->access) + /* Nonzero if the FUNCTION_DECL is a global constructor. */ #define DECL_GLOBAL_CTOR_P(NODE) \ (LANG_DECL_FN_CHECK (NODE)->global_ctor_p) @@ -5514,6 +5579,10 @@ decl_template_parm_check (const_tree t, const char *f, int l, const char *fn) #define DECL_RAMP_FN(NODE) \ (coro_get_ramp_function (NODE)) +/* For a FUNCTION_DECL this is true if it is a coroutine ramp. */ +#define DECL_RAMP_P(NODE) \ + DECL_COROUTINE_P (NODE) && !DECL_RAMP_FN (NODE) + /* True for an OMP_ATOMIC that has dependent parameters. These are stored as an expr in operand 1, and integer_zero_node or clauses in operand 0. */ #define OMP_ATOMIC_DEPENDENT_P(NODE) \ @@ -6753,8 +6822,14 @@ struct GTY((chain_next ("%h.next"))) tinst_level { /* The location where the template is instantiated. */ location_t locus; - /* errorcount + sorrycount when we pushed this level. */ - unsigned short errors; + /* errorcount + sorrycount when we pushed this level. If the value + overflows, it will always seem like we currently have more errors, so we + will limit template recursion even from non-erroneous templates. In a TU + with over 32k errors, that's fine. */ + unsigned short errors : 15; + + /* set in pop_tinst_level if there have been errors since we pushed. */ + bool had_errors : 1; /* Count references to this object. If refcount reaches refcount_infinity value, we don't increment or decrement the @@ -6775,6 +6850,7 @@ enum cp_built_in_function { CP_BUILT_IN_IS_CORRESPONDING_MEMBER, CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS, CP_BUILT_IN_SOURCE_LOCATION, + CP_BUILT_IN_EH_PTR_ADJUST_REF, CP_BUILT_IN_LAST }; @@ -6808,6 +6884,7 @@ extern int class_dump_id; extern int module_dump_id; extern int raw_dump_id; extern int coro_dump_id; +extern int tinst_dump_id; /* Whether the current context is manifestly constant-evaluated. Used by the constexpr machinery to control folding of @@ -6954,6 +7031,7 @@ extern bool type_has_extended_temps (tree); extern tree strip_top_quals (tree); extern bool reference_related_p (tree, tree); extern bool reference_compatible_p (tree, tree); +extern bool handler_match_for_exception_type (tree, tree); extern int remaining_arguments (tree); extern tree build_implicit_conv_flags (tree, tree, int); extern tree perform_implicit_conversion (tree, tree, tsubst_flags_t); @@ -7042,6 +7120,7 @@ extern tree in_class_defaulted_default_constructor (tree); extern bool user_provided_p (tree); extern bool type_has_user_provided_constructor (tree); extern bool type_has_non_user_provided_default_constructor (tree); +extern bool type_has_converting_constructor (tree); extern bool vbase_has_user_provided_move_assign (tree); extern tree default_init_uninitialized_part (tree); extern bool trivial_default_constructor_is_constexpr (tree); @@ -7072,6 +7151,7 @@ extern void adjust_clone_args (tree); extern void deduce_noexcept_on_destructor (tree); extern bool uniquely_derived_from_p (tree, tree); extern bool publicly_uniquely_derived_p (tree, tree); +extern bool publicly_virtually_derived_p (tree, tree); extern tree common_enclosing_class (tree, tree); /* in cvt.cc */ @@ -7307,6 +7387,29 @@ extern void cp_check_const_attributes (tree); extern void maybe_propagate_warmth_attributes (tree, tree); /* in error.cc */ +/* A class for pretty-printing to -flang-dump-XXX files. Used like + + if (cxx_dump_pretty_printer pp {foo_dump_id}) + { + pp_printf (&pp, ...); + } + + If the dump is enabled, the pretty printer will open the dump file and + attach to it, and flush and close the file on destruction. */ + +class cxx_dump_pretty_printer: public pretty_printer +{ + int phase; + FILE *outf; + dump_flags_t flags; + +public: + cxx_dump_pretty_printer (int phase); + operator bool() { return outf != nullptr; } + bool has_flag (dump_flags_t f) { return (flags & f); } + ~cxx_dump_pretty_printer (); +}; + extern const char *type_as_string (tree, int); extern const char *type_as_string_translate (tree, int); extern const char *decl_as_string (tree, int); @@ -7502,6 +7605,8 @@ extern bool ctor_omit_inherited_parms (tree); extern tree locate_ctor (tree); extern tree implicitly_declare_fn (special_function_kind, tree, bool, tree, tree); +extern tree type_order_value (tree, tree); + /* In module.cc */ class module_state; /* Forward declare. */ inline bool modules_p () { return flag_modules != 0; } @@ -7618,7 +7723,6 @@ extern void cp_finish_omp_range_for (tree, tree); extern bool cp_maybe_parse_omp_decl (tree, tree); extern bool parsing_nsdmi (void); extern bool parsing_function_declarator (); -extern bool parsing_default_capturing_generic_lambda_in_template (void); extern void inject_this_parameter (tree, cp_cv_quals); extern location_t defparse_location (tree); extern void maybe_show_extern_c_location (void); @@ -7759,6 +7863,7 @@ extern bool type_dependent_expression_p_push (tree); extern bool value_dependent_expression_p (tree); extern bool instantiation_dependent_uneval_expression_p (tree); extern bool any_value_dependent_elements_p (const_tree); +extern bool dependent_template_arg_p (tree); extern bool dependent_omp_for_p (tree, tree, tree, tree, tree); extern tree resolve_typename_type (tree, bool); extern tree template_for_substitution (tree); @@ -7906,6 +8011,7 @@ extern bool perform_deferred_access_checks (tsubst_flags_t); extern bool perform_or_defer_access_check (tree, tree, tree, tsubst_flags_t, access_failure_info *afi = NULL); +extern tree maybe_convert_cond (tree); /* RAII sentinel to ensures that deferred access checks are popped before a function returns. */ @@ -8054,11 +8160,14 @@ extern tree finish_qualified_id_expr (tree, tree, bool, bool, extern void simplify_aggr_init_expr (tree *); extern void finalize_nrv (tree, tree); extern tree omp_reduction_id (enum tree_code, tree, tree); +extern tree omp_mapper_id (tree, tree); extern tree cp_remove_omp_priv_cleanup_stmt (tree *, int *, void *); extern bool cp_check_omp_declare_reduction (tree); +extern bool cp_check_omp_declare_mapper (tree); extern void finish_omp_declare_simd_methods (tree); extern tree cp_finish_omp_init_prefer_type (tree); extern tree finish_omp_clauses (tree, enum c_omp_region_type); +extern tree omp_instantiate_mappers (tree); extern tree push_omp_privatization_clauses (bool); extern void pop_omp_privatization_clauses (tree); extern void save_omp_privatization_clauses (vec<tree> &); @@ -8654,6 +8763,10 @@ extern tree cxx_omp_clause_copy_ctor (tree, tree, tree); extern tree cxx_omp_clause_assign_op (tree, tree, tree); extern tree cxx_omp_clause_dtor (tree, tree); extern void cxx_omp_finish_clause (tree, gimple_seq *, bool); +extern tree cxx_omp_finish_mapper_clauses (tree); +extern tree cxx_omp_mapper_lookup (tree, tree); +extern tree cxx_omp_extract_mapper_directive (tree); +extern tree cxx_omp_map_array_section (location_t, tree); extern bool cxx_omp_privatize_by_reference (const_tree); extern bool cxx_omp_disregard_value_expr (tree, bool); extern void cp_fold_function (tree); @@ -8780,7 +8893,8 @@ extern hashval_t iterative_hash_constraint (tree, hashval_t); extern hashval_t hash_atomic_constraint (tree); extern void diagnose_constraints (location_t, tree, tree); -extern void note_failed_type_completion_for_satisfaction (tree); +extern void note_failed_type_completion (tree, tsubst_flags_t); +extern location_t failed_completion_location (tree); /* in logic.cc */ extern bool subsumes (tree, tree); @@ -8837,6 +8951,7 @@ extern void cxx_constant_dtor (tree, tree); extern tree cxx_constant_init (tree, tree = NULL_TREE); extern tree maybe_constant_value (tree, tree = NULL_TREE, mce_value = mce_unknown); extern tree maybe_constant_init (tree, tree = NULL_TREE, bool = false); +extern tree maybe_constant_init (tree, tree, mce_value); extern tree fold_non_dependent_expr (tree, tsubst_flags_t = tf_warning_or_error, bool = false, tree = NULL_TREE); @@ -8847,7 +8962,7 @@ extern tree fold_non_dependent_init (tree, bool = false, tree = NULL_TREE); extern tree fold_simple (tree); extern tree fold_to_constant (tree); -extern bool reduced_constant_expression_p (tree); +extern bool reduced_constant_expression_p (tree, tree = NULL_TREE); extern bool is_instantiation_of_constexpr (tree); extern bool var_in_constexpr_fn (tree); extern bool var_in_maybe_constexpr_fn (tree); diff --git a/gcc/cp/cxx-pretty-print.cc b/gcc/cp/cxx-pretty-print.cc index cf301bd..5f24015 100644 --- a/gcc/cp/cxx-pretty-print.cc +++ b/gcc/cp/cxx-pretty-print.cc @@ -24,7 +24,6 @@ along with GCC; see the file COPYING3. If not see #include "cp-tree.h" #include "cxx-pretty-print.h" #include "tree-pretty-print.h" -#include "make-unique.h" static void pp_cxx_unqualified_id (cxx_pretty_printer *, tree); static void pp_cxx_nested_name_specifier (cxx_pretty_printer *, tree); @@ -2954,5 +2953,5 @@ cxx_pretty_printer::cxx_pretty_printer () std::unique_ptr<pretty_printer> cxx_pretty_printer::clone () const { - return ::make_unique<cxx_pretty_printer> (*this); + return std::make_unique<cxx_pretty_printer> (*this); } diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index 2ed94fd..843f0e4 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -846,11 +846,9 @@ poplevel (int keep, int reverse, int functionbody) DECL_INITIAL (current_function_decl) = block ? block : subblocks; if (subblocks) { - if (FUNCTION_NEEDS_BODY_BLOCK (current_function_decl)) - { - if (BLOCK_SUBBLOCKS (subblocks)) - BLOCK_OUTER_CURLY_BRACE_P (BLOCK_SUBBLOCKS (subblocks)) = 1; - } + if (FUNCTION_NEEDS_BODY_BLOCK (current_function_decl) + && BLOCK_SUBBLOCKS (subblocks)) + BLOCK_OUTER_CURLY_BRACE_P (BLOCK_SUBBLOCKS (subblocks)) = 1; else BLOCK_OUTER_CURLY_BRACE_P (subblocks) = 1; } @@ -1313,7 +1311,15 @@ maybe_version_functions (tree newdecl, tree olddecl, bool record) } if (record) - cgraph_node::record_function_versions (olddecl, newdecl); + { + /* Add the new version to the function version structure. */ + cgraph_node *fn_node = cgraph_node::get_create (olddecl); + cgraph_function_version_info *fn_v = fn_node->function_version (); + if (!fn_v) + fn_v = fn_node->insert_new_function_version (); + + cgraph_node::add_function_version (fn_v, newdecl); + } return true; } @@ -2008,8 +2014,10 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden) } /* For function versions, params and types match, but they are not ambiguous. */ - else if ((!DECL_FUNCTION_VERSIONED (newdecl) - && !DECL_FUNCTION_VERSIONED (olddecl)) + else if (((!DECL_FUNCTION_VERSIONED (newdecl) + && !DECL_FUNCTION_VERSIONED (olddecl)) + || !same_type_p (fndecl_declared_return_type (newdecl), + fndecl_declared_return_type (olddecl))) /* Let constrained hidden friends coexist for now, we'll check satisfaction later. */ && !member_like_constrained_friend_p (newdecl) @@ -4362,6 +4370,7 @@ struct typename_info { tree template_id; bool enum_p; bool class_p; + bool union_p; }; struct typename_hasher : ggc_ptr_hash<tree_node> @@ -4400,7 +4409,8 @@ struct typename_hasher : ggc_ptr_hash<tree_node> && TYPE_CONTEXT (t1) == t2->scope && TYPENAME_TYPE_FULLNAME (t1) == t2->template_id && TYPENAME_IS_ENUM_P (t1) == t2->enum_p - && TYPENAME_IS_CLASS_P (t1) == t2->class_p); + && TYPENAME_IS_CLASS_P (t1) == t2->class_p + && TYPENAME_IS_UNION_P (t1) == t2->union_p); } }; @@ -4424,9 +4434,8 @@ build_typename_type (tree context, tree name, tree fullname, ti.name = name; ti.template_id = fullname; ti.enum_p = tag_type == enum_type; - ti.class_p = (tag_type == class_type - || tag_type == record_type - || tag_type == union_type); + ti.class_p = (tag_type == class_type || tag_type == record_type); + ti.union_p = tag_type == union_type; hashval_t hash = typename_hasher::hash (&ti); /* See if we already have this type. */ @@ -4442,6 +4451,7 @@ build_typename_type (tree context, tree name, tree fullname, TYPENAME_TYPE_FULLNAME (t) = ti.template_id; TYPENAME_IS_ENUM_P (t) = ti.enum_p; TYPENAME_IS_CLASS_P (t) = ti.class_p; + TYPENAME_IS_UNION_P (t) = ti.union_p; /* Build the corresponding TYPE_DECL. */ tree d = build_decl (input_location, TYPE_DECL, name, t); @@ -5072,6 +5082,18 @@ cxx_init_decl_processing (void) BUILT_IN_FRONTEND, NULL, NULL_TREE); set_call_expr_flags (decl, ECF_CONST | ECF_NOTHROW | ECF_LEAF); + if (cxx_dialect >= cxx26) + { + tree void_ptrintftype + = build_function_type_list (void_type_node, ptr_type_node, + integer_type_node, NULL_TREE); + decl = add_builtin_function ("__builtin_eh_ptr_adjust_ref", + void_ptrintftype, + CP_BUILT_IN_EH_PTR_ADJUST_REF, + BUILT_IN_FRONTEND, NULL, NULL_TREE); + set_call_expr_flags (decl, ECF_NOTHROW | ECF_LEAF); + } + integer_two_node = build_int_cst (NULL_TREE, 2); /* Guess at the initial static decls size. */ @@ -5339,6 +5361,8 @@ cp_make_fname_decl (location_t loc, tree id, int type_dep) decl = pushdecl_outermost_localscope (decl); if (decl != error_mark_node) add_decl_expr (decl); + else + gcc_assert (seen_error ()); } else { @@ -6198,22 +6222,28 @@ start_decl (const cp_declarator *declarator, } if (current_function_decl && VAR_P (decl) - && DECL_DECLARED_CONSTEXPR_P (current_function_decl) + && maybe_constexpr_fn (current_function_decl) && cxx_dialect < cxx23) { bool ok = false; if (CP_DECL_THREAD_LOCAL_P (decl) && !DECL_REALLY_EXTERN (decl)) - error_at (DECL_SOURCE_LOCATION (decl), - "%qD defined %<thread_local%> in %qs function only " - "available with %<-std=c++23%> or %<-std=gnu++23%>", decl, - DECL_IMMEDIATE_FUNCTION_P (current_function_decl) - ? "consteval" : "constexpr"); + { + if (DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + error_at (DECL_SOURCE_LOCATION (decl), + "%qD defined %<thread_local%> in %qs function only " + "available with %<-std=c++23%> or %<-std=gnu++23%>", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); + } else if (TREE_STATIC (decl)) - error_at (DECL_SOURCE_LOCATION (decl), - "%qD defined %<static%> in %qs function only available " - "with %<-std=c++23%> or %<-std=gnu++23%>", decl, - DECL_IMMEDIATE_FUNCTION_P (current_function_decl) - ? "consteval" : "constexpr"); + { + if (DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + error_at (DECL_SOURCE_LOCATION (decl), + "%qD defined %<static%> in %qs function only available " + "with %<-std=c++23%> or %<-std=gnu++23%>", decl, + DECL_IMMEDIATE_FUNCTION_P (current_function_decl) + ? "consteval" : "constexpr"); + } else ok = true; if (!ok) @@ -7871,6 +7901,12 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups) } else if (!init && DECL_REALLY_EXTERN (decl)) ; + else if (flag_openmp + && VAR_P (decl) + && DECL_LANG_SPECIFIC (decl) + && DECL_OMP_DECLARE_MAPPER_P (decl) + && TREE_CODE (init) == OMP_DECLARE_MAPPER) + return NULL_TREE; else if (init || type_build_ctor_call (type) || TYPE_REF_P (type)) { @@ -8897,13 +8933,18 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, TREE_TYPE (decl) = error_mark_node; return; } + + /* Now that we have a type, try these again. */ + layout_decl (decl, 0); cp_apply_type_quals_to_decl (cp_type_quals (type), decl); /* Update the type of the corresponding TEMPLATE_DECL to match. */ - if (DECL_LANG_SPECIFIC (decl) - && DECL_TEMPLATE_INFO (decl) - && DECL_TEMPLATE_RESULT (DECL_TI_TEMPLATE (decl)) == decl) - TREE_TYPE (DECL_TI_TEMPLATE (decl)) = type; + if (DECL_LANG_SPECIFIC (decl) && DECL_TEMPLATE_INFO (decl)) + { + tree tmpl = template_for_substitution (decl); + if (DECL_TEMPLATE_RESULT (tmpl) == decl) + TREE_TYPE (tmpl) = type; + } } if (ensure_literal_type_for_constexpr_object (decl) == error_mark_node) @@ -9147,6 +9188,10 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, if (decomp) { + if (DECL_DECLARED_CONSTINIT_P (decl) && cxx_dialect < cxx26) + pedwarn (DECL_SOURCE_LOCATION (decl), OPT_Wc__26_extensions, + "%<constinit%> can be applied to structured binding " + "only with %<-std=c++2c%> or %<-std=gnu++2c%>"); cp_maybe_mangle_decomp (decl, decomp); if (TREE_STATIC (decl) && !DECL_FUNCTION_SCOPE_P (decl)) { @@ -9188,14 +9233,23 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, varpool_node::get_create (decl); } + if (flag_openmp + && VAR_P (decl) + && DECL_LANG_SPECIFIC (decl) + && DECL_OMP_DECLARE_MAPPER_P (decl) + && init) + { + gcc_assert (TREE_CODE (init) == OMP_DECLARE_MAPPER); + DECL_INITIAL (decl) = init; + } /* Convert the initializer to the type of DECL, if we have not already initialized DECL. */ - if (!DECL_INITIALIZED_P (decl) - /* If !DECL_EXTERNAL then DECL is being defined. In the - case of a static data member initialized inside the - class-specifier, there can be an initializer even if DECL - is *not* defined. */ - && (!DECL_EXTERNAL (decl) || init)) + else if (!DECL_INITIALIZED_P (decl) + /* If !DECL_EXTERNAL then DECL is being defined. In the + case of a static data member initialized inside the + class-specifier, there can be an initializer even if DECL + is *not* defined. */ + && (!DECL_EXTERNAL (decl) || init)) { cleanups = make_tree_vector (); init = check_initializer (decl, init, flags, &cleanups); @@ -11294,11 +11348,16 @@ grokfndecl (tree ctype, "cannot declare %<::main%> to be %qs", "consteval"); if (!publicp) error_at (location, "cannot declare %<::main%> to be static"); - if (current_lang_depth () != 0) + if (current_lang_name != lang_name_cplusplus) pedwarn (location, OPT_Wpedantic, "cannot declare %<::main%> with a" - " linkage specification"); + " linkage specification other than %<extern \"C++\"%>"); if (module_attach_p ()) - error_at (location, "cannot attach %<::main%> to a named module"); + { + auto_diagnostic_group adg; + error_at (location, "cannot attach %<::main%> to a named module"); + inform (location, "use %<extern \"C++\"%> to attach it to the " + "global module instead"); + } inlinep = 0; publicp = 1; } @@ -13580,9 +13639,10 @@ grokdeclarator (const cp_declarator *declarator, if (typedef_p) error_at (declspecs->locations[ds_typedef], "structured binding declaration cannot be %qs", "typedef"); - if (constexpr_p && !concept_p) - error_at (declspecs->locations[ds_constexpr], "structured " - "binding declaration cannot be %qs", "constexpr"); + if (constexpr_p && !concept_p && cxx_dialect < cxx26) + pedwarn (declspecs->locations[ds_constexpr], OPT_Wc__26_extensions, + "structured binding declaration can be %qs only with " + "%<-std=c++2c%> or %<-std=gnu++2c%>", "constexpr"); if (consteval_p) error_at (declspecs->locations[ds_consteval], "structured " "binding declaration cannot be %qs", "consteval"); @@ -13593,8 +13653,11 @@ grokdeclarator (const cp_declarator *declarator, declspecs->gnu_thread_keyword_p ? "__thread" : "thread_local"); if (concept_p) - error_at (declspecs->locations[ds_concept], - "structured binding declaration cannot be %qs", "concept"); + { + error_at (declspecs->locations[ds_concept], + "structured binding declaration cannot be %qs", "concept"); + constexpr_p = 0; + } /* [dcl.struct.bind] "A cv that includes volatile is deprecated." */ if (type_quals & TYPE_QUAL_VOLATILE) warning_at (declspecs->locations[ds_volatile], OPT_Wvolatile, @@ -13649,7 +13712,6 @@ grokdeclarator (const cp_declarator *declarator, "%<auto%> type %qT", type); inlinep = 0; typedef_p = 0; - constexpr_p = 0; consteval_p = 0; concept_p = 0; if (storage_class != sc_static) @@ -17227,7 +17289,7 @@ xref_tag (enum tag_types tag_code, tree name, if (IDENTIFIER_LAMBDA_P (name)) /* Mark it as a lambda type right now. Our caller will correct the value. */ - CLASSTYPE_LAMBDA_EXPR (t) = error_mark_node; + SET_CLASSTYPE_LAMBDA_EXPR (t, error_mark_node); t = pushtag (name, t, how); } else @@ -19304,6 +19366,19 @@ finish_function (bool inline_p) } } + if (FNDECL_USED_AUTO (fndecl) + && TREE_TYPE (fntype) != DECL_SAVED_AUTO_RETURN_TYPE (fndecl)) + if (location_t fcloc = failed_completion_location (fndecl)) + { + auto_diagnostic_group adg; + if (warning (OPT_Wsfinae_incomplete_, + "defining %qD, which previously failed to be deduced " + "in a SFINAE context", fndecl) + && warn_sfinae_incomplete == 1) + inform (fcloc, "here. Use %qs for a diagnostic at that point", + "-Wsfinae-incomplete=2"); + } + /* Remember that we were in class scope. */ if (current_class_name) ctype = current_class_type; @@ -19834,14 +19909,14 @@ cp_tree_node_structure (union lang_tree_node * t) { switch (TREE_CODE (&t->generic)) { - case ARGUMENT_PACK_SELECT: return TS_CP_ARGUMENT_PACK_SELECT; + case ARGUMENT_PACK_SELECT: return TS_CP_ARGUMENT_PACK_SELECT; case BASELINK: return TS_CP_BASELINK; - case CONSTRAINT_INFO: return TS_CP_CONSTRAINT_INFO; + case CONSTRAINT_INFO: return TS_CP_CONSTRAINT_INFO; case DEFERRED_NOEXCEPT: return TS_CP_DEFERRED_NOEXCEPT; case DEFERRED_PARSE: return TS_CP_DEFERRED_PARSE; case IDENTIFIER_NODE: return TS_CP_IDENTIFIER; case LAMBDA_EXPR: return TS_CP_LAMBDA_EXPR; - case BINDING_VECTOR: return TS_CP_BINDING_VECTOR; + case BINDING_VECTOR: return TS_CP_BINDING_VECTOR; case OVERLOAD: return TS_CP_OVERLOAD; case PTRMEM_CST: return TS_CP_PTRMEM; case STATIC_ASSERT: return TS_CP_STATIC_ASSERT; @@ -19849,6 +19924,7 @@ cp_tree_node_structure (union lang_tree_node * t) case TEMPLATE_INFO: return TS_CP_TEMPLATE_INFO; case TEMPLATE_PARM_INDEX: return TS_CP_TPI; case TRAIT_EXPR: return TS_CP_TRAIT_EXPR; + case TU_LOCAL_ENTITY: return TS_CP_TU_LOCAL_ENTITY; case USERDEF_LITERAL: return TS_CP_USERDEF_LITERAL; default: return TS_CP_GENERIC; } @@ -19956,7 +20032,7 @@ require_deduced_type (tree decl, tsubst_flags_t complain) /* We probably already complained about deduction failure. */; else if (complain & tf_error) error ("use of %qD before deduction of %<auto%>", decl); - note_failed_type_completion_for_satisfaction (decl); + note_failed_type_completion (decl, complain); return false; } return true; diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index 21156f1..2bbc618 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -3160,7 +3160,9 @@ determine_visibility (tree decl) && !attr) { int depth = TMPL_ARGS_DEPTH (args); - if (DECL_VISIBILITY_SPECIFIED (decl)) + if (DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (TI_TEMPLATE (tinfo))) + /* Class template args don't affect template friends. */; + else if (DECL_VISIBILITY_SPECIFIED (decl)) { /* A class template member with explicit visibility overrides the class visibility, so we need to apply @@ -4026,6 +4028,7 @@ get_tls_init_fn (tree var) SET_DECL_LANGUAGE (fn, lang_c); TREE_PUBLIC (fn) = TREE_PUBLIC (var); DECL_ARTIFICIAL (fn) = true; + DECL_CONTEXT (fn) = FROB_CONTEXT (global_namespace); DECL_COMDAT (fn) = DECL_COMDAT (var); DECL_EXTERNAL (fn) = DECL_EXTERNAL (var); if (DECL_ONE_ONLY (var)) @@ -4085,7 +4088,7 @@ get_tls_wrapper_fn (tree var) TREE_PUBLIC (fn) = TREE_PUBLIC (var); DECL_ARTIFICIAL (fn) = true; DECL_IGNORED_P (fn) = 1; - DECL_CONTEXT (fn) = DECL_CONTEXT (var); + DECL_CONTEXT (fn) = FROB_CONTEXT (global_namespace); /* The wrapper is inline and emitted everywhere var is used. */ DECL_DECLARED_INLINE_P (fn) = true; if (TREE_PUBLIC (var)) @@ -4184,7 +4187,11 @@ start_objects (bool initp, unsigned priority, bool has_body, bool omp_target = false) { bool default_init = initp && priority == DEFAULT_INIT_PRIORITY; - bool is_module_init = default_init && module_global_init_needed (); + /* FIXME: We may eventually want to treat OpenMP offload initializers + in modules specially as well. */ + bool is_module_init = (default_init + && !omp_target + && module_global_init_needed ()); tree name = NULL_TREE; if (is_module_init) @@ -5876,12 +5883,8 @@ c_parse_final_cleanups (void) if (static_init_fini_fns[true]->get_or_insert (DEFAULT_INIT_PRIORITY)) has_module_inits = true; - if (flag_openmp) - { - if (!static_init_fini_fns[2 + true]) - static_init_fini_fns[2 + true] = priority_map_t::create_ggc (); - static_init_fini_fns[2 + true]->get_or_insert (DEFAULT_INIT_PRIORITY); - } + /* FIXME: We need to work out what static constructors on OpenMP offload + target in modules will look like. */ } /* Generate initialization and destruction functions for all @@ -6266,6 +6269,33 @@ mark_single_function (tree expr, tsubst_flags_t complain) return true; } +/* True iff we have started, but not finished, defining FUNCTION_DECL DECL. */ + +bool +fn_being_defined (tree decl) +{ + /* DECL_INITIAL is set to error_mark_node in grokfndecl for a definition, and + changed to BLOCK by poplevel at the end of the function. */ + return (TREE_CODE (decl) == FUNCTION_DECL + && DECL_INITIAL (decl) == error_mark_node); +} + +/* True if DECL is an instantiation of a function template currently being + defined. */ + +bool +fn_template_being_defined (tree decl) +{ + if (TREE_CODE (decl) != FUNCTION_DECL + || !DECL_LANG_SPECIFIC (decl) + || !DECL_TEMPLOID_INSTANTIATION (decl) + || DECL_TEMPLATE_INSTANTIATED (decl)) + return false; + tree tinfo = DECL_TEMPLATE_INFO (decl); + tree pattern = DECL_TEMPLATE_RESULT (TI_TEMPLATE (tinfo)); + return fn_being_defined (pattern); +} + /* Mark DECL (either a _DECL or a BASELINK) as "used" in the program. If DECL is a specialization or implicitly declared class member, generate the actual definition. Return false if something goes @@ -6405,15 +6435,23 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */) /* If DECL has a deduced return type, we need to instantiate it now to find out its type. For OpenMP user defined reductions, we need them - instantiated for reduction clauses which inline them by hand directly. */ + instantiated for reduction clauses which inline them by hand directly. + OpenMP declared mappers are used implicitly so must be instantiated + before they can be detected. */ if (undeduced_auto_decl (decl) || (VAR_P (decl) && VAR_HAD_UNKNOWN_BOUND (decl)) || (TREE_CODE (decl) == FUNCTION_DECL - && DECL_OMP_DECLARE_REDUCTION_P (decl))) + && DECL_OMP_DECLARE_REDUCTION_P (decl)) + || (TREE_CODE (decl) == VAR_DECL + && DECL_LANG_SPECIFIC (decl) + && DECL_OMP_DECLARE_MAPPER_P (decl))) maybe_instantiate_decl (decl); if (!decl_dependent_p (decl) + /* Don't require this yet for an instantiation of a function template + we're currently defining (c++/120555). */ + && !fn_template_being_defined (decl) && !require_deduced_type (decl, complain)) return false; @@ -6428,9 +6466,6 @@ mark_used (tree decl, tsubst_flags_t complain /* = tf_warning_or_error */) && uses_template_parms (DECL_TI_ARGS (decl))) return true; - if (!require_deduced_type (decl, complain)) - return false; - if (builtin_pack_fn_p (decl)) { error ("use of built-in parameter pack %qD outside of a template", diff --git a/gcc/cp/error.cc b/gcc/cp/error.cc index ec7527e..eb2ff33 100644 --- a/gcc/cp/error.cc +++ b/gcc/cp/error.cc @@ -38,7 +38,6 @@ along with GCC; see the file COPYING3. If not see #include "cp-name-hint.h" #include "attribs.h" #include "pretty-print-format-impl.h" -#include "make-unique.h" #include "diagnostic-format-text.h" #define pp_separate_with_comma(PP) pp_cxx_separate_with (PP, ',') @@ -183,9 +182,10 @@ class cxx_format_postprocessor : public format_postprocessor : m_type_a (), m_type_b () {} - format_postprocessor *clone() const final override + std::unique_ptr<format_postprocessor> + clone() const final override { - return new cxx_format_postprocessor (); + return std::make_unique<cxx_format_postprocessor> (); } void handle (pretty_printer *pp) final override; @@ -194,6 +194,32 @@ class cxx_format_postprocessor : public format_postprocessor deferred_printed_type m_type_b; }; +/* Constructor and destructor for cxx_dump_pretty_printer, defined here to + avoid needing to move cxx_format_postprocessor into the header as well. */ + +cxx_dump_pretty_printer:: +cxx_dump_pretty_printer (int phase) + : phase (phase) +{ + outf = dump_begin (phase, &flags); + if (outf) + { + pp_format_decoder (this) = cp_printer; + set_format_postprocessor (std::make_unique<cxx_format_postprocessor> ()); + set_output_stream (outf); + } +} + +cxx_dump_pretty_printer:: +~cxx_dump_pretty_printer () +{ + if (outf) + { + pp_flush (this); + dump_end (phase, outf); + } +} + /* Return the in-scope template that's currently being parsed, or NULL_TREE otherwise. */ @@ -275,14 +301,14 @@ void cxx_initialize_diagnostics (diagnostic_context *context) { cxx_pretty_printer *pp = new cxx_pretty_printer (); - pp_format_postprocessor (pp) = new cxx_format_postprocessor (); + pp->set_format_postprocessor (std::make_unique<cxx_format_postprocessor> ()); context->set_pretty_printer (std::unique_ptr<pretty_printer> (pp)); c_common_diagnostics_set_defaults (context); diagnostic_text_starter (context) = cp_diagnostic_text_starter; /* diagnostic_finalizer is already c_diagnostic_text_finalizer. */ context->set_format_decoder (cp_printer); - context->m_adjust_diagnostic_info = cp_adjust_diagnostic_info; + context->set_adjust_diagnostic_info_callback (cp_adjust_diagnostic_info); } /* Dump an '@module' name suffix for DECL, if it's attached to an import. */ @@ -542,12 +568,13 @@ dump_template_bindings (cxx_pretty_printer *pp, tree parms, tree args, /* If the template argument repeats the template parameter (T = T), skip the parameter.*/ if (arg && TREE_CODE (arg) == TEMPLATE_TYPE_PARM - && TREE_CODE (parm_i) == TREE_LIST - && TREE_CODE (TREE_VALUE (parm_i)) == TYPE_DECL - && TREE_CODE (TREE_TYPE (TREE_VALUE (parm_i))) - == TEMPLATE_TYPE_PARM - && DECL_NAME (TREE_VALUE (parm_i)) - == DECL_NAME (TREE_CHAIN (arg))) + && arg == TYPE_MAIN_VARIANT (arg) + && TREE_CODE (parm_i) == TREE_LIST + && TREE_CODE (TREE_VALUE (parm_i)) == TYPE_DECL + && (TREE_CODE (TREE_TYPE (TREE_VALUE (parm_i))) + == TEMPLATE_TYPE_PARM) + && (DECL_NAME (TREE_VALUE (parm_i)) + == DECL_NAME (TYPE_STUB_DECL (arg)))) continue; semicolon_or_introducer (); @@ -783,6 +810,7 @@ dump_type (cxx_pretty_printer *pp, tree t, int flags) pp_cxx_ws_string (pp, TYPENAME_IS_ENUM_P (t) ? "enum" : TYPENAME_IS_CLASS_P (t) ? "class" + : TYPENAME_IS_UNION_P (t) ? "union" : "typename"); dump_typename (pp, t, flags); break; @@ -1255,12 +1283,37 @@ dump_global_iord (cxx_pretty_printer *pp, tree t) pp_printf (pp, p, DECL_SOURCE_FILE (t)); } +/* Write a representation of OpenMP "declare mapper" T to PP in a manner + suitable for error messages. */ + +static void +dump_omp_declare_mapper (cxx_pretty_printer *pp, tree t, int flags) +{ + pp_string (pp, "#pragma omp declare mapper"); + if (t == NULL_TREE || t == error_mark_node) + return; + pp_space (pp); + pp_cxx_left_paren (pp); + if (OMP_DECLARE_MAPPER_ID (t)) + { + pp_cxx_tree_identifier (pp, OMP_DECLARE_MAPPER_ID (t)); + pp_colon (pp); + } + dump_type (pp, TREE_TYPE (t), flags); + pp_cxx_right_paren (pp); +} + static void dump_simple_decl (cxx_pretty_printer *pp, tree t, tree type, int flags) { if (VAR_P (t) && DECL_NTTP_OBJECT_P (t)) return dump_expr (pp, DECL_INITIAL (t), flags); + if (TREE_CODE (t) == VAR_DECL + && DECL_LANG_SPECIFIC (t) + && DECL_OMP_DECLARE_MAPPER_P (t)) + return dump_omp_declare_mapper (pp, DECL_INITIAL (t), flags); + if (flags & TFF_DECL_SPECIFIERS) { if (concept_definition_p (t)) @@ -3217,6 +3270,27 @@ dump_expr (cxx_pretty_printer *pp, tree t, int flags) break; } + case CO_AWAIT_EXPR: + pp_cxx_ws_string (pp, "co_await"); + pp_cxx_whitespace (pp); + dump_expr (pp, TREE_OPERAND (t, 0), flags); + break; + + case CO_YIELD_EXPR: + pp_cxx_ws_string (pp, "co_yield"); + pp_cxx_whitespace (pp); + dump_expr (pp, TREE_OPERAND (t, 0), flags); + break; + + case CO_RETURN_EXPR: + pp_cxx_ws_string (pp, "co_return"); + if (TREE_OPERAND (t, 0)) + { + pp_cxx_whitespace (pp); + dump_expr (pp, TREE_OPERAND (t, 0), flags); + } + break; + /* This list is incomplete, but should suffice for now. It is very important that `sorry' does not call `report_error_function'. That could cause an infinite loop. */ @@ -3788,18 +3862,18 @@ cp_print_error_function (diagnostic_text_output_format &text_output, { if (text_output.show_column_p () && s.column != 0) pp_printf (pp, - _(" inlined from %qD at %r%s:%d:%d%R"), + G_(" inlined from %qD at %r%s:%d:%d%R"), fndecl, "locus", s.file, s.line, s.column); else pp_printf (pp, - _(" inlined from %qD at %r%s:%d%R"), + G_(" inlined from %qD at %r%s:%d%R"), fndecl, "locus", s.file, s.line); } else - pp_printf (pp, _(" inlined from %qD"), + pp_printf (pp, G_(" inlined from %qD"), fndecl); } } @@ -3825,22 +3899,22 @@ function_category (tree fn) && DECL_FUNCTION_MEMBER_P (fn)) { if (DECL_STATIC_FUNCTION_P (fn)) - return _("In static member function %qD"); + return G_("In static member function %qD"); else if (DECL_COPY_CONSTRUCTOR_P (fn)) - return _("In copy constructor %qD"); + return G_("In copy constructor %qD"); else if (DECL_CONSTRUCTOR_P (fn)) - return _("In constructor %qD"); + return G_("In constructor %qD"); else if (DECL_DESTRUCTOR_P (fn)) - return _("In destructor %qD"); + return G_("In destructor %qD"); else if (LAMBDA_FUNCTION_P (fn)) - return _("In lambda function"); + return G_("In lambda function"); else if (DECL_XOBJ_MEMBER_FUNCTION_P (fn)) - return _("In explicit object member function %qD"); + return G_("In explicit object member function %qD"); else - return _("In member function %qD"); + return G_("In member function %qD"); } else - return _("In function %qD"); + return G_("In function %qD"); } /* Disable warnings about missing quoting in GCC diagnostics for @@ -3867,8 +3941,8 @@ print_instantiation_full_context (diagnostic_text_output_format &text_output) char *indent = text_output.build_indent_prefix (true); pp_verbatim (text_output.get_printer (), p->list_p () - ? _("%s%s%sIn substitution of %qS:\n") - : _("%s%s%sIn instantiation of %q#D:\n"), + ? G_("%s%s%sIn substitution of %qS:\n") + : G_("%s%s%sIn instantiation of %q#D:\n"), indent, show_file ? LOCATION_FILE (location) : "", show_file ? ": " : "", @@ -3888,10 +3962,10 @@ print_location (diagnostic_text_output_format &text_output, expanded_location xloc = expand_location (loc); pretty_printer *const pp = text_output.get_printer (); if (text_output.show_column_p ()) - pp_verbatim (pp, _("%r%s:%d:%d:%R "), + pp_verbatim (pp, G_("%r%s:%d:%d:%R "), "locus", xloc.file, xloc.line, xloc.column); else - pp_verbatim (pp, _("%r%s:%d:%R "), + pp_verbatim (pp, G_("%r%s:%d:%R "), "locus", xloc.file, xloc.line); } @@ -3984,22 +4058,22 @@ print_instantiation_partial_context_line (diagnostic_text_output_format &text_ou if (t->list_p ()) pp_verbatim (pp, recursive_p - ? _("recursively required by substitution of %qS\n") - : _("required by substitution of %qS\n"), + ? G_("recursively required by substitution of %qS\n") + : G_("required by substitution of %qS\n"), t->get_node ()); else pp_verbatim (pp, recursive_p - ? _("recursively required from %q#D\n") - : _("required from %q#D\n"), + ? G_("recursively required from %q#D\n") + : G_("required from %q#D\n"), t->get_node ()); } else { pp_verbatim (pp, recursive_p - ? _("recursively required from here\n") - : _("required from here\n")); + ? G_("recursively required from here\n") + : G_("required from here\n")); } } @@ -4049,8 +4123,8 @@ print_instantiation_partial_context (diagnostic_text_output_format &text_output, { auto_context_line sentinel (text_output, loc); pp_verbatim (text_output.get_printer (), - _("[ skipping %d instantiation contexts," - " use -ftemplate-backtrace-limit=0 to disable ]\n"), + G_("[ skipping %d instantiation contexts," + " use -ftemplate-backtrace-limit=0 to disable ]\n"), skip); do { loc = t->locus; @@ -4101,7 +4175,7 @@ maybe_print_constexpr_context (diagnostic_text_output_format &text_output) pretty_printer *const pp = text_output.get_printer (); auto_context_line sentinel (text_output, EXPR_LOCATION (t)); pp_verbatim (pp, - _("in %<constexpr%> expansion of %qs"), + G_("in %<constexpr%> expansion of %qs"), s); pp_newline (pp); } @@ -4114,7 +4188,7 @@ print_constrained_decl_info (diagnostic_text_output_format &text_output, { auto_context_line sentinel (text_output, DECL_SOURCE_LOCATION (decl)); pretty_printer *const pp = text_output.get_printer (); - pp_verbatim (pp, "required by the constraints of %q#D\n", decl); + pp_verbatim (pp, G_("required by the constraints of %q#D\n"), decl); } static void @@ -4129,7 +4203,7 @@ print_concept_check_info (diagnostic_text_output_format &text_output, cxx_pretty_printer *const pp = (cxx_pretty_printer *)text_output.get_printer (); - pp_verbatim (pp, "required for the satisfaction of %qE", expr); + pp_verbatim (pp, G_("required for the satisfaction of %qE"), expr); if (map && map != error_mark_node) { tree subst_map = tsubst_parameter_mapping (map, args, tf_none, NULL_TREE); @@ -4151,7 +4225,7 @@ print_constraint_context_head (diagnostic_text_output_format &text_output, { auto_context_line sentinel (text_output, input_location); pretty_printer *const pp = text_output.get_printer (); - pp_verbatim (pp, "required for constraint satisfaction\n"); + pp_verbatim (pp, G_("required for constraint satisfaction\n")); return NULL_TREE; } if (DECL_P (src)) @@ -4180,11 +4254,10 @@ print_requires_expression_info (diagnostic_text_output_format &text_output, auto_context_line sentinel (text_output, cp_expr_loc_or_input_loc (expr)); cxx_pretty_printer *const pp = static_cast <cxx_pretty_printer *> (text_output.get_printer ()); - pp_verbatim (pp, "in requirements "); tree parms = TREE_OPERAND (expr, 0); - if (parms) - pp_verbatim (pp, "with "); + pp_verbatim (pp, parms ? G_("in requirements with ") + : G_("in requirements ")); while (parms) { pp_verbatim (pp, "%q#D", parms); diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc index a9d8e2f..a7f35e4 100644 --- a/gcc/cp/except.cc +++ b/gcc/cp/except.cc @@ -367,6 +367,8 @@ initialize_handler_parm (tree decl, tree exp) MUST_NOT_THROW_EXPR. */ init = fold_build_cleanup_point_expr (TREE_TYPE (init), init); init = build_must_not_throw_expr (init, NULL_TREE); + if (init && TREE_CODE (init) == MUST_NOT_THROW_EXPR) + MUST_NOT_THROW_CATCH_P (init) = 1; } decl = pushdecl (decl); @@ -523,6 +525,7 @@ begin_eh_spec_block (void) r = build_stmt (spec_location, MUST_NOT_THROW_EXPR, NULL_TREE, NULL_TREE); TREE_SIDE_EFFECTS (r) = 1; + MUST_NOT_THROW_NOEXCEPT_P (r) = 1; } else r = build_stmt (spec_location, EH_SPEC_BLOCK, NULL_TREE, NULL_TREE); @@ -614,6 +617,7 @@ wrap_cleanups_r (tree *tp, int *walk_subtrees, void * /*data*/) { cleanup = build2 (MUST_NOT_THROW_EXPR, void_type_node, cleanup, NULL_TREE); + MUST_NOT_THROW_THROW_P (cleanup) = 1; TARGET_EXPR_CLEANUP (exp) = cleanup; } @@ -712,6 +716,11 @@ build_throw (location_t loc, tree exp, tsubst_flags_t complain) allocate_expr = do_allocate_exception (temp_type); if (allocate_expr == error_mark_node) return error_mark_node; + /* Copy ptr inside of the CLEANUP_POINT_EXPR + added below to a TARGET_EXPR slot added outside of it, + otherwise during constant evaluation of throw expression + we'd diagnose accessing ptr outside of its lifetime. */ + tree ptr_copy = get_internal_target_expr (null_pointer_node); allocate_expr = get_internal_target_expr (allocate_expr); ptr = TARGET_EXPR_SLOT (allocate_expr); TARGET_EXPR_CLEANUP (allocate_expr) = do_free_exception (ptr); @@ -763,10 +772,17 @@ build_throw (location_t loc, tree exp, tsubst_flags_t complain) /* Prepend the allocation. */ exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp); + exp = build2 (COMPOUND_EXPR, void_type_node, exp, + build2 (MODIFY_EXPR, void_type_node, + TARGET_EXPR_SLOT (ptr_copy), ptr)); + ptr = TARGET_EXPR_SLOT (ptr_copy); + /* Force all the cleanups to be evaluated here so that we don't have to do them during unwinding. */ exp = build1 (CLEANUP_POINT_EXPR, void_type_node, exp); + exp = build2 (COMPOUND_EXPR, TREE_TYPE (exp), ptr_copy, exp); + throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object))); cleanup = NULL_TREE; diff --git a/gcc/cp/init.cc b/gcc/cp/init.cc index e589e45..0a389fb 100644 --- a/gcc/cp/init.cc +++ b/gcc/cp/init.cc @@ -3010,7 +3010,6 @@ build_new_constexpr_heap_type (tree elt_type, tree cookie_size, tree itype2) tree atype1 = build_cplus_array_type (sizetype, itype1); tree atype2 = build_cplus_array_type (elt_type, itype2); tree rtype = cxx_make_type (RECORD_TYPE); - TYPE_NAME (rtype) = heap_identifier; tree fld1 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype1); tree fld2 = build_decl (UNKNOWN_LOCATION, FIELD_DECL, NULL_TREE, atype2); DECL_FIELD_CONTEXT (fld1) = rtype; @@ -3019,7 +3018,16 @@ build_new_constexpr_heap_type (tree elt_type, tree cookie_size, tree itype2) DECL_ARTIFICIAL (fld2) = true; TYPE_FIELDS (rtype) = fld1; DECL_CHAIN (fld1) = fld2; + TYPE_ARTIFICIAL (rtype) = true; layout_type (rtype); + + tree decl = build_decl (UNKNOWN_LOCATION, TYPE_DECL, heap_identifier, rtype); + TYPE_NAME (rtype) = decl; + TYPE_STUB_DECL (rtype) = decl; + DECL_CONTEXT (decl) = NULL_TREE; + DECL_ARTIFICIAL (decl) = true; + layout_decl (decl, 0); + return rtype; } @@ -3405,7 +3413,7 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, errval = throw_bad_array_new_length (); if (outer_nelts_check != NULL_TREE) size = build3 (COND_EXPR, sizetype, outer_nelts_check, size, errval); - size = cp_fully_fold (size); + size = fold_to_constant (size); /* Create the argument list. */ vec_safe_insert (*placement, 0, size); /* Do name-lookup to find the appropriate operator. */ @@ -3462,7 +3470,7 @@ build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts, outer_nelts_check = NULL_TREE; } - size = cp_fully_fold (size); + size = fold_to_constant (size); /* If size is zero e.g. due to type having zero size, try to preserve outer_nelts for constant expression evaluation purposes. */ @@ -4747,7 +4755,8 @@ build_vec_init (tree base, tree maxindex, tree init, itself. But that breaks when gimplify_target_expr adds a clobber cleanup that runs before the build_vec_init cleanup. */ if (cleanup_flags) - vec_safe_push (*cleanup_flags, build_tree_list (iterator, maxindex)); + vec_safe_push (*cleanup_flags, + build_tree_list (rval, build_zero_cst (ptype))); } /* Should we try to create a constant initializer? */ diff --git a/gcc/cp/lambda.cc b/gcc/cp/lambda.cc index ed70bb0..525e8ef 100644 --- a/gcc/cp/lambda.cc +++ b/gcc/cp/lambda.cc @@ -59,7 +59,13 @@ build_lambda_object (tree lambda_expr) vec<constructor_elt, va_gc> *elts = NULL; tree node, expr, type; - if (processing_template_decl || lambda_expr == error_mark_node) + if (processing_template_decl && !in_template_context + && current_binding_level->requires_expression) + /* As in cp_parser_lambda_expression, don't get confused by + cp_parser_requires_expression setting processing_template_decl. In that + case we want to return the result of finish_compound_literal, to avoid + tsubst_lambda_expr. */; + else if (processing_template_decl || lambda_expr == error_mark_node) return lambda_expr; /* Make sure any error messages refer to the lambda-introducer. */ @@ -144,7 +150,7 @@ begin_lambda_type (tree lambda) /* Cross-reference the expression and the type. */ LAMBDA_EXPR_CLOSURE (lambda) = type; - CLASSTYPE_LAMBDA_EXPR (type) = lambda; + SET_CLASSTYPE_LAMBDA_EXPR (type, lambda); /* In C++17, assume the closure is literal; we'll clear the flag later if necessary. */ @@ -212,9 +218,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p, tree type; bool is_this = is_this_parameter (tree_strip_nop_conversions (expr)); - if (is_this) - type = TREE_TYPE (expr); - else if (explicit_init_p) + if (explicit_init_p) { tree auto_node = make_auto (); @@ -253,7 +257,7 @@ lambda_capture_field_type (tree expr, bool explicit_init_p, type = non_reference (unlowered_expr_type (expr)); - if (by_reference_p || TREE_CODE (type) == FUNCTION_TYPE) + if ((by_reference_p && !is_this) || TREE_CODE (type) == FUNCTION_TYPE) type = build_reference_type (type); } @@ -342,7 +346,11 @@ insert_capture_proxy (tree var) /* And put a DECL_EXPR in the STATEMENT_LIST for the same block. */ var = build_stmt (DECL_SOURCE_LOCATION (var), DECL_EXPR, var); - tree stmt_list = (*stmt_list_stack)[1]; + /* The first stmt_list is from start_preparsed_function. Then there's a + possible stmt_list from begin_eh_spec_block, then the one from the + lambda's outer {}. */ + unsigned index = 1 + use_eh_spec_block (current_function_decl); + tree stmt_list = (*stmt_list_stack)[index]; gcc_assert (stmt_list); append_to_statement_list_force (var, &stmt_list); } @@ -430,13 +438,21 @@ build_capture_proxy (tree member, tree init) else name = get_identifier (IDENTIFIER_POINTER (DECL_NAME (member)) + 2); - type = lambda_proxy_type (object); - - if (name == this_identifier && !INDIRECT_TYPE_P (type)) + if (name == this_identifier && TYPE_PTR_P (TREE_TYPE (member))) + /* Avoid DECLTYPE_TYPE for by-ref 'this' capture in an xobj lambda; the + constness of the closure doesn't matter just like it doesn't matter to + other by-ref capture. It's simpler to handle this special case here + than in lambda_proxy_type. */ + type = TREE_TYPE (member); + else { - type = build_pointer_type (type); - type = cp_build_qualified_type (type, TYPE_QUAL_CONST); - object = build_fold_addr_expr_with_type (object, type); + type = lambda_proxy_type (object); + if (name == this_identifier) + { + type = build_pointer_type (type); + type = cp_build_qualified_type (type, TYPE_QUAL_CONST); + object = build_fold_addr_expr_with_type (object, type); + } } if (DECL_VLA_CAPTURE_P (member)) @@ -807,6 +823,14 @@ lambda_expr_this_capture (tree lambda, int add_capture_p) if (cp_unevaluated_operand) add_capture_p = false; + /* If we captured 'this' but don't have a capture proxy yet, look up the + captured 'this' again. */ + if (this_capture && TREE_CODE (this_capture) == FIELD_DECL) + { + gcc_assert (!add_capture_p); + this_capture = NULL_TREE; + } + /* Try to default capture 'this' if we can. */ if (!this_capture) { @@ -911,8 +935,9 @@ lambda_expr_this_capture (tree lambda, int add_capture_p) else { /* To make sure that current_class_ref is for the lambda. */ - gcc_assert (TYPE_MAIN_VARIANT (TREE_TYPE (current_class_ref)) - == LAMBDA_EXPR_CLOSURE (lambda)); + gcc_assert (!current_class_ref + || (TYPE_MAIN_VARIANT (TREE_TYPE (current_class_ref)) + == LAMBDA_EXPR_CLOSURE (lambda))); result = this_capture; @@ -923,6 +948,9 @@ lambda_expr_this_capture (tree lambda, int add_capture_p) result = rvalue (result); } + gcc_checking_assert (!result || result == error_mark_node + || TYPE_PTR_P (TREE_TYPE (result))); + return result; } @@ -1027,12 +1055,9 @@ current_nonlambda_function (void) tree nonlambda_method_basetype (void) { - if (!current_class_ref) - return NULL_TREE; - tree type = current_class_type; if (!type || !LAMBDA_TYPE_P (type)) - return type; + return current_class_ref ? type : NULL_TREE; while (true) { @@ -1044,7 +1069,7 @@ nonlambda_method_basetype (void) tree fn = TYPE_CONTEXT (type); if (!fn || TREE_CODE (fn) != FUNCTION_DECL - || !DECL_IOBJ_MEMBER_FUNCTION_P (fn)) + || !DECL_OBJECT_MEMBER_FUNCTION_P (fn)) /* No enclosing non-lambda method. */ return NULL_TREE; if (!LAMBDA_FUNCTION_P (fn)) @@ -1852,6 +1877,12 @@ prune_lambda_captures (tree body) cp_walk_tree_without_duplicates (&body, mark_const_cap_r, &const_vars); + tree bind_expr = expr_single (DECL_SAVED_TREE (lambda_function (lam))); + bool noexcept_p = (bind_expr + && TREE_CODE (bind_expr) == MUST_NOT_THROW_EXPR); + if (noexcept_p) + bind_expr = expr_single (TREE_OPERAND (bind_expr, 0)); + tree *fieldp = &TYPE_FIELDS (LAMBDA_EXPR_CLOSURE (lam)); for (tree *capp = &LAMBDA_EXPR_CAPTURE_LIST (lam); *capp; ) { @@ -1859,11 +1890,23 @@ prune_lambda_captures (tree body) if (tree var = var_to_maybe_prune (cap)) { tree **use = const_vars.get (var); - if (use && TREE_CODE (**use) == DECL_EXPR) + if (TREE_CODE (**use) == DECL_EXPR) { /* All uses of this capture were folded away, leaving only the proxy declaration. */ + if (noexcept_p) + { + /* We didn't handle noexcept lambda captures correctly before + the fix for PR c++/119764. */ + if (abi_version_crosses (21)) + warning_at (location_of (lam), OPT_Wabi, "%qD is no longer" + " captured in noexcept lambda in ABI v21 " + "(GCC 16)", var); + if (!abi_version_at_least (21)) + goto next; + } + /* Splice the capture out of LAMBDA_EXPR_CAPTURE_LIST. */ *capp = TREE_CHAIN (cap); @@ -1873,12 +1916,27 @@ prune_lambda_captures (tree body) fieldp = &DECL_CHAIN (*fieldp); *fieldp = DECL_CHAIN (*fieldp); + /* And out of the bindings for the function. */ + tree *blockp = &BLOCK_VARS (current_binding_level->blocks); + while (*blockp != DECL_EXPR_DECL (**use)) + blockp = &DECL_CHAIN (*blockp); + *blockp = DECL_CHAIN (*blockp); + + /* And maybe out of the vars declared in the containing + BIND_EXPR, if it's listed there. */ + tree *bindp = &BIND_EXPR_VARS (bind_expr); + while (*bindp && *bindp != DECL_EXPR_DECL (**use)) + bindp = &DECL_CHAIN (*bindp); + if (*bindp) + *bindp = DECL_CHAIN (*bindp); + /* And remove the capture proxy declaration. */ **use = void_node; continue; } } + next: capp = &TREE_CHAIN (cap); } } @@ -1892,7 +1950,7 @@ finish_lambda_function (tree body) { finish_function_body (body); - prune_lambda_captures (body); + prune_lambda_captures (cur_stmt_list); /* Finish the function and generate code for it if necessary. */ tree fn = finish_function (/*inline_p=*/true); diff --git a/gcc/cp/lex.cc b/gcc/cp/lex.cc index c12b084..08a6348 100644 --- a/gcc/cp/lex.cc +++ b/gcc/cp/lex.cc @@ -749,6 +749,9 @@ unqualified_name_lookup_error (tree name, location_t loc) if (IDENTIFIER_ANY_OP_P (name)) error_at (loc, "%qD not defined", name); + else if (!flag_concepts && name == ridpointers[(int)RID_REQUIRES]) + error_at (loc, "%<requires%> only available with %<-std=c++20%> or " + "%<-fconcepts%>"); else { if (!objc_diagnose_private_ivar (name)) @@ -1079,15 +1082,17 @@ copy_lang_type (tree node) if (! TYPE_LANG_SPECIFIC (node)) return; - auto *lt = (struct lang_type *) ggc_internal_alloc (sizeof (struct lang_type)); + size_t sz = (c_dialect_objc () ? sizeof (struct lang_type) + : offsetof (struct lang_type, info)); + auto *lt = (struct lang_type *) ggc_internal_alloc (sz); - memcpy (lt, TYPE_LANG_SPECIFIC (node), (sizeof (struct lang_type))); + memcpy (lt, TYPE_LANG_SPECIFIC (node), sz); TYPE_LANG_SPECIFIC (node) = lt; if (GATHER_STATISTICS) { tree_node_counts[(int)lang_type] += 1; - tree_node_sizes[(int)lang_type] += sizeof (struct lang_type); + tree_node_sizes[(int)lang_type] += sz; } } @@ -1111,14 +1116,15 @@ maybe_add_lang_type_raw (tree t) if (!RECORD_OR_UNION_CODE_P (TREE_CODE (t))) return false; - auto *lt = (struct lang_type *) (ggc_internal_cleared_alloc - (sizeof (struct lang_type))); + size_t sz = (c_dialect_objc () ? sizeof (struct lang_type) + : offsetof (struct lang_type, info)); + auto *lt = (struct lang_type *) (ggc_internal_cleared_alloc (sz)); TYPE_LANG_SPECIFIC (t) = lt; if (GATHER_STATISTICS) { tree_node_counts[(int)lang_type] += 1; - tree_node_sizes[(int)lang_type] += sizeof (struct lang_type); + tree_node_sizes[(int)lang_type] += sz; } return true; @@ -1132,8 +1138,8 @@ cxx_make_type (enum tree_code code MEM_STAT_DECL) if (maybe_add_lang_type_raw (t)) { /* Set up some flags that give proper default behavior. */ - struct c_fileinfo *finfo = - get_fileinfo (LOCATION_FILE (input_location)); + struct c_fileinfo *finfo + = get_fileinfo (LOCATION_FILE (input_location)); SET_CLASSTYPE_INTERFACE_UNKNOWN_X (t, finfo->interface_unknown); CLASSTYPE_INTERFACE_ONLY (t) = finfo->interface_only; } diff --git a/gcc/cp/mangle.cc b/gcc/cp/mangle.cc index 02129c6..13d5ded 100644 --- a/gcc/cp/mangle.cc +++ b/gcc/cp/mangle.cc @@ -1048,6 +1048,12 @@ decl_mangling_context (tree decl) tree extra = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (decl)); if (extra) return extra; + tcontext = CP_DECL_CONTEXT (decl); + if (LAMBDA_TYPE_P (tcontext)) + /* Lambda type context means this lambda appears between the + lambda-introducer and the open brace of another lambda (c++/119175). + That isn't a real scope; look further into the enclosing scope. */ + return decl_mangling_context (TYPE_NAME (tcontext)); } else if (template_type_parameter_p (decl)) /* template type parms have no mangling context. */ @@ -4446,23 +4452,12 @@ static tree mangle_decl_string (const tree decl) { tree result; - tree saved_fn = NULL_TREE; - bool template_p = false; + tree saved_fn = current_function_decl; /* We shouldn't be trying to mangle an uninstantiated template. */ gcc_assert (!type_dependent_expression_p (decl)); - if (DECL_LANG_SPECIFIC (decl) && DECL_USE_TEMPLATE (decl)) - { - struct tinst_level *tl = current_instantiation (); - if ((!tl || tl->maybe_get_node () != decl) - && push_tinst_level (decl)) - { - template_p = true; - saved_fn = current_function_decl; - current_function_decl = NULL_TREE; - } - } + current_function_decl = NULL_TREE; iloc_sentinel ils (DECL_SOURCE_LOCATION (decl)); start_mangling (decl); @@ -4477,12 +4472,7 @@ mangle_decl_string (const tree decl) fprintf (stderr, "mangle_decl_string = '%s'\n\n", IDENTIFIER_POINTER (result)); - if (template_p) - { - pop_tinst_level (); - current_function_decl = saved_fn; - } - + current_function_decl = saved_fn; return result; } diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc index 05c19cf..a4089c5 100644 --- a/gcc/cp/method.cc +++ b/gcc/cp/method.cc @@ -2251,6 +2251,8 @@ constructible_expr (tree to, tree from) const int len = TREE_VEC_LENGTH (from); if (CLASS_TYPE_P (to)) { + if (ABSTRACT_CLASS_TYPE_P (to)) + return error_mark_node; tree ctype = to; vec<tree, va_gc> *args = NULL; if (!TYPE_REF_P (to)) @@ -2330,6 +2332,37 @@ constructible_expr (tree to, tree from) return expr; } +/* Valid if "Either T is a reference type, or T is a complete object type for + which the expression declval<U&>().~U() is well-formed when treated as an + unevaluated operand ([expr.context]), where U is remove_all_extents_t<T>." + + For a class U, return the destructor call; otherwise return void_node if + valid or error_mark_node if not. */ + +static tree +destructible_expr (tree to) +{ + cp_unevaluated cp_uneval_guard; + int flags = LOOKUP_NORMAL|LOOKUP_DESTRUCTOR; + if (TYPE_REF_P (to)) + return void_node; + if (!COMPLETE_TYPE_P (complete_type (to))) + return error_mark_node; + to = strip_array_types (to); + if (CLASS_TYPE_P (to)) + { + to = build_trait_object (to); + return build_delete (input_location, TREE_TYPE (to), to, + sfk_complete_destructor, flags, 0, tf_none); + } + /* [expr.prim.id.dtor] If the id-expression names a pseudo-destructor, T + shall be a scalar type.... */ + else if (scalarish_type_p (to)) + return void_node; + else + return error_mark_node; +} + /* Returns a tree iff TO is assignable (if CODE is MODIFY_EXPR) or constructible (otherwise) from FROM, which is a single type for assignment or a list of types for construction. */ @@ -2339,13 +2372,15 @@ is_xible_helper (enum tree_code code, tree to, tree from, bool trivial) { to = complete_type (to); deferring_access_check_sentinel acs (dk_no_deferred); - if (VOID_TYPE_P (to) || ABSTRACT_CLASS_TYPE_P (to) + if (VOID_TYPE_P (to) || (from && FUNC_OR_METHOD_TYPE_P (from) && (TYPE_READONLY (from) || FUNCTION_REF_QUALIFIED (from)))) return error_mark_node; tree expr; if (code == MODIFY_EXPR) expr = assignable_expr (to, from); + else if (code == BIT_NOT_EXPR) + expr = destructible_expr (to); else if (trivial && TREE_VEC_LENGTH (from) > 1 && cxx_dialect < cxx20) return error_mark_node; // only 0- and 1-argument ctors can be trivial @@ -2949,7 +2984,9 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, && BINFO_VIRTUAL_P (base_binfo) && fn && TREE_CODE (fn) == FUNCTION_DECL && move_fn_p (fn) && !trivial_fn_p (fn) - && vbase_has_user_provided_move_assign (BINFO_TYPE (base_binfo))) + && vbase_has_user_provided_move_assign (BINFO_TYPE (base_binfo)) + && warning_enabled_at (DECL_SOURCE_LOCATION (fn), + OPT_Wvirtual_move_assign)) warning (OPT_Wvirtual_move_assign, "defaulted move assignment for %qT calls a non-trivial " "move assignment operator for virtual base %qT", @@ -2987,7 +3024,7 @@ synthesized_method_walk (tree ctype, special_function_kind sfk, bool const_p, /* Vbase cdtors are not relevant. */; else { - if (constexpr_p) + if (constexpr_p && cxx_dialect < cxx26) *constexpr_p = false; FOR_EACH_VEC_ELT (*vbases, i, base_binfo) @@ -3914,5 +3951,26 @@ num_artificial_parms_for (const_tree fn) return count; } +/* Return value of the __builtin_type_order trait. */ + +tree +type_order_value (tree type1, tree type2) +{ + tree rettype = lookup_comparison_category (cc_strong_ordering); + if (rettype == error_mark_node) + return rettype; + int ret; + if (type1 == type2) + ret = 0; + else + { + const char *name1 = ASTRDUP (mangle_type_string (type1)); + const char *name2 = mangle_type_string (type2); + ret = strcmp (name1, name2); + } + return lookup_comparison_result (cc_strong_ordering, rettype, + ret == 0 ? 0 : ret > 0 ? 1 : 2); +} + #include "gt-cp-method.h" diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 894c70f..689319a 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -2811,12 +2811,13 @@ enum tree_tag { tt_decl, /* By-value mergeable decl. */ tt_tpl_parm, /* Template parm. */ - /* The ordering of the following 4 is relied upon in + /* The ordering of the following 5 is relied upon in trees_out::tree_node. */ tt_id, /* Identifier node. */ tt_conv_id, /* Conversion operator name. */ tt_anon_id, /* Anonymous name. */ tt_lambda_id, /* Lambda name. */ + tt_internal_id, /* Internal name. */ tt_typedef_type, /* A (possibly implicit) typedefed type. */ tt_derived_type, /* A type derived from another type. */ @@ -3096,6 +3097,9 @@ private: unsigned section; bool writing_local_entities; /* Whether we might walk into a TU-local entity we need to emit placeholders for. */ + bool walking_bit_field_unit; /* Whether we're walking the underlying + storage for a bit field. There's no other + great way to detect this. */ #if CHECKING_P int importedness; /* Checker that imports not occurring inappropriately. +ve imports ok, @@ -3262,7 +3266,7 @@ trees_out::trees_out (allocator *mem, module_state *state, depset::hash &deps, unsigned section) :parent (mem), state (state), tree_map (500), dep_hash (&deps), ref_num (0), section (section), - writing_local_entities (false) + writing_local_entities (false), walking_bit_field_unit (false) { #if CHECKING_P importedness = 0; @@ -3879,6 +3883,10 @@ class GTY((chain_next ("%h.parent"), for_user)) module_state { void write_macro_maps (elf_out *to, range_t &, unsigned *crc_ptr); bool read_macro_maps (line_map_uint_t); + void write_diagnostic_classification (elf_out *, diagnostic_context *, + unsigned *); + bool read_diagnostic_classification (diagnostic_context *); + private: void write_define (bytes_out &, const cpp_macro *); cpp_macro *read_define (bytes_in &, cpp_reader *) const; @@ -5546,8 +5554,10 @@ trees_in::start (unsigned code) enum class importer_interface { unknown, /* The definition may or may not need to be emitted. */ - always_import, /* The definition can always be found in another TU. */ - always_emit, /* The definition must be emitted in the importer's TU. */ + external, /* The definition can always be found in another TU. */ + internal, /* The definition should be emitted in the importer's TU. */ + always_emit, /* The definition must be emitted in the importer's TU, + regardless of if it's used or not. */ }; /* Returns what kind of interface an importer will have of DECL. */ @@ -5558,13 +5568,13 @@ get_importer_interface (tree decl) /* Internal linkage entities must be emitted in each importer if there is a definition available. */ if (!TREE_PUBLIC (decl)) - return importer_interface::always_emit; + return importer_interface::internal; - /* Entities that aren't vague linkage are either not definitions or - will be emitted in this TU, so importers can just refer to an - external definition. */ + /* Other entities that aren't vague linkage are either not definitions + or will be publicly emitted in this TU, so importers can just refer + to an external definition. */ if (!vague_linkage_p (decl)) - return importer_interface::always_import; + return importer_interface::external; /* For explicit instantiations, importers can always rely on there being a definition in another TU, unless this is a definition @@ -5574,13 +5584,13 @@ get_importer_interface (tree decl) && DECL_EXPLICIT_INSTANTIATION (decl)) return (header_module_p () && !DECL_EXTERNAL (decl) ? importer_interface::always_emit - : importer_interface::always_import); + : importer_interface::external); /* A gnu_inline function is never emitted in any TU. */ if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DECLARED_INLINE_P (decl) && lookup_attribute ("gnu_inline", DECL_ATTRIBUTES (decl))) - return importer_interface::always_import; + return importer_interface::external; /* Everything else has vague linkage. */ return importer_interface::unknown; @@ -5723,28 +5733,16 @@ trees_out::core_bools (tree t, bits_out& bits) == that was a lie, it is here */ bool is_external = t->decl_common.decl_flag_1; - if (!is_external) - /* decl_flag_1 is DECL_EXTERNAL. Things we emit here, might - well be external from the POV of an importer. */ - // FIXME: Do we need to know if this is a TEMPLATE_RESULT -- - // a flag from the caller? - switch (code) - { - default: - break; - - case VAR_DECL: - if (TREE_PUBLIC (t) - && DECL_VTABLE_OR_VTT_P (t)) - /* We handle vtable linkage specially. */ - is_external = true; - gcc_fallthrough (); - case FUNCTION_DECL: - if (get_importer_interface (t) - == importer_interface::always_import) - is_external = true; - break; - } + /* maybe_emit_vtables relies on vtables being marked as + DECL_EXTERNAL and DECL_NOT_REALLY_EXTERN before processing. */ + if (!is_external && VAR_P (t) && DECL_VTABLE_OR_VTT_P (t)) + is_external = true; + /* Things we emit here might well be external from the POV of an + importer. */ + if (!is_external + && VAR_OR_FUNCTION_DECL_P (t) + && get_importer_interface (t) == importer_interface::external) + is_external = true; WB (is_external); } @@ -6024,7 +6022,7 @@ trees_out::lang_decl_bools (tree t, bits_out& bits) WB (lang->u.fn.has_dependent_explicit_spec_p); WB (lang->u.fn.immediate_fn_p); WB (lang->u.fn.maybe_deleted); - /* We do not stream lang->u.fn.implicit_constexpr. */ + WB (lang->u.fn.implicit_constexpr); WB (lang->u.fn.escalated_p); WB (lang->u.fn.xobj_func); goto lds_min; @@ -6095,7 +6093,7 @@ trees_in::lang_decl_bools (tree t, bits_in& bits) RB (lang->u.fn.has_dependent_explicit_spec_p); RB (lang->u.fn.immediate_fn_p); RB (lang->u.fn.maybe_deleted); - /* We do not stream lang->u.fn.implicit_constexpr. */ + RB (lang->u.fn.implicit_constexpr); RB (lang->u.fn.escalated_p); RB (lang->u.fn.xobj_func); goto lds_min; @@ -6517,7 +6515,10 @@ trees_out::core_vals (tree t) case FIELD_DECL: WT (t->field_decl.offset); WT (t->field_decl.bit_field_type); - WT (t->field_decl.qualifier); /* bitfield unit. */ + { + auto ovr = make_temp_override (walking_bit_field_unit, true); + WT (t->field_decl.qualifier); /* bitfield unit. */ + } WT (t->field_decl.bit_offset); WT (t->field_decl.fcontext); WT (t->decl_common.initial); @@ -7386,7 +7387,7 @@ trees_out::lang_decl_vals (tree t) WU (lang->u.fn.ovl_op_code); } - if (DECL_CLASS_SCOPE_P (t)) + if (DECL_CLASS_SCOPE_P (t) || DECL_UNIQUE_FRIEND_P (t)) WT (lang->u.fn.context); if (lang->u.fn.thunk_p) @@ -7470,7 +7471,7 @@ trees_in::lang_decl_vals (tree t) lang->u.fn.ovl_op_code = code; } - if (DECL_CLASS_SCOPE_P (t)) + if (DECL_CLASS_SCOPE_P (t) || DECL_UNIQUE_FRIEND_P (t)) RT (lang->u.fn.context); if (lang->u.fn.thunk_p) @@ -8097,18 +8098,37 @@ trees_in::install_entity (tree decl) gcc_checking_assert (!existed); slot = ident; } - else if (state->is_partition ()) + else { - /* The decl is already in the entity map, but we see it again now from a - partition: we want to overwrite if the original decl wasn't also from - a (possibly different) partition. Otherwise, for things like template - instantiations, make_dependency might not realise that this is also - provided from a partition and should be considered part of this module - (and thus always emitted into the primary interface's CMI). */ unsigned *slot = entity_map->get (DECL_UID (decl)); - module_state *imp = import_entity_module (*slot); - if (!imp->is_partition ()) - *slot = ident; + + /* The entity must be in the entity map already. However, DECL may + be the DECL_TEMPLATE_RESULT of an existing partial specialisation + if we matched it while streaming another instantiation; in this + case we already registered that TEMPLATE_DECL. */ + if (!slot) + { + tree type = TREE_TYPE (decl); + gcc_checking_assert (TREE_CODE (decl) == TYPE_DECL + && CLASS_TYPE_P (type) + && CLASSTYPE_TEMPLATE_SPECIALIZATION (type)); + slot = entity_map->get (DECL_UID (CLASSTYPE_TI_TEMPLATE (type))); + } + gcc_checking_assert (slot); + + if (state->is_partition ()) + { + /* The decl is already in the entity map, but we see it again now + from a partition: we want to overwrite if the original decl + wasn't also from a (possibly different) partition. Otherwise, + for things like template instantiations, make_dependency might + not realise that this is also provided from a partition and + should be considered part of this module (and thus always + emitted into the primary interface's CMI). */ + module_state *imp = import_entity_module (*slot); + if (!imp->is_partition ()) + *slot = ident; + } } return true; @@ -8199,6 +8219,10 @@ trees_out::decl_value (tree decl, depset *dep) inner = DECL_TEMPLATE_RESULT (decl); inner_tag = insert (inner, WK_value); + /* On stream-in we assume that a template and its result will + have the same type. */ + gcc_checking_assert (TREE_TYPE (decl) == TREE_TYPE (inner)); + if (streaming_p ()) { int code = TREE_CODE (inner); @@ -9004,25 +9028,28 @@ trees_out::decl_node (tree decl, walk_kind ref) if (streaming_p ()) i (tt_parm); tree_node (DECL_CONTEXT (decl)); - if (streaming_p ()) - { - /* That must have put this in the map. */ - walk_kind ref = ref_node (decl); - if (ref != WK_none) - // FIXME:OPTIMIZATION We can wander into bits of the - // template this was instantiated from. For instance - // deferred noexcept and default parms. Currently we'll - // end up cloning those bits of tree. It would be nice - // to reference those specific nodes. I think putting - // those things in the map when we reference their - // template by name. See the note in add_indirects. - return true; - dump (dumper::TREE) - && dump ("Wrote %s reference %N", - TREE_CODE (decl) == PARM_DECL ? "parameter" : "result", - decl); - } + /* That must have put this in the map. */ + walk_kind ref = ref_node (decl); + if (ref != WK_none) + // FIXME:OPTIMIZATION We can wander into bits of the + // template this was instantiated from, for instance + // deferred noexcept and default parms, or references + // to parms from earlier forward-decls (PR c++/119608). + // + // Currently we'll end up cloning those bits of tree. + // It would be nice to reference those specific nodes. + // I think putting those things in the map when we + // reference their template by name. + // + // See the note in add_indirects. + return true; + + if (streaming_p ()) + dump (dumper::TREE) + && dump ("Wrote %s reference %N", + TREE_CODE (decl) == PARM_DECL ? "parameter" : "result", + decl); } return false; @@ -9350,6 +9377,7 @@ trees_out::type_node (tree type) tree root = (TYPE_NAME (type) ? TREE_TYPE (TYPE_NAME (type)) : TYPE_MAIN_VARIANT (type)); + gcc_checking_assert (root); if (type != root) { @@ -9428,6 +9456,8 @@ trees_out::type_node (tree type) || TREE_CODE (type) == UNION_TYPE || TREE_CODE (type) == ENUMERAL_TYPE) { + gcc_checking_assert (DECL_P (name)); + /* We can meet template parms that we didn't meet in the tpl_parms walk, because we're referring to a derived type that was previously constructed from equivalent template @@ -9589,6 +9619,8 @@ trees_out::type_node (tree type) tag_type = enum_type; else if (TYPENAME_IS_CLASS_P (type)) tag_type = class_type; + else if (TYPENAME_IS_UNION_P (type)) + tag_type = union_type; u (int (tag_type)); } } @@ -9642,11 +9674,14 @@ trees_out::tree_value (tree t) if (DECL_P (t)) /* No template, type, var or function, except anonymous - non-context vars. */ + non-context vars and types. */ gcc_checking_assert ((TREE_CODE (t) != TEMPLATE_DECL - && TREE_CODE (t) != TYPE_DECL + && (TREE_CODE (t) != TYPE_DECL + || (DECL_ARTIFICIAL (t) && !DECL_CONTEXT (t))) && (TREE_CODE (t) != VAR_DECL - || (!DECL_NAME (t) && !DECL_CONTEXT (t))) + || ((!DECL_NAME (t) + || IDENTIFIER_INTERNAL_P (DECL_NAME (t))) + && !DECL_CONTEXT (t))) && TREE_CODE (t) != FUNCTION_DECL)); if (streaming_p ()) @@ -9658,7 +9693,7 @@ trees_out::tree_value (tree t) tree_node_bools (t); } - if (TREE_CODE (t) == TREE_BINFO) + if (TREE_CODE (t) == TREE_BINFO) /* Binfos are decl-like and need merging information. */ binfo_mergeable (t); @@ -9667,8 +9702,57 @@ trees_out::tree_value (tree t) dump (dumper::TREE) && dump ("Writing tree:%d %C:%N", tag, TREE_CODE (t), t); + int type_tag = 0; + tree type = NULL_TREE; + if (TREE_CODE (t) == TYPE_DECL) + { + type = TREE_TYPE (t); + + /* We only support a limited set of features for uncontexted types; + these are typically types created in the language-independent + parts of the frontend (such as ubsan). */ + gcc_checking_assert (RECORD_OR_UNION_TYPE_P (type) + && TYPE_MAIN_VARIANT (type) == type + && TYPE_NAME (type) == t + && TYPE_STUB_DECL (type) == t + && !TYPE_VFIELD (type) + && !TYPE_BINFO (type) + && !CLASS_TYPE_P (type)); + + if (streaming_p ()) + { + start (type); + tree_node_bools (type); + } + + type_tag = insert (type, WK_value); + if (streaming_p ()) + dump (dumper::TREE) + && dump ("Writing type: %d %C:%N", type_tag, + TREE_CODE (type), type); + } + tree_node_vals (t); + if (type) + { + tree_node_vals (type); + tree_node (TYPE_SIZE (type)); + tree_node (TYPE_SIZE_UNIT (type)); + chained_decls (TYPE_FIELDS (type)); + if (streaming_p ()) + dump (dumper::TREE) + && dump ("Written type:%d %C:%N", type_tag, TREE_CODE (type), type); + } + + /* For uncontexted VAR_DECLs we need to stream the definition so that + importers can recreate their value. */ + if (TREE_CODE (t) == VAR_DECL) + { + gcc_checking_assert (!DECL_NONTRIVIALLY_INITIALIZED_P (t)); + tree_node (DECL_INITIAL (t)); + } + if (streaming_p ()) dump (dumper::TREE) && dump ("Written tree:%d %C:%N", tag, TREE_CODE (t), t); } @@ -9707,14 +9791,55 @@ trees_in::tree_value () dump (dumper::TREE) && dump ("Reading tree:%d %C", tag, TREE_CODE (t)); - if (!tree_node_vals (t)) + int type_tag = 0; + tree type = NULL_TREE; + if (TREE_CODE (t) == TYPE_DECL) { + type = start (); + if (!type || !tree_node_bools (type)) + t = NULL_TREE; + + type_tag = insert (type); + if (t) + dump (dumper::TREE) + && dump ("Reading type:%d %C", type_tag, TREE_CODE (type)); + } + + if (!t) + { +bail: back_refs[~tag] = NULL_TREE; + if (type_tag) + back_refs[~type_tag] = NULL_TREE; set_overrun (); - /* Bail. */ return NULL_TREE; } + if (!tree_node_vals (t)) + goto bail; + + if (type) + { + if (!tree_node_vals (type)) + goto bail; + + TYPE_SIZE (type) = tree_node (); + TYPE_SIZE_UNIT (type) = tree_node (); + TYPE_FIELDS (type) = chained_decls (); + if (get_overrun ()) + goto bail; + + dump (dumper::TREE) + && dump ("Read type:%d %C:%N", type_tag, TREE_CODE (type), type); + } + + if (TREE_CODE (t) == VAR_DECL) + { + DECL_INITIAL (t) = tree_node (); + if (TREE_STATIC (t)) + varpool_node::finalize_decl (t); + } + if (TREE_CODE (t) == LAMBDA_EXPR && CLASSTYPE_LAMBDA_EXPR (TREE_TYPE (t))) { @@ -9857,10 +9982,13 @@ trees_out::tree_node (tree t) if (TREE_CODE (t) == IDENTIFIER_NODE) { - /* An identifier node -> tt_id, tt_conv_id, tt_anon_id, tt_lambda_id. */ + /* An identifier node -> tt_id, tt_conv_id, tt_anon_id, tt_lambda_id, + tt_internal_id. */ int code = tt_id; if (IDENTIFIER_ANON_P (t)) code = IDENTIFIER_LAMBDA_P (t) ? tt_lambda_id : tt_anon_id; + else if (IDENTIFIER_INTERNAL_P (t)) + code = tt_internal_id; else if (IDENTIFIER_CONV_OP_P (t)) code = tt_conv_id; @@ -9875,13 +10003,15 @@ trees_out::tree_node (tree t) } else if (code == tt_id && streaming_p ()) str (IDENTIFIER_POINTER (t), IDENTIFIER_LENGTH (t)); + else if (code == tt_internal_id && streaming_p ()) + str (prefix_for_internal_label (t)); int tag = insert (t); if (streaming_p ()) { - /* We know the ordering of the 4 id tags. */ + /* We know the ordering of the 5 id tags. */ static const char *const kinds[] = - {"", "conv_op ", "anon ", "lambda "}; + {"", "conv_op ", "anon ", "lambda ", "internal "}; dump (dumper::TREE) && dump ("Written:%d %sidentifier:%N", tag, kinds[code - tt_id], @@ -9958,8 +10088,11 @@ trees_out::tree_node (tree t) break; case VAR_DECL: - /* AGGR_INIT_EXPRs cons up anonymous uncontexted VAR_DECLs. */ - gcc_checking_assert (!DECL_NAME (t) + /* AGGR_INIT_EXPRs cons up anonymous uncontexted VAR_DECLs, + and internal vars are created by sanitizers and + __builtin_source_location. */ + gcc_checking_assert ((!DECL_NAME (t) + || IDENTIFIER_INTERNAL_P (DECL_NAME (t))) && DECL_ARTIFICIAL (t)); break; @@ -9968,7 +10101,18 @@ trees_out::tree_node (tree t) PARM_DECLS. It'd be nice if they had a distinguishing flag to double check. */ break; + + case TYPE_DECL: + /* Some parts of the compiler need internal struct types; + these types may not have an appropriate context to use. + Walk the whole type (including its definition) by value. */ + gcc_checking_assert (DECL_ARTIFICIAL (t) + && TYPE_ARTIFICIAL (TREE_TYPE (t)) + && RECORD_OR_UNION_TYPE_P (TREE_TYPE (t)) + && !CLASS_TYPE_P (TREE_TYPE (t))); + break; } + mark_declaration (t, has_definition (t)); goto by_value; } } @@ -10105,6 +10249,17 @@ trees_in::tree_node (bool is_use) } break; + case tt_internal_id: + /* An internal label. */ + { + const char *prefix = str (); + res = generate_internal_label (prefix); + int tag = insert (res); + dump (dumper::TREE) + && dump ("Read internal identifier:%d %N", tag, res); + } + break; + case tt_typedef_type: res = tree_node (); if (res) @@ -10500,7 +10655,8 @@ trees_in::tree_node (bool is_use) res = lookup_field_ident (ctx, u ()); if (!res - || TREE_CODE (res) != FIELD_DECL + || (TREE_CODE (res) != FIELD_DECL + && TREE_CODE (res) != USING_DECL) || DECL_CONTEXT (res) != ctx) res = NULL_TREE; } @@ -11107,6 +11263,11 @@ trees_out::get_merge_kind (tree decl, depset *dep) return MK_local_friend; gcc_checking_assert (TYPE_P (ctx)); + + /* Internal-only types will not need to dedup their members. */ + if (!DECL_CONTEXT (TYPE_NAME (ctx))) + return MK_unique; + if (TREE_CODE (decl) == USING_DECL) return MK_field; @@ -11119,15 +11280,16 @@ trees_out::get_merge_kind (tree decl, depset *dep) return MK_named; } - if (!DECL_NAME (decl) - && !RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)) - && !DECL_BIT_FIELD_REPRESENTATIVE (decl)) + if (walking_bit_field_unit) { /* The underlying storage unit for a bitfield. We do not need to dedup it, because it's only reachable through the bitfields it represents. And those are deduped. */ // FIXME: Is that assertion correct -- do we ever fish it // out and put it in an expr? + gcc_checking_assert (!DECL_NAME (decl) + && !RECORD_OR_UNION_TYPE_P (TREE_TYPE (decl)) + && !DECL_BIT_FIELD_REPRESENTATIVE (decl)); gcc_checking_assert ((TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE ? TREE_CODE (TREE_TYPE (TREE_TYPE (decl))) : TREE_CODE (TREE_TYPE (decl))) @@ -12087,6 +12249,8 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) gcc_checking_assert (TREE_CODE (e_inner) == TREE_CODE (d_inner)); } + // FIXME: do more precise errors at point of mismatch + const char *mismatch_msg = nullptr; if (TREE_CODE (d_inner) == FUNCTION_DECL) { tree e_ret = fndecl_declared_return_type (existing); @@ -12096,13 +12260,20 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) && LAMBDA_TYPE_P (DECL_CONTEXT (d_inner))) /* This has a recursive type that will compare different. */; else if (!same_type_p (d_ret, e_ret)) - goto mismatch; + { + mismatch_msg = G_("conflicting type for imported declaration %#qD"); + goto mismatch; + } tree e_type = TREE_TYPE (e_inner); tree d_type = TREE_TYPE (d_inner); if (DECL_EXTERN_C_P (d_inner) != DECL_EXTERN_C_P (e_inner)) - goto mismatch; + { + mismatch_msg = G_("conflicting language linkage for imported " + "declaration %#qD"); + goto mismatch; + } for (tree e_args = TYPE_ARG_TYPES (e_type), d_args = TYPE_ARG_TYPES (d_type); @@ -12110,10 +12281,18 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) e_args = TREE_CHAIN (e_args), d_args = TREE_CHAIN (d_args)) { if (!(e_args && d_args)) - goto mismatch; + { + mismatch_msg = G_("conflicting argument list for imported " + "declaration %#qD"); + goto mismatch; + } if (!same_type_p (TREE_VALUE (d_args), TREE_VALUE (e_args))) - goto mismatch; + { + mismatch_msg = G_("conflicting argument types for imported " + "declaration %#qD"); + goto mismatch; + } } /* If EXISTING has an undeduced or uninstantiated exception @@ -12122,7 +12301,7 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) instantiate it in the middle of loading. */ tree e_spec = TYPE_RAISES_EXCEPTIONS (e_type); tree d_spec = TYPE_RAISES_EXCEPTIONS (d_type); - if (DEFERRED_NOEXCEPT_SPEC_P (e_spec)) + if (DECL_MAYBE_DELETED (e_inner) || DEFERRED_NOEXCEPT_SPEC_P (e_spec)) { if (!DEFERRED_NOEXCEPT_SPEC_P (d_spec) || (UNEVALUATED_NOEXCEPT_SPEC_P (e_spec) @@ -12144,9 +12323,14 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) } } } - else if (!DEFERRED_NOEXCEPT_SPEC_P (d_spec) + else if (!DECL_MAYBE_DELETED (d_inner) + && !DEFERRED_NOEXCEPT_SPEC_P (d_spec) && !comp_except_specs (d_spec, e_spec, ce_type)) - goto mismatch; + { + mismatch_msg = G_("conflicting %<noexcept%> specifier for " + "imported declaration %#qD"); + goto mismatch; + } /* Similarly if EXISTING has an undeduced return type, but DECL's is already deduced. */ @@ -12154,26 +12338,65 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) { dump (dumper::MERGE) && dump ("Propagating deduced return type to %N", existing); - FNDECL_USED_AUTO (e_inner) = true; + gcc_checking_assert (existing == e_inner); + FNDECL_USED_AUTO (existing) = true; DECL_SAVED_AUTO_RETURN_TYPE (existing) = TREE_TYPE (e_type); TREE_TYPE (existing) = change_return_type (TREE_TYPE (d_type), e_type); } else if (type_uses_auto (d_ret) && !same_type_p (TREE_TYPE (d_type), TREE_TYPE (e_type))) - goto mismatch; + { + mismatch_msg = G_("conflicting deduced return type for " + "imported declaration %#qD"); + goto mismatch; + } + + /* Similarly if EXISTING has undeduced constexpr, but DECL's + is already deduced. */ + if (DECL_DECLARED_CONSTEXPR_P (e_inner) + == DECL_DECLARED_CONSTEXPR_P (d_inner)) + /* Already matches. */; + else if (DECL_DECLARED_CONSTEXPR_P (d_inner) + && (DECL_MAYBE_DELETED (e_inner) + || decl_implicit_constexpr_p (d_inner))) + /* DECL was deduced, copy to EXISTING. */ + { + DECL_DECLARED_CONSTEXPR_P (e_inner) = true; + if (decl_implicit_constexpr_p (d_inner)) + DECL_LANG_SPECIFIC (e_inner)->u.fn.implicit_constexpr = true; + } + else if (DECL_DECLARED_CONSTEXPR_P (e_inner) + && (DECL_MAYBE_DELETED (d_inner) + || decl_implicit_constexpr_p (e_inner))) + /* EXISTING was deduced, leave it alone. */; + else + { + mismatch_msg = G_("conflicting %<constexpr%> for imported " + "declaration %#qD"); + goto mismatch; + } + + /* Don't synthesize a defaulted function if we're importing one + we've already determined. */ + if (!DECL_MAYBE_DELETED (d_inner)) + DECL_MAYBE_DELETED (e_inner) = false; } else if (is_typedef) { if (!DECL_ORIGINAL_TYPE (e_inner) || !same_type_p (DECL_ORIGINAL_TYPE (d_inner), DECL_ORIGINAL_TYPE (e_inner))) - goto mismatch; + { + mismatch_msg = G_("conflicting imported declaration %q#D"); + goto mismatch; + } } /* Using cp_tree_equal because we can meet TYPE_ARGUMENT_PACKs here. I suspect the entities that directly do that are things that shouldn't go to duplicate_decls (FIELD_DECLs etc). */ else if (!cp_tree_equal (TREE_TYPE (decl), TREE_TYPE (existing))) { + mismatch_msg = G_("conflicting type for imported declaration %#qD"); mismatch: if (DECL_IS_UNDECLARED_BUILTIN (existing)) /* Just like duplicate_decls, presum the user knows what @@ -12186,11 +12409,9 @@ trees_in::is_matching_decl (tree existing, tree decl, bool is_typedef) equality isn't feasible in general for local entities. */; else { - // FIXME:QOI Might be template specialization from a module, - // not necessarily global module + gcc_checking_assert (mismatch_msg); auto_diagnostic_group d; - error_at (DECL_SOURCE_LOCATION (decl), - "conflicting global module declaration %#qD", decl); + error_at (DECL_SOURCE_LOCATION (decl), mismatch_msg, decl); inform (DECL_SOURCE_LOCATION (existing), "existing declaration %#qD", existing); return false; @@ -12587,7 +12808,11 @@ trees_out::write_function_def (tree decl) { unsigned flags = 0; - flags |= 1 * DECL_NOT_REALLY_EXTERN (decl); + /* Whether the importer should emit this definition, if used. */ + flags |= 1 * (DECL_NOT_REALLY_EXTERN (decl) + && (get_importer_interface (decl) + != importer_interface::external)); + if (f) { flags |= 2; @@ -12679,9 +12904,10 @@ trees_in::read_function_def (tree decl, tree maybe_template) void trees_out::write_var_def (tree decl) { - /* The initializer of a variable or variable template is ignored for - determining exposures. */ - auto ovr = make_temp_override (dep_hash->ignore_tu_local, VAR_P (decl)); + /* The initializer of a non-inline variable or variable template is + ignored for determining exposures. */ + auto ovr = make_temp_override (dep_hash->ignore_tu_local, + VAR_P (decl) && !DECL_INLINE_VAR_P (decl)); tree init = DECL_INITIAL (decl); tree_node (init); @@ -13167,13 +13393,19 @@ trees_in::read_class_def (tree defn, tree maybe_template) if (TYPE_LANG_SPECIFIC (type)) { - CLASSTYPE_LAMBDA_EXPR (type) = lambda; + if (!TYPE_POLYMORPHIC_P (type)) + SET_CLASSTYPE_LAMBDA_EXPR (type, lambda); + else + gcc_checking_assert (lambda == NULL_TREE); CLASSTYPE_MEMBER_VEC (type) = member_vec; CLASSTYPE_PURE_VIRTUALS (type) = pure_virts; CLASSTYPE_VCALL_INDICES (type) = vcall_indices; - CLASSTYPE_KEY_METHOD (type) = key_method; + if (TYPE_POLYMORPHIC_P (type)) + SET_CLASSTYPE_KEY_METHOD (type, key_method); + else + gcc_checking_assert (key_method == NULL_TREE); CLASSTYPE_VBASECLASSES (type) = vbase_vec; @@ -14010,9 +14242,10 @@ depset::hash::make_dependency (tree decl, entity_kind ek) streaming the definition in such cases. */ dep->clear_flag_bit<DB_DEFN_BIT> (); - if (DECL_DECLARED_CONSTEXPR_P (decl)) - /* Also, a constexpr variable initialized to a TU-local - value is an exposure. */ + if (DECL_DECLARED_CONSTEXPR_P (decl) + || DECL_INLINE_VAR_P (decl)) + /* A constexpr variable initialized to a TU-local value, + or an inline value (PR c++/119996), is an exposure. */ dep->set_flag_bit<DB_EXPOSURE_BIT> (); } } @@ -14770,9 +15003,16 @@ depset::hash::find_dependencies (module_state *module) } walker.end (); + /* If we see either a class template or a deduction guide, make + sure to add all visible deduction guides. We need to check + both in case they have been added in separate modules, or + one is in the GMF and would have otherwise been discarded. */ if (!is_key_order () && DECL_CLASS_TEMPLATE_P (decl)) add_deduction_guides (decl); + if (!is_key_order () + && deduction_guide_p (decl)) + add_deduction_guides (TYPE_NAME (TREE_TYPE (TREE_TYPE (decl)))); if (!is_key_order () && TREE_CODE (decl) == TEMPLATE_DECL @@ -14973,12 +15213,24 @@ depset::hash::finalize_dependencies () break; } - if (!explained && VAR_P (decl) && DECL_DECLARED_CONSTEXPR_P (decl)) + if (!explained + && VAR_P (decl) + && (DECL_DECLARED_CONSTEXPR_P (decl) + || DECL_INLINE_VAR_P (decl))) { auto_diagnostic_group d; - error_at (DECL_SOURCE_LOCATION (decl), - "%qD is declared %<constexpr%> and is initialized to " - "a TU-local value", decl); + if (DECL_DECLARED_CONSTEXPR_P (decl)) + error_at (DECL_SOURCE_LOCATION (decl), + "%qD is declared %<constexpr%> and is initialized to " + "a TU-local value", decl); + else + { + /* This can only occur with references. */ + gcc_checking_assert (TYPE_REF_P (TREE_TYPE (decl))); + error_at (DECL_SOURCE_LOCATION (decl), + "%qD is a reference declared %<inline%> and is " + "constant-initialized to a TU-local value", decl); + } bool informed = is_tu_local_value (decl, DECL_INITIAL (decl), /*explain=*/true); gcc_checking_assert (informed); @@ -16678,6 +16930,15 @@ module_state::read_cluster (unsigned snum) #endif cfun->returns_struct = aggr; expand_or_defer_fn (decl); + + /* If we first see this function after at_eof, it doesn't get + note_vague_linkage_fn from tentative_decl_linkage, so the loop in + c_parse_final_cleanups won't consider it. But with DECL_COMDAT we + can just clear DECL_EXTERNAL and let cgraph decide. + FIXME handle this outside module.cc after GCC 15. */ + if (at_eof && DECL_COMDAT (decl) && DECL_EXTERNAL (decl) + && DECL_NOT_REALLY_EXTERN (decl)) + DECL_EXTERNAL (decl) = false; } } @@ -16760,11 +17021,15 @@ module_state::write_namespaces (elf_out *to, vec<depset *> spaces, bytes_out sec (to); sec.begin (); + hash_map<tree, unsigned> ns_map; + for (unsigned ix = 0; ix != num; ix++) { depset *b = spaces[ix]; tree ns = b->get_entity (); + ns_map.put (ns, ix); + /* This could be an anonymous namespace even for a named module, since we can still emit no-linkage decls. */ gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL); @@ -16806,6 +17071,31 @@ module_state::write_namespaces (elf_out *to, vec<depset *> spaces, } } + /* Now write exported using-directives, as a sequence of 1-origin indices in + the spaces array (not entity indices): First the using namespace, then the + used namespaces. And then a zero terminating the list. :: is + represented as index -1. */ + auto emit_one_ns = [&](unsigned ix, tree ns) { + for (auto udir: NAMESPACE_LEVEL (ns)->using_directives) + { + if (TREE_CODE (udir) != USING_DECL || !DECL_MODULE_EXPORT_P (udir)) + continue; + tree ns2 = USING_DECL_DECLS (udir); + dump() && dump ("Writing using-directive in %N for %N", + ns, ns2); + sec.u (ix); + sec.u (*ns_map.get (ns2) + 1); + } + }; + emit_one_ns (-1, global_namespace); + for (unsigned ix = 0; ix != num; ix++) + { + depset *b = spaces[ix]; + tree ns = b->get_entity (); + emit_one_ns (ix + 1, ns); + } + sec.u (0); + sec.end (to, to->name (MOD_SNAME_PFX ".nms"), crc_p); dump.outdent (); } @@ -16824,6 +17114,8 @@ module_state::read_namespaces (unsigned num) dump () && dump ("Reading namespaces"); dump.indent (); + tree *ns_map = XALLOCAVEC (tree, num); + for (unsigned ix = 0; ix != num; ix++) { unsigned entity_index = sec.u (); @@ -16885,6 +17177,8 @@ module_state::read_namespaces (unsigned num) DECL_ATTRIBUTES (inner) = tree_cons (get_identifier ("abi_tag"), tags, DECL_ATTRIBUTES (inner)); + ns_map[ix] = inner; + /* Install the namespace. */ (*entity_ary)[entity_lwm + entity_index] = inner; if (DECL_MODULE_IMPORT_P (inner)) @@ -16899,6 +17193,44 @@ module_state::read_namespaces (unsigned num) *slot = entity_lwm + entity_index; } } + + /* Read the exported using-directives. */ + while (unsigned ix = sec.u ()) + { + tree ns; + if (ix == (unsigned)-1) + ns = global_namespace; + else + { + if (--ix >= num) + { + sec.set_overrun (); + break; + } + ns = ns_map [ix]; + } + unsigned ix2 = sec.u (); + if (--ix2 >= num) + { + sec.set_overrun (); + break; + } + tree ns2 = ns_map [ix2]; + if (directness) + { + dump() && dump ("Reading using-directive in %N for %N", + ns, ns2); + /* In an export import this will mark the using-directive as + exported, so it will be emitted again. */ + add_using_namespace (ns, ns2); + } + else + /* Ignore using-directives from indirect imports, we only want them + from our own imports. */ + dump() && dump ("Ignoring using-directive in %N for %N", + ns, ns2); + } + dump.outdent (); if (!sec.end (from ())) return false; @@ -17991,6 +18323,168 @@ module_state::write_ordinary_maps (elf_out *to, range_t &info, dump.outdent (); } +/* Return the prefix to use for dumping a #pragma diagnostic change to DK. */ + +static const char * +dk_string (diagnostic_t dk) +{ + gcc_assert (dk > DK_UNSPECIFIED && dk < DK_LAST_DIAGNOSTIC_KIND); + if (dk == DK_IGNORED) + /* diagnostic.def has an empty string for ignored. */ + return "ignored: "; + else + return get_diagnostic_kind_text (dk); +} + +/* Dump one #pragma GCC diagnostic entry. */ + +static bool +dump_dc_change (unsigned index, unsigned opt, diagnostic_t dk) +{ + if (dk == DK_POP) + return dump (" Index %u: pop from %d", index, opt); + else + return dump (" Index %u: %s%s", index, dk_string (dk), + cl_options[opt].opt_text); +} + +/* Write out any #pragma GCC diagnostic info to the .dgc section. */ + +void +module_state::write_diagnostic_classification (elf_out *to, + diagnostic_context *dc, + unsigned *crc_p) +{ + auto &changes = dc->get_classification_history (); + + bytes_out sec (to); + if (sec.streaming_p ()) + { + sec.begin (); + dump () && dump ("Writing diagnostic change locations"); + dump.indent (); + } + + unsigned len = changes.length (); + + /* We don't want to write out any entries that came from one of our imports. + But then we need to adjust the total, and change DK_POP targets to match + the index in our actual output. So remember how many lines we had skipped + at each step, where -1 means this line itself is skipped. */ + int skips = 0; + auto_vec<int> skips_at (len); + skips_at.safe_grow (len); + + for (unsigned i = 0; i < len; ++i) + { + const auto &c = changes[i]; + skips_at[i] = skips; + if (linemap_location_from_module_p (line_table, c.location)) + { + ++skips; + skips_at[i] = -1; + continue; + } + } + + if (sec.streaming_p ()) + { + sec.u (len - skips); + dump () && dump ("Diagnostic changes: %u", len - skips); + } + + for (unsigned i = 0; i < len; ++i) + { + if (skips_at[i] == -1) + continue; + + const auto &c = changes[i]; + write_location (sec, c.location); + if (sec.streaming_p ()) + { + unsigned opt = c.option; + if (c.kind == DK_POP) + opt -= skips_at[opt]; + sec.u (opt); + sec.u (c.kind); + dump () && dump_dc_change (i - skips_at[i], opt, c.kind); + } + } + + if (sec.streaming_p ()) + { + sec.end (to, to->name (MOD_SNAME_PFX ".dgc"), crc_p); + dump.outdent (); + } +} + +/* Read any #pragma GCC diagnostic info from the .dgc section. */ + +bool +module_state::read_diagnostic_classification (diagnostic_context *dc) +{ + bytes_in sec; + + if (!sec.begin (loc, from (), MOD_SNAME_PFX ".dgc")) + return false; + + dump () && dump ("Reading diagnostic change locations"); + dump.indent (); + + unsigned len = sec.u (); + dump () && dump ("Diagnostic changes: %u", len); + + auto &changes = dc->get_classification_history (); + int offset = changes.length (); + changes.reserve (len + 1); + for (unsigned i = 0; i < len; ++i) + { + location_t loc = read_location (sec); + int opt = sec.u (); + diagnostic_t kind = (diagnostic_t) sec.u (); + if (kind == DK_POP) + /* For a pop, opt is the 'changes' index to return to. */ + opt += offset; + changes.quick_push ({ loc, opt, kind }); + dump () && dump_dc_change (changes.length () - 1, opt, kind); + } + + /* Did the import pop all its diagnostic changes? */ + bool last_was_reset = (len == 0); + if (len) + for (int i = changes.length () - 1; ; --i) + { + gcc_checking_assert (i >= offset); + + const auto &c = changes[i]; + if (c.kind != DK_POP) + break; + else if (c.option == offset) + { + last_was_reset = true; + break; + } + else + /* As in update_effective_level_from_pragmas, the loop will decrement + i so we actually jump to c.option - 1. */ + i = c.option; + } + if (!last_was_reset) + { + /* It didn't, so add a pop at its last location to avoid affecting later + imports. */ + location_t last_loc = ordinary_locs.first + ordinary_locs.second - 1; + changes.quick_push ({ last_loc, offset, DK_POP }); + dump () && dump (" Adding final pop from index %d", offset); + } + + dump.outdent (); + if (!sec.end (from ())) + return false; + + return true; +} + void module_state::write_macro_maps (elf_out *to, range_t &info, unsigned *crc_p) { @@ -19158,6 +19652,10 @@ post_load_processing () gcc_checking_assert (DECL_MAYBE_IN_CHARGE_CDTOR_P (decl)); expand_or_defer_fn (decl); + /* As in module_state::read_cluster. */ + if (at_eof && DECL_COMDAT (decl) && DECL_EXTERNAL (decl) + && DECL_NOT_REALLY_EXTERN (decl)) + DECL_EXTERNAL (decl) = false; } cfun = old_cfun; @@ -19673,6 +20171,8 @@ module_state::write_begin (elf_out *to, cpp_reader *reader, } ool->qsort (ool_cmp); + write_diagnostic_classification (nullptr, global_dc, nullptr); + vec<cpp_hashnode *> *macros = nullptr; if (is_header ()) macros = prepare_macros (reader); @@ -19818,7 +20318,10 @@ module_state::write_begin (elf_out *to, cpp_reader *reader, /* Write the line maps. */ if (config.ordinary_locs) - write_ordinary_maps (to, map_info, bool (config.num_partitions), &crc); + { + write_ordinary_maps (to, map_info, bool (config.num_partitions), &crc); + write_diagnostic_classification (to, global_dc, &crc); + } if (config.macro_locs) write_macro_maps (to, map_info, &crc); @@ -19891,6 +20394,10 @@ module_state::read_initial (cpp_reader *reader) else if (!read_ordinary_maps (config.ordinary_locs, config.loc_range_bits)) ok = false; + if (ok && have_locs && config.ordinary_locs + && !read_diagnostic_classification (global_dc)) + ok = false; + /* Allocate the REMAP vector. */ slurp->alloc_remap (config.num_imports); diff --git a/gcc/cp/name-lookup.cc b/gcc/cp/name-lookup.cc index 7fadbcc..9aa7c16 100644 --- a/gcc/cp/name-lookup.cc +++ b/gcc/cp/name-lookup.cc @@ -583,7 +583,7 @@ name_lookup::preserve_state () if (previous) { unsigned length = vec_safe_length (previous->scopes); - vec_safe_reserve (previous->scopes, length * 2); + vec_safe_reserve (previous->scopes, length); for (unsigned ix = length; ix--;) { tree decl = (*previous->scopes)[ix]; @@ -1049,7 +1049,7 @@ name_lookup::search_usings (tree scope) bool found = false; if (vec<tree, va_gc> *usings = NAMESPACE_LEVEL (scope)->using_directives) for (unsigned ix = usings->length (); ix--;) - found |= search_qualified ((*usings)[ix], true); + found |= search_qualified (strip_using_decl ((*usings)[ix]), true); /* Look in its inline children. */ if (vec<tree, va_gc> *inlinees = DECL_NAMESPACE_INLINEES (scope)) @@ -1121,7 +1121,7 @@ name_lookup::queue_usings (using_queue& queue, int depth, vec<tree, va_gc> *usin { if (usings) for (unsigned ix = usings->length (); ix--;) - queue_namespace (queue, depth, (*usings)[ix]); + queue_namespace (queue, depth, strip_using_decl ((*usings)[ix])); } /* Unqualified namespace lookup in SCOPE. @@ -4178,22 +4178,6 @@ mergeable_namespace_slots (tree ns, tree name, bool is_attached, tree *vec) return vslot; } -/* Retrieve the bindings for an existing mergeable entity in namespace - NS slot NAME. Returns NULL if no such bindings exists. */ - -static tree -get_mergeable_namespace_binding (tree ns, tree name, bool is_attached) -{ - tree *mslot = find_namespace_slot (ns, name, false); - if (!mslot || !*mslot || TREE_CODE (*mslot) != BINDING_VECTOR) - return NULL_TREE; - - tree *vslot = get_fixed_binding_slot - (mslot, name, is_attached ? BINDING_SLOT_PARTITION : BINDING_SLOT_GLOBAL, - false); - return vslot ? *vslot : NULL_TREE; -} - /* DECL is a new mergeable namespace-scope decl. Add it to the mergeable entities on GSLOT. */ @@ -4572,11 +4556,12 @@ lookup_imported_hidden_friend (tree friend_tmpl) || !DECL_MODULE_ENTITY_P (inner)) return NULL_TREE; + /* Load any templates matching FRIEND_TMPL from importers. */ lazy_load_pendings (friend_tmpl); - tree bind = get_mergeable_namespace_binding - (current_namespace, DECL_NAME (inner), DECL_MODULE_ATTACH_P (inner)); - if (!bind) + tree name = DECL_NAME (inner); + tree *slot = find_namespace_slot (current_namespace, name, false); + if (!slot || !*slot || TREE_CODE (*slot) != BINDING_VECTOR) return NULL_TREE; /* We're only interested in declarations attached to the same module @@ -4584,9 +4569,28 @@ lookup_imported_hidden_friend (tree friend_tmpl) int m = get_originating_module (friend_tmpl, /*global=-1*/true); gcc_assert (m != 0); + /* First check whether there's a reachable declaration attached to the module + we're looking for. */ + if (m > 0) + if (binding_slot *mslot = search_imported_binding_slot (slot, m)) + { + if (mslot->is_lazy ()) + lazy_load_binding (m, current_namespace, name, mslot); + for (ovl_iterator iter (*mslot); iter; ++iter) + if (DECL_CLASS_TEMPLATE_P (*iter)) + return *iter; + } + + /* Otherwise, look in the mergeable slots for this name, in case an importer + has already instantiated this declaration. */ + tree *vslot = get_fixed_binding_slot + (slot, name, m > 0 ? BINDING_SLOT_PARTITION : BINDING_SLOT_GLOBAL, false); + if (!vslot || !*vslot) + return NULL_TREE; + /* There should be at most one class template from the module we're looking for, return it. */ - for (ovl_iterator iter (bind); iter; ++iter) + for (ovl_iterator iter (*vslot); iter; ++iter) if (DECL_CLASS_TEMPLATE_P (*iter) && get_originating_module (*iter, true) == m) return *iter; @@ -5204,9 +5208,11 @@ pushdecl_outermost_localscope (tree x) cp_binding_level *b = NULL; auto_cond_timevar tv (TV_NAME_LOOKUP); - /* Find the scope just inside the function parms. */ - for (cp_binding_level *n = current_binding_level; - n->kind != sk_function_parms; n = b->level_chain) + /* Find the block scope just inside the function parms. */ + cp_binding_level *n = current_binding_level; + while (n && n->kind != sk_block) + n = n->level_chain; + for (; n && n->kind != sk_function_parms; n = b->level_chain) b = n; return b ? do_pushdecl_with_scope (x, b) : error_mark_node; @@ -6862,7 +6868,7 @@ using_directives_contain_std_p (vec<tree, va_gc> *usings) return false; for (unsigned ix = usings->length (); ix--;) - if ((*usings)[ix] == std_node) + if (strip_using_decl ((*usings)[ix]) == std_node) return true; return false; @@ -7082,13 +7088,16 @@ namespace_hints::convert_candidates_to_name_hint () /* Clean up CANDIDATES. */ m_candidates.release (); return name_hint (expr_to_string (candidate), - new show_candidate_location (m_loc, candidate)); + std::make_unique<show_candidate_location> (m_loc, + candidate)); } else if (m_candidates.length () > 1) /* If we have more than one candidate, issue a name_hint without a single "suggestion", but with a deferred diagnostic that lists the various candidates. This takes ownership of m_candidates. */ - return name_hint (NULL, new suggest_alternatives (m_loc, m_candidates)); + return name_hint (NULL, + std::make_unique<suggest_alternatives> (m_loc, + m_candidates)); /* Otherwise, m_candidates ought to be empty, so no cleanup is necessary. */ gcc_assert (m_candidates.length () == 0); @@ -7108,10 +7117,11 @@ name_hint namespace_hints::maybe_decorate_with_limit (name_hint hint) { if (m_limited) - return name_hint (hint.suggestion (), - new namespace_limit_reached (m_loc, m_limit, - m_name, - hint.take_deferred ())); + return name_hint + (hint.suggestion (), + std::make_unique<namespace_limit_reached> (m_loc, m_limit, + m_name, + hint.take_deferred ())); else return hint; } @@ -7188,10 +7198,11 @@ suggest_alternatives_for_1 (location_t location, tree name, diagnostic_option_id option_id = get_option_for_builtin_define (IDENTIFIER_POINTER (name)); if (option_id.m_idx > 0) - return name_hint (nullptr, - new suggest_missing_option (location, - IDENTIFIER_POINTER (name), - option_id)); + return name_hint + (nullptr, + std::make_unique<suggest_missing_option> (location, + IDENTIFIER_POINTER (name), + option_id)); /* Otherwise, consider misspellings. */ if (!suggest_misspellings) @@ -7319,8 +7330,9 @@ maybe_suggest_missing_std_header (location_t location, tree name) if (!header_hint) return name_hint (); - return name_hint (NULL, new missing_std_header (location, name_str, - header_hint)); + return name_hint (nullptr, + std::make_unique<missing_std_header> (location, name_str, + header_hint)); } /* Attempt to generate a name_hint that suggests a missing header file @@ -7702,12 +7714,12 @@ class macro_use_before_def : public deferred_diagnostic public: /* Factory function. Return a new macro_use_before_def instance if appropriate, or return NULL. */ - static macro_use_before_def * + static std::unique_ptr<macro_use_before_def> maybe_make (location_t use_loc, cpp_hashnode *macro) { location_t def_loc = cpp_macro_definition_location (macro); if (def_loc == UNKNOWN_LOCATION) - return NULL; + return nullptr; /* We only want to issue a note if the macro was used *before* it was defined. @@ -7715,12 +7727,11 @@ class macro_use_before_def : public deferred_diagnostic used, leaving it unexpanded (e.g. by using the wrong argument count). */ if (!linemap_location_before_p (line_table, use_loc, def_loc)) - return NULL; + return nullptr; - return new macro_use_before_def (use_loc, macro); + return std::make_unique<macro_use_before_def> (use_loc, macro); } - private: /* Ctor. LOC is the location of the usage. MACRO is the macro that was used. */ macro_use_before_def (location_t loc, cpp_hashnode *macro) @@ -7787,10 +7798,11 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t loc) const char *header_hint = get_cp_stdlib_header_for_name (IDENTIFIER_POINTER (name)); if (header_hint) - return name_hint (NULL, - new suggest_missing_header (loc, - IDENTIFIER_POINTER (name), - header_hint)); + return name_hint + (nullptr, + std::make_unique<suggest_missing_header> (loc, + IDENTIFIER_POINTER (name), + header_hint)); best_match <tree, const char *> bm (name); @@ -8931,9 +8943,27 @@ add_using_namespace (vec<tree, va_gc> *&usings, tree target) if ((*usings)[ix] == target) return; + if (modules_p ()) + { + tree u = build_lang_decl (USING_DECL, NULL_TREE, NULL_TREE); + USING_DECL_DECLS (u) = target; + DECL_MODULE_EXPORT_P (u) = module_exporting_p (); + DECL_MODULE_PURVIEW_P (u) = module_purview_p (); + target = u; + } vec_safe_push (usings, target); } +/* Convenience overload for the above, taking the user as its first + parameter. */ + +void +add_using_namespace (tree ns, tree target) +{ + add_using_namespace (NAMESPACE_LEVEL (ns)->using_directives, + ORIGINAL_NAMESPACE (target)); +} + /* Tell the debug system of a using directive. */ static void diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h index f1596c6..2fa736b 100644 --- a/gcc/cp/name-lookup.h +++ b/gcc/cp/name-lookup.h @@ -462,6 +462,7 @@ extern cxx_binding *outer_binding (tree, cxx_binding *, bool); extern void cp_emit_debug_info_for_using (tree, tree); extern void finish_nonmember_using_decl (tree scope, tree name); +extern void add_using_namespace (tree, tree); extern void finish_using_directive (tree target, tree attribs); void push_local_extern_decl_alias (tree decl); extern tree pushdecl (tree, bool hiding = false); @@ -500,6 +501,10 @@ enum WMB_Flags WMB_Hidden = 1 << 3, WMB_Purview = 1 << 4, }; +inline WMB_Flags operator|(WMB_Flags x, WMB_Flags y) +{ return WMB_Flags(+x|y); } +inline WMB_Flags& operator|=(WMB_Flags& x, WMB_Flags y) +{ return x = x|y; } extern unsigned walk_module_binding (tree binding, bitmap partitions, bool (*)(tree decl, WMB_Flags, void *data), diff --git a/gcc/cp/optimize.cc b/gcc/cp/optimize.cc index 6f9a77f..fc4d6c2 100644 --- a/gcc/cp/optimize.cc +++ b/gcc/cp/optimize.cc @@ -309,8 +309,8 @@ maybe_thunk_body (tree fn, bool force) defer_mangling_aliases = save_defer_mangling_aliases; cgraph_node::get_create (fns[0])->set_comdat_group (comdat_group); cgraph_node::get_create (fns[1])->add_to_same_comdat_group - (cgraph_node::get_create (fns[0])); - symtab_node::get (fn)->add_to_same_comdat_group + (cgraph_node::get (fns[0])); + symtab_node::get_create (fn)->add_to_same_comdat_group (symtab_node::get (fns[0])); if (fns[2]) /* If *[CD][12]* dtors go into the *[CD]5* comdat group and dtor is diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 88e722d..21bec72c 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -50,6 +50,7 @@ along with GCC; see the file COPYING3. If not see #include "contracts.h" #include "bitmap.h" #include "builtins.h" +#include "analyzer/analyzer-language.h" /* The lexer. */ @@ -286,6 +287,37 @@ static FILE *cp_lexer_debug_stream; sizeof, typeof, or alignof. */ int cp_unevaluated_operand; +#if ENABLE_ANALYZER + +namespace ana { + +/* Concrete implementation of ana::translation_unit for the C++ frontend. */ + +class cp_translation_unit : public translation_unit +{ +public: + tree lookup_constant_by_id (tree /*id*/) const final override + { + return NULL_TREE; + } + + tree + lookup_type_by_id (tree /*id*/) const final override + { + return NULL_TREE; + } + + tree + lookup_global_var_by_id (tree /*id*/) const final override + { + return NULL_TREE; + } +}; + +} // namespace ana + +#endif /* #if ENABLE_ANALYZER */ + /* Dump up to NUM tokens in BUFFER to FILE starting with token START_TOKEN. If START_TOKEN is NULL, the dump starts with the first token in BUFFER. If NUM is 0, dump all the tokens. If @@ -2487,7 +2519,7 @@ static cp_expr cp_parser_id_expression static cp_expr cp_parser_unqualified_id (cp_parser *, bool, bool, bool, bool); static tree cp_parser_nested_name_specifier_opt - (cp_parser *, bool, bool, bool, bool, bool = false); + (cp_parser *, bool, bool, bool, bool, bool = false, bool = false); static tree cp_parser_nested_name_specifier (cp_parser *, bool, bool, bool, bool); static tree cp_parser_qualifying_entity @@ -3059,8 +3091,8 @@ static cp_token *cp_parser_require_keyword (cp_parser *, enum rid, required_token); static bool cp_parser_token_starts_function_definition_p (cp_token *); -static bool cp_parser_next_token_starts_class_definition_p - (cp_parser *); +static bool cp_parser_nth_token_starts_class_definition_p + (cp_parser *, size_t); static bool cp_parser_next_token_ends_template_argument_p (cp_parser *); static bool cp_parser_nth_token_starts_template_argument_list_p @@ -3434,9 +3466,11 @@ cp_parser_error_1 (cp_parser* parser, const char* gmsgid, const char *header_hint = get_cp_stdlib_header_for_string_macro_name (token_name); if (header_hint != NULL) - h = name_hint (NULL, new suggest_missing_header (token->location, - token_name, - header_hint)); + h = name_hint + (nullptr, + std::make_unique<suggest_missing_header> (token->location, + token_name, + header_hint)); } /* Actually emit the error. */ @@ -3901,10 +3935,10 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id, inform (location, "%qE is not recognized as a module control-line", id); else if (cxx_dialect < cxx20) - inform (location, "C++20 %qE only available with %<-fmodules-ts%>", + inform (location, "C++20 %qE only available with %<-fmodules%>", id); else - inform (location, "C++20 %qE only available with %<-fmodules-ts%>" + inform (location, "C++20 %qE only available with %<-fmodules%>" ", which is not yet enabled with %<-std=c++20%>", id); } else if (cxx_dialect < cxx11 @@ -5469,6 +5503,14 @@ cp_parser_translation_unit (cp_parser* parser) cp_parser_toplevel_declaration (parser); } +#if ENABLE_ANALYZER + if (flag_analyzer) + { + ana::cp_translation_unit tu; + ana::on_finish_translation_unit (tu); + } +#endif + /* Get rid of the token array; we don't need it any more. */ cp_lexer_destroy (parser->lexer); parser->lexer = NULL; @@ -6265,7 +6307,10 @@ cp_parser_primary_expression (cp_parser *parser, /* Recognize the `this' keyword. */ case RID_THIS: cp_lexer_consume_token (parser->lexer); - if (parser->local_variables_forbidden_p & THIS_FORBIDDEN) + if ((parser->local_variables_forbidden_p & THIS_FORBIDDEN) + /* It's OK to refer to 'this' in an unevaluated operand in a + lambda default argument (lambda-targ16.C). */ + && !cp_unevaluated_operand) { error_at (token->location, "%<this%> may not be used in this context"); @@ -7197,18 +7242,22 @@ check_template_keyword_in_nested_name_spec (tree name) nested-name-specifier template [opt] simple-template-id :: PARSER->SCOPE should be set appropriately before this function is - called. TYPENAME_KEYWORD_P is TRUE if the `typename' keyword is in - effect. TYPE_P is TRUE if we non-type bindings should be ignored - in name lookups. + called. TYPENAME_KEYWORD_P is true if the `typename' keyword is in + effect. TYPE_P is true if we non-type bindings should be ignored + in name lookups. TEMPLATE_KEYWORD_P is true if the `template' keyword + was seen. GLOBAL_P is true if `::' has already been parsed. + TODO: This function doesn't handle the C++14 change to make `::' + a nested-name-specifier by itself. If it did, GLOBAL_P could probably + go. Sets PARSER->SCOPE to the class (TYPE) or namespace (NAMESPACE_DECL) specified by the nested-name-specifier, or leaves it unchanged if there is no nested-name-specifier. Returns the new scope iff there is a nested-name-specifier, or NULL_TREE otherwise. - If CHECK_DEPENDENCY_P is FALSE, names are looked up in dependent scopes. + If CHECK_DEPENDENCY_P is false, names are looked up in dependent scopes. - If IS_DECLARATION is TRUE, the nested-name-specifier is known to be + If IS_DECLARATION is true, the nested-name-specifier is known to be part of a declaration and/or decl-specifier. */ static tree @@ -7217,7 +7266,8 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, bool check_dependency_p, bool type_p, bool is_declaration, - bool template_keyword_p /* = false */) + bool template_keyword_p /* = false */, + bool global_p /* = false */) { bool success = false; cp_token_position start = 0; @@ -7265,8 +7315,9 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, /* Spot cases that cannot be the beginning of a nested-name-specifier. On the second and subsequent times - through the loop, we look for the `template' keyword. */ - if (success && token->keyword == RID_TEMPLATE) + (or the first, if '::' has already been parsed) through the + loop, we look for the `template' keyword. */ + if ((success || global_p) && token->keyword == RID_TEMPLATE) ; /* A template-id can start a nested-name-specifier. */ else if (token->type == CPP_TEMPLATE_ID) @@ -7314,8 +7365,11 @@ cp_parser_nested_name_specifier_opt (cp_parser *parser, cp_parser_parse_tentatively (parser); /* Look for the optional `template' keyword, if this isn't the - first time through the loop. */ - if (success) + first time through the loop, or if we've already parsed '::'; + this is then the + nested-name-specifier template [opt] simple-template-id :: + production. */ + if (success || global_p) { template_keyword_p = cp_parser_optional_template_keyword (parser); /* DR1710: "In a qualified-id used as the name in @@ -10793,6 +10847,14 @@ cp_parser_binary_expression (cp_parser* parser, bool cast_p, goto pop; } + /* If we skipped build_cplus_new in build_cxx_call because of decltype_p, + call it now that we know current.lhs is a subexpression. */ + if (decltype_p && !processing_template_decl + && TREE_CODE (current.lhs) == CALL_EXPR + && CLASS_TYPE_P (TREE_TYPE (current.lhs))) + current.lhs = build_cplus_new (TREE_TYPE (current.lhs), current.lhs, + tf_warning_or_error); + get_rhs: current.tree_type = binops_by_token[token->type].tree_type; current.loc = token->location; @@ -11736,21 +11798,34 @@ cp_parser_lambda_expression (cp_parser* parser) if (cp_parser_error_occurred (parser)) return error_mark_node; - type = begin_lambda_type (lambda_expr); - if (type == error_mark_node) - return error_mark_node; + { + /* OK, this is a bit tricksy. cp_parser_requires_expression sets + processing_template_decl to make checking more normal, but that confuses + lambda parsing terribly. In non-template context, we want to parse the + lambda once and not tsubst_lambda_expr. So in that case, clear + processing_template_decl now, and restore it before the call to + build_lambda_object; that way we end up with what looks like a templatey + functional cast to the closure type, which is suitable for the + requires-expression tsubst_expr. This is PR99546 and friends. */ + processing_template_decl_sentinel ptds (/*reset*/false); + if (processing_template_decl && !in_template_context + && current_binding_level->requires_expression) + processing_template_decl = 0; + + type = begin_lambda_type (lambda_expr); + if (type == error_mark_node) + return error_mark_node; - record_lambda_scope (lambda_expr); - record_lambda_scope_discriminator (lambda_expr); + record_lambda_scope (lambda_expr); + record_lambda_scope_discriminator (lambda_expr); - /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ - determine_visibility (TYPE_NAME (type)); + /* Do this again now that LAMBDA_EXPR_EXTRA_SCOPE is set. */ + determine_visibility (TYPE_NAME (type)); - /* Now that we've started the type, add the capture fields for any - explicit captures. */ - register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); + /* Now that we've started the type, add the capture fields for any + explicit captures. */ + register_capture_members (LAMBDA_EXPR_CAPTURE_LIST (lambda_expr)); - { /* Inside the class, surrounding template-parameter-lists do not apply. */ unsigned int saved_num_template_parameter_lists = parser->num_template_parameter_lists; @@ -11763,6 +11838,7 @@ cp_parser_lambda_expression (cp_parser* parser) bool auto_is_implicit_function_template_parm_p = parser->auto_is_implicit_function_template_parm_p; bool saved_omp_array_section_p = parser->omp_array_section_p; + bool saved_in_targ = parser->in_template_argument_list_p; parser->num_template_parameter_lists = 0; parser->in_statement = 0; @@ -11772,6 +11848,7 @@ cp_parser_lambda_expression (cp_parser* parser) parser->implicit_template_scope = 0; parser->auto_is_implicit_function_template_parm_p = false; parser->omp_array_section_p = false; + parser->in_template_argument_list_p = false; /* Inside the lambda, outside unevaluated context do not apply. */ cp_evaluated ev; @@ -11826,6 +11903,7 @@ cp_parser_lambda_expression (cp_parser* parser) parser->auto_is_implicit_function_template_parm_p = auto_is_implicit_function_template_parm_p; parser->omp_array_section_p = saved_omp_array_section_p; + parser->in_template_argument_list_p = saved_in_targ; } /* This lambda shouldn't have any proxies left at this point. */ @@ -15376,11 +15454,12 @@ cp_parser_jump_statement (cp_parser* parser, tree &std_attrs) case RID_GOTO: if (parser->in_function_body - && DECL_DECLARED_CONSTEXPR_P (current_function_decl) + && maybe_constexpr_fn (current_function_decl) && cxx_dialect < cxx23) { - error ("%<goto%> in %<constexpr%> function only available with " - "%<-std=c++23%> or %<-std=gnu++23%>"); + if (DECL_DECLARED_CONSTEXPR_P (current_function_decl)) + error ("%<goto%> in %<constexpr%> function only available with " + "%<-std=c++23%> or %<-std=gnu++23%>"); cp_function_chain->invalid_constexpr = true; } @@ -15772,7 +15851,13 @@ cp_parser_import_declaration (cp_parser *parser, module_parse mp_state, " must not be from header inclusion"); } + auto mk = module_kind; + if (exporting) + module_kind |= MK_EXPORTING; + import_module (mod, token->location, exporting, attrs, parse_in); + + module_kind = mk; } } @@ -16797,7 +16882,11 @@ cp_parser_decomposition_declaration (cp_parser *parser, decl = error_mark_node; } else - prev = decl2; + { + prev = decl2; + DECL_DECLARED_CONSTEXPR_P (decl2) = DECL_DECLARED_CONSTEXPR_P (decl); + DECL_DECLARED_CONSTINIT_P (decl2) = DECL_DECLARED_CONSTINIT_P (decl); + } if (elt_pushed_scope) pop_scope (elt_pushed_scope); } @@ -16846,6 +16935,15 @@ cp_parser_decomposition_declaration (cp_parser *parser, /* Ensure DECL_VALUE_EXPR is created for all the decls but the underlying DECL. */ cp_finish_decomp (decl, &decomp); + if (decl_spec_seq_has_spec_p (decl_specifiers, ds_thread)) + pedwarn (decl_specifiers->locations[ds_thread], + 0, "for-range-declaration cannot be %qs", + decl_specifiers->gnu_thread_keyword_p + ? "__thread" : "thread_local"); + else if (decl_specifiers->storage_class == sc_static) + pedwarn (decl_specifiers->locations[ds_storage_class], + 0, "for-range-declaration cannot be %qs", + "static"); } if (pushed_scope) @@ -18845,7 +18943,7 @@ cp_parser_template_declaration (cp_parser* parser, bool member_p) else if (cxx_dialect < cxx20) warning (0, "keyword %<export%> is deprecated, and is ignored"); else - warning (0, "keyword %<export%> is enabled with %<-fmodules-ts%>"); + warning (0, "keyword %<export%> is enabled with %<-fmodules%>"); } cp_parser_template_declaration_after_export (parser, member_p); @@ -20939,9 +21037,6 @@ cp_parser_simple_type_specifier (cp_parser* parser, "only available with " "%<-std=c++14%> or %<-std=gnu++14%>"); } - else if (parser->in_template_argument_list_p) - error_at (token->location, - "use of %<auto%> in template argument"); else if (!flag_concepts) pedwarn (token->location, OPT_Wc__20_extensions, "use of %<auto%> in parameter declaration " @@ -20951,6 +21046,11 @@ cp_parser_simple_type_specifier (cp_parser* parser, "use of %<auto%> in parameter declaration " "only available with " "%<-std=c++14%> or %<-std=gnu++14%>"); + + if (parser->in_template_argument_list_p) + permerror_opt (token->location, + OPT_Wabbreviated_auto_in_template_arg, + "use of %<auto%> in template argument"); } else type = make_auto (); @@ -21079,7 +21179,9 @@ cp_parser_simple_type_specifier (cp_parser* parser, /*typename_keyword_p=*/false, /*check_dependency_p=*/true, /*type_p=*/false, - /*is_declaration=*/false) + /*is_declaration=*/false, + /*template_keyword_p=*/false, + global_p) != NULL_TREE); /* If we have seen a nested-name-specifier, and the next token is `template', then we are using the template-id production. */ @@ -21397,6 +21499,10 @@ cp_parser_placeholder_type_specifier (cp_parser *parser, location_t loc, error_at (loc, "cannot declare a parameter with %<decltype(auto)%>"); return error_mark_node; } + if (parser->in_template_argument_list_p) + permerror_opt (placeholder->location, + OPT_Wabbreviated_auto_in_template_arg, + "use of %<auto%> in template argument"); tree parm = build_constrained_parameter (con, proto, args); return synthesize_implicit_template_parm (parser, parm); } @@ -21925,7 +22031,7 @@ cp_parser_elaborated_type_specifier (cp_parser* parser, bool template_p = (template_parm_lists_apply - && (cp_parser_next_token_starts_class_definition_p (parser) + && (cp_parser_nth_token_starts_class_definition_p (parser, 1) || cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))); /* An unqualified name was used to reference this type, so there were no qualifying templates. */ @@ -24089,7 +24195,26 @@ cp_parser_init_declarator (cp_parser* parser, && token->type != CPP_SEMICOLON) { if (maybe_range_for_decl && *maybe_range_for_decl != error_mark_node) - range_for_decl_p = true; + { + range_for_decl_p = true; + if (decl_spec_seq_has_spec_p (decl_specifiers, ds_thread)) + pedwarn (decl_specifiers->locations[ds_thread], + 0, "for-range-declaration cannot be %qs", + decl_specifiers->gnu_thread_keyword_p + ? "__thread" : "thread_local"); + else if (decl_specifiers->storage_class == sc_static) + pedwarn (decl_specifiers->locations[ds_storage_class], + 0, "for-range-declaration cannot be %qs", + "static"); + else if (decl_specifiers->storage_class == sc_extern) + pedwarn (decl_specifiers->locations[ds_storage_class], + 0, "for-range-declaration cannot be %qs", + "extern"); + else if (decl_specifiers->storage_class == sc_register) + pedwarn (decl_specifiers->locations[ds_storage_class], + 0, "for-range-declaration cannot be %qs", + "register"); + } else { if (!maybe_range_for_decl) @@ -27943,6 +28068,64 @@ cp_parser_class_specifier (cp_parser* parser) return type; } +/* Parse an (optional) class-property-specifier-seq. + + class-property-specifier-seq: + class-property-specifier class-property-specifier-seq [opt] + + class-property-specifier: + final + + Returns a bitmask representing the class-property-specifiers. */ + +static cp_virt_specifiers +cp_parser_class_property_specifier_seq_opt (cp_parser *parser) +{ + cp_virt_specifiers virt_specifiers = VIRT_SPEC_UNSPECIFIED; + + while (true) + { + cp_token *token; + cp_virt_specifiers virt_specifier; + + /* Peek at the next token. */ + token = cp_lexer_peek_token (parser->lexer); + /* See if it's a class-property-specifier. */ + if (token->type != CPP_NAME) + break; + if (id_equal (token->u.value, "final")) + { + /* For C++98, quietly ignore final in e.g. + struct S final = 24; */ + if (cxx_dialect == cxx98 + && virt_specifiers == VIRT_SPEC_UNSPECIFIED + && !cp_parser_nth_token_starts_class_definition_p (parser, 2) + && !cp_lexer_nth_token_is (parser->lexer, 2, CPP_NAME)) + break; + maybe_warn_cpp0x (CPP0X_OVERRIDE_CONTROLS); + virt_specifier = VIRT_SPEC_FINAL; + } + else if (id_equal (token->u.value, "__final")) + virt_specifier = VIRT_SPEC_FINAL; + else + break; + + if (virt_specifiers & virt_specifier) + { + gcc_rich_location richloc (token->location); + richloc.add_fixit_remove (); + error_at (&richloc, "duplicate %qD specifier", token->u.value); + cp_lexer_purge_token (parser->lexer); + } + else + { + cp_lexer_consume_token (parser->lexer); + virt_specifiers |= virt_specifier; + } + } + return virt_specifiers; +} + /* Parse a class-head. class-head: @@ -28133,18 +28316,16 @@ cp_parser_class_head (cp_parser* parser, pop_deferring_access_checks (); if (id) - { - cp_parser_check_for_invalid_template_id (parser, id, - class_key, - type_start_token->location); - } - virt_specifiers = cp_parser_virt_specifier_seq_opt (parser); + cp_parser_check_for_invalid_template_id (parser, id, + class_key, + type_start_token->location); + virt_specifiers = cp_parser_class_property_specifier_seq_opt (parser); /* If it's not a `:' or a `{' then we can't really be looking at a class-head, since a class-head only appears as part of a class-specifier. We have to detect this situation before calling xref_tag, since that has irreversible side-effects. */ - if (!cp_parser_next_token_starts_class_definition_p (parser)) + if (!cp_parser_nth_token_starts_class_definition_p (parser, 1)) { cp_parser_error (parser, "expected %<{%> or %<:%>"); type = error_mark_node; @@ -28154,13 +28335,6 @@ cp_parser_class_head (cp_parser* parser, /* At this point, we're going ahead with the class-specifier, even if some other problem occurs. */ cp_parser_commit_to_tentative_parse (parser); - if (virt_specifiers & VIRT_SPEC_OVERRIDE) - { - cp_parser_error (parser, - "cannot specify %<override%> for a class"); - type = error_mark_node; - goto out; - } /* Issue the error about the overly-qualified name now. */ if (qualified_p) { @@ -30359,6 +30533,9 @@ cp_parser_asm_operand_list (cp_parser* parser) parens.require_open (parser); /* Parse the expression. */ tree expression = cp_parser_expression (parser); + if (check_for_bare_parameter_packs (expression)) + expression = error_mark_node; + /* Look for the `)'. */ parens.require_close (parser); @@ -33621,6 +33798,8 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, = parser->num_template_parameter_lists; parser->num_template_parameter_lists = 0; + int errs = errorcount + sorrycount; + /* If the next token is `try', `__transaction_atomic', or `__transaction_relaxed`, then we are looking at either function-try-block or function-transaction-block. Note that all of these include the @@ -33640,6 +33819,9 @@ cp_parser_function_definition_after_declarator (cp_parser* parser, fn = finish_function (inline_p); check_module_decl_linkage (fn); + if ((errorcount + sorrycount) > errs) + DECL_STRUCT_FUNCTION (fn)->language->erroneous = true; + if (modules_p () && !inline_p && TYPE_P (DECL_CONTEXT (fn)) @@ -35521,15 +35703,15 @@ cp_parser_token_starts_function_definition_p (cp_token* token) || token->keyword == RID_RETURN); } -/* Returns TRUE iff the next token is the ":" or "{" beginning a class +/* Returns TRUE iff the Nth token is the ":" or "{" beginning a class definition. */ static bool -cp_parser_next_token_starts_class_definition_p (cp_parser *parser) +cp_parser_nth_token_starts_class_definition_p (cp_parser *parser, size_t n) { cp_token *token; - token = cp_lexer_peek_token (parser->lexer); + token = cp_lexer_peek_nth_token (parser->lexer, n); return (token->type == CPP_OPEN_BRACE || (token->type == CPP_COLON && !parser->colon_doesnt_start_class_def_p)); @@ -35809,7 +35991,9 @@ cp_parser_check_class_key (cp_parser *parser, location_t key_loc, return; bool seen_as_union = TREE_CODE (type) == UNION_TYPE; - if (seen_as_union != (class_key == union_type)) + if (class_key != typename_type + && TREE_CODE (type) != TYPENAME_TYPE + && seen_as_union != (class_key == union_type)) { auto_diagnostic_group d; if (permerror (input_location, "%qs tag used in naming %q#T", @@ -36583,7 +36767,7 @@ static void cp_parser_abort_tentative_parse (cp_parser* parser) { gcc_assert (parser->context->status != CP_PARSER_STATUS_KIND_COMMITTED - || errorcount > 0); + || seen_error ()); cp_parser_simulate_error (parser); /* Now, pretend that we want to see if the construct was successfully parsed. */ @@ -42357,13 +42541,13 @@ cp_parser_omp_clause_from_to (cp_parser *parser, enum omp_clause_code kind, map ( [map-type-modifier[,] ...] map-kind: variable-list ) map-type-modifier: - always | close */ + always | close | mapper ( mapper-name ) */ static tree -cp_parser_omp_clause_map (cp_parser *parser, tree list) +cp_parser_omp_clause_map (cp_parser *parser, tree list, bool declare_mapper_p) { tree nlist, c; - enum gomp_map_kind kind = GOMP_MAP_TOFROM; + enum gomp_map_kind kind = declare_mapper_p ? GOMP_MAP_UNSET : GOMP_MAP_TOFROM; if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) return list; @@ -42381,12 +42565,17 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list) if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type == CPP_COMMA) pos++; + else if (cp_lexer_peek_nth_token (parser->lexer, pos + 1)->type + == CPP_OPEN_PAREN) + pos = cp_parser_skip_balanced_tokens (parser, pos + 1); pos++; } bool always_modifier = false; bool close_modifier = false; bool present_modifier = false; + bool mapper_modifier = false; + tree mapper_name = NULL_TREE; for (int pos = 1; pos < map_kind_pos; ++pos) { cp_token *tok = cp_lexer_peek_token (parser->lexer); @@ -42409,6 +42598,7 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list) return list; } always_modifier = true; + cp_lexer_consume_token (parser->lexer); } else if (strcmp ("close", p) == 0) { @@ -42422,6 +42612,77 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list) return list; } close_modifier = true; + cp_lexer_consume_token (parser->lexer); + } + else if (strcmp ("mapper", p) == 0) + { + cp_lexer_consume_token (parser->lexer); + + matching_parens parens; + if (parens.require_open (parser)) + { + if (mapper_modifier) + { + cp_parser_error (parser, "too many %<mapper%> modifiers"); + /* Assume it's a well-formed mapper modifier, even if it + seems to be in the wrong place. */ + cp_lexer_consume_token (parser->lexer); + parens.require_close (parser); + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/ + true); + return list; + } + + tok = cp_lexer_peek_token (parser->lexer); + switch (tok->type) + { + case CPP_NAME: + { + cp_expr e = cp_parser_identifier (parser); + if (e != error_mark_node) + mapper_name = e; + else + goto err; + if (declare_mapper_p) + { + error_at (e.get_location (), + "in %<declare mapper%> directives, parameter " + "to %<mapper%> modifier must be %<default%>"); + } + } + break; + + case CPP_KEYWORD: + if (tok->keyword == RID_DEFAULT) + { + cp_lexer_consume_token (parser->lexer); + break; + } + /* Fallthrough. */ + + default: + err: + cp_parser_error (parser, + "expected identifier or %<default%>"); + return list; + } + + if (!parens.require_close (parser)) + { + cp_parser_skip_to_closing_parenthesis (parser, + /*recovering=*/true, + /*or_comma=*/false, + /*consume_paren=*/ + true); + return list; + } + + mapper_modifier = true; + pos += 3; + } } else if (strcmp ("present", p) == 0) { @@ -42435,19 +42696,19 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list) return list; } present_modifier = true; - } + cp_lexer_consume_token (parser->lexer); + } else { - cp_parser_error (parser, "%<map%> clause with map-type modifier other" - " than %<always%>, %<close%> or %<present%>"); + cp_parser_error (parser, "%<map%> clause with map-type modifier " + "other than %<always%>, %<close%>, " + "%<mapper%> or %<present%>"); cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true, /*or_comma=*/false, /*consume_paren=*/true); return list; } - - cp_lexer_consume_token (parser->lexer); } if (cp_lexer_next_token_is (parser->lexer, CPP_NAME) @@ -42503,8 +42764,30 @@ cp_parser_omp_clause_map (cp_parser *parser, tree list) NULL, true); finish_scope (); + tree last_new = NULL_TREE; + for (c = nlist; c != list; c = OMP_CLAUSE_CHAIN (c)) - OMP_CLAUSE_SET_MAP_KIND (c, kind); + { + OMP_CLAUSE_SET_MAP_KIND (c, kind); + last_new = c; + } + + if (mapper_name) + { + tree name = build_omp_clause (input_location, OMP_CLAUSE_MAP); + OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_PUSH_MAPPER_NAME); + OMP_CLAUSE_DECL (name) = mapper_name; + OMP_CLAUSE_CHAIN (name) = nlist; + nlist = name; + + gcc_assert (last_new); + + name = build_omp_clause (input_location, OMP_CLAUSE_MAP); + OMP_CLAUSE_SET_MAP_KIND (name, GOMP_MAP_POP_MAPPER_NAME); + OMP_CLAUSE_DECL (name) = null_pointer_node; + OMP_CLAUSE_CHAIN (name) = OMP_CLAUSE_CHAIN (last_new); + OMP_CLAUSE_CHAIN (last_new) = name; + } return nlist; } @@ -43121,8 +43404,8 @@ cp_parser_omp_clause_init_modifiers (cp_parser *parser, bool *target, while (true); fail: - cp_parser_error (parser, "%<init%> clause with modifier other than " - "%<prefer_type%>, %<target%> or %<targetsync%>"); + cp_parser_error (parser, + "expected %<prefer_type%>, %<target%>, or %<targetsync%>"); return false; } @@ -43140,39 +43423,14 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list) if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) return list; - unsigned raw_pos = 1; - while (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type == CPP_NAME) - { - raw_pos++; - if (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type - == CPP_OPEN_PAREN) - { - unsigned n = cp_parser_skip_balanced_tokens (parser, raw_pos); - if (n == raw_pos) - { - raw_pos = 0; - break; - } - raw_pos = n; - } - if (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type == CPP_COLON) - break; - if (cp_lexer_peek_nth_token (parser->lexer, raw_pos)->type != CPP_COMMA) - { - raw_pos = 0; - break; - } - raw_pos++; - } - bool target = false; bool targetsync = false; tree prefer_type_tree = NULL_TREE; + location_t loc = cp_lexer_peek_token (parser->lexer)->location; - if (raw_pos > 1 - && (!cp_parser_omp_clause_init_modifiers (parser, &target, &targetsync, - &prefer_type_tree) - || !cp_parser_require (parser, CPP_COLON, RT_COLON))) + if (!cp_parser_omp_clause_init_modifiers (parser, &target, &targetsync, + &prefer_type_tree) + || !cp_parser_require (parser, CPP_COLON, RT_COLON)) { if (prefer_type_tree == error_mark_node) return error_mark_node; @@ -43182,6 +43440,10 @@ cp_parser_omp_clause_init (cp_parser *parser, tree list) return list; } + if (!target && !targetsync) + error_at (loc, + "missing required %<target%> and/or %<targetsync%> modifier"); + tree nl = cp_parser_omp_var_list_no_open (parser, OMP_CLAUSE_INIT, list, NULL, false); for (tree c = nl; c != list; c = OMP_CLAUSE_CHAIN (c)) @@ -43853,7 +44115,8 @@ cp_parser_omp_all_clauses (cp_parser *parser, omp_clause_mask mask, c_name = "detach"; break; case PRAGMA_OMP_CLAUSE_MAP: - clauses = cp_parser_omp_clause_map (parser, clauses); + clauses = cp_parser_omp_clause_map (parser, clauses, + /*mapper=*/false); c_name = "map"; break; case PRAGMA_OMP_CLAUSE_DEVICE: @@ -48859,6 +49122,8 @@ cp_parser_omp_target (cp_parser *parser, cp_token *pragma_tok, OMP_CLAUSE_CHAIN (nc) = OMP_CLAUSE_CHAIN (c); OMP_CLAUSE_CHAIN (c) = nc; } + if (!processing_template_decl) + clauses = c_omp_instantiate_mappers (clauses); clauses = finish_omp_clauses (clauses, C_ORT_OMP_TARGET); c_omp_adjust_map_clauses (clauses, true); @@ -49553,7 +49818,8 @@ cp_parser_oacc_update (cp_parser *parser, cp_token *pragma_tok) */ #define OACC_WAIT_CLAUSE_MASK \ - ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC)) + ( (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_ASYNC) \ + | (OMP_CLAUSE_MASK_1 << PRAGMA_OACC_CLAUSE_IF)) static tree cp_parser_oacc_wait (cp_parser *parser, cp_token *pragma_tok) @@ -49838,12 +50104,25 @@ cp_parser_omp_context_selector (cp_parser *parser, enum omp_tss_code set, && !value_dependent_expression_p (t)) { t = fold_non_dependent_expr (t); - if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + if (property_kind == OMP_TRAIT_PROPERTY_BOOL_EXPR) { - error_at (token->location, - "property must be integer expression"); - return error_mark_node; + t = maybe_convert_cond (t); + if (t == error_mark_node) + return error_mark_node; } + else + { + t = convert_from_reference (t); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + { + error_at (token->location, + "property must be integer expression"); + return error_mark_node; + } + } + if (!processing_template_decl + && TREE_CODE (t) != CLEANUP_POINT_EXPR) + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); } properties = make_trait_property (NULL_TREE, t, properties); break; @@ -50581,6 +50860,10 @@ cp_finish_omp_declare_variant (cp_parser *parser, cp_token *pragma_tok, &targetsync, &prefer_type_tree)) goto fail; + if (!target && !targetsync) + error_at (loc, + "missing required %<target%> and/or " + "%<targetsync%> modifier"); tree t = build_tree_list (target ? boolean_true_node : boolean_false_node, targetsync ? boolean_true_node @@ -51408,7 +51691,6 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, { error_at (match_loc, "too many %<otherwise%> or %<default%> " "clauses in %<metadirective%>"); - cp_parser_skip_to_end_of_block_or_statement (parser, true); goto fail; } else @@ -51418,14 +51700,12 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, { error_at (match_loc, "%<otherwise%> or %<default%> clause " "must appear last in %<metadirective%>"); - cp_parser_skip_to_end_of_block_or_statement (parser, true); goto fail; } if (!default_p && strcmp (p, "when") != 0) { error_at (match_loc, "%qs is not valid for %qs", p, "metadirective"); - cp_parser_skip_to_end_of_block_or_statement (parser, true); goto fail; } @@ -51494,7 +51774,6 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, if (i == 0) { error_at (loc, "expected directive name"); - cp_parser_skip_to_end_of_block_or_statement (parser, true); goto fail; } @@ -51567,7 +51846,10 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, goto add; case CPP_CLOSE_PAREN: if (nesting_depth-- == 0) - break; + { + cp_lexer_consume_token (parser->lexer); + break; + } goto add; default: add: @@ -51579,8 +51861,6 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, break; } - cp_lexer_consume_token (parser->lexer); - if (!skip) { cp_token eol_token = {}; @@ -51712,11 +51992,8 @@ cp_parser_omp_metadirective (cp_parser *parser, cp_token *pragma_tok, return; fail: - /* Skip the metadirective pragma. */ + /* Skip the metadirective pragma. Do not skip the metadirective body. */ cp_parser_skip_to_pragma_eol (parser, pragma_tok); - - /* Skip the metadirective body. */ - cp_parser_skip_to_end_of_block_or_statement (parser, true); } @@ -52135,6 +52412,175 @@ cp_parser_omp_declare_reduction (cp_parser *parser, cp_token *pragma_tok, obstack_free (&declarator_obstack, p); } +/* OpenMP 5.0 + #pragma omp declare mapper([mapper-identifier:]type var) \ + [clause[[,] clause] ... ] new-line */ + +static void +cp_parser_omp_declare_mapper (cp_parser *parser, cp_token *pragma_tok, + enum pragma_context) +{ + cp_token *token = NULL; + tree type = NULL_TREE, vardecl = NULL_TREE, block = NULL_TREE; + bool block_scope = false; + /* Don't create location wrapper nodes within "declare mapper" + directives. */ + auto_suppress_location_wrappers sentinel; + tree mapper_name = NULL_TREE; + tree mapper_id, id, placeholder, mapper, maplist = NULL_TREE; + + if (!cp_parser_require (parser, CPP_OPEN_PAREN, RT_OPEN_PAREN)) + goto fail; + + if (current_function_decl) + block_scope = true; + + token = cp_lexer_peek_token (parser->lexer); + + if (cp_lexer_nth_token_is (parser->lexer, 2, CPP_COLON)) + { + switch (token->type) + { + case CPP_NAME: + { + cp_expr e = cp_parser_identifier (parser); + if (e != error_mark_node) + mapper_name = e; + else + goto fail; + } + break; + + case CPP_KEYWORD: + if (token->keyword == RID_DEFAULT) + { + mapper_name = NULL_TREE; + cp_lexer_consume_token (parser->lexer); + break; + } + /* Fallthrough. */ + + default: + cp_parser_error (parser, "expected identifier or %<default%>"); + } + + if (!cp_parser_require (parser, CPP_COLON, RT_COLON)) + goto fail; + } + + { + const char *saved_message = parser->type_definition_forbidden_message; + parser->type_definition_forbidden_message + = G_("types may not be defined within %<declare mapper%>"); + type_id_in_expr_sentinel s (parser); + type = cp_parser_type_id (parser); + parser->type_definition_forbidden_message = saved_message; + } + + if (type == error_mark_node) + goto fail; + if (dependent_type_p (type)) + mapper_id = omp_mapper_id (mapper_name, NULL_TREE); + else + mapper_id = omp_mapper_id (mapper_name, type); + + vardecl = build_lang_decl (VAR_DECL, mapper_id, type); + DECL_ARTIFICIAL (vardecl) = 1; + TREE_STATIC (vardecl) = 1; + TREE_PUBLIC (vardecl) = 0; + DECL_EXTERNAL (vardecl) = 0; + DECL_DECLARED_CONSTEXPR_P (vardecl) = 1; + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (vardecl) = 1; + DECL_OMP_DECLARE_MAPPER_P (vardecl) = 1; + + keep_next_level (true); + block = begin_omp_structured_block (); + + if (block_scope) + DECL_CONTEXT (vardecl) = current_function_decl; + else if (current_class_type) + DECL_CONTEXT (vardecl) = current_class_type; + else + DECL_CONTEXT (vardecl) = current_namespace; + + if (processing_template_decl) + vardecl = push_template_decl (vardecl); + + if ((id = cp_parser_declarator_id (parser, false)) == error_mark_node) + goto fail; + + if (!cp_parser_require (parser, CPP_CLOSE_PAREN, RT_CLOSE_PAREN)) + { + finish_omp_structured_block (block); + goto fail; + } + + placeholder = build_lang_decl (VAR_DECL, id, type); + DECL_CONTEXT (placeholder) = DECL_CONTEXT (vardecl); + if (processing_template_decl) + placeholder = push_template_decl (placeholder); + pushdecl (placeholder); + cp_finish_decl (placeholder, NULL_TREE, 0, NULL_TREE, 0); + DECL_ARTIFICIAL (placeholder) = 1; + TREE_USED (placeholder) = 1; + + while (cp_lexer_next_token_is_not (parser->lexer, CPP_PRAGMA_EOL)) + { + pragma_omp_clause c_kind = cp_parser_omp_clause_name (parser); + if (c_kind != PRAGMA_OMP_CLAUSE_MAP) + { + if (c_kind != PRAGMA_OMP_CLAUSE_NONE) + cp_parser_error (parser, "unexpected clause"); + finish_omp_structured_block (block); + goto fail; + } + maplist = cp_parser_omp_clause_map (parser, maplist, /*mapper=*/true); + if (maplist == NULL_TREE) + break; + } + + if (maplist == NULL_TREE) + { + cp_parser_error (parser, "missing %<map%> clause"); + finish_omp_structured_block (block); + goto fail; + } + + mapper = make_node (OMP_DECLARE_MAPPER); + TREE_TYPE (mapper) = type; + OMP_DECLARE_MAPPER_ID (mapper) = mapper_name; + OMP_DECLARE_MAPPER_DECL (mapper) = placeholder; + OMP_DECLARE_MAPPER_CLAUSES (mapper) = maplist; + + finish_omp_structured_block (block); + + DECL_INITIAL (vardecl) = mapper; + + if (current_class_type) + { + if (processing_template_decl) + { + retrofit_lang_decl (vardecl); + SET_DECL_VAR_DECLARED_INLINE_P (vardecl); + } + finish_static_data_member_decl (vardecl, mapper, + /*init_const_expr_p=*/true, NULL_TREE, 0); + finish_member_declaration (vardecl); + } + else if (processing_template_decl && block_scope) + add_decl_expr (vardecl); + else + pushdecl (vardecl); + + cp_check_omp_declare_mapper (vardecl); + + cp_parser_require_pragma_eol (parser, pragma_tok); + return; + +fail: + cp_parser_skip_to_pragma_eol (parser, pragma_tok); +} + /* OpenMP 4.0 #pragma omp declare simd declare-simd-clauses[optseq] new-line #pragma omp declare reduction (reduction-id : typename-list : expression) \ @@ -52179,6 +52625,12 @@ cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok, context); return false; } + if (strcmp (p, "mapper") == 0) + { + cp_lexer_consume_token (parser->lexer); + cp_parser_omp_declare_mapper (parser, pragma_tok, context); + return false; + } if (!flag_openmp) /* flag_openmp_simd */ { cp_parser_skip_to_pragma_eol (parser, pragma_tok); @@ -52192,7 +52644,7 @@ cp_parser_omp_declare (cp_parser *parser, cp_token *pragma_tok, } } cp_parser_error (parser, "expected %<simd%>, %<reduction%>, " - "%<target%> or %<variant%>"); + "%<target%>, %<mapper%> or %<variant%>"); cp_parser_require_pragma_eol (parser, pragma_tok); return false; } diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index f7c56a1..8f3822c 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -202,7 +202,6 @@ static tree for_each_template_parm_r (tree *, int *, void *); static tree copy_default_args_to_explicit_spec_1 (tree, tree); static void copy_default_args_to_explicit_spec (tree); static bool invalid_nontype_parm_type_p (tree, tsubst_flags_t); -static bool dependent_template_arg_p (tree); static bool dependent_type_p_r (tree); static tree tsubst_stmt (tree, tree, tsubst_flags_t, tree); static tree tsubst_decl (tree, tree, tsubst_flags_t, bool = true); @@ -7492,8 +7491,13 @@ get_template_parm_object (tree expr, tree name, bool check_init/*=true*/) { /* The EXPR is the already processed initializer, set it on the NTTP object now so that cp_finish_decl doesn't do it again later. */ + gcc_checking_assert (reduced_constant_expression_p (expr)); DECL_INITIAL (decl) = expr; - DECL_INITIALIZED_P (decl) = 1; + DECL_INITIALIZED_P (decl) = true; + DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (decl) = true; + /* FIXME setting TREE_CONSTANT on refs breaks the back end. */ + if (!TYPE_REF_P (type)) + TREE_CONSTANT (decl) = true; } pushdecl_top_level_and_finish (decl, expr); @@ -10047,15 +10051,20 @@ tsubst_entering_scope (tree t, tree args, tsubst_flags_t complain, tree in_decl) D1 is the PTYPENAME terminal, and ARGLIST is the list of arguments. + If D1 is an identifier and CONTEXT is non-NULL, then the lookup is + carried out in CONTEXT. Currently, only namespaces are supported for + CONTEXT. + + If D1 is an identifier and CONTEXT is NULL, the lookup is performed + in the innermost non-namespace binding. + + Otherwise CONTEXT is ignored and no lookup is carried out. + IN_DECL, if non-NULL, is the template declaration we are trying to instantiate. Issue error and warning messages under control of COMPLAIN. - If the template class is really a local class in a template - function, then the FUNCTION_CONTEXT is the function in which it is - being instantiated. - ??? Note that this function is currently called *twice* for each template-id: the first time from the parser, while creating the incomplete type (finish_template_type), and the second type during the @@ -10074,20 +10083,23 @@ lookup_template_class (tree d1, tree arglist, tree in_decl, tree context, spec_entry **slot; spec_entry *entry; - if (identifier_p (d1)) + if (identifier_p (d1) && context) + { + gcc_checking_assert (TREE_CODE (context) == NAMESPACE_DECL); + push_decl_namespace (context); + templ = lookup_name (d1, LOOK_where::NAMESPACE, LOOK_want::NORMAL); + pop_decl_namespace (); + } + else if (identifier_p (d1)) { tree value = innermost_non_namespace_value (d1); if (value && DECL_TEMPLATE_TEMPLATE_PARM_P (value)) templ = value; else - { - if (context) - push_decl_namespace (context); + { templ = lookup_name (d1); templ = maybe_get_template_decl_from_type_decl (templ); - if (context) - pop_decl_namespace (); - } + } } else if (TREE_CODE (d1) == TYPE_DECL && MAYBE_CLASS_TYPE_P (TREE_TYPE (d1))) { @@ -11117,6 +11129,18 @@ any_template_parm_r (tree t, void *data) case LAMBDA_EXPR: { + /* TREE_STATIC on LAMBDA_EXPR_EXTRA_ARGS means a full set of + arguments, so we can just look there; they will replace + any template parms in the rest of the LAMBDA_EXPR. */ + if (tree args = LAMBDA_EXPR_EXTRA_ARGS (t)) + { + WALK_SUBTREE (args); + /* Without TREE_STATIC the args are just outer levels, so we'd + still need to look through the lambda for just inner + parameters. Hopefully that's not necessary. */ + gcc_checking_assert (TREE_STATIC (args)); + return 0; + } /* Look in the parms and body. */ tree fn = lambda_function (t); WALK_SUBTREE (TREE_TYPE (fn)); @@ -11361,6 +11385,7 @@ limit_bad_template_recursion (tree decl) static int tinst_depth; extern int max_tinst_depth; int depth_reached; +int tinst_dump_id; static GTY(()) struct tinst_level *last_error_tinst_level; @@ -11406,12 +11431,47 @@ push_tinst_level_loc (tree tldcl, tree targs, location_t loc) new_level->targs = targs; new_level->locus = loc; new_level->errors = errorcount + sorrycount; + new_level->had_errors = false; new_level->next = NULL; new_level->refcount = 0; new_level->path = new_level->visible = nullptr; set_refcount_ptr (new_level->next, current_tinst_level); set_refcount_ptr (current_tinst_level, new_level); + if (cxx_dump_pretty_printer pp {tinst_dump_id}) + { +#if __GNUC__ >= 10 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-diag" +#endif + bool list_p = new_level->list_p (); + if (list_p && !pp.has_flag (TDF_DETAILS)) + /* Skip non-instantiations unless -details. */; + else + { + if (tinst_depth == 0) + pp_newline (&pp); + if (loc && pp.has_flag (TDF_LINENO)) + { + for (int i = 0; i < tinst_depth; ++i) + pp_space (&pp); + const expanded_location el = expand_location (loc); + pp_printf (&pp, "%s:%d:%d", el.file, el.line, el.column); + pp_newline (&pp); + } + for (int i = 0; i < tinst_depth; ++i) + pp_space (&pp); + if (list_p) + pp_printf (&pp, "S %S", new_level->get_node ()); + else + pp_printf (&pp, "I %D", tldcl); + pp_newline (&pp); + } +#if __GNUC__ >= 10 +#pragma GCC diagnostic pop +#endif + } + ++tinst_depth; if (GATHER_STATISTICS && (tinst_depth > depth_reached)) depth_reached = tinst_depth; @@ -11456,10 +11516,27 @@ pop_tinst_level (void) /* Restore the filename and line number stashed away when we started this instantiation. */ input_location = current_tinst_level->locus; + if (unsigned errs = errorcount + sorrycount) + if (errs > current_tinst_level->errors) + current_tinst_level->had_errors = true; set_refcount_ptr (current_tinst_level, current_tinst_level->next); --tinst_depth; } +/* True if the instantiation represented by LEVEL is complete. */ + +static bool +tinst_complete_p (struct tinst_level *level) +{ + gcc_assert (!level->list_p ()); + tree node = level->get_node (); + if (TYPE_P (node)) + return COMPLETE_TYPE_P (node); + else + return (DECL_TEMPLATE_INSTANTIATED (node) + || DECL_TEMPLATE_SPECIALIZATION (node)); +} + /* We're instantiating a deferred template; restore the template instantiation context in which the instantiation was requested, which is one step out from LEVEL. Return the corresponding DECL or TYPE. */ @@ -11475,9 +11552,41 @@ reopen_tinst_level (struct tinst_level *level) set_refcount_ptr (current_tinst_level, level); pop_tinst_level (); - if (current_tinst_level) + if (current_tinst_level && !current_tinst_level->had_errors) current_tinst_level->errors = errorcount+sorrycount; + if (cxx_dump_pretty_printer pp {tinst_dump_id}) + { +#if __GNUC__ >= 10 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-diag" +#endif + /* Dump the reopened instantiation context. */ + t = current_tinst_level; + if (!pp.has_flag (TDF_DETAILS)) + /* Skip non-instantiations unless -details. */ + while (t && t->list_p ()) + t = t->next; + if (t) + { + static tree last_ctx = NULL_TREE; + tree ctx = t->get_node (); + if (ctx != last_ctx) + { + last_ctx = ctx; + pp_newline (&pp); + if (t->list_p ()) + pp_printf (&pp, "RS %S", ctx); + else + pp_printf (&pp, "RI %D", ctx); + pp_newline (&pp); + } + } +#if __GNUC__ >= 10 +#pragma GCC diagnostic pop +#endif + } + tree decl = level->maybe_get_node (); if (decl && modules_p ()) { @@ -11760,6 +11869,10 @@ tsubst_friend_function (tree decl, tree args) elt.args = DECL_TI_ARGS (spec); elt.spec = NULL_TREE; + if (TMPL_ARGS_HAVE_MULTIPLE_LEVELS (DECL_TI_ARGS (spec)) + && !is_specialization_of_friend (spec, new_template)) + continue; + decl_specializations->remove_elt (&elt); tree& spec_args = DECL_TI_ARGS (spec); @@ -12413,6 +12526,8 @@ apply_late_template_attributes (tree *decl_p, tree attributes, int attr_flags, auto o4 = make_temp_override (scope_chain->omp_declare_target_attribute, NULL); auto o5 = make_temp_override (scope_chain->omp_begin_assumes, NULL); + auto o6 = make_temp_override (target_option_current_node, + target_option_default_node); cplus_decl_attributes (decl_p, late_attrs, attr_flags); @@ -13736,11 +13851,12 @@ add_extra_args (tree extra, tree args, tsubst_flags_t complain, tree in_decl) inst = local; /* else inst is already a full instantiation of the pack. */ register_local_specialization (inst, gen); + if (is_normal_capture_proxy (gen)) + register_local_specialization (inst, DECL_CAPTURED_VARIABLE (gen)); } gcc_assert (!TREE_PURPOSE (extra)); extra = TREE_VALUE (extra); } - gcc_checking_assert (TREE_STATIC (extra) == uses_template_parms (extra)); if (TREE_STATIC (extra)) /* This is a partial substitution into e.g. a requires-expr or lambda-expr inside a default template argument; we expect 'extra' to be a full set @@ -14874,6 +14990,8 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) parms = DECL_CHAIN (parms); parms = tsubst (parms, args, complain, t); + if (parms == error_mark_node) + return error_mark_node; for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) DECL_CONTEXT (parm) = r; if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) @@ -15446,6 +15564,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, /* We're dealing with a normal parameter. */ type = tsubst (TREE_TYPE (t), args, complain, in_decl); + if (type == error_mark_node && !(complain & tf_error)) + RETURN (error_mark_node); + type = type_decays_to (type); TREE_TYPE (r) = type; cp_apply_type_quals_to_decl (cp_type_quals (type), r); @@ -15483,8 +15604,13 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, /* If cp_unevaluated_operand is set, we're just looking for a single dummy parameter, so don't keep going. */ if (DECL_CHAIN (t) && !cp_unevaluated_operand) - DECL_CHAIN (r) = tsubst (DECL_CHAIN (t), args, - complain, DECL_CHAIN (t)); + { + tree chain = tsubst (DECL_CHAIN (t), args, + complain, DECL_CHAIN (t)); + if (chain == error_mark_node) + RETURN (error_mark_node); + DECL_CHAIN (r) = chain; + } /* FIRST_R contains the start of the chain we've built. */ r = first_r; @@ -15880,7 +16006,10 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, = remove_attribute ("visibility", DECL_ATTRIBUTES (r)); } determine_visibility (r); - if ((!local_p || TREE_STATIC (t)) && DECL_SECTION_NAME (t)) + if ((!local_p || TREE_STATIC (t)) + && !(flag_openmp && DECL_LANG_SPECIFIC (t) + && DECL_OMP_DECLARE_MAPPER_P (t)) + && DECL_SECTION_NAME (t)) set_decl_section_name (r, t); } @@ -15932,6 +16061,13 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, SET_TYPE_STRUCTURAL_EQUALITY (TREE_TYPE (r)); } + if (flag_openmp + && VAR_P (t) + && DECL_LANG_SPECIFIC (t) + && DECL_OMP_DECLARE_MAPPER_P (t) + && strchr (IDENTIFIER_POINTER (DECL_NAME (t)), '~') == NULL) + DECL_NAME (r) = omp_mapper_id (DECL_NAME (t), TREE_TYPE (r)); + layout_decl (r, 0); } break; @@ -17114,13 +17250,14 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) return error_mark_node; } - /* FIXME: TYPENAME_IS_CLASS_P conflates 'class' vs 'struct' vs 'union' - tags. TYPENAME_TYPE should probably remember the exact tag that - was written. */ + /* FIXME: TYPENAME_IS_CLASS_P conflates 'class' vs 'struct' tags. + TYPENAME_TYPE should probably remember the exact tag that + was written for -Wmismatched-tags. */ enum tag_types tag_type - = TYPENAME_IS_CLASS_P (t) ? class_type - : TYPENAME_IS_ENUM_P (t) ? enum_type - : typename_type; + = (TYPENAME_IS_CLASS_P (t) ? class_type + : TYPENAME_IS_UNION_P (t) ? union_type + : TYPENAME_IS_ENUM_P (t) ? enum_type + : typename_type); tsubst_flags_t tcomplain = complain | tf_keep_type_decl; tcomplain |= tst_ok_flag | qualifying_scope_flag; f = make_typename_type (ctx, f, tag_type, tcomplain); @@ -17142,10 +17279,18 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) else return error_mark_node; } - else if (TYPENAME_IS_CLASS_P (t) && !CLASS_TYPE_P (f)) + else if (TYPENAME_IS_CLASS_P (t) && !NON_UNION_CLASS_TYPE_P (f)) + { + if (complain & tf_error) + error ("%qT resolves to %qT, which is not a non-union " + "class type", t, f); + else + return error_mark_node; + } + else if (TYPENAME_IS_UNION_P (t) && !UNION_TYPE_P (f)) { if (complain & tf_error) - error ("%qT resolves to %qT, which is not a class type", + error ("%qT resolves to %qT, which is not a union type", t, f); else return error_mark_node; @@ -17158,18 +17303,24 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl) case UNBOUND_CLASS_TEMPLATE: { - ++processing_template_decl; - tree ctx = tsubst_entering_scope (TYPE_CONTEXT (t), args, - complain, in_decl); - --processing_template_decl; tree name = TYPE_IDENTIFIER (t); + if (name == error_mark_node) + return error_mark_node; + tree parm_list = DECL_TEMPLATE_PARMS (TYPE_NAME (t)); + parm_list = tsubst_template_parms (parm_list, args, complain); + if (parm_list == error_mark_node) + return error_mark_node; - if (ctx == error_mark_node || name == error_mark_node) + if (parm_list && TMPL_PARMS_DEPTH (parm_list) > 1) + ++processing_template_decl; + tree ctx = tsubst_entering_scope (TYPE_CONTEXT (t), args, + complain, in_decl); + if (parm_list && TMPL_PARMS_DEPTH (parm_list) > 1) + --processing_template_decl; + if (ctx == error_mark_node) return error_mark_node; - if (parm_list) - parm_list = tsubst_template_parms (parm_list, args, complain); return make_unbound_class_template (ctx, name, parm_list, complain); } @@ -17443,10 +17594,18 @@ tsubst_baselink (tree baselink, tree object_type, if (!baselink) { - if ((complain & tf_error) - && constructor_name_p (name, qualifying_scope)) - error ("cannot call constructor %<%T::%D%> directly", - qualifying_scope, name); + if (complain & tf_error) + { + if (constructor_name_p (name, qualifying_scope)) + error ("cannot call constructor %<%T::%D%> directly", + qualifying_scope, name); + else + /* Lookup succeeded at parse time, but failed during + instantiation; must be because we're trying to refer to it + while forming its declaration (c++/120204). */ + error ("declaration of %<%T::%D%> depends on itself", + qualifying_scope, name); + } return error_mark_node; } @@ -17741,13 +17900,37 @@ maybe_dependent_member_ref (tree t, tree args, tsubst_flags_t complain, if (TYPE_P (t)) { + bool stripped = false; if (typedef_variant_p (t)) - t = strip_typedefs (t); - tree decl = TYPE_NAME (t); + { + /* Since this transformation may undesirably turn a deduced context + into a non-deduced one, we'd rather strip typedefs than perform + the transformation. */ + tree u = strip_typedefs (t); + if (u != t) + { + stripped = true; + t = u; + } + } + decl = TYPE_NAME (t); if (decl) decl = maybe_dependent_member_ref (decl, args, complain, in_decl); if (!decl) - return NULL_TREE; + { + if (stripped) + /* The original type was an alias from the current instantiation + which we stripped to something outside it. At this point we + need to commit to using the stripped type rather than deferring + to the caller (which would use the original type), to ensure + eligible bits of the stripped type get transformed. */ + return tsubst (t, args, complain, in_decl); + else + /* The original type wasn't a typedef, and we decided it doesn't + need rewriting, so just let the caller (tsubst) substitute it + normally. */ + return NULL_TREE; + } return cp_build_qualified_type (TREE_TYPE (decl), cp_type_quals (t), complain); } @@ -17765,7 +17948,8 @@ maybe_dependent_member_ref (tree t, tree args, tsubst_flags_t complain, if (TREE_CODE (t) == TYPE_DECL) { - if (TREE_CODE (TREE_TYPE (t)) == TYPENAME_TYPE + if (!is_typedef_decl (t) + && TREE_CODE (TREE_TYPE (t)) == TYPENAME_TYPE && TYPE_NAME (TREE_TYPE (t)) == t) /* The TYPE_DECL for a typename has DECL_CONTEXT of the typename scope, but it doesn't need to be rewritten again. */ @@ -18165,8 +18349,10 @@ tsubst_omp_clauses (tree clauses, enum c_omp_region_type ort, } new_clauses = nreverse (new_clauses); - if (ort != C_ORT_OMP_DECLARE_SIMD) + if (ort != C_ORT_OMP_DECLARE_SIMD && ort != C_ORT_OMP_DECLARE_MAPPER) { + if (ort == C_ORT_OMP_TARGET) + new_clauses = c_omp_instantiate_mappers (new_clauses); new_clauses = finish_omp_clauses (new_clauses, ort); if (linear_no_step) for (nc = new_clauses; nc; nc = OMP_CLAUSE_CHAIN (nc)) @@ -18220,7 +18406,9 @@ tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain, } } - switch (omp_ts_map[OMP_TS_CODE (sel)].tp_type) + enum omp_tp_type property_kind + = omp_ts_map[OMP_TS_CODE (sel)].tp_type; + switch (property_kind) { case OMP_TRAIT_PROPERTY_DEV_NUM_EXPR: case OMP_TRAIT_PROPERTY_BOOL_EXPR: @@ -18228,12 +18416,26 @@ tsubst_omp_context_selector (tree ctx, tree args, tsubst_flags_t complain, args, complain, in_decl); t = fold_non_dependent_expr (t); if (!value_dependent_expression_p (t) - && !type_dependent_expression_p (t) - && !INTEGRAL_TYPE_P (TREE_TYPE (t))) - error_at (cp_expr_loc_or_input_loc (t), - "property must be integer expression"); - else - properties = make_trait_property (NULL_TREE, t, NULL_TREE); + && !type_dependent_expression_p (t)) + { + if (property_kind == OMP_TRAIT_PROPERTY_BOOL_EXPR) + t = maybe_convert_cond (t); + else + { + t = convert_from_reference (t); + if (!INTEGRAL_TYPE_P (TREE_TYPE (t))) + { + error_at (cp_expr_loc_or_input_loc (t), + "property must be integer expression"); + t = error_mark_node; + } + } + } + if (t != error_mark_node + && !processing_template_decl + && TREE_CODE (t) != CLEANUP_POINT_EXPR) + t = fold_build_cleanup_point_expr (TREE_TYPE (t), t); + properties = make_trait_property (NULL_TREE, t, NULL_TREE); break; case OMP_TRAIT_PROPERTY_CLAUSE_LIST: if (OMP_TS_CODE (sel) == OMP_TRAIT_CONSTRUCT_SIMD) @@ -19886,6 +20088,22 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) break; } + case OMP_DECLARE_MAPPER: + { + t = copy_node (t); + + tree decl = OMP_DECLARE_MAPPER_DECL (t); + decl = tsubst (decl, args, complain, in_decl); + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl); + tree clauses = OMP_DECLARE_MAPPER_CLAUSES (t); + clauses = tsubst_omp_clauses (clauses, C_ORT_OMP_DECLARE_MAPPER, args, + complain, in_decl); + TREE_TYPE (t) = type; + OMP_DECLARE_MAPPER_DECL (t) = decl; + OMP_DECLARE_MAPPER_CLAUSES (t) = clauses; + RETURN (t); + } + case TRANSACTION_EXPR: { int flags = 0; @@ -19929,7 +20147,14 @@ tsubst_stmt (tree t, tree args, tsubst_flags_t complain, tree in_decl) { tree op0 = RECUR (TREE_OPERAND (t, 0)); tree cond = RECUR (MUST_NOT_THROW_COND (t)); - RETURN (build_must_not_throw_expr (op0, cond)); + stmt = build_must_not_throw_expr (op0, cond); + if (stmt && TREE_CODE (stmt) == MUST_NOT_THROW_EXPR) + { + MUST_NOT_THROW_NOEXCEPT_P (stmt) = MUST_NOT_THROW_NOEXCEPT_P (t); + MUST_NOT_THROW_THROW_P (stmt) = MUST_NOT_THROW_THROW_P (t); + MUST_NOT_THROW_CATCH_P (stmt) = MUST_NOT_THROW_CATCH_P (t); + } + RETURN (stmt); } case EXPR_PACK_EXPANSION: @@ -20945,6 +21170,23 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) RETURN (build_omp_array_section (EXPR_LOCATION (t), op0, op1, op2)); } + case OMP_DECLARE_MAPPER: + { + t = copy_node (t); + + tree decl = OMP_DECLARE_MAPPER_DECL (t); + DECL_OMP_DECLARE_MAPPER_P (decl) = 1; + decl = tsubst (decl, args, complain, in_decl); + tree type = tsubst (TREE_TYPE (t), args, complain, in_decl); + tree clauses = OMP_DECLARE_MAPPER_CLAUSES (t); + clauses = tsubst_omp_clauses (clauses, C_ORT_OMP_DECLARE_MAPPER, args, + complain, in_decl); + TREE_TYPE (t) = type; + OMP_DECLARE_MAPPER_DECL (t) = decl; + OMP_DECLARE_MAPPER_CLAUSES (t) = clauses; + RETURN (t); + } + case SIZEOF_EXPR: if (PACK_EXPANSION_P (TREE_OPERAND (t, 0)) || ARGUMENT_PACK_P (TREE_OPERAND (t, 0))) @@ -21276,13 +21518,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl) /* Avoid error about taking the address of a constructor. */ function = TREE_OPERAND (function, 0); - tsubst_flags_t subcomplain = complain; - if (koenig_p && TREE_CODE (function) == FUNCTION_DECL) - /* When KOENIG_P, we don't want to mark_used the callee before - augmenting the overload set via ADL, so during this initial - substitution we disable mark_used by setting tf_conv (68942). */ - subcomplain |= tf_conv; - function = tsubst_expr (function, args, subcomplain, in_decl); + function = tsubst_expr (function, args, complain, in_decl); if (BASELINK_P (function)) qualified_p = true; @@ -22650,7 +22886,7 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain) FUNCTION_DECL which is the desired context for access checking is not built yet. We solve this chicken-and-egg problem by deferring all checks until we have the FUNCTION_DECL. */ - push_deferring_access_checks (dk_deferred); + deferring_access_check_sentinel dacs (dk_deferred); /* Instantiation of the function happens in the context of the function template, not the context of the overload resolution we're doing. */ @@ -22708,10 +22944,13 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain) pop_from_top_level (); if (fndecl == error_mark_node) - { - pop_deferring_access_checks (); - return error_mark_node; - } + return error_mark_node; + + /* Substituting the type might have recursively instantiated this + same alias (c++/117530). */ + if (DECL_ALIAS_TEMPLATE_P (gen_tmpl) + && (spec = retrieve_specialization (gen_tmpl, targ_ptr, hash))) + return spec; /* The DECL_TI_TEMPLATE should always be the immediate parent template, not the most general template. */ @@ -22746,7 +22985,6 @@ instantiate_template (tree tmpl, tree orig_args, tsubst_flags_t complain) access_ok = false; pop_access_scope (fndecl); } - pop_deferring_access_checks (); /* If we've just instantiated the main entry point for a function, instantiate all the alternate entry points as well. We do this @@ -23199,7 +23437,7 @@ fn_type_unification (tree fn, conversions that we know are not going to induce template instantiation (PR99599). */ if (strict == DEDUCE_CALL - && incomplete + && incomplete && flag_concepts && check_non_deducible_conversions (parms, args, nargs, fn, strict, flags, convs, explain_p, /*noninst_only_p=*/true)) @@ -23446,9 +23684,13 @@ maybe_adjust_types_for_deduction (tree tparms, return result; } -/* Return true if computing a conversion from FROM to TO might induce template - instantiation. Conversely, if this predicate returns false then computing - the conversion definitely won't induce template instantiation. */ +/* Return true if computing a conversion from FROM to TO might consider + user-defined conversions, which could lead to arbitrary template + instantiations (e.g. g++.dg/cpp2a/concepts-nondep1.C). If this predicate + returns false then computing the conversion definitely won't try UDCs. + + Note that this restriction parallels LOOKUP_DEFAULTED for CWG1092, but in + this case we want the early filter to pass instead of fail. */ static bool conversion_may_instantiate_p (tree to, tree from) @@ -23456,45 +23698,23 @@ conversion_may_instantiate_p (tree to, tree from) to = non_reference (to); from = non_reference (from); - bool ptr_conv_p = false; - if (TYPE_PTR_P (to) - && TYPE_PTR_P (from)) - { - to = TREE_TYPE (to); - from = TREE_TYPE (from); - ptr_conv_p = true; - } - - /* If one of the types is a not-yet-instantiated class template - specialization, then computing the conversion might instantiate - it in order to inspect bases, conversion functions and/or - converting constructors. */ - if ((CLASS_TYPE_P (to) - && !COMPLETE_TYPE_P (to) - && CLASSTYPE_TEMPLATE_INSTANTIATION (to)) - || (CLASS_TYPE_P (from) - && !COMPLETE_TYPE_P (from) - && CLASSTYPE_TEMPLATE_INSTANTIATION (from))) - return true; - - /* Converting from one pointer type to another, or between - reference-related types, always yields a standard conversion. */ - if (ptr_conv_p || reference_related_p (to, from)) + /* Converting between reference-related types is a standard conversion. */ + if (reference_related_p (to, from)) return false; /* Converting to a non-aggregate class type will consider its user-declared constructors, which might induce instantiation. */ - if (CLASS_TYPE_P (to) - && CLASSTYPE_NON_AGGREGATE (to)) + if (CLASS_TYPE_P (complete_type (to)) + && type_has_converting_constructor (to)) return true; /* Similarly, converting from a class type will consider its conversion functions. */ - if (CLASS_TYPE_P (from) + if (CLASS_TYPE_P (complete_type (from)) && TYPE_HAS_CONVERSION (from)) return true; - /* Otherwise, computing this conversion definitely won't induce + /* Otherwise, computing this conversion won't risk arbitrary template instantiation. */ return false; } @@ -24400,7 +24620,8 @@ resolve_nondeduced_context (tree orig_expr, tsubst_flags_t complain) } if (good == 1) { - mark_used (goodfn); + if (!mark_used (goodfn, complain) && !(complain & tf_error)) + return error_mark_node; expr = goodfn; if (baselink) expr = build_baselink (BASELINK_BINFO (baselink), @@ -25740,10 +25961,10 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict, INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (parm)), INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (t)), UNIFY_ALLOW_NONE, explain_p); - else - return unify_success (explain_p); + gcc_checking_assert (t == arg); } - else if (!same_type_ignoring_top_level_qualifiers_p (parm, arg)) + + if (!same_type_ignoring_top_level_qualifiers_p (parm, arg)) return unify_type_mismatch (explain_p, parm, arg); return unify_success (explain_p); @@ -27332,6 +27553,10 @@ tree template_for_substitution (tree decl) { tree tmpl = DECL_TI_TEMPLATE (decl); + if (VAR_P (decl)) + if (tree partial = most_specialized_partial_spec (decl, tf_none)) + if (partial != error_mark_node) + tmpl = TI_TEMPLATE (partial); /* Set TMPL to the template whose DECL_TEMPLATE_RESULT is the pattern for the instantiation. This is not always the most general @@ -27712,6 +27937,11 @@ instantiate_body (tree pattern, tree args, tree d, bool nested_p) if (DECL_OMP_DECLARE_REDUCTION_P (code_pattern)) cp_check_omp_declare_reduction (d); + + if (int errs = errorcount + sorrycount) + if (errs > current_tinst_level->errors) + if (function *f = DECL_STRUCT_FUNCTION (d)) + f->language->erroneous = true; } /* We're not deferring instantiation any more. */ @@ -27905,7 +28135,9 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p) || (external_p && VAR_P (d)) /* Handle here a deleted function too, avoid generating its body (c++/61080). */ - || deleted_p) + || deleted_p + /* We need the initializer for an OpenMP declare mapper. */ + || (VAR_P (d) && DECL_LANG_SPECIFIC (d) && DECL_OMP_DECLARE_MAPPER_P (d))) { /* The definition of the static data member is now required so we must substitute the initializer. */ @@ -28018,12 +28250,16 @@ instantiate_pending_templates (int retries) reconsider = 0; while (*t) { - tree instantiation = reopen_tinst_level ((*t)->tinst); - bool complete = false; + struct tinst_level *tinst = (*t)->tinst; + bool complete = tinst_complete_p (tinst); - if (TYPE_P (instantiation)) + if (!complete) { - if (!COMPLETE_TYPE_P (instantiation)) + tree instantiation = reopen_tinst_level (tinst); + + if (limit_bad_template_recursion (instantiation)) + /* Do nothing. */; + else if (TYPE_P (instantiation)) { instantiate_class_template (instantiation); if (CLASSTYPE_TEMPLATE_INSTANTIATION (instantiation)) @@ -28040,13 +28276,7 @@ instantiate_pending_templates (int retries) if (COMPLETE_TYPE_P (instantiation)) reconsider = 1; } - - complete = COMPLETE_TYPE_P (instantiation); - } - else - { - if (!DECL_TEMPLATE_SPECIALIZATION (instantiation) - && !DECL_TEMPLATE_INSTANTIATED (instantiation)) + else { instantiation = instantiate_decl (instantiation, @@ -28056,8 +28286,10 @@ instantiate_pending_templates (int retries) reconsider = 1; } - complete = (DECL_TEMPLATE_SPECIALIZATION (instantiation) - || DECL_TEMPLATE_INSTANTIATED (instantiation)); + complete = tinst_complete_p (tinst); + + tinst_depth = 0; + set_refcount_ptr (current_tinst_level); } if (complete) @@ -28074,8 +28306,6 @@ instantiate_pending_templates (int retries) last = *t; t = &(*t)->next; } - tinst_depth = 0; - set_refcount_ptr (current_tinst_level); } last_pending_template = last; } @@ -30898,9 +31128,8 @@ alias_ctad_tweaks (tree tmpl, tree uguides) tree aguides = NULL_TREE; tree atparms = INNERMOST_TEMPLATE_PARMS (fullatparms); unsigned natparms = TREE_VEC_LENGTH (atparms); - for (ovl_iterator iter (uguides); iter; ++iter) + for (tree f : lkp_range (uguides)) { - tree f = *iter; tree in_decl = f; location_t loc = DECL_SOURCE_LOCATION (f); tree ret = TREE_TYPE (TREE_TYPE (f)); diff --git a/gcc/cp/rtti.cc b/gcc/cp/rtti.cc index 3539962..c06a18b 100644 --- a/gcc/cp/rtti.cc +++ b/gcc/cp/rtti.cc @@ -468,6 +468,7 @@ get_tinfo_decl_direct (tree type, tree name, int pseudo_ix) DECL_IGNORED_P (d) = 1; TREE_READONLY (d) = 1; TREE_STATIC (d) = 1; + TREE_ADDRESSABLE (d) = 1; /* Tell equal_address_to that different tinfo decls never overlap. */ if (vec_safe_is_empty (unemitted_tinfo_decls)) @@ -1318,18 +1319,9 @@ get_pseudo_ti_index (tree type) static tinfo_s * get_tinfo_desc (unsigned ix) { - unsigned len = tinfo_descs->length (); - - if (len <= ix) - { - /* too short, extend. */ - len = ix + 1 - len; - vec_safe_reserve (tinfo_descs, len); - tinfo_s elt; - elt.type = elt.vtable = elt.name = NULL_TREE; - while (len--) - tinfo_descs->quick_push (elt); - } + if (tinfo_descs->length () <= ix) + /* too short, extend. */ + vec_safe_grow_cleared (tinfo_descs, ix + 1); tinfo_s *res = &(*tinfo_descs)[ix]; diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 7d8beb8..b57547c 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -45,13 +45,13 @@ along with GCC; see the file COPYING3. If not see #include "gomp-constants.h" #include "predict.h" #include "memmodel.h" +#include "gimplify.h" /* There routines provide a modular interface to perform many parsing operations. They may therefore be used during actual parsing, or during template instantiation, which may be regarded as a degenerate form of parsing. */ -static tree maybe_convert_cond (tree); static tree finalize_nrv_r (tree *, int *, void *); /* Used for OpenMP non-static data member privatization. */ @@ -1116,7 +1116,7 @@ annotate_saver::restore (tree new_inner) statement. Convert it to a boolean value, if appropriate. In addition, verify sequence points if -Wsequence-point is enabled. */ -static tree +tree maybe_convert_cond (tree cond) { /* Empty conditions remain empty. */ @@ -1180,10 +1180,13 @@ finish_expr_stmt (tree expr) expr = error_mark_node; /* Simplification of inner statement expressions, compound exprs, - etc can result in us already having an EXPR_STMT. */ + etc can result in us already having an EXPR_STMT or other statement + tree. Don't wrap them in EXPR_STMT. */ if (TREE_CODE (expr) != CLEANUP_POINT_EXPR) { - if (TREE_CODE (expr) != EXPR_STMT) + if (TREE_CODE (expr) != EXPR_STMT + && !STATEMENT_CLASS_P (expr) + && TREE_CODE (expr) != STATEMENT_LIST) expr = build_stmt (loc, EXPR_STMT, expr); expr = maybe_cleanup_point_expr_void (expr); } @@ -2767,7 +2770,7 @@ finish_non_static_data_member (tree decl, tree object, tree qualifying_scope, else if (PACK_EXPANSION_P (type)) /* Don't bother trying to represent this. */ type = NULL_TREE; - else if (WILDCARD_TYPE_P (TREE_TYPE (object))) + else if (!TREE_TYPE (object) || WILDCARD_TYPE_P (TREE_TYPE (object))) /* We don't know what the eventual quals will be, so punt until instantiation time. @@ -3082,6 +3085,7 @@ finish_stmt_expr_expr (tree expr, tree stmt_expr) } else if (processing_template_decl) { + /* Not finish_expr_stmt because we don't want convert_to_void. */ expr = build_stmt (input_location, EXPR_STMT, expr); expr = add_stmt (expr); /* Mark the last statement so that we can recognize it as such at @@ -3321,6 +3325,14 @@ finish_call_expr (tree fn, vec<tree, va_gc> **args, bool disallow_virtual, if (type_dependent_expression_p (fn) || any_type_dependent_arguments_p (*args)) { + if (koenig_p + && TREE_CODE (orig_fn) == FUNCTION_DECL + && !fndecl_built_in_p (orig_fn)) + /* For an ADL-enabled call where unqualified lookup found a + single non-template function, wrap it in an OVERLOAD so that + later substitution doesn't overeagerly mark the function as + used. */ + orig_fn = ovl_make (orig_fn, NULL_TREE); result = build_min_nt_call_vec (orig_fn, *args); SET_EXPR_LOCATION (result, cp_expr_loc_or_input_loc (fn)); KOENIG_LOOKUP_P (result) = koenig_p; @@ -3593,16 +3605,13 @@ finish_this_expr (void) { tree result = NULL_TREE; - if (current_class_ptr) - { - tree type = TREE_TYPE (current_class_ref); - - /* In a lambda expression, 'this' refers to the captured 'this'. */ - if (LAMBDA_TYPE_P (type)) - result = lambda_expr_this_capture (CLASSTYPE_LAMBDA_EXPR (type), true); - else - result = current_class_ptr; - } + if (current_class_ref && !LAMBDA_TYPE_P (TREE_TYPE (current_class_ref))) + result = current_class_ptr; + else if (current_class_type && LAMBDA_TYPE_P (current_class_type)) + result = (lambda_expr_this_capture + (CLASSTYPE_LAMBDA_EXPR (current_class_type), /*add*/true)); + else + gcc_checking_assert (!current_class_ptr); if (result) /* The keyword 'this' is a prvalue expression. */ @@ -4755,6 +4764,7 @@ finish_id_expression_1 (tree id_expression, body, except inside an unevaluated context (i.e. decltype). */ if (TREE_CODE (decl) == PARM_DECL && DECL_CONTEXT (decl) == NULL_TREE + && !CONSTRAINT_VAR_P (decl) && !cp_unevaluated_operand && !processing_contract_condition && !processing_omp_trait_property_expr) @@ -5087,22 +5097,32 @@ static tree finish_type_pack_element (tree idx, tree types, tsubst_flags_t complain) { idx = maybe_constant_value (idx, NULL_TREE, mce_true); - if (TREE_CODE (idx) != INTEGER_CST || !INTEGRAL_TYPE_P (TREE_TYPE (idx))) + if (!INTEGRAL_TYPE_P (TREE_TYPE (idx))) { if (complain & tf_error) - error ("pack index is not an integral constant"); + error ("pack index has non-integral type %qT", TREE_TYPE (idx)); + return error_mark_node; + } + if (TREE_CODE (idx) != INTEGER_CST) + { + if (complain & tf_error) + { + error ("pack index is not an integral constant"); + cxx_constant_value (idx); + } return error_mark_node; } if (tree_int_cst_sgn (idx) < 0) { if (complain & tf_error) - error ("pack index is negative"); + error ("pack index %qE is negative", idx); return error_mark_node; } if (wi::to_widest (idx) >= TREE_VEC_LENGTH (types)) { if (complain & tf_error) - error ("pack index is out of range"); + error ("pack index %qE is out of range for pack of length %qd", + idx, TREE_VEC_LENGTH (types)); return error_mark_node; } return TREE_VEC_ELT (types, tree_to_shwi (idx)); @@ -6722,6 +6742,97 @@ omp_reduction_lookup (location_t loc, tree id, tree type, tree *baselinkp, return id; } +/* Return identifier to look up for omp declare mapper. */ + +tree +omp_mapper_id (tree mapper_id, tree type) +{ + const char *p = NULL; + const char *m = NULL; + + if (mapper_id == NULL_TREE) + p = ""; + else if (TREE_CODE (mapper_id) == IDENTIFIER_NODE) + p = IDENTIFIER_POINTER (mapper_id); + else + return error_mark_node; + + if (type != NULL_TREE) + m = mangle_type_string (TYPE_MAIN_VARIANT (type)); + + const char prefix[] = "omp declare mapper "; + size_t lenp = sizeof (prefix); + if (strncmp (p, prefix, lenp - 1) == 0) + lenp = 1; + size_t len = strlen (p); + size_t lenm = m ? strlen (m) + 1 : 0; + char *name = XALLOCAVEC (char, lenp + len + lenm); + memcpy (name, prefix, lenp - 1); + memcpy (name + lenp - 1, p, len + 1); + if (m) + { + name[lenp + len - 1] = '~'; + memcpy (name + lenp + len, m, lenm); + } + return get_identifier (name); +} + +tree +cxx_omp_mapper_lookup (tree id, tree type) +{ + if (!RECORD_OR_UNION_TYPE_P (type)) + return NULL_TREE; + id = omp_mapper_id (id, type); + return lookup_name (id); +} + +tree +cxx_omp_extract_mapper_directive (tree vardecl) +{ + gcc_assert (TREE_CODE (vardecl) == VAR_DECL); + + /* Instantiate the decl if we haven't already. */ + mark_used (vardecl); + tree body = DECL_INITIAL (vardecl); + + if (TREE_CODE (body) == STATEMENT_LIST) + { + tree_stmt_iterator tsi = tsi_start (body); + gcc_assert (TREE_CODE (tsi_stmt (tsi)) == DECL_EXPR); + tsi_next (&tsi); + body = tsi_stmt (tsi); + } + + gcc_assert (TREE_CODE (body) == OMP_DECLARE_MAPPER); + + return body; +} + +/* For now we can handle singleton OMP_ARRAY_SECTIONs with custom mappers, but + nothing more complicated. */ + +tree +cxx_omp_map_array_section (location_t loc, tree t) +{ + tree low = TREE_OPERAND (t, 1); + tree len = TREE_OPERAND (t, 2); + + if (len && integer_onep (len)) + { + t = TREE_OPERAND (t, 0); + + if (!low) + low = integer_zero_node; + + if (TREE_CODE (TREE_TYPE (t)) == REFERENCE_TYPE) + t = convert_from_reference (t); + + t = build_array_ref (loc, t, low); + } + + return t; +} + /* Helper function for cp_parser_omp_declare_reduction_exprs and tsubst_omp_udr. Remove CLEANUP_STMT for data (omp_priv variable). @@ -7203,6 +7314,33 @@ finish_omp_reduction_clause (tree c, bool *need_default_ctor, bool *need_dtor) return false; } +/* Check an instance of an "omp declare mapper" function. */ + +bool +cp_check_omp_declare_mapper (tree udm) +{ + tree type = TREE_TYPE (udm); + location_t loc = DECL_SOURCE_LOCATION (udm); + + if (type == error_mark_node) + return false; + + if (!processing_template_decl && !RECORD_OR_UNION_TYPE_P (type)) + { + error_at (loc, "%qT is not a struct, union or class type in " + "%<#pragma omp declare mapper%>", type); + return false; + } + if (!processing_template_decl && CLASSTYPE_VBASECLASSES (type)) + { + error_at (loc, "%qT must not be a virtual base class in " + "%<#pragma omp declare mapper%>", type); + return false; + } + + return true; +} + /* Called from finish_struct_1. linear(this) or linear(this:step) clauses might not be finalized yet because the class has been incomplete when parsing #pragma omp declare simd methods. Fix those up now. */ @@ -8847,6 +8985,12 @@ finish_omp_clauses (tree clauses, enum c_omp_region_type ort) case OMP_CLAUSE_MAP: if (OMP_CLAUSE_MAP_IMPLICIT (c) && !implicit_moved) goto move_implicit; + if (OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_PUSH_MAPPER_NAME + || OMP_CLAUSE_MAP_KIND (c) == GOMP_MAP_POP_MAPPER_NAME) + { + remove = true; + break; + } /* FALLTHRU */ case OMP_CLAUSE_TO: case OMP_CLAUSE_FROM: @@ -10466,6 +10610,8 @@ struct omp_target_walk_data /* Local variables declared inside a BIND_EXPR, used to filter out such variables when recording lambda_objects_accessed. */ hash_set<tree> local_decls; + + omp_mapper_list<tree> *mappers; }; /* Helper function of finish_omp_target_clauses, called via @@ -10479,6 +10625,7 @@ finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr) struct omp_target_walk_data *data = (struct omp_target_walk_data *) ptr; tree current_object = data->current_object; tree current_closure = data->current_closure; + omp_mapper_list<tree> *mlist = data->mappers; /* References inside of these expression codes shouldn't incur any form of mapping, so return early. */ @@ -10492,6 +10639,27 @@ finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr) if (TREE_CODE (t) == OMP_CLAUSE) return NULL_TREE; + if (!processing_template_decl) + { + tree aggr_type = NULL_TREE; + + if (TREE_CODE (t) == COMPONENT_REF + && RECORD_OR_UNION_TYPE_P (TREE_TYPE (TREE_OPERAND (t, 0)))) + aggr_type = TREE_TYPE (TREE_OPERAND (t, 0)); + else if ((TREE_CODE (t) == VAR_DECL + || TREE_CODE (t) == PARM_DECL + || TREE_CODE (t) == RESULT_DECL) + && RECORD_OR_UNION_TYPE_P (TREE_TYPE (t))) + aggr_type = TREE_TYPE (t); + + if (aggr_type) + { + tree mapper_fn = cxx_omp_mapper_lookup (NULL_TREE, aggr_type); + if (mapper_fn) + mlist->add_mapper (NULL_TREE, aggr_type, mapper_fn); + } + } + if (current_object) { tree this_expr = TREE_OPERAND (current_object, 0); @@ -10542,10 +10710,10 @@ finish_omp_target_clauses_r (tree *tp, int *walk_subtrees, void *ptr) if (TREE_CODE (t) == BIND_EXPR) { - tree block = BIND_EXPR_BLOCK (t); - for (tree var = BLOCK_VARS (block); var; var = DECL_CHAIN (var)) - if (!data->local_decls.contains (var)) - data->local_decls.add (var); + if (tree block = BIND_EXPR_BLOCK (t)) + for (tree var = BLOCK_VARS (block); var; var = DECL_CHAIN (var)) + if (!data->local_decls.contains (var)) + data->local_decls.add (var); return NULL_TREE; } @@ -10594,10 +10762,48 @@ finish_omp_target_clauses (location_t loc, tree body, tree *clauses_ptr) else data.current_closure = NULL_TREE; - cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r, &data); - auto_vec<tree, 16> new_clauses; + if (!processing_template_decl) + { + hash_set<omp_name_type<tree> > seen_types; + auto_vec<tree> mapper_fns; + omp_mapper_list<tree> mlist (&seen_types, &mapper_fns); + data.mappers = &mlist; + + cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r, + &data); + + unsigned int i; + tree mapper_fn; + FOR_EACH_VEC_ELT (mapper_fns, i, mapper_fn) + c_omp_find_nested_mappers (&mlist, mapper_fn); + + FOR_EACH_VEC_ELT (mapper_fns, i, mapper_fn) + { + tree mapper = cxx_omp_extract_mapper_directive (mapper_fn); + if (mapper == error_mark_node) + continue; + tree mapper_name = OMP_DECLARE_MAPPER_ID (mapper); + tree decl = OMP_DECLARE_MAPPER_DECL (mapper); + if (BASELINK_P (mapper_fn)) + mapper_fn = BASELINK_FUNCTIONS (mapper_fn); + + tree c = build_omp_clause (loc, OMP_CLAUSE__MAPPER_BINDING_); + OMP_CLAUSE__MAPPER_BINDING__ID (c) = mapper_name; + OMP_CLAUSE__MAPPER_BINDING__DECL (c) = decl; + OMP_CLAUSE__MAPPER_BINDING__MAPPER (c) = mapper_fn; + + new_clauses.safe_push (c); + } + } + else + { + data.mappers = NULL; + cp_walk_tree_without_duplicates (&body, finish_omp_target_clauses_r, + &data); + } + tree omp_target_this_expr = NULL_TREE; tree *explicit_this_deref_map = NULL; if (data.this_expr_accessed) @@ -13212,6 +13418,15 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_HAS_TRIVIAL_DESTRUCTOR: type1 = strip_array_types (type1); + if (CLASS_TYPE_P (type1) && type_build_dtor_call (type1)) + { + deferring_access_check_sentinel dacs (dk_no_check); + cp_unevaluated un; + tree fn = get_dtor (type1, tf_none); + if (!fn && !seen_error ()) + warning (0, "checking %qs for type %qT with a destructor that " + "cannot be called", "__has_trivial_destructor", type1); + } return (trivial_type_p (type1) || type_code1 == REFERENCE_TYPE || (CLASS_TYPE_P (type1) && TYPE_HAS_TRIVIAL_DESTRUCTOR (type1))); @@ -13267,6 +13482,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_CONVERTIBLE: return is_convertible (type1, type2); + case CPTK_IS_DESTRUCTIBLE: + return is_xible (BIT_NOT_EXPR, type1, NULL_TREE); + case CPTK_IS_EMPTY: return NON_UNION_CLASS_TYPE_P (type1) && CLASSTYPE_EMPTY_P (type1); @@ -13306,6 +13524,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_NOTHROW_CONVERTIBLE: return is_nothrow_convertible (type1, type2); + case CPTK_IS_NOTHROW_DESTRUCTIBLE: + return is_nothrow_xible (BIT_NOT_EXPR, type1, NULL_TREE); + case CPTK_IS_NOTHROW_INVOCABLE: return expr_noexcept_p (build_invoke (type1, type2, tf_none), tf_none); @@ -13348,6 +13569,9 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_TRIVIALLY_COPYABLE: return trivially_copyable_p (type1); + case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: + return is_trivially_xible (BIT_NOT_EXPR, type1, NULL_TREE); + case CPTK_IS_UNBOUNDED_ARRAY: return array_of_unknown_bound_p (type1); @@ -13371,8 +13595,10 @@ trait_expr_value (cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_DEDUCIBLE: return type_targs_deducible_from (type1, type2); - /* __array_rank is handled in finish_trait_expr. */ + /* __array_rank and __builtin_type_order are handled in + finish_trait_expr. */ case CPTK_RANK: + case CPTK_TYPE_ORDER: gcc_unreachable (); #define DEFTRAIT_TYPE(CODE, NAME, ARITY) \ @@ -13502,6 +13728,12 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) tree trait_expr = make_node (TRAIT_EXPR); if (kind == CPTK_RANK) TREE_TYPE (trait_expr) = size_type_node; + else if (kind == CPTK_TYPE_ORDER) + { + tree val = type_order_value (type1, type1); + if (val != error_mark_node) + TREE_TYPE (trait_expr) = TREE_TYPE (val); + } else TREE_TYPE (trait_expr) = boolean_type_node; TRAIT_EXPR_TYPE1 (trait_expr) = type1; @@ -13520,6 +13752,9 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) case CPTK_HAS_NOTHROW_COPY: case CPTK_HAS_TRIVIAL_COPY: case CPTK_HAS_TRIVIAL_DESTRUCTOR: + case CPTK_IS_DESTRUCTIBLE: + case CPTK_IS_NOTHROW_DESTRUCTIBLE: + case CPTK_IS_TRIVIALLY_DESTRUCTIBLE: if (!check_trait_type (type1)) return error_mark_node; break; @@ -13606,6 +13841,7 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) case CPTK_IS_UNION: case CPTK_IS_VOLATILE: case CPTK_RANK: + case CPTK_TYPE_ORDER: break; case CPTK_IS_LAYOUT_COMPATIBLE: @@ -13645,6 +13881,8 @@ finish_trait_expr (location_t loc, cp_trait_kind kind, tree type1, tree type2) ++rank; val = build_int_cst (size_type_node, rank); } + else if (kind == CPTK_TYPE_ORDER) + val = type_order_value (type1, type2); else val = (trait_expr_value (kind, type1, type2) ? boolean_true_node : boolean_false_node); diff --git a/gcc/cp/std-name-hint.h b/gcc/cp/std-name-hint.h index dde2f9b..c5f8004 100644 --- a/gcc/cp/std-name-hint.h +++ b/gcc/cp/std-name-hint.h @@ -144,979 +144,979 @@ std_name_hint_lookup::find (const char *str, size_t len) static const struct std_name_hint wordlist[] = { -#line 130 "../../src/gcc/cp/std-name-hint.gperf" +#line 130 "std-name-hint.gperf" {"regular", "<concepts>", cxx20}, -#line 292 "../../src/gcc/cp/std-name-hint.gperf" +#line 292 "std-name-hint.gperf" {"reverse_iterator", "<iterator>", cxx98}, -#line 454 "../../src/gcc/cp/std-name-hint.gperf" +#line 454 "std-name-hint.gperf" {"range_error", "<stdexcept>", cxx98}, -#line 408 "../../src/gcc/cp/std-name-hint.gperf" +#line 408 "std-name-hint.gperf" {"set", "<set>", cxx98}, -#line 231 "../../src/gcc/cp/std-name-hint.gperf" +#line 231 "std-name-hint.gperf" {"setbase", "<iomanip>", cxx98}, -#line 325 "../../src/gcc/cp/std-name-hint.gperf" +#line 325 "std-name-hint.gperf" {"reinterpret_pointer_cast", "<memory>", cxx17}, -#line 288 "../../src/gcc/cp/std-name-hint.gperf" +#line 288 "std-name-hint.gperf" {"next", "<iterator>", cxx11}, -#line 171 "../../src/gcc/cp/std-name-hint.gperf" +#line 171 "std-name-hint.gperf" {"format", "<format>", cxx20}, -#line 181 "../../src/gcc/cp/std-name-hint.gperf" +#line 181 "std-name-hint.gperf" {"formatter", "<format>", cxx20}, -#line 196 "../../src/gcc/cp/std-name-hint.gperf" +#line 196 "std-name-hint.gperf" {"basic_filebuf", "<fstream>", cxx98}, -#line 575 "../../src/gcc/cp/std-name-hint.gperf" +#line 575 "std-name-hint.gperf" {"pair", "<utility>", cxx98}, -#line 276 "../../src/gcc/cp/std-name-hint.gperf" +#line 276 "std-name-hint.gperf" {"begin", "<iterator>", cxx11}, -#line 179 "../../src/gcc/cp/std-name-hint.gperf" +#line 179 "std-name-hint.gperf" {"formattable", "<format>", cxx23}, -#line 541 "../../src/gcc/cp/std-name-hint.gperf" +#line 541 "std-name-hint.gperf" {"bad_cast", "<typeinfo>", cxx98}, -#line 233 "../../src/gcc/cp/std-name-hint.gperf" +#line 233 "std-name-hint.gperf" {"setiosflags", "<iomanip>", cxx98}, -#line 393 "../../src/gcc/cp/std-name-hint.gperf" +#line 393 "std-name-hint.gperf" {"print", "<print>", cxx23}, -#line 221 "../../src/gcc/cp/std-name-hint.gperf" +#line 221 "std-name-hint.gperf" {"promise", "<future>", cxx11}, -#line 581 "../../src/gcc/cp/std-name-hint.gperf" +#line 581 "std-name-hint.gperf" {"bad_variant_access", "<variant>", cxx17}, -#line 328 "../../src/gcc/cp/std-name-hint.gperf" +#line 328 "std-name-hint.gperf" {"to_address", "<memory>", cxx20}, -#line 420 "../../src/gcc/cp/std-name-hint.gperf" +#line 420 "std-name-hint.gperf" {"basic_spanbuf", "<spanstream>", cxx23}, -#line 106 "../../src/gcc/cp/std-name-hint.gperf" +#line 106 "std-name-hint.gperf" {"same_as", "<concepts>", cxx20}, -#line 336 "../../src/gcc/cp/std-name-hint.gperf" +#line 336 "std-name-hint.gperf" {"pmr", "<memory_resource>", cxx17}, -#line 180 "../../src/gcc/cp/std-name-hint.gperf" +#line 180 "std-name-hint.gperf" {"formatted_size", "<format>", cxx20}, -#line 275 "../../src/gcc/cp/std-name-hint.gperf" +#line 275 "std-name-hint.gperf" {"back_inserter", "<iterator>", cxx98}, -#line 251 "../../src/gcc/cp/std-name-hint.gperf" +#line 251 "std-name-hint.gperf" {"nouppercase", "<ios>", cxx98}, -#line 250 "../../src/gcc/cp/std-name-hint.gperf" +#line 250 "std-name-hint.gperf" {"nounitbuf", "<ios>", cxx98}, -#line 433 "../../src/gcc/cp/std-name-hint.gperf" +#line 433 "std-name-hint.gperf" {"basic_stringbuf", "<sstream>", cxx98}, -#line 592 "../../src/gcc/cp/std-name-hint.gperf" +#line 592 "std-name-hint.gperf" {"vector", "<vector>", cxx98}, -#line 246 "../../src/gcc/cp/std-name-hint.gperf" +#line 246 "std-name-hint.gperf" {"noshowbase", "<ios>", cxx98}, -#line 219 "../../src/gcc/cp/std-name-hint.gperf" +#line 219 "std-name-hint.gperf" {"future", "<future>", cxx11}, -#line 340 "../../src/gcc/cp/std-name-hint.gperf" +#line 340 "std-name-hint.gperf" {"pmr::new_delete_resource", "<memory_resource>", cxx17}, -#line 337 "../../src/gcc/cp/std-name-hint.gperf" +#line 337 "std-name-hint.gperf" {"pmr::get_default_resource", "<memory_resource>", cxx17}, -#line 343 "../../src/gcc/cp/std-name-hint.gperf" +#line 343 "std-name-hint.gperf" {"pmr::set_default_resource", "<memory_resource>", cxx17}, -#line 455 "../../src/gcc/cp/std-name-hint.gperf" +#line 455 "std-name-hint.gperf" {"runtime_error", "<stdexcept>", cxx98}, -#line 516 "../../src/gcc/cp/std-name-hint.gperf" +#line 516 "std-name-hint.gperf" {"tuple", "<tuple>", cxx11}, -#line 132 "../../src/gcc/cp/std-name-hint.gperf" +#line 132 "std-name-hint.gperf" {"regular_invocable", "<concepts>", cxx20}, -#line 116 "../../src/gcc/cp/std-name-hint.gperf" +#line 116 "std-name-hint.gperf" {"swappable", "<concepts>", cxx20}, -#line 186 "../../src/gcc/cp/std-name-hint.gperf" +#line 186 "std-name-hint.gperf" {"runtime_format", "<format>", cxx26}, -#line 211 "../../src/gcc/cp/std-name-hint.gperf" +#line 211 "std-name-hint.gperf" {"not_fn", "<functional>", cxx17}, -#line 142 "../../src/gcc/cp/std-name-hint.gperf" +#line 142 "std-name-hint.gperf" {"byte", "<cstddef>", cxx17}, -#line 518 "../../src/gcc/cp/std-name-hint.gperf" +#line 518 "std-name-hint.gperf" {"tuple_element", "<tuple>", cxx11}, -#line 519 "../../src/gcc/cp/std-name-hint.gperf" +#line 519 "std-name-hint.gperf" {"tuple_element_t", "<tuple>", cxx14}, -#line 584 "../../src/gcc/cp/std-name-hint.gperf" +#line 584 "std-name-hint.gperf" {"variant", "<variant>", cxx17}, -#line 386 "../../src/gcc/cp/std-name-hint.gperf" +#line 386 "std-name-hint.gperf" {"ends", "<ostream>", cxx98}, -#line 520 "../../src/gcc/cp/std-name-hint.gperf" +#line 520 "std-name-hint.gperf" {"tuple_size", "<tuple>", cxx11}, -#line 123 "../../src/gcc/cp/std-name-hint.gperf" +#line 123 "std-name-hint.gperf" {"equality_comparable", "<concepts>", cxx20}, -#line 247 "../../src/gcc/cp/std-name-hint.gperf" +#line 247 "std-name-hint.gperf" {"noshowpoint", "<ios>", cxx98}, -#line 394 "../../src/gcc/cp/std-name-hint.gperf" +#line 394 "std-name-hint.gperf" {"println", "<print>", cxx23}, -#line 248 "../../src/gcc/cp/std-name-hint.gperf" +#line 248 "std-name-hint.gperf" {"noshowpos", "<ios>", cxx98}, -#line 388 "../../src/gcc/cp/std-name-hint.gperf" +#line 388 "std-name-hint.gperf" {"flush_emit", "<ostream>", cxx20}, -#line 585 "../../src/gcc/cp/std-name-hint.gperf" +#line 585 "std-name-hint.gperf" {"variant_alternative", "<variant>", cxx17}, -#line 537 "../../src/gcc/cp/std-name-hint.gperf" +#line 537 "std-name-hint.gperf" {"void_t", "<type_traits>", cxx17}, -#line 586 "../../src/gcc/cp/std-name-hint.gperf" +#line 586 "std-name-hint.gperf" {"variant_alternative_t", "<variant>", cxx17}, -#line 134 "../../src/gcc/cp/std-name-hint.gperf" +#line 134 "std-name-hint.gperf" {"relation", "<concepts>", cxx20}, -#line 461 "../../src/gcc/cp/std-name-hint.gperf" +#line 461 "std-name-hint.gperf" {"float32_t", "<stdfloat>", cxx23}, -#line 459 "../../src/gcc/cp/std-name-hint.gperf" +#line 459 "std-name-hint.gperf" {"float128_t", "<stdfloat>", cxx23}, -#line 381 "../../src/gcc/cp/std-name-hint.gperf" +#line 381 "std-name-hint.gperf" {"nullopt", "<optional>", cxx17}, -#line 291 "../../src/gcc/cp/std-name-hint.gperf" +#line 291 "std-name-hint.gperf" {"prev", "<iterator>", cxx11}, -#line 425 "../../src/gcc/cp/std-name-hint.gperf" +#line 425 "std-name-hint.gperf" {"spanbuf", "<spanstream>", cxx23}, -#line 513 "../../src/gcc/cp/std-name-hint.gperf" +#line 513 "std-name-hint.gperf" {"make_from_tuple", "<tuple>", cxx17}, -#line 253 "../../src/gcc/cp/std-name-hint.gperf" +#line 253 "std-name-hint.gperf" {"right", "<ios>", cxx98}, -#line 210 "../../src/gcc/cp/std-name-hint.gperf" +#line 210 "std-name-hint.gperf" {"mem_fn", "<functional>", cxx11}, -#line 515 "../../src/gcc/cp/std-name-hint.gperf" +#line 515 "std-name-hint.gperf" {"tie", "<tuple>", cxx11}, -#line 197 "../../src/gcc/cp/std-name-hint.gperf" +#line 197 "std-name-hint.gperf" {"basic_fstream", "<fstream>", cxx98}, -#line 573 "../../src/gcc/cp/std-name-hint.gperf" +#line 573 "std-name-hint.gperf" {"move", "<utility>", cxx11}, -#line 462 "../../src/gcc/cp/std-name-hint.gperf" +#line 462 "std-name-hint.gperf" {"float64_t", "<stdfloat>", cxx23}, -#line 127 "../../src/gcc/cp/std-name-hint.gperf" +#line 127 "std-name-hint.gperf" {"movable", "<concepts>", cxx20}, -#line 282 "../../src/gcc/cp/std-name-hint.gperf" +#line 282 "std-name-hint.gperf" {"front_inserter", "<iterator>", cxx98}, -#line 296 "../../src/gcc/cp/std-name-hint.gperf" +#line 296 "std-name-hint.gperf" {"map", "<map>", cxx98}, -#line 460 "../../src/gcc/cp/std-name-hint.gperf" +#line 460 "std-name-hint.gperf" {"float16_t", "<stdfloat>", cxx23}, -#line 56 "../../src/gcc/cp/std-name-hint.gperf" +#line 56 "std-name-hint.gperf" {"bitset", "<bitset>", cxx11}, -#line 205 "../../src/gcc/cp/std-name-hint.gperf" +#line 205 "std-name-hint.gperf" {"bind_front", "<functional>", cxx20}, -#line 574 "../../src/gcc/cp/std-name-hint.gperf" +#line 574 "std-name-hint.gperf" {"move_if_noexcept", "<utility>", cxx11}, -#line 274 "../../src/gcc/cp/std-name-hint.gperf" +#line 274 "std-name-hint.gperf" {"advance", "<iterator>", cxx98}, -#line 287 "../../src/gcc/cp/std-name-hint.gperf" +#line 287 "std-name-hint.gperf" {"move_iterator", "<iterator>", cxx11}, -#line 161 "../../src/gcc/cp/std-name-hint.gperf" +#line 161 "std-name-hint.gperf" {"terminate", "<exception>", cxx98}, -#line 531 "../../src/gcc/cp/std-name-hint.gperf" +#line 531 "std-name-hint.gperf" {"negation", "<type_traits>", cxx17}, -#line 318 "../../src/gcc/cp/std-name-hint.gperf" +#line 318 "std-name-hint.gperf" {"make_obj_using_allocator", "<memory>", cxx20}, -#line 321 "../../src/gcc/cp/std-name-hint.gperf" +#line 321 "std-name-hint.gperf" {"make_unique", "<memory>", cxx14}, -#line 255 "../../src/gcc/cp/std-name-hint.gperf" +#line 255 "std-name-hint.gperf" {"showbase", "<ios>", cxx98}, -#line 416 "../../src/gcc/cp/std-name-hint.gperf" +#line 416 "std-name-hint.gperf" {"span", "<span>", cxx20}, -#line 185 "../../src/gcc/cp/std-name-hint.gperf" +#line 185 "std-name-hint.gperf" {"range_formatter", "<format>", cxx23}, -#line 299 "../../src/gcc/cp/std-name-hint.gperf" +#line 299 "std-name-hint.gperf" {"addressof", "<memory>", cxx11}, -#line 571 "../../src/gcc/cp/std-name-hint.gperf" +#line 571 "std-name-hint.gperf" {"make_integer_sequence", "<utility>", cxx14}, -#line 184 "../../src/gcc/cp/std-name-hint.gperf" +#line 184 "std-name-hint.gperf" {"range_format", "<format>", cxx23}, -#line 322 "../../src/gcc/cp/std-name-hint.gperf" +#line 322 "std-name-hint.gperf" {"make_unique_for_overwrite", "<memory>", cxx20}, -#line 421 "../../src/gcc/cp/std-name-hint.gperf" +#line 421 "std-name-hint.gperf" {"basic_spanstream", "<spanstream>", cxx23}, -#line 257 "../../src/gcc/cp/std-name-hint.gperf" +#line 257 "std-name-hint.gperf" {"showpos", "<ios>", cxx98}, -#line 550 "../../src/gcc/cp/std-name-hint.gperf" +#line 550 "std-name-hint.gperf" {"as_const", "<utility>", cxx17}, -#line 308 "../../src/gcc/cp/std-name-hint.gperf" +#line 308 "std-name-hint.gperf" {"bad_weak_ptr", "<memory>", cxx11}, -#line 168 "../../src/gcc/cp/std-name-hint.gperf" +#line 168 "std-name-hint.gperf" {"basic_format_context", "<format>", cxx20}, -#line 434 "../../src/gcc/cp/std-name-hint.gperf" +#line 434 "std-name-hint.gperf" {"basic_stringstream", "<sstream>", cxx98}, -#line 539 "../../src/gcc/cp/std-name-hint.gperf" +#line 539 "std-name-hint.gperf" {"type_index", "<typeindex>", cxx11}, -#line 169 "../../src/gcc/cp/std-name-hint.gperf" +#line 169 "std-name-hint.gperf" {"basic_format_parse_context", "<format>", cxx20}, -#line 160 "../../src/gcc/cp/std-name-hint.gperf" +#line 160 "std-name-hint.gperf" {"make_exception_ptr", "<exception>", cxx11}, -#line 468 "../../src/gcc/cp/std-name-hint.gperf" +#line 468 "std-name-hint.gperf" {"basic_streambuf", "<streambuf>", cxx98}, -#line 167 "../../src/gcc/cp/std-name-hint.gperf" +#line 167 "std-name-hint.gperf" {"basic_format_args", "<format>", cxx20}, -#line 588 "../../src/gcc/cp/std-name-hint.gperf" +#line 588 "std-name-hint.gperf" {"variant_size", "<variant>", cxx17}, -#line 407 "../../src/gcc/cp/std-name-hint.gperf" +#line 407 "std-name-hint.gperf" {"multiset", "<set>", cxx98}, -#line 133 "../../src/gcc/cp/std-name-hint.gperf" +#line 133 "std-name-hint.gperf" {"predicate", "<concepts>", cxx20}, -#line 397 "../../src/gcc/cp/std-name-hint.gperf" +#line 397 "std-name-hint.gperf" {"queue", "<queue>", cxx98}, -#line 353 "../../src/gcc/cp/std-name-hint.gperf" +#line 353 "std-name-hint.gperf" {"recursive_timed_mutex", "<mutex>", cxx11}, -#line 399 "../../src/gcc/cp/std-name-hint.gperf" +#line 399 "std-name-hint.gperf" {"ranges::enable_borrowed_range", "<ranges>", cxx20}, -#line 533 "../../src/gcc/cp/std-name-hint.gperf" +#line 533 "std-name-hint.gperf" {"remove_cvref", "<type_traits>", cxx20}, -#line 206 "../../src/gcc/cp/std-name-hint.gperf" +#line 206 "std-name-hint.gperf" {"function", "<functional>", cxx11}, -#line 532 "../../src/gcc/cp/std-name-hint.gperf" +#line 532 "std-name-hint.gperf" {"negation_v", "<type_traits>", cxx17}, -#line 534 "../../src/gcc/cp/std-name-hint.gperf" +#line 534 "std-name-hint.gperf" {"remove_cvref_t", "<type_traits>", cxx20}, -#line 289 "../../src/gcc/cp/std-name-hint.gperf" +#line 289 "std-name-hint.gperf" {"ostream_iterator", "<iterator>", cxx98}, -#line 587 "../../src/gcc/cp/std-name-hint.gperf" +#line 587 "std-name-hint.gperf" {"variant_npos", "<variant>", cxx17}, -#line 244 "../../src/gcc/cp/std-name-hint.gperf" +#line 244 "std-name-hint.gperf" {"left", "<ios>", cxx98}, -#line 200 "../../src/gcc/cp/std-name-hint.gperf" +#line 200 "std-name-hint.gperf" {"fstream", "<fstream>", cxx98}, -#line 590 "../../src/gcc/cp/std-name-hint.gperf" +#line 590 "std-name-hint.gperf" {"visit", "<variant>", cxx17}, -#line 208 "../../src/gcc/cp/std-name-hint.gperf" +#line 208 "std-name-hint.gperf" {"invoke", "<functional>", cxx17}, -#line 137 "../../src/gcc/cp/std-name-hint.gperf" +#line 137 "std-name-hint.gperf" {"ranges::swap", "<concepts>", cxx20}, -#line 283 "../../src/gcc/cp/std-name-hint.gperf" +#line 283 "std-name-hint.gperf" {"inserter", "<iterator>", cxx98}, -#line 362 "../../src/gcc/cp/std-name-hint.gperf" +#line 362 "std-name-hint.gperf" {"launder", "<new>", cxx17}, -#line 228 "../../src/gcc/cp/std-name-hint.gperf" +#line 228 "std-name-hint.gperf" {"put_time", "<iomanip>", cxx11}, -#line 237 "../../src/gcc/cp/std-name-hint.gperf" +#line 237 "std-name-hint.gperf" {"boolalpha", "<ios>", cxx98}, -#line 361 "../../src/gcc/cp/std-name-hint.gperf" +#line 361 "std-name-hint.gperf" {"hardware_destructive_interference_size", "<new>", cxx17}, -#line 199 "../../src/gcc/cp/std-name-hint.gperf" +#line 199 "std-name-hint.gperf" {"basic_ofstream", "<fstream>", cxx98}, -#line 324 "../../src/gcc/cp/std-name-hint.gperf" +#line 324 "std-name-hint.gperf" {"pointer_traits", "<memory>", cxx11}, -#line 131 "../../src/gcc/cp/std-name-hint.gperf" +#line 131 "std-name-hint.gperf" {"invocable", "<concepts>", cxx20}, -#line 426 "../../src/gcc/cp/std-name-hint.gperf" +#line 426 "std-name-hint.gperf" {"spanstream", "<spanstream>", cxx23}, -#line 182 "../../src/gcc/cp/std-name-hint.gperf" +#line 182 "std-name-hint.gperf" {"make_format_args", "<format>", cxx20}, -#line 121 "../../src/gcc/cp/std-name-hint.gperf" +#line 121 "std-name-hint.gperf" {"move_constructible", "<concepts>", cxx20}, -#line 114 "../../src/gcc/cp/std-name-hint.gperf" +#line 114 "std-name-hint.gperf" {"floating_point", "<concepts>", cxx20}, -#line 258 "../../src/gcc/cp/std-name-hint.gperf" +#line 258 "std-name-hint.gperf" {"skipws", "<ios>", cxx98}, -#line 241 "../../src/gcc/cp/std-name-hint.gperf" +#line 241 "std-name-hint.gperf" {"hex", "<ios>", cxx98}, -#line 568 "../../src/gcc/cp/std-name-hint.gperf" +#line 568 "std-name-hint.gperf" {"index_sequence_for", "<utility>", cxx14}, -#line 567 "../../src/gcc/cp/std-name-hint.gperf" +#line 567 "std-name-hint.gperf" {"index_sequence", "<utility>", cxx14}, -#line 350 "../../src/gcc/cp/std-name-hint.gperf" +#line 350 "std-name-hint.gperf" {"mutex", "<mutex>", cxx11}, -#line 284 "../../src/gcc/cp/std-name-hint.gperf" +#line 284 "std-name-hint.gperf" {"istream_iterator", "<iterator>", cxx98}, -#line 334 "../../src/gcc/cp/std-name-hint.gperf" +#line 334 "std-name-hint.gperf" {"weak_ptr", "<memory>", cxx11}, -#line 173 "../../src/gcc/cp/std-name-hint.gperf" +#line 173 "std-name-hint.gperf" {"format_context", "<format>", cxx20}, -#line 451 "../../src/gcc/cp/std-name-hint.gperf" +#line 451 "std-name-hint.gperf" {"logic_error", "<stdexcept>", cxx98}, -#line 372 "../../src/gcc/cp/std-name-hint.gperf" +#line 372 "std-name-hint.gperf" {"numbers::ln2_v", "<numbers>", cxx20}, -#line 371 "../../src/gcc/cp/std-name-hint.gperf" +#line 371 "std-name-hint.gperf" {"numbers::ln10_v", "<numbers>", cxx20}, -#line 368 "../../src/gcc/cp/std-name-hint.gperf" +#line 368 "std-name-hint.gperf" {"numbers::inv_pi_v", "<numbers>", cxx20}, -#line 235 "../../src/gcc/cp/std-name-hint.gperf" +#line 235 "std-name-hint.gperf" {"setw", "<iomanip>", cxx98}, -#line 369 "../../src/gcc/cp/std-name-hint.gperf" +#line 369 "std-name-hint.gperf" {"numbers::inv_sqrt3_v", "<numbers>", cxx20}, -#line 370 "../../src/gcc/cp/std-name-hint.gperf" +#line 370 "std-name-hint.gperf" {"numbers::inv_sqrtpi_v", "<numbers>", cxx20}, -#line 230 "../../src/gcc/cp/std-name-hint.gperf" +#line 230 "std-name-hint.gperf" {"resetiosflags", "<iomanip>", cxx98}, -#line 303 "../../src/gcc/cp/std-name-hint.gperf" +#line 303 "std-name-hint.gperf" {"allocator", "<memory>", cxx98}, -#line 135 "../../src/gcc/cp/std-name-hint.gperf" +#line 135 "std-name-hint.gperf" {"equivalence_relation", "<concepts>", cxx20}, -#line 272 "../../src/gcc/cp/std-name-hint.gperf" +#line 272 "std-name-hint.gperf" {"ws", "<istream>", cxx98}, -#line 44 "../../src/gcc/cp/std-name-hint.gperf" +#line 44 "std-name-hint.gperf" {"any_cast", "<any>", cxx17}, -#line 198 "../../src/gcc/cp/std-name-hint.gperf" +#line 198 "std-name-hint.gperf" {"basic_ifstream", "<fstream>", cxx98}, -#line 175 "../../src/gcc/cp/std-name-hint.gperf" +#line 175 "std-name-hint.gperf" {"format_parse_context", "<format>", cxx20}, -#line 341 "../../src/gcc/cp/std-name-hint.gperf" +#line 341 "std-name-hint.gperf" {"pmr::polymorphic_allocator", "<memory_resource>", cxx17}, -#line 419 "../../src/gcc/cp/std-name-hint.gperf" +#line 419 "std-name-hint.gperf" {"basic_ospanstream", "<spanstream>", cxx23}, -#line 572 "../../src/gcc/cp/std-name-hint.gperf" +#line 572 "std-name-hint.gperf" {"make_pair", "<utility>", cxx98}, -#line 172 "../../src/gcc/cp/std-name-hint.gperf" +#line 172 "std-name-hint.gperf" {"format_args", "<format>", cxx20}, -#line 475 "../../src/gcc/cp/std-name-hint.gperf" +#line 475 "std-name-hint.gperf" {"stof", "<string>", cxx11}, -#line 339 "../../src/gcc/cp/std-name-hint.gperf" +#line 339 "std-name-hint.gperf" {"pmr::monotonic_buffer_resource", "<memory_resource>", cxx17}, -#line 342 "../../src/gcc/cp/std-name-hint.gperf" +#line 342 "std-name-hint.gperf" {"pmr::pool_options", "<memory_resource>", cxx17}, -#line 256 "../../src/gcc/cp/std-name-hint.gperf" +#line 256 "std-name-hint.gperf" {"showpoint", "<ios>", cxx98}, -#line 446 "../../src/gcc/cp/std-name-hint.gperf" +#line 446 "std-name-hint.gperf" {"stacktrace", "<stacktrace>", cxx23}, -#line 589 "../../src/gcc/cp/std-name-hint.gperf" +#line 589 "std-name-hint.gperf" {"variant_size_v", "<variant>", cxx17}, -#line 212 "../../src/gcc/cp/std-name-hint.gperf" +#line 212 "std-name-hint.gperf" {"reference_wrapper", "<functional>", cxx11}, -#line 262 "../../src/gcc/cp/std-name-hint.gperf" +#line 262 "std-name-hint.gperf" {"cerr", "<iostream>", cxx98}, -#line 300 "../../src/gcc/cp/std-name-hint.gperf" +#line 300 "std-name-hint.gperf" {"align", "<memory>", cxx11}, -#line 438 "../../src/gcc/cp/std-name-hint.gperf" +#line 438 "std-name-hint.gperf" {"stringbuf", "<sstream>", cxx98}, -#line 469 "../../src/gcc/cp/std-name-hint.gperf" +#line 469 "std-name-hint.gperf" {"streambuf", "<streambuf>", cxx98}, -#line 306 "../../src/gcc/cp/std-name-hint.gperf" +#line 306 "std-name-hint.gperf" {"allocator_traits", "<memory>", cxx11}, -#line 183 "../../src/gcc/cp/std-name-hint.gperf" +#line 183 "std-name-hint.gperf" {"make_wformat_args", "<format>", cxx20}, -#line 583 "../../src/gcc/cp/std-name-hint.gperf" +#line 583 "std-name-hint.gperf" {"monostate", "<variant>", cxx17}, -#line 387 "../../src/gcc/cp/std-name-hint.gperf" +#line 387 "std-name-hint.gperf" {"flush", "<ostream>", cxx98}, -#line 527 "../../src/gcc/cp/std-name-hint.gperf" +#line 527 "std-name-hint.gperf" {"enable_if", "<type_traits>", cxx11}, -#line 352 "../../src/gcc/cp/std-name-hint.gperf" +#line 352 "std-name-hint.gperf" {"recursive_mutex", "<mutex>", cxx11}, -#line 418 "../../src/gcc/cp/std-name-hint.gperf" +#line 418 "std-name-hint.gperf" {"basic_ispanstream", "<spanstream>", cxx23}, -#line 178 "../../src/gcc/cp/std-name-hint.gperf" +#line 178 "std-name-hint.gperf" {"format_to_n", "<format>", cxx20}, -#line 338 "../../src/gcc/cp/std-name-hint.gperf" +#line 338 "std-name-hint.gperf" {"pmr::memory_resource", "<memory_resource>", cxx17}, -#line 124 "../../src/gcc/cp/std-name-hint.gperf" +#line 124 "std-name-hint.gperf" {"equality_comparable_with", "<concepts>", cxx20}, -#line 493 "../../src/gcc/cp/std-name-hint.gperf" +#line 493 "std-name-hint.gperf" {"errc", "<system_error>", cxx11}, -#line 344 "../../src/gcc/cp/std-name-hint.gperf" +#line 344 "std-name-hint.gperf" {"pmr::synchronized_pool_resource", "<memory_resource>", cxx17}, -#line 385 "../../src/gcc/cp/std-name-hint.gperf" +#line 385 "std-name-hint.gperf" {"endl", "<ostream>", cxx98}, -#line 495 "../../src/gcc/cp/std-name-hint.gperf" +#line 495 "std-name-hint.gperf" {"error_code", "<system_error>", cxx11}, -#line 528 "../../src/gcc/cp/std-name-hint.gperf" +#line 528 "std-name-hint.gperf" {"enable_if_t", "<type_traits>", cxx14}, -#line 390 "../../src/gcc/cp/std-name-hint.gperf" +#line 390 "std-name-hint.gperf" {"ostream", "<ostream>", cxx98}, -#line 374 "../../src/gcc/cp/std-name-hint.gperf" +#line 374 "std-name-hint.gperf" {"numbers::log2e_v", "<numbers>", cxx20}, -#line 373 "../../src/gcc/cp/std-name-hint.gperf" +#line 373 "std-name-hint.gperf" {"numbers::log10e_v", "<numbers>", cxx20}, -#line 364 "../../src/gcc/cp/std-name-hint.gperf" +#line 364 "std-name-hint.gperf" {"nothrow_t", "<new>", cxx98}, -#line 512 "../../src/gcc/cp/std-name-hint.gperf" +#line 512 "std-name-hint.gperf" {"ignore", "<tuple>", cxx11}, -#line 360 "../../src/gcc/cp/std-name-hint.gperf" +#line 360 "std-name-hint.gperf" {"hardware_constructive_interference_size", "<new>", cxx17}, -#line 396 "../../src/gcc/cp/std-name-hint.gperf" +#line 396 "std-name-hint.gperf" {"priority_queue", "<queue>", cxx98}, -#line 424 "../../src/gcc/cp/std-name-hint.gperf" +#line 424 "std-name-hint.gperf" {"ospanstream", "<spanstream>", cxx23}, -#line 316 "../../src/gcc/cp/std-name-hint.gperf" +#line 316 "std-name-hint.gperf" {"enable_shared_from_this", "<memory>", cxx11}, -#line 265 "../../src/gcc/cp/std-name-hint.gperf" +#line 265 "std-name-hint.gperf" {"cout", "<iostream>", cxx98}, -#line 552 "../../src/gcc/cp/std-name-hint.gperf" +#line 552 "std-name-hint.gperf" {"cmp_greater", "<utility>", cxx20}, -#line 490 "../../src/gcc/cp/std-name-hint.gperf" +#line 490 "std-name-hint.gperf" {"basic_string_view", "<string_view>", cxx17}, -#line 249 "../../src/gcc/cp/std-name-hint.gperf" +#line 249 "std-name-hint.gperf" {"noskipws", "<ios>", cxx98}, -#line 177 "../../src/gcc/cp/std-name-hint.gperf" +#line 177 "std-name-hint.gperf" {"format_to", "<format>", cxx20}, -#line 502 "../../src/gcc/cp/std-name-hint.gperf" +#line 502 "std-name-hint.gperf" {"make_error_code", "<system_error>", cxx11}, -#line 174 "../../src/gcc/cp/std-name-hint.gperf" +#line 174 "std-name-hint.gperf" {"format_error", "<format>", cxx20}, -#line 347 "../../src/gcc/cp/std-name-hint.gperf" +#line 347 "std-name-hint.gperf" {"call_once", "<mutex>", cxx11}, -#line 377 "../../src/gcc/cp/std-name-hint.gperf" +#line 377 "std-name-hint.gperf" {"numbers::sqrt2_v", "<numbers>", cxx20}, -#line 378 "../../src/gcc/cp/std-name-hint.gperf" +#line 378 "std-name-hint.gperf" {"numbers::sqrt3_v", "<numbers>", cxx20}, -#line 242 "../../src/gcc/cp/std-name-hint.gperf" +#line 242 "std-name-hint.gperf" {"hexfloat", "<ios>", cxx11}, -#line 326 "../../src/gcc/cp/std-name-hint.gperf" +#line 326 "std-name-hint.gperf" {"shared_ptr", "<memory>", cxx11}, -#line 569 "../../src/gcc/cp/std-name-hint.gperf" +#line 569 "std-name-hint.gperf" {"integer_sequence", "<utility>", cxx14}, -#line 271 "../../src/gcc/cp/std-name-hint.gperf" +#line 271 "std-name-hint.gperf" {"istream", "<istream>", cxx98}, -#line 376 "../../src/gcc/cp/std-name-hint.gperf" +#line 376 "std-name-hint.gperf" {"numbers::pi_v", "<numbers>", cxx20}, -#line 259 "../../src/gcc/cp/std-name-hint.gperf" +#line 259 "std-name-hint.gperf" {"unitbuf", "<ios>", cxx98}, -#line 294 "../../src/gcc/cp/std-name-hint.gperf" +#line 294 "std-name-hint.gperf" {"list", "<list>", cxx98}, -#line 554 "../../src/gcc/cp/std-name-hint.gperf" +#line 554 "std-name-hint.gperf" {"cmp_less", "<utility>", cxx20}, -#line 422 "../../src/gcc/cp/std-name-hint.gperf" +#line 422 "std-name-hint.gperf" {"ispanstream", "<spanstream>", cxx23}, -#line 423 "../../src/gcc/cp/std-name-hint.gperf" +#line 423 "std-name-hint.gperf" {"ispanstream", "<spanstream>", cxx23}, -#line 470 "../../src/gcc/cp/std-name-hint.gperf" +#line 470 "std-name-hint.gperf" {"wstreambuf", "<streambuf>", cxx98}, -#line 432 "../../src/gcc/cp/std-name-hint.gperf" +#line 432 "std-name-hint.gperf" {"basic_ostringstream", "<sstream>", cxx98}, -#line 503 "../../src/gcc/cp/std-name-hint.gperf" +#line 503 "std-name-hint.gperf" {"make_error_condition", "<system_error>", cxx11}, -#line 570 "../../src/gcc/cp/std-name-hint.gperf" +#line 570 "std-name-hint.gperf" {"make_index_sequence", "<utility>", cxx14}, -#line 405 "../../src/gcc/cp/std-name-hint.gperf" +#line 405 "std-name-hint.gperf" {"counting_semaphore", "<semaphore>", cxx20}, -#line 355 "../../src/gcc/cp/std-name-hint.gperf" +#line 355 "std-name-hint.gperf" {"timed_mutex", "<mutex>", cxx11}, -#line 297 "../../src/gcc/cp/std-name-hint.gperf" +#line 297 "std-name-hint.gperf" {"multimap", "<map>", cxx98}, -#line 227 "../../src/gcc/cp/std-name-hint.gperf" +#line 227 "std-name-hint.gperf" {"put_money", "<iomanip>", cxx11}, -#line 309 "../../src/gcc/cp/std-name-hint.gperf" +#line 309 "std-name-hint.gperf" {"const_pointer_cast", "<memory>", cxx11}, -#line 45 "../../src/gcc/cp/std-name-hint.gperf" +#line 45 "std-name-hint.gperf" {"make_any", "<any>", cxx17}, -#line 465 "../../src/gcc/cp/std-name-hint.gperf" +#line 465 "std-name-hint.gperf" {"stop_source", "<stop_token>", cxx20}, -#line 548 "../../src/gcc/cp/std-name-hint.gperf" +#line 548 "std-name-hint.gperf" {"unordered_set", "<unordered_set>", cxx11}, -#line 582 "../../src/gcc/cp/std-name-hint.gperf" +#line 582 "std-name-hint.gperf" {"holds_alternative", "<variant>", cxx17}, -#line 514 "../../src/gcc/cp/std-name-hint.gperf" +#line 514 "std-name-hint.gperf" {"make_tuple", "<tuple>", cxx11}, -#line 47 "../../src/gcc/cp/std-name-hint.gperf" +#line 47 "std-name-hint.gperf" {"array", "<array>", cxx11}, -#line 517 "../../src/gcc/cp/std-name-hint.gperf" +#line 517 "std-name-hint.gperf" {"tuple_cat", "<tuple>", cxx11}, -#line 323 "../../src/gcc/cp/std-name-hint.gperf" +#line 323 "std-name-hint.gperf" {"owner_less", "<memory>", cxx11}, -#line 158 "../../src/gcc/cp/std-name-hint.gperf" +#line 158 "std-name-hint.gperf" {"exception", "<exception>", cxx98}, -#line 159 "../../src/gcc/cp/std-name-hint.gperf" +#line 159 "std-name-hint.gperf" {"exception_ptr", "<exception>", cxx11}, -#line 207 "../../src/gcc/cp/std-name-hint.gperf" +#line 207 "std-name-hint.gperf" {"hash", "<functional>", cxx11}, -#line 521 "../../src/gcc/cp/std-name-hint.gperf" +#line 521 "std-name-hint.gperf" {"tuple_size_v", "<tuple>", cxx17}, -#line 431 "../../src/gcc/cp/std-name-hint.gperf" +#line 431 "std-name-hint.gperf" {"basic_istringstream", "<sstream>", cxx98}, -#line 153 "../../src/gcc/cp/std-name-hint.gperf" +#line 153 "std-name-hint.gperf" {"uintptr_t", "<cstdint>", cxx11}, -#line 476 "../../src/gcc/cp/std-name-hint.gperf" +#line 476 "std-name-hint.gperf" {"stoi", "<string>", cxx11}, -#line 466 "../../src/gcc/cp/std-name-hint.gperf" +#line 466 "std-name-hint.gperf" {"stop_token", "<stop_token>", cxx20}, -#line 151 "../../src/gcc/cp/std-name-hint.gperf" +#line 151 "std-name-hint.gperf" {"uint_least8_t", "<cstdint>", cxx11}, -#line 380 "../../src/gcc/cp/std-name-hint.gperf" +#line 380 "std-name-hint.gperf" {"make_optional", "<optional>", cxx17}, -#line 345 "../../src/gcc/cp/std-name-hint.gperf" +#line 345 "std-name-hint.gperf" {"pmr::unsynchronized_pool_resource", "<memory_resource>", cxx17}, -#line 48 "../../src/gcc/cp/std-name-hint.gperf" +#line 48 "std-name-hint.gperf" {"to_array", "<array>", cxx20}, -#line 104 "../../src/gcc/cp/std-name-hint.gperf" +#line 104 "std-name-hint.gperf" {"complex_literals", "<complex>", cxx14}, -#line 411 "../../src/gcc/cp/std-name-hint.gperf" +#line 411 "std-name-hint.gperf" {"shared_mutex", "<shared_mutex>", cxx17}, -#line 375 "../../src/gcc/cp/std-name-hint.gperf" +#line 375 "std-name-hint.gperf" {"numbers::phi_v", "<numbers>", cxx20}, -#line 499 "../../src/gcc/cp/std-name-hint.gperf" +#line 499 "std-name-hint.gperf" {"is_error_code_enum_v", "<system_error>", cxx17}, -#line 155 "../../src/gcc/cp/std-name-hint.gperf" +#line 155 "std-name-hint.gperf" {"deque", "<deque>", cxx98}, -#line 412 "../../src/gcc/cp/std-name-hint.gperf" +#line 412 "std-name-hint.gperf" {"shared_timed_mutex", "<shared_mutex>", cxx14}, -#line 290 "../../src/gcc/cp/std-name-hint.gperf" +#line 290 "std-name-hint.gperf" {"ostreambuf_iterator", "<iterator>", cxx98}, -#line 501 "../../src/gcc/cp/std-name-hint.gperf" +#line 501 "std-name-hint.gperf" {"is_error_condition_enum_v", "<system_error>", cxx17}, -#line 148 "../../src/gcc/cp/std-name-hint.gperf" +#line 148 "std-name-hint.gperf" {"uint_least16_t", "<cstdint>", cxx11}, -#line 305 "../../src/gcc/cp/std-name-hint.gperf" +#line 305 "std-name-hint.gperf" {"allocator_arg_t", "<memory>", cxx11}, -#line 366 "../../src/gcc/cp/std-name-hint.gperf" +#line 366 "std-name-hint.gperf" {"numbers::e_v", "<numbers>", cxx20}, -#line 209 "../../src/gcc/cp/std-name-hint.gperf" +#line 209 "std-name-hint.gperf" {"invoke_r", "<functional>", cxx23}, -#line 441 "../../src/gcc/cp/std-name-hint.gperf" +#line 441 "std-name-hint.gperf" {"wostringstream", "<sstream>", cxx98}, -#line 122 "../../src/gcc/cp/std-name-hint.gperf" +#line 122 "std-name-hint.gperf" {"copy_constructible", "<concepts>", cxx20}, -#line 149 "../../src/gcc/cp/std-name-hint.gperf" +#line 149 "std-name-hint.gperf" {"uint_least32_t", "<cstdint>", cxx11}, -#line 108 "../../src/gcc/cp/std-name-hint.gperf" +#line 108 "std-name-hint.gperf" {"convertible_to", "<concepts>", cxx20}, -#line 234 "../../src/gcc/cp/std-name-hint.gperf" +#line 234 "std-name-hint.gperf" {"setprecision", "<iomanip>", cxx98}, -#line 498 "../../src/gcc/cp/std-name-hint.gperf" +#line 498 "std-name-hint.gperf" {"is_error_code_enum", "<system_error>", cxx11}, -#line 453 "../../src/gcc/cp/std-name-hint.gperf" +#line 453 "std-name-hint.gperf" {"overflow_error", "<stdexcept>", cxx98}, -#line 414 "../../src/gcc/cp/std-name-hint.gperf" +#line 414 "std-name-hint.gperf" {"source_location", "<source_location>", cxx20}, -#line 500 "../../src/gcc/cp/std-name-hint.gperf" +#line 500 "std-name-hint.gperf" {"is_error_condition_enum", "<system_error>", cxx11}, -#line 400 "../../src/gcc/cp/std-name-hint.gperf" +#line 400 "std-name-hint.gperf" {"ranges::enable_view", "<ranges>", cxx20}, -#line 218 "../../src/gcc/cp/std-name-hint.gperf" +#line 218 "std-name-hint.gperf" {"async", "<future>", cxx11}, -#line 103 "../../src/gcc/cp/std-name-hint.gperf" +#line 103 "std-name-hint.gperf" {"complex", "<complex>", cxx98}, -#line 150 "../../src/gcc/cp/std-name-hint.gperf" +#line 150 "std-name-hint.gperf" {"uint_least64_t", "<cstdint>", cxx11}, -#line 232 "../../src/gcc/cp/std-name-hint.gperf" +#line 232 "std-name-hint.gperf" {"setfill", "<iomanip>", cxx98}, -#line 79 "../../src/gcc/cp/std-name-hint.gperf" +#line 79 "std-name-hint.gperf" {"chrono::parse", "<chrono>", cxx20}, -#line 285 "../../src/gcc/cp/std-name-hint.gperf" +#line 285 "std-name-hint.gperf" {"istreambuf_iterator", "<iterator>", cxx98}, -#line 473 "../../src/gcc/cp/std-name-hint.gperf" +#line 473 "std-name-hint.gperf" {"char_traits", "<string>", cxx98}, -#line 263 "../../src/gcc/cp/std-name-hint.gperf" +#line 263 "std-name-hint.gperf" {"cin", "<iostream>", cxx98}, -#line 61 "../../src/gcc/cp/std-name-hint.gperf" +#line 61 "std-name-hint.gperf" {"chrono::duration_cast", "<chrono>", cxx11}, -#line 152 "../../src/gcc/cp/std-name-hint.gperf" +#line 152 "std-name-hint.gperf" {"uintmax_t", "<cstdint>", cxx11}, -#line 43 "../../src/gcc/cp/std-name-hint.gperf" +#line 43 "std-name-hint.gperf" {"any", "<any>", cxx17}, -#line 389 "../../src/gcc/cp/std-name-hint.gperf" +#line 389 "std-name-hint.gperf" {"noemit_on_flush", "<ostream>", cxx20}, -#line 66 "../../src/gcc/cp/std-name-hint.gperf" +#line 66 "std-name-hint.gperf" {"chrono::get_tzdb_list", "<chrono>", cxx20}, -#line 93 "../../src/gcc/cp/std-name-hint.gperf" +#line 93 "std-name-hint.gperf" {"chrono::weeks", "<chrono>", cxx20}, -#line 65 "../../src/gcc/cp/std-name-hint.gperf" +#line 65 "std-name-hint.gperf" {"chrono::get_tzdb", "<chrono>", cxx20}, -#line 302 "../../src/gcc/cp/std-name-hint.gperf" +#line 302 "std-name-hint.gperf" {"allocate_shared_for_overwrite", "<memory>", cxx20}, -#line 547 "../../src/gcc/cp/std-name-hint.gperf" +#line 547 "std-name-hint.gperf" {"unordered_multiset", "<unordered_set>", cxx11}, -#line 194 "../../src/gcc/cp/std-name-hint.gperf" +#line 194 "std-name-hint.gperf" {"forward_list", "<forward_list>", cxx11}, -#line 496 "../../src/gcc/cp/std-name-hint.gperf" +#line 496 "std-name-hint.gperf" {"error_condition", "<system_error>", cxx11}, -#line 542 "../../src/gcc/cp/std-name-hint.gperf" +#line 542 "std-name-hint.gperf" {"bad_typeid", "<typeinfo>", cxx98}, -#line 60 "../../src/gcc/cp/std-name-hint.gperf" +#line 60 "std-name-hint.gperf" {"chrono::duration", "<chrono>", cxx11}, -#line 510 "../../src/gcc/cp/std-name-hint.gperf" +#line 510 "std-name-hint.gperf" {"apply", "<tuple>", cxx17}, -#line 95 "../../src/gcc/cp/std-name-hint.gperf" +#line 95 "std-name-hint.gperf" {"chrono::zoned_time", "<chrono>", cxx20}, -#line 477 "../../src/gcc/cp/std-name-hint.gperf" +#line 477 "std-name-hint.gperf" {"stol", "<string>", cxx11}, -#line 479 "../../src/gcc/cp/std-name-hint.gperf" +#line 479 "std-name-hint.gperf" {"stoll", "<string>", cxx11}, -#line 480 "../../src/gcc/cp/std-name-hint.gperf" +#line 480 "std-name-hint.gperf" {"stoul", "<string>", cxx11}, -#line 481 "../../src/gcc/cp/std-name-hint.gperf" +#line 481 "std-name-hint.gperf" {"stoull", "<string>", cxx11}, -#line 220 "../../src/gcc/cp/std-name-hint.gperf" +#line 220 "std-name-hint.gperf" {"packaged_task", "<future>", cxx11}, -#line 544 "../../src/gcc/cp/std-name-hint.gperf" +#line 544 "std-name-hint.gperf" {"unordered_map", "<unordered_map>", cxx11}, -#line 77 "../../src/gcc/cp/std-name-hint.gperf" +#line 77 "std-name-hint.gperf" {"chrono::months", "<chrono>", cxx20}, -#line 76 "../../src/gcc/cp/std-name-hint.gperf" +#line 76 "std-name-hint.gperf" {"chrono::minutes", "<chrono>", cxx11}, -#line 545 "../../src/gcc/cp/std-name-hint.gperf" +#line 545 "std-name-hint.gperf" {"unordered_multimap", "<unordered_map>", cxx11}, -#line 78 "../../src/gcc/cp/std-name-hint.gperf" +#line 78 "std-name-hint.gperf" {"chrono::nanoseconds", "<chrono>", cxx11}, -#line 286 "../../src/gcc/cp/std-name-hint.gperf" +#line 286 "std-name-hint.gperf" {"iterator_traits", "<iterator>", cxx98}, -#line 404 "../../src/gcc/cp/std-name-hint.gperf" +#line 404 "std-name-hint.gperf" {"binary_semaphore", "<semaphore>", cxx20}, -#line 281 "../../src/gcc/cp/std-name-hint.gperf" +#line 281 "std-name-hint.gperf" {"end", "<iterator>", cxx11}, -#line 429 "../../src/gcc/cp/std-name-hint.gperf" +#line 429 "std-name-hint.gperf" {"wspanbuf", "<spanstream>", cxx23}, -#line 252 "../../src/gcc/cp/std-name-hint.gperf" +#line 252 "std-name-hint.gperf" {"oct", "<ios>", cxx98}, -#line 245 "../../src/gcc/cp/std-name-hint.gperf" +#line 245 "std-name-hint.gperf" {"noboolalpha", "<ios>", cxx98}, -#line 450 "../../src/gcc/cp/std-name-hint.gperf" +#line 450 "std-name-hint.gperf" {"length_error", "<stdexcept>", cxx98}, -#line 560 "../../src/gcc/cp/std-name-hint.gperf" +#line 560 "std-name-hint.gperf" {"in_place", "<utility>", cxx17}, -#line 563 "../../src/gcc/cp/std-name-hint.gperf" +#line 563 "std-name-hint.gperf" {"in_place_t", "<utility>", cxx17}, -#line 384 "../../src/gcc/cp/std-name-hint.gperf" +#line 384 "std-name-hint.gperf" {"emit_on_flush", "<ostream>", cxx20}, -#line 440 "../../src/gcc/cp/std-name-hint.gperf" +#line 440 "std-name-hint.gperf" {"wistringstream", "<sstream>", cxx98}, -#line 327 "../../src/gcc/cp/std-name-hint.gperf" +#line 327 "std-name-hint.gperf" {"static_pointer_cast", "<memory>", cxx11}, -#line 260 "../../src/gcc/cp/std-name-hint.gperf" +#line 260 "std-name-hint.gperf" {"uppercase", "<ios>", cxx98}, -#line 277 "../../src/gcc/cp/std-name-hint.gperf" +#line 277 "std-name-hint.gperf" {"common_iterator", "<iterator>", cxx20}, -#line 117 "../../src/gcc/cp/std-name-hint.gperf" +#line 117 "std-name-hint.gperf" {"swappable_with", "<concepts>", cxx20}, -#line 359 "../../src/gcc/cp/std-name-hint.gperf" +#line 359 "std-name-hint.gperf" {"bad_alloc", "<new>", cxx98}, -#line 391 "../../src/gcc/cp/std-name-hint.gperf" +#line 391 "std-name-hint.gperf" {"wostream", "<ostream>", cxx98}, -#line 348 "../../src/gcc/cp/std-name-hint.gperf" +#line 348 "std-name-hint.gperf" {"lock", "<mutex>", cxx11}, -#line 504 "../../src/gcc/cp/std-name-hint.gperf" +#line 504 "std-name-hint.gperf" {"system_category", "<system_error>", cxx11}, -#line 128 "../../src/gcc/cp/std-name-hint.gperf" +#line 128 "std-name-hint.gperf" {"copyable", "<concepts>", cxx20}, -#line 139 "../../src/gcc/cp/std-name-hint.gperf" +#line 139 "std-name-hint.gperf" {"condition_variable", "<condition_variable>", cxx11}, -#line 240 "../../src/gcc/cp/std-name-hint.gperf" +#line 240 "std-name-hint.gperf" {"fixed", "<ios>", cxx98}, -#line 553 "../../src/gcc/cp/std-name-hint.gperf" +#line 553 "std-name-hint.gperf" {"cmp_greater_equal", "<utility>", cxx20}, -#line 508 "../../src/gcc/cp/std-name-hint.gperf" +#line 508 "std-name-hint.gperf" {"thread", "<thread>", cxx11}, -#line 439 "../../src/gcc/cp/std-name-hint.gperf" +#line 439 "std-name-hint.gperf" {"stringstream", "<sstream>", cxx98}, -#line 58 "../../src/gcc/cp/std-name-hint.gperf" +#line 58 "std-name-hint.gperf" {"chrono::clock_cast", "<chrono>", cxx20}, -#line 579 "../../src/gcc/cp/std-name-hint.gperf" +#line 579 "std-name-hint.gperf" {"unreachable", "<utility>", cxx23}, -#line 428 "../../src/gcc/cp/std-name-hint.gperf" +#line 428 "std-name-hint.gperf" {"wospanstream", "<spanstream>", cxx23}, -#line 330 "../../src/gcc/cp/std-name-hint.gperf" +#line 330 "std-name-hint.gperf" {"unique_ptr", "<memory>", cxx11}, -#line 562 "../../src/gcc/cp/std-name-hint.gperf" +#line 562 "std-name-hint.gperf" {"in_place_index_t", "<utility>", cxx17}, -#line 363 "../../src/gcc/cp/std-name-hint.gperf" +#line 363 "std-name-hint.gperf" {"nothrow", "<new>", cxx98}, -#line 64 "../../src/gcc/cp/std-name-hint.gperf" +#line 64 "std-name-hint.gperf" {"chrono::get_leap_second_info", "<chrono>", cxx20}, -#line 204 "../../src/gcc/cp/std-name-hint.gperf" +#line 204 "std-name-hint.gperf" {"bind", "<functional>", cxx11}, -#line 89 "../../src/gcc/cp/std-name-hint.gperf" +#line 89 "std-name-hint.gperf" {"chrono::time_zone", "<chrono>", cxx20}, -#line 120 "../../src/gcc/cp/std-name-hint.gperf" +#line 120 "std-name-hint.gperf" {"default_initializable", "<concepts>", cxx20}, -#line 145 "../../src/gcc/cp/std-name-hint.gperf" +#line 145 "std-name-hint.gperf" {"uint_fast32_t", "<cstdint>", cxx11}, -#line 87 "../../src/gcc/cp/std-name-hint.gperf" +#line 87 "std-name-hint.gperf" {"chrono::time_point", "<chrono>", cxx11}, -#line 444 "../../src/gcc/cp/std-name-hint.gperf" +#line 444 "std-name-hint.gperf" {"stack", "<stack>", cxx98}, -#line 215 "../../src/gcc/cp/std-name-hint.gperf" +#line 215 "std-name-hint.gperf" {"unwrap_reference", "<functional>", cxx20}, -#line 88 "../../src/gcc/cp/std-name-hint.gperf" +#line 88 "std-name-hint.gperf" {"chrono::time_point_cast", "<chrono>", cxx11}, -#line 223 "../../src/gcc/cp/std-name-hint.gperf" +#line 223 "std-name-hint.gperf" {"generator", "<generator>", cxx23}, -#line 216 "../../src/gcc/cp/std-name-hint.gperf" +#line 216 "std-name-hint.gperf" {"unwrap_reference_t", "<functional>", cxx20}, -#line 382 "../../src/gcc/cp/std-name-hint.gperf" +#line 382 "std-name-hint.gperf" {"optional", "<optional>", cxx17}, -#line 112 "../../src/gcc/cp/std-name-hint.gperf" +#line 112 "std-name-hint.gperf" {"signed_integral", "<concepts>", cxx20}, -#line 456 "../../src/gcc/cp/std-name-hint.gperf" +#line 456 "std-name-hint.gperf" {"underflow_error", "<stdexcept>", cxx98}, -#line 239 "../../src/gcc/cp/std-name-hint.gperf" +#line 239 "std-name-hint.gperf" {"defaultfloat", "<ios>", cxx11}, -#line 146 "../../src/gcc/cp/std-name-hint.gperf" +#line 146 "std-name-hint.gperf" {"uint_fast64_t", "<cstdint>", cxx11}, -#line 129 "../../src/gcc/cp/std-name-hint.gperf" +#line 129 "std-name-hint.gperf" {"semiregular", "<concepts>", cxx20}, -#line 111 "../../src/gcc/cp/std-name-hint.gperf" +#line 111 "std-name-hint.gperf" {"integral", "<concepts>", cxx20}, -#line 243 "../../src/gcc/cp/std-name-hint.gperf" +#line 243 "std-name-hint.gperf" {"internal", "<ios>", cxx98}, -#line 144 "../../src/gcc/cp/std-name-hint.gperf" +#line 144 "std-name-hint.gperf" {"uint_fast16_t", "<cstdint>", cxx11}, -#line 329 "../../src/gcc/cp/std-name-hint.gperf" +#line 329 "std-name-hint.gperf" {"uninitialized_construct_using_allocator", "<memory>", cxx20}, -#line 81 "../../src/gcc/cp/std-name-hint.gperf" +#line 81 "std-name-hint.gperf" {"chrono::remote_version", "<chrono>", cxx20}, -#line 458 "../../src/gcc/cp/std-name-hint.gperf" +#line 458 "std-name-hint.gperf" {"bfloat16_t", "<stdfloat>", cxx23}, -#line 576 "../../src/gcc/cp/std-name-hint.gperf" +#line 576 "std-name-hint.gperf" {"piecewise_construct", "<utility>", cxx11}, -#line 577 "../../src/gcc/cp/std-name-hint.gperf" +#line 577 "std-name-hint.gperf" {"piecewise_construct_t", "<utility>", cxx11}, -#line 278 "../../src/gcc/cp/std-name-hint.gperf" +#line 278 "std-name-hint.gperf" {"const_iterator", "<iterator>", cxx23}, -#line 484 "../../src/gcc/cp/std-name-hint.gperf" +#line 484 "std-name-hint.gperf" {"to_wstring", "<string>", cxx17}, -#line 356 "../../src/gcc/cp/std-name-hint.gperf" +#line 356 "std-name-hint.gperf" {"try_lock", "<mutex>", cxx11}, -#line 561 "../../src/gcc/cp/std-name-hint.gperf" +#line 561 "std-name-hint.gperf" {"in_place_index", "<utility>", cxx17}, -#line 51 "../../src/gcc/cp/std-name-hint.gperf" +#line 51 "std-name-hint.gperf" {"atomic_ref", "<atomic>", cxx20}, -#line 113 "../../src/gcc/cp/std-name-hint.gperf" +#line 113 "std-name-hint.gperf" {"unsigned_integral", "<concepts>", cxx20}, -#line 94 "../../src/gcc/cp/std-name-hint.gperf" +#line 94 "std-name-hint.gperf" {"chrono::years", "<chrono>", cxx20}, -#line 229 "../../src/gcc/cp/std-name-hint.gperf" +#line 229 "std-name-hint.gperf" {"quoted", "<iomanip>", cxx14}, -#line 115 "../../src/gcc/cp/std-name-hint.gperf" +#line 115 "std-name-hint.gperf" {"assignable_from", "<concepts>", cxx20}, -#line 53 "../../src/gcc/cp/std-name-hint.gperf" +#line 53 "std-name-hint.gperf" {"atomic_uintmax_t", "<atomic>", cxx20}, -#line 162 "../../src/gcc/cp/std-name-hint.gperf" +#line 162 "std-name-hint.gperf" {"uncaught_exceptions", "<exception>", cxx17}, -#line 331 "../../src/gcc/cp/std-name-hint.gperf" +#line 331 "std-name-hint.gperf" {"uses_allocator", "<memory>", cxx11}, -#line 472 "../../src/gcc/cp/std-name-hint.gperf" +#line 472 "std-name-hint.gperf" {"basic_string", "<string>", cxx98}, -#line 311 "../../src/gcc/cp/std-name-hint.gperf" +#line 311 "std-name-hint.gperf" {"default_delete", "<memory>", cxx11}, -#line 164 "../../src/gcc/cp/std-name-hint.gperf" +#line 164 "std-name-hint.gperf" {"expected", "<expected>", cxx23}, -#line 313 "../../src/gcc/cp/std-name-hint.gperf" +#line 313 "std-name-hint.gperf" {"destroy_at", "<memory>", cxx20}, -#line 536 "../../src/gcc/cp/std-name-hint.gperf" +#line 536 "std-name-hint.gperf" {"type_identity_t", "<type_traits>", cxx20}, -#line 63 "../../src/gcc/cp/std-name-hint.gperf" +#line 63 "std-name-hint.gperf" {"chrono::from_stream", "<chrono>", cxx20}, -#line 427 "../../src/gcc/cp/std-name-hint.gperf" +#line 427 "std-name-hint.gperf" {"wispanstream", "<spanstream>", cxx23}, -#line 52 "../../src/gcc/cp/std-name-hint.gperf" +#line 52 "std-name-hint.gperf" {"atomic_signed_lock_free", "<atomic>", cxx11}, -#line 266 "../../src/gcc/cp/std-name-hint.gperf" +#line 266 "std-name-hint.gperf" {"wcerr", "<iostream>", cxx98}, -#line 511 "../../src/gcc/cp/std-name-hint.gperf" +#line 511 "std-name-hint.gperf" {"forward_as_tuple", "<tuple>", cxx11}, -#line 187 "../../src/gcc/cp/std-name-hint.gperf" +#line 187 "std-name-hint.gperf" {"vformat", "<format>", cxx20}, -#line 332 "../../src/gcc/cp/std-name-hint.gperf" +#line 332 "std-name-hint.gperf" {"uses_allocator_construction_args", "<memory>", cxx20}, -#line 269 "../../src/gcc/cp/std-name-hint.gperf" +#line 269 "std-name-hint.gperf" {"wcout", "<iostream>", cxx98}, -#line 314 "../../src/gcc/cp/std-name-hint.gperf" +#line 314 "std-name-hint.gperf" {"destroy_n", "<memory>", cxx20}, -#line 226 "../../src/gcc/cp/std-name-hint.gperf" +#line 226 "std-name-hint.gperf" {"get_time", "<iomanip>", cxx11}, -#line 555 "../../src/gcc/cp/std-name-hint.gperf" +#line 555 "std-name-hint.gperf" {"cmp_less_equal", "<utility>", cxx20}, -#line 80 "../../src/gcc/cp/std-name-hint.gperf" +#line 80 "std-name-hint.gperf" {"chrono::reload_tzdb", "<chrono>", cxx20}, -#line 523 "../../src/gcc/cp/std-name-hint.gperf" +#line 523 "std-name-hint.gperf" {"conjunction", "<type_traits>", cxx17}, -#line 75 "../../src/gcc/cp/std-name-hint.gperf" +#line 75 "std-name-hint.gperf" {"chrono::milliseconds", "<chrono>", cxx11}, -#line 59 "../../src/gcc/cp/std-name-hint.gperf" +#line 59 "std-name-hint.gperf" {"chrono::days", "<chrono>", cxx20}, -#line 474 "../../src/gcc/cp/std-name-hint.gperf" +#line 474 "std-name-hint.gperf" {"stod", "<string>", cxx11}, -#line 478 "../../src/gcc/cp/std-name-hint.gperf" +#line 478 "std-name-hint.gperf" {"stold", "<string>", cxx11}, -#line 267 "../../src/gcc/cp/std-name-hint.gperf" +#line 267 "std-name-hint.gperf" {"wcin", "<iostream>", cxx98}, -#line 529 "../../src/gcc/cp/std-name-hint.gperf" +#line 529 "std-name-hint.gperf" {"invoke_result", "<type_traits>", cxx17}, -#line 530 "../../src/gcc/cp/std-name-hint.gperf" +#line 530 "std-name-hint.gperf" {"invoke_result_t", "<type_traits>", cxx17}, -#line 50 "../../src/gcc/cp/std-name-hint.gperf" +#line 50 "std-name-hint.gperf" {"atomic", "<atomic>", cxx11}, -#line 71 "../../src/gcc/cp/std-name-hint.gperf" +#line 71 "std-name-hint.gperf" {"chrono::leap_second_info", "<chrono>", cxx20}, -#line 238 "../../src/gcc/cp/std-name-hint.gperf" +#line 238 "std-name-hint.gperf" {"dec", "<ios>", cxx98}, -#line 157 "../../src/gcc/cp/std-name-hint.gperf" +#line 157 "std-name-hint.gperf" {"current_exception", "<exception>", cxx11}, -#line 119 "../../src/gcc/cp/std-name-hint.gperf" +#line 119 "std-name-hint.gperf" {"constructible_from", "<concepts>", cxx20}, -#line 100 "../../src/gcc/cp/std-name-hint.gperf" +#line 100 "std-name-hint.gperf" {"weak_equality", "<compare>", cxx20}, -#line 333 "../../src/gcc/cp/std-name-hint.gperf" +#line 333 "std-name-hint.gperf" {"uses_allocator_v", "<memory>", cxx17}, -#line 69 "../../src/gcc/cp/std-name-hint.gperf" +#line 69 "std-name-hint.gperf" {"chrono::hours", "<chrono>", cxx11}, -#line 109 "../../src/gcc/cp/std-name-hint.gperf" +#line 109 "std-name-hint.gperf" {"common_reference_with", "<concepts>", cxx20}, -#line 483 "../../src/gcc/cp/std-name-hint.gperf" +#line 483 "std-name-hint.gperf" {"to_string", "<string>", cxx17}, -#line 524 "../../src/gcc/cp/std-name-hint.gperf" +#line 524 "std-name-hint.gperf" {"conjunction_v", "<type_traits>", cxx17}, -#line 578 "../../src/gcc/cp/std-name-hint.gperf" +#line 578 "std-name-hint.gperf" {"to_underlying", "<utility>", cxx23}, -#line 402 "../../src/gcc/cp/std-name-hint.gperf" +#line 402 "std-name-hint.gperf" {"scoped_allocator_adaptor", "<scoped_allocator>", cxx11}, -#line 310 "../../src/gcc/cp/std-name-hint.gperf" +#line 310 "std-name-hint.gperf" {"construct_at", "<memory>", cxx20}, -#line 564 "../../src/gcc/cp/std-name-hint.gperf" +#line 564 "std-name-hint.gperf" {"in_place_type", "<utility>", cxx17}, -#line 320 "../../src/gcc/cp/std-name-hint.gperf" +#line 320 "std-name-hint.gperf" {"make_shared_for_overwrite", "<memory>", cxx20}, -#line 166 "../../src/gcc/cp/std-name-hint.gperf" +#line 166 "std-name-hint.gperf" {"basic_format_arg", "<format>", cxx20}, -#line 73 "../../src/gcc/cp/std-name-hint.gperf" +#line 73 "std-name-hint.gperf" {"chrono::locate_zone", "<chrono>", cxx20}, -#line 72 "../../src/gcc/cp/std-name-hint.gperf" +#line 72 "std-name-hint.gperf" {"chrono::local_t", "<chrono>", cxx20}, -#line 170 "../../src/gcc/cp/std-name-hint.gperf" +#line 170 "std-name-hint.gperf" {"basic_format_string", "<format>", cxx20}, -#line 565 "../../src/gcc/cp/std-name-hint.gperf" +#line 565 "std-name-hint.gperf" {"in_place_type_t", "<utility>", cxx17}, -#line 279 "../../src/gcc/cp/std-name-hint.gperf" +#line 279 "std-name-hint.gperf" {"counted_iterator", "<iterator>", cxx20}, -#line 448 "../../src/gcc/cp/std-name-hint.gperf" +#line 448 "std-name-hint.gperf" {"domain_error", "<stdexcept>", cxx98}, -#line 84 "../../src/gcc/cp/std-name-hint.gperf" +#line 84 "std-name-hint.gperf" {"chrono::steady_clock", "<chrono>", cxx11}, -#line 188 "../../src/gcc/cp/std-name-hint.gperf" +#line 188 "std-name-hint.gperf" {"vformat_to", "<format>", cxx20}, -#line 54 "../../src/gcc/cp/std-name-hint.gperf" +#line 54 "std-name-hint.gperf" {"atomic_unsigned_lock_free", "<atomic>", cxx11}, -#line 147 "../../src/gcc/cp/std-name-hint.gperf" +#line 147 "std-name-hint.gperf" {"uint_fast8_t", "<cstdint>", cxx11}, -#line 83 "../../src/gcc/cp/std-name-hint.gperf" +#line 83 "std-name-hint.gperf" {"chrono::seconds", "<chrono>", cxx11}, -#line 74 "../../src/gcc/cp/std-name-hint.gperf" +#line 74 "std-name-hint.gperf" {"chrono::microseconds", "<chrono>", cxx11}, -#line 67 "../../src/gcc/cp/std-name-hint.gperf" +#line 67 "std-name-hint.gperf" {"chrono::gps_clock", "<chrono>", cxx20}, -#line 85 "../../src/gcc/cp/std-name-hint.gperf" +#line 85 "std-name-hint.gperf" {"chrono::system_clock", "<chrono>", cxx11}, -#line 140 "../../src/gcc/cp/std-name-hint.gperf" +#line 140 "std-name-hint.gperf" {"condition_variable_any", "<condition_variable>", cxx11}, -#line 214 "../../src/gcc/cp/std-name-hint.gperf" +#line 214 "std-name-hint.gperf" {"unwrap_ref_decay_t", "<functional>", cxx20}, -#line 136 "../../src/gcc/cp/std-name-hint.gperf" +#line 136 "std-name-hint.gperf" {"strict_weak_order", "<concepts>", cxx20}, -#line 437 "../../src/gcc/cp/std-name-hint.gperf" +#line 437 "std-name-hint.gperf" {"ostringstream", "<sstream>", cxx98}, -#line 317 "../../src/gcc/cp/std-name-hint.gperf" +#line 317 "std-name-hint.gperf" {"get_deleter", "<memory>", cxx11}, -#line 367 "../../src/gcc/cp/std-name-hint.gperf" +#line 367 "std-name-hint.gperf" {"numbers::egamma_v", "<numbers>", cxx20}, -#line 566 "../../src/gcc/cp/std-name-hint.gperf" +#line 566 "std-name-hint.gperf" {"in_range", "<utility>", cxx20}, -#line 558 "../../src/gcc/cp/std-name-hint.gperf" +#line 558 "std-name-hint.gperf" {"exchange", "<utility>", cxx14}, -#line 551 "../../src/gcc/cp/std-name-hint.gperf" +#line 551 "std-name-hint.gperf" {"cmp_equal", "<utility>", cxx20}, -#line 556 "../../src/gcc/cp/std-name-hint.gperf" +#line 556 "std-name-hint.gperf" {"cmp_not_equal", "<utility>", cxx20}, -#line 435 "../../src/gcc/cp/std-name-hint.gperf" +#line 435 "std-name-hint.gperf" {"istringstream", "<sstream>", cxx98}, -#line 436 "../../src/gcc/cp/std-name-hint.gperf" +#line 436 "std-name-hint.gperf" {"istringstream", "<sstream>", cxx98}, -#line 442 "../../src/gcc/cp/std-name-hint.gperf" +#line 442 "std-name-hint.gperf" {"wstringbuf", "<sstream>", cxx98}, -#line 225 "../../src/gcc/cp/std-name-hint.gperf" +#line 225 "std-name-hint.gperf" {"get_money", "<iomanip>", cxx11}, -#line 191 "../../src/gcc/cp/std-name-hint.gperf" +#line 191 "std-name-hint.gperf" {"wformat_parse_context", "<format>", cxx20}, -#line 482 "../../src/gcc/cp/std-name-hint.gperf" +#line 482 "std-name-hint.gperf" {"string", "<string>", cxx98}, -#line 280 "../../src/gcc/cp/std-name-hint.gperf" +#line 280 "std-name-hint.gperf" {"distance", "<iterator>", cxx98}, -#line 557 "../../src/gcc/cp/std-name-hint.gperf" +#line 557 "std-name-hint.gperf" {"declval", "<utility>", cxx11}, -#line 254 "../../src/gcc/cp/std-name-hint.gperf" +#line 254 "std-name-hint.gperf" {"scientific", "<ios>", cxx98}, -#line 98 "../../src/gcc/cp/std-name-hint.gperf" +#line 98 "std-name-hint.gperf" {"strong_equality", "<compare>", cxx20}, -#line 487 "../../src/gcc/cp/std-name-hint.gperf" +#line 487 "std-name-hint.gperf" {"u8string", "<string>", cxx20}, -#line 190 "../../src/gcc/cp/std-name-hint.gperf" +#line 190 "std-name-hint.gperf" {"wformat_context", "<format>", cxx20}, -#line 312 "../../src/gcc/cp/std-name-hint.gperf" +#line 312 "std-name-hint.gperf" {"destroy", "<memory>", cxx20}, -#line 559 "../../src/gcc/cp/std-name-hint.gperf" +#line 559 "std-name-hint.gperf" {"forward", "<utility>", cxx11}, -#line 535 "../../src/gcc/cp/std-name-hint.gperf" +#line 535 "std-name-hint.gperf" {"type_identity", "<type_traits>", cxx20}, -#line 464 "../../src/gcc/cp/std-name-hint.gperf" +#line 464 "std-name-hint.gperf" {"stop_callback", "<stop_token>", cxx20}, -#line 86 "../../src/gcc/cp/std-name-hint.gperf" +#line 86 "std-name-hint.gperf" {"chrono::tai_clock", "<chrono>", cxx20}, -#line 91 "../../src/gcc/cp/std-name-hint.gperf" +#line 91 "std-name-hint.gperf" {"chrono::tzdb_list", "<chrono>", cxx20}, -#line 301 "../../src/gcc/cp/std-name-hint.gperf" +#line 301 "std-name-hint.gperf" {"allocate_shared", "<memory>", cxx11}, -#line 90 "../../src/gcc/cp/std-name-hint.gperf" +#line 90 "std-name-hint.gperf" {"chrono::tzdb", "<chrono>", cxx20}, -#line 126 "../../src/gcc/cp/std-name-hint.gperf" +#line 126 "std-name-hint.gperf" {"totally_ordered_with", "<concepts>", cxx20}, -#line 525 "../../src/gcc/cp/std-name-hint.gperf" +#line 525 "std-name-hint.gperf" {"disjunction", "<type_traits>", cxx17}, -#line 110 "../../src/gcc/cp/std-name-hint.gperf" +#line 110 "std-name-hint.gperf" {"common_with", "<concepts>", cxx20}, -#line 202 "../../src/gcc/cp/std-name-hint.gperf" +#line 202 "std-name-hint.gperf" {"ofstream", "<fstream>", cxx98}, -#line 349 "../../src/gcc/cp/std-name-hint.gperf" +#line 349 "std-name-hint.gperf" {"lock_guard", "<mutex>", cxx11}, -#line 118 "../../src/gcc/cp/std-name-hint.gperf" +#line 118 "std-name-hint.gperf" {"destructible", "<concepts>", cxx20}, -#line 176 "../../src/gcc/cp/std-name-hint.gperf" +#line 176 "std-name-hint.gperf" {"format_string", "<format>", cxx20}, -#line 410 "../../src/gcc/cp/std-name-hint.gperf" +#line 410 "std-name-hint.gperf" {"shared_lock", "<shared_mutex>", cxx14}, -#line 315 "../../src/gcc/cp/std-name-hint.gperf" +#line 315 "std-name-hint.gperf" {"dynamic_pointer_cast", "<memory>", cxx11}, -#line 101 "../../src/gcc/cp/std-name-hint.gperf" +#line 101 "std-name-hint.gperf" {"weak_ordering", "<compare>", cxx20}, -#line 201 "../../src/gcc/cp/std-name-hint.gperf" +#line 201 "std-name-hint.gperf" {"ifstream", "<fstream>", cxx98}, -#line 452 "../../src/gcc/cp/std-name-hint.gperf" +#line 452 "std-name-hint.gperf" {"out_of_range", "<stdexcept>", cxx98}, -#line 264 "../../src/gcc/cp/std-name-hint.gperf" +#line 264 "std-name-hint.gperf" {"clog", "<iostream>", cxx98}, -#line 62 "../../src/gcc/cp/std-name-hint.gperf" +#line 62 "std-name-hint.gperf" {"chrono::file_clock", "<chrono>", cxx20}, -#line 526 "../../src/gcc/cp/std-name-hint.gperf" +#line 526 "std-name-hint.gperf" {"disjunction_v", "<type_traits>", cxx17}, -#line 485 "../../src/gcc/cp/std-name-hint.gperf" +#line 485 "std-name-hint.gperf" {"u16string", "<string>", cxx11}, -#line 351 "../../src/gcc/cp/std-name-hint.gperf" +#line 351 "std-name-hint.gperf" {"once_flag", "<mutex>", cxx11}, -#line 491 "../../src/gcc/cp/std-name-hint.gperf" +#line 491 "std-name-hint.gperf" {"string_view", "<string_view>", cxx17}, -#line 304 "../../src/gcc/cp/std-name-hint.gperf" +#line 304 "std-name-hint.gperf" {"allocator_arg", "<memory>", cxx11}, -#line 486 "../../src/gcc/cp/std-name-hint.gperf" +#line 486 "std-name-hint.gperf" {"u32string", "<string>", cxx11}, -#line 213 "../../src/gcc/cp/std-name-hint.gperf" +#line 213 "std-name-hint.gperf" {"unwrap_ref_decay", "<functional>", cxx20}, -#line 70 "../../src/gcc/cp/std-name-hint.gperf" +#line 70 "std-name-hint.gperf" {"chrono::leap_second", "<chrono>", cxx20}, -#line 92 "../../src/gcc/cp/std-name-hint.gperf" +#line 92 "std-name-hint.gperf" {"chrono::utc_clock", "<chrono>", cxx20}, -#line 107 "../../src/gcc/cp/std-name-hint.gperf" +#line 107 "std-name-hint.gperf" {"derived_from", "<concepts>", cxx20}, -#line 99 "../../src/gcc/cp/std-name-hint.gperf" +#line 99 "std-name-hint.gperf" {"strong_ordering", "<compare>", cxx20}, -#line 497 "../../src/gcc/cp/std-name-hint.gperf" +#line 497 "std-name-hint.gperf" {"generic_category", "<system_error>", cxx11}, -#line 125 "../../src/gcc/cp/std-name-hint.gperf" +#line 125 "std-name-hint.gperf" {"totally_ordered", "<concepts>", cxx20}, -#line 494 "../../src/gcc/cp/std-name-hint.gperf" +#line 494 "std-name-hint.gperf" {"error_category", "<system_error>", cxx11}, -#line 82 "../../src/gcc/cp/std-name-hint.gperf" +#line 82 "std-name-hint.gperf" {"chrono::round", "<chrono>", cxx17}, -#line 357 "../../src/gcc/cp/std-name-hint.gperf" +#line 357 "std-name-hint.gperf" {"unique_lock", "<mutex>", cxx11}, -#line 319 "../../src/gcc/cp/std-name-hint.gperf" +#line 319 "std-name-hint.gperf" {"make_shared", "<memory>", cxx11}, -#line 507 "../../src/gcc/cp/std-name-hint.gperf" +#line 507 "std-name-hint.gperf" {"this_thread", "<thread>", cxx11}, -#line 449 "../../src/gcc/cp/std-name-hint.gperf" +#line 449 "std-name-hint.gperf" {"invalid_argument", "<stdexcept>", cxx98}, -#line 354 "../../src/gcc/cp/std-name-hint.gperf" +#line 354 "std-name-hint.gperf" {"scoped_lock", "<mutex>", cxx17}, -#line 268 "../../src/gcc/cp/std-name-hint.gperf" +#line 268 "std-name-hint.gperf" {"wclog", "<iostream>", cxx98}, -#line 506 "../../src/gcc/cp/std-name-hint.gperf" +#line 506 "std-name-hint.gperf" {"jthread", "<thread>", cxx20}, -#line 97 "../../src/gcc/cp/std-name-hint.gperf" +#line 97 "std-name-hint.gperf" {"partial_ordering", "<compare>", cxx20}, -#line 68 "../../src/gcc/cp/std-name-hint.gperf" +#line 68 "std-name-hint.gperf" {"chrono::high_resolution_clock", "<chrono>", cxx11}, -#line 307 "../../src/gcc/cp/std-name-hint.gperf" +#line 307 "std-name-hint.gperf" {"assume_aligned", "<memory>", cxx20}, -#line 488 "../../src/gcc/cp/std-name-hint.gperf" +#line 488 "std-name-hint.gperf" {"wstring", "<string>", cxx98}, -#line 192 "../../src/gcc/cp/std-name-hint.gperf" +#line 192 "std-name-hint.gperf" {"wformat_string", "<format>", cxx20}, -#line 189 "../../src/gcc/cp/std-name-hint.gperf" +#line 189 "std-name-hint.gperf" {"wformat_args", "<format>", cxx20} }; diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 5863b68..accce0e 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -488,6 +488,7 @@ builtin_valid_in_constant_expr_p (const_tree decl) case CP_BUILT_IN_SOURCE_LOCATION: case CP_BUILT_IN_IS_CORRESPONDING_MEMBER: case CP_BUILT_IN_IS_POINTER_INTERCONVERTIBLE_WITH_CLASS: + case CP_BUILT_IN_EH_PTR_ADJUST_REF: return true; default: break; diff --git a/gcc/cp/typeck.cc b/gcc/cp/typeck.cc index 88f8f34..0bf5ae4 100644 --- a/gcc/cp/typeck.cc +++ b/gcc/cp/typeck.cc @@ -156,7 +156,7 @@ complete_type_or_maybe_complain (tree type, tree value, tsubst_flags_t complain) { if (complain & tf_error) cxx_incomplete_type_diagnostic (value, type, DK_ERROR); - note_failed_type_completion_for_satisfaction (type); + note_failed_type_completion (type, complain); return NULL_TREE; } else @@ -1372,17 +1372,12 @@ cxx_safe_arg_type_equiv_p (tree t1, tree t2) && TYPE_PTR_P (t2)) return true; - /* The signedness of the parameter matters only when an integral - type smaller than int is promoted to int, otherwise only the - precision of the parameter matters. - This check should make sure that the callee does not see - undefined values in argument registers. */ + /* Only the precision of the parameter matters. This check should + make sure that the callee does not see undefined values in argument + registers. */ if (INTEGRAL_TYPE_P (t1) && INTEGRAL_TYPE_P (t2) - && TYPE_PRECISION (t1) == TYPE_PRECISION (t2) - && (TYPE_UNSIGNED (t1) == TYPE_UNSIGNED (t2) - || !targetm.calls.promote_prototypes (NULL_TREE) - || TYPE_PRECISION (t1) >= TYPE_PRECISION (integer_type_node))) + && TYPE_PRECISION (t1) == TYPE_PRECISION (t2)) return true; return same_type_p (t1, t2); @@ -2089,7 +2084,14 @@ cxx_sizeof_or_alignof_type (location_t loc, tree type, enum tree_code op, bool dependent_p = dependent_type_p (type); if (!dependent_p) - complete_type (type); + { + complete_type (type); + if (!COMPLETE_TYPE_P (type)) + /* Call this here because the incompleteness diagnostic comes from + c_sizeof_or_alignof_type instead of + complete_type_or_maybe_complain. */ + note_failed_type_completion (type, complain); + } if (dependent_p /* VLA types will have a non-constant size. In the body of an uninstantiated template, we don't need to try to compute the @@ -2111,7 +2113,7 @@ cxx_sizeof_or_alignof_type (location_t loc, tree type, enum tree_code op, return c_sizeof_or_alignof_type (loc, complete_type (type), op == SIZEOF_EXPR, std_alignof, - complain); + complain & (tf_warning_or_error)); } /* Return the size of the type, without producing any warnings for @@ -3999,13 +4001,61 @@ cp_build_array_ref (location_t loc, tree array, tree idx, } case COND_EXPR: - ret = build_conditional_expr - (loc, TREE_OPERAND (array, 0), - cp_build_array_ref (loc, TREE_OPERAND (array, 1), idx, - complain), - cp_build_array_ref (loc, TREE_OPERAND (array, 2), idx, - complain), - complain); + tree op0, op1, op2; + op0 = TREE_OPERAND (array, 0); + op1 = TREE_OPERAND (array, 1); + op2 = TREE_OPERAND (array, 2); + if (TREE_SIDE_EFFECTS (idx) || !tree_invariant_p (idx)) + { + /* If idx could possibly have some SAVE_EXPRs, turning + (op0 ? op1 : op2)[idx] into + op0 ? op1[idx] : op2[idx] can lead into temporaries + initialized in one conditional path and uninitialized + uses of them in the other path. + And if idx is a really large expression, evaluating it + twice is also not optimal. + On the other side, op0 must be sequenced before evaluation + of op1 and op2 and for C++17 op0, op1 and op2 must be + sequenced before idx. + If idx is INTEGER_CST, we can just do the optimization + without any SAVE_EXPRs, if op1 and op2 are both ARRAY_TYPE + VAR_DECLs or COMPONENT_REFs thereof (so their address + is constant or relative to frame), optimize into + (SAVE_EXPR <op0>, SAVE_EXPR <idx>, SAVE_EXPR <op0>) + ? op1[SAVE_EXPR <idx>] : op2[SAVE_EXPR <idx>] + Otherwise avoid this optimization. */ + if (flag_strong_eval_order == 2) + { + if (TREE_CODE (TREE_TYPE (array)) == ARRAY_TYPE) + { + if (!address_invariant_p (op1) || !address_invariant_p (op2)) + { + /* Force default conversion on array if + we can't optimize this and array is ARRAY_TYPE + COND_EXPR, we can't leave COND_EXPRs with + ARRAY_TYPE in the IL. */ + array = cp_default_conversion (array, complain); + if (error_operand_p (array)) + return error_mark_node; + break; + } + } + else if (!POINTER_TYPE_P (TREE_TYPE (array)) + || !tree_invariant_p (op1) + || !tree_invariant_p (op2)) + break; + } + if (TREE_SIDE_EFFECTS (idx)) + { + idx = save_expr (idx); + op0 = save_expr (op0); + tree tem = build_compound_expr (loc, op0, idx); + op0 = build_compound_expr (loc, tem, op0); + } + } + op1 = cp_build_array_ref (loc, op1, idx, complain); + op2 = cp_build_array_ref (loc, op2, idx, complain); + ret = build_conditional_expr (loc, op0, op1, op2, complain); protected_set_expr_location (ret, loc); return ret; @@ -11468,6 +11518,12 @@ check_return_expr (tree retval, bool *no_warning, bool *dangling) && call_from_lambda_thunk_p (retval)) converted = true; + /* Don't check copy-initialization for NRV in a coroutine ramp; we + implement this case as NRV, but it's specified as directly + initializing the return value from get_return_object(). */ + if (DECL_RAMP_P (current_function_decl) && named_return_value_okay_p) + converted = true; + /* First convert the value to the function's return type, then to the type of return value's location to handle the case that functype is smaller than the valtype. */ |