Skip to content

Commit

Permalink
Merge pull request #3 from NinaRanns/contracts-nonattr
Browse files Browse the repository at this point in the history
contract_assert
  • Loading branch information
villevoutilainen authored May 28, 2024
2 parents 045af73 + ba8a899 commit 9ec18b3
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 5 deletions.
1 change: 1 addition & 0 deletions gcc/c-family/c-common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ const struct c_common_resword c_common_reswords[] =
{ "constinit", RID_CONSTINIT, D_CXXONLY | D_CXX20 | D_CXXWARN },
{ "const_cast", RID_CONSTCAST, D_CXXONLY | D_CXXWARN },
{ "continue", RID_CONTINUE, 0 },
{ "contract_assert", RID_CONTASSERT, D_CXXONLY | D_CXX20 | D_CXXWARN },
{ "decltype", RID_DECLTYPE, D_CXXONLY | D_CXX11 | D_CXXWARN },
{ "default", RID_DEFAULT, 0 },
{ "delete", RID_DELETE, D_CXXONLY | D_CXXWARN },
Expand Down
2 changes: 1 addition & 1 deletion gcc/c-family/c-common.h
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ enum rid
RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,

/* C++20 */
RID_CONSTINIT, RID_CONSTEVAL,
RID_CONSTINIT, RID_CONSTEVAL, RID_CONTASSERT,

/* char8_t */
RID_CHAR8,
Expand Down
2 changes: 1 addition & 1 deletion gcc/cp/contracts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -746,7 +746,7 @@ grok_contract (tree attribute, tree mode, tree result, cp_expr condition,
location_t loc)
{
tree_code code;
if (is_attribute_p ("assert", attribute))
if (is_attribute_p ("assert", attribute) || is_attribute_p ("contract_assert", attribute))
code = ASSERTION_STMT;
else if (is_attribute_p ("pre", attribute))
code = PRECONDITION_STMT;
Expand Down
47 changes: 45 additions & 2 deletions gcc/cp/parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12440,7 +12440,7 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
error_at (EXPR_LOCATION (TREE_VALUE (post)),
"postconditions cannot be statements");

/* Check that assertions are null statements. */
/* Check that assertions are null statements. This only checks for atttribute like assertions */
if (cp_contract_assertion_p (std_attrs))
if (token->type != CPP_SEMICOLON)
error_at (token->location, "assertions must be followed by %<;%>");
Expand Down Expand Up @@ -12579,6 +12579,38 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr,
std_attrs = process_stmt_hotness_attribute (std_attrs, attrs_loc);
statement = cp_parser_transaction_cancel (parser);
break;
case RID_CONTASSERT:
if (flag_contracts_nonattr && flag_contracts)
{
tree mode = NULL_TREE; // not needed for non attribute contracts
tree result = NULL_TREE; // not needed for assertions
tree cont_assert = NULL_TREE;
cont_assert = token->u.value;

cp_token *token = cp_lexer_consume_token (parser->lexer);
location_t loc = token->location;
tree contract;
matching_parens parens;
parens.require_open (parser);
/* Defer the parsing of pre/post contracts inside class definitions. */
/* Enable location wrappers when parsing contracts. */
auto suppression = make_temp_override (suppress_location_wrappers, 0);

/* Parse the condition, ensuring that parameters or the return variable
aren't flagged for use outside the body of a function. */
++processing_contract_condition;
cp_expr condition = cp_parser_conditional_expression (parser);
--processing_contract_condition;

parens.require_close (parser);

/* Build the contract. */
contract = grok_contract (cont_assert, mode, result, condition, loc);
std_attrs = finish_contract_attribute (cont_assert, contract);
}
else
error_at (token->location, "%<contract_assertions%> are only available with %<-fcontracts%> and %<-fcontracts-nonattr%>");
break;

default:
/* It might be a keyword like `int' that can start a
Expand Down Expand Up @@ -30283,7 +30315,18 @@ void cp_parser_late_contract_condition (cp_parser *parser,
[ [ assert : contract-mode [opt] : conditional-expression ] ]
[ [ pre : contract-mode [opt] : conditional-expression ] ]
[ [ post : contract-mode [opt] identifier [opt] :
conditional-expression ] ] */
conditional-expression ] ]

if attr_mode is true, also parse :
function-contract-specifier :
pre attribute-specifier-seqopt ( conditional-expression )
post attribute-specifier-seqopt ( result-name-introducer[opt] conditional-expression )
contract_assert attribute-specifier-seq[opt] ( conditional-expression ) ;

TODO :
- test if we allow attribute-specifier in the new contracts syntax
- constify the entities in contracts
*/

static tree
cp_parser_std_attribute_spec (cp_parser *parser, bool attr_mode)
Expand Down
26 changes: 26 additions & 0 deletions gcc/testsuite/g++.dg/contracts/new/contract-assert-disabled.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// generic assert contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// check that all concrete semantics parse
// check omitted, '%default' contract roles parse
// ensure that an invalid contract level 'invalid' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// ensure that an invalid contract role 'invalid' errors
// ensure that a missing colon after contract role errors
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts " }

static_assert (__cpp_contracts >= 201906);
static_assert (__cpp_contracts_literal_semantics >= 201906);
static_assert (__cpp_contracts_roles >= 201906);

// { dg-prune-output "expected primary-expression" }

int main()
{
int x;

contract_assert( x >= 0); // { dg-error "'contract_assertions' are only available with" }

return 0;
}
59 changes: 59 additions & 0 deletions gcc/testsuite/g++.dg/contracts/new/contract-assert-run.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// generic assert contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// check that all concrete semantics parse
// check omitted, '%default' contract roles parse
// ensure that an invalid contract level 'invalid' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// ensure that an invalid contract role 'invalid' errors
// ensure that a missing colon after contract role errors
// { dg-do run }
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr -fcontract-continuation-mode=on" }
#include <iostream>
#include <experimental/contract>

#define VERIFY_ASSERT(statement, asserts) \
{ \
bool violation = false;\
try{ \
statement; \
} catch(int &ex) { \
violation = true; \
} \
if ((asserts && !violation) || (!(asserts) && violation)) __builtin_abort(); \
} \

static_assert (__cpp_contracts >= 201906);
static_assert (__cpp_contracts_literal_semantics >= 201906);
static_assert (__cpp_contracts_roles >= 201906);

void handle_contract_violation(const std::experimental::contract_violation &violation) {
std::cerr << "custom std::handle_contract_violation called:"
<< " " << violation.line_number()
<< " " << violation.file_name()
<< std::endl;
throw -(int)violation.line_number();
}

void foo(int x) pre (x>10){};

int main()
{
VERIFY_ASSERT([[assert: false]], true);
VERIFY_ASSERT([[assert: true]], false);

VERIFY_ASSERT(contract_assert(true), false);
VERIFY_ASSERT(contract_assert(false), true);

int i = 4;
VERIFY_ASSERT(foo(i), true);
VERIFY_ASSERT(contract_assert( i == 4 ? true : false), false)
VERIFY_ASSERT(contract_assert( i > 4 ? true : false), true)

i = 18;
VERIFY_ASSERT(foo(i), false);
VERIFY_ASSERT(contract_assert( i == 4 ? true : false), true)
VERIFY_ASSERT(contract_assert( i > 4 ? true : false), false)

return 0;
}
41 changes: 41 additions & 0 deletions gcc/testsuite/g++.dg/contracts/new/contract-assert.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// generic assert contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// check that all concrete semantics parse
// check omitted, '%default' contract roles parse
// ensure that an invalid contract level 'invalid' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// ensure that an invalid contract role 'invalid' errors
// ensure that a missing colon after contract role errors
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr" }

static_assert (__cpp_contracts >= 201906);
static_assert (__cpp_contracts_literal_semantics >= 201906);
static_assert (__cpp_contracts_roles >= 201906);

int main()
{
int x;

contract_assert( x >= 0);
contract_assert( x < 0);
contract_assert( x == 0);

contract_assert( x > 0 ? true : false);
contract_assert( x < 0 ? true : false);

contract_assert( x >= 0);
contract_assert( x >= 0);
contract_assert( x >= 0);
contract_assert( x >= 0);

contract_assert( x >= 0);
contract_assert( x < 0);
contract_assert( x == 0);
contract_assert( x == 1);



return 0;
}
37 changes: 37 additions & 0 deletions gcc/testsuite/g++.dg/contracts/new/contract-assert_err.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// generic assert contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// check that all concrete semantics parse
// check omitted, '%default' contract roles parse
// ensure that an invalid contract level 'invalid' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// ensure that an invalid contract role 'invalid' errors
// ensure that a missing colon after contract role errors
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr" }

// { dg-prune-output "not declared" }

static_assert (__cpp_contracts >= 201906);
static_assert (__cpp_contracts_literal_semantics >= 201906);
static_assert (__cpp_contracts_roles >= 201906);


contract_assert f(); // { dg-error "expected unqualified-id before" }
void f(contract_assert); // { dg-error "expected primary-expression before"}
struct contract_assert{}; // { dg-error "expected unqualified-id before" }
void contract_assert();
int main()
{

contract_assert(x==0); // { dg-error }
contract_assert int i = 0; // { dg-error }

i = 7;
[[assert: i == 0]] contract_assert(x==0); // { dg-error }

contract_assert( x = 0); // { dg-error "expected .). before .=. token"}

contract_assert( y == 0); // { dg-error ".y. was not declared in this scope" }
return 0;
}
2 changes: 1 addition & 1 deletion gcc/testsuite/g++.dg/contracts/new/contracts1.C
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ int g2(int a) [[pre: f(a) > a ]]
}

int fun(int n) pre (n > 0 );
void fun2(int n) pre n > 0 ]]; // { dg-error }
void fun2(int n) pre n > 0 ; // { dg-error }
void fun2(int n) pre (: n > 0 ]]; // { dg-error }
int fun3(int n) [[ pre : n > 0 ); // { dg-error "expected .]. before " }

Expand Down
25 changes: 25 additions & 0 deletions gcc/testsuite/g++.dg/contracts/new/init-declarator.C
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// generic assert contract parsing checks
// check omitted, 'default', 'audit', and 'axiom' contract levels parse
// check that all concrete semantics parse
// check omitted, '%default' contract roles parse
// ensure that an invalid contract level 'invalid' errors
// ensure that a predicate referencing an undefined variable errors
// ensure that a missing colon after contract level errors
// ensure that an invalid contract role 'invalid' errors
// ensure that a missing colon after contract role errors
// { dg-do compile }
// { dg-options "-std=c++2a -fcontracts -fcontracts-nonattr" }

static_assert (__cpp_contracts >= 201906);
static_assert (__cpp_contracts_literal_semantics >= 201906);
static_assert (__cpp_contracts_roles >= 201906);
i
int fun(int n) pre (n > 0 );

int main()
{
int x() [[ pre fun(0) > 0 ]];
int y() pre (fun(0) > 0);
int z() [[ pre fun(0) > 0 ]] pre (fun(0) > 0);
return 0;
}
5 changes: 5 additions & 0 deletions gcc/testsuite/g++.dg/contracts/new/preexisting_tests.C
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ namespace parsing_ambig_test4 {
template <typename T>
void g() requires d < e > pre(c);
// d<e> is a bool variable template, pre(c) is the precondition


template <typename T>
void f() requires (a < b > pre(c));
// just a requires clause, no postcondition
}

int main() {
Expand Down

0 comments on commit 9ec18b3

Please sign in to comment.