@@ -932,6 +932,27 @@ tree splice_out_contracts (tree attributes)
932
932
return head;
933
933
}
934
934
935
+ /* Extract comtract attributes, leaving none in the original function decl. */
936
+
937
+ tree
938
+ extract_contract_attributes (tree fndecl)
939
+ {
940
+ if (!DECL_CONTRACTS (fndecl))
941
+ return NULL_TREE;
942
+ tree contracts = NULL_TREE;
943
+ tree other = NULL_TREE;
944
+ for (tree a = DECL_ATTRIBUTES (fndecl); a; a = TREE_CHAIN (a))
945
+ {
946
+ tree list = tree_cons (TREE_PURPOSE (a), TREE_VALUE (a), NULL_TREE);
947
+ if (cxx_contract_attribute_p (a))
948
+ contracts = chainon (contracts, list);
949
+ else
950
+ other = chainon (other, list);
951
+ }
952
+ DECL_ATTRIBUTES (fndecl) = other;
953
+ return contracts;
954
+ }
955
+
935
956
/* Copy contract attributes from NEWDECL onto the attribute list of OLDDECL. */
936
957
937
958
void copy_contract_attributes (tree olddecl, tree newdecl)
@@ -1088,8 +1109,7 @@ remap_contracts (tree src, tree dst, tree contracts, bool duplicate_p)
1088
1109
{
1089
1110
for (tree attr = contracts; attr; attr = CONTRACT_CHAIN (attr))
1090
1111
{
1091
- if (!cxx_contract_attribute_p (attr))
1092
- continue ;
1112
+ gcc_checking_assert (cxx_contract_attribute_p (attr));
1093
1113
tree contract = CONTRACT_STATEMENT (attr);
1094
1114
if (TREE_CODE (CONTRACT_CONDITION (contract)) != DEFERRED_PARSE)
1095
1115
remap_contract (src, dst, contract, duplicate_p);
@@ -1192,11 +1212,7 @@ check_for_mismatched_contracts (tree old_attr, tree new_attr,
1192
1212
1193
1213
/* A deferred contract tentatively matches. */
1194
1214
if (CONTRACT_CONDITION_DEFERRED_P (new_contract))
1195
- {
1196
- /* If one is deferred, the other better be. */
1197
- // gcc_checking_assert (CONTRACT_CONDITION_DEFERRED_P (old_contract));
1198
- return false ;
1199
- }
1215
+ return false ;
1200
1216
1201
1217
/* Compare the conditions of the contracts. We fold immediately to avoid
1202
1218
issues comparing contracts on overrides that use parameters -- see
@@ -3021,57 +3037,105 @@ finish_function_contracts (tree fndecl)
3021
3037
}
3022
3038
}
3023
3039
3040
+ static location_t
3041
+ get_contract_end_loc (tree contracts)
3042
+ {
3043
+ tree last = NULL_TREE;
3044
+ for (tree a = contracts; a; a = TREE_CHAIN (a))
3045
+ last = a;
3046
+ gcc_checking_assert (last);
3047
+ last = CONTRACT_STATEMENT (last);
3048
+ /* FIXME: We should have a location including the condition, but maybe it is
3049
+ not available here. */
3050
+ #if 0
3051
+ if ((TREE_CODE (last) == POSTCONDITION_STMT)
3052
+ || (TREE_CODE (last) == PRECONDITION_STMT))
3053
+ last = CONTRACT_CONDITION (last);
3054
+ #endif
3055
+ return EXPR_LOCATION (last);
3056
+ }
3057
+
3058
+ struct contract_redecl
3059
+ {
3060
+ tree original_contracts;
3061
+ location_t note_loc;
3062
+ tree pending_list;
3063
+ };
3064
+
3065
+ static hash_map<tree_decl_hash, contract_redecl> redeclared_contracts;
3066
+
3067
+ /* This is called from push decl, and is used to determine if two sets of
3068
+ contract attributes match. */
3024
3069
void
3025
- p2900_duplicate_contracts (location_t new_loc, tree new_contracts, tree newdecl,
3026
- location_t old_loc, tree old_contracts, tree olddecl)
3070
+ p2900_duplicate_contracts (tree newdecl, tree olddecl)
3027
3071
{
3072
+ /* Aggregate the contracts and strip them from the input decls. */
3073
+ tree new_contracts = extract_contract_attributes (newdecl);
3074
+ tree old_contracts = extract_contract_attributes (olddecl);
3075
+
3076
+ if (!old_contracts && !new_contracts)
3077
+ return ;
3078
+
3079
+ location_t old_loc = DECL_SOURCE_LOCATION (olddecl);
3080
+ location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
3081
+
3082
+ /* We should always be comparing with the 'first' declaration - however re-
3083
+ declarations are merged in, so keep a record of the first one. This will
3084
+ also be needed when we process deferred contracts. */
3085
+ bool existed = false ;
3086
+ contract_redecl& rd = redeclared_contracts.get_or_insert (olddecl, &existed);
3087
+ if (!existed)
3088
+ {
3089
+ rd.original_contracts = old_contracts;
3090
+ location_t cont_end = old_loc;
3091
+ if (old_contracts)
3092
+ cont_end = get_contract_end_loc (old_contracts);
3093
+ rd.note_loc = make_location (old_loc, old_loc, cont_end);
3094
+ }
3095
+
3028
3096
if (new_contracts && !old_contracts)
3029
3097
{
3030
3098
auto_diagnostic_group d;
3031
- /* In P2900, virtual functions do not inherit the contracts.
3032
- Also, if a function has contracts, they must appear on the
3033
- first declaration */
3034
- error_at (new_loc, " declaration adds contracts to %q#D" , olddecl);
3035
- inform (DECL_SOURCE_LOCATION (olddecl), " previous definition here" );
3036
- remove_contract_attributes (newdecl);
3099
+ /* If a re-declaration has contracts, they must be the same as those that
3100
+ appear on the first declaration seen (they cannot be added). */
3101
+ location_t cont_end = get_contract_end_loc (new_contracts);
3102
+ cont_end = make_location (new_loc, new_loc, cont_end);
3103
+ error_at (cont_end, " declaration adds contracts to %q#D" , olddecl);
3104
+ inform (rd.note_loc , " first declared here" );
3105
+ /* We have stripped the contracts from the new decl, so that they will
3106
+ not be merged into the original decl (which had none). */
3037
3107
return ;
3038
3108
}
3039
3109
3110
+ /* If have now parsed deferred contracts for the 'first' decl, update the
3111
+ saved record. */
3112
+ if (rd.original_contracts
3113
+ && contract_any_deferred_p (rd.original_contracts )
3114
+ && old_contracts
3115
+ && !contract_any_deferred_p (old_contracts))
3116
+ rd.original_contracts = old_contracts;
3117
+
3040
3118
if (old_contracts && !new_contracts)
3119
+ /* We allow re-declarations to omit contracts declared on the initial decl.
3120
+ In fact, this is required if the conditions contain lambdas. */
3121
+ ;
3122
+ else if (contract_any_deferred_p (new_contracts))
3123
+ /* TODO: stash these and figure out how to process them later. */
3124
+ ;
3125
+ else
3041
3126
{
3042
- /* duplicate_decls () will overwrite (or merge) the attributes of
3043
- old_decl with those of newdecl, so copy the contracts from old to
3044
- new. */
3045
- if (!DECL_ATTRIBUTES (newdecl))
3046
- {
3047
- DECL_ATTRIBUTES (newdecl) = old_contracts;
3048
- old_contracts = CONTRACT_CHAIN (olddecl);
3049
- }
3050
- for (; old_contracts; old_contracts = CONTRACT_CHAIN (olddecl))
3051
- DECL_ATTRIBUTES (newdecl)
3052
- = attr_chainon (DECL_ATTRIBUTES (newdecl), old_contracts);
3053
- remove_contract_attributes (olddecl);
3054
- return ;
3127
+ location_t cont_end = get_contract_end_loc (new_contracts);
3128
+ cont_end = make_location (new_loc, new_loc, cont_end);
3129
+ /* We have two sets - they should match or we issue a diagnostic. */
3130
+ match_contract_conditions (rd.note_loc , rd.original_contracts ,
3131
+ cont_end, new_contracts, cmc_declaration);
3055
3132
}
3056
3133
3057
- /* So either they match or we should issue a diagnostic. */
3058
- if (!match_contract_conditions (old_loc, old_contracts,
3059
- new_loc, new_contracts, cmc_declaration))
3060
- {
3061
- /* We've issued a diagnostic - but because of the behaviour mentioned
3062
- above, we need to copy the contracts from old -> new, otherwise any
3063
- following comparisons and therefore diagnostics will be bogus. */
3064
- remove_contract_attributes (newdecl);
3065
- if (!DECL_ATTRIBUTES (newdecl))
3066
- {
3067
- DECL_ATTRIBUTES (newdecl) = old_contracts;
3068
- old_contracts = CONTRACT_CHAIN (olddecl);
3069
- }
3070
- for (; old_contracts; old_contracts = CONTRACT_CHAIN (olddecl))
3071
- DECL_ATTRIBUTES (newdecl)
3072
- = attr_chainon (DECL_ATTRIBUTES (newdecl), old_contracts);
3073
- }
3074
- remove_contract_attributes (olddecl);
3134
+ /* We have maybe issued a diagnostic - but because the caller will smash the
3135
+ attributes on the old decl with those on the new, we need to copy the old
3136
+ ones onto the new. */
3137
+ DECL_ATTRIBUTES (newdecl)
3138
+ = attr_chainon (DECL_ATTRIBUTES (newdecl), old_contracts);
3075
3139
return ;
3076
3140
}
3077
3141
@@ -3086,6 +3150,12 @@ duplicate_contracts (tree newdecl, tree olddecl)
3086
3150
if (TREE_CODE (olddecl) == TEMPLATE_DECL)
3087
3151
olddecl = DECL_TEMPLATE_RESULT (olddecl);
3088
3152
3153
+ if (flag_contracts_nonattr)
3154
+ {
3155
+ p2900_duplicate_contracts (newdecl, olddecl);
3156
+ return ;
3157
+ }
3158
+
3089
3159
/* Compare contracts to see if they match. */
3090
3160
tree old_contracts = DECL_CONTRACTS (olddecl);
3091
3161
tree new_contracts = DECL_CONTRACTS (newdecl);
@@ -3096,13 +3166,6 @@ duplicate_contracts (tree newdecl, tree olddecl)
3096
3166
location_t old_loc = DECL_SOURCE_LOCATION (olddecl);
3097
3167
location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
3098
3168
3099
- if (flag_contracts_nonattr)
3100
- {
3101
- p2900_duplicate_contracts (new_loc, new_contracts, newdecl,
3102
- old_loc, old_contracts, olddecl);
3103
- return ;
3104
- }
3105
-
3106
3169
/* If both declarations specify contracts, ensure they match.
3107
3170
3108
3171
TODO: This handles a potential error a little oddly. Consider:
0 commit comments