Skip to content

Commit

Permalink
Merge pull request #17 from NinaRanns/contracts-nonattr
Browse files Browse the repository at this point in the history
preparation for P3097R0
  • Loading branch information
villevoutilainen authored Aug 2, 2024
2 parents ce8653c + 887cc3b commit 3e5995e
Show file tree
Hide file tree
Showing 24 changed files with 1,076 additions and 54 deletions.
53 changes: 35 additions & 18 deletions gcc/cp/contracts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1243,6 +1243,12 @@ match_deferred_contracts (tree decl)
location_t new_loc = CONTRACT_SOURCE_LOCATION (new_contracts);
tree old_contracts = DECL_CONTRACTS (decl);
location_t old_loc = CONTRACT_SOURCE_LOCATION (old_contracts);
/* todo : this is suspicious : with P2900 we have a TREE_PURPOSE set despite the fact we
* no longer inherit the contracts from the base. Where is TREE_PURPOSE being
* set ? It looks like it's set for declarations of friend functions.
* This means the diagnostic may claim override even in case of
* re declarations.
*/
tree base = TREE_PURPOSE (pending);
match_contract_conditions (new_loc, new_contracts,
old_loc, old_contracts,
Expand Down Expand Up @@ -2269,6 +2275,16 @@ duplicate_contracts (tree newdecl, tree olddecl)
/* We are adding contracts to a declaration. */
if (new_contracts)
{
if (flag_contracts_nonattr)
{
/* In P2900, the virtual functions do not inherit the contracts.
* Also, if a function has contracts, they must appear on the
* first declaration */
error_at (new_loc,
"declaration adds contracts to %q#D",
olddecl);
return;
}
/* We can't add to a previously defined function. */
if (DECL_INITIAL (olddecl))
{
Expand All @@ -2277,25 +2293,26 @@ duplicate_contracts (tree newdecl, tree olddecl)
inform (DECL_SOURCE_LOCATION (olddecl), "original definition here");
return;
}

/* We can't add to an unguarded virtual function declaration. */
if (DECL_VIRTUAL_P (olddecl) && new_contracts)
{
auto_diagnostic_group d;
error_at (new_loc, "cannot add contracts to a virtual function");
inform (DECL_SOURCE_LOCATION (olddecl), "original declaration here");
return;
}

/* Depending on the "first declaration" rule, we may not be able
to add contracts to a function after the fact. */
if (flag_contract_strict_declarations)
else
{
warning_at (new_loc,
OPT_fcontract_strict_declarations_,
"declaration adds contracts to %q#D",
olddecl);
return;
/* We can't add to an unguarded virtual function declaration. */
if (DECL_VIRTUAL_P (olddecl) && new_contracts)
{
auto_diagnostic_group d;
error_at (new_loc, "cannot add contracts to a virtual function");
inform (DECL_SOURCE_LOCATION (olddecl), "original declaration here");
return;
}

/* Depending on the "first declaration" rule, we may not be able
to add contracts to a function after the fact. */
if (flag_contract_strict_declarations)
{
warning_at (new_loc,
OPT_fcontract_strict_declarations_,
"declaration adds contracts to %q#D",
olddecl);
}
}

/* Copy the contracts from NEWDECL to OLDDECL. We shouldn't need to
Expand Down
4 changes: 0 additions & 4 deletions gcc/cp/contracts.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,10 +238,6 @@ enum contract_matching_context
#define CONTRACT_LEVEL(NODE) \
(TREE_VALUE (CONTRACT_MODE (NODE)))

/* The identifier denoting the role of the contract */
#define CONTRACT_ROLE(NODE) \
(TREE_PURPOSE (CONTRACT_MODE (NODE)))

/* The parsed condition of the contract. */
#define CONTRACT_CONDITION(NODE) \
(TREE_OPERAND (CONTRACT_CHECK (NODE), 1))
Expand Down
7 changes: 3 additions & 4 deletions gcc/cp/decl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6100,7 +6100,7 @@ start_decl (const cp_declarator *declarator,
permerror (declarator->id_loc,
"declaration of %q#D outside of class is not definition",
decl);
else if (flag_contract_strict_declarations)
else if (!flag_contracts_nonattr && flag_contract_strict_declarations)
warning_at (declarator->id_loc, OPT_fcontract_strict_declarations_,
"declaration of %q#D outside of class is not definition",
decl);
Expand Down Expand Up @@ -13344,9 +13344,8 @@ grokdeclarator (const cp_declarator *declarator,

/* Check that contracts aren't misapplied. */
if (tree contract_attr = find_contract (declarator->std_attributes))
if (!flag_contracts_nonattr
&& (declarator->kind != cdk_function
|| innermost_code != cdk_function))
if (declarator->kind != cdk_function
|| innermost_code != cdk_function)
diagnose_misapplied_contracts (contract_attr);

/* We don't want to warn in parameter context because we don't
Expand Down
5 changes: 2 additions & 3 deletions gcc/cp/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22908,7 +22908,7 @@ cp_parser_using_directive (cp_parser* parser)
cp_warn_deprecated_use_scopes (namespace_decl);
/* And any specified GNU attributes. */
if (cp_next_tokens_can_be_gnu_attribute_p (parser))
attribs = chainon (attribs, cp_parser_gnu_attributes_opt (parser));
attribs = attr_chainon (attribs, cp_parser_gnu_attributes_opt (parser));

/* Update the symbol table. */
finish_using_directive (namespace_decl, attribs);
Expand Down Expand Up @@ -24306,8 +24306,7 @@ cp_parser_direct_declarator (cp_parser* parser,
if (flag_contracts_nonattr)
{
tree cca_attrs = cp_parser_contract_specifier_seq (parser);
if (cca_attrs && cca_attrs!=error_mark_node)
attrs = chainon(attrs, cca_attrs);
attrs = attr_chainon(attrs, cca_attrs);
}

location_t parens_loc = make_location (parens_start,
Expand Down
52 changes: 27 additions & 25 deletions gcc/cp/search.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2164,32 +2164,34 @@ check_final_overrider (tree overrider, tree basefn)
}
return 0;
}
if (!flag_contracts_nonattr)
{
if (!DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
auto_diagnostic_group d;
error ("function with contracts %q+D overriding contractless function",
overrider);
inform (DECL_SOURCE_LOCATION (basefn),
"overridden function is %qD", basefn);
return 0;
}
else if (DECL_HAS_CONTRACTS_P (basefn) && !DECL_HAS_CONTRACTS_P (overrider))
{
/* We're inheriting basefn's contracts; create a copy of them but
replace references to their parms to our parms. */
inherit_base_contracts (overrider, basefn);
}
else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
/* We're in the process of completing the overrider's class, which means
our conditions definitely are not parsed so simply chain on the
basefn for later checking.
if (!DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
auto_diagnostic_group d;
error ("function with contracts %q+D overriding contractless function",
overrider);
inform (DECL_SOURCE_LOCATION (basefn),
"overridden function is %qD", basefn);
return 0;
}
else if (DECL_HAS_CONTRACTS_P (basefn) && !DECL_HAS_CONTRACTS_P (overrider))
{
/* We're inheriting basefn's contracts; create a copy of them but
replace references to their parms to our parms. */
inherit_base_contracts (overrider, basefn);
}
else if (DECL_HAS_CONTRACTS_P (basefn) && DECL_HAS_CONTRACTS_P (overrider))
{
/* We're in the process of completing the overrider's class, which means
our conditions definitely are not parsed so simply chain on the
basefn for later checking.
Note that OVERRIDER's contracts will have been fully parsed at the
point the deferred match is run. */
defer_guarded_contract_match (overrider, basefn, DECL_CONTRACTS (basefn));
}
Note that OVERRIDER's contracts will have been fully parsed at the
point the deferred match is run. */
defer_guarded_contract_match (overrider, basefn, DECL_CONTRACTS (basefn));
}
}

if (DECL_FINAL_P (basefn))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,67 @@ struct E : D {
};


namespace other{

static_assert (__cpp_contracts >= 201906);


auto f(int x) -> int pre(x >= 0);

auto f(int x) pre(x >= 0) -> int pre(x >= 0); // { dg-error "expected initializer before" }

auto f(int x) pre(x >= 0) -> int; // { dg-error "expected initializer before" }

struct X
{
virtual void f(int x) pre(x >= 0);
};

struct Y : X
{
void f(int x) override pre(x >= 0);
};

struct Y2 : X
{
void f(int x) pre(x >= 0) override; // { dg-error "expected .;. at end of member declaration|does not name a type" }
};

struct Y3 : X
{
void f(int x) pre(x >= 0) override pre(x >= 0); // { dg-error "expected .;. at end of member declaration|does not name a type" }
};

struct X2
{
virtual void f(int x) pre(x >= 0);
};

struct X3
{
virtual void f(int x) final pre(x >= 0);
};

struct X4
{
virtual void f(int x) pre(x >= 0) final; // { dg-error "expected .;. at end of member declaration|does not name a type" }
};

struct X5
{
virtual void f(int x) pre(x >= 0) final pre(x >= 0); // { dg-error "expected .;. at end of member declaration|does not name a type" }
};

template <class T>
void g(int x) requires(true) pre(x >= 0);

template <class T>
void g2(int x) pre(x >= 0) requires(true); // { dg-error "expected initializer" }

template <class T>
void g3(int x) pre(x >= 0) requires(true) pre(x >= 0); // { dg-error "expected initializer" }

}

int main()
{
Expand Down
36 changes: 36 additions & 0 deletions gcc/testsuite/g++.dg/contracts/cpp26/contracts-friend1.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// ensure contracts on friend declarations are a complete class context
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr" }


struct X {
friend void fn0(X x) [[ pre: x.a > 0 ]] { }

static void fns0(X x) [[ pre: x.a > 0 ]] { }
static void fns1(X x) [[ pre: x.a > 0 ]];
static void fns2(X x) [[ pre: x.a > 0 ]] ;

friend void fn(X &x) { x.a = -5; }

private:
int a{10};
};
void X::fns1(X x) { }
void X::fns2(X x) [[ pre: x.a > 0 ]] { }

int main(int, char**) {
X x;
fn(x); // no contract

fn0(x);

X::fns0(x);
X::fns1(x);
X::fns2(x);
return 0;
}

// { dg-output "contract violation in function fn0 at .*.C:7: .*(\n|\r\n|\r)" }
// { dg-output "contract violation in function X::fns0 at .*.C:9: .*(\n|\r\n|\r)" }
// { dg-output "contract violation in function X::fns1 at .*.C:10: .*(\n|\r\n|\r)" }
// { dg-output "contract violation in function X::fns2 at .*.C:19: .*(\n|\r\n|\r)" }
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr" }

struct BaseA {
virtual int fun(int n) [[ pre: n > 0 ]] { return -n; }
};

struct BaseB {
virtual int fun(int n) [[ pre: n < 0 ]] { return -n; }
};

struct Child1 : public BaseA, BaseB {
int fun(int n) [[ pre: n > 0 ]] { return -n; }
};

struct Child2 : public BaseA, BaseB {
int fun(int n) [[ pre: n < 0 ]] { return -n; }
};

struct Child3 : public BaseA, BaseB {
int fun(int n) { return -n; }
};

struct Child4 : public BaseA {
int fun(int n);
};

int Child4::fun(int n) [[ pre: n != 0 ]] // { dg-error "declaration adds contracts" }
{
return -n;
}

23 changes: 23 additions & 0 deletions gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class1.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr" }

void gfn3(int n) [[ pre: n > 0 ]];

struct Outer {
struct Inner {
void fn(int n) [[ pre: n > 0 && bob > 1 ]];
void fn2(int n) [[ pre: n > 0 && bob > 1 ]];
};

void fn(int m) [[ pre: m > 1 ]];
friend void Inner::fn(int n) [[ pre: n > 0 && bob > 1 ]]; // { dg-error "not declared" }

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

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

static int bob;
};
int Outer::bob{-1};
38 changes: 38 additions & 0 deletions gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class2.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontract-continuation-mode=on -fcontracts-nonattr" }

void gfn3(int n) [[ pre: n > 0 ]];

struct Outer {
struct Inner {
void fn(int n) [[ pre: n > 0 && bob > 1 ]];
};

void fn(int m) [[ pre: m > 1 ]];

friend void gfn1(int p) [[ pre: p > 0 ]] { }

friend void gfn2(int p, Outer *) [[ pre: p > 0 ]] { }

friend void gfn3(int n);

static int bob;
};
int Outer::bob{-1};

void Outer::Inner::fn(int x) { }
void Outer::fn(int y) { }

void gfn3(int n) { }
void gfn1(int q);

int main(int, char **) {
Outer::Inner in;
in.fn(-5);
Outer out;
out.fn(-6);
gfn1(-7);
gfn2(-8, &out);
gfn3(-9);
}

Loading

0 comments on commit 3e5995e

Please sign in to comment.