Skip to content

Commit 1ce0b37

Browse files
NinaRannsiains
authored andcommitted
addressing an ICE due to folding of original contracts
Currently, we keep the contracts in attribute trees on the function decl. This causes two issues : 1) when folding the function, the contracts get folded when we fold the attributes. If the contracts are emitted after the function gets folded, they may be folded twice. With the fix, we remove the contracts from the attributes, fold the remaining attributes, then apply contracts again. 2) additionally, the same contract tree can get emitted in multiple functions. For example, with '-fno-contract-checks-outlined' in case of a virtual function, they get emitted in the body of the function and also at the call site. At the moment, we emit the same contract check statement in both places. With the fix, we make a copy of the contract statement when emitting a contract assertion so any changes to the contract statements does not affect later emits.
1 parent 19bb027 commit 1ce0b37

File tree

5 files changed

+132
-38
lines changed

5 files changed

+132
-38
lines changed

gcc/cp/contracts.cc

Lines changed: 76 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -2651,26 +2651,6 @@ emit_contract_attr (tree attr)
26512651
emit_contract_statement (CONTRACT_STATEMENT (attr));
26522652
}
26532653

2654-
/* Add the statements of contract attributes ATTRS of the type specified
2655-
with CODE to the current block. */
2656-
2657-
static void
2658-
emit_contract_conditions (tree attrs, tree_code code)
2659-
{
2660-
if (!attrs) return;
2661-
gcc_checking_assert (TREE_CODE (attrs) == TREE_LIST);
2662-
gcc_checking_assert (code == PRECONDITION_STMT
2663-
|| code == POSTCONDITION_STMT);
2664-
for (attrs = find_contract (attrs); attrs;
2665-
attrs = CONTRACT_CHAIN (attrs))
2666-
{
2667-
tree contract = CONTRACT_STATEMENT (attrs);
2668-
if (TREE_CODE (contract) != code)
2669-
continue;
2670-
emit_contract_attr (attrs);
2671-
}
2672-
}
2673-
26742654
/* Emit the statement for an assertion attribute. */
26752655

26762656
void
@@ -2691,22 +2671,6 @@ emit_assertion (tree attr)
26912671
}
26922672
}
26932673

2694-
/* Emit statements for precondition attributes. */
2695-
2696-
static void
2697-
emit_preconditions (tree attr)
2698-
{
2699-
return emit_contract_conditions (attr, PRECONDITION_STMT);
2700-
}
2701-
2702-
/* Emit statements for postcondition attributes. */
2703-
2704-
static void
2705-
emit_postconditions (tree attr)
2706-
{
2707-
return emit_contract_conditions (attr, POSTCONDITION_STMT);
2708-
}
2709-
27102674
/* We're compiling the pre/postcondition function CONDFN; remap any FN
27112675
attributes that match CODE and emit them. */
27122676

@@ -3010,6 +2974,72 @@ add_post_condition_fn_call (tree fndecl)
30102974
finish_expr_stmt (call);
30112975
}
30122976

2977+
/* Returns a copy of FNDECL contracts. This is used when emiting a contract.
2978+
If we were to emit the original contract tree, any folding of the contract
2979+
condition would affect the original contract too. The original contract
2980+
tree needs to be preserved in case it is used to apply to a different
2981+
function (for inheritance or wrapping reasons). */
2982+
2983+
tree
2984+
copy_contracts (tree fndecl, contract_match_kind remap_kind = cmk_all )
2985+
{
2986+
tree last = NULL_TREE, contract_attrs = NULL_TREE;
2987+
for (tree a = DECL_CONTRACTS (fndecl);
2988+
a != NULL_TREE;
2989+
a = CONTRACT_CHAIN (a))
2990+
{
2991+
if ((remap_kind == cmk_pre
2992+
&& (TREE_CODE (CONTRACT_STATEMENT (a)) == POSTCONDITION_STMT))
2993+
|| (remap_kind == cmk_post
2994+
&& (TREE_CODE (CONTRACT_STATEMENT (a)) == PRECONDITION_STMT)))
2995+
continue;
2996+
2997+
tree c = copy_node (a);
2998+
TREE_VALUE (c) = build_tree_list (TREE_PURPOSE (TREE_VALUE (c)),
2999+
copy_node (CONTRACT_STATEMENT (c)));
3000+
3001+
copy_body_data id;
3002+
hash_map<tree, tree> decl_map;
3003+
3004+
memset (&id, 0, sizeof (id));
3005+
3006+
id.src_fn = fndecl;
3007+
id.dst_fn = fndecl;
3008+
id.src_cfun = DECL_STRUCT_FUNCTION (fndecl);
3009+
id.decl_map = &decl_map;
3010+
3011+
id.copy_decl = retain_decl;
3012+
3013+
id.transform_call_graph_edges = CB_CGE_DUPLICATE;
3014+
id.transform_new_cfg = false;
3015+
id.transform_return_to_modify = false;
3016+
id.transform_parameter = true;
3017+
3018+
/* Make sure not to unshare trees behind the front-end's back
3019+
since front-end specific mechanisms may rely on sharing. */
3020+
id.regimplify = false;
3021+
id.do_not_unshare = true;
3022+
id.do_not_fold = true;
3023+
3024+
/* We're not inside any EH region. */
3025+
id.eh_lp_nr = 0;
3026+
walk_tree (&CONTRACT_CONDITION (CONTRACT_STATEMENT (c)),
3027+
copy_tree_body_r, &id, NULL);
3028+
3029+
3030+
CONTRACT_COMMENT (CONTRACT_STATEMENT (c))
3031+
= copy_node (CONTRACT_COMMENT (CONTRACT_STATEMENT (c)));
3032+
3033+
chainon (last, c);
3034+
last = c;
3035+
if (!contract_attrs)
3036+
contract_attrs = c;
3037+
}
3038+
3039+
return contract_attrs;
3040+
}
3041+
3042+
30133043
/* Add a call or a direct evaluation of the pre checks. */
30143044

30153045
static void
@@ -3018,7 +3048,11 @@ apply_preconditions (tree fndecl)
30183048
if (outline_contracts_p (fndecl))
30193049
add_pre_condition_fn_call (fndecl);
30203050
else
3021-
emit_preconditions (DECL_CONTRACTS (fndecl));
3051+
{
3052+
tree contract_copy = copy_contracts (fndecl, cmk_pre);
3053+
for (; contract_copy; contract_copy = CONTRACT_CHAIN (contract_copy))
3054+
emit_contract_attr (contract_copy);
3055+
}
30223056
}
30233057

30243058
/* Add a call or a direct evaluation of the post checks. */
@@ -3029,7 +3063,11 @@ apply_postconditions (tree fndecl)
30293063
if (outline_contracts_p (fndecl))
30303064
add_post_condition_fn_call (fndecl);
30313065
else
3032-
emit_postconditions (DECL_CONTRACTS (fndecl));
3066+
{
3067+
tree contract_copy = copy_contracts (fndecl, cmk_post);
3068+
for (; contract_copy; contract_copy = CONTRACT_CHAIN (contract_copy))
3069+
emit_contract_attr (contract_copy);
3070+
}
30333071
}
30343072

30353073
/* Add contract handling to the function in FNDECL.

gcc/cp/cp-gimplify.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1533,7 +1533,15 @@ cp_fold_function (tree fndecl)
15331533
&data, NULL);
15341534
data.pset.empty ();
15351535
}
1536+
1537+
/* The attribute tree contains the original contracts which could be emitted
1538+
in multiple locations (e.g. caller side or definition side). They may also
1539+
be copied onto different functions. We do not want to fold contract trees
1540+
at this point in time. They will get folded when they are emitted.
1541+
*/
1542+
tree contracts = extract_contract_attributes (fndecl);
15361543
cp_walk_tree (&DECL_SAVED_TREE (fndecl), cp_fold_r, &data, NULL);
1544+
set_contract_attributes (fndecl, contracts);
15371545

15381546
/* This is merely an optimization: if FNDECL has no i-e expressions,
15391547
we'll not save c_f_d, and we can safely say that FNDECL will not

gcc/cp/cp-tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9020,6 +9020,7 @@ extern void check_param_in_redecl (tree, tree);
90209020
extern tree view_as_const (tree);
90219021
extern tree maybe_contract_wrap_call (tree, tree);
90229022
extern bool emit_contract_wrapper_func (bool);
9023+
extern tree extract_contract_attributes (tree);
90239024
extern void set_contract_attributes (tree, tree);
90249025

90259026
/* Return the first contract in ATTRS, or NULL_TREE if there are none. */
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Test that there is no ICE with outlined contracts, caller side checks and Nontrivial types
2+
// in precondition checks
3+
// { dg-do compile }
4+
// { dg-options "-std=c++2b -fcontracts -fcontracts-nonattr -fno-contract-checks-outlined -fcontracts-nonattr-client-check=all" }
5+
struct NonTrivial{
6+
NonTrivial(){};
7+
NonTrivial(const NonTrivial&){}
8+
~NonTrivial(){};
9+
int x = 0;
10+
};
11+
12+
void f(const NonTrivial s) pre(s.x >0);
13+
14+
void f(const NonTrivial g) {};
15+
16+
17+
int main()
18+
{
19+
NonTrivial nt;
20+
f(nt);
21+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Test that there is no ICE with outlined contracts on a virtual function with Nontrivial types
2+
// in precondition checks
3+
// { dg-do compile }
4+
// { dg-options "-std=c++2b -fcontracts -fcontracts-nonattr -fno-contract-checks-outlined -fcontracts-on-virtual-functions=P2900R13" }
5+
struct NonTrivial{
6+
NonTrivial(){};
7+
NonTrivial(const NonTrivial&){}
8+
~NonTrivial(){};
9+
int x = 0;
10+
};
11+
12+
struct S
13+
{
14+
15+
virtual void f(const NonTrivial s) pre(s.x >0 );
16+
17+
};
18+
19+
void S::f(const NonTrivial g) pre(g.x >0 ){};
20+
21+
int main()
22+
{
23+
NonTrivial nt;
24+
S s;
25+
s.f(nt);
26+
}

0 commit comments

Comments
 (0)