aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-03-11 16:12:22 -0800
committerIan Lance Taylor <iant@golang.org>2021-03-11 16:12:22 -0800
commitbc636c218f2b28da06cd1404d5b35d1f8cc43fd1 (patch)
tree764937d8460563db6132d7c75e19b95ef3ea6ea8 /gcc/cp
parent89d7be42db00cd0953e7d4584877cf50a56ed046 (diff)
parent7ad5a72c8bc6aa71a0d195ddfa207db01265fe0b (diff)
downloadgcc-bc636c218f2b28da06cd1404d5b35d1f8cc43fd1.zip
gcc-bc636c218f2b28da06cd1404d5b35d1f8cc43fd1.tar.gz
gcc-bc636c218f2b28da06cd1404d5b35d1f8cc43fd1.tar.bz2
Merge from trunk revision 7ad5a72c8bc6aa71a0d195ddfa207db01265fe0b.
Diffstat (limited to 'gcc/cp')
-rw-r--r--gcc/cp/ChangeLog747
-rw-r--r--gcc/cp/call.c126
-rw-r--r--gcc/cp/class.c67
-rw-r--r--gcc/cp/constexpr.c99
-rw-r--r--gcc/cp/constraint.cc906
-rw-r--r--gcc/cp/coroutines.cc473
-rw-r--r--gcc/cp/cp-gimplify.c9
-rw-r--r--gcc/cp/cp-tree.h77
-rw-r--r--gcc/cp/cvt.c14
-rw-r--r--gcc/cp/decl.c49
-rw-r--r--gcc/cp/decl2.c3
-rw-r--r--gcc/cp/error.c4
-rw-r--r--gcc/cp/init.c14
-rw-r--r--gcc/cp/lambda.c3
-rw-r--r--gcc/cp/lex.c4
-rw-r--r--gcc/cp/mapper-client.cc2
-rw-r--r--gcc/cp/module.cc1729
-rw-r--r--gcc/cp/name-lookup.c361
-rw-r--r--gcc/cp/name-lookup.h21
-rw-r--r--gcc/cp/parser.c151
-rw-r--r--gcc/cp/pt.c408
-rw-r--r--gcc/cp/ptree.c3
-rw-r--r--gcc/cp/rtti.c24
-rw-r--r--gcc/cp/tree.c3
-rw-r--r--gcc/cp/type-utils.h23
-rw-r--r--gcc/cp/typeck.c14
26 files changed, 3220 insertions, 2114 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index 46cce66..06c6dfa 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,750 @@
+2021-03-10 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99423
+ * module.cc (post_load_processing): Assert not gcable.
+ (laxy_load_pendings): Extend no-gc region around
+ post_load_processing.
+
+2021-03-10 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99508
+ * decl.c (make_rtl_for_nonlocal_decl): Propagate local-extern's
+ assembler name to the ns alias.
+
+2021-03-09 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/99459
+ * coroutines.cc (build_co_await): Look through NOP_EXPRs in
+ build_special_member_call return value to find the CALL_EXPR.
+ Simplify.
+
+2021-03-09 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99472
+ * parser.c (cp_parser_diagnose_invalid_type_name): Clarify
+ that C++20 does not yet imply modules.
+
+2021-03-08 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99436
+ * name-lookup.c (get_cxx_dialect_name): Add cxx23.
+
+2021-03-08 Nathan Sidwell <nathan@acm.org>
+
+ * lex.c (module_token_filter::resume): Ignore module-decls inside
+ header-unit.
+ * parser.c (cp_parser_module_declaration): Reject in header-unit.
+
+2021-03-08 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99285
+ * cp-tree.h (match_mergeable_specialization)
+ (add_mergeable_specialization): Adjust parms.
+ * module.cc (trees_in::decl_value): Adjust
+ add_mergeable_specialization calls.
+ (trees_out::key_mergeable): Adjust match_mergeable_specialization
+ calls.
+ (specialization_add): Likewise.
+ * pt.c (match_mergeable_specialization): Do not insert.
+ (add_mergeable_specialization): Add to hash table here.
+
+2021-03-06 Patrick Palka <ppalka@redhat.com>
+ Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/99287
+ * constexpr.c (cxx_eval_increment_expression): Pass lval when
+ evaluating the MODIFY_EXPR, and update 'mod' with the result of
+ this evaluation. Check *non_constant_p afterwards. For prefix
+ ops, just return 'mod'.
+
+2021-03-06 Patrick Palka <ppalka@redhat.com>
+ Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/96330
+ * pt.c (tsubst_copy) <case TEMPLATE_ID_EXPR>: Rename local
+ variable 'fn' to 'tmpl'. Handle a variable template-id by
+ calling lookup_template_variable.
+
+2021-03-06 Patrick Palka <ppalka@redhat.com>
+
+ PR c++/99365
+ * pt.c (unify) <case TEMPLATE_TYPE_PARM>: Pass targs as
+ outer_targs to do_auto_deduction.
+ (placeholder_type_constraint_dependent_p): Define.
+ (do_auto_deduction): When processing_template_decl != 0
+ and context is adc_unify and we have constraints, pretend the
+ constraints are satisfied instead of punting. Otherwise don't
+ punt unless placeholder_type_constraint_dependent_p holds.
+ Add some clarifying sanity checks. Add a hack to add missing
+ outermost template levels to outer_args before checking
+ satisfaction. Don't substitute outer_targs into type if it's
+ already been done.
+
+2021-03-05 Marek Polacek <polacek@redhat.com>
+
+ PR c++/99374
+ * call.c (standard_conversion): When converting pointers to
+ member, don't return NULL when the bases are equivalent but
+ incomplete.
+
+2021-03-05 Marek Polacek <polacek@redhat.com>
+
+ PR c++/99120
+ * name-lookup.c (check_local_shadow): Check if the type of decl
+ is non-null before checking TYPE_PTR*.
+
+2021-03-05 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99245
+ * module.cc (module_state::write_cluster): Relax binding assert.
+
+2021-03-05 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99377
+ * pt.c (instantiate_decl): Call set_instantiating_module.
+
+2021-03-05 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/98118
+ * coroutines.cc (build_co_await): Use type_build_ctor_call()
+ to determine cases when a CTOR needs to be built.
+ (flatten_await_stmt): Likewise.
+ (morph_fn_to_coro): Likewise.
+
+2021-03-05 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/95616
+ * coroutines.cc (coro_diagnose_throwing_fn): New helper.
+ (coro_diagnose_throwing_final_aw_expr): New helper.
+ (build_co_await): Diagnose throwing final await expression
+ components.
+ (build_init_or_final_await): Diagnose a throwing promise
+ final_suspend() call.
+
+2021-03-05 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/95615
+ * coroutines.cc (struct param_info): Track parameter copies that need
+ a DTOR.
+ (coro_get_frame_dtor): New helper function factored from build_actor().
+ (build_actor_fn): Use coro_get_frame_dtor().
+ (morph_fn_to_coro): Track parameters that need DTORs on exception,
+ likewise the frame promise and the return object. On exception, run the
+ DTORs for these, destroy the frame and then rethrow the exception.
+
+2021-03-05 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99389
+ * pt.c (instantiate_class_template_1): Set instantiating module
+ here.
+
+2021-03-05 Tobias Burnus <tobias@codesourcery.com>
+
+ PR c/99137
+ * parser.c (cp_parser_oacc_clause_async): Reject comma expressions.
+
+2021-03-04 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/88146
+ PR c++/99362
+ * cvt.c (convert_to_void): Revert 2019-10-17 changes. Clarify
+ comment.
+
+2021-03-04 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99170
+ * module.cc (class uintset): Delete.
+ (typedef attached_map_t): A hash map.
+ (attached_table): Use attached_map_t. Adjust uses ...
+ (trees_out::decl_value, trees_in::decl_value): ... here ...
+ (trees_out::key_mergeable): ... here ...
+ (trees_in::key_mergeable): ... here ...
+ (maybe_attach_decl): ... here ...
+ (direct_import): ... and here.
+
+2021-03-04 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99170
+ * cp-tree.h
+ * lex.c (cxx_dup_lang_specific_decl): Adjust for module_attached_p
+ rename.
+ * module.cc (class pending_key): New.
+ (default_hash_traits<pending_key>): New specialization.
+ (pending_map_t): New typedef.
+ (pending_table): Replace old table.
+ (trees_out::lang_decl_bools): Adjust.
+ (trees_in::lang_decl_bools): Adjust.
+ (trees_in::install_entity): Drop pending member and specialization
+ handling.
+ (find_pending_key): New.
+ (depset::hash::fiund_dependencies): Use it.
+ (pendset_lazy_load): Delete.
+ (module_state::write_cluster): Don't count pendings here. Bye
+ Duff's device-like thing.
+ (module_state::write_pendings): Reimplement.
+ (module_state::read_pendings): Reimplement.
+ (lazy_specializations_p): Delete.
+ (module_state::write): Adjust write_pendings call.
+ (lazy_load_pendings): New.
+ (lazy_load_specializations): Delete.
+ (lazy_load_members): Delete.
+ (init_modules): Adjust.
+ * name-lookup.c (maybe_lazily_declare): Call lazy_load_pendings
+ not lazy_load_members.
+ (note_pending_specializations): Delete.
+ (load_pending_specializations): Delete.
+ * name-lookup.h (BINDING_VECTR_PENDING_SPECIALIZATIONS_P): Delete.
+ (BINDING_VECTOR_PENDING_MEMBERS_P): Delete.
+ (BINDING_VECTR_PENDING_MEMBERS_P): Delete.
+ (note_pending_specializations): Delete.
+ (load_pending_specializations): Delete.
+ * pt.c (lookup_template_class_1): Call lazy_load_pendings not
+ lazy_load_specializations.
+ (instantiate_template_class_1): Likewise.
+ (instantiate_decl): Call lazy_load_pendings.
+ * typeck.c (complete_type): Likewise.
+
+2021-03-03 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99170
+ * module.cc (post_load_decls): New.
+ (lazy_snum, recursive_lazy): Move earlier.
+ (module_state::read_cluster): Push cloning onto post_load_decls.
+ (post_load_processing): New. Do the cloning here.
+ (module_state::read_inits): Call post_load_processing.
+ (module_state::read_language): Likewise.
+ (lazy_load_binding, lazy_load_specializations): Likewise
+ (lazy_load_members): Likewise
+
+2021-03-03 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99170
+ * module.cc (trees_out::decl_value): Stream specialization keys
+ after decl.
+ (trees_in::decl_value): Stream them back and insert after
+ completing the decl.
+ (trees_out::key_mergeable): Drop some streaming here ...
+ (trees_in::key_mergeable): ... and here. Don't insert into
+ specialization tables.
+
+2021-03-03 Patrick Palka <ppalka@redhat.com>
+
+ * constraint.cc (struct sat_info): Document the different
+ meanings of noisy() and diagnose_unsatisfaction_p() during
+ satisfaction and requires-expression evaluation.
+ (tsubst_valid_expression_requirement): Take a sat_info instead
+ of a subst_info. Perform the substitution quietly first. Fold
+ in error-replaying code from diagnose_valid_expression.
+ (tsubst_simple_requirement): Take a sat_info instead of a
+ subst_info.
+ (tsubst_type_requirement_1): New. Fold in error-replaying code
+ from diagnose_valid_type.
+ (tsubst_type_requirement): Use the above. Take a sat_info
+ instead of a subst_info.
+ (tsubst_compound_requirement): Likewise. Fold in
+ error-replaying code from diagnose_compound_requirement.
+ (tsubst_nested_requirement): Take a sat_info instead of a
+ subst_info. Fold in error-replaying code from
+ diagnose_nested_requirement.
+ (tsubst_requirement): Take a sat_info instead of a subst_info.
+ (tsubst_requires_expr): Split into two versions, one that takes
+ a sat_info argument and another that takes a complain and
+ in_decl argument. Remove outdated documentation. Document the
+ effects of the sat_info argument. Don't short-circuit
+ processing of requirements when diagnosing unsatisfaction,
+ mirroring diagnose_requires_expr.
+ (satisfy_nondeclaration_constraint) <case REQUIRES_EXPR>: Remove
+ assert, and se the three-parameter version of tsubst_requires_expr.
+ (diagnose_trait_expr): Make static. Take a template argument
+ vector instead of a parameter mapping.
+ (diagnose_valid_expression): Remove.
+ (diagnose_valid_type): Remove.
+ (diagnose_simple_requirement): Remove.
+ (diagnose_compound_requirement): Remove.
+ (diagnose_type_requirement): Remove.
+ (diagnose_nested_requirement): Remove.
+ (diagnose_requirement): Remove.
+ (diagnose_requires_expr): Remove.
+ (diagnose_atomic_constraint): Take a sat_info instead of a
+ subst_info. Adjust call to diagnose_trait_expr. Call
+ tsubst_requires_expr instead of diagnose_requires_expr.
+ (diagnose_constraints): Remove special casing of REQUIRES_EXPR
+ and just always call constraint_satisfaction_value.
+
+2021-03-03 Patrick Palka <ppalka@redhat.com>
+
+ * constexpr.c (cxx_eval_call_expression): Adjust call to
+ evaluate_concept_check.
+ (cxx_eval_constant_expression) <case REQUIRES_EXPR>: Use
+ evaluate_requires_expression instead of
+ satisfy_constraint_expression.
+ <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
+ * constraint.cc (struct sat_info): Adjust comment about which
+ satisfaction entrypoints use noisy-unsat.
+ (normalize_template_requirements): Remove (and adjust callers
+ appropriately).
+ (normalize_nontemplate_requirements): Likewise.
+ (tsubst_nested_requirement): Use constraint_satisfaction_value
+ instead of satisfy_constraint_expression, which'll do the
+ noisy replaying of ill-formed quiet satisfaction for us.
+ (decl_satisfied_cache): Adjust comment.
+ (satisfy_constraint): Rename to ...
+ (satisfy_normalized_constraints): ... this.
+ (satisfy_associated_constraints): Remove (and make its
+ callers check for dependent arguments).
+ (satisfy_constraint_expression): Rename to ...
+ (satisfy_nondeclaration_constraints): ... this. Assert that
+ 'args' is empty when 't' is a concept-id. Removing handling
+ bare constraint-expressions, and handle REQUIRES_EXPRs
+ specially. Adjust comment accordingly.
+ (satisfy_declaration_constraints): Assert in the two-parameter
+ version that 't' is not a TEMPLATE_DECL. Adjust following
+ removal of normalize_(non)?template_requirements and
+ satisfy_asociated_constraints.
+ (constraint_satisfaction_value): Combine the two- and
+ three-parameter versions in the natural way.
+ (constraints_satisfied_p): Combine the one- and two-parameter
+ versions in the natural way. Improve documentation.
+ (evaluate_requires_expr): Define.
+ (evaluate_concept_check): Remove 'complain' parameter. Use
+ constraint_satisfaction_value instead of
+ satisfy_constraint_expression.
+ (diagnose_nested_requirement): Adjust following renaming of
+ satisfy_constraint_expression.
+ (diagnose_constraints): Handle REQUIRES_EXPR by going through
+ diagnose_requires_expr directly instead of treating it as a
+ constraint-expression. Improve documentation.
+ * cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Adjust call
+ to evaluate_concept_check.
+ <case REQUIRES_EXPR>: Use evaluate_requires_expr instead of
+ constraints_satisfied_p.
+ <case TEMPLATE_ID_EXPR>: Adjust call to evaluate_concept_check.
+ * cp-tree.h (evaluate_requires_expr): Declare.
+ (evaluate_concept_check): Remove tsubst_flag_t parameter.
+ (satisfy_constraint_expression): Remove declaration.
+ (constraints_satisfied_p): Remove one-parameter declaration.
+ Add a default argument to the two-parameter declaration.
+ * cvt.c (convert_to_void): Adjust call to
+ evaluate_concept_check.
+
+2021-03-03 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/82959
+ * call.c (op_is_ordered): Handle TRUTH_ANDIF_EXPR, TRUTH_ORIF_EXPR
+ and COMPOUND_EXPR.
+
+2021-03-03 Marek Polacek <polacek@redhat.com>
+
+ PR c++/97034
+ PR c++/99009
+ * pt.c (build_deduction_guide): Use INNERMOST_TEMPLATE_ARGS.
+ (maybe_aggr_guide): Use the original template type where needed. In
+ a class member template, partially instantiate the result of
+ collect_ctor_idx_types.
+ (do_class_deduction): Defer the deduction until the enclosing
+ scope is non-dependent.
+
+2021-03-03 Jason Merrill <jason@redhat.com>
+
+ PR c++/95675
+ * call.c (build_temp): Wrap a CALL_EXPR in a TARGET_EXPR
+ if it didn't get one before.
+
+2021-03-03 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99344
+ * module.cc (trees_out::decl_node): Small refactor.
+ (depset::hash::add_binding_entity): Return true on meeting an
+ import. Set namespace's import here.
+ (module_state:write_namespaces): Inform of purview too.
+ (module_state:read_namespaces): Adjust.
+ * name-lookup.c (implicitly_export_namespace): Delete.
+ (do_pushdecl): Don't call it.
+ (push_namespace): Likewise, set purview.
+ (add_imported_namespace): Reorder parms.
+ * name-lookup.h (add_imported_namespace): Alter param ordering.
+
+2021-03-02 Martin Sebor <msebor@redhat.com>
+
+ PR c++/99251
+ * class.c (build_base_path): Call build_if_nonnull.
+ * cp-tree.h (build_if_nonnull): Declare.
+ * rtti.c (ifnonnull): Rename...
+ (build_if_nonnull): ...to this. Set no-warning bit on COND_EXPR.
+ (build_dynamic_cast_1): Adjust to name change.
+
+2021-03-02 Patrick Palka <ppalka@redhat.com>
+
+ PR c++/96443
+ PR c++/96960
+ * constraint.cc (type_deducible_p): Don't substitute into the
+ constraints, and instead just pass 'args' to do_auto_deduction
+ as the outer template arguments.
+ (tsubst_parameter_mapping): Remove confused code for handling
+ placeholder type arguments.
+ (normalize_placeholder_type_constraint): Define.
+ (satisfy_constraint_expression): Use it to handle placeholder
+ 'auto' types.
+ * cp-tree.h (PLACEHOLDER_TYPE_CONSTRAINTS_INFO): Define.
+ (PLACEHOLDER_TYPE_CONSTRAINTS): Redefine in terms of the above.
+ * pt.c (tsubst) <case TEMPLATE_TYPE_PARM>: Use
+ PLACEHOLDER_TYPE_CONSTRAINTS_INFO instead.
+ (make_constrained_placeholder_type): Set
+ PLACEHOLDER_TYPE_CONSTRAINTS_INFO instead.
+ (do_auto_deduction): Clarify comments about the outer_targs
+ parameter. Rework satisfaction of a placeholder type constraint
+ to pass in the complete set of template arguments directly to
+ constraints_satisfied_p.
+ (splice_late_return_type): Use PLACEHOLDER_TYPE_CONSTRAINTS_INFO
+ instead. Also rebuild the the constraint info on the new auto.
+
+2021-03-02 Patrick Palka <ppalka@redhat.com>
+
+ * constraint.cc (build_parameter_mapping): Rely on the caller to
+ determine the in-scope template parameters.
+ (norm_info::norm_info): Delegate the tsubst_flags_t constructor
+ to the two-parameter constructor. In the two-parameter
+ constructor, fold in the definition of make_context, set
+ initial_parms appropriately, and don't set the now-removed
+ orig_decl member.
+ (norm_info::make_context): Remove, now that its only use is
+ inlined into the caller.
+ (norm_info::update_context): Adjust call to
+ build_parameter_mapping to pass in the relevant set of in-scope
+ template parameters.
+ (norm_info::ctx_parms): Define this member function.
+ (norm_info::context): Initialize to NULL_TREE.
+ (norm_info::orig_decl): Remove this data member.
+ (norm_info::initial_parms): Define this data member.
+ (normalize_atom): Adjust call to build_parameter_mapping to pass
+ in the relevant set of in-scope template parameters. Use
+ info.initial_parms instead of info.orig_decl.
+ (normalize_constraint_expression): Take a norm_info object
+ instead of a bool. Cache the result of normalization.
+ (tsubst_nested_requirement): Call satisfy_constraint_expression
+ instead of satisfy_constraint, so that we normalize on demand.
+ (satisfy_constraint_expression): Handle a NESTED_REQ argument.
+ Adjust call to normalize_constraint_expression.
+ (finish_nested_requirement): Set the TREE_TYPE of the NESTED_REQ
+ to current_template_parms.
+ (diagnose_nested_requirements): Go through
+ satisfy_constraint_expression, as with tsubst_nested_requirement.
+
+2021-03-02 Patrick Palka <ppalka@redhat.com>
+
+ * constraint.cc (tsubst_parameter_mapping): Canonicalize the
+ arguments of a substituted TYPE_ARGUMENT_PACK even if we've
+ started with a TYPE_ARGUMENT_PACK.
+ (finish_requires_expr): Don't set DECL_CONTEXT and
+ CONSTRAINT_VAR_P on each of the introduced parameters here.
+ * parser.c (cp_parser_requirement_parameter_list): Instead set
+ these fields earlier, here.
+ * pt.c (do_auto_deduction): Canonicalize the result of
+ do_auto_deduction. Pass 'complain' to finish_decltype_type.
+
+2021-03-02 Patrick Palka <ppalka@redhat.com>
+
+ * constraint.cc (tsubst_simple_requirement): Just return
+ boolean_true_node on success.
+ (tsubst_type_requirement): Likewise.
+ (tsubst_compound_requirement): Likewise.
+ (tsubst_nested_requirement): Likewise.
+ (tsubst_requirement_body): Remove.
+ (check_constaint_variables): Rename to ...
+ (check_constraint_variables): ... this.
+ (tsubst_constraint_variables): Adjust.
+ (tsubst_requires_expr): Fold tsubst_requirement_body into here.
+
+2021-03-01 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99294
+ * class.c (fixup_type_variants): Propagate mode, precision,
+ alignment & emptiness.
+ * module.cc (trees_out::type_node): Use TYPE_ALIGN_RAW.
+ (trees_in::tree_node): Rematerialize alignment here.
+
+2021-02-27 Jason Merrill <jason@redhat.com>
+
+ PR c++/90333
+ * parser.c (cp_parser_lambda_declarator_opt): Accept GNU attributes
+ between () and ->.
+
+2021-02-26 Jakub Jelinek <jakub@redhat.com>
+
+ * parser.c (cp_parser_lambda_declarator_opt): Implement
+ P1102R2 - Down with ()! Make ()s optional before lambda specifiers
+ for -std={c,gnu}++2b or with pedwarn in earlier versions.
+
+2021-02-26 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/95451
+ * lambda.c (is_lambda_ignored_entity): Before checking for
+ LAMBDA_FUNCTION_P, use OVL_FIRST. Drop FUNCTION_DECL check.
+
+2021-02-26 Jason Merrill <jason@redhat.com>
+
+ PR c++/98810
+ * pt.c (tsubst_copy) [VIEW_CONVERT_EXPR]: Add const
+ to a class non-type template argument that needs it.
+
+2021-02-26 Patrick Palka <ppalka@redhat.com>
+
+ PR c++/98990
+ * pt.c (splice_late_return_type): Rebuild the entire return type
+ if we have to adjust the level of an auto within.
+ (type_uses_auto): Adjust call to find_type_usage.
+ * type-utils.h (find_type_usage): Revert r10-6571 change that
+ made this function return a pointer to the auto node.
+
+2021-02-25 Patrick Palka <ppalka@redhat.com>
+
+ PR c++/99213
+ PR c++/94521
+ * error.c (dump_scope): Pass TFF_NO_TEMPLATE_BINDINGS instead of
+ TFF_NO_FUNCTION_ARGUMENTS when dumping a function scope.
+
+2021-02-25 Patrick Palka <ppalka@redhat.com>
+
+ PR c++/99103
+ * pt.c (is_spec_or_derived): Drop cv-qualifiers from 'etype'.
+ (maybe_aggr_guide): Fix order of arguments to is_spec_or_derived.
+
+2021-02-25 Marek Polacek <polacek@redhat.com>
+
+ DR 1312
+ PR c++/99176
+ * constexpr.c (is_std_construct_at): New overload.
+ (is_std_allocator_allocate): New overload.
+ (cxx_eval_call_expression): Use the new overloads.
+ (cxx_eval_constant_expression): Reject casting
+ from void * as per DR 1312. Don't check can_convert.
+
+2021-02-25 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/97587
+ * coroutines.cc (struct param_info): Track rvalue refs.
+ (morph_fn_to_coro): Track rvalue refs, and call the promise
+ CTOR with the frame copy of passed parms.
+
+2021-02-25 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/95822
+ * coroutines.cc (morph_fn_to_coro): Unconditionally remove any
+ set throwing_cleanup marker.
+
+2021-02-25 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99166
+ * module.cc (module_state::inform_cmi_p): Renamed field.
+ (module_state::do_import): Adjust.
+ (init_modules, finish_module_processing): Likewise.
+ (handle_module_option): Likewise.
+
+2021-02-25 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/98318
+ * mapper-client.cc (module_client::open_module_client): Fix typo
+ of fd init.
+
+2021-02-24 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/98718
+ * module.cc (ool): New indirection vector.
+ (loc_spans::maybe_propagate): Location is not optional.
+ (loc_spans::open): Likewise. Assert monotonically advancing.
+ (module_for_ordinary_loc): Use ool indirection vector.
+ (module_state::write_prepare_maps): Do not count empty macro
+ expansions. Elide empty spans.
+ (module_state::write_macro_maps): Skip empty expansions.
+ (ool_cmp): New qsort comparator.
+ (module_state::write): Create and destroy ool vector.
+ (name_pending_imports): Fix dump push/pop.
+ (preprocess_module): Likewise. Add more dumping.
+ (preprocessed_module): Likewise.
+
+2021-02-24 Iain Sandoe <iain@sandoe.co.uk>
+
+ PR c++/96251
+ * coroutines.cc (coro_common_keyword_context_valid_p): Suppress
+ error reporting when instantiating for a constexpr.
+
+2021-02-23 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99208
+ * decl.c (name_unnamed_type): Check DECL identity, not IDENTIFIER
+ identity.
+
+2021-02-23 Patrick Palka <ppalka@redhat.com>
+
+ PR c++/95468
+ * pt.c (tsubst_copy_and_build) <case BASELINK>: New case, copied
+ over from tsubst_copy.
+
+2021-02-23 Patrick Palka <ppalka@redhat.com>
+
+ * pt.c (instantiation_dependent_expression_p): Check
+ processing_template_decl before calling
+ potential_constant_expression.
+
+2021-02-22 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99174
+ * module.cc (struct module_state): Add visited_p flag.
+ (name_pending_imports): Use it to avoid duplicate requests.
+ (preprocess_module): Don't read preprocessor state if we failed to
+ load a module's config.
+
+2021-02-22 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99153
+ * decl.c (duplicate_decls): Move DECL_MODULE_IMPORT_P propagation
+ to common-path.
+ * module.cc (set_defining_module): Add assert.
+
+2021-02-19 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/98741
+ * module.cc (pending_imports): New.
+ (declare_module): Adjust test condition.
+ (name_pending_imports): New.
+ (preprocess_module): Reimplement using pending_imports.
+ (preprocessed_module): Move name-getting to name_pending_imports.
+ * name-lookup.c (append_imported_binding_slot): Assert module
+ ordering is increasing.
+
+2021-02-19 Nathan Sidwell <nathan@acm.org>
+
+ * module.cc (note_cmis): New.
+ (struct module_state): Add inform_read_p bit.
+ (module_state::do_import): Inform of CMI location, if enabled.
+ (init_modules): Canonicalize note_cmis entries.
+ (handle_module_option): Handle -flang-info-module-read=FOO.
+
+2021-02-19 Jason Merrill <jason@redhat.com>
+
+ PR c++/96926
+ * call.c (perfect_conversion_p): Limit rvalueness
+ test to reference bindings.
+
+2021-02-19 Jason Merrill <jason@redhat.com>
+
+ PR c++/96926
+ * call.c (perfect_conversion_p): New.
+ (perfect_candidate_p): New.
+ (add_candidates): Ignore templates after a perfect non-template.
+
+2021-02-18 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99023
+ * module.cc (canonicalize_header_name): Use
+ cpp_probe_header_unit.
+ (maybe_translate_include): Fix note_includes comparison.
+ (init_modules): Fix note_includes string termination.
+
+2021-02-18 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/99132
+ * cp-gimplify.c (cp_genericize_r) <case CALL_EXPR>: Use
+ cp_get_callee_fndecl_nofold instead of cp_get_callee_fndecl to check
+ for immediate function calls.
+
+2021-02-17 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99023
+ * module.cc (struct macro_export): Add GTY markers.
+ (macro_exports): Likewise, us a va_gc Vector.
+
+2021-02-17 Jakub Jelinek <jakub@redhat.com>
+
+ PR sanitizer/99106
+ * init.c (build_zero_init_1): For flexible array members just return
+ NULL_TREE instead of returning empty CONSTRUCTOR with non-complete
+ ARRAY_TYPE.
+
+2021-02-17 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99116
+ * name-lookup.c (do_pushdecl): Don't peek under template_parm
+ bindings here ...
+ (set_identifier_type_value_with_scope): ... or here.
+ (do_pushtag): Only set_identifier_type_value_with_scope at
+ non-class template parm scope, and use parent scope.
+
+2021-02-17 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99071
+ * name-lookup.c (maybe_record_mergeable_decl): Deref the correct
+ pointer.
+
+2021-02-17 Patrick Palka <ppalka@redhat.com>
+
+ PR debug/96997
+ PR c++/94034
+ * tree.c (build_aggr_init_expr): Revert r10-7718 change.
+
+2021-02-12 Nathan Sidwell <nathan@acm.org>
+
+ * module.cc (module_state::write_cluster): Check bindings for
+ imported using-decls.
+
+2021-02-12 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99040
+ * module.cc (trees_in::decl_value): Call add_module_namespace_decl
+ for new namespace-scope entities.
+ (module_state::read_cluster): Don't call add_module_decl here.
+ * name-lookup.h (add_module_decl): Rename to ...
+ (add_module_namespace_decl): ... this.
+ * name-lookup.c (newbinding_bookkeeping): Move into ...
+ (do_pushdecl): ... here. Its only remaining caller.
+ (add_module_decl): Rename to ...
+ (add_module_namespace_decl): ... here. Add checking-assert for
+ circularity. Don't call newbinding_bookkeeping, just extern_c
+ checking and incomplete var checking.
+
+2021-02-12 Nathan Sidwell <nathan@acm.org>
+
+ PR c++/99039
+ PR c++/99040
+ * cp-tree.h (CPTI_GLOBAL_TYPE): Delete.
+ (global_type_node): Delete.
+ (IDENTIFIER_TYPE_VALUE): Delete.
+ (IDENTIFIER_HAS_TYPE_VALUE): Delete.
+ (get_type_value): Delete.
+ * name-lookup.h (identifier_type_value): Delete.
+ * name-lookup.c (check_module_override): Don't
+ SET_IDENTIFIER_TYPE_VALUE here.
+ (do_pushdecl): Nor here.
+ (identifier_type_value_1, identifier_type_value): Delete.
+ (set_identifier_type_value_with_scope): Only
+ SET_IDENTIFIER_TYPE_VALUE for local and class scopes.
+ (pushdecl_nanmespace_level): Remove shadow stack nadgering.
+ (do_pushtag): Use REAL_IDENTIFIER_TYPE_VALUE.
+ * call.c (check_dtor_name): Use lookup_name.
+ * decl.c (cxx_init_decl_processing): Drop global_type_node.
+ * decl2.c (cplus_decl_attributes): Don't SET_IDENTIFIER_TYPE_VALUE
+ here.
+ * init.c (get_type_value): Delete.
+ * pt.c (instantiate_class_template_1): Don't call pushtag or
+ SET_IDENTIFIER_TYPE_VALUE here.
+ (tsubst): Assert never an identifier.
+ (dependent_type_p): Drop global_type_node assert.
+ * typeck.c (error_args_num): Don't use IDENTIFIER_HAS_TYPE_VALUE
+ to determine ctorness.
+
+2021-02-12 Jakub Jelinek <jakub@redhat.com>
+
+ PR c++/97742
+ * parser.c (cp_parser_requirement_seq): Stop iterating after reaching
+ CPP_EOF.
+
+2021-02-12 Jason Merrill <jason@redhat.com>
+
+ PR c++/97246
+ PR c++/94546
+ * pt.c (extract_fnparm_pack): Check DECL_PACK_P here.
+ (register_parameter_specializations): Not here.
+
2021-02-11 Marek Polacek <polacek@redhat.com>
PR c++/95888
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 4744c97..7d12fea 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -236,8 +236,15 @@ check_dtor_name (tree basetype, tree name)
|| TREE_CODE (basetype) == ENUMERAL_TYPE)
&& name == constructor_name (basetype))
return true;
- else
- name = get_type_value (name);
+
+ /* Otherwise lookup the name, it could be an unrelated typedef
+ of the correct type. */
+ name = lookup_name (name, LOOK_want::TYPE);
+ if (!name)
+ return false;
+ name = TREE_TYPE (name);
+ if (name == error_mark_node)
+ return false;
}
else
{
@@ -252,8 +259,6 @@ check_dtor_name (tree basetype, tree name)
return false;
}
- if (!name || name == error_mark_node)
- return false;
return same_type_p (TYPE_MAIN_VARIANT (basetype), TYPE_MAIN_VARIANT (name));
}
@@ -1444,7 +1449,9 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
tree fbase = class_of_this_parm (fromfn);
tree tbase = class_of_this_parm (tofn);
- if (!DERIVED_FROM_P (fbase, tbase))
+ /* If FBASE and TBASE are equivalent but incomplete, DERIVED_FROM_P
+ yields false. But a pointer to member of incomplete class is OK. */
+ if (!same_type_p (fbase, tbase) && !DERIVED_FROM_P (fbase, tbase))
return NULL;
tree fstat = static_fn_type (fromfn);
@@ -5848,6 +5855,49 @@ prep_operand (tree operand)
return operand;
}
+/* True iff CONV represents a conversion sequence which no other can be better
+ than under [over.ics.rank]: in other words, a "conversion" to the exact same
+ type (including binding to a reference to the same type). This is stronger
+ than the standard's "identity" category, which also includes reference
+ bindings that add cv-qualifiers or change rvalueness. */
+
+static bool
+perfect_conversion_p (conversion *conv)
+{
+ if (CONVERSION_RANK (conv) != cr_identity)
+ return false;
+ if (conv->kind == ck_ref_bind)
+ {
+ if (!conv->rvaluedness_matches_p)
+ return false;
+ if (!same_type_p (TREE_TYPE (conv->type),
+ next_conversion (conv)->type))
+ return false;
+ }
+ return true;
+}
+
+/* True if CAND represents a perfect match, i.e. all perfect conversions, so no
+ other candidate can be a better match. Since the template/non-template
+ tiebreaker comes immediately after the conversion comparison in
+ [over.match.best], a perfect non-template candidate is better than all
+ templates. */
+
+static bool
+perfect_candidate_p (z_candidate *cand)
+{
+ if (cand->viable < 1)
+ return false;
+ int len = cand->num_convs;
+ for (int i = 0; i < len; ++i)
+ if (!perfect_conversion_p (cand->convs[i]))
+ return false;
+ if (conversion *conv = cand->second_conv)
+ if (!perfect_conversion_p (conv))
+ return false;
+ return true;
+}
+
/* Add each of the viable functions in FNS (a FUNCTION_DECL or
OVERLOAD) to the CANDIDATES, returning an updated list of
CANDIDATES. The ARGS are the arguments provided to the call;
@@ -5915,6 +5965,18 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
/* Delay creating the implicit this parameter until it is needed. */
non_static_args = NULL;
+ /* If there's a non-template perfect match, we don't need to consider
+ templates. So check non-templates first. This optimization is only
+ really needed for the defaulted copy constructor of tuple and the like
+ (96926), but it seems like we might as well enable it more generally. */
+ bool seen_perfect = false;
+ enum { templates, non_templates, either } which = either;
+ if (template_only)
+ which = templates;
+ else /*if (flags & LOOKUP_DEFAULTED)*/
+ which = non_templates;
+
+ again:
for (lkp_iterator iter (fns); iter; ++iter)
{
fn = *iter;
@@ -5923,6 +5985,10 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
continue;
if (check_list_ctor && !is_list_ctor (fn))
continue;
+ if (which == templates && TREE_CODE (fn) != TEMPLATE_DECL)
+ continue;
+ if (which == non_templates && TREE_CODE (fn) == TEMPLATE_DECL)
+ continue;
tree fn_first_arg = NULL_TREE;
const vec<tree, va_gc> *fn_args = args;
@@ -5962,7 +6028,7 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
fn,
ctype,
explicit_targs,
- fn_first_arg,
+ fn_first_arg,
fn_args,
return_type,
access_path,
@@ -5970,17 +6036,26 @@ add_candidates (tree fns, tree first_arg, const vec<tree, va_gc> *args,
flags,
strict,
complain);
- else if (!template_only)
- add_function_candidate (candidates,
- fn,
- ctype,
- fn_first_arg,
- fn_args,
- access_path,
- conversion_path,
- flags,
- NULL,
- complain);
+ else
+ {
+ add_function_candidate (candidates,
+ fn,
+ ctype,
+ fn_first_arg,
+ fn_args,
+ access_path,
+ conversion_path,
+ flags,
+ NULL,
+ complain);
+ if (perfect_candidate_p (*candidates))
+ seen_perfect = true;
+ }
+ }
+ if (which == non_templates && !seen_perfect)
+ {
+ which = templates;
+ goto again;
}
}
@@ -6010,6 +6085,15 @@ op_is_ordered (tree_code code)
case LSHIFT_EXPR:
// 8. a >> b
case RSHIFT_EXPR:
+ // a && b
+ // Predates P0145R3.
+ case TRUTH_ANDIF_EXPR:
+ // a || b
+ // Predates P0145R3.
+ case TRUTH_ORIF_EXPR:
+ // a , b
+ // Predates P0145R3.
+ case COMPOUND_EXPR:
return (flag_strong_eval_order ? 1 : 0);
default:
@@ -7218,6 +7302,14 @@ build_temp (tree expr, tree type, int flags,
&& !type_has_nontrivial_copy_init (TREE_TYPE (expr)))
return get_target_expr_sfinae (expr, complain);
+ /* In decltype, we might have decided not to wrap this call in a TARGET_EXPR.
+ But it turns out to be a subexpression, so perform temporary
+ materialization now. */
+ if (TREE_CODE (expr) == CALL_EXPR
+ && CLASS_TYPE_P (type)
+ && same_type_ignoring_top_level_qualifiers_p (type, TREE_TYPE (expr)))
+ expr = build_cplus_new (type, expr, complain);
+
savew = warningcount + werrorcount, savee = errorcount;
releasing_vec args (make_tree_vector_single (expr));
expr = build_special_member_call (NULL_TREE, complete_ctor_identifier,
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 40f5fef..856e81e 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -402,16 +402,9 @@ build_base_path (enum tree_code code,
if (TREE_SIDE_EFFECTS (expr) && (null_test || virtual_access))
expr = save_expr (expr);
- /* Now that we've saved expr, build the real null test. */
+ /* Store EXPR and build the real null test just before returning. */
if (null_test)
- {
- tree zero = cp_convert (TREE_TYPE (expr), nullptr_node, complain);
- null_test = build2_loc (input_location, NE_EXPR, boolean_type_node,
- expr, zero);
- /* This is a compiler generated comparison, don't emit
- e.g. -Wnonnull-compare warning for it. */
- TREE_NO_WARNING (null_test) = 1;
- }
+ null_test = expr;
/* If this is a simple base reference, express it as a COMPONENT_REF. */
if (code == PLUS_EXPR && !virtual_access
@@ -516,14 +509,8 @@ build_base_path (enum tree_code code,
out:
if (null_test)
- {
- expr = fold_build3_loc (input_location, COND_EXPR, target_type, null_test,
- expr, build_zero_cst (target_type));
- /* Avoid warning for the whole conditional expression (in addition
- to NULL_TEST itself -- see above) in case the result is used in
- a nonnull context that the front end -Wnonnull checks. */
- TREE_NO_WARNING (expr) = 1;
- }
+ /* Wrap EXPR in a null test. */
+ expr = build_if_nonnull (null_test, expr, complain);
return expr;
}
@@ -2005,35 +1992,45 @@ determine_primary_bases (tree t)
/* Update the variant types of T. */
void
-fixup_type_variants (tree t)
+fixup_type_variants (tree type)
{
- tree variants;
-
- if (!t)
+ if (!type)
return;
- for (variants = TYPE_NEXT_VARIANT (t);
- variants;
- variants = TYPE_NEXT_VARIANT (variants))
+ for (tree variant = TYPE_NEXT_VARIANT (type);
+ variant;
+ variant = TYPE_NEXT_VARIANT (variant))
{
/* These fields are in the _TYPE part of the node, not in
the TYPE_LANG_SPECIFIC component, so they are not shared. */
- TYPE_HAS_USER_CONSTRUCTOR (variants) = TYPE_HAS_USER_CONSTRUCTOR (t);
- TYPE_NEEDS_CONSTRUCTING (variants) = TYPE_NEEDS_CONSTRUCTING (t);
- TYPE_HAS_NONTRIVIAL_DESTRUCTOR (variants)
- = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t);
+ TYPE_HAS_USER_CONSTRUCTOR (variant) = TYPE_HAS_USER_CONSTRUCTOR (type);
+ TYPE_NEEDS_CONSTRUCTING (variant) = TYPE_NEEDS_CONSTRUCTING (type);
+ TYPE_HAS_NONTRIVIAL_DESTRUCTOR (variant)
+ = TYPE_HAS_NONTRIVIAL_DESTRUCTOR (type);
- TYPE_POLYMORPHIC_P (variants) = TYPE_POLYMORPHIC_P (t);
- CLASSTYPE_FINAL (variants) = CLASSTYPE_FINAL (t);
+ TYPE_POLYMORPHIC_P (variant) = TYPE_POLYMORPHIC_P (type);
+ CLASSTYPE_FINAL (variant) = CLASSTYPE_FINAL (type);
- TYPE_BINFO (variants) = TYPE_BINFO (t);
+ TYPE_BINFO (variant) = TYPE_BINFO (type);
/* Copy whatever these are holding today. */
- TYPE_VFIELD (variants) = TYPE_VFIELD (t);
- TYPE_FIELDS (variants) = TYPE_FIELDS (t);
+ TYPE_VFIELD (variant) = TYPE_VFIELD (type);
+ TYPE_FIELDS (variant) = TYPE_FIELDS (type);
+
+ TYPE_SIZE (variant) = TYPE_SIZE (type);
+ TYPE_SIZE_UNIT (variant) = TYPE_SIZE_UNIT (type);
+
+ if (!TYPE_USER_ALIGN (variant)
+ || TYPE_NAME (variant) == TYPE_NAME (type)
+ || TYPE_ALIGN_RAW (variant) < TYPE_ALIGN_RAW (type))
+ {
+ TYPE_ALIGN_RAW (variant) = TYPE_ALIGN_RAW (type);
+ TYPE_USER_ALIGN (variant) = TYPE_USER_ALIGN (type);
+ }
- TYPE_SIZE (variants) = TYPE_SIZE (t);
- TYPE_SIZE_UNIT (variants) = TYPE_SIZE_UNIT (t);
+ TYPE_PRECISION (variant) = TYPE_PRECISION (type);
+ TYPE_MODE_RAW (variant) = TYPE_MODE_RAW (type);
+ TYPE_EMPTY_P (variant) = TYPE_EMPTY_P (type);
}
}
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 377fe32..c946744 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -1837,6 +1837,16 @@ is_std_construct_at (tree fndecl)
return name && id_equal (name, "construct_at");
}
+/* Overload for the above taking constexpr_call*. */
+
+static inline bool
+is_std_construct_at (const constexpr_call *call)
+{
+ return (call
+ && call->fundef
+ && is_std_construct_at (call->fundef->decl));
+}
+
/* Return true if FNDECL is std::allocator<T>::{,de}allocate. */
static inline bool
@@ -1859,6 +1869,16 @@ is_std_allocator_allocate (tree fndecl)
return decl_in_std_namespace_p (decl);
}
+/* Overload for the above taking constexpr_call*. */
+
+static inline bool
+is_std_allocator_allocate (const constexpr_call *call)
+{
+ return (call
+ && call->fundef
+ && is_std_allocator_allocate (call->fundef->decl));
+}
+
/* Return true if FNDECL is __dynamic_cast. */
static inline bool
@@ -2237,7 +2257,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
{
/* Handle concept checks separately. */
if (concept_check_p (t))
- return evaluate_concept_check (t, tf_warning_or_error);
+ return evaluate_concept_check (t);
location_t loc = cp_expr_loc_or_input_loc (t);
tree fun = get_function_named_in_call (t);
@@ -2313,9 +2333,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
if (TREE_CODE (t) == CALL_EXPR
&& cxx_replaceable_global_alloc_fn (fun)
&& (CALL_FROM_NEW_OR_DELETE_P (t)
- || (ctx->call
- && ctx->call->fundef
- && is_std_allocator_allocate (ctx->call->fundef->decl))))
+ || is_std_allocator_allocate (ctx->call)))
{
const int nargs = call_expr_nargs (t);
tree arg0 = NULL_TREE;
@@ -2423,9 +2441,7 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
argument. */
if (TREE_CODE (t) == CALL_EXPR
&& cxx_placement_new_fn (fun)
- && ctx->call
- && ctx->call->fundef
- && is_std_construct_at (ctx->call->fundef->decl))
+ && is_std_construct_at (ctx->call))
{
const int nargs = call_expr_nargs (t);
tree arg1 = NULL_TREE;
@@ -5566,20 +5582,17 @@ cxx_eval_increment_expression (const constexpr_ctx *ctx, tree t,
/* Storing the modified value. */
tree store = build2_loc (cp_expr_loc_or_loc (t, input_location),
MODIFY_EXPR, type, op, mod);
- cxx_eval_constant_expression (ctx, store,
- true, non_constant_p, overflow_p);
+ mod = cxx_eval_constant_expression (ctx, store, lval,
+ non_constant_p, overflow_p);
ggc_free (store);
+ if (*non_constant_p)
+ return t;
/* And the value of the expression. */
if (code == PREINCREMENT_EXPR || code == PREDECREMENT_EXPR)
- {
- /* Prefix ops are lvalues. */
- if (lval)
- return op;
- else
- /* But we optimize when the caller wants an rvalue. */
- return mod;
- }
+ /* Prefix ops are lvalues, but the caller might want an rvalue;
+ lval has already been taken into account in the store above. */
+ return mod;
else
/* Postfix ops are rvalues. */
return val;
@@ -6653,6 +6666,36 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
return t;
}
+ /* [expr.const]: a conversion from type cv void* to a pointer-to-object
+ type cannot be part of a core constant expression as a resolution to
+ DR 1312. */
+ if (integer_zerop (op) /* FIXME: Remove in GCC 12. */
+ && TYPE_PTROB_P (type)
+ && TYPE_PTR_P (TREE_TYPE (op))
+ && VOID_TYPE_P (TREE_TYPE (TREE_TYPE (op)))
+ /* Inside a call to std::construct_at or to
+ std::allocator<T>::{,de}allocate, we permit casting from void*
+ because that is compiler-generated code. */
+ && !is_std_construct_at (ctx->call)
+ && !is_std_allocator_allocate (ctx->call))
+ {
+ /* Likewise, don't error when casting from void* when OP is
+ &heap uninit and similar. */
+ tree sop = tree_strip_nop_conversions (op);
+ if (TREE_CODE (sop) == ADDR_EXPR
+ && VAR_P (TREE_OPERAND (sop, 0))
+ && DECL_ARTIFICIAL (TREE_OPERAND (sop, 0)))
+ /* OK */;
+ else
+ {
+ if (!ctx->quiet)
+ error_at (loc, "cast from %qT is not allowed",
+ TREE_TYPE (op));
+ *non_constant_p = true;
+ return t;
+ }
+ }
+
if (TREE_CODE (op) == PTRMEM_CST && !TYPE_PTRMEM_P (type))
op = cplus_expand_constant (op);
@@ -6671,26 +6714,10 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if (TYPE_REF_P (type))
{
if (!ctx->quiet)
- error_at (loc,
- "dereferencing a null pointer");
+ error_at (loc, "dereferencing a null pointer");
*non_constant_p = true;
return t;
}
- else if (TYPE_PTR_P (TREE_TYPE (op)))
- {
- tree from = TREE_TYPE (op);
-
- if (!can_convert (type, from, tf_none))
- {
- if (!ctx->quiet)
- error_at (loc,
- "conversion of %qT null pointer to %qT "
- "is not a constant expression",
- from, type);
- *non_constant_p = true;
- return t;
- }
- }
}
else
{
@@ -6875,7 +6902,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
'!requires (T t) { ... }' which is not transformed into
a constraint. */
if (!processing_template_decl)
- return satisfy_constraint_expression (t);
+ return evaluate_requires_expr (t);
else
*non_constant_p = true;
return t;
@@ -6911,7 +6938,7 @@ cxx_eval_constant_expression (const constexpr_ctx *ctx, tree t,
if (!processing_template_decl
&& !uid_sensitive_constexpr_evaluation_p ())
- r = evaluate_concept_check (t, tf_warning_or_error);
+ r = evaluate_concept_check (t);
else
*non_constant_p = true;
diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 31e0fb5..5cf43bd 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -100,18 +100,31 @@ struct subst_info
/* Provides additional context for satisfaction.
- The flag noisy() controls whether to diagnose ill-formed satisfaction,
- such as the satisfaction value of an atom being non-bool or non-constant.
-
- The flag diagnose_unsatisfaction_p() controls whether to explain why
- a constraint is not satisfied.
-
- The entrypoints to satisfaction for which we set noisy+unsat are
- diagnose_constraints and diagnose_nested_requirement. The entrypoints for
- which we set noisy-unsat are the replays inside constraint_satisfaction_value,
- evaluate_concept_check and tsubst_nested_requirement. In other entrypoints,
- e.g. constraints_satisfied_p, we enter satisfaction quietly (both flags
- cleared). */
+ During satisfaction:
+ - The flag noisy() controls whether to diagnose ill-formed satisfaction,
+ such as the satisfaction value of an atom being non-bool or non-constant.
+ - The flag diagnose_unsatisfaction_p() controls whether to additionally
+ explain why a constraint is not satisfied.
+ - We enter satisfaction with noisy+unsat from diagnose_constraints.
+ - We enter satisfaction with noisy-unsat from the replay inside
+ constraint_satisfaction_value.
+ - We enter satisfaction quietly (both flags cleared) from
+ constraints_satisfied_p.
+
+ During evaluation of a requires-expression:
+ - The flag noisy() controls whether to diagnose ill-formed types and
+ expressions inside its requirements.
+ - The flag diagnose_unsatisfaction_p() controls whether to additionally
+ explain why the requires-expression evaluates to false.
+ - We enter tsubst_requires_expr with noisy+unsat from
+ diagnose_atomic_constraint and potentially from
+ satisfy_nondeclaration_constraints.
+ - We enter tsubst_requires_expr with noisy-unsat from
+ cp_parser_requires_expression when processing a requires-expression that
+ appears outside a template.
+ - We enter tsubst_requires_expr quietly (both flags cleared) when
+ substituting through a requires-expression as part of template
+ instantiation. */
struct sat_info : subst_info
{
@@ -133,7 +146,7 @@ struct sat_info : subst_info
bool diagnose_unsatisfaction;
};
-static tree satisfy_constraint (tree, tree, sat_info);
+static tree constraint_satisfaction_value (tree, tree, sat_info);
/* True if T is known to be some type other than bool. Note that this
is false for dependent types and errors. */
@@ -594,26 +607,12 @@ map_arguments (tree parms, tree args)
return parms;
}
-/* Build the parameter mapping for EXPR using ARGS. */
+/* Build the parameter mapping for EXPR using ARGS, where CTX_PARMS
+ are the template parameters in scope for EXPR. */
static tree
-build_parameter_mapping (tree expr, tree args, tree decl)
+build_parameter_mapping (tree expr, tree args, tree ctx_parms)
{
- tree ctx_parms = NULL_TREE;
- if (decl)
- {
- gcc_assert (TREE_CODE (decl) == TEMPLATE_DECL);
- ctx_parms = DECL_TEMPLATE_PARMS (decl);
- }
- else if (current_template_parms)
- {
- /* TODO: This should probably be the only case, but because the
- point of declaration of concepts is currently set after the
- initializer, the template parameter lists are not available
- when normalizing concept definitions, hence the case above. */
- ctx_parms = current_template_parms;
- }
-
tree parms = find_template_parameters (expr, ctx_parms);
tree map = map_arguments (parms, args);
return map;
@@ -645,53 +644,63 @@ parameter_mapping_equivalent_p (tree t1, tree t2)
struct norm_info : subst_info
{
- explicit norm_info (tsubst_flags_t complain)
- : subst_info (tf_warning_or_error | complain, NULL_TREE),
- context()
+ explicit norm_info (tsubst_flags_t cmp)
+ : norm_info (NULL_TREE, cmp)
{}
/* Construct a top-level context for DECL. */
norm_info (tree in_decl, tsubst_flags_t complain)
- : subst_info (tf_warning_or_error | complain, in_decl),
- context (make_context (in_decl)),
- orig_decl (in_decl)
- {}
-
- bool generate_diagnostics() const
+ : subst_info (tf_warning_or_error | complain, in_decl)
{
- return complain & tf_norm;
+ if (in_decl)
+ {
+ initial_parms = DECL_TEMPLATE_PARMS (in_decl);
+ if (generate_diagnostics ())
+ context = build_tree_list (NULL_TREE, in_decl);
+ }
+ else
+ initial_parms = current_template_parms;
}
- tree make_context(tree in_decl)
+ bool generate_diagnostics() const
{
- if (generate_diagnostics ())
- return build_tree_list (NULL_TREE, in_decl);
- return NULL_TREE;
+ return complain & tf_norm;
}
void update_context(tree expr, tree args)
{
if (generate_diagnostics ())
{
- tree map = build_parameter_mapping (expr, args, in_decl);
+ tree map = build_parameter_mapping (expr, args, ctx_parms ());
context = tree_cons (map, expr, context);
}
in_decl = get_concept_check_template (expr);
}
+ /* Returns the template parameters that are in scope for the current
+ normalization context. */
+
+ tree ctx_parms()
+ {
+ if (in_decl)
+ return DECL_TEMPLATE_PARMS (in_decl);
+ else
+ return initial_parms;
+ }
+
/* Provides information about the source of a constraint. This is a
TREE_LIST whose VALUE is either a concept check or a constrained
declaration. The PURPOSE, for concept checks is a parameter mapping
for that check. */
- tree context;
+ tree context = NULL_TREE;
/* The declaration whose constraints we're normalizing. The targets
of the parameter mapping of each atom will be in terms of the
template parameters of ORIG_DECL. */
- tree orig_decl = NULL_TREE;
+ tree initial_parms = NULL_TREE;
};
static tree normalize_expression (tree, tree, norm_info);
@@ -773,7 +782,7 @@ normalize_atom (tree t, tree args, norm_info info)
return normalize_concept_check (t, args, info);
/* Build the parameter mapping for the atom. */
- tree map = build_parameter_mapping (t, args, info.in_decl);
+ tree map = build_parameter_mapping (t, args, info.ctx_parms ());
/* Build a new info object for the atom. */
tree ci = build_tree_list (t, info.context);
@@ -803,10 +812,8 @@ normalize_atom (tree t, tree args, norm_info info)
tree target = TREE_PURPOSE (node);
TREE_VEC_ELT (targets, i++) = target;
}
- tree ctx_parms = (info.orig_decl
- ? DECL_TEMPLATE_PARMS (info.orig_decl)
- : current_template_parms);
- tree target_parms = find_template_parameters (targets, ctx_parms);
+ tree target_parms = find_template_parameters (targets,
+ info.initial_parms);
TREE_TYPE (map) = target_parms;
}
@@ -964,33 +971,25 @@ normalize_concept_definition (tree tmpl, bool diag = false)
return norm;
}
-/* Returns the normal form of TMPL's requirements. */
-
-static tree
-normalize_template_requirements (tree tmpl, bool diag = false)
-{
- return get_normalized_constraints_from_decl (tmpl, diag);
-}
-
-/* Returns the normal form of TMPL's requirements. */
-
-static tree
-normalize_nontemplate_requirements (tree decl, bool diag = false)
-{
- return get_normalized_constraints_from_decl (decl, diag);
-}
-
/* Normalize an EXPR as a constraint. */
static tree
-normalize_constraint_expression (tree expr, bool diag)
+normalize_constraint_expression (tree expr, norm_info info)
{
if (!expr || expr == error_mark_node)
return expr;
+
+ if (!info.generate_diagnostics ())
+ if (tree *p = hash_map_safe_get (normalized_map, expr))
+ return *p;
+
++processing_template_decl;
- norm_info info (diag ? tf_norm : tf_none);
tree norm = get_normalized_constraints (expr, info);
--processing_template_decl;
+
+ if (!info.generate_diagnostics ())
+ hash_map_safe_put<hm_ggc> (normalized_map, expr, norm);
+
return norm;
}
@@ -1941,40 +1940,90 @@ hash_placeholder_constraint (tree c)
return val;
}
-/* Substitute through the simple requirement. */
+/* Substitute through the expression of a simple requirement or
+ compound requirement. */
static tree
-tsubst_valid_expression_requirement (tree t, tree args, subst_info info)
+tsubst_valid_expression_requirement (tree t, tree args, sat_info info)
{
- tree r = tsubst_expr (t, args, info.complain, info.in_decl, false);
- if (convert_to_void (r, ICV_STATEMENT, info.complain) == error_mark_node)
- return error_mark_node;
- return r;
+ tree r = tsubst_expr (t, args, tf_none, info.in_decl, false);
+ if (convert_to_void (r, ICV_STATEMENT, tf_none) != error_mark_node)
+ return r;
+
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ location_t loc = cp_expr_loc_or_input_loc (t);
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ inform (loc, "the required expression %qE is invalid, because", t);
+ if (r == error_mark_node)
+ tsubst_expr (t, args, info.complain, info.in_decl, false);
+ else
+ convert_to_void (r, ICV_STATEMENT, info.complain);
+ }
+ else
+ inform (loc, "the required expression %qE is invalid", t);
+ }
+ else if (info.noisy ())
+ {
+ r = tsubst_expr (t, args, info.complain, info.in_decl, false);
+ convert_to_void (r, ICV_STATEMENT, info.complain);
+ }
+
+ return error_mark_node;
}
/* Substitute through the simple requirement. */
static tree
-tsubst_simple_requirement (tree t, tree args, subst_info info)
+tsubst_simple_requirement (tree t, tree args, sat_info info)
{
tree t0 = TREE_OPERAND (t, 0);
tree expr = tsubst_valid_expression_requirement (t0, args, info);
if (expr == error_mark_node)
return error_mark_node;
- return finish_simple_requirement (EXPR_LOCATION (t), expr);
+ return boolean_true_node;
+}
+
+/* Subroutine of tsubst_type_requirement that performs the actual substitution
+ and diagnosing. Also used by tsubst_compound_requirement. */
+
+static tree
+tsubst_type_requirement_1 (tree t, tree args, sat_info info, location_t loc)
+{
+ tree r = tsubst (t, args, tf_none, info.in_decl);
+ if (r != error_mark_node)
+ return r;
+
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ /* Replay the substitution error. */
+ inform (loc, "the required type %qT is invalid, because", t);
+ tsubst (t, args, info.complain, info.in_decl);
+ }
+ else
+ inform (loc, "the required type %qT is invalid", t);
+ }
+ else if (info.noisy ())
+ tsubst (t, args, info.complain, info.in_decl);
+
+ return error_mark_node;
}
+
/* Substitute through the type requirement. */
static tree
-tsubst_type_requirement (tree t, tree args, subst_info info)
+tsubst_type_requirement (tree t, tree args, sat_info info)
{
tree t0 = TREE_OPERAND (t, 0);
- tree type = tsubst (t0, args, info.complain, info.in_decl);
+ tree type = tsubst_type_requirement_1 (t0, args, info, EXPR_LOCATION (t));
if (type == error_mark_node)
return error_mark_node;
- return finish_type_requirement (EXPR_LOCATION (t), type);
+ return boolean_true_node;
}
/* True if TYPE can be deduced from EXPR. */
@@ -1987,39 +2036,19 @@ type_deducible_p (tree expr, tree type, tree placeholder, tree args,
references are preserved in the result. */
expr = force_paren_expr_uneval (expr);
- /* Replace the constraints with the instantiated constraints. This
- substitutes args into any template parameters in the trailing
- result type. */
- tree saved_constr = PLACEHOLDER_TYPE_CONSTRAINTS (placeholder);
- tree subst_constr
- = tsubst_constraint (saved_constr,
- args,
- info.complain | tf_partial,
- info.in_decl);
-
- if (subst_constr == error_mark_node)
- return false;
-
- PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = subst_constr;
-
- /* Temporarily unlink the canonical type. */
- tree saved_type = TYPE_CANONICAL (placeholder);
- TYPE_CANONICAL (placeholder) = NULL_TREE;
-
- tree deduced_type
- = do_auto_deduction (type,
- expr,
- placeholder,
- info.complain,
- adc_requirement);
-
- PLACEHOLDER_TYPE_CONSTRAINTS (placeholder) = saved_constr;
- TYPE_CANONICAL (placeholder) = saved_type;
+ /* When args is NULL, we're evaluating a non-templated requires expression,
+ but even those are parsed under processing_template_decl == 1, and so the
+ placeholder 'auto' inside this return-type-requirement has level 2. In
+ order to have all parms and arguments match up for satisfaction, we need
+ to pass an empty level of OUTER_TARGS in this case. */
+ if (!args)
+ args = make_tree_vec (0);
- if (deduced_type == error_mark_node)
- return false;
+ tree deduced_type = do_auto_deduction (type, expr, placeholder,
+ info.complain, adc_requirement,
+ /*outer_targs=*/args);
- return true;
+ return deduced_type != error_mark_node;
}
/* True if EXPR can not be converted to TYPE. */
@@ -2048,7 +2077,7 @@ expression_convertible_p (tree expr, tree type, subst_info info)
/* Substitute through the compound requirement. */
static tree
-tsubst_compound_requirement (tree t, tree args, subst_info info)
+tsubst_compound_requirement (tree t, tree args, sat_info info)
{
tree t0 = TREE_OPERAND (t, 0);
tree t1 = TREE_OPERAND (t, 1);
@@ -2056,13 +2085,20 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
if (expr == error_mark_node)
return error_mark_node;
+ location_t loc = cp_expr_loc_or_input_loc (expr);
+
/* Check the noexcept condition. */
bool noexcept_p = COMPOUND_REQ_NOEXCEPT_P (t);
if (noexcept_p && !expr_noexcept_p (expr, tf_none))
- return error_mark_node;
+ {
+ if (info.diagnose_unsatisfaction_p ())
+ inform (loc, "%qE is not %<noexcept%>", expr);
+ else
+ return error_mark_node;
+ }
/* Substitute through the type expression, if any. */
- tree type = tsubst (t1, args, info.complain, info.in_decl);
+ tree type = tsubst_type_requirement_1 (t1, args, info, EXPR_LOCATION (t));
if (type == error_mark_node)
return error_mark_node;
@@ -2074,39 +2110,76 @@ tsubst_compound_requirement (tree t, tree args, subst_info info)
if (tree placeholder = type_uses_auto (type))
{
if (!type_deducible_p (expr, type, placeholder, args, quiet))
- return error_mark_node;
+ {
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ inform (loc,
+ "%qE does not satisfy return-type-requirement, "
+ "because", t0);
+ /* Further explain the reason for the error. */
+ type_deducible_p (expr, type, placeholder, args, info);
+ }
+ else
+ inform (loc,
+ "%qE does not satisfy return-type-requirement", t0);
+ }
+ return error_mark_node;
+ }
}
else if (!expression_convertible_p (expr, type, quiet))
- return error_mark_node;
+ {
+ if (info.diagnose_unsatisfaction_p ())
+ {
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ inform (loc, "cannot convert %qE to %qT because", t0, type);
+ /* Further explain the reason for the error. */
+ expression_convertible_p (expr, type, info);
+ }
+ else
+ inform (loc, "cannot convert %qE to %qT", t0, type);
+ }
+ return error_mark_node;
+ }
}
- return finish_compound_requirement (EXPR_LOCATION (t),
- expr, type, noexcept_p);
+ return boolean_true_node;
}
+/* Substitute through the nested requirement. */
+
static tree
-tsubst_nested_requirement (tree t, tree args, subst_info info)
+tsubst_nested_requirement (tree t, tree args, sat_info info)
{
- /* Perform satisfaction quietly with the regular normal form. */
sat_info quiet (tf_none, info.in_decl);
- tree norm = TREE_VALUE (TREE_TYPE (t));
- tree diag_norm = TREE_PURPOSE (TREE_TYPE (t));
- tree result = satisfy_constraint (norm, args, quiet);
- if (result == error_mark_node)
+ tree result = constraint_satisfaction_value (t, args, quiet);
+ if (result == boolean_true_node)
+ return boolean_true_node;
+
+ if (result == boolean_false_node
+ && info.diagnose_unsatisfaction_p ())
{
- /* Replay the error using the diagnostic normal form. */
- sat_info noisy (tf_warning_or_error, info.in_decl);
- satisfy_constraint (diag_norm, args, noisy);
+ tree expr = TREE_OPERAND (t, 0);
+ location_t loc = cp_expr_location (t);
+ if (diagnosing_failed_constraint::replay_errors_p ())
+ {
+ /* Replay the substitution error. */
+ inform (loc, "nested requirement %qE is not satisfied, because", expr);
+ constraint_satisfaction_value (t, args, info);
+ }
+ else
+ inform (loc, "nested requirement %qE is not satisfied", expr);
}
- if (result != boolean_true_node)
- return error_mark_node;
- return result;
+
+ return error_mark_node;
}
/* Substitute ARGS into the requirement T. */
static tree
-tsubst_requirement (tree t, tree args, subst_info info)
+tsubst_requirement (tree t, tree args, sat_info info)
{
iloc_sentinel loc_s (cp_expr_location (t));
switch (TREE_CODE (t))
@@ -2125,24 +2198,6 @@ tsubst_requirement (tree t, tree args, subst_info info)
gcc_unreachable ();
}
-/* Substitute ARGS into the list of requirements T. Note that
- substitution failures here result in ill-formed programs. */
-
-static tree
-tsubst_requirement_body (tree t, tree args, subst_info info)
-{
- tree result = NULL_TREE;
- while (t)
- {
- tree req = tsubst_requirement (TREE_VALUE (t), args, info);
- if (req == error_mark_node)
- return error_mark_node;
- result = tree_cons (NULL_TREE, req, result);
- t = TREE_CHAIN (t);
- }
- return nreverse (result);
-}
-
static tree
declare_constraint_vars (tree parms, tree vars)
{
@@ -2168,7 +2223,7 @@ declare_constraint_vars (tree parms, tree vars)
if an error occurred. */
static tree
-check_constaint_variables (tree t, tree args, subst_info info)
+check_constraint_variables (tree t, tree args, subst_info info)
{
tree types = NULL_TREE;
tree p = t;
@@ -2193,7 +2248,7 @@ static tree
tsubst_constraint_variables (tree t, tree args, subst_info info)
{
/* Perform a trial substitution to check for type errors. */
- tree parms = check_constaint_variables (t, args, info);
+ tree parms = check_constraint_variables (t, args, info);
if (parms == error_mark_node)
return error_mark_node;
@@ -2214,30 +2269,22 @@ tsubst_constraint_variables (tree t, tree args, subst_info info)
in its requirements ... In such cases, the expression evaluates
to false; it does not cause the program to be ill-formed.
- However, there are cases where substitution must produce a
- new requires-expression, that is not a template constraint.
- For example:
+ When substituting through a REQUIRES_EXPR as part of template
+ instantiation, we call this routine with info.quiet() true.
- template<typename T>
- class X {
- template<typename U>
- static constexpr bool var = requires (U u) { T::fn(u); };
- };
+ When evaluating a REQUIRES_EXPR that appears outside a template in
+ cp_parser_requires_expression, we call this routine with
+ info.noisy() true.
- In the instantiation of X<Y> (assuming Y defines fn), then the
- instantiated requires-expression would include Y::fn(u). If any
- substitution in the requires-expression fails, we can immediately
- fold the expression to false, as would be the case e.g., when
- instantiation X<int>. */
+ Finally, when diagnosing unsatisfaction from diagnose_atomic_constraint
+ and when diagnosing a false REQUIRES_EXPR via diagnose_constraints,
+ we call this routine with info.diagnose_unsatisfaction_p() true. */
-tree
-tsubst_requires_expr (tree t, tree args,
- tsubst_flags_t complain, tree in_decl)
+static tree
+tsubst_requires_expr (tree t, tree args, sat_info info)
{
local_specialization_stack stack (lss_copy);
- subst_info info (complain, in_decl);
-
/* A requires-expression is an unevaluated context. */
cp_unevaluated u;
@@ -2249,24 +2296,41 @@ tsubst_requires_expr (tree t, tree args,
checked out of order, so instead just remember the template
arguments and wait until we can substitute them all at once. */
t = copy_node (t);
- REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, complain);
+ REQUIRES_EXPR_EXTRA_ARGS (t) = build_extra_args (t, args, info.complain);
return t;
}
- tree parms = REQUIRES_EXPR_PARMS (t);
- if (parms)
+ if (tree parms = REQUIRES_EXPR_PARMS (t))
{
parms = tsubst_constraint_variables (parms, args, info);
if (parms == error_mark_node)
return boolean_false_node;
}
- tree reqs = REQUIRES_EXPR_REQS (t);
- reqs = tsubst_requirement_body (reqs, args, info);
- if (reqs == error_mark_node)
- return boolean_false_node;
+ tree result = boolean_true_node;
+ for (tree reqs = REQUIRES_EXPR_REQS (t); reqs; reqs = TREE_CHAIN (reqs))
+ {
+ tree req = TREE_VALUE (reqs);
+ if (tsubst_requirement (req, args, info) == error_mark_node)
+ {
+ result = boolean_false_node;
+ if (info.diagnose_unsatisfaction_p ())
+ /* Keep going so that we diagnose all failed requirements. */;
+ else
+ break;
+ }
+ }
+ return result;
+}
- return boolean_true_node;
+/* Public wrapper for the above. */
+
+tree
+tsubst_requires_expr (tree t, tree args,
+ tsubst_flags_t complain, tree in_decl)
+{
+ sat_info info (complain, in_decl);
+ return tsubst_requires_expr (t, args, info);
}
/* Substitute ARGS into the constraint information CI, producing a new
@@ -2304,48 +2368,23 @@ tsubst_parameter_mapping (tree map, tree args, subst_info info)
return error_mark_node;
tree parm = TREE_VALUE (p);
tree arg = TREE_PURPOSE (p);
- tree new_arg = NULL_TREE;
- if (TYPE_P (arg))
- {
- /* If a template parameter is declared with a placeholder, we can
- get those in the argument list if decltype is applied to the
- placeholder. For example:
-
- template<auto T>
- requires C<decltype(T)>
- void f() { }
-
- The normalized argument for C will be an auto type, so we'll
- need to deduce the actual argument from the corresponding
- initializer (whatever argument is provided for T), and use
- that result in the instantiated parameter mapping. */
- if (tree auto_node = type_uses_auto (arg))
- {
- int level;
- int index;
- template_parm_level_and_index (parm, &level, &index);
- tree init = TMPL_ARG (args, level, index);
- new_arg = do_auto_deduction (arg, init, auto_node,
- complain, adc_variable_type,
- make_tree_vec (0));
- }
- }
- else if (ARGUMENT_PACK_P (arg))
+ tree new_arg;
+ if (ARGUMENT_PACK_P (arg))
new_arg = tsubst_argument_pack (arg, args, complain, in_decl);
- if (!new_arg)
+ else
{
new_arg = tsubst_template_arg (arg, args, complain, in_decl);
if (TYPE_P (new_arg))
new_arg = canonicalize_type_argument (new_arg, complain);
- if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
+ }
+ if (TREE_CODE (new_arg) == TYPE_ARGUMENT_PACK)
+ {
+ tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
+ for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
{
- tree pack_args = ARGUMENT_PACK_ARGS (new_arg);
- for (int i = 0; i < TREE_VEC_LENGTH (pack_args); i++)
- {
- tree& pack_arg = TREE_VEC_ELT (pack_args, i);
- if (TYPE_P (pack_arg))
- pack_arg = canonicalize_type_argument (pack_arg, complain);
- }
+ tree& pack_arg = TREE_VEC_ELT (pack_args, i);
+ if (TYPE_P (pack_arg))
+ pack_arg = canonicalize_type_argument (pack_arg, complain);
}
}
if (new_arg == error_mark_node)
@@ -2522,7 +2561,7 @@ struct sat_hasher : ggc_ptr_hash<sat_entry>
/* Cache the result of satisfy_atom. */
static GTY((deletable)) hash_table<sat_hasher> *sat_cache;
-/* Cache the result of constraint_satisfaction_value. */
+/* Cache the result of satisfy_declaration_constraints. */
static GTY((deletable)) hash_map<tree, tree> *decl_satisfied_cache;
/* A tool used by satisfy_atom to help manage satisfaction caching and to
@@ -2877,7 +2916,7 @@ get_mapped_args (tree map)
return args;
}
-static void diagnose_atomic_constraint (tree, tree, tree, subst_info);
+static void diagnose_atomic_constraint (tree, tree, tree, sat_info);
/* Compute the satisfaction of an atomic constraint. */
@@ -3004,7 +3043,7 @@ satisfy_constraint_r (tree t, tree args, sat_info info)
/* Check that the normalized constraint T is satisfied for ARGS. */
static tree
-satisfy_constraint (tree t, tree args, sat_info info)
+satisfy_normalized_constraints (tree t, tree args, sat_info info)
{
auto_timevar time (TV_CONSTRAINT_SAT);
@@ -3020,65 +3059,94 @@ satisfy_constraint (tree t, tree args, sat_info info)
return satisfy_constraint_r (t, args, info);
}
-/* Check the normalized constraints T against ARGS, returning a satisfaction
- value (either true, false, or error). */
+/* Return the normal form of the constraints on the placeholder 'auto'
+ type T. */
static tree
-satisfy_associated_constraints (tree t, tree args, sat_info info)
+normalize_placeholder_type_constraints (tree t, bool diag)
{
- /* If there are no constraints then this is trivially satisfied. */
- if (!t)
- return boolean_true_node;
+ gcc_assert (is_auto (t));
+ tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t);
+ if (!ci)
+ return NULL_TREE;
- /* If any arguments depend on template parameters, we can't
- check constraints. Pretend they're satisfied for now. */
- if (args && uses_template_parms (args))
- return boolean_true_node;
+ tree constr = TREE_VALUE (ci);
+ /* The TREE_PURPOSE contains the set of template parameters that were in
+ scope for this placeholder type; use them as the initial template
+ parameters for normalization. */
+ tree initial_parms = TREE_PURPOSE (ci);
+ /* The 'auto' itself is used as the first argument in its own constraints,
+ and its level is one greater than its template depth. So in order to
+ capture all used template parameters, we need to add an extra level of
+ template parameters to the context; a dummy level suffices. */
+ initial_parms
+ = tree_cons (size_int (initial_parms
+ ? TMPL_PARMS_DEPTH (initial_parms) + 1 : 1),
+ make_tree_vec (0), initial_parms);
- return satisfy_constraint (t, args, info);
+ norm_info info (diag ? tf_norm : tf_none);
+ info.initial_parms = initial_parms;
+ return normalize_constraint_expression (constr, info);
}
-/* Evaluate EXPR as a constraint expression using ARGS, returning a
- satisfaction value. */
+/* Evaluate the constraints of T using ARGS, returning a satisfaction value.
+ Here, T can be a concept-id, nested-requirement, placeholder 'auto', or
+ requires-expression. */
static tree
-satisfy_constraint_expression (tree t, tree args, sat_info info)
+satisfy_nondeclaration_constraints (tree t, tree args, sat_info info)
{
if (t == error_mark_node)
return error_mark_node;
- gcc_assert (EXPR_P (t));
+ /* Handle REQUIRES_EXPR directly, bypassing satisfaction. */
+ if (TREE_CODE (t) == REQUIRES_EXPR)
+ {
+ auto ovr = make_temp_override (current_constraint_diagnosis_depth);
+ if (info.noisy ())
+ ++current_constraint_diagnosis_depth;
+ return tsubst_requires_expr (t, args, info);
+ }
/* Get the normalized constraints. */
tree norm;
- if (args == NULL_TREE && concept_check_p (t))
+ if (concept_check_p (t))
{
+ gcc_assert (!args);
tree id = unpack_concept_check (t);
args = TREE_OPERAND (id, 1);
tree tmpl = get_concept_check_template (id);
norm = normalize_concept_definition (tmpl, info.noisy ());
}
+ else if (TREE_CODE (t) == NESTED_REQ)
+ {
+ norm_info ninfo (info.noisy () ? tf_norm : tf_none);
+ /* The TREE_TYPE contains the set of template parameters that were in
+ scope for this nested requirement; use them as the initial template
+ parameters for normalization. */
+ ninfo.initial_parms = TREE_TYPE (t);
+ norm = normalize_constraint_expression (TREE_OPERAND (t, 0), ninfo);
+ }
+ else if (is_auto (t))
+ {
+ norm = normalize_placeholder_type_constraints (t, info.noisy ());
+ if (!norm)
+ return boolean_true_node;
+ }
else
- norm = normalize_constraint_expression (t, info.noisy ());
+ gcc_unreachable ();
/* Perform satisfaction. */
- return satisfy_constraint (norm, args, info);
+ return satisfy_normalized_constraints (norm, args, info);
}
-/* Used only to evaluate requires-expressions during constant expression
- evaluation. */
-
-tree
-satisfy_constraint_expression (tree expr)
-{
- sat_info info (tf_none, NULL_TREE);
- return satisfy_constraint_expression (expr, NULL_TREE, info);
-}
+/* Evaluate the associated constraints of the template specialization T
+ according to INFO, returning a satisfaction value. */
static tree
satisfy_declaration_constraints (tree t, sat_info info)
{
- gcc_assert (DECL_P (t));
+ gcc_assert (DECL_P (t) && TREE_CODE (t) != TEMPLATE_DECL);
const tree saved_t = t;
/* For inherited constructors, consider the original declaration;
@@ -3098,25 +3166,23 @@ satisfy_declaration_constraints (tree t, sat_info info)
if (tree *result = hash_map_safe_get (decl_satisfied_cache, saved_t))
return *result;
- /* Get the normalized constraints. */
- tree norm = NULL_TREE;
tree args = NULL_TREE;
if (tree ti = DECL_TEMPLATE_INFO (t))
{
- tree tmpl = TI_TEMPLATE (ti);
- norm = normalize_template_requirements (tmpl, info.noisy ());
-
/* The initial parameter mapping is the complete set of
template arguments substituted into the declaration. */
args = TI_ARGS (ti);
if (inh_ctor_targs)
args = add_outermost_template_args (args, inh_ctor_targs);
+
+ /* If any arguments depend on template parameters, we can't
+ check constraints. Pretend they're satisfied for now. */
+ if (uses_template_parms (args))
+ return boolean_true_node;
}
- else
- {
- /* These should be empty until we allow constraints on non-templates. */
- norm = normalize_nontemplate_requirements (t, info.noisy ());
- }
+
+ /* Get the normalized constraints. */
+ tree norm = get_normalized_constraints_from_decl (t, info.noisy ());
unsigned ftc_count = vec_safe_length (failed_type_completions);
@@ -3126,7 +3192,7 @@ satisfy_declaration_constraints (tree t, sat_info info)
if (!push_tinst_level (t))
return result;
push_access_scope (t);
- result = satisfy_associated_constraints (norm, args, info);
+ result = satisfy_normalized_constraints (norm, args, info);
pop_access_scope (t);
pop_tinst_level ();
}
@@ -3149,6 +3215,10 @@ satisfy_declaration_constraints (tree t, sat_info info)
return result;
}
+/* Evaluate the associated constraints of the template T using ARGS as the
+ innermost set of template arguments and according to INFO, returning a
+ satisfaction value. */
+
static tree
satisfy_declaration_constraints (tree t, tree args, sat_info info)
{
@@ -3159,14 +3229,19 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
args = add_outermost_template_args (t, args);
+ /* If any arguments depend on template parameters, we can't
+ check constraints. Pretend they're satisfied for now. */
+ if (uses_template_parms (args))
+ return boolean_true_node;
+
tree result = boolean_true_node;
- if (tree norm = normalize_template_requirements (t, info.noisy ()))
+ if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
{
if (!push_tinst_level (t, args))
return result;
tree pattern = DECL_TEMPLATE_RESULT (t);
push_access_scope (pattern);
- result = satisfy_associated_constraints (norm, args, info);
+ result = satisfy_normalized_constraints (norm, args, info);
pop_access_scope (pattern);
pop_tinst_level ();
}
@@ -3174,62 +3249,50 @@ satisfy_declaration_constraints (tree t, tree args, sat_info info)
return result;
}
+/* A wrapper around satisfy_declaration_constraints and
+ satisfy_nondeclaration_constraints which additionally replays
+ quiet ill-formed satisfaction noisily, so that ill-formed
+ satisfaction always gets diagnosed. */
+
static tree
-constraint_satisfaction_value (tree t, sat_info info)
+constraint_satisfaction_value (tree t, tree args, sat_info info)
{
tree r;
if (DECL_P (t))
- r = satisfy_declaration_constraints (t, info);
+ {
+ if (args)
+ r = satisfy_declaration_constraints (t, args, info);
+ else
+ r = satisfy_declaration_constraints (t, info);
+ }
else
- r = satisfy_constraint_expression (t, NULL_TREE, info);
+ r = satisfy_nondeclaration_constraints (t, args, info);
if (r == error_mark_node && info.quiet ()
&& !(DECL_P (t) && TREE_NO_WARNING (t)))
{
- /* Replay the error with re-normalized requirements. */
+ /* Replay the error noisily. */
sat_info noisy (tf_warning_or_error, info.in_decl);
- constraint_satisfaction_value (t, noisy);
- if (DECL_P (t))
+ constraint_satisfaction_value (t, args, noisy);
+ if (DECL_P (t) && !args)
/* Avoid giving these errors again. */
TREE_NO_WARNING (t) = true;
}
return r;
}
-static tree
-constraint_satisfaction_value (tree t, tree args, sat_info info)
-{
- tree r;
- if (DECL_P (t))
- r = satisfy_declaration_constraints (t, args, info);
- else
- r = satisfy_constraint_expression (t, args, info);
- if (r == error_mark_node && info.quiet ())
- {
- /* Replay the error with re-normalized requirements. */
- sat_info noisy (tf_warning_or_error, info.in_decl);
- constraint_satisfaction_value (t, args, noisy);
- }
- return r;
-}
-
-/* True iff the result of satisfying T is BOOLEAN_TRUE_NODE and false
- otherwise, even in the case of errors. */
+/* True iff the result of satisfying T using ARGS is BOOLEAN_TRUE_NODE
+ and false otherwise, even in the case of errors.
-bool
-constraints_satisfied_p (tree t)
-{
- if (!flag_concepts)
- return true;
-
- sat_info quiet (tf_none, NULL_TREE);
- return constraint_satisfaction_value (t, quiet) == boolean_true_node;
-}
-
-/* True iff the result of satisfying T with ARGS is BOOLEAN_TRUE_NODE
- and false otherwise, even in the case of errors. */
+ Here, T can be:
+ - a template declaration
+ - a template specialization (in which case ARGS must be empty)
+ - a concept-id (in which case ARGS must be empty)
+ - a nested-requirement
+ - a placeholder 'auto'
+ - a requires-expression. */
bool
-constraints_satisfied_p (tree t, tree args)
+constraints_satisfied_p (tree t, tree args/*= NULL_TREE */)
{
if (!flag_concepts)
return true;
@@ -3242,7 +3305,7 @@ constraints_satisfied_p (tree t, tree args)
evaluation of template-ids as id-expressions. */
tree
-evaluate_concept_check (tree check, tsubst_flags_t complain)
+evaluate_concept_check (tree check)
{
if (check == error_mark_node)
return error_mark_node;
@@ -3251,14 +3314,19 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
/* Check for satisfaction without diagnostics. */
sat_info quiet (tf_none, NULL_TREE);
- tree result = satisfy_constraint_expression (check, NULL_TREE, quiet);
- if (result == error_mark_node && (complain & tf_error))
- {
- /* Replay the error with re-normalized requirements. */
- sat_info noisy (tf_warning_or_error, NULL_TREE);
- satisfy_constraint_expression (check, NULL_TREE, noisy);
- }
- return result;
+ return constraint_satisfaction_value (check, /*args=*/NULL_TREE, quiet);
+}
+
+/* Evaluate the requires-expression T, returning either boolean_true_node
+ or boolean_false_node. This is used during gimplification and constexpr
+ evaluation. */
+
+tree
+evaluate_requires_expr (tree t)
+{
+ gcc_assert (TREE_CODE (t) == REQUIRES_EXPR);
+ sat_info quiet (tf_none, NULL_TREE);
+ return constraint_satisfaction_value (t, /*args=*/NULL_TREE, quiet);
}
/*---------------------------------------------------------------------------
@@ -3271,15 +3339,6 @@ evaluate_concept_check (tree check, tsubst_flags_t complain)
tree
finish_requires_expr (location_t loc, tree parms, tree reqs)
{
- /* Modify the declared parameters by removing their context
- so they don't refer to the enclosing scope and explicitly
- indicating that they are constraint variables. */
- for (tree parm = parms; parm; parm = DECL_CHAIN (parm))
- {
- DECL_CONTEXT (parm) = NULL_TREE;
- CONSTRAINT_VAR_P (parm) = true;
- }
-
/* Build the node. */
tree r = build_min (REQUIRES_EXPR, boolean_type_node, parms, reqs, NULL_TREE);
TREE_SIDE_EFFECTS (r) = false;
@@ -3328,15 +3387,9 @@ finish_compound_requirement (location_t loc, tree expr, tree type, bool noexcept
tree
finish_nested_requirement (location_t loc, tree expr)
{
- /* We need to normalize the constraints now, at parse time, while
- we have the necessary template context. We normalize twice,
- once without diagnostic information and once with, which we'll
- later use for quiet and noisy satisfaction respectively. */
- tree norm = normalize_constraint_expression (expr, /*diag=*/false);
- tree diag_norm = normalize_constraint_expression (expr, /*diag=*/true);
-
- /* Build the constraint, saving its two normalizations as its type. */
- tree r = build1 (NESTED_REQ, build_tree_list (diag_norm, norm), expr);
+ /* Build the requirement, saving the set of in-scope template
+ parameters as its type. */
+ tree r = build1 (NESTED_REQ, current_template_parms, expr);
SET_EXPR_LOCATION (r, loc);
return r;
}
@@ -3535,11 +3588,10 @@ get_constraint_error_location (tree t)
/* Emit a diagnostic for a failed trait. */
-void
-diagnose_trait_expr (tree expr, tree map)
+static void
+diagnose_trait_expr (tree expr, tree args)
{
location_t loc = cp_expr_location (expr);
- tree args = get_mapped_args (map);
/* Build a "fake" version of the instantiated trait, so we can
get the instantiated types from result. */
@@ -3619,194 +3671,11 @@ diagnose_trait_expr (tree expr, tree map)
}
}
-static tree
-diagnose_valid_expression (tree expr, tree args, tree in_decl)
-{
- tree result = tsubst_expr (expr, args, tf_none, in_decl, false);
- if (result != error_mark_node
- && convert_to_void (result, ICV_STATEMENT, tf_none) != error_mark_node)
- return result;
-
- location_t loc = cp_expr_loc_or_input_loc (expr);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- /* Replay the substitution error. */
- inform (loc, "the required expression %qE is invalid, because", expr);
- if (result == error_mark_node)
- tsubst_expr (expr, args, tf_error, in_decl, false);
- else
- convert_to_void (result, ICV_STATEMENT, tf_error);
- }
- else
- inform (loc, "the required expression %qE is invalid", expr);
-
- return error_mark_node;
-}
-
-static tree
-diagnose_valid_type (tree type, tree args, tree in_decl)
-{
- tree result = tsubst (type, args, tf_none, in_decl);
- if (result != error_mark_node)
- return result;
-
- location_t loc = cp_expr_loc_or_input_loc (type);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- /* Replay the substitution error. */
- inform (loc, "the required type %qT is invalid, because", type);
- tsubst (type, args, tf_error, in_decl);
- }
- else
- inform (loc, "the required type %qT is invalid", type);
-
- return error_mark_node;
-}
-
-static void
-diagnose_simple_requirement (tree req, tree args, tree in_decl)
-{
- diagnose_valid_expression (TREE_OPERAND (req, 0), args, in_decl);
-}
-
-static void
-diagnose_compound_requirement (tree req, tree args, tree in_decl)
-{
- tree expr = TREE_OPERAND (req, 0);
- expr = diagnose_valid_expression (expr, args, in_decl);
- if (expr == error_mark_node)
- return;
-
- location_t loc = cp_expr_loc_or_input_loc (expr);
-
- /* Check the noexcept condition. */
- if (COMPOUND_REQ_NOEXCEPT_P (req) && !expr_noexcept_p (expr, tf_none))
- inform (loc, "%qE is not %<noexcept%>", expr);
-
- tree type = TREE_OPERAND (req, 1);
- type = diagnose_valid_type (type, args, in_decl);
- if (type == error_mark_node)
- return;
-
- if (type)
- {
- subst_info quiet (tf_none, in_decl);
- subst_info noisy (tf_error, in_decl);
-
- /* Check the expression against the result type. */
- if (tree placeholder = type_uses_auto (type))
- {
- if (!type_deducible_p (expr, type, placeholder, args, quiet))
- {
- tree orig_expr = TREE_OPERAND (req, 0);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- inform (loc,
- "%qE does not satisfy return-type-requirement, "
- "because", orig_expr);
- /* Further explain the reason for the error. */
- type_deducible_p (expr, type, placeholder, args, noisy);
- }
- else
- inform (loc, "%qE does not satisfy return-type-requirement",
- orig_expr);
- }
- }
- else if (!expression_convertible_p (expr, type, quiet))
- {
- tree orig_expr = TREE_OPERAND (req, 0);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- inform (loc, "cannot convert %qE to %qT because", orig_expr, type);
- /* Further explain the reason for the error. */
- expression_convertible_p (expr, type, noisy);
- }
- else
- inform (loc, "cannot convert %qE to %qT", orig_expr, type);
- }
- }
-}
-
-static void
-diagnose_type_requirement (tree req, tree args, tree in_decl)
-{
- tree type = TREE_OPERAND (req, 0);
- diagnose_valid_type (type, args, in_decl);
-}
-
-static void
-diagnose_nested_requirement (tree req, tree args)
-{
- /* Quietly check for satisfaction first using the regular normal form.
- We can elaborate details later if needed. */
- tree norm = TREE_VALUE (TREE_TYPE (req));
- tree diag_norm = TREE_PURPOSE (TREE_TYPE (req));
- sat_info info (tf_none, NULL_TREE);
- tree result = satisfy_constraint (norm, args, info);
- if (result == boolean_true_node)
- return;
-
- tree expr = TREE_OPERAND (req, 0);
- location_t loc = cp_expr_location (expr);
- if (diagnosing_failed_constraint::replay_errors_p ())
- {
- /* Replay the substitution error using the diagnostic normal form. */
- inform (loc, "nested requirement %qE is not satisfied, because", expr);
- sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
- satisfy_constraint (diag_norm, args, noisy);
- }
- else
- inform (loc, "nested requirement %qE is not satisfied", expr);
-
-}
-
-static void
-diagnose_requirement (tree req, tree args, tree in_decl)
-{
- iloc_sentinel loc_s (cp_expr_location (req));
- switch (TREE_CODE (req))
- {
- case SIMPLE_REQ:
- return diagnose_simple_requirement (req, args, in_decl);
- case COMPOUND_REQ:
- return diagnose_compound_requirement (req, args, in_decl);
- case TYPE_REQ:
- return diagnose_type_requirement (req, args, in_decl);
- case NESTED_REQ:
- return diagnose_nested_requirement (req, args);
- default:
- gcc_unreachable ();
- }
-}
-
-static void
-diagnose_requires_expr (tree expr, tree map, tree in_decl)
-{
- local_specialization_stack stack (lss_copy);
- tree parms = TREE_OPERAND (expr, 0);
- tree body = TREE_OPERAND (expr, 1);
- tree args = get_mapped_args (map);
-
- cp_unevaluated u;
- subst_info info (tf_warning_or_error, NULL_TREE);
- tree vars = tsubst_constraint_variables (parms, args, info);
- if (vars == error_mark_node)
- return;
-
- tree p = body;
- while (p)
- {
- tree req = TREE_VALUE (p);
- diagnose_requirement (req, args, in_decl);
- p = TREE_CHAIN (p);
- }
-}
-
/* Diagnose a substitution failure in the atomic constraint T when applied
with the instantiated parameter mapping MAP. */
static void
-diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info)
+diagnose_atomic_constraint (tree t, tree map, tree result, sat_info info)
{
/* If the constraint is already ill-formed, we've previously diagnosed
the reason. We should still say why the constraints aren't satisfied. */
@@ -3827,13 +3696,19 @@ diagnose_atomic_constraint (tree t, tree map, tree result, subst_info info)
/* Generate better diagnostics for certain kinds of expressions. */
tree expr = ATOMIC_CONSTR_EXPR (t);
STRIP_ANY_LOCATION_WRAPPER (expr);
+ tree args = get_mapped_args (map);
switch (TREE_CODE (expr))
{
case TRAIT_EXPR:
- diagnose_trait_expr (expr, map);
+ diagnose_trait_expr (expr, args);
break;
case REQUIRES_EXPR:
- diagnose_requires_expr (expr, map, info.in_decl);
+ gcc_checking_assert (info.diagnose_unsatisfaction_p ());
+ /* Clear in_decl before replaying the substitution to avoid emitting
+ seemingly unhelpful "in declaration ..." notes that follow some
+ substitution failure error messages. */
+ info.in_decl = NULL_TREE;
+ tsubst_requires_expr (expr, args, info);
break;
default:
if (!same_type_p (TREE_TYPE (result), boolean_type_node))
@@ -3886,7 +3761,7 @@ diagnosing_failed_constraint::replay_errors_p ()
}
/* Emit diagnostics detailing the failure ARGS to satisfy the constraints
- of T. Here, T can be either a constraint or a declaration. */
+ of T. Here, T and ARGS are as in constraints_satisfied_p. */
void
diagnose_constraints (location_t loc, tree t, tree args)
@@ -3898,10 +3773,7 @@ diagnose_constraints (location_t loc, tree t, tree args)
/* Replay satisfaction, but diagnose unsatisfaction. */
sat_info noisy (tf_warning_or_error, NULL_TREE, /*diag_unsat=*/true);
- if (!args)
- constraint_satisfaction_value (t, noisy);
- else
- constraint_satisfaction_value (t, args, noisy);
+ constraint_satisfaction_value (t, args, noisy);
static bool suggested_p;
if (concepts_diagnostics_max_depth_exceeded_p
diff --git a/gcc/cp/coroutines.cc b/gcc/cp/coroutines.cc
index e61de1f..c5aeb66 100644
--- a/gcc/cp/coroutines.cc
+++ b/gcc/cp/coroutines.cc
@@ -683,11 +683,14 @@ coro_common_keyword_context_valid_p (tree fndecl, location_t kw_loc,
if (DECL_DECLARED_CONSTEXPR_P (fndecl))
{
- /* [dcl.constexpr] 3.3 it shall not be a coroutine. */
- error_at (kw_loc, "%qs cannot be used in a %<constexpr%> function",
- kw_name);
cp_function_chain->invalid_constexpr = true;
- return false;
+ if (!is_instantiation_of_constexpr (fndecl))
+ {
+ /* [dcl.constexpr] 3.3 it shall not be a coroutine. */
+ error_at (kw_loc, "%qs cannot be used in a %<constexpr%> function",
+ kw_name);
+ return false;
+ }
}
if (FNDECL_USED_AUTO (fndecl))
@@ -790,6 +793,43 @@ get_awaitable_var (suspend_point_kind suspend_kind, tree v_type)
return ret;
}
+/* Helpers to diagnose missing noexcept on final await expressions. */
+
+static bool
+coro_diagnose_throwing_fn (tree fndecl)
+{
+ if (!TYPE_NOTHROW_P (TREE_TYPE (fndecl)))
+ {
+ location_t f_loc = cp_expr_loc_or_loc (fndecl,
+ DECL_SOURCE_LOCATION (fndecl));
+ error_at (f_loc, "the expression %qE is required to be non-throwing",
+ fndecl);
+ inform (f_loc, "must be declared with %<noexcept(true)%>");
+ return true;
+ }
+ return false;
+}
+
+static bool
+coro_diagnose_throwing_final_aw_expr (tree expr)
+{
+ tree t = TARGET_EXPR_INITIAL (expr);
+ tree fn = NULL_TREE;
+ if (TREE_CODE (t) == CALL_EXPR)
+ fn = CALL_EXPR_FN(t);
+ else if (TREE_CODE (t) == AGGR_INIT_EXPR)
+ fn = AGGR_INIT_EXPR_FN (t);
+ else if (TREE_CODE (t) == CONSTRUCTOR)
+ return false;
+ else
+ {
+ gcc_checking_assert (0 && "unhandled expression type");
+ return false;
+ }
+ fn = TREE_OPERAND (fn, 0);
+ return coro_diagnose_throwing_fn (fn);
+}
+
/* This performs [expr.await] bullet 3.3 and validates the interface obtained.
It is also used to build the initial and final suspend points.
@@ -812,6 +852,29 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
/* If no viable functions are found, o is a. */
if (!o || o == error_mark_node)
o = a;
+ else if (flag_exceptions && suspend_kind == FINAL_SUSPEND_POINT)
+ {
+ /* We found an overload for co_await(), diagnose throwing cases. */
+ if (TREE_CODE (o) == TARGET_EXPR
+ && coro_diagnose_throwing_final_aw_expr (o))
+ return error_mark_node;
+
+ /* We now know that the final suspend object is distinct from the
+ final awaiter, so check for a non-throwing DTOR where needed. */
+ tree a_type = TREE_TYPE (a);
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (a_type))
+ if (tree dummy
+ = build_special_member_call (a, complete_dtor_identifier,
+ NULL, a_type, LOOKUP_NORMAL,
+ tf_none))
+ {
+ if (CONVERT_EXPR_P (dummy))
+ dummy = TREE_OPERAND (dummy, 0);
+ dummy = TREE_OPERAND (CALL_EXPR_FN (dummy), 0);
+ if (coro_diagnose_throwing_fn (dummy))
+ return error_mark_node;
+ }
+ }
}
else
o = a; /* This is most likely about to fail anyway. */
@@ -887,7 +950,7 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
e_proxy = o;
o = NULL_TREE; /* The var is already present. */
}
- else if (CLASS_TYPE_P (o_type) || TYPE_NEEDS_CONSTRUCTING (o_type))
+ else if (type_build_ctor_call (o_type))
{
e_proxy = get_awaitable_var (suspend_kind, o_type);
releasing_vec arg (make_tree_vector_single (rvalue (o)));
@@ -955,6 +1018,28 @@ build_co_await (location_t loc, tree a, suspend_point_kind suspend_kind)
if (!awrs_func || !awrs_call || awrs_call == error_mark_node)
return error_mark_node;
+ if (flag_exceptions && suspend_kind == FINAL_SUSPEND_POINT)
+ {
+ if (coro_diagnose_throwing_fn (awrd_func))
+ return error_mark_node;
+ if (coro_diagnose_throwing_fn (awsp_func))
+ return error_mark_node;
+ if (coro_diagnose_throwing_fn (awrs_func))
+ return error_mark_node;
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (o_type))
+ if (tree dummy
+ = build_special_member_call (e_proxy, complete_dtor_identifier,
+ NULL, o_type, LOOKUP_NORMAL,
+ tf_none))
+ {
+ if (CONVERT_EXPR_P (dummy))
+ dummy = TREE_OPERAND (dummy, 0);
+ dummy = TREE_OPERAND (CALL_EXPR_FN (dummy), 0);
+ if (coro_diagnose_throwing_fn (dummy))
+ return error_mark_node;
+ }
+ }
+
/* We now have three call expressions, in terms of the promise, handle and
'e' proxies. Save them in the await expression for later expansion. */
@@ -1812,8 +1897,11 @@ 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. */
+ bool rv_ref; /* Was an rvalue ref. */
bool trivial_dtor; /* The frame type has a trivial DTOR. */
bool this_ptr; /* Is 'this' */
bool lambda_cobj; /* Lambda capture object */
@@ -1983,6 +2071,73 @@ transform_local_var_uses (tree *stmt, int *do_subtree, void *d)
return NULL_TREE;
}
+/* A helper to build the frame DTOR.
+ [dcl.fct.def.coroutine] / 12
+ The deallocation function’s name is looked up in the scope of the promise
+ type. If this lookup fails, the deallocation function’s name is looked up
+ in the global scope. If deallocation function lookup finds both a usual
+ deallocation function with only a pointer parameter and a usual
+ deallocation function with both a pointer parameter and a size parameter,
+ then the selected deallocation function shall be the one with two
+ parameters. Otherwise, the selected deallocation function shall be the
+ function with one parameter. If no usual deallocation function is found
+ the program is ill-formed. The selected deallocation function shall be
+ called with the address of the block of storage to be reclaimed as its
+ first argument. If a deallocation function with a parameter of type
+ std::size_t is used, the size of the block is passed as the corresponding
+ argument. */
+
+static tree
+coro_get_frame_dtor (tree coro_fp, tree orig, tree frame_size,
+ tree promise_type, location_t loc)
+{
+ tree del_coro_fr = NULL_TREE;
+ tree frame_arg = build1 (CONVERT_EXPR, ptr_type_node, coro_fp);
+ tree delname = ovl_op_identifier (false, DELETE_EXPR);
+ tree fns = lookup_promise_method (orig, delname, loc,
+ /*musthave=*/false);
+ if (fns && BASELINK_P (fns))
+ {
+ /* Look for sized version first, since this takes precedence. */
+ vec<tree, va_gc> *args = make_tree_vector ();
+ vec_safe_push (args, frame_arg);
+ vec_safe_push (args, frame_size);
+ tree dummy_promise = build_dummy_object (promise_type);
+
+ /* It's OK to fail for this one... */
+ del_coro_fr = build_new_method_call (dummy_promise, fns, &args,
+ NULL_TREE, LOOKUP_NORMAL, NULL,
+ tf_none);
+
+ if (!del_coro_fr || del_coro_fr == error_mark_node)
+ {
+ release_tree_vector (args);
+ args = make_tree_vector_single (frame_arg);
+ del_coro_fr = build_new_method_call (dummy_promise, fns, &args,
+ NULL_TREE, LOOKUP_NORMAL, NULL,
+ tf_none);
+ }
+
+ /* But one of them must succeed, or the program is ill-formed. */
+ if (!del_coro_fr || del_coro_fr == error_mark_node)
+ {
+ error_at (loc, "%qE is provided by %qT but is not usable with"
+ " the function signature %qD", delname, promise_type, orig);
+ del_coro_fr = error_mark_node;
+ }
+ }
+ else
+ {
+ del_coro_fr = build_op_delete_call (DELETE_EXPR, frame_arg, frame_size,
+ /*global_p=*/true, /*placement=*/NULL,
+ /*alloc_fn=*/NULL,
+ tf_warning_or_error);
+ if (!del_coro_fr || del_coro_fr == error_mark_node)
+ del_coro_fr = error_mark_node;
+ }
+ return del_coro_fr;
+}
+
/* The actor transform. */
static void
@@ -2279,68 +2434,10 @@ build_actor_fn (location_t loc, tree coro_frame_type, tree actor, tree fnbody,
}
}
- /* [dcl.fct.def.coroutine] / 12
- The deallocation function’s name is looked up in the scope of the promise
- type. If this lookup fails, the deallocation function’s name is looked up
- in the global scope. If deallocation function lookup finds both a usual
- deallocation function with only a pointer parameter and a usual
- deallocation function with both a pointer parameter and a size parameter,
- then the selected deallocation function shall be the one with two
- parameters. Otherwise, the selected deallocation function shall be the
- function with one parameter. If no usual deallocation function is found
- the program is ill-formed. The selected deallocation function shall be
- called with the address of the block of storage to be reclaimed as its
- first argument. If a deallocation function with a parameter of type
- std::size_t is used, the size of the block is passed as the corresponding
- argument. */
-
- tree del_coro_fr = NULL_TREE;
- tree frame_arg = build1 (CONVERT_EXPR, ptr_type_node, actor_fp);
-
- tree delname = ovl_op_identifier (false, DELETE_EXPR);
- tree fns = lookup_promise_method (orig, delname, loc, /*musthave=*/false);
- if (fns && BASELINK_P (fns))
- {
- /* Look for sized version first, since this takes precedence. */
- vec<tree, va_gc> *args = make_tree_vector ();
- vec_safe_push (args, frame_arg);
- vec_safe_push (args, frame_size);
- tree dummy_promise = build_dummy_object (promise_type);
-
- /* It's OK to fail for this one... */
- del_coro_fr = build_new_method_call (dummy_promise, fns, &args,
- NULL_TREE, LOOKUP_NORMAL, NULL,
- tf_none);
-
- if (!del_coro_fr || del_coro_fr == error_mark_node)
- {
- release_tree_vector (args);
- args = make_tree_vector_single (frame_arg);
- del_coro_fr = build_new_method_call (dummy_promise, fns, &args,
- NULL_TREE, LOOKUP_NORMAL, NULL,
- tf_none);
- }
-
- /* But one of them must succeed, or the program is ill-formed. */
- if (!del_coro_fr || del_coro_fr == error_mark_node)
- {
- error_at (loc, "%qE is provided by %qT but is not usable with"
- " the function signature %qD", delname, promise_type, orig);
- del_coro_fr = error_mark_node;
- }
- }
- else
- {
- del_coro_fr = build_op_delete_call (DELETE_EXPR, frame_arg, frame_size,
- /*global_p=*/true, /*placement=*/NULL,
- /*alloc_fn=*/NULL,
- tf_warning_or_error);
- if (!del_coro_fr || del_coro_fr == error_mark_node)
- del_coro_fr = error_mark_node;
- }
-
- del_coro_fr = coro_build_cvt_void_expr_stmt (del_coro_fr, loc);
- add_stmt (del_coro_fr);
+ /* Build the frame DTOR. */
+ tree del_coro_fr = coro_get_frame_dtor (actor_fp, orig, 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;
@@ -2523,6 +2620,11 @@ build_init_or_final_await (location_t loc, bool is_final)
= coro_build_promise_expression (current_function_decl, NULL, suspend_alt,
loc, NULL, /*musthave=*/true);
+ /* Check for noexcept on the final_suspend call. */
+ if (flag_exceptions && is_final && setup_call != error_mark_node
+ && coro_diagnose_throwing_final_aw_expr (setup_call))
+ return error_mark_node;
+
/* So build the co_await for this */
/* For initial/final suspends the call is "a" per [expr.await] 3.2. */
return build_co_await (loc, setup_call, (is_final ? FINAL_SUSPEND_POINT
@@ -2865,7 +2967,7 @@ flatten_await_stmt (var_nest_node *n, hash_set<tree> *promoted,
gcc_checking_assert (!already_present);
tree inner = TREE_OPERAND (init, 1);
gcc_checking_assert (TREE_CODE (inner) != COND_EXPR);
- if (TYPE_NEEDS_CONSTRUCTING (var_type))
+ if (type_build_ctor_call (var_type))
{
releasing_vec p_in (make_tree_vector_single (init));
init = build_special_member_call (var, complete_ctor_identifier,
@@ -2877,9 +2979,9 @@ flatten_await_stmt (var_nest_node *n, hash_set<tree> *promoted,
var_nest_node *ins
= new var_nest_node (var, init, n->prev, n);
/* We have to replace the target expr... */
- proxy_replace pr = {TREE_OPERAND (t, 0), var};
*v.entry = var;
/* ... and any uses of its var. */
+ proxy_replace pr = {TREE_OPERAND (t, 0), var};
cp_walk_tree (&n->init, replace_proxy, &pr, NULL);
/* Compiler-generated temporaries can also have uses in following
arms of compound expressions, which will be listed in 'replace_in'
@@ -4026,6 +4128,17 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
TREE_OPERAND (body_start, 0) = push_stmt_list ();
}
+ /* If the original function has a return value with a non-trivial DTOR
+ and the body contains a var with a DTOR that might throw, the decl is
+ marked "throwing_cleanup".
+ We do not [in the ramp, which is synthesised here], use any body var
+ types with DTORs that might throw.
+ The original body is transformed into the actor function which only
+ contains void returns, and is also wrapped in a try-catch block.
+ So (a) the 'throwing_cleanup' is not correct for the ramp and (b) we do
+ not need to transfer it to the actor which only contains void returns. */
+ cp_function_chain->throwing_cleanup = false;
+
/* Create the coro frame type, as far as it can be known at this stage.
1. Types we already know. */
@@ -4107,7 +4220,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
if (actual_type == NULL_TREE)
actual_type = error_mark_node;
parm.orig_type = actual_type;
- parm.by_ref = parm.pt_ref = false;
+ parm.by_ref = parm.pt_ref = parm.rv_ref = false;
if (TREE_CODE (actual_type) == REFERENCE_TYPE)
{
/* If the user passes by reference, then we will save the
@@ -4115,8 +4228,10 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
[dcl.fct.def.coroutine] / 13, if the lifetime of the
referenced item ends and then the coroutine is resumed,
we have UB; well, the user asked for it. */
- actual_type = build_pointer_type (TREE_TYPE (actual_type));
- parm.pt_ref = true;
+ if (TYPE_REF_IS_RVALUE (actual_type))
+ parm.rv_ref = true;
+ else
+ parm.pt_ref = true;
}
else if (TYPE_REF_P (DECL_ARG_TYPE (arg)))
parm.by_ref = true;
@@ -4126,7 +4241,6 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
parm.this_ptr = is_this_parameter (arg);
parm.lambda_cobj = lambda_p && DECL_NAME (arg) == closure_identifier;
- parm.trivial_dtor = TYPE_HAS_TRIVIAL_DESTRUCTOR (parm.frame_type);
char *buf;
if (DECL_NAME (arg))
{
@@ -4135,6 +4249,20 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
}
else
buf = xasprintf ("__unnamed_parm.%d", no_name_parm++);
+
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (parm.frame_type))
+ {
+ char *gbuf = xasprintf ("%s.live", buf);
+ parm.guard_var
+ = build_lang_decl (VAR_DECL, get_identifier (gbuf),
+ boolean_type_node);
+ free (gbuf);
+ DECL_ARTIFICIAL (parm.guard_var) = true;
+ DECL_INITIAL (parm.guard_var) = boolean_false_node;
+ parm.trivial_dtor = false;
+ }
+ else
+ parm.trivial_dtor = true;
parm.field_id = coro_make_frame_entry
(&field_list, buf, actual_type, DECL_SOURCE_LOCATION (arg));
free (buf);
@@ -4191,6 +4319,37 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
coro_frame_ptr);
tree varlist = coro_fp;
+ /* To signal that we need to cleanup copied function args. */
+ if (flag_exceptions && DECL_ARGUMENTS (orig))
+ for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+ arg = DECL_CHAIN (arg))
+ {
+ param_info *parm_i = param_uses->get (arg);
+ gcc_checking_assert (parm_i);
+ if (parm_i->trivial_dtor)
+ continue;
+ DECL_CHAIN (parm_i->guard_var) = varlist;
+ varlist = parm_i->guard_var;
+ }
+
+ /* Signal that we need to clean up the promise object on exception. */
+ tree coro_promise_live
+ = build_lang_decl (VAR_DECL, get_identifier ("coro.promise.live"),
+ boolean_type_node);
+ DECL_ARTIFICIAL (coro_promise_live) = true;
+ DECL_CHAIN (coro_promise_live) = varlist;
+ varlist = coro_promise_live;
+ DECL_INITIAL (coro_promise_live) = boolean_false_node;
+ /* When the get-return-object is in the RETURN slot, we need to arrange for
+ cleanup on exception. */
+ tree coro_gro_live
+ = build_lang_decl (VAR_DECL, get_identifier ("coro.gro.live"),
+ boolean_type_node);
+ DECL_ARTIFICIAL (coro_gro_live) = true;
+ DECL_CHAIN (coro_gro_live) = varlist;
+ varlist = coro_gro_live;
+ DECL_INITIAL (coro_gro_live) = boolean_false_node;
+
/* Collected the scope vars we need ... only one for now. */
BIND_EXPR_VARS (ramp_bind) = nreverse (varlist);
@@ -4208,6 +4367,17 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tree zeroinit = build1 (CONVERT_EXPR, coro_frame_ptr, integer_zero_node);
DECL_INITIAL (coro_fp) = zeroinit;
add_decl_expr (coro_fp);
+ if (flag_exceptions && DECL_ARGUMENTS (orig))
+ for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+ arg = DECL_CHAIN (arg))
+ {
+ param_info *parm_i = param_uses->get (arg);
+ if (parm_i->trivial_dtor)
+ continue;
+ add_decl_expr (parm_i->guard_var);;
+ }
+ add_decl_expr (coro_promise_live);
+ add_decl_expr (coro_gro_live);
/* The CO_FRAME internal function is a mechanism to allow the middle end
to adjust the allocation in response to optimizations. We provide the
@@ -4401,6 +4571,20 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
finish_if_stmt (if_stmt);
}
+ /* 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_cleanup = NULL_TREE;
+ if (flag_exceptions)
+ {
+ ramp_cleanup = build_stmt (fn_start, TRY_BLOCK, NULL, NULL);
+ add_stmt (ramp_cleanup);
+ TRY_STMTS (ramp_cleanup) = push_stmt_list ();
+ }
+
/* deref the frame pointer, to use in member access code. */
tree deref_fp = build_x_arrow (fn_start, coro_fp, tf_warning_or_error);
@@ -4484,16 +4668,22 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tree this_ref = build1 (INDIRECT_REF, ct, arg);
tree rt = cp_build_reference_type (ct, false);
this_ref = convert_to_reference (rt, this_ref, CONV_STATIC,
- LOOKUP_NORMAL , NULL_TREE,
+ LOOKUP_NORMAL, NULL_TREE,
tf_warning_or_error);
vec_safe_push (promise_args, this_ref);
}
- else if (parm.by_ref)
- vec_safe_push (promise_args, fld_idx);
+ else if (parm.rv_ref)
+ vec_safe_push (promise_args, rvalue(fld_idx));
else
- vec_safe_push (promise_args, arg);
+ vec_safe_push (promise_args, fld_idx);
- if (TYPE_NEEDS_CONSTRUCTING (parm.frame_type))
+ if (parm.rv_ref || parm.pt_ref)
+ /* Initialise the frame reference field directly. */
+ r = build_modify_expr (fn_start, TREE_OPERAND (fld_idx, 0),
+ parm.frame_type, INIT_EXPR,
+ DECL_SOURCE_LOCATION (arg), arg,
+ DECL_ARG_TYPE (arg));
+ else if (type_build_ctor_call (parm.frame_type))
{
vec<tree, va_gc> *p_in;
if (CLASS_TYPE_P (parm.frame_type)
@@ -4521,13 +4711,23 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
INIT_EXPR, DECL_SOURCE_LOCATION (arg), r,
TREE_TYPE (r));
}
- r = coro_build_cvt_void_expr_stmt (r, fn_start);
- add_stmt (r);
+ finish_expr_stmt (r);
if (!parm.trivial_dtor)
{
if (param_dtor_list == NULL)
param_dtor_list = make_tree_vector ();
vec_safe_push (param_dtor_list, parm.field_id);
+ /* Cleanup this frame copy on exception. */
+ parm.fr_copy_dtor
+ = build_special_member_call (fld_idx, complete_dtor_identifier,
+ NULL, parm.frame_type,
+ LOOKUP_NORMAL,
+ tf_warning_or_error);
+ /* This var is now live. */
+ r = build_modify_expr (fn_start, parm.guard_var,
+ boolean_type_node, INIT_EXPR, fn_start,
+ boolean_true_node, boolean_type_node);
+ finish_expr_stmt (r);
}
}
}
@@ -4540,7 +4740,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tree p = build_class_member_access_expr (deref_fp, promise_m, NULL_TREE,
false, tf_warning_or_error);
- if (TYPE_NEEDS_CONSTRUCTING (promise_type))
+ tree promise_dtor = NULL_TREE;
+ if (type_build_ctor_call (promise_type))
{
/* Do a placement new constructor for the promise type (we never call
the new operator, just the constructor on the object in place in the
@@ -4566,7 +4767,17 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tf_warning_or_error);
r = coro_build_cvt_void_expr_stmt (r, fn_start);
- add_stmt (r);
+ finish_expr_stmt (r);
+
+ r = build_modify_expr (fn_start, coro_promise_live, boolean_type_node,
+ INIT_EXPR, fn_start, boolean_true_node,
+ boolean_type_node);
+ finish_expr_stmt (r);
+
+ promise_dtor
+ = build_special_member_call (p, complete_dtor_identifier,
+ NULL, promise_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
}
/* Set up a new bind context for the GRO. */
@@ -4598,6 +4809,8 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
tree gro = NULL_TREE;
tree gro_bind_vars = NULL_TREE;
+ /* Used for return objects in the RESULT slot. */
+ tree gro_ret_dtor = NULL_TREE;
tree gro_cleanup_stmt = NULL_TREE;
/* We have to sequence the call to get_return_object before initial
suspend. */
@@ -4609,7 +4822,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
The expression promise.get_return_object() is used to initialize the
glvalue result or... (see below)
Construct the return result directly. */
- if (TYPE_NEEDS_CONSTRUCTING (gro_type))
+ if (type_build_ctor_call (gro_type))
{
vec<tree, va_gc> *arg = make_tree_vector_single (get_ro);
r = build_special_member_call (DECL_RESULT (orig),
@@ -4621,6 +4834,16 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
else
r = build2_loc (fn_start, INIT_EXPR, gro_type,
DECL_RESULT (orig), get_ro);
+
+ if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (gro_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. */
+ gro_ret_dtor
+ = build_special_member_call (DECL_RESULT (orig),
+ complete_dtor_identifier, NULL,
+ gro_type, LOOKUP_NORMAL,
+ tf_warning_or_error);
}
else
{
@@ -4632,7 +4855,7 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
DECL_IGNORED_P (gro) = true;
add_decl_expr (gro);
gro_bind_vars = gro;
- if (TYPE_NEEDS_CONSTRUCTING (gro_type))
+ if (type_build_ctor_call (gro_type))
{
vec<tree, va_gc> *arg = make_tree_vector_single (get_ro);
r = build_special_member_call (gro, complete_ctor_identifier,
@@ -4645,19 +4868,28 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
/* The constructed object might require a cleanup. */
if (TYPE_HAS_NONTRIVIAL_DESTRUCTOR (gro_type))
{
- tree cleanup
+ gro_cleanup_stmt
= build_special_member_call (gro, complete_dtor_identifier,
NULL, gro_type, LOOKUP_NORMAL,
tf_warning_or_error);
gro_cleanup_stmt = build_stmt (input_location, CLEANUP_STMT, NULL,
- cleanup, gro);
+ gro_cleanup_stmt, gro);
}
}
finish_expr_stmt (r);
- if (gro_cleanup_stmt)
+ if (gro_cleanup_stmt && gro_cleanup_stmt != error_mark_node)
CLEANUP_BODY (gro_cleanup_stmt) = push_stmt_list ();
+ /* If we have a live g.r.o in the return slot, then signal this for exception
+ cleanup. */
+ if (gro_ret_dtor)
+ {
+ r = build_modify_expr (fn_start, coro_gro_live, boolean_type_node,
+ INIT_EXPR, fn_start, boolean_true_node,
+ boolean_type_node);
+ finish_expr_stmt (r);
+ }
/* Initialize the resume_idx_name to 0, meaning "not started". */
tree resume_idx_m
= lookup_member (coro_frame_type, resume_idx_name,
@@ -4736,6 +4968,73 @@ morph_fn_to_coro (tree orig, tree *resumer, tree *destroyer)
BIND_EXPR_VARS (gro_context_bind) = gro_bind_vars;
BIND_EXPR_BODY (gro_context_bind) = pop_stmt_list (gro_context_body);
TREE_SIDE_EFFECTS (gro_context_bind) = true;
+
+ if (flag_exceptions)
+ {
+ TRY_HANDLERS (ramp_cleanup) = push_stmt_list ();
+ 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.
+ When the return object is constructed from a separate g.r.o, this is
+ already handled by its regular cleanup. */
+ if (gro_ret_dtor && gro_ret_dtor != error_mark_node)
+ {
+ tree gro_d_if = begin_if_stmt ();
+ finish_if_stmt_cond (coro_gro_live, gro_d_if);
+ finish_expr_stmt (gro_ret_dtor);
+ finish_then_clause (gro_d_if);
+ tree gro_d_if_scope = IF_SCOPE (gro_d_if);
+ IF_SCOPE (gro_d_if) = NULL;
+ gro_d_if = do_poplevel (gro_d_if_scope);
+ add_stmt (gro_d_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);
+ tree promise_d_if_scope = IF_SCOPE (promise_d_if);
+ IF_SCOPE (promise_d_if) = NULL;
+ promise_d_if = do_poplevel (promise_d_if_scope);
+ add_stmt (promise_d_if);
+ }
+
+ /* Clean up any frame copies of parms with non-trivial dtors. */
+ if (DECL_ARGUMENTS (orig))
+ for (tree arg = DECL_ARGUMENTS (orig); arg != NULL;
+ arg = DECL_CHAIN (arg))
+ {
+ param_info *parm_i = param_uses->get (arg);
+ if (parm_i->trivial_dtor)
+ continue;
+ 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);
+ tree parm_d_if_scope = IF_SCOPE (dtor_if);
+ IF_SCOPE (dtor_if) = NULL;
+ dtor_if = do_poplevel (parm_d_if_scope);
+ add_stmt (dtor_if);
+ }
+ }
+
+ /* We always expect to delete the frame. */
+ tree del_coro_fr = coro_get_frame_dtor (coro_fp, orig, frame_size,
+ promise_type, fn_start);
+ finish_expr_stmt (del_coro_fr);
+ tree rethrow = build_throw (fn_start, NULL_TREE);
+ TREE_NO_WARNING (rethrow) = true;
+ finish_expr_stmt (rethrow);
+ finish_handler (handler);
+ TRY_HANDLERS (ramp_cleanup) = pop_stmt_list (TRY_HANDLERS (ramp_cleanup));
+ }
+
BIND_EXPR_BODY (ramp_bind) = pop_stmt_list (ramp_body);
TREE_SIDE_EFFECTS (ramp_bind) = true;
diff --git a/gcc/cp/cp-gimplify.c b/gcc/cp/cp-gimplify.c
index 1c5e15b..df89ff3 100644
--- a/gcc/cp/cp-gimplify.c
+++ b/gcc/cp/cp-gimplify.c
@@ -1381,12 +1381,12 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
normal functions. */
if (concept_check_p (stmt))
{
- *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+ *stmt_p = evaluate_concept_check (stmt);
* walk_subtrees = 0;
break;
}
- if (tree fndecl = cp_get_callee_fndecl (stmt))
+ if (tree fndecl = cp_get_callee_fndecl_nofold (stmt))
if (DECL_IMMEDIATE_FUNCTION_P (fndecl))
{
gcc_assert (source_location_current_p (fndecl));
@@ -1453,15 +1453,14 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void *data)
case REQUIRES_EXPR:
/* Emit the value of the requires-expression. */
- *stmt_p = constant_boolean_node (constraints_satisfied_p (stmt),
- boolean_type_node);
+ *stmt_p = evaluate_requires_expr (stmt);
*walk_subtrees = 0;
break;
case TEMPLATE_ID_EXPR:
gcc_assert (concept_check_p (stmt));
/* Emit the value of the concept check. */
- *stmt_p = evaluate_concept_check (stmt, tf_warning_or_error);
+ *stmt_p = evaluate_concept_check (stmt);
walk_subtrees = 0;
break;
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 4ed3936..81ff375 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -129,7 +129,6 @@ enum cp_tree_index
CPTI_VTBL_TYPE,
CPTI_VTBL_PTR_TYPE,
CPTI_GLOBAL,
- CPTI_GLOBAL_TYPE,
CPTI_ABORT_FNDECL,
CPTI_AGGR_TAG,
CPTI_CONV_OP_MARKER,
@@ -250,7 +249,6 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
#define std_node cp_global_trees[CPTI_STD]
#define abi_node cp_global_trees[CPTI_ABI]
#define global_namespace cp_global_trees[CPTI_GLOBAL]
-#define global_type_node cp_global_trees[CPTI_GLOBAL_TYPE]
#define const_type_info_type_node cp_global_trees[CPTI_CONST_TYPE_INFO_TYPE]
#define type_info_ptr_type cp_global_trees[CPTI_TYPE_INFO_PTR_TYPE]
#define conv_op_marker cp_global_trees[CPTI_CONV_OP_MARKER]
@@ -1149,24 +1147,16 @@ enum GTY(()) abstract_class_use {
/* Macros for access to language-specific slots in an identifier. */
-/* The IDENTIFIER_BINDING is the innermost cxx_binding for the
- identifier. Its PREVIOUS is the next outermost binding. Each
- VALUE field is a DECL for the associated declaration. Thus,
- name lookup consists simply of pulling off the node at the front
- of the list (modulo oddities for looking up the names of types,
- and such.) You can use SCOPE field to determine the scope
- that bound the name. */
+/* Identifiers map directly to block or class-scope bindings.
+ Namespace-scope bindings are held in hash tables on the respective
+ namespaces. The identifier bindings are the innermost active
+ binding, from whence you can get the decl and/or implicit-typedef
+ of an elaborated type. When not bound to a local entity the
+ values are NULL. */
#define IDENTIFIER_BINDING(NODE) \
(LANG_IDENTIFIER_CAST (NODE)->bindings)
-
-/* TREE_TYPE only indicates on local and class scope the current
- type. For namespace scope, the presence of a type in any namespace
- is indicated with global_type_node, and the real type behind must
- be found through lookup. */
-#define IDENTIFIER_TYPE_VALUE(NODE) identifier_type_value (NODE)
#define REAL_IDENTIFIER_TYPE_VALUE(NODE) TREE_TYPE (NODE)
#define SET_IDENTIFIER_TYPE_VALUE(NODE,TYPE) (TREE_TYPE (NODE) = (TYPE))
-#define IDENTIFIER_HAS_TYPE_VALUE(NODE) (IDENTIFIER_TYPE_VALUE (NODE) ? 1 : 0)
/* Kinds of identifiers. Values are carefully chosen. */
enum cp_identifier_kind {
@@ -1588,11 +1578,19 @@ check_constraint_info (tree t)
#define COMPOUND_REQ_NOEXCEPT_P(NODE) \
TREE_LANG_FLAG_0 (TREE_CHECK (NODE, COMPOUND_REQ))
-/* The constraints on an 'auto' placeholder type, used in an argument deduction
- constraint. */
-#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \
+/* A TREE_LIST whose TREE_VALUE is the constraints on the 'auto' placeholder
+ type NODE, used in an argument deduction constraint. The TREE_PURPOSE
+ holds the set of template parameters that were in-scope when this 'auto'
+ was formed. */
+#define PLACEHOLDER_TYPE_CONSTRAINTS_INFO(NODE) \
DECL_SIZE_UNIT (TYPE_NAME (NODE))
+/* The constraints on the 'auto' placeholder type NODE. */
+#define PLACEHOLDER_TYPE_CONSTRAINTS(NODE) \
+ (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (NODE) \
+ ? TREE_VALUE (PLACEHOLDER_TYPE_CONSTRAINTS_INFO (NODE)) \
+ : NULL_TREE)
+
/* True if NODE is a constraint. */
#define CONSTR_P(NODE) \
(TREE_CODE (NODE) == ATOMIC_CONSTR \
@@ -1680,21 +1678,10 @@ check_constraint_info (tree t)
#define DECL_MODULE_ENTITY_P(NODE) \
(DECL_LANG_SPECIFIC (DECL_MODULE_CHECK (NODE))->u.base.module_entity_p)
-/* True if there are unloaded specializations keyed to this template. */
-#define DECL_MODULE_PENDING_SPECIALIZATIONS_P(NODE) \
- (DECL_LANG_SPECIFIC (TEMPLATE_DECL_CHECK (NODE)) \
- ->u.base.module_pending_p)
-
-/* True if this class has unloaded members. These should be loaded
- before we do member lookups. */
-#define DECL_MODULE_PENDING_MEMBERS_P(NODE) \
- (DECL_LANG_SPECIFIC (TYPE_DECL_CHECK (NODE)) \
- ->u.base.module_pending_p)
-
/* DECL that has attached decls for ODR-relatedness. */
#define DECL_MODULE_ATTACHMENTS_P(NODE) \
(DECL_LANG_SPECIFIC (TREE_CHECK2(NODE,FUNCTION_DECL,VAR_DECL))\
- ->u.base.module_pending_p)
+ ->u.base.module_attached_p)
/* Whether this is an exported DECL. Held on any decl that can appear
at namespace scope (function, var, type, template, const or
@@ -2773,10 +2760,8 @@ struct GTY(()) lang_decl_base {
unsigned module_import_p : 1; /* from an import */
unsigned module_entity_p : 1; /* is in the entitity ary &
hash. */
- /* TEMPLATE_DECL has specializations or,
- TYPE_DECL has class members yet to load, or
- VAR_DECL or FUNCTION_DECL has attached decls. */
- unsigned module_pending_p : 1;
+ /* VAR_DECL or FUNCTION_DECL has attached decls. */
+ unsigned module_attached_p : 1;
/* 12 spare bits. */
};
@@ -6862,7 +6847,6 @@ extern void emit_mem_initializers (tree);
extern tree build_aggr_init (tree, tree, int,
tsubst_flags_t);
extern int is_class_type (tree, int);
-extern tree get_type_value (tree);
extern tree build_zero_init (tree, tree, bool);
extern tree build_value_init (tree, tsubst_flags_t);
extern tree build_value_init_noctor (tree, tsubst_flags_t);
@@ -7028,9 +7012,7 @@ extern void mangle_module (int m, bool include_partition);
extern void mangle_module_fini ();
extern void lazy_load_binding (unsigned mod, tree ns, tree id,
binding_slot *bslot);
-extern void lazy_load_specializations (tree tmpl);
-extern void lazy_load_members (tree decl);
-extern bool lazy_specializations_p (unsigned, bool, bool);
+extern void lazy_load_pendings (tree decl);
extern module_state *preprocess_module (module_state *, location_t,
bool in_purview,
bool is_import, bool export_p,
@@ -7257,11 +7239,10 @@ extern void walk_specializations (bool,
void (*)(bool, spec_entry *,
void *),
void *);
-extern tree match_mergeable_specialization (bool is_decl, spec_entry *,
- bool insert = true);
+extern tree match_mergeable_specialization (bool is_decl, spec_entry *);
extern unsigned get_mergeable_specialization_flags (tree tmpl, tree spec);
-extern void add_mergeable_specialization (tree tmpl, tree args,
- tree spec, unsigned);
+extern void add_mergeable_specialization (bool is_decl, spec_entry *,
+ tree outer, unsigned);
extern tree add_outermost_template_args (tree, tree);
extern tree add_extra_args (tree, tree);
extern tree build_extra_args (tree, tree, tsubst_flags_t);
@@ -7282,6 +7263,7 @@ extern void emit_support_tinfos (void);
extern bool emit_tinfo_decl (tree);
extern unsigned get_pseudo_tinfo_index (tree);
extern tree get_pseudo_tinfo_type (unsigned);
+extern tree build_if_nonnull (tree, tree, tsubst_flags_t);
/* in search.c */
extern tree get_parent_with_private_access (tree decl, tree binfo);
@@ -8112,6 +8094,7 @@ extern tree finish_compound_requirement (location_t, tree, tree, bool);
extern tree finish_nested_requirement (location_t, tree);
extern void check_constrained_friend (tree, tree);
extern tree tsubst_requires_expr (tree, tree, tsubst_flags_t, tree);
+extern tree evaluate_requires_expr (tree);
extern tree tsubst_constraint (tree, tree, tsubst_flags_t, tree);
extern tree tsubst_constraint_info (tree, tree, tsubst_flags_t, tree);
extern tree tsubst_parameter_mapping (tree, tree, tsubst_flags_t, tree);
@@ -8126,10 +8109,8 @@ struct processing_constraint_expression_sentinel
extern bool processing_constraint_expression_p ();
extern tree unpack_concept_check (tree);
-extern tree evaluate_concept_check (tree, tsubst_flags_t);
-extern tree satisfy_constraint_expression (tree);
-extern bool constraints_satisfied_p (tree);
-extern bool constraints_satisfied_p (tree, tree);
+extern tree evaluate_concept_check (tree);
+extern bool constraints_satisfied_p (tree, tree = NULL_TREE);
extern bool* lookup_subsumption_result (tree, tree);
extern bool save_subsumption_result (tree, tree, bool);
extern tree find_template_parameters (tree, tree);
@@ -8431,7 +8412,7 @@ set_implicit_rvalue_p (tree ot)
inline bool
is_constrained_auto (const_tree t)
{
- return is_auto (t) && PLACEHOLDER_TYPE_CONSTRAINTS (t);
+ return is_auto (t) && PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t);
}
/* RAII class to push/pop class scope T; if T is not a class, do nothing. */
diff --git a/gcc/cp/cvt.c b/gcc/cp/cvt.c
index e809f0e..2ea3210 100644
--- a/gcc/cp/cvt.c
+++ b/gcc/cp/cvt.c
@@ -1170,7 +1170,7 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
/* Explicitly evaluate void-converted concept checks since their
satisfaction may produce ill-formed programs. */
if (concept_check_p (expr))
- expr = evaluate_concept_check (expr, tf_warning_or_error);
+ expr = evaluate_concept_check (expr);
if (VOID_TYPE_P (TREE_TYPE (expr)))
return expr;
@@ -1231,12 +1231,14 @@ convert_to_void (tree expr, impl_conv_void implicit, tsubst_flags_t complain)
case CALL_EXPR: /* We have a special meaning for volatile void fn(). */
/* cdtors may return this or void, depending on
targetm.cxx.cdtor_returns_this, but this shouldn't affect our
- decisions here: neither nodiscard warnings (nodiscard cdtors
- are nonsensical), nor should any constexpr or template
- instantiations be affected by an ABI property that is, or at
- least ought to be transparent to the language. */
+ decisions here: neither nodiscard warnings (nodiscard dtors
+ are nonsensical and ctors have a different behavior with that
+ attribute that is handled in the TARGET_EXPR case), nor should
+ any constexpr or template instantiations be affected by an ABI
+ property that is, or at least ought to be transparent to the
+ language. */
if (tree fn = cp_get_callee_fndecl_nofold (expr))
- if (DECL_DESTRUCTOR_P (fn))
+ if (DECL_CONSTRUCTOR_P (fn) || DECL_DESTRUCTOR_P (fn))
return expr;
if (complain & tf_warning)
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 0aa49f9..9c7f6e5 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -2879,19 +2879,6 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
(char *) newdecl + sizeof (struct tree_common),
sizeof (struct tree_decl_common) - sizeof (struct tree_common));
- if (DECL_LANG_SPECIFIC (olddecl) && DECL_TEMPLATE_INFO (olddecl))
- {
- /* Repropagate the module information to the template. */
- tree tmpl = DECL_TI_TEMPLATE (olddecl);
-
- if (DECL_TEMPLATE_RESULT (tmpl) == olddecl)
- {
- DECL_MODULE_PURVIEW_P (tmpl) = DECL_MODULE_PURVIEW_P (olddecl);
- gcc_checking_assert (!DECL_MODULE_IMPORT_P (olddecl));
- DECL_MODULE_IMPORT_P (tmpl) = false;
- }
- }
-
switch (TREE_CODE (newdecl))
{
case LABEL_DECL:
@@ -2925,6 +2912,19 @@ duplicate_decls (tree newdecl, tree olddecl, bool hiding, bool was_hidden)
}
}
+ if (DECL_LANG_SPECIFIC (olddecl) && DECL_TEMPLATE_INFO (olddecl))
+ {
+ /* Repropagate the module information to the template. */
+ tree tmpl = DECL_TI_TEMPLATE (olddecl);
+
+ if (DECL_TEMPLATE_RESULT (tmpl) == olddecl)
+ {
+ DECL_MODULE_PURVIEW_P (tmpl) = DECL_MODULE_PURVIEW_P (olddecl);
+ gcc_checking_assert (!DECL_MODULE_IMPORT_P (olddecl));
+ DECL_MODULE_IMPORT_P (tmpl) = false;
+ }
+ }
+
if (VAR_OR_FUNCTION_DECL_P (newdecl))
{
if (DECL_EXTERNAL (olddecl)
@@ -4510,9 +4510,6 @@ cxx_init_decl_processing (void)
abi_node = current_namespace;
pop_namespace ();
- global_type_node = make_node (LANG_TYPE);
- record_unknown_type (global_type_node, "global type");
-
any_targ_node = make_node (LANG_TYPE);
record_unknown_type (any_targ_node, "any type");
@@ -7160,6 +7157,13 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec)
&& fndecl_built_in_p (decl, BUILT_IN_NORMAL))
set_builtin_user_assembler_name (decl, asmspec);
set_user_assembler_name (decl, asmspec);
+ if (DECL_LOCAL_DECL_P (decl))
+ if (auto ns_decl = DECL_LOCAL_DECL_ALIAS (decl))
+ /* We have to propagate the name to the ns-alias.
+ This is horrible, as we're affecting a
+ possibly-shared decl. Again, a one-true-decl
+ model breaks down. */
+ set_user_assembler_name (ns_decl, asmspec);
}
}
@@ -11084,21 +11088,18 @@ name_unnamed_type (tree type, tree decl)
{
gcc_assert (TYPE_UNNAMED_P (type));
- /* Replace the anonymous name with the real name everywhere. */
+ /* Replace the anonymous decl with the real decl. Be careful not to
+ rename other typedefs (such as the self-reference) of type. */
+ tree orig = TYPE_NAME (type);
for (tree t = TYPE_MAIN_VARIANT (type); t; t = TYPE_NEXT_VARIANT (t))
- if (IDENTIFIER_ANON_P (TYPE_IDENTIFIER (t)))
- /* We do not rename the debug info representing the unnamed
- tagged type because the standard says in [dcl.typedef] that
- the naming applies only for linkage purposes. */
- /*debug_hooks->set_name (t, decl);*/
+ if (TYPE_NAME (t) == orig)
TYPE_NAME (t) = decl;
/* If this is a typedef within a template class, the nested
type is a (non-primary) template. The name for the
template needs updating as well. */
if (TYPE_LANG_SPECIFIC (type) && CLASSTYPE_TEMPLATE_INFO (type))
- DECL_NAME (CLASSTYPE_TI_TEMPLATE (type))
- = TYPE_IDENTIFIER (type);
+ DECL_NAME (CLASSTYPE_TI_TEMPLATE (type)) = DECL_NAME (decl);
/* Adjust linkage now that we aren't unnamed anymore. */
reset_type_linkage (type);
diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index b087753..c46100d 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -1596,9 +1596,6 @@ cplus_decl_attributes (tree *decl, tree attributes, int flags)
decl_attributes (decl, attributes, flags, last_decl);
}
- if (TREE_CODE (*decl) == TYPE_DECL)
- SET_IDENTIFIER_TYPE_VALUE (DECL_NAME (*decl), TREE_TYPE (*decl));
-
/* Propagate deprecation out to the template. */
if (TREE_DEPRECATED (*decl))
if (tree ti = get_template_info (*decl))
diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index 5213a80..ff4ae6f 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -243,9 +243,7 @@ dump_scope (cxx_pretty_printer *pp, tree scope, int flags)
}
else if ((flags & TFF_SCOPE) && TREE_CODE (scope) == FUNCTION_DECL)
{
- if (DECL_USE_TEMPLATE (scope))
- f |= TFF_NO_FUNCTION_ARGUMENTS;
- dump_function_decl (pp, scope, f);
+ dump_function_decl (pp, scope, f | TFF_NO_TEMPLATE_BINDINGS);
pp_cxx_colon_colon (pp);
}
}
diff --git a/gcc/cp/init.c b/gcc/cp/init.c
index 1381a23..7d598f6 100644
--- a/gcc/cp/init.c
+++ b/gcc/cp/init.c
@@ -252,7 +252,7 @@ build_zero_init_1 (tree type, tree nelts, bool static_storage_p,
build_one_cst (TREE_TYPE (nelts)));
/* Treat flexible array members like [0] arrays. */
else if (TYPE_DOMAIN (type) == NULL_TREE)
- max_index = build_minus_one_cst (sizetype);
+ return NULL_TREE;
else
max_index = array_type_nelts (type);
@@ -2118,18 +2118,6 @@ is_class_type (tree type, int or_else)
return 1;
}
-tree
-get_type_value (tree name)
-{
- if (name == error_mark_node)
- return NULL_TREE;
-
- if (IDENTIFIER_HAS_TYPE_VALUE (name))
- return IDENTIFIER_TYPE_VALUE (name);
- else
- return NULL_TREE;
-}
-
/* Build a reference to a member of an aggregate. This is not a C++
`&', but really something which can have its address taken, and
then act as a pointer to member, for example TYPE :: FIELD can have
diff --git a/gcc/cp/lambda.c b/gcc/cp/lambda.c
index 586b7ff..421685c 100644
--- a/gcc/cp/lambda.c
+++ b/gcc/cp/lambda.c
@@ -1352,7 +1352,8 @@ is_lambda_ignored_entity (tree val)
/* None of the lookups that use qualify_lookup want the op() from the
lambda; they want the one from the enclosing class. */
- if (TREE_CODE (val) == FUNCTION_DECL && LAMBDA_FUNCTION_P (val))
+ val = OVL_FIRST (val);
+ if (LAMBDA_FUNCTION_P (val))
return true;
return false;
diff --git a/gcc/cp/lex.c b/gcc/cp/lex.c
index 848f3d9..73e14b8 100644
--- a/gcc/cp/lex.c
+++ b/gcc/cp/lex.c
@@ -515,7 +515,7 @@ struct module_token_filter
{
module_end:;
/* End of the directive, handle the name. */
- if (import)
+ if (import && (is_import || !flag_header_unit))
if (module_state *m
= preprocess_module (import, token_loc, module != NULL,
is_import, got_export, reader))
@@ -1010,7 +1010,7 @@ cxx_dup_lang_specific_decl (tree node)
(module_purview_p still does). */
ld->u.base.module_entity_p = false;
ld->u.base.module_import_p = false;
- ld->u.base.module_pending_p = false;
+ ld->u.base.module_attached_p = false;
if (GATHER_STATISTICS)
{
diff --git a/gcc/cp/mapper-client.cc b/gcc/cp/mapper-client.cc
index a72b4b7..774e2b2 100644
--- a/gcc/cp/mapper-client.cc
+++ b/gcc/cp/mapper-client.cc
@@ -249,7 +249,7 @@ module_client::open_module_client (location_t loc, const char *o,
if (port && endp != cptr + 1 && !*endp)
{
name[colon] = 0;
- int fd = 01;
+ int fd = -1;
#if CODY_NETWORKING
fd = Cody::OpenInet6 (&errmsg, name.c_str (), port);
#endif
diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc
index 0749db8..03359db 100644
--- a/gcc/cp/module.cc
+++ b/gcc/cp/module.cc
@@ -53,16 +53,13 @@ along with GCC; see the file COPYING3. If not see
the third indicates whether it was an import into this TU or not.
The more detailed flags are DECL_MODULE_PARTITION_P,
- DECL_MODULE_ENTITY_P & DECL_MODULE_PENDING_SPECIALIZATIONS_P. The
- first is set in a primary interface unit on decls that were read
- from module partitions (these will have DECL_MODULE_IMPORT_P set
- too). Such decls will be streamed out to the primary's CMI.
- DECL_MODULE_ENTITY_P is set when an entity is imported, even if it
- matched a non-imported entity. Such a decl will not have
- DECL_MODULE_IMPORT_P set, even though it has an entry in the entity
- map and array. DECL_MODULE_PENDING_SPECIALIZATIONS_P is set on a
- primary template, and indicates there are specializations that
- should be streamed in before trying to specialize this template.
+ DECL_MODULE_ENTITY_P. The first is set in a primary interface unit
+ on decls that were read from module partitions (these will have
+ DECL_MODULE_IMPORT_P set too). Such decls will be streamed out to
+ the primary's CMI. DECL_MODULE_ENTITY_P is set when an entity is
+ imported, even if it matched a non-imported entity. Such a decl
+ will not have DECL_MODULE_IMPORT_P set, even though it has an entry
+ in the entity map and array.
Header units are module-like.
@@ -317,6 +314,9 @@ version2string (unsigned version, verstr_t &out)
/* Include files to note translation for. */
static vec<const char *, va_heap, vl_embed> *note_includes;
+/* Modules to note CMI pathames. */
+static vec<const char *, va_heap, vl_embed> *note_cmis;
+
/* Traits to hash an arbitrary pointer. Entries are not deletable,
and removal is a noop (removal needed upon destruction). */
template <typename T>
@@ -2374,8 +2374,10 @@ public:
}
public:
+ /* This class-member is defined here, but the class was imported. */
bool is_member () const
{
+ gcc_checking_assert (get_entity_kind () == EK_DECL);
return get_flag_bit<DB_IS_MEMBER_BIT> ();
}
public:
@@ -2639,181 +2641,67 @@ depset *depset::make_entity (tree entity, entity_kind ek, bool is_defn)
return r;
}
-/* Values keyed to some unsigned integer. This is not GTY'd, so if
- T is tree they must be reachable via some other path. */
-
-template<typename T>
-class uintset {
+class pending_key
+{
public:
- unsigned key; /* Entity index of the other entity. */
+ tree ns;
+ tree id;
+};
- /* Payload. */
- unsigned allocp2 : 5; /* log(2) allocated pending */
- unsigned num : 27; /* Number of pending. */
+template<>
+struct default_hash_traits<pending_key>
+{
+ using value_type = pending_key;
- /* Trailing array of values. */
- T values[1];
+ static const bool empty_zero_p = false;
+ static hashval_t hash (const value_type &k)
+ {
+ hashval_t h = IDENTIFIER_HASH_VALUE (k.id);
+ h = iterative_hash_hashval_t (DECL_UID (k.ns), h);
-public:
- /* Even with ctors, we're very pod-like. */
- uintset (unsigned uid)
- : key (uid), allocp2 (0), num (0)
+ return h;
+ }
+ static bool equal (const value_type &k, const value_type &l)
{
+ return k.ns == l.ns && k.id == l.id;
}
- /* Copy constructor, which is exciting because of the trailing
- array. */
- uintset (const uintset *from)
+ static void mark_empty (value_type &k)
{
- size_t size = (offsetof (uintset, values)
- + sizeof (uintset::values) * from->num);
- memmove (this, from, size);
- if (from->num)
- allocp2++;
+ k.ns = k.id = NULL_TREE;
}
-
-public:
- struct traits : delete_ptr_hash<uintset> {
- typedef unsigned compare_type;
- typedef typename delete_ptr_hash<uintset>::value_type value_type;
-
- /* Hash and equality for compare_type. */
- inline static hashval_t hash (const compare_type k)
- {
- return hashval_t (k);
- }
- inline static hashval_t hash (const value_type v)
- {
- return hash (v->key);
- }
-
- inline static bool equal (const value_type v, const compare_type k)
- {
- return v->key == k;
- }
- };
-
-public:
- class hash : public hash_table<traits>
+ static void mark_deleted (value_type &k)
{
- typedef typename traits::compare_type key_t;
- typedef hash_table<traits> parent;
-
- public:
- hash (size_t size)
- : parent (size)
- {
- }
- ~hash ()
- {
- }
-
- private:
- uintset **find_slot (key_t key, insert_option insert)
- {
- return this->find_slot_with_hash (key, traits::hash (key), insert);
- }
-
- public:
- uintset *get (key_t key, bool extract = false);
- bool add (key_t key, T value);
- uintset *create (key_t key, unsigned num, T init = 0);
- };
+ k.ns = NULL_TREE;
+ gcc_checking_assert (k.id);
+ }
+ static bool is_empty (const value_type &k)
+ {
+ return k.ns == NULL_TREE && k.id == NULL_TREE;
+ }
+ static bool is_deleted (const value_type &k)
+ {
+ return k.ns == NULL_TREE && k.id != NULL_TREE;
+ }
+ static void remove (value_type &)
+ {
+ }
};
-/* Add VALUE to KEY's uintset, creating it if necessary. Returns true
- if we created the uintset. */
+typedef hash_map<pending_key, auto_vec<unsigned>> pending_map_t;
-template<typename T>
-bool
-uintset<T>::hash::add (typename uintset<T>::hash::key_t key, T value)
-{
- uintset **slot = this->find_slot (key, INSERT);
- uintset *set = *slot;
- bool is_new = !set;
+/* Not-loaded entities that are keyed to a namespace-scope
+ identifier. See module_state::write_pendings for details. */
+pending_map_t *pending_table;
- if (is_new || set->num == (1u << set->allocp2))
- {
- if (set)
- {
- unsigned n = set->num * 2;
- size_t new_size = (offsetof (uintset, values)
- + sizeof (uintset (0u).values) * n);
- uintset *new_set = new (::operator new (new_size)) uintset (set);
- delete set;
- set = new_set;
- }
- else
- set = new (::operator new (sizeof (*set))) uintset (key);
- *slot = set;
- }
-
- set->values[set->num++] = value;
-
- return is_new;
-}
-
-template<typename T>
-uintset<T> *
-uintset<T>::hash::create (typename uintset<T>::hash::key_t key, unsigned num,
- T init)
-{
- unsigned p2alloc = 0;
- for (unsigned v = num; v != 1; v = (v >> 1) | (v & 1))
- p2alloc++;
-
- size_t new_size = (offsetof (uintset, values)
- + (sizeof (uintset (0u).values) << p2alloc));
- uintset *set = new (::operator new (new_size)) uintset (key);
- set->allocp2 = p2alloc;
- set->num = num;
- while (num--)
- set->values[num] = init;
-
- uintset **slot = this->find_slot (key, INSERT);
- gcc_checking_assert (!*slot);
- *slot = set;
-
- return set;
-}
-
-/* Locate KEY's uintset, potentially removing it from the hash table */
-
-template<typename T>
-uintset<T> *
-uintset<T>::hash::get (typename uintset<T>::hash::key_t key, bool extract)
-{
- uintset *res = NULL;
-
- if (uintset **slot = this->find_slot (key, NO_INSERT))
- {
- res = *slot;
- if (extract)
- /* We need to remove the pendset without deleting it. */
- traits::mark_deleted (*slot);
- }
-
- return res;
-}
-
-/* Entities keyed to some other entity. When we load the other
- entity, we mark it in some way to indicate there are further
- entities to load when you start looking inside it. For instance
- template specializations are keyed to their most general template.
- When we instantiate that, we need to know all the partial
- specializations (to pick the right template), and all the known
- specializations (to avoid reinstantiating it, and/or whether it's
- extern). The values split into two ranges. If !MSB set, indices
- into the entity array. If MSB set, an indirection to another
- pendset. */
-
-typedef uintset<unsigned> pendset;
-static pendset::hash *pending_table;
+/* Decls that need some post processing once a batch of lazy loads has
+ completed. */
+vec<tree, va_heap, vl_embed> *post_load_decls;
/* Some entities are attached to another entitity for ODR purposes.
For example, at namespace scope, 'inline auto var = []{};', that
lambda is attached to 'var', and follows its ODRness. */
-typedef uintset<tree> attachset;
-static attachset::hash *attached_table;
+typedef hash_map<tree, auto_vec<tree>> attached_map_t;
+static attached_map_t *attached_table;
/********************************************************************/
/* Tree streaming. The tree streaming is very specific to the tree
@@ -2895,11 +2783,7 @@ enum merge_kind
MK_tmpl_tmpl_mask = 0x1, /* We want TEMPLATE_DECL. */
MK_type_spec = MK_template_mask,
- MK_type_tmpl_spec = MK_type_spec | MK_tmpl_tmpl_mask,
-
MK_decl_spec = MK_template_mask | MK_tmpl_decl_mask,
- MK_decl_tmpl_spec = MK_decl_spec | MK_tmpl_tmpl_mask,
-
MK_alias_spec = MK_decl_spec | MK_tmpl_alias_mask,
MK_hwm = 0x20
@@ -3108,7 +2992,8 @@ private:
unsigned section;
#if CHECKING_P
int importedness; /* Checker that imports not occurring
- inappropriately. */
+ inappropriately. +ve imports ok,
+ -ve imports not ok. */
#endif
public:
@@ -3359,6 +3244,8 @@ public:
};
static loc_spans spans;
+/* Indirection to allow bsearching imports by ordinary location. */
+static vec<module_state *> *ool;
/********************************************************************/
/* Data needed by a module during the process of loading. */
@@ -3546,9 +3433,11 @@ class GTY((chain_next ("%h.parent"), for_user)) module_state {
do it again */
bool call_init_p : 1; /* This module's global initializer needs
calling. */
+ bool inform_cmi_p : 1; /* Inform of a read/write. */
+ bool visited_p : 1; /* A walk-once flag. */
/* Record extensions emitted or permitted. */
unsigned extensions : SE_BITS;
- /* 12 bits used, 4 bits remain */
+ /* 14 bits used, 2 bits remain */
public:
module_state (tree name, module_state *, bool);
@@ -3695,8 +3584,8 @@ class GTY((chain_next ("%h.parent"), for_user)) module_state {
bool read_inits (unsigned count);
private:
- void write_pendings (elf_out *to, vec<depset *> depsets,
- depset::hash &, unsigned count, unsigned *crc_ptr);
+ unsigned write_pendings (elf_out *to, vec<depset *> depsets,
+ depset::hash &, unsigned *crc_ptr);
bool read_pendings (unsigned count);
private:
@@ -3781,6 +3670,9 @@ module_state::module_state (tree name, module_state *parent, bool partition)
partition_p = partition;
+ inform_cmi_p = false;
+ visited_p = false;
+
extensions = 0;
if (name && TREE_CODE (name) == STRING_CST)
{
@@ -3866,6 +3758,9 @@ module_state_hash::equal (const value_type existing,
/* Mapper name. */
static const char *module_mapper_name;
+/* Deferred import queue (FIFO). */
+static vec<module_state *, va_heap, vl_embed> *pending_imports;
+
/* CMI repository path and workspace. */
static char *cmi_repo;
static size_t cmi_repo_length;
@@ -5624,7 +5519,7 @@ trees_out::lang_decl_bools (tree t)
WB (lang->u.base.dependent_init_p);
WB (lang->u.base.module_purview_p);
if (VAR_OR_FUNCTION_DECL_P (t))
- WB (lang->u.base.module_pending_p);
+ WB (lang->u.base.module_attached_p);
switch (lang->u.base.selector)
{
default:
@@ -5694,7 +5589,7 @@ trees_in::lang_decl_bools (tree t)
RB (lang->u.base.dependent_init_p);
RB (lang->u.base.module_purview_p);
if (VAR_OR_FUNCTION_DECL_P (t))
- RB (lang->u.base.module_pending_p);
+ RB (lang->u.base.module_attached_p);
switch (lang->u.base.selector)
{
default:
@@ -7552,12 +7447,7 @@ trees_in::install_entity (tree decl)
/* Insert the real decl into the entity ary. */
unsigned ident = state->entity_lwm + entity_index - 1;
- binding_slot &elt = (*entity_ary)[ident];
-
- /* See module_state::read_pendings for how this got set. */
- int pending = elt.get_lazy () & 3;
-
- elt = decl;
+ (*entity_ary)[ident] = decl;
/* And into the entity map, if it's not already there. */
if (!DECL_LANG_SPECIFIC (decl)
@@ -7572,26 +7462,6 @@ trees_in::install_entity (tree decl)
gcc_checking_assert (!existed);
slot = ident;
}
- else if (pending != 0)
- {
- unsigned key_ident = import_entity_index (decl);
- if (pending & 1)
- if (!pending_table->add (key_ident, ~ident))
- pending &= ~1;
-
- if (pending & 2)
- if (!pending_table->add (~key_ident, ~ident))
- pending &= ~2;
- }
-
- if (pending & 1)
- DECL_MODULE_PENDING_SPECIALIZATIONS_P (decl) = true;
-
- if (pending & 2)
- {
- DECL_MODULE_PENDING_MEMBERS_P (decl) = true;
- gcc_checking_assert (TREE_CODE (decl) != TEMPLATE_DECL);
- }
return true;
}
@@ -7792,7 +7662,31 @@ trees_out::decl_value (tree decl, depset *dep)
}
if (!is_key_order ())
- tree_node (get_constraints (decl));
+ {
+ if (mk & MK_template_mask
+ || mk == MK_partial
+ || mk == MK_friend_spec)
+ {
+ if (mk != MK_partial)
+ {
+ // FIXME: We should make use of the merge-key by
+ // exposing it outside of key_mergeable. But this gets
+ // the job done.
+ auto *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
+
+ if (streaming_p ())
+ u (get_mergeable_specialization_flags (entry->tmpl, decl));
+ tree_node (entry->tmpl);
+ tree_node (entry->args);
+ }
+ else
+ {
+ tree_node (CLASSTYPE_TI_TEMPLATE (TREE_TYPE (inner)));
+ tree_node (CLASSTYPE_TI_ARGS (TREE_TYPE (inner)));
+ }
+ }
+ tree_node (get_constraints (decl));
+ }
if (streaming_p ())
{
@@ -7811,13 +7705,13 @@ trees_out::decl_value (tree decl, depset *dep)
&& !is_key_order ())
{
/* Stream the attached entities. */
- attachset *set = attached_table->get (DECL_UID (inner));
- unsigned num = set->num;
+ auto *attach_vec = attached_table->get (inner);
+ unsigned num = attach_vec->length ();
if (streaming_p ())
u (num);
for (unsigned ix = 0; ix != num; ix++)
{
- tree attached = set->values[ix];
+ tree attached = (*attach_vec)[ix];
tree_node (attached);
if (streaming_p ())
dump (dumper::MERGE)
@@ -8080,7 +7974,22 @@ trees_in::decl_value ()
|| (stub_decl && !tree_node_vals (stub_decl))))
goto bail;
- tree constraints = tree_node ();
+ spec_entry spec;
+ unsigned spec_flags = 0;
+ if (mk & MK_template_mask
+ || mk == MK_partial
+ || mk == MK_friend_spec)
+ {
+ if (mk == MK_partial)
+ spec_flags = 2;
+ else
+ spec_flags = u ();
+
+ spec.tmpl = tree_node ();
+ spec.args = tree_node ();
+ }
+ /* Hold constraints on the spec field, for a short while. */
+ spec.spec = tree_node ();
dump (dumper::TREE) && dump ("Read:%d %C:%N", tag, TREE_CODE (decl), decl);
@@ -8100,13 +8009,14 @@ trees_in::decl_value ()
&& DECL_MODULE_ATTACHMENTS_P (inner))
{
/* Read and maybe install the attached entities. */
- attachset *set
- = attached_table->get (DECL_UID (STRIP_TEMPLATE (existing)));
+ bool existed;
+ auto &set = attached_table->get_or_insert (STRIP_TEMPLATE (existing),
+ &existed);
unsigned num = u ();
- if (!is_new == !set)
+ if (is_new == existed)
set_overrun ();
if (is_new)
- set = attached_table->create (DECL_UID (inner), num, NULL_TREE);
+ set.reserve (num);
for (unsigned ix = 0; !get_overrun () && ix != num; ix++)
{
tree attached = tree_node ();
@@ -8114,8 +8024,8 @@ trees_in::decl_value ()
&& dump ("Read %d[%u] %s attached decl %N", tag, ix,
is_new ? "new" : "matched", attached);
if (is_new)
- set->values[ix] = attached;
- else if (set->values[ix] != attached)
+ set.quick_push (attached);
+ else if (set[ix] != attached)
set_overrun ();
}
}
@@ -8141,8 +8051,18 @@ trees_in::decl_value ()
}
}
- if (constraints)
- set_constraints (decl, constraints);
+ if (spec.spec)
+ set_constraints (decl, spec.spec);
+ if (mk & MK_template_mask
+ || mk == MK_partial)
+ {
+ /* Add to specialization tables now that constraints etc are
+ added. */
+ bool is_type = mk == MK_partial || !(mk & MK_tmpl_decl_mask);
+
+ spec.spec = is_type ? type : mk & MK_tmpl_tmpl_mask ? inner : decl;
+ add_mergeable_specialization (!is_type, &spec, decl, spec_flags);
+ }
if (TREE_CODE (decl) == INTEGER_CST && !TREE_OVERFLOW (decl))
{
@@ -8162,6 +8082,12 @@ trees_in::decl_value ()
/* Set the TEMPLATE_DECL's type. */
TREE_TYPE (decl) = TREE_TYPE (inner);
+ if (NAMESPACE_SCOPE_P (decl)
+ && (mk == MK_named || mk == MK_unique
+ || mk == MK_enum || mk == MK_friend_spec)
+ && !(VAR_OR_FUNCTION_DECL_P (decl) && DECL_LOCAL_DECL_P (decl)))
+ add_module_namespace_decl (CP_DECL_CONTEXT (decl), decl);
+
/* The late insertion of an alias here or an implicit member
(next block), is ok, because we ensured that all imports were
loaded up before we started this cluster. Thus an insertion
@@ -8225,6 +8151,18 @@ trees_in::decl_value ()
decl = existing;
}
+ if (mk == MK_friend_spec)
+ {
+ tree e = match_mergeable_specialization (true, &spec);
+ if (!e)
+ {
+ spec.spec = inner;
+ add_mergeable_specialization (true, &spec, decl, spec_flags);
+ }
+ else if (e != existing)
+ set_overrun ();
+ }
+
if (is_typedef)
{
/* Insert the type into the array now. */
@@ -8593,12 +8531,13 @@ trees_out::decl_node (tree decl, walk_kind ref)
else if (TREE_CODE (ctx) != FUNCTION_DECL
|| TREE_CODE (decl) == TEMPLATE_DECL
|| (dep_hash->sneakoscope && DECL_IMPLICIT_TYPEDEF_P (decl))
- || (DECL_LANG_SPECIFIC (decl)
- && DECL_MODULE_IMPORT_P (decl)))
- dep = dep_hash->add_dependency (decl,
- TREE_CODE (decl) == NAMESPACE_DECL
- && !DECL_NAMESPACE_ALIAS (decl)
- ? depset::EK_NAMESPACE : depset::EK_DECL);
+ || (DECL_LANG_SPECIFIC (decl) && DECL_MODULE_IMPORT_P (decl)))
+ {
+ auto kind = (TREE_CODE (decl) == NAMESPACE_DECL
+ && !DECL_NAMESPACE_ALIAS (decl)
+ ? depset::EK_NAMESPACE : depset::EK_DECL);
+ dep = dep_hash->add_dependency (decl, kind);
+ }
if (!dep)
{
@@ -8694,7 +8633,7 @@ trees_out::type_node (tree type)
else
{
if (TYPE_USER_ALIGN (type))
- flags = exact_log2 (TYPE_ALIGN (type));
+ flags = TYPE_ALIGN_RAW (type);
}
if (streaming_p ())
@@ -9490,7 +9429,7 @@ trees_in::tree_node (bool is_use)
}
else
{
- res = build_aligned_type (res, 1u << flags);
+ res = build_aligned_type (res, (1u << flags) >> 1);
TYPE_USER_ALIGN (res) = true;
}
@@ -10286,7 +10225,6 @@ trees_out::get_merge_kind (tree decl, depset *dep)
case depset::EK_SPECIALIZATION:
{
gcc_checking_assert (dep->is_special ());
- spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
if (TREE_CODE (DECL_CONTEXT (decl)) == FUNCTION_DECL)
/* An block-scope classes of templates are themselves
@@ -10304,13 +10242,8 @@ trees_out::get_merge_kind (tree decl, depset *dep)
if (TREE_CODE (decl) == TEMPLATE_DECL)
{
- tree res = DECL_TEMPLATE_RESULT (decl);
- if (!(mk & MK_tmpl_decl_mask))
- res = TREE_TYPE (res);
-
- if (res == entry->spec)
- /* We check we can get back to the template during
- streaming. */
+ spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
+ if (TREE_CODE (entry->spec) != TEMPLATE_DECL)
mk = merge_kind (mk | MK_tmpl_tmpl_mask);
}
}
@@ -10392,8 +10325,6 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
tree_node (entry->tmpl);
tree_node (entry->args);
- if (streaming_p ())
- u (get_mergeable_specialization_flags (entry->tmpl, decl));
if (mk & MK_tmpl_decl_mask)
if (flag_concepts && TREE_CODE (inner) == VAR_DECL)
{
@@ -10411,25 +10342,24 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
{
/* Make sure we can locate the decl. */
tree existing = match_mergeable_specialization
- (bool (mk & MK_tmpl_decl_mask), entry, false);
+ (bool (mk & MK_tmpl_decl_mask), entry);
gcc_assert (existing);
if (mk & MK_tmpl_decl_mask)
{
if (mk & MK_tmpl_alias_mask)
/* It should be in both tables. */
- gcc_assert (match_mergeable_specialization (false, entry, false)
+ gcc_assert (match_mergeable_specialization (false, entry)
== TREE_TYPE (existing));
else if (mk & MK_tmpl_tmpl_mask)
- if (tree ti = DECL_TEMPLATE_INFO (existing))
- existing = TI_TEMPLATE (ti);
+ existing = DECL_TI_TEMPLATE (existing);
}
else
{
- if (!(mk & MK_tmpl_tmpl_mask))
+ if (mk & MK_tmpl_tmpl_mask)
+ existing = CLASSTYPE_TI_TEMPLATE (existing);
+ else
existing = TYPE_NAME (existing);
- else if (tree ti = CLASSTYPE_TEMPLATE_INFO (existing))
- existing = TI_TEMPLATE (ti);
}
/* The walkabout should have found ourselves. */
@@ -10480,17 +10410,6 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
// FIXME: What if the return type is a voldemort?
key.ret = fndecl_declared_return_type (inner);
}
-
- if (mk == MK_friend_spec)
- {
- gcc_checking_assert (dep && dep->is_special ());
- spec_entry *entry = reinterpret_cast <spec_entry *> (dep->deps[0]);
-
- tree_node (entry->tmpl);
- tree_node (entry->args);
- if (streaming_p ())
- u (get_mergeable_specialization_flags (entry->tmpl, decl));
- }
break;
case MK_field:
@@ -10573,12 +10492,12 @@ trees_out::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
tree scope = LAMBDA_EXPR_EXTRA_SCOPE (CLASSTYPE_LAMBDA_EXPR
(TREE_TYPE (inner)));
gcc_checking_assert (TREE_CODE (scope) == VAR_DECL);
- attachset *root = attached_table->get (DECL_UID (scope));
- unsigned ix = root->num;
+ auto *root = attached_table->get (scope);
+ unsigned ix = root->length ();
/* If we don't find it, we'll write a really big number
that the reader will ignore. */
while (ix--)
- if (root->values[ix] == inner)
+ if ((*root)[ix] == inner)
break;
/* Use the attached-to decl as the 'name'. */
@@ -10737,23 +10656,16 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
if (mk & MK_template_mask)
{
+ // FIXME: We could stream the specialization hash?
spec_entry spec;
spec.tmpl = tree_node ();
spec.args = tree_node ();
- unsigned flags = u ();
DECL_NAME (decl) = DECL_NAME (spec.tmpl);
DECL_CONTEXT (decl) = DECL_CONTEXT (spec.tmpl);
DECL_NAME (inner) = DECL_NAME (decl);
DECL_CONTEXT (inner) = DECL_CONTEXT (decl);
- spec.spec = decl;
- if (mk & MK_tmpl_tmpl_mask)
- {
- if (inner == decl)
- return error_mark_node;
- spec.spec = inner;
- }
tree constr = NULL_TREE;
bool is_decl = mk & MK_tmpl_decl_mask;
if (is_decl)
@@ -10764,42 +10676,30 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
if (constr)
set_constraints (inner, constr);
}
+ spec.spec = (mk & MK_tmpl_tmpl_mask) ? inner : decl;
}
else
- {
- if (mk == MK_type_spec && inner != decl)
- return error_mark_node;
- spec.spec = type;
- }
+ spec.spec = type;
existing = match_mergeable_specialization (is_decl, &spec);
if (constr)
/* We'll add these back later, if this is the new decl. */
remove_constraints (inner);
if (!existing)
- add_mergeable_specialization (spec.tmpl, spec.args, decl, flags);
+ ; /* We'll add to the table once read. */
else if (mk & MK_tmpl_decl_mask)
{
/* A declaration specialization. */
if (mk & MK_tmpl_tmpl_mask)
- if (tree ti = DECL_TEMPLATE_INFO (existing))
- {
- tree tmpl = TI_TEMPLATE (ti);
- if (DECL_TEMPLATE_RESULT (tmpl) == existing)
- existing = tmpl;
- }
+ existing = DECL_TI_TEMPLATE (existing);
}
else
{
/* A type specialization. */
- if (!(mk & MK_tmpl_tmpl_mask))
+ if (mk & MK_tmpl_tmpl_mask)
+ existing = CLASSTYPE_TI_TEMPLATE (existing);
+ else
existing = TYPE_NAME (existing);
- else if (tree ti = CLASSTYPE_TEMPLATE_INFO (existing))
- {
- tree tmpl = TI_TEMPLATE (ti);
- if (DECL_TEMPLATE_RESULT (tmpl) == TYPE_NAME (existing))
- existing = tmpl;
- }
}
}
else if (mk == MK_unique)
@@ -10862,8 +10762,6 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
break;
}
}
- if (!existing)
- add_mergeable_specialization (key.ret, key.args, decl, 2);
}
else
switch (TREE_CODE (container))
@@ -10877,10 +10775,10 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
if (DECL_LANG_SPECIFIC (name)
&& VAR_OR_FUNCTION_DECL_P (name)
&& DECL_MODULE_ATTACHMENTS_P (name))
- if (attachset *set = attached_table->get (DECL_UID (name)))
- if (key.index < set->num)
+ if (auto *set = attached_table->get (name))
+ if (key.index < set->length ())
{
- existing = set->values[key.index];
+ existing = (*set)[key.index];
if (existing)
{
gcc_checking_assert
@@ -11031,22 +10929,6 @@ trees_in::key_mergeable (int tag, merge_kind mk, tree decl, tree inner,
}
}
}
-
- if (mk == MK_friend_spec)
- {
- spec_entry spec;
- spec.tmpl = tree_node ();
- spec.args = tree_node ();
- spec.spec = decl;
- unsigned flags = u ();
-
- tree e = match_mergeable_specialization (true, &spec);
- if (!e)
- add_mergeable_specialization (spec.tmpl, spec.args,
- existing ? existing : decl, flags);
- else if (e != existing)
- set_overrun ();
- }
}
dump (dumper::MERGE)
@@ -12731,12 +12613,14 @@ struct add_binding_data
bool met_namespace;
};
+/* Return true if we are, or contain something that is exported. */
+
bool
depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
{
auto data = static_cast <add_binding_data *> (data_);
- if (TREE_CODE (decl) != NAMESPACE_DECL || DECL_NAMESPACE_ALIAS (decl))
+ if (!(TREE_CODE (decl) == NAMESPACE_DECL && !DECL_NAMESPACE_ALIAS (decl)))
{
tree inner = decl;
@@ -12791,7 +12675,7 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
d->clear_hidden_binding ();
if (flags & WMB_Export)
OVL_EXPORT_P (d->get_entity ()) = true;
- return false;
+ return bool (flags & WMB_Export);
}
}
}
@@ -12837,30 +12721,37 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
/* Binding and contents are mutually dependent. */
dep->deps.safe_push (data->binding);
- return true;
+ return (flags & WMB_Using
+ ? flags & WMB_Export : DECL_MODULE_EXPORT_P (decl));
}
else if (DECL_NAME (decl) && !data->met_namespace)
{
/* Namespace, walk exactly once. */
gcc_checking_assert (TREE_PUBLIC (decl));
data->met_namespace = true;
- if (data->hash->add_namespace_entities (decl, data->partitions)
- || DECL_MODULE_EXPORT_P (decl))
+ if (data->hash->add_namespace_entities (decl, data->partitions))
+ {
+ /* It contains an exported thing, so it is exported. */
+ gcc_checking_assert (DECL_MODULE_PURVIEW_P (decl));
+ DECL_MODULE_EXPORT_P (decl) = true;
+ }
+
+ if (DECL_MODULE_PURVIEW_P (decl))
{
data->hash->make_dependency (decl, depset::EK_NAMESPACE);
- return true;
+
+ return DECL_MODULE_EXPORT_P (decl);
}
}
return false;
}
-/* Recursively find all the namespace bindings of NS.
- Add a depset for every binding that contains an export or
- module-linkage entity. Add a defining depset for every such decl
- that we need to write a definition. Such defining depsets depend
- on the binding depset. Returns true if we contain something
- explicitly exported. */
+/* Recursively find all the namespace bindings of NS. Add a depset
+ for every binding that contains an export or module-linkage entity.
+ Add a defining depset for every such decl that we need to write a
+ definition. Such defining depsets depend on the binding depset.
+ Returns true if we contain something exported. */
bool
depset::hash::add_namespace_entities (tree ns, bitmap partitions)
@@ -12957,7 +12848,7 @@ specialization_add (bool decl_p, spec_entry *entry, void *data_)
/* Only alias templates can appear in both tables (and
if they're in the type table they must also be in the decl table). */
gcc_checking_assert
- (!match_mergeable_specialization (true, entry, false)
+ (!match_mergeable_specialization (true, entry)
== (decl_p || !DECL_ALIAS_TEMPLATE_P (entry->tmpl)));
}
else if (VAR_OR_FUNCTION_DECL_P (entry->spec))
@@ -13167,6 +13058,28 @@ depset::hash::add_mergeable (depset *mergeable)
dep->deps.safe_push (mergeable);
}
+/* Find the innermost-namespace scope of DECL, and that
+ namespace-scope decl. */
+
+tree
+find_pending_key (tree decl, tree *decl_p = nullptr)
+{
+ tree ns = decl;
+ do
+ {
+ decl = ns;
+ ns = CP_DECL_CONTEXT (ns);
+ if (TYPE_P (ns))
+ ns = TYPE_NAME (ns);
+ }
+ while (TREE_CODE (ns) != NAMESPACE_DECL);
+
+ if (decl_p)
+ *decl_p = decl;
+
+ return ns;
+}
+
/* Iteratively find dependencies. During the walk we may find more
entries on the same binding that need walking. */
@@ -13201,13 +13114,22 @@ depset::hash::find_dependencies (module_state *module)
walker.tree_node (OVL_FUNCTION (decl));
else if (TREE_VISITED (decl))
/* A global tree. */;
- else if (TREE_CODE (decl) == NAMESPACE_DECL
- && !DECL_NAMESPACE_ALIAS (decl))
+ else if (item->get_entity_kind () == EK_NAMESPACE)
add_namespace_context (current, CP_DECL_CONTEXT (decl));
else
{
walker.mark_declaration (decl, current->has_defn ());
+ if (!walker.is_key_order ()
+ && (item->get_entity_kind () == EK_SPECIALIZATION
+ || item->get_entity_kind () == EK_PARTIAL
+ || (item->get_entity_kind () == EK_DECL
+ && item->is_member ())))
+ {
+ tree ns = find_pending_key (decl, nullptr);
+ add_namespace_context (item, ns);
+ }
+
// FIXME: Perhaps p1815 makes this redundant? Or at
// least simplifies it. Voldemort types are only
// ever emissable when containing (inline) function
@@ -13651,43 +13573,6 @@ depset::hash::connect ()
return connector.result;
}
-/* Load the entities referred to by this pendset. */
-
-static bool
-pendset_lazy_load (pendset *pendings, bool specializations_p)
-{
- bool ok = true;
-
- for (unsigned ix = 0; ok && ix != pendings->num; ix++)
- {
- unsigned index = pendings->values[ix];
- if (index & ~(~0u >> 1))
- {
- /* An indirection. */
- if (specializations_p)
- index = ~index;
- pendset *other = pending_table->get (index, true);
- if (!pendset_lazy_load (other, specializations_p))
- ok = false;
- }
- else
- {
- module_state *module = import_entity_module (index);
- binding_slot *slot = &(*entity_ary)[index];
- if (!slot->is_lazy ())
- dump () && dump ("Specialiation %M[%u] already loaded",
- module, index - module->entity_lwm);
- else if (!module->lazy_load (index - module->entity_lwm, slot))
- ok = false;
- }
- }
-
- /* We own set, so delete it now. */
- delete pendings;
-
- return ok;
-}
-
/* Initialize location spans. */
void
@@ -13740,13 +13625,12 @@ loc_spans::init (const line_maps *lmaps, const line_map_ordinary *map)
interface and we're importing a partition. */
bool
-loc_spans::maybe_propagate (module_state *import,
- location_t loc = UNKNOWN_LOCATION)
+loc_spans::maybe_propagate (module_state *import, location_t hwm)
{
bool opened = (module_interface_p () && !module_partition_p ()
&& import->is_partition ());
if (opened)
- open (loc);
+ open (hwm);
return opened;
}
@@ -13754,11 +13638,8 @@ loc_spans::maybe_propagate (module_state *import,
first map of the interval. */
void
-loc_spans::open (location_t hwm = UNKNOWN_LOCATION)
+loc_spans::open (location_t hwm)
{
- if (hwm == UNKNOWN_LOCATION)
- hwm = MAP_START_LOCATION (LINEMAPS_LAST_ORDINARY_MAP (line_table));
-
span interval;
interval.ordinary.first = interval.ordinary.second = hwm;
interval.macro.first = interval.macro.second
@@ -13768,6 +13649,13 @@ loc_spans::open (location_t hwm = UNKNOWN_LOCATION)
&& dump ("Opening span %u ordinary:[%u,... macro:...,%u)",
spans->length (), interval.ordinary.first,
interval.macro.second);
+ if (spans->length ())
+ {
+ /* No overlapping! */
+ auto &last = spans->last ();
+ gcc_checking_assert (interval.ordinary.first >= last.ordinary.second);
+ gcc_checking_assert (interval.macro.second <= last.macro.first);
+ }
spans->safe_push (interval);
}
@@ -13966,6 +13854,21 @@ make_mapper (location_t loc)
return mapper;
}
+static unsigned lazy_snum;
+
+static bool
+recursive_lazy (unsigned snum = ~0u)
+{
+ if (lazy_snum)
+ {
+ error_at (input_location, "recursive lazy load");
+ return true;
+ }
+
+ lazy_snum = snum;
+ return false;
+}
+
/* If THIS is the current purview, issue an import error and return false. */
bool
@@ -14572,29 +14475,30 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
gcc_unreachable ();
case depset::EK_BINDING:
- dump (dumper::CLUSTER)
- && dump ("[%u]=%s %P", ix, b->entity_kind_name (),
- b->get_entity (), b->get_name ());
- for (unsigned jx = b->deps.length (); jx--;)
- {
- depset *dep = b->deps[jx];
- if (jx)
- gcc_checking_assert (dep->get_entity_kind () == depset::EK_USING
- || TREE_VISITED (dep->get_entity ()));
- else
- gcc_checking_assert (dep->get_entity_kind ()
- == depset::EK_NAMESPACE
- && dep->get_entity () == b->get_entity ());
- }
+ {
+ dump (dumper::CLUSTER)
+ && dump ("[%u]=%s %P", ix, b->entity_kind_name (),
+ b->get_entity (), b->get_name ());
+ depset *ns_dep = b->deps[0];
+ gcc_checking_assert (ns_dep->get_entity_kind ()
+ == depset::EK_NAMESPACE
+ && ns_dep->get_entity () == b->get_entity ());
+ for (unsigned jx = b->deps.length (); --jx;)
+ {
+ depset *dep = b->deps[jx];
+ // We could be declaring something that is also a
+ // (merged) import
+ gcc_checking_assert (dep->is_import ()
+ || TREE_VISITED (dep->get_entity ())
+ || (dep->get_entity_kind ()
+ == depset::EK_USING));
+ }
+ }
break;
case depset::EK_DECL:
- if (b->is_member ())
- {
- case depset::EK_SPECIALIZATION: /* Yowzer! */
- case depset::EK_PARTIAL: /* Hey, let's do it again! */
- counts[MSC_pendings]++;
- }
+ case depset::EK_SPECIALIZATION:
+ case depset::EK_PARTIAL:
b->cluster = counts[MSC_entities]++;
sec.mark_declaration (b->get_entity (), b->has_defn ());
/* FALLTHROUGH */
@@ -14612,10 +14516,9 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
dump (dumper::CLUSTER) && (dump.outdent (), true);
/* Ensure every imported decl is referenced before we start
- streaming. This ensures that we never encounter the
- situation where this cluster instantiates some implicit
- member that importing some other decl causes to be
- instantiated. */
+ streaming. This ensures that we never encounter the situation
+ where this cluster instantiates some implicit member that
+ importing some other decl causes to be instantiated. */
sec.set_importing (+1);
for (unsigned ix = 0; ix != size; ix++)
{
@@ -14626,13 +14529,36 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
{
depset *dep = b->deps[jx];
- if (!dep->is_binding ()
- && dep->is_import () && !TREE_VISITED (dep->get_entity ()))
+ if (dep->is_binding ())
{
- tree import = dep->get_entity ();
+ for (unsigned ix = dep->deps.length (); --ix;)
+ {
+ depset *bind = dep->deps[ix];
+ if (bind->get_entity_kind () == depset::EK_USING)
+ bind = bind->deps[1];
+ if (bind->is_import ())
+ {
+ tree import = bind->get_entity ();
+ if (!TREE_VISITED (import))
+ {
+ sec.tree_node (import);
+ dump (dumper::CLUSTER)
+ && dump ("Seeded import %N", import);
+ }
+ }
+ }
+ /* Also check the namespace itself. */
+ dep = dep->deps[0];
+ }
- sec.tree_node (import);
- dump (dumper::CLUSTER) && dump ("Seeded import %N", import);
+ if (dep->is_import ())
+ {
+ tree import = dep->get_entity ();
+ if (!TREE_VISITED (import))
+ {
+ sec.tree_node (import);
+ dump (dumper::CLUSTER) && dump ("Seeded import %N", import);
+ }
}
}
}
@@ -14893,20 +14819,6 @@ module_state::read_cluster (unsigned snum)
: 0,
decls, type, visible))
sec.set_overrun ();
-
- if (type
- && CP_DECL_CONTEXT (type) == ns
- && !sec.is_duplicate (type))
- add_module_decl (ns, name, type);
-
- for (ovl_iterator iter (decls); iter; ++iter)
- if (!iter.using_p ())
- {
- tree decl = *iter;
- if (CP_DECL_CONTEXT (decl) == ns
- && !sec.is_duplicate (decl))
- add_module_decl (ns, name, decl);
- }
}
break;
@@ -14950,11 +14862,7 @@ module_state::read_cluster (unsigned snum)
if (abstract)
;
else if (DECL_ABSTRACT_P (decl))
- {
- bool cloned = maybe_clone_body (decl);
- if (!cloned)
- from ()->set_error ();
- }
+ vec_safe_push (post_load_decls, decl);
else
{
bool aggr = aggregate_value_p (DECL_RESULT (decl), decl);
@@ -15056,36 +14964,29 @@ module_state::write_namespaces (elf_out *to, vec<depset *> spaces,
tree ns = b->get_entity ();
gcc_checking_assert (TREE_CODE (ns) == NAMESPACE_DECL);
+ /* P1815 may have something to say about this. */
+ gcc_checking_assert (TREE_PUBLIC (ns));
- bool export_p = DECL_MODULE_EXPORT_P (ns);
- bool inline_p = DECL_NAMESPACE_INLINE_P (ns);
- bool public_p = TREE_PUBLIC (ns);
-
- /* We should only be naming public namespaces, or our own
- private ones. Internal linkage ones never get to be written
- out -- because that means something erroneously referred to a
- member. However, Davis Herring's paper probably changes that
- by permitting them to be written out, but then an error if on
- touches them. (Certain cases cannot be detected until that
- point.) */
- gcc_checking_assert (public_p || !DECL_MODULE_IMPORT_P (ns));
unsigned flags = 0;
- if (export_p)
+ if (TREE_PUBLIC (ns))
flags |= 1;
- if (inline_p)
+ if (DECL_NAMESPACE_INLINE_P (ns))
flags |= 2;
- if (public_p)
+ if (DECL_MODULE_PURVIEW_P (ns))
flags |= 4;
- dump () && dump ("Writing namespace:%u %N%s%s%s",
- b->cluster, ns, export_p ? ", export" : "",
- public_p ? ", public" : "",
- inline_p ? ", inline" : "");
+ if (DECL_MODULE_EXPORT_P (ns))
+ flags |= 8;
+
+ dump () && dump ("Writing namespace:%u %N%s%s%s%s",
+ b->cluster, ns,
+ flags & 1 ? ", public" : "",
+ flags & 2 ? ", inline" : "",
+ flags & 4 ? ", purview" : "",
+ flags & 8 ? ", export" : "");
sec.u (b->cluster);
sec.u (to->name (DECL_NAME (ns)));
write_namespace (sec, b->deps[0]);
- /* Don't use bools, because this can be near the end of the
- section, and it won't save anything anyway. */
sec.u (flags);
write_location (sec, DECL_SOURCE_LOCATION (ns));
}
@@ -15119,26 +15020,40 @@ module_state::read_namespaces (unsigned num)
unsigned flags = sec.u ();
location_t src_loc = read_location (sec);
- if (entity_index >= entity_num || !parent)
+ if (entity_index >= entity_num
+ || !parent
+ || (flags & 0xc) == 0x8)
sec.set_overrun ();
if (sec.get_overrun ())
break;
tree id = name ? get_identifier (from ()->name (name)) : NULL_TREE;
- bool public_p = flags & 4;
- bool inline_p = flags & 2;
- bool export_p = flags & 1;
-
- dump () && dump ("Read namespace:%u %P%s%s%s",
- entity_index, parent, id, export_p ? ", export" : "",
- public_p ? ", public" : "",
- inline_p ? ", inline" : "");
- bool visible_p = (export_p
- || (public_p && (is_partition () || is_module ())));
- tree inner = add_imported_namespace (parent, id, mod,
- src_loc, visible_p, inline_p);
- if (export_p && is_partition ())
- DECL_MODULE_EXPORT_P (inner) = true;
+
+ dump () && dump ("Read namespace:%u %P%s%s%s%s",
+ entity_index, parent, id,
+ flags & 1 ? ", public" : "",
+ flags & 2 ? ", inline" : "",
+ flags & 4 ? ", purview" : "",
+ flags & 8 ? ", export" : "");
+ bool visible_p = ((flags & 8)
+ || ((flags & 1)
+ && (flags & 4)
+ && (is_partition () || is_module ())));
+ tree inner = add_imported_namespace (parent, id, src_loc, mod,
+ bool (flags & 2), visible_p);
+ if (!inner)
+ {
+ sec.set_overrun ();
+ break;
+ }
+
+ if (is_partition ())
+ {
+ if (flags & 4)
+ DECL_MODULE_PURVIEW_P (inner) = true;
+ if (flags & 8)
+ DECL_MODULE_EXPORT_P (inner) = true;
+ }
/* Install the namespace. */
(*entity_ary)[entity_lwm + entity_index] = inner;
@@ -15309,98 +15224,208 @@ module_state::read_entities (unsigned count, unsigned lwm, unsigned hwm)
/* Write the pending table to MOD_SNAME_PFX.pnd
- Specializations & partials are keyed to their primary template.
- Members are keyed to their context.
+ The pending table holds information about clusters that need to be
+ loaded because they contain information about something that is not
+ found by namespace-scope lookup.
+
+ The three cases are:
+
+ (a) Template (maybe-partial) specializations that we have
+ instantiated or defined. When an importer needs to instantiate
+ that template, they /must have/ the partial, explicit & extern
+ specializations available. If they have the other specializations
+ available, they'll have less work to do. Thus, when we're about to
+ instantiate FOO, we have to be able to ask 'are there any
+ specialization of FOO in our imports?'.
+
+ (b) (Maybe-implicit) member functions definitions. A class could
+ be defined in one header, and an inline member defined in a
+ different header (this occurs in the STL). Similarly, like the
+ specialization case, an implicit member function could have been
+ 'instantiated' in one module, and it'd be nice to not have to
+ reinstantiate it in another.
+
+ (c) A member classes completed elsewhere. A member class could be
+ declared in one header and defined in another. We need to know to
+ load the class definition before looking in it. This turns out to
+ be a specific case of #b, so we can treat these the same. But it
+ does highlight an issue -- there could be an intermediate import
+ between the outermost containing namespace-scope class and the
+ innermost being-defined member class. This is actually possible
+ with all of these cases, so be aware -- we're not just talking of
+ one level of import to get to the innermost namespace.
+
+ This gets complicated fast, it took me multiple attempts to even
+ get something remotely working. Partially because I focussed on
+ optimizing what I think turns out to be a smaller problem, given
+ the known need to do the more general case *anyway*. I document
+ the smaller problem, because it does appear to be the natural way
+ to do it. It's trap!
+
+ **** THE TRAP
+
+ Let's refer to the primary template or the containing class as the
+ KEY. And the specialization or member as the PENDING-ENTITY. (To
+ avoid having to say those mouthfuls all the time.)
+
+ In either case, we have an entity and we need some way of mapping
+ that to a set of entities that need to be loaded before we can
+ proceed with whatever processing of the entity we were going to do.
+
+ We need to link the key to the pending-entity in some way. Given a
+ key, tell me the pending-entities I need to have loaded. However
+ we tie the key to the pending-entity must not rely on the key being
+ loaded -- that'd defeat the lazy loading scheme.
+
+ As the key will be an import in we know its entity number (either
+ because we imported it, or we're writing it out too). Thus we can
+ generate a map of key-indices to pending-entities. The
+ pending-entity indices will be into our span of the entity table,
+ and thus allow them to be lazily loaded. The key index will be
+ into another slot of the entity table. Notice that this checking
+ could be expensive, we don't want to iterate over a bunch of
+ pending-entity indices (across multiple imports), every time we're
+ about do to the thing with the key. We need to quickly determine
+ 'definitely nothing needed'.
+
+ That's almost good enough, except that key indices are not unique
+ in a couple of cases :( Specifically the Global Module or a module
+ partition can result in multiple modules assigning an entity index
+ for the key. The decl-merging on loading will detect that so we
+ only have one Key loaded, and in the entity hash it'll indicate the
+ entity index of first load. Which might be different to how we
+ know it. Notice this is restricted to GM entities or this-module
+ entities. Foreign imports cannot have this.
+
+ We can simply resolve this in the direction of how this module
+ referred to the key to how the importer knows it. Look in the
+ entity table slot that we nominate, maybe lazy load it, and then
+ lookup the resultant entity in the entity hash to learn how the
+ importer knows it.
+
+ But we need to go in the other direction :( Given the key, find all
+ the index-aliases of that key. We can partially solve that by
+ adding an alias hash table. Whenever we load a merged decl, add or
+ augment a mapping from the entity (or its entity-index) to the
+ newly-discovered index. Then when we look for pending entities of
+ a key, we also iterate over this aliases this mapping provides.
+
+ But that requires the alias to be loaded. And that's not
+ necessarily true.
+
+ *** THE SIMPLER WAY
+
+ The remaining fixed thing we have is the innermost namespace
+ containing the ultimate namespace-scope container of the key and
+ the name of that container (which might be the key itself). I.e. a
+ namespace-decl/identifier/module tuple. Let's call this the
+ top-key. We'll discover that the module is not important here,
+ because of cross-module possibilities mentioned in case #c above.
+ We can't markup namespace-binding slots. The best we can do is
+ mark the binding vector with 'there's something here', and have
+ another map from namespace/identifier pairs to a vector of pending
+ entity indices.
+
+ Maintain a pending-entity map. This is keyed by top-key, and
+ maps to a vector of pending-entity indices. On the binding vector
+ have flags saying whether the pending-name-entity map has contents.
+ (We might want to further extend the key to be GM-vs-Partition and
+ specialization-vs-member, but let's not get ahead of ourselves.)
+
+ For every key-like entity, find the outermost namespace-scope
+ name. Use that to lookup in the pending-entity map and then make
+ sure the specified entities are loaded.
+
+ An optimization might be to have a flag in each key-entity saying
+ that it's top key might be in the entity table. It's not clear to
+ me how to set that flag cheaply -- cheaper than just looking.
+
+ FIXME: It'd be nice to have a bit in decls to tell us whether to
+ even try this. We can have a 'already done' flag, that we set when
+ we've done KLASS's lazy pendings. When we import a module that
+ registers pendings on the same top-key as KLASS we need to clear
+ the flag. A recursive walk of the top-key clearing the bit will
+ suffice. Plus we only need to recurse on classes that have the bit
+ set. (That means we need to set the bit on parents of KLASS here,
+ don't forget.) However, first: correctness, second: efficiency. */
- For specializations & partials, primary templates are keyed to the
- (namespace name) of their originating decl (because that's the only
- handle we have). */
-
-void
+unsigned
module_state::write_pendings (elf_out *to, vec<depset *> depsets,
- depset::hash &table,
- unsigned count, unsigned *crc_p)
+ depset::hash &table, unsigned *crc_p)
{
- dump () && dump ("Writing %u pendings", count);
+ dump () && dump ("Writing pending-entities");
dump.indent ();
trees_out sec (to, this, table);
sec.begin ();
+ unsigned count = 0;
+ tree cache_ns = NULL_TREE;
+ tree cache_id = NULL_TREE;
+ unsigned cache_section = ~0;
for (unsigned ix = 0; ix < depsets.length (); ix++)
{
depset *d = depsets[ix];
- depset::entity_kind kind = d->get_entity_kind ();
- tree key = NULL_TREE;
- bool is_spec = false;
-
- if (kind == depset::EK_SPECIALIZATION)
- {
- is_spec = true;
- key = reinterpret_cast <spec_entry *> (d->deps[0])->tmpl;
- }
- else if (kind == depset::EK_PARTIAL)
- {
- is_spec = true;
- key = CLASSTYPE_TI_TEMPLATE (TREE_TYPE (d->get_entity ()));
- }
- else if (kind == depset::EK_DECL && d->is_member ())
- {
- tree ctx = DECL_CONTEXT (d->get_entity ());
- key = TYPE_NAME (ctx);
- if (tree ti = CLASSTYPE_TEMPLATE_INFO (ctx))
- if (DECL_TEMPLATE_RESULT (TI_TEMPLATE (ti)) == key)
- key = TI_TEMPLATE (ti);
- }
+ if (d->is_binding ())
+ continue;
- // FIXME:OPTIMIZATION More than likely when there is one pending
- // member, there will be others. All written in the same
- // section and keyed to the same class. We only need to record
- // one of them. The same is not true for specializations
+ if (d->is_import ())
+ continue;
- if (key)
- {
- gcc_checking_assert (!d->is_import ());
+ if (!(d->get_entity_kind () == depset::EK_SPECIALIZATION
+ || d->get_entity_kind () == depset::EK_PARTIAL
+ || (d->get_entity_kind () == depset::EK_DECL && d->is_member ())))
+ continue;
- {
- /* Key the entity to its key. */
- depset *key_dep = table.find_dependency (key);
- if (key_dep->get_entity_kind () == depset::EK_REDIRECT)
- key_dep = key_dep->deps[0];
- unsigned key_origin
- = key_dep->is_import () ? key_dep->section : 0;
- sec.u (key_origin);
- sec.u (key_dep->cluster);
- sec.u (d->cluster);
- dump () && dump ("%s %N entity:%u keyed to %M[%u] %N",
- is_spec ? "Specialization" : "Member",
- d->get_entity (),
- d->cluster, (*modules)[key_origin],
- key_dep->cluster, key);
- }
+ tree key_decl = nullptr;
+ tree key_ns = find_pending_key (d->get_entity (), &key_decl);
+ tree key_name = DECL_NAME (key_decl);
- if (is_spec)
+ if (IDENTIFIER_ANON_P (key_name))
+ {
+ gcc_checking_assert (IDENTIFIER_LAMBDA_P (key_name));
+ if (tree attached = LAMBDA_TYPE_EXTRA_SCOPE (TREE_TYPE (key_decl)))
+ key_name = DECL_NAME (attached);
+ else
{
- /* Key the general template to the originating decl. */
- tree origin = get_originating_module_decl (key);
- sec.tree_node (CP_DECL_CONTEXT (origin));
- sec.tree_node (DECL_NAME (origin));
-
- unsigned origin_ident = import_entity_index (origin);
- module_state *origin_from = this;
- if (!(origin_ident & ~(~0u>>1)))
- origin_from = import_entity_module (origin_ident);
- sec.u (origin_from->remap);
+ /* There's nothing to attach it to. Must
+ always reinstantiate. */
+ dump ()
+ && dump ("Unattached lambda %N[%u] section:%u",
+ d->get_entity_kind () == depset::EK_DECL
+ ? "Member" : "Specialization", d->get_entity (),
+ d->cluster, d->section);
+ continue;
}
- else
- sec.tree_node (NULL);
- count--;
}
+
+ char const *also = "";
+ if (d->section == cache_section
+ && key_ns == cache_ns
+ && key_name == cache_id)
+ /* Same section & key as previous, no need to repeat ourselves. */
+ also = "also ";
+ else
+ {
+ cache_ns = key_ns;
+ cache_id = key_name;
+ cache_section = d->section;
+ gcc_checking_assert (table.find_dependency (cache_ns));
+ sec.tree_node (cache_ns);
+ sec.tree_node (cache_id);
+ sec.u (d->cluster);
+ count++;
+ }
+ dump () && dump ("Pending %s %N entity:%u section:%u %skeyed to %P",
+ d->get_entity_kind () == depset::EK_DECL
+ ? "member" : "specialization", d->get_entity (),
+ d->cluster, cache_section, also, cache_ns, cache_id);
}
- gcc_assert (!count);
sec.end (to, to->name (MOD_SNAME_PFX ".pnd"), crc_p);
dump.outdent ();
+
+ return count;
}
bool
@@ -15416,72 +15441,28 @@ module_state::read_pendings (unsigned count)
for (unsigned ix = 0; ix != count; ix++)
{
- unsigned key_origin = slurp->remap_module (sec.u ());
- unsigned key_index = sec.u ();
- unsigned ent_index = sec.u ();
- module_state *from = (*modules)[key_origin];
- tree ns = sec.tree_node ();
+ pending_key key;
+ unsigned index;
- if (!key_origin
- || key_index >= from->entity_num || ent_index >= entity_num
- || (ns && TREE_CODE (ns) != NAMESPACE_DECL))
+ key.ns = sec.tree_node ();
+ key.id = sec.tree_node ();
+ index = sec.u ();
+
+ if (!key.ns || !key.id
+ || !(TREE_CODE (key.ns) == NAMESPACE_DECL
+ && !DECL_NAMESPACE_ALIAS (key.ns))
+ || !identifier_p (key.id)
+ || index >= entity_num)
sec.set_overrun ();
if (sec.get_overrun ())
break;
- bool loaded = false;
- dump () && dump ("%s keyed to %M[%u] entity:%u",
- ns ? "Specialization" : "Member",
- from, key_index, ent_index);
- unsigned key_ident = from->entity_lwm + key_index;
- if (pending_table->add (ns ? key_ident : ~key_ident,
- ent_index + entity_lwm))
- {
- binding_slot &slot = (*entity_ary)[key_ident];
- if (slot.is_lazy ())
- slot.or_lazy (ns ? 1 : 2);
- else
- {
- tree key = slot;
-
- loaded = true;
- if (ns)
- {
- if (key && TREE_CODE (key) == TEMPLATE_DECL)
- DECL_MODULE_PENDING_SPECIALIZATIONS_P (key) = true;
- else
- sec.set_overrun ();
- }
- else
- {
- if (key && TREE_CODE (key) == TYPE_DECL)
- DECL_MODULE_PENDING_MEMBERS_P (key) = true;
- else
- sec.set_overrun ();
- }
- }
- }
-
- if (ns)
- {
- /* We also need to mark the namespace binding of the
- originating template, so we know to set its pending
- specializations flag, when we load it. */
- tree name = sec.tree_node ();
- unsigned origin = slurp->remap_module (sec.u ());
- if (!origin || !name || TREE_CODE (name) != IDENTIFIER_NODE)
- sec.set_overrun ();
- if (sec.get_overrun ())
- break;
+ dump () && dump ("Pending:%u keyed to %P", index, key.ns, key.id);
- module_state *origin_from = (*modules)[origin];
- if (!loaded
- && (origin_from->is_header ()
- || (origin_from->is_partition ()
- || origin_from->is_module ())))
- note_pending_specializations (ns, name, origin_from->is_header ());
- }
+ index += entity_lwm;
+ auto &vec = pending_table->get_or_insert (key);
+ vec.safe_push (index);
}
dump.outdent ();
@@ -15490,23 +15471,6 @@ module_state::read_pendings (unsigned count)
return true;
}
-/* Return true if module MOD cares about lazy specializations keyed to
- possibly duplicated entity bindings. */
-
-bool
-lazy_specializations_p (unsigned mod, bool header_p, bool partition_p)
-{
- module_state *module = (*modules)[mod];
-
- if (module->is_header ())
- return header_p;
-
- if (module->is_module () || module->is_partition ())
- return partition_p;
-
- return false;
-}
-
/* Read & write locations. */
enum loc_kind {
LK_ORDINARY,
@@ -15520,13 +15484,13 @@ enum loc_kind {
static const module_state *
module_for_ordinary_loc (location_t loc)
{
- unsigned pos = 1;
- unsigned len = modules->length () - pos;
+ unsigned pos = 0;
+ unsigned len = ool->length () - pos;
while (len)
{
unsigned half = len / 2;
- module_state *probe = (*modules)[pos + half];
+ module_state *probe = (*ool)[pos + half];
if (loc < probe->ordinary_locs.first)
len = half;
else if (loc < probe->ordinary_locs.second)
@@ -15538,7 +15502,7 @@ module_for_ordinary_loc (location_t loc)
}
}
- return NULL;
+ return nullptr;
}
static const module_state *
@@ -15822,31 +15786,49 @@ module_state::write_prepare_maps (module_state_config *)
for (unsigned ix = loc_spans::SPAN_FIRST; ix != spans.length (); ix++)
{
loc_spans::span &span = spans[ix];
- line_map_ordinary const *omap
- = linemap_check_ordinary (linemap_lookup (line_table,
- span.ordinary.first));
- /* We should exactly match up. */
- gcc_checking_assert (MAP_START_LOCATION (omap) == span.ordinary.first);
-
- line_map_ordinary const *fmap = omap;
- for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++)
+ if (span.ordinary.first != span.ordinary.second)
{
- /* We should never find a module linemap in an interval. */
- gcc_checking_assert (!MAP_MODULE_P (omap));
+ line_map_ordinary const *omap
+ = linemap_check_ordinary (linemap_lookup (line_table,
+ span.ordinary.first));
- if (max_range < omap->m_range_bits)
- max_range = omap->m_range_bits;
- }
+ /* We should exactly match up. */
+ gcc_checking_assert (MAP_START_LOCATION (omap) == span.ordinary.first);
+
+ line_map_ordinary const *fmap = omap;
+ for (; MAP_START_LOCATION (omap) < span.ordinary.second; omap++)
+ {
+ /* We should never find a module linemap in an interval. */
+ gcc_checking_assert (!MAP_MODULE_P (omap));
+
+ if (max_range < omap->m_range_bits)
+ max_range = omap->m_range_bits;
+ }
- unsigned count = omap - fmap;
- info.num_maps.first += count;
+ info.num_maps.first += omap - fmap;
+ }
if (span.macro.first != span.macro.second)
{
- count = linemap_lookup_macro_index (line_table, span.macro.first) + 1;
- count -= linemap_lookup_macro_index (line_table,
+ /* Iterate over the span's macros, to elide the empty
+ expansions. */
+ unsigned count = 0;
+ for (unsigned macro
+ = linemap_lookup_macro_index (line_table,
span.macro.second - 1);
+ macro < LINEMAPS_MACRO_USED (line_table);
+ macro++)
+ {
+ line_map_macro const *mmap
+ = LINEMAPS_MACRO_MAP_AT (line_table, macro);
+ if (MAP_START_LOCATION (mmap) < span.macro.first)
+ /* Fallen out of the span. */
+ break;
+
+ if (mmap->n_tokens)
+ count++;
+ }
dump (dumper::LOCATION) && dump ("Span:%u %u macro maps", ix, count);
info.num_maps.second += count;
}
@@ -15874,7 +15856,7 @@ module_state::write_prepare_maps (module_state_config *)
line_map_ordinary const *omap
= linemap_check_ordinary (linemap_lookup (line_table,
- span.ordinary.first));
+ span.ordinary.first));
location_t base = MAP_START_LOCATION (omap);
/* Preserve the low MAX_RANGE bits of base by incrementing ORD_OFF. */
@@ -15889,24 +15871,28 @@ module_state::write_prepare_maps (module_state_config *)
location_t start_loc = MAP_START_LOCATION (omap);
unsigned to = start_loc + span.ordinary_delta;
location_t end_loc = MAP_START_LOCATION (omap + 1);
-
- dump () && dump ("Ordinary span:%u [%u,%u):%u->%d(%u)", ix, start_loc,
+
+ dump () && dump ("Ordinary span:%u [%u,%u):%u->%d(%u)",
+ ix, start_loc,
end_loc, end_loc - start_loc,
span.ordinary_delta, to);
/* There should be no change in the low order bits. */
gcc_checking_assert (((start_loc ^ to) & range_mask) == 0);
}
+
/* The ending serialized value. */
ord_off = span.ordinary.second + span.ordinary_delta;
}
- dump () && dump ("Ordinary hwm:%u macro lwm:%u", ord_off, mac_off);
+ dump () && dump ("Ordinary:%u maps hwm:%u macro:%u maps lwm:%u ",
+ info.num_maps.first, ord_off,
+ info.num_maps.second, mac_off);
dump.outdent ();
info.max_range = max_range;
-
+
return info;
}
@@ -15974,14 +15960,15 @@ module_state::write_ordinary_maps (elf_out *to, location_map_info &info,
/* We should never find a module linemap in an interval. */
gcc_checking_assert (!MAP_MODULE_P (omap));
- /* We expect very few filenames, so just an array. */
+ /* We expect very few filenames, so just an array.
+ (Not true when headers are still in play :() */
for (unsigned jx = filenames.length (); jx--;)
{
const char *name = filenames[jx];
if (0 == strcmp (name, fname))
{
/* Reset the linemap's name, because for things like
- preprocessed input we could have multple
+ preprocessed input we could have multiple
instances of the same name, and we'd rather not
percolate that. */
const_cast<line_map_ordinary *> (omap)->to_file = name;
@@ -16106,27 +16093,24 @@ module_state::write_macro_maps (elf_out *to, location_map_info &info,
{
loc_spans::span &span = spans[ix];
if (span.macro.first == span.macro.second)
+ /* Empty span. */
continue;
- for (unsigned first
+ for (unsigned macro
= linemap_lookup_macro_index (line_table, span.macro.second - 1);
- first < LINEMAPS_MACRO_USED (line_table);
- first++)
+ macro < LINEMAPS_MACRO_USED (line_table);
+ macro++)
{
line_map_macro const *mmap
- = LINEMAPS_MACRO_MAP_AT (line_table, first);
+ = LINEMAPS_MACRO_MAP_AT (line_table, macro);
location_t start_loc = MAP_START_LOCATION (mmap);
if (start_loc < span.macro.first)
+ /* Fallen out of the span. */
break;
- if (macro_num == info.num_maps.second)
- {
- /* We're ending on an empty macro expansion. The
- preprocessor doesn't prune such things. */
- // FIXME:QOI This is an example of the non-pruning of
- // locations. See write_prepare_maps.
- gcc_checking_assert (!mmap->n_tokens);
- continue;
- }
+
+ if (!mmap->n_tokens)
+ /* Empty expansion. */
+ continue;
sec.u (offset);
sec.u (mmap->n_tokens);
@@ -16291,7 +16275,8 @@ module_state::read_macro_maps ()
location_t zero = sec.u ();
dump () && dump ("Macro maps:%u zero:%u", num_macros, zero);
- bool propagated = spans.maybe_propagate (this);
+ bool propagated = spans.maybe_propagate (this,
+ line_table->highest_location + 1);
location_t offset = LINEMAPS_MACRO_LOWEST_LOCATION (line_table);
slurp->loc_deltas.second = zero - offset;
@@ -16550,7 +16535,7 @@ module_state::read_define (bytes_in &sec, cpp_reader *reader, bool located) cons
}
/* Exported macro data. */
-struct macro_export {
+struct GTY(()) macro_export {
cpp_macro *def;
location_t undef_loc;
@@ -16715,7 +16700,7 @@ static vec<macro_import, va_heap, vl_embed> *macro_imports;
indexes this array. If the zeroth slot is not for module zero,
there is no export. */
-static vec<macro_export, va_heap, vl_embed> *macro_exports;
+static GTY(()) vec<macro_export, va_gc> *macro_exports;
/* The reachable set of header imports from this TU. */
@@ -17173,6 +17158,37 @@ module_state::write_inits (elf_out *to, depset::hash &table, unsigned *crc_ptr)
return count;
}
+/* We have to defer some post-load processing until we've completed
+ reading, because they can cause more reading. */
+
+static void
+post_load_processing ()
+{
+ /* We mustn't cause a GC, our caller should have arranged for that
+ not to happen. */
+ gcc_checking_assert (function_depth);
+
+ if (!post_load_decls)
+ return;
+
+ tree old_cfd = current_function_decl;
+ struct function *old_cfun = cfun;
+ while (post_load_decls->length ())
+ {
+ tree decl = post_load_decls->pop ();
+
+ dump () && dump ("Post-load processing of %N", decl);
+
+ gcc_checking_assert (DECL_ABSTRACT_P (decl));
+ /* Cloning can cause loading -- specifically operator delete for
+ the deleting dtor. */
+ maybe_clone_body (decl);
+ }
+
+ cfun = old_cfun;
+ current_function_decl = old_cfd;
+}
+
bool
module_state::read_inits (unsigned count)
{
@@ -17182,6 +17198,7 @@ module_state::read_inits (unsigned count)
dump () && dump ("Reading %u initializers", count);
dump.indent ();
+ lazy_snum = ~0u;
for (unsigned ix = 0; ix != count; ix++)
{
/* Merely referencing the decl causes its initializer to be read
@@ -17193,6 +17210,8 @@ module_state::read_inits (unsigned count)
if (decl)
dump ("Initializer:%u for %N", count, decl);
}
+ lazy_snum = 0;
+ post_load_processing ();
dump.outdent ();
if (!sec.end (from ()))
return false;
@@ -17492,6 +17511,21 @@ module_state::read_config (module_state_config &config)
return cfg.end (from ());
}
+/* Comparator for ordering the Ordered Ordinary Location array. */
+
+static int
+ool_cmp (const void *a_, const void *b_)
+{
+ auto *a = *static_cast<const module_state *const *> (a_);
+ auto *b = *static_cast<const module_state *const *> (b_);
+ if (a == b)
+ return 0;
+ else if (a->ordinary_locs.first < b->ordinary_locs.second)
+ return -1;
+ else
+ return +1;
+}
+
/* Use ELROND format to record the following sections:
qualified-names : binding value(s)
MOD_SNAME_PFX.README : human readable, strings
@@ -17598,6 +17632,16 @@ module_state::write (elf_out *to, cpp_reader *reader)
/* Determine Strongy Connected Components. */
vec<depset *> sccs = table.connect ();
+ vec_alloc (ool, modules->length ());
+ for (unsigned ix = modules->length (); --ix;)
+ {
+ auto *import = (*modules)[ix];
+ if (import->loadedness > ML_NONE
+ && !(partitions && bitmap_bit_p (partitions, import->mod)))
+ ool->quick_push (import);
+ }
+ ool->qsort (ool_cmp);
+
unsigned crc = 0;
module_state_config config;
location_map_info map_info = write_prepare_maps (&config);
@@ -17709,6 +17753,8 @@ module_state::write (elf_out *to, cpp_reader *reader)
}
}
+ /* depset::cluster - entity number (on entities)
+ depset::section - cluster number */
/* We'd better have written as many sections and found as many
namespaces as we predicted. */
gcc_assert (counts[MSC_sec_hwm] == to->get_section_limit ()
@@ -17728,8 +17774,7 @@ module_state::write (elf_out *to, cpp_reader *reader)
counts[MSC_bindings] = write_bindings (to, sccs, &crc);
/* Write the unnamed. */
- if (counts[MSC_pendings])
- write_pendings (to, sccs, table, counts[MSC_pendings], &crc);
+ counts[MSC_pendings] = write_pendings (to, sccs, table, &crc);
/* Write the import table. */
if (config.num_imports > 1)
@@ -17761,6 +17806,8 @@ module_state::write (elf_out *to, cpp_reader *reader)
spaces.release ();
sccs.release ();
+ vec_free (ool);
+
/* Human-readable info. */
write_readme (to, reader, config.dialect_str, extensions);
@@ -17904,21 +17951,6 @@ module_state::read_preprocessor (bool outermost)
return check_read (outermost, ok);
}
-static unsigned lazy_snum;
-
-static bool
-recursive_lazy (unsigned snum = ~0u)
-{
- if (lazy_snum)
- {
- error_at (input_location, "recursive lazy load");
- return true;
- }
-
- lazy_snum = snum;
- return false;
-}
-
/* Read language state. */
bool
@@ -17993,16 +18025,15 @@ module_state::read_language (bool outermost)
unsigned hwm = counts[MSC_sec_hwm];
for (unsigned ix = counts[MSC_sec_lwm]; ok && ix != hwm; ix++)
- {
- if (!load_section (ix, NULL))
- {
- ok = false;
- break;
- }
- ggc_collect ();
- }
-
+ if (!load_section (ix, NULL))
+ {
+ ok = false;
+ break;
+ }
lazy_snum = 0;
+ post_load_processing ();
+
+ ggc_collect ();
if (ok && CHECKING_P)
for (unsigned ix = 0; ix != entity_num; ix++)
@@ -18491,6 +18522,7 @@ set_defining_module (tree decl)
gcc_checking_assert (!use_tpl);
/* Get to the TEMPLATE_DECL. */
decl = TI_TEMPLATE (ti);
+ gcc_checking_assert (!DECL_MODULE_IMPORT_P (decl));
}
/* Record it on the class_members list. */
@@ -18537,13 +18569,15 @@ maybe_attach_decl (tree ctx, tree decl)
gcc_checking_assert (DECL_NAMESPACE_SCOPE_P (ctx));
if (!attached_table)
- attached_table = new attachset::hash (EXPERIMENT (1, 400));
+ attached_table = new attached_map_t (EXPERIMENT (1, 400));
- if (attached_table->add (DECL_UID (ctx), decl))
- {
- retrofit_lang_decl (ctx);
- DECL_MODULE_ATTACHMENTS_P (ctx) = true;
- }
+ auto &vec = attached_table->get_or_insert (ctx);
+ if (!vec.length ())
+ {
+ retrofit_lang_decl (ctx);
+ DECL_MODULE_ATTACHMENTS_P (ctx) = true;
+ }
+ vec.safe_push (decl);
}
/* Create the flat name string. It is simplest to have it handy. */
@@ -18618,6 +18652,8 @@ module_state::do_import (cpp_reader *reader, bool outermost)
{
const char *file = maybe_add_cmi_prefix (filename);
dump () && dump ("CMI is %s", file);
+ if (note_module_cmi_yes || inform_cmi_p)
+ inform (loc, "reading CMI %qs", file);
fd = open (file, O_RDONLY | O_CLOEXEC | O_BINARY);
e = errno;
}
@@ -18749,6 +18785,7 @@ lazy_load_binding (unsigned mod, tree ns, tree id, binding_slot *mslot)
{
ok = module->load_section (snum, mslot);
lazy_snum = 0;
+ post_load_processing ();
}
dump.pop (n);
@@ -18774,14 +18811,19 @@ lazy_load_binding (unsigned mod, tree ns, tree id, binding_slot *mslot)
module->get_flatname ());
}
-/* Load any pending specializations of TMPL. Called just before
- instantiating TMPL. */
+/* Load any pending entities keyed to the top-key of DECL. */
void
-lazy_load_specializations (tree tmpl)
+lazy_load_pendings (tree decl)
{
- gcc_checking_assert (DECL_MODULE_PENDING_SPECIALIZATIONS_P (tmpl)
- && DECL_MODULE_ENTITY_P (tmpl));
+ tree key_decl;
+ pending_key key;
+ key.ns = find_pending_key (decl, &key_decl);
+ key.id = DECL_NAME (key_decl);
+
+ auto *pending_vec = pending_table ? pending_table->get (key) : nullptr;
+ if (!pending_vec)
+ return;
int count = errorcount + warningcount;
@@ -18789,67 +18831,45 @@ lazy_load_specializations (tree tmpl)
bool ok = !recursive_lazy ();
if (ok)
{
- unsigned ident = import_entity_index (tmpl);
- if (pendset *set = pending_table->get (ident, true))
+ function_depth++; /* Prevent GC */
+ unsigned n = dump.push (NULL);
+ dump () && dump ("Reading %u pending entities keyed to %P",
+ pending_vec->length (), key.ns, key.id);
+ for (unsigned ix = pending_vec->length (); ix--;)
{
- function_depth++; /* Prevent GC */
- unsigned n = dump.push (NULL);
- dump ()
- && dump ("Reading %u pending specializations keyed to %M[%u] %N",
- set->num, import_entity_module (ident),
- ident - import_entity_module (ident)->entity_lwm, tmpl);
- if (!pendset_lazy_load (set, true))
- ok = false;
- dump.pop (n);
+ unsigned index = (*pending_vec)[ix];
+ binding_slot *slot = &(*entity_ary)[index];
- function_depth--;
+ if (slot->is_lazy ())
+ {
+ module_state *import = import_entity_module (index);
+ if (!import->lazy_load (index - import->entity_lwm, slot))
+ ok = false;
+ }
+ else if (dump ())
+ {
+ module_state *import = import_entity_module (index);
+ dump () && dump ("Entity %M[%u] already loaded",
+ import, index - import->entity_lwm);
+ }
}
+
+ pending_table->remove (key);
+ dump.pop (n);
lazy_snum = 0;
+ post_load_processing ();
+ function_depth--;
}
timevar_stop (TV_MODULE_IMPORT);
if (!ok)
- fatal_error (input_location, "failed to load specializations keyed to %qD",
- tmpl);
+ fatal_error (input_location, "failed to load pendings for %<%E%s%E%>",
+ key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
if (count != errorcount + warningcount)
- inform (input_location,
- "during load of specializations keyed to %qD", tmpl);
-}
-
-void
-lazy_load_members (tree decl)
-{
- gcc_checking_assert (DECL_MODULE_PENDING_MEMBERS_P (decl));
- if (!DECL_MODULE_ENTITY_P (decl))
- {
- // FIXME: I can't help feeling that DECL_TEMPLATE_RESULT should
- // be inserted into the entity map, or perhaps have the same
- // DECL_UID as the template, so I don't have to do this dance
- // here and elsewhere. It also simplifies when DECL is a
- // partial specialization. (also noted elsewhere as an issue)
- tree ti = CLASSTYPE_TEMPLATE_INFO (TREE_TYPE (decl));
- tree tmpl = TI_TEMPLATE (ti);
- gcc_checking_assert (DECL_TEMPLATE_RESULT (tmpl) == decl);
- decl = tmpl;
- }
-
- timevar_start (TV_MODULE_IMPORT);
- unsigned ident = import_entity_index (decl);
- if (pendset *set = pending_table->get (~ident, true))
- {
- function_depth++; /* Prevent GC */
- unsigned n = dump.push (NULL);
- dump () && dump ("Reading %u pending members keyed to %M[%u] %N",
- set->num, import_entity_module (ident),
- ident - import_entity_module (ident)->entity_lwm, decl);
- pendset_lazy_load (set, false);
- dump.pop (n);
-
- function_depth--;
- }
- timevar_stop (TV_MODULE_IMPORT);
+ inform (input_location, "during load of pendings for %<%E%s%E%>",
+ key.ns, &"::"[key.ns == global_namespace ? 2 : 0], key.id);
}
static void
@@ -18866,7 +18886,7 @@ direct_import (module_state *import, cpp_reader *reader)
if (import->loadedness < ML_LANGUAGE)
{
if (!attached_table)
- attached_table = new attachset::hash (EXPERIMENT (1, 400));
+ attached_table = new attached_map_t (EXPERIMENT (1, 400));
import->read_language (true);
}
@@ -18920,7 +18940,7 @@ declare_module (module_state *module, location_t from_loc, bool exporting_p,
gcc_assert (global_namespace == current_scope ());
module_state *current = (*modules)[0];
- if (module_purview_p () || module->loadedness != ML_NONE)
+ if (module_purview_p () || module->loadedness > ML_CONFIG)
{
error_at (from_loc, module_purview_p ()
? G_("module already declared")
@@ -19075,7 +19095,7 @@ canonicalize_header_name (cpp_reader *reader, location_t loc, bool unquoted,
buf[len] = 0;
if (const char *hdr
- = cpp_find_header_unit (reader, buf, str[-1] == '<', loc))
+ = cpp_probe_header_unit (reader, buf, str[-1] == '<', loc))
{
len = strlen (hdr);
str = hdr;
@@ -19169,19 +19189,11 @@ maybe_translate_include (cpp_reader *reader, line_maps *lmaps, location_t loc,
else if (note_include_translate_no && xlate == 0)
note = true;
else if (note_includes)
- {
- /* We do not expect the note_includes vector to be large, so O(N)
- iteration. */
- for (unsigned ix = note_includes->length (); !note && ix--;)
- {
- const char *hdr = (*note_includes)[ix];
- size_t hdr_len = strlen (hdr);
- if ((hdr_len == len
- || (hdr_len < len && IS_DIR_SEPARATOR (path[len - hdr_len - 1])))
- && !memcmp (hdr, path + len - hdr_len, hdr_len))
- note = true;
- }
- }
+ /* We do not expect the note_includes vector to be large, so O(N)
+ iteration. */
+ for (unsigned ix = note_includes->length (); !note && ix--;)
+ if (!strcmp ((*note_includes)[ix], path))
+ note = true;
if (note)
inform (loc, xlate
@@ -19259,6 +19271,68 @@ module_begin_main_file (cpp_reader *reader, line_maps *lmaps,
}
}
+/* Process the pending_import queue, making sure we know the
+ filenames. */
+
+static void
+name_pending_imports (cpp_reader *reader, bool at_end)
+{
+ auto *mapper = get_mapper (cpp_main_loc (reader));
+
+ bool only_headers = (flag_preprocess_only
+ && !bool (mapper->get_flags () & Cody::Flags::NameOnly)
+ && !cpp_get_deps (reader));
+ if (at_end
+ && (!vec_safe_length (pending_imports) || only_headers))
+ /* Not doing anything. */
+ return;
+
+ timevar_start (TV_MODULE_MAPPER);
+
+ auto n = dump.push (NULL);
+ dump () && dump ("Resolving direct import names");
+
+ mapper->Cork ();
+ for (unsigned ix = 0; ix != pending_imports->length (); ix++)
+ {
+ module_state *module = (*pending_imports)[ix];
+ gcc_checking_assert (module->is_direct ());
+ if (!module->filename
+ && !module->visited_p
+ && (module->is_header () || !only_headers))
+ {
+ module->visited_p = true;
+ Cody::Flags flags = (flag_preprocess_only
+ ? Cody::Flags::None : Cody::Flags::NameOnly);
+
+ if (module->module_p
+ && (module->is_partition () || module->exported_p))
+ mapper->ModuleExport (module->get_flatname (), flags);
+ else
+ mapper->ModuleImport (module->get_flatname (), flags);
+ }
+ }
+
+ auto response = mapper->Uncork ();
+ auto r_iter = response.begin ();
+ for (unsigned ix = 0; ix != pending_imports->length (); ix++)
+ {
+ module_state *module = (*pending_imports)[ix];
+ if (module->visited_p)
+ {
+ module->visited_p = false;
+ gcc_checking_assert (!module->filename);
+
+ module->set_filename (*r_iter);
+ ++r_iter;
+ }
+ }
+
+ dump.pop (n);
+
+ timevar_stop (TV_MODULE_MAPPER);
+}
+
/* We've just lexed a module-specific control line for MODULE. Mark
the module as a direct import, and possibly load up its macro
state. Returns the primary module, if this is a module
@@ -19306,21 +19380,28 @@ preprocess_module (module_state *module, location_t from_loc,
}
}
+ auto desired = ML_CONFIG;
if (is_import
- && !module->is_module () && module->is_header ()
- && module->loadedness < ML_PREPROCESSOR
+ && module->is_header ()
&& (!cpp_get_options (reader)->preprocessed
|| cpp_get_options (reader)->directives_only))
+ /* We need preprocessor state now. */
+ desired = ML_PREPROCESSOR;
+
+ if (!is_import || module->loadedness < desired)
{
- timevar_start (TV_MODULE_IMPORT);
- unsigned n = dump.push (module);
+ vec_safe_push (pending_imports, module);
- if (module->loadedness == ML_NONE)
+ if (desired == ML_PREPROCESSOR)
{
- unsigned pre_hwm = 0;
+ unsigned n = dump.push (NULL);
+
+ dump () && dump ("Reading %M preprocessor state", module);
+ name_pending_imports (reader, false);
/* Preserve the state of the line-map. */
- pre_hwm = LINEMAPS_ORDINARY_USED (line_table);
+ unsigned pre_hwm = LINEMAPS_ORDINARY_USED (line_table);
+
/* We only need to close the span, if we're going to emit a
CMI. But that's a little tricky -- our token scanner
needs to be smarter -- and this isn't much state.
@@ -19329,25 +19410,38 @@ preprocess_module (module_state *module, location_t from_loc,
spans.maybe_init ();
spans.close ();
- if (!module->filename)
+ timevar_start (TV_MODULE_IMPORT);
+
+ /* Load the config of each pending import -- we must assign
+ module numbers monotonically. */
+ for (unsigned ix = 0; ix != pending_imports->length (); ix++)
{
- auto *mapper = get_mapper (cpp_main_loc (reader));
- auto packet = mapper->ModuleImport (module->get_flatname ());
- module->set_filename (packet);
+ auto *import = (*pending_imports)[ix];
+ if (!(import->module_p
+ && (import->is_partition () || import->exported_p))
+ && import->loadedness == ML_NONE
+ && (import->is_header () || !flag_preprocess_only))
+ {
+ unsigned n = dump.push (import);
+ import->do_import (reader, true);
+ dump.pop (n);
+ }
}
- module->do_import (reader, true);
+ vec_free (pending_imports);
/* Restore the line-map state. */
- linemap_module_restore (line_table, pre_hwm);
- spans.open ();
- }
+ spans.open (linemap_module_restore (line_table, pre_hwm));
- if (module->loadedness < ML_PREPROCESSOR)
- if (module->read_preprocessor (true))
- module->import_macros ();
+ /* Now read the preprocessor state of this particular
+ import. */
+ if (module->loadedness == ML_CONFIG
+ && module->read_preprocessor (true))
+ module->import_macros ();
- dump.pop (n);
- timevar_stop (TV_MODULE_IMPORT);
+ timevar_stop (TV_MODULE_IMPORT);
+
+ dump.pop (n);
+ }
}
return is_import ? NULL : get_primary (module);
@@ -19361,68 +19455,17 @@ preprocess_module (module_state *module, location_t from_loc,
void
preprocessed_module (cpp_reader *reader)
{
- auto *mapper = get_mapper (cpp_main_loc (reader));
-
- spans.maybe_init ();
- spans.close ();
-
- /* Stupid GTY doesn't grok a typedef here. And using type = is, too
- modern. */
-#define iterator hash_table<module_state_hash>::iterator
- /* using iterator = hash_table<module_state_hash>::iterator; */
-
- /* Walk the module hash, asking for the names of all unknown
- direct imports and informing of an export (if that's what we
- are). Notice these are emitted even when preprocessing as they
- inform the server of dependency edges. */
- timevar_start (TV_MODULE_MAPPER);
-
- dump.push (NULL);
- dump () && dump ("Resolving direct import names");
-
- if (!flag_preprocess_only
- || bool (mapper->get_flags () & Cody::Flags::NameOnly)
- || cpp_get_deps (reader))
- {
- mapper->Cork ();
- iterator end = modules_hash->end ();
- for (iterator iter = modules_hash->begin (); iter != end; ++iter)
- {
- module_state *module = *iter;
- if (module->is_direct () && !module->filename)
- {
- Cody::Flags flags
- = (flag_preprocess_only ? Cody::Flags::None
- : Cody::Flags::NameOnly);
-
- if (module->module_p
- && (module->is_partition () || module->exported_p))
- mapper->ModuleExport (module->get_flatname (), flags);
- else
- mapper->ModuleImport (module->get_flatname (), flags);
- }
- }
-
- auto response = mapper->Uncork ();
- auto r_iter = response.begin ();
- for (iterator iter = modules_hash->begin (); iter != end; ++iter)
- {
- module_state *module = *iter;
-
- if (module->is_direct () && !module->filename)
- {
- Cody::Packet const &p = *r_iter;
- ++r_iter;
+ unsigned n = dump.push (NULL);
- module->set_filename (p);
- }
- }
- }
+ dump () && dump ("Completed phase-4 (tokenization) processing");
- dump.pop (0);
+ name_pending_imports (reader, true);
+ vec_free (pending_imports);
- timevar_stop (TV_MODULE_MAPPER);
+ spans.maybe_init ();
+ spans.close ();
+ using iterator = hash_table<module_state_hash>::iterator;
if (mkdeps *deps = cpp_get_deps (reader))
{
/* Walk the module hash, informing the dependency machinery. */
@@ -19446,6 +19489,8 @@ preprocessed_module (cpp_reader *reader)
if (flag_header_unit && !flag_preprocess_only)
{
+ /* Find the main module -- remember, it's not yet in the module
+ array. */
iterator end = modules_hash->end ();
for (iterator iter = modules_hash->begin (); iter != end; ++iter)
{
@@ -19457,7 +19502,8 @@ preprocessed_module (cpp_reader *reader)
}
}
}
-#undef iterator
+
+ dump.pop (n);
}
/* VAL is a global tree, add it to the global vec if it is
@@ -19537,6 +19583,7 @@ init_modules (cpp_reader *reader)
headers = BITMAP_GGC_ALLOC ();
if (note_includes)
+ /* Canonicalize header names. */
for (unsigned ix = 0; ix != note_includes->length (); ix++)
{
const char *hdr = (*note_includes)[ix];
@@ -19554,11 +19601,42 @@ init_modules (cpp_reader *reader)
0, !delimed, hdr, len);
char *path = XNEWVEC (char, len + 1);
memcpy (path, hdr, len);
- path[len+1] = 0;
+ path[len] = 0;
(*note_includes)[ix] = path;
}
+ if (note_cmis)
+ /* Canonicalize & mark module names. */
+ for (unsigned ix = 0; ix != note_cmis->length (); ix++)
+ {
+ const char *name = (*note_cmis)[ix];
+ size_t len = strlen (name);
+
+ bool is_system = name[0] == '<';
+ bool is_user = name[0] == '"';
+ bool is_pathname = false;
+ if (!(is_system || is_user))
+ for (unsigned ix = len; !is_pathname && ix--;)
+ is_pathname = IS_DIR_SEPARATOR (name[ix]);
+ if (is_system || is_user || is_pathname)
+ {
+ if (len <= (is_pathname ? 0 : 2)
+ || (!is_pathname && name[len-1] != (is_system ? '>' : '"')))
+ {
+ error ("invalid header name %qs", name);
+ continue;
+ }
+ else
+ name = canonicalize_header_name (is_pathname ? nullptr : reader,
+ 0, is_pathname, name, len);
+ }
+ if (auto module = get_module (name))
+ module->inform_cmi_p = 1;
+ else
+ error ("invalid module name %qs", name);
+ }
+
dump.push (NULL);
/* Determine lazy handle bound. */
@@ -19659,8 +19737,7 @@ init_modules (cpp_reader *reader)
if (!flag_preprocess_only)
{
- pending_table = new pendset::hash (EXPERIMENT (1, 400));
-
+ pending_table = new pending_map_t (EXPERIMENT (1, 400));
entity_map = new entity_map_t (EXPERIMENT (1, 400));
vec_safe_reserve (entity_ary, EXPERIMENT (1, 400));
}
@@ -19762,6 +19839,8 @@ finish_module_processing (cpp_reader *reader)
break;
create_dirs (tmp_name);
}
+ if (note_module_cmi_yes || state->inform_cmi_p)
+ inform (state->loc, "writing CMI %qs", path);
dump () && dump ("CMI is %s", path);
}
@@ -19774,7 +19853,7 @@ finish_module_processing (cpp_reader *reader)
if (to.begin ())
{
auto loc = input_location;
- /* So crashes finger point the module decl. */
+ /* So crashes finger-point the module decl. */
input_location = state->loc;
state->write (&to, reader);
input_location = loc;
@@ -19944,6 +20023,10 @@ handle_module_option (unsigned code, const char *str, int)
vec_safe_push (note_includes, str);
return true;
+ case OPT_flang_info_module_cmi_:
+ vec_safe_push (note_cmis, str);
+ return true;
+
default:
return false;
}
diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 1cd4f67..d8839e2 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -367,6 +367,11 @@ append_imported_binding_slot (tree *slot, tree name, unsigned ix)
last->indices[off].base = ix;
last->indices[off].span = 1;
last->slots[off] = NULL_TREE;
+ /* Check monotonicity. */
+ gcc_checking_assert (last[off ? 0 : -1]
+ .indices[off ? off - 1
+ : BINDING_VECTOR_SLOTS_PER_CLUSTER - 1]
+ .base < ix);
return &last->slots[off];
}
@@ -382,7 +387,8 @@ add_decl_to_level (cp_binding_level *b, tree decl)
/* Make sure we don't create a circular list. xref_tag can end
up pushing the same artificial decl more than once. We
- should have already detected that in update_binding. */
+ should have already detected that in update_binding. (This isn't a
+ complete verification of non-circularity.) */
gcc_assert (b->names != decl);
/* We build up the list in reverse order, and reverse it later if
@@ -1910,10 +1916,10 @@ get_class_binding_direct (tree klass, tree name, bool want_type)
static void
maybe_lazily_declare (tree klass, tree name)
{
- tree main_decl = TYPE_NAME (TYPE_MAIN_VARIANT (klass));
- if (DECL_LANG_SPECIFIC (main_decl)
- && DECL_MODULE_PENDING_MEMBERS_P (main_decl))
- lazy_load_members (main_decl);
+ /* See big comment anout module_state::write_pendings regarding adding a check
+ bit. */
+ if (modules_p ())
+ lazy_load_pendings (TYPE_NAME (klass));
/* Lazily declare functions, if we're going to search these. */
if (IDENTIFIER_CTOR_P (name))
@@ -3303,7 +3309,7 @@ check_local_shadow (tree decl)
/* Don't warn for artificial things that are not implicit typedefs. */
if (DECL_ARTIFICIAL (decl) && !DECL_IMPLICIT_TYPEDEF_P (decl))
return;
-
+
if (nonlambda_method_basetype ())
if (tree member = lookup_member (current_nonlambda_class_type (),
DECL_NAME (decl), /*protect=*/0,
@@ -3315,8 +3321,9 @@ check_local_shadow (tree decl)
is a function or a pointer-to-function. */
if (!OVL_P (member)
|| TREE_CODE (decl) == FUNCTION_DECL
- || TYPE_PTRFN_P (TREE_TYPE (decl))
- || TYPE_PTRMEMFUNC_P (TREE_TYPE (decl)))
+ || (TREE_TYPE (decl)
+ && (TYPE_PTRFN_P (TREE_TYPE (decl))
+ || TYPE_PTRMEMFUNC_P (TREE_TYPE (decl)))))
{
auto_diagnostic_group d;
if (warning_at (DECL_SOURCE_LOCATION (decl), OPT_Wshadow,
@@ -3484,53 +3491,6 @@ push_local_extern_decl_alias (tree decl)
DECL_LOCAL_DECL_ALIAS (decl) = alias;
}
-/* NS needs to be exported, mark it and all its parents as exported. */
-
-static void
-implicitly_export_namespace (tree ns)
-{
- while (!DECL_MODULE_EXPORT_P (ns))
- {
- DECL_MODULE_EXPORT_P (ns) = true;
- ns = CP_DECL_CONTEXT (ns);
- }
-}
-
-/* DECL has just been bound at LEVEL. finish up the bookkeeping. */
-
-static void
-newbinding_bookkeeping (tree name, tree decl, cp_binding_level *level)
-{
- if (TREE_CODE (decl) == TYPE_DECL)
- {
- tree type = TREE_TYPE (decl);
-
- if (type != error_mark_node)
- {
- if (TYPE_NAME (type) != decl)
- set_underlying_type (decl);
-
- set_identifier_type_value_with_scope (name, decl, level);
-
- if (level->kind != sk_namespace
- && !instantiating_current_function_p ())
- /* This is a locally defined typedef in a function that
- is not a template instantation, record it to implement
- -Wunused-local-typedefs. */
- record_locally_defined_typedef (decl);
- }
- }
- else
- {
- if (VAR_P (decl) && !DECL_LOCAL_DECL_P (decl))
- maybe_register_incomplete_var (decl);
-
- if (VAR_OR_FUNCTION_DECL_P (decl)
- && DECL_EXTERN_C_P (decl))
- check_extern_c_conflict (decl);
- }
-}
-
/* DECL is a global or module-purview entity. If it has non-internal
linkage, and we have a module vector, record it in the appropriate
slot. We have already checked for duplicates. */
@@ -3559,7 +3519,7 @@ maybe_record_mergeable_decl (tree *slot, tree name, tree decl)
if (!partition)
{
binding_slot &orig
- = BINDING_VECTOR_CLUSTER (*gslot, 0).slots[BINDING_SLOT_CURRENT];
+ = BINDING_VECTOR_CLUSTER (*slot, 0).slots[BINDING_SLOT_CURRENT];
if (!STAT_HACK_P (tree (orig)))
orig = stat_hack (tree (orig));
@@ -3620,14 +3580,7 @@ check_module_override (tree decl, tree mvec, bool hiding,
if (iter.using_p ())
;
else if (tree match = duplicate_decls (decl, *iter, hiding))
- {
- if (TREE_CODE (match) == TYPE_DECL)
- /* The IDENTIFIER will have the type referring to the
- now-smashed TYPE_DECL, because ...? Reset it. */
- SET_IDENTIFIER_TYPE_VALUE (name, TREE_TYPE (match));
-
- return match;
- }
+ return match;
}
if (TREE_PUBLIC (scope) && TREE_PUBLIC (decl) && !not_module_p ()
@@ -3649,11 +3602,7 @@ check_module_override (tree decl, tree mvec, bool hiding,
tree match = *iter;
if (duplicate_decls (decl, match, hiding))
- {
- if (TREE_CODE (match) == TYPE_DECL)
- SET_IDENTIFIER_TYPE_VALUE (name, TREE_TYPE (match));
- return match;
- }
+ return match;
}
}
@@ -3736,9 +3685,9 @@ do_pushdecl (tree decl, bool hiding)
if (match == error_mark_node)
;
else if (TREE_CODE (match) == TYPE_DECL)
- /* The IDENTIFIER will have the type referring to the
- now-smashed TYPE_DECL, because ...? Reset it. */
- SET_IDENTIFIER_TYPE_VALUE (name, TREE_TYPE (match));
+ gcc_checking_assert (REAL_IDENTIFIER_TYPE_VALUE (name)
+ == (level->kind == sk_namespace
+ ? NULL_TREE : TREE_TYPE (match)));
else if (iter.hidden_p () && !hiding)
{
/* Unhiding a previously hidden decl. */
@@ -3781,10 +3730,6 @@ do_pushdecl (tree decl, bool hiding)
decl = update_binding (NULL, binding, mslot, old,
match, hiding);
- if (match == decl && DECL_MODULE_EXPORT_P (decl)
- && !DECL_MODULE_EXPORT_P (level->this_entity))
- implicitly_export_namespace (level->this_entity);
-
return decl;
}
@@ -3845,24 +3790,43 @@ do_pushdecl (tree decl, bool hiding)
decl = old;
else
{
- newbinding_bookkeeping (name, decl, level);
+ if (TREE_CODE (decl) == TYPE_DECL)
+ {
+ tree type = TREE_TYPE (decl);
- if (VAR_OR_FUNCTION_DECL_P (decl)
- && DECL_LOCAL_DECL_P (decl)
- && TREE_CODE (CP_DECL_CONTEXT (decl)) == NAMESPACE_DECL)
- push_local_extern_decl_alias (decl);
+ if (type != error_mark_node)
+ {
+ if (TYPE_NAME (type) != decl)
+ set_underlying_type (decl);
- if (level->kind == sk_namespace
- && TREE_PUBLIC (level->this_entity))
+ set_identifier_type_value_with_scope (name, decl, level);
+
+ if (level->kind != sk_namespace
+ && !instantiating_current_function_p ())
+ /* This is a locally defined typedef in a function that
+ is not a template instantation, record it to implement
+ -Wunused-local-typedefs. */
+ record_locally_defined_typedef (decl);
+ }
+ }
+ else if (VAR_OR_FUNCTION_DECL_P (decl))
{
- if (TREE_CODE (decl) != CONST_DECL
- && DECL_MODULE_EXPORT_P (decl)
- && !DECL_MODULE_EXPORT_P (level->this_entity))
- implicitly_export_namespace (level->this_entity);
+ if (DECL_EXTERN_C_P (decl))
+ check_extern_c_conflict (decl);
+
+ if (!DECL_LOCAL_DECL_P (decl)
+ && VAR_P (decl))
+ maybe_register_incomplete_var (decl);
- if (!not_module_p ())
- maybe_record_mergeable_decl (slot, name, decl);
+ if (DECL_LOCAL_DECL_P (decl)
+ && NAMESPACE_SCOPE_P (decl))
+ push_local_extern_decl_alias (decl);
}
+
+ if (level->kind == sk_namespace
+ && TREE_PUBLIC (level->this_entity)
+ && !not_module_p ())
+ maybe_record_mergeable_decl (slot, name, decl);
}
}
else
@@ -3950,7 +3914,8 @@ lookup_class_binding (tree klass, tree name)
/* Given a namespace-level binding BINDING, walk it, calling CALLBACK
for all decls of the current module. When partitions are involved,
- decls might be mentioned more than once. */
+ decls might be mentioned more than once. Return the accumulation of
+ CALLBACK results. */
unsigned
walk_module_binding (tree binding, bitmap partitions,
@@ -4137,62 +4102,25 @@ set_module_binding (tree ns, tree name, unsigned mod, int mod_glob,
}
void
-note_pending_specializations (tree ns, tree name, bool is_header)
-{
- if (tree *slot = find_namespace_slot (ns, name, false))
- if (TREE_CODE (*slot) == BINDING_VECTOR)
- {
- tree vec = *slot;
- BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = true;
- if (is_header)
- BINDING_VECTOR_PENDING_IS_HEADER_P (vec) = true;
- else
- BINDING_VECTOR_PENDING_IS_PARTITION_P (vec) = true;
- }
-}
-
-void
-load_pending_specializations (tree ns, tree name)
+add_module_namespace_decl (tree ns, tree decl)
{
- tree *slot = find_namespace_slot (ns, name, false);
-
- if (!slot || TREE_CODE (*slot) != BINDING_VECTOR
- || !BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (*slot))
- return;
-
- tree vec = *slot;
- BINDING_VECTOR_PENDING_SPECIALIZATIONS_P (vec) = false;
-
- bool do_header = BINDING_VECTOR_PENDING_IS_HEADER_P (vec);
- bool do_partition = BINDING_VECTOR_PENDING_IS_PARTITION_P (vec);
- BINDING_VECTOR_PENDING_IS_HEADER_P (vec) = false;
- BINDING_VECTOR_PENDING_IS_PARTITION_P (vec) = false;
+ gcc_assert (!DECL_CHAIN (decl));
+ gcc_checking_assert (!(VAR_OR_FUNCTION_DECL_P (decl)
+ && DECL_LOCAL_DECL_P (decl)));
+ if (CHECKING_P)
+ /* Expensive already-there? check. */
+ for (auto probe = NAMESPACE_LEVEL (ns)->names; probe;
+ probe = DECL_CHAIN (probe))
+ gcc_assert (decl != probe);
- gcc_checking_assert (do_header | do_partition);
- binding_cluster *cluster = BINDING_VECTOR_CLUSTER_BASE (vec);
- unsigned ix = BINDING_VECTOR_NUM_CLUSTERS (vec);
- if (BINDING_VECTOR_SLOTS_PER_CLUSTER == BINDING_SLOTS_FIXED)
- {
- ix--;
- cluster++;
- }
+ add_decl_to_level (NAMESPACE_LEVEL (ns), decl);
- for (; ix--; cluster++)
- for (unsigned jx = 0; jx != BINDING_VECTOR_SLOTS_PER_CLUSTER; jx++)
- if (cluster->indices[jx].span
- && cluster->slots[jx].is_lazy ()
- && lazy_specializations_p (cluster->indices[jx].base,
- do_header, do_partition))
- lazy_load_binding (cluster->indices[jx].base, ns, name,
- &cluster->slots[jx]);
-}
+ if (VAR_P (decl))
+ maybe_register_incomplete_var (decl);
-void
-add_module_decl (tree ns, tree name, tree decl)
-{
- gcc_assert (!DECL_CHAIN (decl));
- add_decl_to_level (NAMESPACE_LEVEL (ns), decl);
- newbinding_bookkeeping (name, decl, NAMESPACE_LEVEL (ns));
+ if (VAR_OR_FUNCTION_DECL_P (decl)
+ && DECL_EXTERN_C_P (decl))
+ check_extern_c_conflict (decl);
}
/* Enter DECL into the symbol table, if that's appropriate. Returns
@@ -4748,37 +4676,6 @@ print_binding_stack (void)
print_binding_level (NAMESPACE_LEVEL (global_namespace));
}
-/* Return the type associated with ID. */
-
-static tree
-identifier_type_value_1 (tree id)
-{
- /* There is no type with that name, anywhere. */
- if (REAL_IDENTIFIER_TYPE_VALUE (id) == NULL_TREE)
- return NULL_TREE;
- /* This is not the type marker, but the real thing. */
- if (REAL_IDENTIFIER_TYPE_VALUE (id) != global_type_node)
- return REAL_IDENTIFIER_TYPE_VALUE (id);
- /* Have to search for it. It must be on the global level, now.
- Ask lookup_name not to return non-types. */
- id = lookup_name (id, LOOK_where::BLOCK_NAMESPACE, LOOK_want::TYPE);
- if (id)
- return TREE_TYPE (id);
- return NULL_TREE;
-}
-
-/* Wrapper for identifier_type_value_1. */
-
-tree
-identifier_type_value (tree id)
-{
- tree ret;
- timevar_start (TV_NAME_LOOKUP);
- ret = identifier_type_value_1 (id);
- timevar_stop (TV_NAME_LOOKUP);
- return ret;
-}
-
/* Push a definition of struct, union or enum tag named ID. into
binding_level B. DECL is a TYPE_DECL for the type. DECL has
already been pushed into its binding level. This is bookkeeping to
@@ -4787,26 +4684,22 @@ identifier_type_value (tree id)
static void
set_identifier_type_value_with_scope (tree id, tree decl, cp_binding_level *b)
{
- tree type;
-
- if (b->kind != sk_namespace)
- {
- /* Shadow the marker, not the real thing, so that the marker
- gets restored later. */
- tree old_type_value = REAL_IDENTIFIER_TYPE_VALUE (id);
- b->type_shadowed = tree_cons (id, old_type_value, b->type_shadowed);
- type = decl ? TREE_TYPE (decl) : NULL_TREE;
- TREE_TYPE (b->type_shadowed) = type;
- }
+ if (b->kind == sk_namespace)
+ /* At namespace scope we should not see an identifier type value. */
+ gcc_checking_assert (!REAL_IDENTIFIER_TYPE_VALUE (id)
+ /* We could be pushing a friend underneath a template
+ parm (ill-formed). */
+ || (TEMPLATE_PARM_P
+ (TYPE_NAME (REAL_IDENTIFIER_TYPE_VALUE (id)))));
else
{
- gcc_assert (decl);
-
- /* Store marker instead of real type. */
- type = global_type_node;
+ /* Push the current type value, so we can restore it later */
+ tree old = REAL_IDENTIFIER_TYPE_VALUE (id);
+ b->type_shadowed = tree_cons (id, old, b->type_shadowed);
+ tree type = decl ? TREE_TYPE (decl) : NULL_TREE;
+ TREE_TYPE (b->type_shadowed) = type;
+ SET_IDENTIFIER_TYPE_VALUE (id, type);
}
-
- SET_IDENTIFIER_TYPE_VALUE (id, type);
}
/* As set_identifier_type_value_with_scope, but using
@@ -6238,51 +6131,17 @@ do_namespace_alias (tree alias, tree name_space)
(*debug_hooks->early_global_decl) (alias);
}
-/* Like pushdecl, only it places X in the current namespace,
+/* Like pushdecl, only it places DECL in the current namespace,
if appropriate. */
tree
-pushdecl_namespace_level (tree x, bool hiding)
+pushdecl_namespace_level (tree decl, bool hiding)
{
- cp_binding_level *b = current_binding_level;
- tree t;
-
bool subtime = timevar_cond_start (TV_NAME_LOOKUP);
- t = do_pushdecl_with_scope (x, NAMESPACE_LEVEL (current_namespace), hiding);
-
- /* Now, the type_shadowed stack may screw us. Munge it so it does
- what we want. */
- if (TREE_CODE (t) == TYPE_DECL)
- {
- tree name = DECL_NAME (t);
- tree newval;
- tree *ptr = (tree *)0;
- for (; !global_scope_p (b); b = b->level_chain)
- {
- tree shadowed = b->type_shadowed;
- for (; shadowed; shadowed = TREE_CHAIN (shadowed))
- if (TREE_PURPOSE (shadowed) == name)
- {
- ptr = &TREE_VALUE (shadowed);
- /* Can't break out of the loop here because sometimes
- a binding level will have duplicate bindings for
- PT names. It's gross, but I haven't time to fix it. */
- }
- }
- newval = TREE_TYPE (t);
- if (ptr == (tree *)0)
- {
- /* @@ This shouldn't be needed. My test case "zstring.cc" trips
- up here if this is changed to an assertion. --KR */
- SET_IDENTIFIER_TYPE_VALUE (name, t);
- }
- else
- {
- *ptr = newval;
- }
- }
+ tree res = do_pushdecl_with_scope (decl, NAMESPACE_LEVEL (current_namespace),
+ hiding);
timevar_cond_stop (TV_NAME_LOOKUP, subtime);
- return t;
+ return res;
}
/* Wrapper around push_local_binding to push the bindings for
@@ -7104,6 +6963,8 @@ get_cxx_dialect_name (enum cxx_dialect dialect)
return "C++17";
case cxx20:
return "C++20";
+ case cxx23:
+ return "C++23";
}
}
@@ -8079,7 +7940,7 @@ lookup_elaborated_type_1 (tree name, TAG_how how)
if (*slot == bind)
*slot = decl;
else
- BINDING_VECTOR_CLUSTER (bind, 0)
+ BINDING_VECTOR_CLUSTER (*slot, 0)
.slots[BINDING_SLOT_CURRENT] = decl;
}
}
@@ -8257,6 +8118,8 @@ do_pushtag (tree name, tree type, TAG_how how)
{
tree decl;
+ gcc_assert (identifier_p (name));
+
cp_binding_level *b = current_binding_level;
while (true)
{
@@ -8282,10 +8145,8 @@ do_pushtag (tree name, tree type, TAG_how how)
break;
}
- gcc_assert (identifier_p (name));
-
/* Do C++ gratuitous typedefing. */
- if (identifier_type_value_1 (name) != type)
+ if (REAL_IDENTIFIER_TYPE_VALUE (name) != type)
{
tree tdef;
tree context = TYPE_CONTEXT (type);
@@ -8327,10 +8188,8 @@ do_pushtag (tree name, tree type, TAG_how how)
if (decl == error_mark_node)
return decl;
- bool in_class = false;
if (b->kind == sk_class)
{
- in_class = true;
if (!TYPE_BEING_DEFINED (current_class_type))
/* Don't push anywhere if the class is complete; a lambda in an
NSDMI is not a member of the class. */
@@ -8345,7 +8204,12 @@ do_pushtag (tree name, tree type, TAG_how how)
pushdecl_class_level (decl);
}
else if (b->kind == sk_template_parms)
- in_class = b->level_chain->kind == sk_class;
+ {
+ /* Do not push the tag here -- we'll want to push the
+ TEMPLATE_DECL. */
+ if (b->level_chain->kind != sk_class)
+ set_identifier_type_value_with_scope (name, tdef, b->level_chain);
+ }
else
{
decl = do_pushdecl_with_scope
@@ -8363,9 +8227,6 @@ do_pushtag (tree name, tree type, TAG_how how)
}
}
- if (!in_class)
- set_identifier_type_value_with_scope (name, tdef, b);
-
TYPE_CONTEXT (type) = DECL_CONTEXT (decl);
/* If this is a local class, keep track of it. We need this
@@ -9001,9 +8862,10 @@ push_namespace (tree name, bool make_inline)
{
/* A public namespace is exported only if explicitly marked, or
it contains exported entities. */
- if (!DECL_MODULE_EXPORT_P (ns) && TREE_PUBLIC (ns)
- && module_exporting_p ())
- implicitly_export_namespace (ns);
+ if (TREE_PUBLIC (ns) && module_exporting_p ())
+ DECL_MODULE_EXPORT_P (ns) = true;
+ if (module_purview_p ())
+ DECL_MODULE_PURVIEW_P (ns) = true;
if (make_inline && !DECL_NAMESPACE_INLINE_P (ns))
{
@@ -9035,12 +8897,12 @@ pop_namespace (void)
timevar_cond_stop (TV_NAME_LOOKUP, subtime);
}
-/* An import is defining namespace NAME inside CTX. Find or create
- that namespace and add it to the container's binding-vector. */
+/* An IMPORT is an import that is defining namespace NAME inside CTX. Find or
+ create that namespace and add it to the container's binding-vector. */
tree
-add_imported_namespace (tree ctx, tree name, unsigned origin, location_t loc,
- bool visible_p, bool inline_p)
+add_imported_namespace (tree ctx, tree name, location_t loc, unsigned import,
+ bool inline_p, bool visible_p)
{
// FIXME: Something is not correct about the VISIBLE_P handling. We
// need to insert this namespace into
@@ -9049,9 +8911,10 @@ add_imported_namespace (tree ctx, tree name, unsigned origin, location_t loc,
// (c) Do we need to put it in the CURRENT slot? This is the
// confused piece.
- gcc_checking_assert (origin);
tree *slot = find_namespace_slot (ctx, name, true);
tree decl = reuse_namespace (slot, ctx, name);
+
+ /* Creating and binding. */
if (!decl)
{
decl = make_namespace (ctx, name, loc, inline_p);
@@ -9079,7 +8942,7 @@ add_imported_namespace (tree ctx, tree name, unsigned origin, location_t loc,
tree final = last->slots[jx];
if (visible_p == !STAT_HACK_P (final)
&& MAYBE_STAT_DECL (final) == decl
- && last->indices[jx].base + last->indices[jx].span == origin
+ && last->indices[jx].base + last->indices[jx].span == import
&& (BINDING_VECTOR_NUM_CLUSTERS (*slot) > 1
|| (BINDING_VECTOR_SLOTS_PER_CLUSTER > BINDING_SLOTS_FIXED
&& jx >= BINDING_SLOTS_FIXED)))
@@ -9090,7 +8953,7 @@ add_imported_namespace (tree ctx, tree name, unsigned origin, location_t loc,
}
/* Append a new slot. */
- tree *mslot = &(tree &)*append_imported_binding_slot (slot, name, origin);
+ tree *mslot = &(tree &)*append_imported_binding_slot (slot, name, import);
gcc_assert (!*mslot);
*mslot = visible_p ? decl : stat_hack (decl, NULL_TREE);
diff --git a/gcc/cp/name-lookup.h b/gcc/cp/name-lookup.h
index 75db5b3..67e923f 100644
--- a/gcc/cp/name-lookup.h
+++ b/gcc/cp/name-lookup.h
@@ -177,18 +177,6 @@ struct GTY(()) tree_binding_vec {
#define MODULE_BINDING_PARTITION_P(NODE) \
(OVERLOAD_CHECK (NODE)->base.volatile_flag)
-/* There are specializations of a template keyed to this binding. */
-#define BINDING_VECTOR_PENDING_SPECIALIZATIONS_P(NODE) \
- (BINDING_VECTOR_CHECK (NODE)->base.public_flag)
-/* The key is in a header unit (not a named module partition or
- primary). */
-#define BINDING_VECTOR_PENDING_IS_HEADER_P(NODE) \
- (BINDING_VECTOR_CHECK (NODE)->base.protected_flag)
-/* The key is in a named module (primary or partition). */
-#define BINDING_VECTOR_PENDING_IS_PARTITION_P(NODE) \
- (BINDING_VECTOR_CHECK (NODE)->base.private_flag)
-
-extern tree identifier_type_value (tree);
extern void set_identifier_type_value (tree, tree);
extern void push_binding (tree, tree, cp_binding_level*);
extern void pop_local_binding (tree, tree);
@@ -491,7 +479,7 @@ extern bool import_module_binding (tree ctx, tree name, unsigned mod,
extern bool set_module_binding (tree ctx, tree name, unsigned mod,
int mod_glob_flag,
tree value, tree type, tree visible);
-extern void add_module_decl (tree ctx, tree name, tree decl);
+extern void add_module_namespace_decl (tree ns, tree decl);
enum WMB_Flags
{
@@ -505,10 +493,9 @@ enum WMB_Flags
extern unsigned walk_module_binding (tree binding, bitmap partitions,
bool (*)(tree decl, WMB_Flags, void *data),
void *data);
-extern tree add_imported_namespace (tree ctx, tree name, unsigned module,
- location_t, bool visible_p, bool inline_p);
-extern void note_pending_specializations (tree ns, tree name, bool is_header);
-extern void load_pending_specializations (tree ns, tree name);
+extern tree add_imported_namespace (tree ctx, tree name, location_t,
+ unsigned module,
+ bool inline_p, bool visible_p);
extern const char *get_cxx_dialect_name (enum cxx_dialect dialect);
#endif /* GCC_CP_NAME_LOOKUP_H */
diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c
index 7077579..0a7d18a 100644
--- a/gcc/cp/parser.c
+++ b/gcc/cp/parser.c
@@ -3469,11 +3469,15 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
else if (TREE_CODE (id) == IDENTIFIER_NODE
&& (id_equal (id, "module") || id_equal (id, "import")))
{
- if (!modules_p ())
- inform (location, "%qE only available with %<-fmodules-ts%>", id);
- else
- inform (location, "%qE was not recognized as a module control-line",
+ if (modules_p ())
+ 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%>",
id);
+ else
+ inform (location, "C++20 %qE only available with %<-fmodules-ts%>"
+ ", which is not yet enabled with %<-std=c++20%>", id);
}
else if (cxx_dialect < cxx11
&& TREE_CODE (id) == IDENTIFIER_NODE
@@ -11223,12 +11227,12 @@ cp_parser_lambda_introducer (cp_parser* parser, tree lambda_expr)
/* Parse the (optional) middle of a lambda expression.
lambda-declarator:
- ( parameter-declaration-clause )
- decl-specifier-seq [opt]
- noexcept-specifier [opt]
- attribute-specifier-seq [opt]
- trailing-return-type [opt]
- requires-clause [opt]
+ ( parameter-declaration-clause ) lambda-specifiers requires-clause [opt]
+ lambda-specifiers (C++23)
+
+ lambda-specifiers:
+ decl-specifier-seq [opt] noexcept-specifier [opt]
+ attribute-specifier-seq [opt] trailing-return-type [opt]
LAMBDA_EXPR is the current representation of the lambda expression. */
@@ -11248,6 +11252,8 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
tree tx_qual = NULL_TREE;
tree return_type = NULL_TREE;
tree trailing_requires_clause = NULL_TREE;
+ bool has_param_list = false;
+ location_t omitted_parms_loc = UNKNOWN_LOCATION;
cp_decl_specifier_seq lambda_specs;
clear_decl_specs (&lambda_specs);
/* A lambda op() is const unless explicitly 'mutable'. */
@@ -11334,42 +11340,87 @@ cp_parser_lambda_declarator_opt (cp_parser* parser, tree lambda_expr)
"default argument specified for lambda parameter");
parens.require_close (parser);
+ has_param_list = true;
+ }
+ else if (cxx_dialect < cxx23)
+ omitted_parms_loc = cp_lexer_peek_token (parser->lexer)->location;
- /* In the decl-specifier-seq of the lambda-declarator, each
- decl-specifier shall either be mutable or constexpr. */
- int declares_class_or_enum;
- if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer)
- && !cp_next_tokens_can_be_gnu_attribute_p (parser))
- cp_parser_decl_specifier_seq (parser,
- CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR,
- &lambda_specs, &declares_class_or_enum);
- if (lambda_specs.storage_class == sc_mutable)
- {
- LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
- quals = TYPE_UNQUALIFIED;
- if (lambda_specs.conflicting_specifiers_p)
- error_at (lambda_specs.locations[ds_storage_class],
- "duplicate %<mutable%>");
- }
+ /* In the decl-specifier-seq of the lambda-declarator, each
+ decl-specifier shall either be mutable or constexpr. */
+ int declares_class_or_enum;
+ if (cp_lexer_next_token_is_decl_specifier_keyword (parser->lexer)
+ && !cp_next_tokens_can_be_gnu_attribute_p (parser))
+ cp_parser_decl_specifier_seq (parser,
+ CP_PARSER_FLAGS_ONLY_MUTABLE_OR_CONSTEXPR,
+ &lambda_specs, &declares_class_or_enum);
- tx_qual = cp_parser_tx_qualifier_opt (parser);
+ if (omitted_parms_loc && lambda_specs.any_specifiers_p)
+ {
+ pedwarn (omitted_parms_loc, 0,
+ "parameter declaration before lambda declaration "
+ "specifiers only optional with %<-std=c++2b%> or "
+ "%<-std=gnu++2b%>");
+ omitted_parms_loc = UNKNOWN_LOCATION;
+ }
- /* Parse optional exception specification. */
- exception_spec
- = cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
+ if (lambda_specs.storage_class == sc_mutable)
+ {
+ LAMBDA_EXPR_MUTABLE_P (lambda_expr) = 1;
+ quals = TYPE_UNQUALIFIED;
+ if (lambda_specs.conflicting_specifiers_p)
+ error_at (lambda_specs.locations[ds_storage_class],
+ "duplicate %<mutable%>");
+ }
- std_attrs = cp_parser_std_attribute_spec_seq (parser);
+ tx_qual = cp_parser_tx_qualifier_opt (parser);
+ if (omitted_parms_loc && tx_qual)
+ {
+ pedwarn (omitted_parms_loc, 0,
+ "parameter declaration before lambda transaction "
+ "qualifier only optional with %<-std=c++2b%> or "
+ "%<-std=gnu++2b%>");
+ omitted_parms_loc = UNKNOWN_LOCATION;
+ }
- /* Parse optional trailing return type. */
- if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF))
- {
- cp_lexer_consume_token (parser->lexer);
- return_type = cp_parser_trailing_type_id (parser);
- }
+ /* Parse optional exception specification. */
+ exception_spec
+ = cp_parser_exception_specification_opt (parser, CP_PARSER_FLAGS_NONE);
+
+ if (omitted_parms_loc && exception_spec)
+ {
+ pedwarn (omitted_parms_loc, 0,
+ "parameter declaration before lambda exception "
+ "specification only optional with %<-std=c++2b%> or "
+ "%<-std=gnu++2b%>");
+ omitted_parms_loc = UNKNOWN_LOCATION;
+ }
+
+ /* GCC 8 accepted attributes here, and this is the place for standard C++11
+ attributes that appertain to the function type. */
+ if (cp_next_tokens_can_be_gnu_attribute_p (parser))
+ gnu_attrs = cp_parser_gnu_attributes_opt (parser);
+ else
+ std_attrs = cp_parser_std_attribute_spec_seq (parser);
+
+ /* Parse optional trailing return type. */
+ if (cp_lexer_next_token_is (parser->lexer, CPP_DEREF))
+ {
+ if (omitted_parms_loc)
+ pedwarn (omitted_parms_loc, 0,
+ "parameter declaration before lambda trailing "
+ "return type only optional with %<-std=c++2b%> or "
+ "%<-std=gnu++2b%>");
+ cp_lexer_consume_token (parser->lexer);
+ return_type = cp_parser_trailing_type_id (parser);
+ }
- if (cp_next_tokens_can_be_gnu_attribute_p (parser))
- gnu_attrs = cp_parser_gnu_attributes_opt (parser);
+ /* Also allow GNU attributes at the very end of the declaration, the usual
+ place for GNU attributes. */
+ if (cp_next_tokens_can_be_gnu_attribute_p (parser))
+ gnu_attrs = chainon (gnu_attrs, cp_parser_gnu_attributes_opt (parser));
+ if (has_param_list)
+ {
/* Parse optional trailing requires clause. */
trailing_requires_clause = cp_parser_requires_clause_opt (parser, false);
@@ -13698,7 +13749,13 @@ cp_parser_module_declaration (cp_parser *parser, module_parse mp_state,
parser->lexer->in_pragma = true;
cp_token *token = cp_lexer_consume_token (parser->lexer);
- if (mp_state == MP_FIRST && !exporting
+ if (flag_header_unit)
+ {
+ error_at (token->location,
+ "module-declaration not permitted in header-unit");
+ goto skip_eol;
+ }
+ else if (mp_state == MP_FIRST && !exporting
&& cp_lexer_next_token_is (parser->lexer, CPP_SEMICOLON))
{
/* Start global module fragment. */
@@ -28770,6 +28827,18 @@ cp_parser_requirement_parameter_list (cp_parser *parser)
if (!parens.require_close (parser))
return error_mark_node;
+ /* Modify the declared parameters by removing their context
+ so they don't refer to the enclosing scope and explicitly
+ indicating that they are constraint variables. */
+ for (tree parm = parms; parm; parm = TREE_CHAIN (parm))
+ {
+ if (parm == void_list_node || parm == explicit_void_list_node)
+ break;
+ tree decl = TREE_VALUE (parm);
+ DECL_CONTEXT (decl) = NULL_TREE;
+ CONSTRAINT_VAR_P (decl) = true;
+ }
+
return parms;
}
@@ -37991,7 +38060,7 @@ cp_parser_oacc_clause_async (cp_parser *parser, tree list)
matching_parens parens;
parens.consume_open (parser);
- t = cp_parser_expression (parser);
+ t = cp_parser_assignment_expression (parser);
if (t == error_mark_node
|| !parens.require_close (parser))
cp_parser_skip_to_closing_parenthesis (parser, /*recovering=*/true,
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c
index 50cdb00..5e485f1 100644
--- a/gcc/cp/pt.c
+++ b/gcc/cp/pt.c
@@ -9796,13 +9796,7 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
gen_tmpl = most_general_template (templ);
if (modules_p ())
- {
- tree origin = get_originating_module_decl (gen_tmpl);
- load_pending_specializations (CP_DECL_CONTEXT (origin),
- DECL_NAME (origin));
- if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
- lazy_load_specializations (gen_tmpl);
- }
+ lazy_load_pendings (gen_tmpl);
parmlist = DECL_TEMPLATE_PARMS (gen_tmpl);
parm_depth = TMPL_PARMS_DEPTH (parmlist);
@@ -11822,6 +11816,8 @@ instantiate_class_template_1 (tree type)
input_location = DECL_SOURCE_LOCATION (TYPE_NAME (type)) =
DECL_SOURCE_LOCATION (typedecl);
+ set_instantiating_module (TYPE_NAME (type));
+
TYPE_PACKED (type) = TYPE_PACKED (pattern);
SET_TYPE_ALIGN (type, TYPE_ALIGN (pattern));
TYPE_USER_ALIGN (type) = TYPE_USER_ALIGN (pattern);
@@ -11971,14 +11967,13 @@ instantiate_class_template_1 (tree type)
instantiation, but the TYPE is not.) */
CLASSTYPE_USE_TEMPLATE (newtag) = 0;
- /* Now, we call pushtag to put this NEWTAG into the scope of
- TYPE. We first set up the IDENTIFIER_TYPE_VALUE to avoid
- pushtag calling push_template_decl. We don't have to do
- this for enums because it will already have been done in
- tsubst_enum. */
- if (name)
- SET_IDENTIFIER_TYPE_VALUE (name, newtag);
- pushtag (name, newtag);
+ /* Now, install the tag. We don't use pushtag
+ because that does too much work -- creating an
+ implicit typedef, which we've already done. */
+ set_identifier_type_value (name, TYPE_NAME (newtag));
+ maybe_add_class_template_decl_list (type, newtag, false);
+ TREE_PUBLIC (TYPE_NAME (newtag)) = true;
+ determine_visibility (TYPE_NAME (newtag));
}
}
else if (DECL_DECLARES_FUNCTION_P (t))
@@ -15424,10 +15419,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
code = TREE_CODE (t);
- if (code == IDENTIFIER_NODE)
- type = IDENTIFIER_TYPE_VALUE (t);
- else
- type = TREE_TYPE (t);
+ gcc_assert (code != IDENTIFIER_NODE);
+ type = TREE_TYPE (t);
gcc_assert (type != unknown_type_node);
@@ -15713,15 +15706,15 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
? tf_ignore_bad_quals : 0));
}
else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
- && PLACEHOLDER_TYPE_CONSTRAINTS (t)
+ && PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)
&& (r = (TEMPLATE_PARM_DESCENDANTS
(TEMPLATE_TYPE_PARM_INDEX (t))))
&& (r = TREE_TYPE (r))
- && !PLACEHOLDER_TYPE_CONSTRAINTS (r))
+ && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r))
/* Break infinite recursion when substituting the constraints
of a constrained placeholder. */;
else if (TREE_CODE (t) == TEMPLATE_TYPE_PARM
- && !PLACEHOLDER_TYPE_CONSTRAINTS (t)
+ && !PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t)
&& !CLASS_PLACEHOLDER_TEMPLATE (t)
&& (arg = TEMPLATE_TYPE_PARM_INDEX (t),
r = TEMPLATE_PARM_DESCENDANTS (arg))
@@ -15744,8 +15737,8 @@ tsubst (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
/* Propagate constraints on placeholders since they are
only instantiated during satisfaction. */
- if (tree constr = PLACEHOLDER_TYPE_CONSTRAINTS (t))
- PLACEHOLDER_TYPE_CONSTRAINTS (r) = constr;
+ if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (t))
+ PLACEHOLDER_TYPE_CONSTRAINTS_INFO (r) = ci;
else if (tree pl = CLASS_PLACEHOLDER_TEMPLATE (t))
{
pl = tsubst_copy (pl, args, complain, in_decl);
@@ -16829,18 +16822,27 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
{
/* Wrapper to make a C++20 template parameter object const. */
op = tsubst_copy (op, args, complain, in_decl);
- if (TREE_CODE (op) == TEMPLATE_PARM_INDEX)
+ if (!CP_TYPE_CONST_P (TREE_TYPE (op)))
{
+ /* The template argument is not const, presumably because
+ it is still dependent, and so not the const template parm
+ object. */
tree type = tsubst (TREE_TYPE (t), args, complain, in_decl);
- return build1 (code, type, op);
- }
- else
- {
- gcc_assert (CP_TYPE_CONST_P (TREE_TYPE (op))
- || (TREE_CODE (op) == IMPLICIT_CONV_EXPR
- && IMPLICIT_CONV_EXPR_NONTYPE_ARG (op)));
- return op;
+ gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
+ (type, TREE_TYPE (op)));
+ if (TREE_CODE (op) == CONSTRUCTOR
+ || TREE_CODE (op) == IMPLICIT_CONV_EXPR)
+ {
+ /* Don't add a wrapper to these. */
+ op = copy_node (op);
+ TREE_TYPE (op) = type;
+ }
+ else
+ /* Do add a wrapper otherwise (in particular, if op is
+ another TEMPLATE_PARM_INDEX). */
+ op = build1 (code, type, op);
}
+ return op;
}
/* force_paren_expr can also create a VIEW_CONVERT_EXPR. */
else if (code == VIEW_CONVERT_EXPR && REF_PARENTHESIZED_P (t))
@@ -17108,14 +17110,17 @@ tsubst_copy (tree t, tree args, tsubst_flags_t complain, tree in_decl)
case TEMPLATE_ID_EXPR:
{
/* Substituted template arguments */
- tree fn = TREE_OPERAND (t, 0);
+ tree tmpl = TREE_OPERAND (t, 0);
tree targs = TREE_OPERAND (t, 1);
- fn = tsubst_copy (fn, args, complain, in_decl);
+ tmpl = tsubst_copy (tmpl, args, complain, in_decl);
if (targs)
targs = tsubst_template_args (targs, args, complain, in_decl);
- return lookup_template_function (fn, targs);
+ if (variable_template_p (tmpl))
+ return lookup_template_variable (tmpl, targs);
+ else
+ return lookup_template_function (tmpl, targs);
}
case TREE_LIST:
@@ -19859,6 +19864,11 @@ tsubst_copy_and_build (tree t,
case SCOPE_REF:
RETURN (tsubst_qualified_id (t, args, complain, in_decl, /*done=*/true,
/*address_p=*/false));
+
+ case BASELINK:
+ RETURN (tsubst_baselink (t, current_nonlambda_class_type (),
+ args, complain, in_decl));
+
case ARRAY_REF:
op1 = tsubst_non_call_postfix_expression (TREE_OPERAND (t, 0),
args, complain, in_decl);
@@ -20990,6 +21000,9 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
gcc_assert (TREE_CODE (tmpl) == TEMPLATE_DECL);
+ if (modules_p ())
+ lazy_load_pendings (tmpl);
+
/* If this function is a clone, handle it specially. */
if (DECL_CLONED_FUNCTION_P (tmpl))
{
@@ -21026,15 +21039,6 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
(DECL_TI_ARGS (DECL_TEMPLATE_RESULT (tmpl)),
targ_ptr));
- if (modules_p ())
- {
- tree origin = get_originating_module_decl (gen_tmpl);
- load_pending_specializations (CP_DECL_CONTEXT (origin),
- DECL_NAME (origin));
- if (DECL_MODULE_PENDING_SPECIALIZATIONS_P (gen_tmpl))
- lazy_load_specializations (gen_tmpl);
- }
-
/* It would be nice to avoid hashing here and then again in tsubst_decl,
but it doesn't seem to be on the hot path. */
spec = retrieve_specialization (gen_tmpl, targ_ptr, 0);
@@ -23682,7 +23686,8 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
if (tree a = type_uses_auto (tparm))
{
- tparm = do_auto_deduction (tparm, arg, a, complain, adc_unify);
+ tparm = do_auto_deduction (tparm, arg, a,
+ complain, adc_unify, targs);
if (tparm == error_mark_node)
return 1;
}
@@ -25935,6 +25940,10 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
gcc_checking_assert (!DECL_FUNCTION_SCOPE_P (d));
+ if (modules_p ())
+ /* We may have a pending instantiation of D itself. */
+ lazy_load_pendings (d);
+
/* Variables are never deferred; if instantiation is required, they
are instantiated right away. That allows for better code in the
case that an expression refers to the value of the variable --
@@ -26149,6 +26158,7 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
}
else
{
+ set_instantiating_module (d);
if (variable_template_p (gen_tmpl))
note_variable_template_instantiation (d);
instantiate_body (td, args, d, false);
@@ -26774,10 +26784,6 @@ dependent_type_p (tree type)
if (type == error_mark_node)
return false;
- /* Getting here with global_type_node means we improperly called this
- function on the TREE_TYPE of an IDENTIFIER_NODE. */
- gcc_checking_assert (type != global_type_node);
-
/* If we have not already computed the appropriate value for TYPE,
do so now. */
if (!TYPE_DEPENDENT_P_VALID (type))
@@ -27526,7 +27532,8 @@ bool
instantiation_dependent_expression_p (tree expression)
{
return (instantiation_dependent_uneval_expression_p (expression)
- || (potential_constant_expression (expression)
+ || (processing_template_decl
+ && potential_constant_expression (expression)
&& value_dependent_expression_p (expression)));
}
@@ -28175,7 +28182,8 @@ make_constrained_placeholder_type (tree type, tree con, tree args)
expr = build_concept_check (expr, type, args, tf_warning_or_error);
--processing_template_decl;
- PLACEHOLDER_TYPE_CONSTRAINTS (type) = expr;
+ PLACEHOLDER_TYPE_CONSTRAINTS_INFO (type)
+ = build_tree_list (current_template_parms, expr);
/* Our canonical type depends on the constraint. */
TYPE_CANONICAL (type) = canonical_type_parameter (type);
@@ -28202,6 +28210,23 @@ make_constrained_decltype_auto (tree con, tree args)
return make_constrained_placeholder_type (type, con, args);
}
+/* Returns true if the placeholder type constraint T has any dependent
+ (explicit) template arguments. */
+
+static bool
+placeholder_type_constraint_dependent_p (tree t)
+{
+ tree id = unpack_concept_check (t);
+ tree args = TREE_OPERAND (id, 1);
+ tree first = TREE_VEC_ELT (args, 0);
+ gcc_checking_assert (TREE_CODE (first) == WILDCARD_DECL
+ || is_auto (first));
+ for (int i = 1; i < TREE_VEC_LENGTH (args); ++i)
+ if (dependent_template_arg_p (TREE_VEC_ELT (args, i)))
+ return true;
+ return false;
+}
+
/* Build and return a concept definition. Like other templates, the
CONCEPT_DECL node is wrapped by a TEMPLATE_DECL. This returns the
the TEMPLATE_DECL. */
@@ -28634,7 +28659,7 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com
tree ctmpl = CLASSTYPE_TI_TEMPLATE (type);
tparms = DECL_TEMPLATE_PARMS (ctmpl);
- targs = CLASSTYPE_TI_ARGS (type);
+ targs = INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (type));
ci = NULL_TREE;
fargs = NULL_TREE;
loc = DECL_SOURCE_LOCATION (ctmpl);
@@ -28834,6 +28859,7 @@ is_spec_or_derived (tree etype, tree tmpl)
if (!etype || !CLASS_TYPE_P (etype))
return false;
+ etype = cv_unqualified (etype);
tree type = TREE_TYPE (tmpl);
tree tparms = (INNERMOST_TEMPLATE_PARMS
(DECL_TEMPLATE_PARMS (tmpl)));
@@ -28856,15 +28882,29 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
if (init == NULL_TREE)
return NULL_TREE;
+ /* We might be creating a guide for a class member template, e.g.,
+
+ template<typename U> struct A {
+ template<typename T> struct B { T t; };
+ };
+
+ At this point, A will have been instantiated. Below, we need to
+ use both A<U>::B<T> (TEMPLATE_TYPE) and A<int>::B<T> (TYPE) types. */
+ const bool member_template_p
+ = (DECL_TEMPLATE_INFO (tmpl)
+ && DECL_MEMBER_TEMPLATE_P (DECL_TI_TEMPLATE (tmpl)));
tree type = TREE_TYPE (tmpl);
- if (!CP_AGGREGATE_TYPE_P (type))
+ tree template_type = (member_template_p
+ ? TREE_TYPE (DECL_TI_TEMPLATE (tmpl))
+ : type);
+ if (!CP_AGGREGATE_TYPE_P (template_type))
return NULL_TREE;
/* No aggregate candidate for copy-initialization. */
if (args->length() == 1)
{
tree val = (*args)[0];
- if (is_spec_or_derived (tmpl, TREE_TYPE (val)))
+ if (is_spec_or_derived (TREE_TYPE (val), tmpl))
return NULL_TREE;
}
@@ -28874,10 +28914,21 @@ maybe_aggr_guide (tree tmpl, tree init, vec<tree,va_gc> *args)
tree parms = NULL_TREE;
if (BRACE_ENCLOSED_INITIALIZER_P (init))
{
- init = reshape_init (type, init, complain);
+ init = reshape_init (template_type, init, complain);
if (init == error_mark_node)
return NULL_TREE;
parms = collect_ctor_idx_types (init, parms);
+ /* If we're creating a deduction guide for a member class template,
+ we've used the original template pattern type for the reshape_init
+ above; this is done because we want PARMS to be a template parameter
+ type, something that can be deduced when used as a function template
+ parameter. At this point the outer class template has already been
+ partially instantiated (we deferred the deduction until the enclosing
+ scope is non-dependent). Therefore we have to partially instantiate
+ PARMS, so that its template level is properly reduced and we don't get
+ mismatches when deducing types using the guide with PARMS. */
+ if (member_template_p)
+ parms = tsubst (parms, DECL_TI_ARGS (tmpl), complain, init);
}
else if (TREE_CODE (init) == TREE_LIST)
{
@@ -29215,6 +29266,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
return ptype;
+ /* Wait until the enclosing scope is non-dependent. */
+ if (DECL_CLASS_SCOPE_P (tmpl)
+ && dependent_type_p (DECL_CONTEXT (tmpl)))
+ return ptype;
+
/* Initializing one placeholder from another. */
if (init && TREE_CODE (init) == TEMPLATE_PARM_INDEX
&& is_auto (TREE_TYPE (init))
@@ -29414,9 +29470,11 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE.
The CONTEXT determines the context in which auto deduction is performed
and is used to control error diagnostics. FLAGS are the LOOKUP_* flags.
- OUTER_TARGS are used during template argument deduction
- (context == adc_unify) to properly substitute the result, and is ignored
- in other contexts.
+
+ OUTER_TARGS is used during template argument deduction (context == adc_unify)
+ to properly substitute the result. It's also used in the adc_unify and
+ adc_requirement contexts to communicate the the necessary template arguments
+ to satisfaction. OUTER_TARGS is ignored in other contexts.
For partial-concept-ids, extra args may be appended to the list of deduced
template arguments prior to determining constraint satisfaction. */
@@ -29508,9 +29566,13 @@ do_auto_deduction (tree type, tree init, tree auto_node,
|| ((TREE_CODE (init) == COMPONENT_REF
|| TREE_CODE (init) == SCOPE_REF)
&& !REF_PARENTHESIZED_P (init)));
+ tree deduced = finish_decltype_type (init, id, complain);
+ deduced = canonicalize_type_argument (deduced, complain);
+ if (deduced == error_mark_node)
+ return error_mark_node;
targs = make_tree_vec (1);
- TREE_VEC_ELT (targs, 0)
- = finish_decltype_type (init, id, tf_warning_or_error);
+ TREE_VEC_ELT (targs, 0) = deduced;
+ /* FIXME: These errors ought to be diagnosed at parse time. */
if (type != auto_node)
{
if (complain & tf_error)
@@ -29573,63 +29635,82 @@ do_auto_deduction (tree type, tree init, tree auto_node,
}
/* Check any placeholder constraints against the deduced type. */
- if (flag_concepts && !processing_template_decl)
- if (tree check = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
- {
- /* Use the deduced type to check the associated constraints. If we
- have a partial-concept-id, rebuild the argument list so that
- we check using the extra arguments. */
- check = unpack_concept_check (check);
- gcc_assert (TREE_CODE (check) == TEMPLATE_ID_EXPR);
- tree cdecl = TREE_OPERAND (check, 0);
- if (OVL_P (cdecl))
- cdecl = OVL_FIRST (cdecl);
- tree cargs = TREE_OPERAND (check, 1);
- if (TREE_VEC_LENGTH (cargs) > 1)
- {
- cargs = copy_node (cargs);
- TREE_VEC_ELT (cargs, 0) = TREE_VEC_ELT (targs, 0);
- }
- else
- cargs = targs;
+ if (processing_template_decl && context == adc_unify)
+ /* Constraints will be checked after deduction. */;
+ else if (tree constr = NON_ERROR (PLACEHOLDER_TYPE_CONSTRAINTS (auto_node)))
+ {
+ if (processing_template_decl)
+ {
+ gcc_checking_assert (context == adc_variable_type
+ || context == adc_return_type);
+ gcc_checking_assert (!type_dependent_expression_p (init));
+ /* If the constraint is dependent, we need to wait until
+ instantiation time to resolve the placeholder. */
+ if (placeholder_type_constraint_dependent_p (constr))
+ return type;
+ }
- /* Rebuild the check using the deduced arguments. */
- check = build_concept_check (cdecl, cargs, tf_none);
+ if ((context == adc_return_type || context == adc_variable_type)
+ && current_function_decl
+ && DECL_TEMPLATE_INFO (current_function_decl))
+ outer_targs = DECL_TI_ARGS (current_function_decl);
- if (!constraints_satisfied_p (check))
- {
- if (complain & tf_warning_or_error)
- {
- auto_diagnostic_group d;
- switch (context)
- {
- case adc_unspecified:
- case adc_unify:
- error("placeholder constraints not satisfied");
- break;
- case adc_variable_type:
- case adc_decomp_type:
- error ("deduced initializer does not satisfy "
- "placeholder constraints");
- break;
- case adc_return_type:
- error ("deduced return type does not satisfy "
- "placeholder constraints");
- break;
- case adc_requirement:
- error ("deduced expression type does not satisfy "
- "placeholder constraints");
- break;
- }
- diagnose_constraints (input_location, check, targs);
- }
- return error_mark_node;
- }
- }
+ tree full_targs = add_to_template_args (outer_targs, targs);
+
+ /* HACK: Compensate for callers not always communicating all levels of
+ outer template arguments by filling in the outermost missing levels
+ with dummy levels before checking satisfaction. We'll still crash
+ if the constraint depends on a template argument belonging to one of
+ these missing levels, but this hack otherwise allows us to handle a
+ large subset of possible constraints (including all non-dependent
+ constraints). */
+ if (int missing_levels = (TEMPLATE_TYPE_ORIG_LEVEL (auto_node)
+ - TMPL_ARGS_DEPTH (full_targs)))
+ {
+ tree dummy_levels = make_tree_vec (missing_levels);
+ for (int i = 0; i < missing_levels; ++i)
+ TREE_VEC_ELT (dummy_levels, i) = make_tree_vec (0);
+ full_targs = add_to_template_args (dummy_levels, full_targs);
+ }
+
+ if (!constraints_satisfied_p (auto_node, full_targs))
+ {
+ if (complain & tf_warning_or_error)
+ {
+ auto_diagnostic_group d;
+ switch (context)
+ {
+ case adc_unspecified:
+ case adc_unify:
+ error("placeholder constraints not satisfied");
+ break;
+ case adc_variable_type:
+ case adc_decomp_type:
+ error ("deduced initializer does not satisfy "
+ "placeholder constraints");
+ break;
+ case adc_return_type:
+ error ("deduced return type does not satisfy "
+ "placeholder constraints");
+ break;
+ case adc_requirement:
+ error ("deduced expression type does not satisfy "
+ "placeholder constraints");
+ break;
+ }
+ diagnose_constraints (input_location, auto_node, full_targs);
+ }
+ return error_mark_node;
+ }
+ }
- if (processing_template_decl && context != adc_unify)
- outer_targs = current_template_args ();
- targs = add_to_template_args (outer_targs, targs);
+ if (TEMPLATE_TYPE_LEVEL (auto_node) == 1)
+ /* The outer template arguments are already substituted into type
+ (but we still may have used them for constraint checking above). */;
+ else if (context == adc_unify)
+ targs = add_to_template_args (outer_targs, targs);
+ else if (processing_template_decl)
+ targs = add_to_template_args (current_template_args (), targs);
return tsubst (type, targs, complain, NULL_TREE);
}
@@ -29645,22 +29726,26 @@ splice_late_return_type (tree type, tree late_return_type)
return late_return_type;
}
- if (tree *auto_node = find_type_usage (&type, is_auto))
- {
- tree idx = get_template_parm_index (*auto_node);
- if (TEMPLATE_PARM_LEVEL (idx) <= processing_template_decl)
- {
- /* In an abbreviated function template we didn't know we were dealing
- with a function template when we saw the auto return type, so update
- it to have the correct level. */
- tree new_auto = make_auto_1 (TYPE_IDENTIFIER (*auto_node), false);
- PLACEHOLDER_TYPE_CONSTRAINTS (new_auto)
- = PLACEHOLDER_TYPE_CONSTRAINTS (*auto_node);
- TYPE_CANONICAL (new_auto) = canonical_type_parameter (new_auto);
- new_auto = cp_build_qualified_type (new_auto, TYPE_QUALS (*auto_node));
- *auto_node = new_auto;
- }
- }
+ if (tree auto_node = find_type_usage (type, is_auto))
+ if (TEMPLATE_TYPE_LEVEL (auto_node) <= processing_template_decl)
+ {
+ /* In an abbreviated function template we didn't know we were dealing
+ with a function template when we saw the auto return type, so rebuild
+ the return type using an auto with the correct level. */
+ tree new_auto = make_auto_1 (TYPE_IDENTIFIER (auto_node), false);
+ tree auto_vec = make_tree_vec (1);
+ TREE_VEC_ELT (auto_vec, 0) = new_auto;
+ tree targs = add_outermost_template_args (current_template_args (),
+ auto_vec);
+ /* Also rebuild the constraint info in terms of the new auto. */
+ if (tree ci = PLACEHOLDER_TYPE_CONSTRAINTS_INFO (auto_node))
+ PLACEHOLDER_TYPE_CONSTRAINTS_INFO (new_auto)
+ = build_tree_list (current_template_parms,
+ tsubst_constraint (TREE_VALUE (ci), targs,
+ tf_none, NULL_TREE));
+ TYPE_CANONICAL (new_auto) = canonical_type_parameter (new_auto);
+ return tsubst (type, targs, tf_none, NULL_TREE);
+ }
return type;
}
@@ -29705,10 +29790,8 @@ type_uses_auto (tree type)
else
return NULL_TREE;
}
- else if (tree *tp = find_type_usage (&type, is_auto))
- return *tp;
else
- return NULL_TREE;
+ return find_type_usage (type, is_auto);
}
/* Report ill-formed occurrences of auto types in ARGUMENTS. If
@@ -29872,28 +29955,19 @@ walk_specializations (bool decls_p,
}
/* Lookup the specialization of *ELT, in the decl or type
- specialization table. Return the SPEC that's already there (NULL if
- nothing). If INSERT is true, and there was nothing, add the new
- spec. */
+ specialization table. Return the SPEC that's already there, or
+ NULL if nothing. */
tree
-match_mergeable_specialization (bool decl_p, spec_entry *elt, bool insert)
+match_mergeable_specialization (bool decl_p, spec_entry *elt)
{
hash_table<spec_hasher> *specializations
= decl_p ? decl_specializations : type_specializations;
hashval_t hash = spec_hasher::hash (elt);
- spec_entry **slot
- = specializations->find_slot_with_hash (elt, hash,
- insert ? INSERT : NO_INSERT);
- if (slot && *slot)
- return (*slot)->spec;
+ auto *slot = specializations->find_slot_with_hash (elt, hash, NO_INSERT);
- if (insert)
- {
- auto entry = ggc_alloc<spec_entry> ();
- *entry = *elt;
- *slot = entry;
- }
+ if (slot)
+ return (*slot)->spec;
return NULL_TREE;
}
@@ -29929,23 +30003,43 @@ get_mergeable_specialization_flags (tree tmpl, tree decl)
return flags;
}
-/* Add a new specialization of TMPL. FLAGS is as returned from
+/* Add a new specialization described by SPEC. DECL is the
+ maybe-template decl and FLAGS is as returned from
get_mergeable_specialization_flags. */
void
-add_mergeable_specialization (tree tmpl, tree args, tree decl, unsigned flags)
+add_mergeable_specialization (bool decl_p, spec_entry *elt,
+ tree decl, unsigned flags)
{
+ hash_table<spec_hasher> *specializations
+ = decl_p ? decl_specializations : type_specializations;
+
+ hashval_t hash = spec_hasher::hash (elt);
+ auto *slot = specializations->find_slot_with_hash (elt, hash, INSERT);
+
+ /* We don't distinguish different constrained partial type
+ specializations, so there could be duplicates. Everything else
+ must be new. */
+ if (!(flags & 2 && *slot))
+ {
+ gcc_checking_assert (!*slot);
+
+ auto entry = ggc_alloc<spec_entry> ();
+ *entry = *elt;
+ *slot = entry;
+ }
+
if (flags & 1)
- DECL_TEMPLATE_INSTANTIATIONS (tmpl)
- = tree_cons (args, decl, DECL_TEMPLATE_INSTANTIATIONS (tmpl));
+ DECL_TEMPLATE_INSTANTIATIONS (elt->tmpl)
+ = tree_cons (elt->args, decl, DECL_TEMPLATE_INSTANTIATIONS (elt->tmpl));
if (flags & 2)
{
/* A partial specialization. */
- DECL_TEMPLATE_SPECIALIZATIONS (tmpl)
- = tree_cons (args, decl, DECL_TEMPLATE_SPECIALIZATIONS (tmpl));
- TREE_TYPE (DECL_TEMPLATE_SPECIALIZATIONS (tmpl))
- = TREE_TYPE (DECL_TEMPLATE_RESULT (decl));
+ tree cons = tree_cons (elt->args, decl,
+ DECL_TEMPLATE_SPECIALIZATIONS (elt->tmpl));
+ TREE_TYPE (cons) = elt->spec;
+ DECL_TEMPLATE_SPECIALIZATIONS (elt->tmpl) = cons;
}
}
diff --git a/gcc/cp/ptree.c b/gcc/cp/ptree.c
index e06fe6f..95a4fdf 100644
--- a/gcc/cp/ptree.c
+++ b/gcc/cp/ptree.c
@@ -310,8 +310,7 @@ cxx_print_xnode (FILE *file, tree node, int indent)
{
indent_to (file, indent + 4);
unsigned lazy = slot.get_lazy ();
- fprintf (file, "%s snum:%u flags:%d",
- pfx, lazy >> 2, lazy & 3);
+ fprintf (file, "%s snum:%u", pfx, lazy);
}
else if (slot)
print_node (file, pfx, slot, indent + 4);
diff --git a/gcc/cp/rtti.c b/gcc/cp/rtti.c
index b41d954..5a33b83 100644
--- a/gcc/cp/rtti.c
+++ b/gcc/cp/rtti.c
@@ -121,7 +121,6 @@ vec<tree, va_gc> *unemitted_tinfo_decls;
and are generated as needed. */
static GTY (()) vec<tinfo_s, va_gc> *tinfo_descs;
-static tree ifnonnull (tree, tree, tsubst_flags_t);
static tree tinfo_name (tree, bool);
static tree build_dynamic_cast_1 (location_t, tree, tree, tsubst_flags_t);
static tree throw_bad_cast (void);
@@ -529,16 +528,23 @@ get_typeid (tree type, tsubst_flags_t complain)
/* Check whether TEST is null before returning RESULT. If TEST is used in
RESULT, it must have previously had a save_expr applied to it. */
-static tree
-ifnonnull (tree test, tree result, tsubst_flags_t complain)
+tree
+build_if_nonnull (tree test, tree result, tsubst_flags_t complain)
{
- tree cond = build2 (NE_EXPR, boolean_type_node, test,
- cp_convert (TREE_TYPE (test), nullptr_node, complain));
+ tree null_ptr = cp_convert (TREE_TYPE (test), nullptr_node, complain);
+ tree cond = build2 (NE_EXPR, boolean_type_node, test, null_ptr);
+
/* This is a compiler generated comparison, don't emit
e.g. -Wnonnull-compare warning for it. */
TREE_NO_WARNING (cond) = 1;
- return build3 (COND_EXPR, TREE_TYPE (result), cond, result,
- cp_convert (TREE_TYPE (result), nullptr_node, complain));
+
+ null_ptr = cp_convert (TREE_TYPE (result), nullptr_node, complain);
+ cond = build3 (COND_EXPR, TREE_TYPE (result), cond, result, null_ptr);
+
+ /* Likewise, don't emit -Wnonnull for using the result to call
+ a member function. */
+ TREE_NO_WARNING (cond) = 1;
+ return cond;
}
/* Execute a dynamic cast, as described in section 5.2.6 of the 9/93 working
@@ -671,7 +677,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
expr1 = build_headof (expr);
if (TREE_TYPE (expr1) != type)
expr1 = build1 (NOP_EXPR, type, expr1);
- return ifnonnull (expr, expr1, complain);
+ return build_if_nonnull (expr, expr1, complain);
}
else
{
@@ -786,7 +792,7 @@ build_dynamic_cast_1 (location_t loc, tree type, tree expr,
/* Now back to the type we want from a void*. */
result = cp_convert (type, result, complain);
- return ifnonnull (expr, result, complain);
+ return build_if_nonnull (expr, result, complain);
}
}
else
diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c
index e6ced27..3c46975 100644
--- a/gcc/cp/tree.c
+++ b/gcc/cp/tree.c
@@ -674,9 +674,6 @@ build_aggr_init_expr (tree type, tree init)
else
rval = init;
- if (location_t loc = EXPR_LOCATION (init))
- SET_EXPR_LOCATION (rval, loc);
-
return rval;
}
diff --git a/gcc/cp/type-utils.h b/gcc/cp/type-utils.h
index 5551e8f..138fed6 100644
--- a/gcc/cp/type-utils.h
+++ b/gcc/cp/type-utils.h
@@ -20,22 +20,21 @@ along with GCC; see the file COPYING3. If not see
#ifndef GCC_CP_TYPE_UTILS_H
#define GCC_CP_TYPE_UTILS_H
-/* Returns a pointer to the first tree within *TP that is directly matched by
- PRED. *TP may be a type or PARM_DECL and is incrementally decomposed toward
- its type-specifier until a match is found. NULL is returned if PRED does not
- match any part of *TP.
+/* Returns the first tree within T that is directly matched by PRED. T may be a
+ type or PARM_DECL and is incrementally decomposed toward its type-specifier
+ until a match is found. NULL is returned if PRED does not match any
+ part of T.
- This is primarily intended for detecting whether *TP uses `auto' or a concept
+ This is primarily intended for detecting whether T uses `auto' or a concept
identifier. Since either of these can only appear as a type-specifier for
the declaration in question, only top-level qualifications are traversed;
find_type_usage does not look through the whole type. */
-inline tree *
-find_type_usage (tree *tp, bool (*pred) (const_tree))
+inline tree
+find_type_usage (tree t, bool (*pred) (const_tree))
{
- tree t = *tp;
if (pred (t))
- return tp;
+ return t;
enum tree_code code = TREE_CODE (t);
@@ -43,13 +42,13 @@ find_type_usage (tree *tp, bool (*pred) (const_tree))
|| code == PARM_DECL || code == OFFSET_TYPE
|| code == FUNCTION_TYPE || code == METHOD_TYPE
|| code == ARRAY_TYPE)
- return find_type_usage (&TREE_TYPE (t), pred);
+ return find_type_usage (TREE_TYPE (t), pred);
if (TYPE_PTRMEMFUNC_P (t))
return find_type_usage
- (&TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (t)), pred);
+ (TREE_TYPE (TYPE_PTRMEMFUNC_FN_TYPE (t)), pred);
- return NULL;
+ return NULL_TREE;
}
#endif // GCC_CP_TYPE_UTILS_H
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index a87d5e5..dff4e9b 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -133,8 +133,15 @@ complete_type (tree type)
TYPE_HAS_NONTRIVIAL_DESTRUCTOR (t) = has_nontrivial_dtor;
}
}
- else if (CLASS_TYPE_P (type) && CLASSTYPE_TEMPLATE_INSTANTIATION (type))
- instantiate_class_template (TYPE_MAIN_VARIANT (type));
+ else if (CLASS_TYPE_P (type))
+ {
+ if (modules_p ())
+ /* TYPE could be a class member we've not loaded the definition of. */
+ lazy_load_pendings (TYPE_NAME (TYPE_MAIN_VARIANT (type)));
+
+ if (CLASSTYPE_TEMPLATE_INSTANTIATION (type))
+ instantiate_class_template (TYPE_MAIN_VARIANT (type));
+ }
return type;
}
@@ -4060,7 +4067,8 @@ error_args_num (location_t loc, tree fndecl, bool too_many_p)
if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE)
{
if (DECL_NAME (fndecl) == NULL_TREE
- || IDENTIFIER_HAS_TYPE_VALUE (DECL_NAME (fndecl)))
+ || (DECL_NAME (fndecl)
+ == DECL_NAME (TYPE_NAME (DECL_CONTEXT (fndecl)))))
error_at (loc,
too_many_p
? G_("too many arguments to constructor %q#D")