Skip to content

Commit b777fc7

Browse files
committed
c++, contracts: Update duplicate_contracts().
This updates the logic of duplicate_contracts() in preparation for deferring parsing. Signed-off-by: Iain Sandoe <[email protected]>
1 parent 0c545ca commit b777fc7

File tree

1 file changed

+116
-53
lines changed

1 file changed

+116
-53
lines changed

gcc/cp/contracts.cc

Lines changed: 116 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -931,6 +931,27 @@ tree splice_out_contracts (tree attributes)
931931
return head;
932932
}
933933

934+
/* Extract comtract attributes, leaving none in the original function decl. */
935+
936+
tree
937+
extract_contract_attributes (tree fndecl)
938+
{
939+
if (!DECL_CONTRACTS (fndecl))
940+
return NULL_TREE;
941+
tree contracts = NULL_TREE;
942+
tree other = NULL_TREE;
943+
for (tree a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
944+
{
945+
tree list = tree_cons (TREE_PURPOSE (a), TREE_VALUE (a), NULL_TREE);
946+
if (cxx_contract_attribute_p (a))
947+
contracts = chainon (contracts, list);
948+
else
949+
other = chainon (other, list);
950+
}
951+
DECL_ATTRIBUTES (fndecl) = other;
952+
return contracts;
953+
}
954+
934955
/* Copy contract attributes from NEWDECL onto the attribute list of OLDDECL. */
935956

936957
void copy_contract_attributes (tree olddecl, tree newdecl)
@@ -1087,8 +1108,7 @@ remap_contracts (tree src, tree dst, tree contracts, bool duplicate_p)
10871108
{
10881109
for (tree attr = contracts; attr; attr = CONTRACT_CHAIN (attr))
10891110
{
1090-
if (!cxx_contract_attribute_p (attr))
1091-
continue;
1111+
gcc_checking_assert (cxx_contract_attribute_p (attr));
10921112
tree contract = CONTRACT_STATEMENT (attr);
10931113
if (TREE_CODE (CONTRACT_CONDITION (contract)) != DEFERRED_PARSE)
10941114
remap_contract (src, dst, contract, duplicate_p);
@@ -1191,11 +1211,7 @@ check_for_mismatched_contracts (tree old_attr, tree new_attr,
11911211

11921212
/* A deferred contract tentatively matches. */
11931213
if (CONTRACT_CONDITION_DEFERRED_P (new_contract))
1194-
{
1195-
/* If one is deferred, the other better be. */
1196-
// gcc_checking_assert (CONTRACT_CONDITION_DEFERRED_P (old_contract));
1197-
return false;
1198-
}
1214+
return false;
11991215

12001216
/* Compare the conditions of the contracts. We fold immediately to avoid
12011217
issues comparing contracts on overrides that use parameters -- see
@@ -3020,57 +3036,105 @@ finish_function_contracts (tree fndecl)
30203036
}
30213037
}
30223038

3039+
static location_t
3040+
get_contract_end_loc (tree contracts)
3041+
{
3042+
tree last = NULL_TREE;
3043+
for (tree a = contracts; a; a = TREE_CHAIN (a))
3044+
last = a;
3045+
gcc_checking_assert (last);
3046+
last = CONTRACT_STATEMENT (last);
3047+
/* FIXME: We should have a location including the condition, but maybe it is
3048+
not available here. */
3049+
#if 0
3050+
if ((TREE_CODE (last) == POSTCONDITION_STMT)
3051+
|| (TREE_CODE (last) == PRECONDITION_STMT))
3052+
last = CONTRACT_CONDITION (last);
3053+
#endif
3054+
return EXPR_LOCATION (last);
3055+
}
3056+
3057+
struct contract_redecl
3058+
{
3059+
tree original_contracts;
3060+
location_t note_loc;
3061+
tree pending_list;
3062+
};
3063+
3064+
static hash_map<tree_decl_hash, contract_redecl> redeclared_contracts;
3065+
3066+
/* This is called from push decl, and is used to determine if two sets of
3067+
contract attributes match. */
30233068
void
3024-
p2900_duplicate_contracts (location_t new_loc, tree new_contracts, tree newdecl,
3025-
location_t old_loc, tree old_contracts, tree olddecl)
3069+
p2900_duplicate_contracts (tree newdecl, tree olddecl)
30263070
{
3071+
/* Aggregate the contracts and strip them from the input decls. */
3072+
tree new_contracts = extract_contract_attributes (newdecl);
3073+
tree old_contracts = extract_contract_attributes (olddecl);
3074+
3075+
if (!old_contracts && !new_contracts)
3076+
return;
3077+
3078+
location_t old_loc = DECL_SOURCE_LOCATION (olddecl);
3079+
location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
3080+
3081+
/* We should always be comparing with the 'first' declaration - however re-
3082+
declarations are merged in, so keep a record of the first one. This will
3083+
also be needed when we process deferred contracts. */
3084+
bool existed = false;
3085+
contract_redecl& rd = redeclared_contracts.get_or_insert (olddecl, &existed);
3086+
if (!existed)
3087+
{
3088+
rd.original_contracts = old_contracts;
3089+
location_t cont_end = old_loc;
3090+
if (old_contracts)
3091+
cont_end = get_contract_end_loc (old_contracts);
3092+
rd.note_loc = make_location (old_loc, old_loc, cont_end);
3093+
}
3094+
30273095
if (new_contracts && !old_contracts)
30283096
{
30293097
auto_diagnostic_group d;
3030-
/* In P2900, virtual functions do not inherit the contracts.
3031-
Also, if a function has contracts, they must appear on the
3032-
first declaration */
3033-
error_at (new_loc, "declaration adds contracts to %q#D", olddecl);
3034-
inform (DECL_SOURCE_LOCATION (olddecl), "previous definition here");
3035-
remove_contract_attributes (newdecl);
3098+
/* If a re-declaration has contracts, they must be the same as those that
3099+
appear on the first declaration seen (they cannot be added). */
3100+
location_t cont_end = get_contract_end_loc (new_contracts);
3101+
cont_end = make_location (new_loc, new_loc, cont_end);
3102+
error_at (cont_end, "declaration adds contracts to %q#D", olddecl);
3103+
inform (rd.note_loc , "first declared here");
3104+
/* We have stripped the contracts from the new decl, so that they will
3105+
not be merged into the original decl (which had none). */
30363106
return;
30373107
}
30383108

3109+
/* If have now parsed deferred contracts for the 'first' decl, update the
3110+
saved record. */
3111+
if (rd.original_contracts
3112+
&& contract_any_deferred_p (rd.original_contracts)
3113+
&& old_contracts
3114+
&& !contract_any_deferred_p (old_contracts))
3115+
rd.original_contracts = old_contracts;
3116+
30393117
if (old_contracts && !new_contracts)
3118+
/* We allow re-declarations to omit contracts declared on the initial decl.
3119+
In fact, this is required if the conditions contain lambdas. */
3120+
;
3121+
else if (contract_any_deferred_p (new_contracts))
3122+
/* TODO: stash these and figure out how to process them later. */
3123+
;
3124+
else
30403125
{
3041-
/* duplicate_decls () will overwrite (or merge) the attributes of
3042-
old_decl with those of newdecl, so copy the contracts from old to
3043-
new. */
3044-
if (!DECL_ATTRIBUTES (newdecl))
3045-
{
3046-
DECL_ATTRIBUTES (newdecl) = old_contracts;
3047-
old_contracts = CONTRACT_CHAIN (olddecl);
3048-
}
3049-
for (; old_contracts; old_contracts = CONTRACT_CHAIN (olddecl))
3050-
DECL_ATTRIBUTES (newdecl)
3051-
= attr_chainon (DECL_ATTRIBUTES (newdecl), old_contracts);
3052-
remove_contract_attributes (olddecl);
3053-
return;
3126+
location_t cont_end = get_contract_end_loc (new_contracts);
3127+
cont_end = make_location (new_loc, new_loc, cont_end);
3128+
/* We have two sets - they should match or we issue a diagnostic. */
3129+
match_contract_conditions (rd.note_loc, rd.original_contracts,
3130+
cont_end, new_contracts, cmc_declaration);
30543131
}
30553132

3056-
/* So either they match or we should issue a diagnostic. */
3057-
if (!match_contract_conditions (old_loc, old_contracts,
3058-
new_loc, new_contracts, cmc_declaration))
3059-
{
3060-
/* We've issued a diagnostic - but because of the behaviour mentioned
3061-
above, we need to copy the contracts from old -> new, otherwise any
3062-
following comparisons and therefore diagnostics will be bogus. */
3063-
remove_contract_attributes (newdecl);
3064-
if (!DECL_ATTRIBUTES (newdecl))
3065-
{
3066-
DECL_ATTRIBUTES (newdecl) = old_contracts;
3067-
old_contracts = CONTRACT_CHAIN (olddecl);
3068-
}
3069-
for (; old_contracts; old_contracts = CONTRACT_CHAIN (olddecl))
3070-
DECL_ATTRIBUTES (newdecl)
3071-
= attr_chainon (DECL_ATTRIBUTES (newdecl), old_contracts);
3072-
}
3073-
remove_contract_attributes (olddecl);
3133+
/* We have maybe issued a diagnostic - but because the caller will smash the
3134+
attributes on the old decl with those on the new, we need to copy the old
3135+
ones onto the new. */
3136+
DECL_ATTRIBUTES (newdecl)
3137+
= attr_chainon (DECL_ATTRIBUTES (newdecl), old_contracts);
30743138
return;
30753139
}
30763140

@@ -3085,6 +3149,12 @@ duplicate_contracts (tree newdecl, tree olddecl)
30853149
if (TREE_CODE (olddecl) == TEMPLATE_DECL)
30863150
olddecl = DECL_TEMPLATE_RESULT (olddecl);
30873151

3152+
if (flag_contracts_nonattr)
3153+
{
3154+
p2900_duplicate_contracts (newdecl, olddecl);
3155+
return;
3156+
}
3157+
30883158
/* Compare contracts to see if they match. */
30893159
tree old_contracts = DECL_CONTRACTS (olddecl);
30903160
tree new_contracts = DECL_CONTRACTS (newdecl);
@@ -3095,13 +3165,6 @@ duplicate_contracts (tree newdecl, tree olddecl)
30953165
location_t old_loc = DECL_SOURCE_LOCATION (olddecl);
30963166
location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
30973167

3098-
if (flag_contracts_nonattr)
3099-
{
3100-
p2900_duplicate_contracts (new_loc, new_contracts, newdecl,
3101-
old_loc, old_contracts, olddecl);
3102-
return;
3103-
}
3104-
31053168
/* If both declarations specify contracts, ensure they match.
31063169
31073170
TODO: This handles a potential error a little oddly. Consider:

0 commit comments

Comments
 (0)