@@ -931,6 +931,27 @@ tree splice_out_contracts (tree attributes)
931
931
return head;
932
932
}
933
933
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
+
934
955
/* Copy contract attributes from NEWDECL onto the attribute list of OLDDECL. */
935
956
936
957
void copy_contract_attributes (tree olddecl, tree newdecl)
@@ -1087,8 +1108,7 @@ remap_contracts (tree src, tree dst, tree contracts, bool duplicate_p)
1087
1108
{
1088
1109
for (tree attr = contracts; attr; attr = CONTRACT_CHAIN (attr))
1089
1110
{
1090
- if (!cxx_contract_attribute_p (attr))
1091
- continue ;
1111
+ gcc_checking_assert (cxx_contract_attribute_p (attr));
1092
1112
tree contract = CONTRACT_STATEMENT (attr);
1093
1113
if (TREE_CODE (CONTRACT_CONDITION (contract)) != DEFERRED_PARSE)
1094
1114
remap_contract (src, dst, contract, duplicate_p);
@@ -1191,11 +1211,7 @@ check_for_mismatched_contracts (tree old_attr, tree new_attr,
1191
1211
1192
1212
/* A deferred contract tentatively matches. */
1193
1213
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 ;
1199
1215
1200
1216
/* Compare the conditions of the contracts. We fold immediately to avoid
1201
1217
issues comparing contracts on overrides that use parameters -- see
@@ -3020,57 +3036,105 @@ finish_function_contracts (tree fndecl)
3020
3036
}
3021
3037
}
3022
3038
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. */
3023
3068
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)
3026
3070
{
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
+
3027
3095
if (new_contracts && !old_contracts)
3028
3096
{
3029
3097
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). */
3036
3106
return ;
3037
3107
}
3038
3108
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
+
3039
3117
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
3040
3125
{
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);
3054
3131
}
3055
3132
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);
3074
3138
return ;
3075
3139
}
3076
3140
@@ -3085,6 +3149,12 @@ duplicate_contracts (tree newdecl, tree olddecl)
3085
3149
if (TREE_CODE (olddecl) == TEMPLATE_DECL)
3086
3150
olddecl = DECL_TEMPLATE_RESULT (olddecl);
3087
3151
3152
+ if (flag_contracts_nonattr)
3153
+ {
3154
+ p2900_duplicate_contracts (newdecl, olddecl);
3155
+ return ;
3156
+ }
3157
+
3088
3158
/* Compare contracts to see if they match. */
3089
3159
tree old_contracts = DECL_CONTRACTS (olddecl);
3090
3160
tree new_contracts = DECL_CONTRACTS (newdecl);
@@ -3095,13 +3165,6 @@ duplicate_contracts (tree newdecl, tree olddecl)
3095
3165
location_t old_loc = DECL_SOURCE_LOCATION (olddecl);
3096
3166
location_t new_loc = DECL_SOURCE_LOCATION (newdecl);
3097
3167
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
-
3105
3168
/* If both declarations specify contracts, ensure they match.
3106
3169
3107
3170
TODO: This handles a potential error a little oddly. Consider:
0 commit comments