[gcc r17-3516] c++, contracts: Use a tree-vec for contract specifiers.
Iain D Sandoe via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:73c0e8f8550420f8966eeaa6e75f90cf6e15f9b1 commit r17-3516-g73c0e8f8550420f8966eeaa6e75f90cf6e15f9b1 Author: Iain Sandoe <[email protected]> Date: Tue Aug 4 15:38:53 2026 +0100 c++, contracts: Use a tree-vec for contract specifiers. We made use of an attribute list throughout the project so that we could continue to support cxx2a contracts in parallel. Since those have now been removed there is no longer any use and the attribute lists are just unneccessary obfuscation. Replace them with tree-vecs. gcc/cp/ChangeLog: * contracts.cc: Replace use of attribute lists to hold function contract specifiers with tree-vecs. * contracts.h (CONTRACT_SOURCE_LOCATION_WRAPPER, CONTRACT_SOURCE_LOCATION, CONTRACT_STATEMENT): Remove. (finish_contract_specifier): Remove. (build_contract_specifiers): Use tree-vec for contracts. (contract_specifiers_concat): Likewise. * decl.cc (start_decl): Use tree-vec for contracts. (grokdeclarator): Likewise. * module.cc (trees_out::fn_parms_init): Likewise. * parser.cc (cp_parser_late_contracts): Likewise. (cp_parser_function_contract_specifier_seq): Likewise. * pt.cc (tsubst_contract_specifier): Likewise. (tsubst_contract_specifiers): Likewise. Signed-off-by: Iain Sandoe <[email protected]> Diff: --- gcc/cp/contracts.cc | 361 ++++++++++++++++++++++++++-------------------------- gcc/cp/contracts.h | 19 +-- gcc/cp/decl.cc | 25 ++-- gcc/cp/module.cc | 6 +- gcc/cp/parser.cc | 35 ++--- gcc/cp/pt.cc | 44 ++++--- 6 files changed, 241 insertions(+), 249 deletions(-) diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index 25da963d0b4e..8bd2a8df5b9b 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -161,14 +161,6 @@ contract_valid_p (tree contract) return CONTRACT_CONDITION (contract) != error_mark_node; } -/* True if the contract specifier is valid. */ - -static bool -contract_specifier_valid_p (tree contract) -{ - return contract_valid_p (TREE_VALUE (TREE_VALUE (contract))); -} - /* Compare the contract conditions of OLD_CONTRACT and NEW_CONTRACT. Returns false if the conditions are equivalent, and true otherwise. */ @@ -224,41 +216,43 @@ match_contract_specifiers (location_t oldloc, tree old_contracts, if (!old_contracts || !new_contracts) return true; - /* Compare each contract in turn. */ - while (old_contracts && new_contracts) - { - /* If either contract is ill-formed, skip the rest of the comparison, - since we've already diagnosed an error. */ - if (!contract_specifier_valid_p (new_contracts) - || !contract_specifier_valid_p (old_contracts)) - return false; - - if (mismatched_contracts_p (CONTRACT_STATEMENT (old_contracts), - CONTRACT_STATEMENT (new_contracts))) - return false; - old_contracts = TREE_CHAIN (old_contracts); - new_contracts = TREE_CHAIN (new_contracts); - } + int old_len = TREE_VEC_LENGTH (old_contracts); + int new_len = TREE_VEC_LENGTH (new_contracts); - /* If we didn't compare all specifiers, the contracts don't match. */ - if (old_contracts || new_contracts) + /* If we don't have the same number, the contracts don't match. */ + if (old_len != new_len) { auto_diagnostic_group d; error_at (newloc, "declaration has a different number of contracts than " "previously declared"); inform (oldloc, - new_contracts + new_len > old_len ? "previous declaration with fewer contracts here" : "previous declaration with more contracts here"); return false; } + /* Compare each contract in turn. */ + for (int ix = 0; ix < MIN (old_len, new_len); ix++) + { + tree old_contract = TREE_VEC_ELT (old_contracts, ix); + tree new_contract = TREE_VEC_ELT (new_contracts, ix); + + /* If either contract is ill-formed, skip the rest of the comparison, + since we've already diagnosed an error. */ + if (!contract_valid_p (new_contract) || !contract_valid_p (old_contract)) + return false; + + if (mismatched_contracts_p (old_contract, new_contract)) + return false; + } + + return true; } -/* Return true if CONTRACT is checked or assumed under the current build - configuration. */ +/* Return true if CONTRACT is checked under the current semantic. */ static bool contract_active_p (tree contract) @@ -266,19 +260,35 @@ contract_active_p (tree contract) return get_evaluation_semantic (contract) != CES_IGNORE; } -/* True if FNDECL has any checked or assumed contracts whose TREE_CODE is +/* Return true if any contract of FNDECL is checked under the + current semantic. */ + +static bool +contract_any_active_p (tree fndecl) +{ + tree contracts = get_fn_contract_specifiers (fndecl); + if (!contracts) + return false; + + for (tree contract : tree_vec_range (contracts)) + if (contract_active_p (contract)) + return true; + return false; +} + +/* True if FNDECL has any checked contracts whose TREE_CODE is C. */ static bool has_active_contract_condition (tree fndecl, tree_code c) { - tree as = get_fn_contract_specifiers (fndecl); - for (; as != NULL_TREE; as = TREE_CHAIN (as)) - { - tree contract = TREE_VALUE (TREE_VALUE (as)); - if (TREE_CODE (contract) == c && contract_active_p (contract)) - return true; - } + tree contracts = get_fn_contract_specifiers (fndecl); + if (!contracts) + return false; + + for (tree contract : tree_vec_range (contracts)) + if (TREE_CODE (contract) == c && contract_active_p (contract)) + return true; return false; } @@ -298,26 +308,16 @@ has_active_postconditions (tree fndecl) return has_active_contract_condition (fndecl, POSTCONDITION_STMT); } -/* Return true if any contract in the CONTRACT list is checked or assumed - under the current build configuration. */ - -static bool -contract_any_active_p (tree fndecl) -{ - tree as = get_fn_contract_specifiers (fndecl); - for (; as; as = TREE_CHAIN (as)) - if (contract_active_p (TREE_VALUE (TREE_VALUE (as)))) - return true; - return false; -} - /* Return true if any contract in CONTRACTS is not yet parsed. */ bool contract_any_deferred_p (tree contracts) { - for (; contracts; contracts = TREE_CHAIN (contracts)) - if (CONTRACT_CONDITION_DEFERRED_P (CONTRACT_STATEMENT (contracts))) + if (!contracts) + return false; + + for (tree contract : tree_vec_range (contracts)) + if (CONTRACT_CONDITION_DEFERRED_P (contract)) return true; return false; } @@ -425,19 +425,53 @@ get_evaluation_semantic (const_tree contract) gcc_unreachable (); } -/* Get location of the last contract in the CONTRACTS tree chain. */ +/* Get location of the last contract in CONTRACTS. */ static location_t get_contract_end_loc (tree contracts) { - tree last = NULL_TREE; - for (tree a = contracts; a; a = TREE_CHAIN (a)) - last = a; - gcc_checking_assert (last); - last = CONTRACT_STATEMENT (last); + gcc_checking_assert (contracts && TREE_VEC_LENGTH (contracts) > 0); + tree last = TREE_VEC_ELT (contracts, TREE_VEC_LENGTH (contracts) - 1); return EXPR_LOCATION (last); } +/* Build the contract specifiers for a function from CONTRACTS, which are in + source order. Returns NULL_TREE when there are none. */ + +tree +build_contract_specifiers (vec<tree, va_gc> *contracts) +{ + unsigned len = vec_safe_length (contracts); + if (!len) + return NULL_TREE; + + tree specs = make_tree_vec (len); + for (unsigned ix = 0; ix < len; ix++) + TREE_VEC_ELT (specs, ix) = (*contracts)[ix]; + return specs; +} + +/* Append the contract specifiers in SECOND to those in FIRST, either of + which may be NULL_TREE. Neither input is modified. */ + +tree +contract_specifiers_concat (tree first, tree second) +{ + if (!first) + return second; + if (!second) + return first; + + int flen = TREE_VEC_LENGTH (first); + int slen = TREE_VEC_LENGTH (second); + tree specs = make_tree_vec (flen + slen); + for (int ix = 0; ix < flen; ix++) + TREE_VEC_ELT (specs, ix) = TREE_VEC_ELT (first, ix); + for (int ix = 0; ix < slen; ix++) + TREE_VEC_ELT (specs, flen + ix) = TREE_VEC_ELT (second, ix); + return specs; +} + struct GTY(()) contract_decl { tree contract_specifiers; @@ -1013,40 +1047,41 @@ start_function_contracts (tree fndecl) /* Check that the postcondition result name, if any, does not shadow a function parameter. */ - for (tree ca = get_fn_contract_specifiers (fndecl); ca; ca = TREE_CHAIN (ca)) - if (POSTCONDITION_P (CONTRACT_STATEMENT (ca))) - if (tree id = POSTCONDITION_IDENTIFIER (CONTRACT_STATEMENT (ca))) - { - if (id == error_mark_node) - { - CONTRACT_CONDITION (CONTRACT_STATEMENT (ca)) = error_mark_node; - continue; - } - tree r_name = tree_strip_any_location_wrapper (id); - if (TREE_CODE (id) == PARM_DECL) - r_name = DECL_NAME (id); - gcc_checking_assert (r_name && TREE_CODE (r_name) == IDENTIFIER_NODE); - tree seen = lookup_name (r_name); - if (seen - && TREE_CODE (seen) == PARM_DECL - && DECL_CONTEXT (seen) == fndecl) - { + if (tree specs = get_fn_contract_specifiers (fndecl)) + for (tree ca : tree_vec_range (specs)) + if (POSTCONDITION_P (ca)) + if (tree id = POSTCONDITION_IDENTIFIER (ca)) + { + if (id == error_mark_node) + { + CONTRACT_CONDITION (ca) = error_mark_node; + continue; + } + tree r_name = tree_strip_any_location_wrapper (id); + if (TREE_CODE (id) == PARM_DECL) + r_name = DECL_NAME (id); + gcc_checking_assert (r_name + && TREE_CODE (r_name) == IDENTIFIER_NODE); + tree seen = lookup_name (r_name); + if (seen + && TREE_CODE (seen) == PARM_DECL + && DECL_CONTEXT (seen) == fndecl) + { auto_diagnostic_group d; location_t id_l = location_wrapper_p (id) ? EXPR_LOCATION (id) : DECL_SOURCE_LOCATION (id); - location_t co_l = EXPR_LOCATION (CONTRACT_STATEMENT (ca)); + location_t co_l = EXPR_LOCATION (ca); if (id_l != UNKNOWN_LOCATION) co_l = make_location (id_l, co_l, co_l); error_at (co_l, "contract postcondition result name shadows a" " function parameter"); inform (DECL_SOURCE_LOCATION (seen), "parameter declared here"); - POSTCONDITION_IDENTIFIER (CONTRACT_STATEMENT (ca)) - = error_mark_node; - CONTRACT_CONDITION (CONTRACT_STATEMENT (ca)) = error_mark_node; - } - } + POSTCONDITION_IDENTIFIER (ca) = error_mark_node; + CONTRACT_CONDITION (ca) = error_mark_node; + } + } /* If we are expanding contract assertions inline then no need to declare the outline function decls. */ @@ -1179,20 +1214,19 @@ static tree copy_contracts_list (tree contracts, tree fndecl, contract_match_kind remap_kind = cmk_all) { - tree last = NULL_TREE, new_contracts = NULL_TREE; - for (; contracts; contracts = TREE_CHAIN (contracts)) + if (!contracts) + return NULL_TREE; + + auto_vec<tree> copies (TREE_VEC_LENGTH (contracts)); + for (tree contract : tree_vec_range (contracts)) { if ((remap_kind == cmk_pre - && (TREE_CODE (CONTRACT_STATEMENT (contracts)) - == POSTCONDITION_STMT)) + && TREE_CODE (contract) == POSTCONDITION_STMT) || (remap_kind == cmk_post - && (TREE_CODE (CONTRACT_STATEMENT (contracts)) - == PRECONDITION_STMT))) + && TREE_CODE (contract) == PRECONDITION_STMT)) continue; - tree c = copy_node (contracts); - TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)), - copy_node (CONTRACT_STATEMENT (c))); + tree c = copy_node (contract); copy_body_data id; hash_map<tree, tree> decl_map; @@ -1219,18 +1253,19 @@ copy_contracts_list (tree contracts, tree fndecl, /* We're not inside any EH region. */ id.eh_lp_nr = 0; - walk_tree (&CONTRACT_CONDITION (CONTRACT_STATEMENT (c)), - copy_tree_body_r, &id, NULL); + walk_tree (&CONTRACT_CONDITION (c), copy_tree_body_r, &id, NULL); + CONTRACT_COMMENT (c) = copy_node (CONTRACT_COMMENT (c)); - CONTRACT_COMMENT (CONTRACT_STATEMENT (c)) - = copy_node (CONTRACT_COMMENT (CONTRACT_STATEMENT (c))); - - chainon (last, c); - last = c; - if (!new_contracts) - new_contracts = c; + copies.quick_push (c); } + + if (copies.is_empty ()) + return NULL_TREE; + + tree new_contracts = make_tree_vec (copies.length ()); + for (unsigned ix = 0; ix < copies.length (); ix++) + TREE_VEC_ELT (new_contracts, ix) = copies[ix]; return new_contracts; } @@ -1264,19 +1299,6 @@ emit_contract_statement (tree contract) return true; } -/* Generate the statement for the given contract by adding the contract - statement to the current block. Returns the next contract in the chain. */ - -static tree -emit_contract (tree contract) -{ - gcc_assert (TREE_CODE (contract) == TREE_LIST); - - emit_contract_statement (CONTRACT_STATEMENT (contract)); - - return TREE_CHAIN (contract); -} - /* Add a call or a direct evaluation of the pre checks. */ static void @@ -1286,9 +1308,9 @@ apply_preconditions (tree fndecl) add_pre_condition_fn_call (fndecl); else { - tree contract_copy = copy_contracts (fndecl, cmk_pre); - for (; contract_copy; contract_copy = TREE_CHAIN (contract_copy)) - emit_contract (contract_copy); + if (tree contract_copy = copy_contracts (fndecl, cmk_pre)) + for (tree contract : tree_vec_range (contract_copy)) + emit_contract_statement (contract); } } @@ -1301,9 +1323,9 @@ apply_postconditions (tree fndecl) add_post_condition_fn_call (fndecl); else { - tree contract_copy = copy_contracts (fndecl, cmk_post); - for (; contract_copy; contract_copy = TREE_CHAIN (contract_copy)) - emit_contract (contract_copy); + if (tree contract_copy = copy_contracts (fndecl, cmk_post)) + for (tree contract : tree_vec_range (contract_copy)) + emit_contract_statement (contract); } } @@ -1513,25 +1535,20 @@ tree copy_and_remap_contracts (tree dest, tree source, contract_match_kind remap_kind) { - tree last = NULL_TREE, contracts_copy= NULL_TREE; tree contracts = get_fn_contract_specifiers (source); - for (; contracts; contracts = TREE_CHAIN (contracts)) + if (!contracts) + return NULL_TREE; + + auto_vec<tree> copies (TREE_VEC_LENGTH (contracts)); + for (tree contract : tree_vec_range (contracts)) { if ((remap_kind == cmk_pre - && (TREE_CODE (CONTRACT_STATEMENT (contracts)) - == POSTCONDITION_STMT)) + && TREE_CODE (contract) == POSTCONDITION_STMT) || (remap_kind == cmk_post - && (TREE_CODE (CONTRACT_STATEMENT (contracts)) - == PRECONDITION_STMT))) + && TREE_CODE (contract) == PRECONDITION_STMT)) continue; - /* The first part is copying of the legacy attribute layout - eventually - this will go away. */ - tree c = copy_node (contracts); - TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)), - copy_node (CONTRACT_STATEMENT (c))); - /* This is the copied contract statement. */ - tree stmt = CONTRACT_STATEMENT (c); + tree stmt = copy_node (contract); /* If we have an erroneous postcondition identifier, we also mark the condition as invalid so only need to check that. */ @@ -1550,23 +1567,30 @@ copy_and_remap_contracts (tree dest, tree source, if (CONTRACT_COMMENT (stmt) != error_mark_node) CONTRACT_COMMENT (stmt) = copy_node (CONTRACT_COMMENT (stmt)); - chainon (last, c); - last = c; - if (!contracts_copy) - contracts_copy = c; + copies.quick_push (stmt); } + if (copies.is_empty ()) + return NULL_TREE; + + tree contracts_copy = make_tree_vec (copies.length ()); + for (unsigned ix = 0; ix < copies.length (); ix++) + TREE_VEC_ELT (contracts_copy, ix) = copies[ix]; + return contracts_copy; } -/* Set the (maybe) parsed contract specifier LIST for DECL. */ +/* Set the (maybe) parsed contract specifiers CONTRACTS for DECL. + CONTRACTS is either NULL_TREE or a TREE_VEC of contract statements. */ void -set_fn_contract_specifiers (tree decl, tree list) +set_fn_contract_specifiers (tree decl, tree contracts) { if (!decl || error_operand_p (decl)) return; + gcc_checking_assert (!contracts || TREE_CODE (contracts) == TREE_VEC); + bool existed = false; contract_decl& rd = hash_map_safe_get_or_insert<hm_ggc> (contract_decl_map, decl, &existed); @@ -1577,18 +1601,18 @@ set_fn_contract_specifiers (tree decl, tree list) contracts used for the function. */ location_t decl_loc = DECL_SOURCE_LOCATION (decl); location_t cont_end = decl_loc; - if (list) - cont_end = get_contract_end_loc (list); + if (contracts) + cont_end = get_contract_end_loc (contracts); rd.note_loc = make_location (decl_loc, decl_loc, cont_end); } - rd.contract_specifiers = list; + rd.contract_specifiers = contracts; } /* Update the entry for DECL in the map of contract specifiers with the - contracts in LIST. */ + contracts in CONTRACTS. */ void -update_fn_contract_specifiers (tree decl, tree list) +update_fn_contract_specifiers (tree decl, tree contracts) { if (!decl || error_operand_p (decl)) return; @@ -1599,9 +1623,9 @@ update_fn_contract_specifiers (tree decl, tree list) gcc_checking_assert (existed); /* We should only get here when we parse deferred contracts. */ - gcc_checking_assert (!contract_any_deferred_p (list)); + gcc_checking_assert (!contract_any_deferred_p (contracts)); - rd.contract_specifiers = list; + rd.contract_specifiers = contracts; } /* When a decl is about to be removed, then we need to release its content and @@ -1962,9 +1986,11 @@ rebuild_postconditions (tree fndecl) return; tree contract_spec = get_fn_contract_specifiers (fndecl); - for (; contract_spec ; contract_spec = TREE_CHAIN (contract_spec)) + if (!contract_spec) + return; + + for (tree contract : tree_vec_range (contract_spec)) { - tree contract = TREE_VALUE (TREE_VALUE (contract_spec)); if (TREE_CODE (contract) != POSTCONDITION_STMT) continue; tree condition = CONTRACT_CONDITION (contract); @@ -2115,27 +2141,6 @@ grok_contract (tree contract_spec, tree mode, tree result, cp_expr condition, return contract; } -/* Build the contract specifier where IDENTIFIER is one of 'pre', - 'post' or 'assert' and CONTRACT is the underlying statement. */ - -tree -finish_contract_specifier (tree identifier, tree contract) -{ - if (contract == error_mark_node) - return error_mark_node; - - tree contract_spec = build_tree_list (build_tree_list (NULL_TREE, identifier), - build_tree_list (NULL_TREE, contract)); - - /* Mark the contract as dependent if the condition is dependent. */ - tree condition = CONTRACT_CONDITION (contract); - if (TREE_CODE (condition) != DEFERRED_PARSE - && value_dependent_expression_p (condition)) - ATTR_IS_DEPENDENT (contract_spec) = true; - - return contract_spec; -} - /* Update condition of a late-parsed contract and postcondition variable, if any. */ @@ -2196,17 +2201,17 @@ remap_and_emit_conditions (tree fn, tree condfn, tree_code code) { gcc_assert (code == PRECONDITION_STMT || code == POSTCONDITION_STMT); tree contract_spec = get_fn_contract_specifiers (fn); - for (; contract_spec; contract_spec = TREE_CHAIN (contract_spec)) - { - tree contract = CONTRACT_STATEMENT (contract_spec); - if (TREE_CODE (contract) == code) - { - contract = copy_node (contract); - if (CONTRACT_CONDITION (contract) != error_mark_node) - remap_contract (fn, condfn, contract, /*duplicate_p=*/false); - emit_contract_statement (contract); - } - } + if (!contract_spec) + return; + + for (tree contract : tree_vec_range (contract_spec)) + if (TREE_CODE (contract) == code) + { + contract = copy_node (contract); + if (CONTRACT_CONDITION (contract) != error_mark_node) + remap_contract (fn, condfn, contract, /*duplicate_p=*/false); + emit_contract_statement (contract); + } } /* Finish up the pre & post function definitions for a guarded FNDECL, diff --git a/gcc/cp/contracts.h b/gcc/cp/contracts.h index 705de1584f8e..c52d49de2008 100644 --- a/gcc/cp/contracts.h +++ b/gcc/cp/contracts.h @@ -83,22 +83,14 @@ enum detection_mode : uint16_t { #define POSTCONDITION_P(NODE) \ (TREE_CODE (NODE) == POSTCONDITION_STMT) +/* The contract specifiers of a function are held in a TREE_VEC, each element + of which is a PRECONDITION_STMT or a POSTCONDITION_STMT. A function with + no contracts has NULL_TREE rather than an empty vector. */ + /* True iff the FUNCTION_DECL NODE currently has any contracts. */ #define DECL_HAS_CONTRACTS_P(NODE) \ (get_fn_contract_specifiers (NODE) != NULL_TREE) -/* The wrapper of the original source location of a list of contracts. */ -#define CONTRACT_SOURCE_LOCATION_WRAPPER(NODE) \ - (TREE_PURPOSE (TREE_VALUE (NODE))) - -/* The original source location of a list of contracts. */ -#define CONTRACT_SOURCE_LOCATION(NODE) \ - (EXPR_LOCATION (CONTRACT_SOURCE_LOCATION_WRAPPER (NODE))) - -/* The actual code _STMT for a contract specifier. */ -#define CONTRACT_STATEMENT(NODE) \ - (TREE_VALUE (TREE_VALUE (NODE))) - /* The parsed condition of the contract. */ #define CONTRACT_CONDITION(NODE) \ (TREE_OPERAND (CONTRACT_CHECK (NODE), 2)) @@ -157,7 +149,8 @@ enum contract_match_kind extern void init_contracts (void); extern tree grok_contract (tree, tree, tree, cp_expr, location_t); -extern tree finish_contract_specifier (tree, tree); +extern tree build_contract_specifiers (vec<tree, va_gc> *); +extern tree contract_specifiers_concat (tree, tree); extern tree finish_contract_condition (cp_expr); extern void update_late_contract (tree, tree, cp_expr); extern void check_redecl_contract (tree, tree); diff --git a/gcc/cp/decl.cc b/gcc/cp/decl.cc index bd9903c4e739..55badc9eed97 100644 --- a/gcc/cp/decl.cc +++ b/gcc/cp/decl.cc @@ -6608,15 +6608,15 @@ start_decl (const cp_declarator *declarator, && !processing_template_decl && DECL_RESULT (decl) && is_auto (TREE_TYPE (DECL_RESULT (decl)))) - for (tree ca = get_fn_contract_specifiers (decl); ca; ca = TREE_CHAIN (ca)) - if (POSTCONDITION_P (CONTRACT_STATEMENT (ca)) - && POSTCONDITION_IDENTIFIER (CONTRACT_STATEMENT (ca))) - { - error_at (DECL_SOURCE_LOCATION (decl), - "postconditions with deduced result name types must only" - " appear on function definitions"); - return error_mark_node; - } + if (tree specs = get_fn_contract_specifiers (decl)) + for (tree ca : tree_vec_range (specs)) + if (POSTCONDITION_P (ca) && POSTCONDITION_IDENTIFIER (ca)) + { + error_at (DECL_SOURCE_LOCATION (decl), + "postconditions with deduced result name types must only" + " appear on function definitions"); + return error_mark_node; + } /* Save the DECL_INITIAL value in case it gets clobbered to assist with attribute validation. */ initial = DECL_INITIAL (decl); @@ -15741,11 +15741,12 @@ grokdeclarator (const cp_declarator *declarator, returned_attrs = attr_chainon (returned_attrs, att); } - /* Actually apply the contract attributes to the declaration. */ + /* Actually apply the contract specifiers to the declaration. */ if (flag_contracts) contract_specifiers - = attr_chainon (contract_specifiers, - declarator->u.function.contract_specifiers); + = contract_specifiers_concat + (contract_specifiers, + declarator->u.function.contract_specifiers); if (attrs) /* [dcl.fct]/2: diff --git a/gcc/cp/module.cc b/gcc/cp/module.cc index 09c6c4467347..e61cd700d8fb 100644 --- a/gcc/cp/module.cc +++ b/gcc/cp/module.cc @@ -11363,9 +11363,9 @@ trees_out::fn_parms_init (tree fn) { /* We must walk contract specifiers so the dependency graph is complete. */ - tree contract = get_fn_contract_specifiers (fn); - for (; contract; contract = TREE_CHAIN (contract)) - tree_node (contract); + if (tree contracts = get_fn_contract_specifiers (fn)) + for (tree contract : tree_vec_range (contracts)) + tree_node (contract); } /* Write a reference to contracts pre/post functions, if any, to avoid diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 3b4de7c7d3a3..8b8fad10f526 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -33862,27 +33862,22 @@ void cp_parser_late_contracts (cp_parser *parser, tree fndecl) { - tree new_contracts = NULL_TREE; - tree old_contracts = get_fn_contract_specifiers (fndecl); + tree contracts = get_fn_contract_specifiers (fndecl); - if (old_contracts == NULL_TREE || !contract_any_deferred_p (old_contracts)) + if (contracts == NULL_TREE || !contract_any_deferred_p (contracts)) return; - for (; old_contracts; old_contracts = TREE_CHAIN (old_contracts)) + /* The conditions are updated in place; the statements are shared with any + other declaration that refers to them, exactly as before. */ + for (tree contract : tree_vec_range (contracts)) { - tree contract = TREE_VALUE (TREE_VALUE (old_contracts)); - - tree condition = CONTRACT_CONDITION (contract); /* All contracts should be deferred if one of them is deferred */ - gcc_checking_assert (TREE_CODE (condition) == DEFERRED_PARSE); + gcc_checking_assert (CONTRACT_CONDITION_DEFERRED_P (contract)); cp_parser_late_contract_condition (parser, fndecl, contract); - tree list = tree_cons (TREE_PURPOSE (old_contracts), - TREE_VALUE (old_contracts), NULL_TREE); - new_contracts = chainon (new_contracts, list); } - update_fn_contract_specifiers (fndecl, new_contracts); + update_fn_contract_specifiers (fndecl, contracts); } static tree @@ -34140,7 +34135,7 @@ cp_parser_function_contract_specifier (cp_parser *parser) static tree cp_parser_function_contract_specifier_seq (cp_parser *parser) { - tree contract_specs = NULL_TREE; + releasing_vec contract_specs; while (true) { @@ -34154,19 +34149,11 @@ cp_parser_function_contract_specifier_seq (cp_parser *parser) if (contract_spec == error_mark_node) continue; - /* For now, turn this into an attribute. */ - tree contract_name = TREE_CODE (contract_spec) == PRECONDITION_STMT - ? get_identifier ("pre") - : get_identifier ("post"); - contract_spec = finish_contract_specifier (contract_name, contract_spec); - /* Arrange to build the list in the correct order. */ - if (contract_specs) - contract_specs = attr_chainon (contract_specs, contract_spec); - else - contract_specs = contract_spec; + /* The specifiers are collected in source order. */ + vec_safe_push (contract_specs, contract_spec); } - return contract_specs; + return build_contract_specifiers (contract_specs); } /* Parse a standard C++-11 attribute specifier. diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index ab78bbb87c0a..d9a002e36b74 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -12436,10 +12436,11 @@ tsubst_contract (tree decl, tree t, tree args, tsubst_flags_t complain, return r; } -/* Update T instantiating a contract specifier. */ +/* Instantiate the contract specifier CONTRACT, returning the substituted + contract statement. */ -static void -tsubst_contract_specifier (tree decl, tree t, tree args, +static tree +tsubst_contract_specifier (tree decl, tree contract, tree args, tsubst_flags_t complain, tree in_decl) { /* For non-specializations, adjust the current declaration to the most general @@ -12452,9 +12453,6 @@ tsubst_contract_specifier (tree decl, tree t, tree args, local_specialization_stack specs (lss_copy); register_parameter_specializations (in_decl, decl); - /* Get the contract to be instantiated. */ - tree contract = CONTRACT_STATEMENT (t); - /* Use the complete set of template arguments for instantiation. The contract may not have been instantiated and still refer to outer levels of template parameters. */ @@ -12475,28 +12473,36 @@ tsubst_contract_specifier (tree decl, tree t, tree args, current_class_ptr = save_ccp; current_class_ref = save_ccr; - /* Rebuild the attribute. */ - TREE_VALUE (t) = build_tree_list (NULL_TREE, contract); + return contract; } -/* For unsubstituted list of contracts in SPECIFIERS, instantiate contracts - for DECL and set the list as contracts for decl. Substitution creates a deep - copy of the contract. */ +/* For the unsubstituted contract specifiers SPECIFIERS, instantiate the + contracts for DECL and set them as the contracts for DECL. Substitution + creates a deep copy of the contract. */ void -tsubst_contract_specifiers (tree specfiers, tree decl, tree args, +tsubst_contract_specifiers (tree specifiers, tree decl, tree args, tsubst_flags_t complain, tree in_decl) { - tree subst_contract_list = NULL_TREE; - for (tree spec = specfiers; spec; spec = TREE_CHAIN (spec)) + if (!specifiers) { - tree nc = copy_node (spec); - tsubst_contract_specifier (decl, nc, args, complain, in_decl); - TREE_CHAIN (nc) = subst_contract_list; - subst_contract_list = nc; + if (flag_contracts) + set_fn_contract_specifiers (decl, NULL_TREE); + return; } + + /* SPECIFIERS may be shared with the pattern (see the copy in + tsubst_function_decl), so build a fresh vector rather than substituting + in place. tsubst_contract () copies each statement it substitutes. */ + int len = TREE_VEC_LENGTH (specifiers); + tree subst_contracts = make_tree_vec (len); + for (int ix = 0; ix < len; ix++) + TREE_VEC_ELT (subst_contracts, ix) + = tsubst_contract_specifier (decl, TREE_VEC_ELT (specifiers, ix), args, + complain, in_decl); + if (flag_contracts) - set_fn_contract_specifiers (decl, nreverse (subst_contract_list)); + set_fn_contract_specifiers (decl, subst_contracts); } /* Instantiate a single dependent attribute T (a TREE_LIST), and return either