Skip to content

Commit a0dfe96

Browse files
Merge pull request gcc-mirror#92 from NinaRanns/contracts_virtual_function2
Contracts virtual function2
2 parents 8dacc78 + 50c56b9 commit a0dfe96

33 files changed

+824
-238
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(P3653) Value(2)
1982+
1983+
fcontracts-nonattr-inheritance-mode=
1984+
C++ Joined RejectNegative Enum(contract_inheritance) Var(flag_contract_nonattr_inheritance_mode) Init(0)
1985+
-fcontracts-nonattr-inheritance-mode=[none|P2900R13|P3653] 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: 11 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)
@@ -3223,6 +3225,15 @@ check_for_override (tree decl, tree ctype)
32233225

32243226
if (DECL_DESTRUCTOR_P (decl))
32253227
TYPE_HAS_NONTRIVIAL_DESTRUCTOR (ctype) = true;
3228+
3229+
if (DECL_HAS_CONTRACTS_P(decl)
3230+
&& flag_contracts_nonattr
3231+
&& flag_contract_nonattr_inheritance_mode
3232+
== CONTRACT_INHERITANCE_NONE)
3233+
{
3234+
error_at (DECL_SOURCE_LOCATION(decl),
3235+
"Contracts can not be added to virtual functions.");
3236+
}
32263237
}
32273238
else if (DECL_FINAL_P (decl))
32283239
error ("%q+#D marked %<final%>, but is not virtual", decl);

gcc/cp/contracts.cc

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,12 +1080,14 @@ remap_contract (tree src, tree dst, tree contract, bool duplicate_p)
10801080
bool do_remap = false;
10811081

10821082
/* Insert parameter remappings. */
1083-
if (TREE_CODE (src) == FUNCTION_DECL)
1084-
src = DECL_ARGUMENTS (src);
1085-
if (TREE_CODE (dst) == FUNCTION_DECL)
1086-
dst = DECL_ARGUMENTS (dst);
1083+
gcc_assert(TREE_CODE (src) == FUNCTION_DECL);
1084+
gcc_assert(TREE_CODE (dst) == FUNCTION_DECL);
10871085

1088-
for (tree sp = src, dp = dst;
1086+
int src_num_artificial_args = num_artificial_parms_for (src);
1087+
int dst_num_artificial_args = num_artificial_parms_for (dst);
1088+
1089+
1090+
for (tree sp = DECL_ARGUMENTS (src), dp = DECL_ARGUMENTS (dst);
10891091
sp || dp;
10901092
sp = DECL_CHAIN (sp), dp = DECL_CHAIN (dp))
10911093
{
@@ -1109,6 +1111,20 @@ remap_contract (tree src, tree dst, tree contract, bool duplicate_p)
11091111

11101112
insert_decl_map (&id, sp, dp);
11111113
do_remap = true;
1114+
1115+
/* First artificial arg is *this. We want to remap that. However, we
1116+
want to skip _in_charge param and __vtt_parm. Do so now. */
1117+
if (src_num_artificial_args > 0)
1118+
{
1119+
while (--src_num_artificial_args,src_num_artificial_args > 0)
1120+
sp = DECL_CHAIN (sp);
1121+
}
1122+
if (dst_num_artificial_args > 0)
1123+
{
1124+
while (--dst_num_artificial_args,dst_num_artificial_args > 0)
1125+
dp = DECL_CHAIN (dp);
1126+
}
1127+
11121128
}
11131129
if (!do_remap)
11141130
return;
@@ -3065,17 +3081,17 @@ maybe_apply_function_contracts (tree fndecl)
30653081
/* The DECL_SAVED_TREE stmt list will be popped by our caller. */
30663082
}
30673083

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. */
3084+
/* Returns a copy of SOURCE contracts where any references to SOURCE's
3085+
PARM_DECLs have been rewritten to the corresponding PARM_DECL in DEST. If
3086+
remap_result is true, result identifier is also re-mapped.
3087+
C++20 version used this function to remap contracts on virtual functions
3088+
from base class to derived class. In such a case postcondition identifier
3089+
wasn't remapped. Caller side wrapper functions need to remap the result
3090+
identifier.
3091+
If remap_post is false, postconditions are dropped from the destination. */
30763092

3077-
void
3078-
copy_and_remap_contracts (tree dest, tree source, bool remap_result = true,
3093+
tree
3094+
remap_contracts (tree dest, tree source, bool remap_result = true,
30793095
bool remap_post = true )
30803096
{
30813097
tree last = NULL_TREE, contract_attrs = NULL_TREE;
@@ -3111,7 +3127,7 @@ copy_and_remap_contracts (tree dest, tree source, bool remap_result = true,
31113127
contract_attrs = c;
31123128
}
31133129

3114-
set_decl_contracts (dest, contract_attrs);
3130+
return contract_attrs;
31153131
}
31163132

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

34083424
/* We always wrap virtual function calls, and non-virtual calls when
34093425
client-side checking is enabled for all contracts. */
3410-
if (is_virt || (flag_contract_nonattr_client_check > 1))
3426+
if ((is_virt
3427+
&& (flag_contract_nonattr_inheritance_mode
3428+
== CONTRACT_INHERITANCE_P2900R13))
3429+
|| (flag_contract_nonattr_client_check > 1))
34113430
return true;
34123431

34133432
/* Otherwise, any function with pre-conditions when selected. */
@@ -3482,11 +3501,16 @@ define_contract_wrapper_func (const tree& fndecl, const tree& wrapdecl, void*)
34823501
/* We check postconditions on virtual function calls or if postcondition
34833502
checks are enabled for all clients. We should not get here unless there
34843503
are some checks to make. */
3485-
bool check_post = (flag_contract_nonattr_client_check > 1) || is_virtual;
3504+
bool check_post
3505+
= (flag_contract_nonattr_client_check > 1)
3506+
|| (is_virtual
3507+
&& (flag_contract_nonattr_inheritance_mode
3508+
== CONTRACT_INHERITANCE_P2900R13));
34863509
/* For wrappers on CDTORs we need to refer to the original contracts,
34873510
when the wrapper is around a clone. */
3488-
copy_and_remap_contracts (wrapdecl, DECL_ORIGIN (fndecl),
3489-
/*remap_result*/true, check_post);
3511+
set_decl_contracts( wrapdecl,
3512+
remap_contracts (wrapdecl, DECL_ORIGIN (fndecl),
3513+
/*remap_result*/true, check_post));
34903514

34913515
start_preparsed_function (wrapdecl, /*DECL_ATTRIBUTES*/NULL_TREE,
34923516
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_P3653 = 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: 61 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+
/*remap_result*/ false,
2200+
/*remap_post*/ 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,62 @@ 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_P3653)
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+
22242280
/* Given a class TYPE, and a function decl FNDECL, look for
22252281
virtual functions in TYPE's hierarchy which FNDECL overrides.
22262282
We do not look in TYPE itself, only its bases.

gcc/testsuite/g++.dg/contracts/cpp26/virtualSMF-noexcept-pre.C renamed to gcc/testsuite/g++.dg/contracts/cpp26/P2900R13-virtual-func/contract_specifier_seq.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// behaves as if the function exited via an exception.
33
// This tests the behaviour of a pre condition on a destructor
44
// { dg-do run }
5-
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-evaluation-semantic=observe " }
5+
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-evaluation-semantic=observe -fcontracts-nonattr-inheritance-mode=P2900R13" }
66

77
#include <experimental/contract>
88
#include <exception>

gcc/testsuite/g++.dg/contracts/cpp26/contracts-multiple-inheritance2.C renamed to gcc/testsuite/g++.dg/contracts/cpp26/P2900R13-virtual-func/contracts-multiple-inheritance2.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// { dg-do compile }
2-
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr" }
2+
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr -fcontracts-nonattr-inheritance-mode=P2900R13" }
33

44
struct BaseA {
55
virtual int fun(int n) pre ( n > 0 ) { return -n; }

gcc/testsuite/g++.dg/contracts/cpp26/contracts-pre4.C renamed to gcc/testsuite/g++.dg/contracts/cpp26/P2900R13-virtual-func/contracts-pre4.C

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// test that contracts on overriding functions are found correctly
22
// { dg-do run }
3-
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr" }
3+
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr -fcontracts-nonattr-inheritance-mode=P2900R13" }
44

55
struct Base
66
{
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// generic error tests for generalized contract redecls
2+
// { dg-do compile }
3+
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontracts-nonattr-inheritance-mode=P2900R13" }
4+
5+
6+
struct Base
7+
{
8+
virtual int f(int a) pre (a > 0);
9+
};
10+
11+
struct Child : Base
12+
{
13+
int f(int a) pre (a < 0); // ok, does not inherit contracts
14+
};
15+
16+
struct F1
17+
{
18+
virtual int f(int a);
19+
};
20+
21+
int F1::f(int a) pre (a > 0) // { dg-error "declaration adds contracts" }
22+
{
23+
return -a;
24+
}
25+
26+
struct Foo {
27+
virtual void f10 (int) pre ( false ) {}
28+
};
29+
30+
struct Bar : Foo {
31+
void f10 (int n = 0) override pre ( false );
32+
};
33+
34+
// we currently don't diagnose an error here if the original contract was erroneous.
35+
void Bar::f10(int n) pre (n >10) {}; // { dg-error "mismatched contract" }
36+
37+
template <typename T>
38+
struct Base2
39+
{
40+
virtual int f(const int a) pre( a > 5 ) post( a > 7) { return a;};
41+
int g(const int a) pre( a > 5 ) post( a > 7) { return a;};
42+
virtual int h(int a) pre( a > 5 ) post( a > 7) { return a;}; // { dg-error "a value parameter used in a postcondition must be const" }
43+
int i(int a) pre( a > 5 ) post( a > 7) { return a;}; // { dg-error "a value parameter used in a postcondition must be const" }
44+
};

0 commit comments

Comments
 (0)