diff --git a/gcc/cp/contracts.cc b/gcc/cp/contracts.cc index dbc4ee5f9be8e..07eef09ea5f1c 100644 --- a/gcc/cp/contracts.cc +++ b/gcc/cp/contracts.cc @@ -697,6 +697,10 @@ tree grok_contract (tree attribute, tree mode, tree result, cp_expr condition, location_t loc) { + + if (condition == error_mark_node) + return error_mark_node; + tree_code code; if (is_attribute_p ("assert", attribute) || is_attribute_p ("contract_assert", attribute)) code = ASSERTION_STMT; @@ -732,6 +736,10 @@ grok_contract (tree attribute, tree mode, tree result, cp_expr condition, /* The condition is converted to bool. */ condition = finish_contract_condition (condition); + + if (condition == error_mark_node) + return error_mark_node; + CONTRACT_CONDITION (contract) = condition; return contract; diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index dc458a246fd33..9d19dec58d34f 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -12834,9 +12834,14 @@ cp_parser_statement (cp_parser* parser, tree in_statement_expr, /* Build the contract. */ tree contract = grok_contract (cont_assert, NULL_TREE /*mode*/, NULL_TREE /*result*/, condition, loc); - std_attrs = finish_contract_attribute (cont_assert, contract); + std_attrs = finish_contract_attribute (cont_assert, contract); + // If there are errors in the contract, we do not create + // the attribute tree. This assumes no attributes on + // 'contract_assert' + if (std_attrs == error_mark_node) + std_attrs = NULL_TREE; } else error_at ( @@ -30881,13 +30886,14 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute, cp_expr condition = cp_parser_conditional_expression (parser); --processing_contract_condition; + /* For natural syntax, we eat the parens here. For the attribute syntax, + it will be done one level up, we just need to skip to it. */ if (!attr_mode) parens.require_close (parser); - /* Try to recover from errors by scanning up to the end of the attribute. Sometimes we get partially parsed expressions, so we need to search the condition for errors. */ - if (contains_error_p (condition)) + else if (contains_error_p (condition)) cp_parser_skip_up_to_closing_square_bracket (parser); /* Build the contract. */ @@ -30907,6 +30913,9 @@ cp_parser_contract_attribute_spec (cp_parser *parser, tree attribute, return error_mark_node; } + if (contract == error_mark_node) + return error_mark_node; + return finish_contract_attribute (attribute, contract); } diff --git a/gcc/testsuite/g++.dg/contracts/new/contract-assert_err.C b/gcc/testsuite/g++.dg/contracts/new/contract-assert_err.C index 921b74b4dc73e..84b6cb5a0ce3f 100644 --- a/gcc/testsuite/g++.dg/contracts/new/contract-assert_err.C +++ b/gcc/testsuite/g++.dg/contracts/new/contract-assert_err.C @@ -26,13 +26,13 @@ void contract_assert(); // { dg-error "expected unqualified-id before" } int main() { - contract_assert(x==0); // { dg-error ".x. was not declared in this scope"} - contract_assert int i = 0; // // { dg-error "expected primary-expression before|before .int." } + contract_assert(x==0); // { dg-error ".x. was not declared in this scope"} + contract_assert int i = 0; // // { dg-error "expected primary-expression before|before .int." } - i = 7; - [[assert: i == 0]] contract_assert(x==0); // { dg-error "assertions must be followed by" } + i = 7; + [[assert: i == 0]] contract_assert(x==0); // { dg-error "assertions must be followed by" } - contract_assert( x = 0); // { dg-error "expected primary-expression before|expected .\\). before .=. token" } + contract_assert( x = 0); // { dg-error "expected primary-expression before|expected .\\). before .=. token" } contract_assert( y == 0); // { dg-error ".y. was not declared in this scope" } return 0; diff --git a/gcc/testsuite/g++.dg/contracts/new/pr113968.C b/gcc/testsuite/g++.dg/contracts/new/pr113968.C new file mode 100644 index 0000000000000..0ad87572359ea --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/new/pr113968.C @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-options "-std=c++23 -fcontracts -fcontracts-nonattr" } +struct A { + A(A &); +}; +struct S{ + void f(A a) pre(a) // { dg-error "could not convert" } + pre(a.b) // { dg-error "has no member" } + { + + } +}; +void f(A a) pre(a) // { dg-error "could not convert" } + pre(a.b) // { dg-error "has no member" } + { + contract_assert(a); // { dg-error "could not convert" } + contract_assert(a.b); // { dg-error "has no member" } +} + +int main() +{ +} + + + + diff --git a/gcc/testsuite/g++.dg/contracts/pr113968.C b/gcc/testsuite/g++.dg/contracts/pr113968.C new file mode 100644 index 0000000000000..471d6647323b3 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/pr113968.C @@ -0,0 +1,26 @@ +// { dg-do compile } +// { dg-options "-std=c++2a -fcontracts " } +struct A { + A(A &); +}; +struct S{ + void f(A a) [[ pre : a]] // { dg-error "could not convert" } + [[ pre : a.b]] // { dg-error "has no member" } + { + + } +}; +void f(A a) [[ pre : a]] // { dg-error "could not convert" } + [[ pre : a.b]] // { dg-error "has no member" } + { + [[ assert : a ]] ; // { dg-error "could not convert" } + [[ assert : a.b ]]; // { dg-error "has no member" } +} + +int main() +{ +} + + + +