Skip to content

Commit 4afcda3

Browse files
committed
adding inhritance flags and implicit inheritance
1 parent 8dacc78 commit 4afcda3

File tree

9 files changed

+576
-21
lines changed

9 files changed

+576
-21
lines changed

gcc/c-family/c.opt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,26 @@ fcontracts-nonattr-definition-check=
19681968
C++ Joined RejectNegative Enum(on_off) Var(flag_contracts_nonattr_definition_check) Init(1)
19691969
-fcontracts-nonattr-definition-check=[on|off] Enable or disable contract checks on the definition side for all functions (default on).
19701970

1971+
Enum
1972+
Name(contract_inheritance) Type(int) UnknownError(unrecognized contract inheritance mode option %qs)
1973+
1974+
EnumValue
1975+
Enum(contract_inheritance) String(none) Value(0)
1976+
1977+
EnumValue
1978+
Enum(contract_inheritance) String(P2900R13) Value(1)
1979+
1980+
EnumValue
1981+
Enum(contract_inheritance) String(Ville) Value(2)
1982+
1983+
fcontracts-nonattr-inheritance-mode=
1984+
C++ Joined RejectNegative Enum(contract_inheritance) Var(flag_contract_nonattr_inheritance_mode) Init(1)
1985+
-fcontracts-nonattr-inheritance-mode=[none|P2900R13|Ville] Select how contracts are inherited for virtual functions
1986+
1987+
Wsuggest-explicit-contract
1988+
C++ ObjC++ Var(suggest_explicit_contract) Warning
1989+
Warn about functions with implicitly inherited contracts.
1990+
19711991
fcoroutines
19721992
C++ LTO Var(flag_coroutines)
19731993
Enable C++ coroutines (experimental).

gcc/cp/class.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3202,6 +3202,8 @@ check_for_override (tree decl, tree ctype)
32023202
function. */
32033203
DECL_VINDEX (decl) = decl;
32043204

3205+
check_override_contracts (decl);
3206+
32053207
if (warn_override
32063208
&& !DECL_OVERRIDE_P (decl)
32073209
&& !DECL_FINAL_P (decl)

gcc/cp/contracts.cc

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3065,17 +3065,17 @@ maybe_apply_function_contracts (tree fndecl)
30653065
/* The DECL_SAVED_TREE stmt list will be popped by our caller. */
30663066
}
30673067

3068-
/* Replace any contract attributes on SOURCE with a copy where any
3069-
references to SOURCE's PARM_DECLs have been rewritten to the corresponding
3070-
PARM_DECL in DEST. If remap_result is true, result identifier is
3071-
also re-mapped. C++20 version used this function to remap contracts on
3072-
virtual functions from base class to derived class. In such a case
3073-
postcondition identified wasn't remapped. Caller side wrapper functions
3074-
need to remap the result identifier.
3075-
I remap_post is false, postconditions are dropped from the destination. */
3068+
/* Returns a copy of SOURCE contracts where any references to SOURCE's
3069+
PARM_DECLs have been rewritten to the corresponding PARM_DECL in DEST. If
3070+
remap_result is true, result identifier is also re-mapped.
3071+
C++20 version used this function to remap contracts on virtual functions
3072+
from base class to derived class. In such a case postcondition identifier
3073+
wasn't remapped. Caller side wrapper functions need to remap the result
3074+
identifier.
3075+
If remap_post is false, postconditions are dropped from the destination. */
30763076

3077-
void
3078-
copy_and_remap_contracts (tree dest, tree source, bool remap_result = true,
3077+
tree
3078+
remap_contracts (tree dest, tree source, bool remap_result = true,
30793079
bool remap_post = true )
30803080
{
30813081
tree last = NULL_TREE, contract_attrs = NULL_TREE;
@@ -3111,7 +3111,7 @@ copy_and_remap_contracts (tree dest, tree source, bool remap_result = true,
31113111
contract_attrs = c;
31123112
}
31133113

3114-
set_decl_contracts (dest, contract_attrs);
3114+
return contract_attrs;
31153115
}
31163116

31173117
/* Finish up the pre & post function definitions for a guarded FNDECL,
@@ -3407,7 +3407,10 @@ should_contract_wrap_call (bool do_pre, bool do_post, bool is_virt)
34073407

34083408
/* We always wrap virtual function calls, and non-virtual calls when
34093409
client-side checking is enabled for all contracts. */
3410-
if (is_virt || (flag_contract_nonattr_client_check > 1))
3410+
if ((is_virt
3411+
&& (flag_contract_nonattr_inheritance_mode
3412+
== CONTRACT_INHERITANCE_P2900R13))
3413+
|| (flag_contract_nonattr_client_check > 1))
34113414
return true;
34123415

34133416
/* Otherwise, any function with pre-conditions when selected. */
@@ -3482,11 +3485,16 @@ define_contract_wrapper_func (const tree& fndecl, const tree& wrapdecl, void*)
34823485
/* We check postconditions on virtual function calls or if postcondition
34833486
checks are enabled for all clients. We should not get here unless there
34843487
are some checks to make. */
3485-
bool check_post = (flag_contract_nonattr_client_check > 1) || is_virtual;
3488+
bool check_post
3489+
= (flag_contract_nonattr_client_check > 1)
3490+
|| (is_virtual
3491+
&& (flag_contract_nonattr_inheritance_mode
3492+
== CONTRACT_INHERITANCE_P2900R13));
34863493
/* For wrappers on CDTORs we need to refer to the original contracts,
34873494
when the wrapper is around a clone. */
3488-
copy_and_remap_contracts (wrapdecl, DECL_ORIGIN (fndecl),
3489-
/*remap_result*/true, check_post);
3495+
set_decl_contracts( wrapdecl,
3496+
remap_contracts (wrapdecl, DECL_ORIGIN (fndecl),
3497+
/*remap_result*/true, check_post));
34903498

34913499
start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES*/NULL_TREE,
34923500
SF_DEFAULT | SF_PRE_PARSED);

gcc/cp/contracts.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@ enum contract_semantic
5555
CCS_NOEXCEPT_OBSERVE
5656
};
5757

58+
/* Contract inheritance model. */
59+
60+
enum contract_inheritance
61+
{
62+
CONTRACT_INHERITANCE_NONE = 0,
63+
CONTRACT_INHERITANCE_P2900R13 = 1,
64+
CONTRACT_INHERITANCE_VILLE = 2,
65+
};
66+
5867
/* True if the contract is unchecked. */
5968

6069
inline bool
@@ -340,7 +349,7 @@ extern tree finish_contract_attribute (tree, tree);
340349
extern tree invalidate_contract (tree);
341350
extern tree splice_out_contracts (tree);
342351
extern bool all_attributes_are_contracts_p (tree);
343-
extern void copy_and_remap_contracts (tree, tree, bool, bool);
352+
extern tree remap_contracts (tree, tree, bool, bool);
344353
extern void start_function_contracts (tree);
345354
extern void maybe_apply_function_contracts (tree);
346355
extern void finish_function_contracts (tree);

gcc/cp/cp-tree.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7894,6 +7894,7 @@ extern tree lookup_member (tree, tree, int, bool,
78947894
extern tree lookup_member_fuzzy (tree, tree, bool);
78957895
extern tree locate_field_accessor (tree, tree, bool);
78967896
extern int look_for_overrides (tree, tree);
7897+
extern void check_override_contracts (tree);
78977898
extern void get_pure_virtuals (tree);
78987899
extern void maybe_suppress_debug_info (tree);
78997900
extern void note_debug_info_needed (tree);

gcc/cp/search.cc

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,10 +2193,11 @@ check_final_overrider (tree overrider, tree basefn)
21932193
{
21942194
/* We're inheriting basefn's contracts; create a copy of them but
21952195
replace references to their parms to our parms. */
2196-
copy_and_remap_contracts (overrider,
2197-
basefn,
2198-
/* don't remap result */ false,
2199-
/* remap post conditions */ true);
2196+
set_decl_contracts( overrider,
2197+
remap_contracts (overrider,
2198+
basefn,
2199+
/* don't remap result */ false,
2200+
/* remap post conditions */ true));
22002201
}
22012202
else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
22022203
{
@@ -2209,7 +2210,6 @@ check_final_overrider (tree overrider, tree basefn)
22092210
defer_guarded_contract_match (overrider, basefn, DECL_CONTRACTS (basefn));
22102211
}
22112212
}
2212-
22132213
if (DECL_FINAL_P (basefn))
22142214
{
22152215
auto_diagnostic_group d;
@@ -2221,6 +2221,63 @@ check_final_overrider (tree overrider, tree basefn)
22212221
return 1;
22222222
}
22232223

2224+
/* Given a class TYPE, and a virtual function decl FNDECL, check the direct
2225+
base classes for contracts.
2226+
*/
2227+
2228+
void
2229+
check_override_contracts (tree fndecl)
2230+
{
2231+
2232+
if (!flag_contracts || !flag_contracts_nonattr
2233+
|| flag_contract_nonattr_inheritance_mode != CONTRACT_INHERITANCE_VILLE )
2234+
return;
2235+
2236+
/* A constructor for a class T does not override a function T
2237+
in a base class. */
2238+
if (DECL_CONSTRUCTOR_P (fndecl))
2239+
return;
2240+
2241+
bool explicit_contracts = DECL_HAS_CONTRACTS_P(fndecl);
2242+
tree binfo = TYPE_BINFO (DECL_CONTEXT (fndecl));
2243+
tree base_binfo;
2244+
2245+
for (int i = 0; BINFO_BASE_ITERATE (binfo, i, base_binfo); ++i)
2246+
{
2247+
tree basetype = BINFO_TYPE (base_binfo);
2248+
2249+
if (!TYPE_POLYMORPHIC_P (basetype))
2250+
continue;
2251+
2252+
tree basefn = look_for_overrides_here (basetype, fndecl);
2253+
if (!basefn)
2254+
continue;
2255+
2256+
if (!explicit_contracts && DECL_HAS_CONTRACTS_P(basefn))
2257+
{
2258+
/* We're inheriting basefn's contracts; create a copy of them but
2259+
* replace references to their parms to our parms. */
2260+
tree base_contracts = remap_contracts (
2261+
fndecl, basefn,
2262+
/* don't remap result */false,
2263+
/* remap post conditions */true);
2264+
tree contracts = chainon (DECL_CONTRACTS (fndecl), base_contracts);
2265+
2266+
set_decl_contracts(fndecl, contracts);
2267+
2268+
if (suggest_explicit_contract)
2269+
{
2270+
warning_at (DECL_SOURCE_LOCATION(fndecl),
2271+
suggest_explicit_contract,
2272+
"Function implicitly inherits a contract");
2273+
inform (DECL_SOURCE_LOCATION(basefn),
2274+
"overridden function is %qD", basefn);
2275+
}
2276+
}
2277+
}
2278+
2279+
}
2280+
22242281
/* Given a class TYPE, and a function decl FNDECL, look for
22252282
virtual functions in TYPE's hierarchy which FNDECL overrides.
22262283
We do not look in TYPE itself, only its bases.
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
// test that contracts on overriding functions are found correctly
2+
// { dg-do run }
3+
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=off -fcontracts-nonattr -fcontracts-nonattr-inheritance-mode=none " }
4+
#include <cassert>
5+
6+
struct contract{
7+
int checked = 0;
8+
};
9+
10+
11+
contract a,b,c;
12+
13+
bool checkA(){
14+
a.checked++;
15+
return true;
16+
}
17+
18+
bool checkB(){
19+
b.checked++;
20+
return true;
21+
}
22+
23+
bool checkC(){
24+
c.checked++;
25+
return true;
26+
}
27+
28+
void clear_checks(){
29+
a.checked = b.checked = c.checked = 0;
30+
31+
}
32+
33+
struct Base
34+
{
35+
virtual void f() pre (checkA()){};
36+
};
37+
38+
39+
struct Child0 : Base
40+
{
41+
virtual void f() {}; //no contract
42+
};
43+
44+
struct Child1 : Base
45+
{
46+
virtual void f() pre (checkB()){};
47+
};
48+
49+
50+
struct GChild1 : Child0
51+
{
52+
virtual void f() pre (checkC()){};
53+
};
54+
55+
struct GChild2 : Child1
56+
{
57+
virtual void f() {};
58+
};
59+
60+
61+
void fooBase(Base* b)
62+
{
63+
b->f();
64+
}
65+
66+
int main(int, char**)
67+
{
68+
Base b0;
69+
Child0 c0;
70+
Child1 c1;
71+
GChild1 g1;
72+
GChild2 g2;
73+
74+
75+
clear_checks();
76+
fooBase(&b0);
77+
assert(a.checked > 0);
78+
79+
clear_checks();
80+
fooBase(&c0);
81+
assert(a.checked == 0);
82+
83+
clear_checks();
84+
fooBase(&c1);
85+
assert(a.checked == 0);
86+
assert(b.checked > 0);
87+
88+
89+
clear_checks();
90+
fooBase(&g1);
91+
assert(a.checked == 0);
92+
assert(b.checked == 0);
93+
assert(c.checked > 0);
94+
95+
96+
clear_checks();
97+
fooBase(&g2);
98+
assert(a.checked == 0);
99+
assert(b.checked == 0);
100+
assert(c.checked == 0);
101+
102+
clear_checks();
103+
b0.f();
104+
assert(a.checked > 0);
105+
106+
clear_checks();
107+
c0.f();
108+
assert(a.checked == 0);
109+
110+
clear_checks();
111+
c1.f();
112+
assert(a.checked == 0);
113+
assert(b.checked > 0);
114+
115+
116+
clear_checks();
117+
g1.f();;
118+
assert(a.checked == 0);
119+
assert(b.checked == 0);
120+
assert(c.checked > 0);
121+
122+
123+
clear_checks();
124+
g2.f();
125+
assert(a.checked == 0);
126+
assert(b.checked == 0);
127+
assert(c.checked == 0);
128+
129+
return 0;
130+
}
131+
132+

0 commit comments

Comments
 (0)