Skip to content

Commit 026e87f

Browse files
Merge pull request #50 from iains/contracts-nonattr-keep-first-contract
Contracts nonattr keep first contract (and fix a wrapper generation ICE)
2 parents 843e50f + c7b2f88 commit 026e87f

File tree

8 files changed

+131
-67
lines changed

8 files changed

+131
-67
lines changed

gcc/cp/contracts.cc

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

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+
935956
/* Copy contract attributes from NEWDECL onto the attribute list of OLDDECL. */
936957

937958
void copy_contract_attributes (tree olddecl, tree newdecl)
@@ -1088,8 +1109,7 @@ remap_contracts (tree src, tree dst, tree contracts, bool duplicate_p)
10881109
{
10891110
for (tree attr = contracts; attr; attr = CONTRACT_CHAIN (attr))
10901111
{
1091-
if (!cxx_contract_attribute_p (attr))
1092-
continue;
1112+
gcc_checking_assert (cxx_contract_attribute_p (attr));
10931113
tree contract = CONTRACT_STATEMENT (attr);
10941114
if (TREE_CODE (CONTRACT_CONDITION (contract)) != DEFERRED_PARSE)
10951115
remap_contract (src, dst, contract, duplicate_p);
@@ -1192,11 +1212,7 @@ check_for_mismatched_contracts (tree old_attr, tree new_attr,
11921212

11931213
/* A deferred contract tentatively matches. */
11941214
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;
12001216

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

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. */
30243069
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)
30273071
{
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+
30283096
if (new_contracts && !old_contracts)
30293097
{
30303098
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). */
30373107
return;
30383108
}
30393109

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+
30403118
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
30413126
{
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);
30553132
}
30563133

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);
30753139
return;
30763140
}
30773141

@@ -3086,6 +3150,12 @@ duplicate_contracts (tree newdecl, tree olddecl)
30863150
if (TREE_CODE (olddecl) == TEMPLATE_DECL)
30873151
olddecl = DECL_TEMPLATE_RESULT (olddecl);
30883152

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

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-
31063169
/* If both declarations specify contracts, ensure they match.
31073170
31083171
TODO: This handles a potential error a little oddly. Consider:

gcc/testsuite/g++.dg/contracts/cpp26-attr/contracts-friend1.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ int main(int, char**) {
3333
// { dg-output "contract violation in function fn0 at .*.C:7: .*(\n|\r\n|\r)" }
3434
// { dg-output "contract violation in function X::fns0 at .*.C:9: .*(\n|\r\n|\r)" }
3535
// { dg-output "contract violation in function X::fns1 at .*.C:10: .*(\n|\r\n|\r)" }
36-
// { dg-output "contract violation in function X::fns2 at .*.C:19: .*(\n|\r\n|\r)" }
36+
// { dg-output "contract violation in function X::fns2 at .*.C:11: .*(\n|\r\n|\r)" }

gcc/testsuite/g++.dg/contracts/cpp26-attr/contracts-nested-class1.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct Outer {
1313
friend void Inner::fn(int n) [[ pre: n > 0 && bob > 1 ]]; // { dg-error "not declared" }
1414

1515
friend void gfn(int p) [[ pre: p > 0 ]];
16-
friend void gfn(int q) [[ pre: q > 1 ]]; // { dg-error "'q' was not declared" }
16+
friend void gfn(int q) [[ pre: q > 1 ]];
1717

1818
friend void gfn2(int q);
1919
friend void gfn2(int p) [[ pre: p > 0 ]] { } // { dg-error "declaration adds contracts" }

gcc/testsuite/g++.dg/contracts/cpp26-attr/contracts-tmpl-spec2.C

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,9 @@ int main(int, char**)
359359
// { dg-output {contract violation in function G3<double, double>::f at .*:125: s > 0(\n|\r\n|\r)} }
360360
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
361361
// { dg-output {G3 general T S(\n|\r\n|\r)} }
362-
// { dg-output {contract violation in function G3<int, int>::f at .*:139: t > 1(\n|\r\n|\r)} }
362+
// { dg-output {contract violation in function G3<int, int>::f at .*:134: t > 1(\n|\r\n|\r)} }
363363
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
364-
// { dg-output {contract violation in function G3<int, int>::f at .*:140: s > 1(\n|\r\n|\r)} }
364+
// { dg-output {contract violation in function G3<int, int>::f at .*:134: s > 1(\n|\r\n|\r)} }
365365
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
366366
// { dg-output {G3 partial int S(\n|\r\n|\r)} }
367367
// { dg-output {contract violation in function G3<int, double>::f at .*:147: t > 2(\n|\r\n|\r)} }
@@ -374,9 +374,9 @@ int main(int, char**)
374374
// { dg-output {contract violation in function G3<char, char>::f at .*:125: s > 0(\n|\r\n|\r)} }
375375
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
376376
// { dg-output {G3 general T S(\n|\r\n|\r)} }
377-
// { dg-output {contract violation in function G3<int, char>::f at .*:139: t > 1(\n|\r\n|\r)} }
377+
// { dg-output {contract violation in function G3<int, char>::f at .*:134: t > 1(\n|\r\n|\r)} }
378378
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
379-
// { dg-output {contract violation in function G3<int, char>::f at .*:140: s > 1(\n|\r\n|\r)} }
379+
// { dg-output {contract violation in function G3<int, char>::f at .*:134: s > 1(\n|\r\n|\r)} }
380380
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
381381
// { dg-output {G3 partial int S(\n|\r\n|\r)} }
382382
// { dg-output {G3 full int C(\n|\r\n|\r)} }

gcc/testsuite/g++.dg/contracts/cpp26/contracts-friend1.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,4 @@ int main(int, char**) {
3333
// { dg-output "contract violation in function fn0 at .*.C:7: .*(\n|\r\n|\r)" }
3434
// { dg-output "contract violation in function X::fns0 at .*.C:9: .*(\n|\r\n|\r)" }
3535
// { dg-output "contract violation in function X::fns1 at .*.C:10: .*(\n|\r\n|\r)" }
36-
// { dg-output "contract violation in function X::fns2 at .*.C:19: .*(\n|\r\n|\r)" }
36+
// { dg-output "contract violation in function X::fns2 at .*.C:11: .*(\n|\r\n|\r)" }

gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class1.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ struct Outer {
1313
friend void Inner::fn(int n) pre ( n > 0 && bob > 1 ); // { dg-error "not declared" }
1414

1515
friend void gfn(int p) pre ( p > 0 );
16-
friend void gfn(int q) pre ( q > 1 ); // { dg-error "'q' was not declared" }
16+
friend void gfn(int q) pre ( q > 1 );
1717

1818
friend void gfn2(int q);
1919
friend void gfn2(int p) pre ( p > 0 ) { } // { dg-error "declaration adds contracts" }

gcc/testsuite/g++.dg/contracts/cpp26/contracts-tmpl-spec2.C

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -359,9 +359,9 @@ int main(int, char**)
359359
// { dg-output {contract violation in function G3<double, double>::f at .*:125: s > 0(\n|\r\n|\r)} }
360360
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
361361
// { dg-output {G3 general T S(\n|\r\n|\r)} }
362-
// { dg-output {contract violation in function G3<int, int>::f at .*:139: t > 1(\n|\r\n|\r)} }
362+
// { dg-output {contract violation in function G3<int, int>::f at .*:134: t > 1(\n|\r\n|\r)} }
363363
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
364-
// { dg-output {contract violation in function G3<int, int>::f at .*:140: s > 1(\n|\r\n|\r)} }
364+
// { dg-output {contract violation in function G3<int, int>::f at .*:134: s > 1(\n|\r\n|\r)} }
365365
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
366366
// { dg-output {G3 partial int S(\n|\r\n|\r)} }
367367
// { dg-output {contract violation in function G3<int, double>::f at .*:147: t > 2(\n|\r\n|\r)} }
@@ -374,9 +374,9 @@ int main(int, char**)
374374
// { dg-output {contract violation in function G3<char, char>::f at .*:125: s > 0(\n|\r\n|\r)} }
375375
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
376376
// { dg-output {G3 general T S(\n|\r\n|\r)} }
377-
// { dg-output {contract violation in function G3<int, char>::f at .*:139: t > 1(\n|\r\n|\r)} }
377+
// { dg-output {contract violation in function G3<int, char>::f at .*:134: t > 1(\n|\r\n|\r)} }
378378
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
379-
// { dg-output {contract violation in function G3<int, char>::f at .*:140: s > 1(\n|\r\n|\r)} }
379+
// { dg-output {contract violation in function G3<int, char>::f at .*:134: s > 1(\n|\r\n|\r)} }
380380
// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" }
381381
// { dg-output {G3 partial int S(\n|\r\n|\r)} }
382382
// { dg-output {G3 full int C(\n|\r\n|\r)} }

gcc/testsuite/g++.dg/contracts/cpp26/contracts1.C

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,13 @@ PostCond::postcond2(T x) post (x >= 0) // { dg-error "a value parameter used in
9292

9393
template <class T>
9494
void
95-
postcond3(T x) post(x >= 0) // { dg-error "a value parameter used in a postcondition must be const" }
95+
postcond3(T x) post(x >= 0)
9696
{ }
9797

9898
void postcond7()
9999
{
100100
PostCond p;
101101
p.postcond2 (2);
102-
postcond3 (4);
102+
postcond3 (4); // { dg-error "a value parameter used in a postcondition must be const" "" {target *-*-* } 63 }
103+
103104
}

0 commit comments

Comments
 (0)