https://gcc.gnu.org/g:7f914294fedfe7117712cb802fe93afa3f5e68a8
commit r16-7090-g7f914294fedfe7117712cb802fe93afa3f5e68a8 Author: Iain Sandoe <[email protected]> Date: Sun Oct 19 15:49:31 2025 +0100 c++, contracts: Add tests for C++26 contracts. This adds in a set of tests for the C++26 contracts, and also ensures that cases that previously failed with attribute syntax continue to work as expected with the revised C++26 syntax. PR c++/113968 PR c++/110871 PR c++/110872 gcc/testsuite/ChangeLog: * g++.dg/contracts/cpp26/BZ121936-workaround-noipa.C: New test. * g++.dg/contracts/cpp26/assertion-statement-errors.C: New test. * g++.dg/contracts/cpp26/assertion-statement.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p11-observe.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p14.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-2.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-3.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-4.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-5.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-post.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-pre.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF2.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF3.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF4.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p17.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p4-error.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p4.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p6.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p6.observe.C: New test. * g++.dg/contracts/cpp26/basic.scope.contract.p1.C: New test. * g++.dg/contracts/cpp26/basic.scope.contract.p2.1.C: New test. * g++.dg/contracts/cpp26/contract-assert-run.C: New test. * g++.dg/contracts/cpp26/contract-assert-warn-attributes.C: New test. * g++.dg/contracts/cpp26/contract-violation-noexcept.C: New test. * g++.dg/contracts/cpp26/contract-violation-noexcept2.C: New test. * g++.dg/contracts/cpp26/contract_genericize.C: New test. * g++.dg/contracts/cpp26/contracts-friend1.C: New test. * g++.dg/contracts/cpp26/contracts-nested-class1.C: New test. * g++.dg/contracts/cpp26/contracts-nested-class2.C: New test. * g++.dg/contracts/cpp26/contracts-tmpl-spec2.C: New test. * g++.dg/contracts/cpp26/dcl.contract.func.p4.C: New test. * g++.dg/contracts/cpp26/dcl.contract.func.p6.C: New test. * g++.dg/contracts/cpp26/dcl.contract.res.p1-NT.C: New test. * g++.dg/contracts/cpp26/dcl.contract.res.p1.C: New test. * g++.dg/contracts/cpp26/dcl.contract.res.p2.C: New test. * g++.dg/contracts/cpp26/debug-and-opt.C: New test. * g++.dg/contracts/cpp26/deferred1.C: New test. * g++.dg/contracts/cpp26/dependent_contract.C: New test. * g++.dg/contracts/cpp26/empty-nt-param.C: New test. * g++.dg/contracts/cpp26/function-contract-specifier-seq-error.C: New test. * g++.dg/contracts/cpp26/function-contract-specifier-seq.C: New test. * g++.dg/contracts/cpp26/lambda.C: New test. * g++.dg/contracts/cpp26/name_mangling.C: New test. * g++.dg/contracts/cpp26/over.call.func.p3.1.C: New test. * g++.dg/contracts/cpp26/pr113968.C: New test. * g++.dg/contracts/cpp26/src-loc-0.C: New test. * g++.dg/contracts/cpp26/src-loc-1.C: New test. * g++.dg/contracts/cpp26/src-loc-2.C: New test. * g++.dg/contracts/cpp26/throwing-violation-handler.cc: New test. * g++.dg/contracts/cpp26/unused_warning.C: New test. * g++.dg/contracts/cpp26/vaargs.C: New test. * g++.dg/contracts/cpp2a/check-err.C: New test. * g++.dg/coroutines/pr110871.C: New test. * g++.dg/coroutines/pr110872.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p8.C: New test. * g++.dg/contracts/cpp26/dcl.contract.func.p7.C: New test. * g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C: New test. * g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C: New test. * g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C: New test. * g++.dg/contracts/cpp26/basic.contract.eval.p7.3.C: New test. * g++.dg/contracts/cpp26/intro.compliance.general.p2.3.4.C: New test. Co-Authored-by: Nina Ranns <[email protected]> Co-Authored-by: Ville Voutilainen <[email protected]> Signed-off-by: Iain Sandoe <[email protected]> Diff: --- .../contracts/cpp26/BZ121936-workaround-noipa.C | 22 + .../contracts/cpp26/assertion-statement-errors.C | 41 ++ .../g++.dg/contracts/cpp26/assertion-statement.C | 34 ++ .../cpp26/basic.contract.eval.p11-observe.C | 31 ++ .../contracts/cpp26/basic.contract.eval.p14.C | 25 ++ .../contracts/cpp26/basic.contract.eval.p17-2.C | 47 +++ .../contracts/cpp26/basic.contract.eval.p17-3.C | 58 +++ .../contracts/cpp26/basic.contract.eval.p17-4.C | 55 +++ .../contracts/cpp26/basic.contract.eval.p17-5.C | 52 +++ .../cpp26/basic.contract.eval.p17-SMF-post.C | 54 +++ .../cpp26/basic.contract.eval.p17-SMF-pre.C | 54 +++ .../contracts/cpp26/basic.contract.eval.p17-SMF2.C | 57 +++ .../contracts/cpp26/basic.contract.eval.p17-SMF3.C | 56 +++ .../contracts/cpp26/basic.contract.eval.p17-SMF4.C | 56 +++ .../contracts/cpp26/basic.contract.eval.p17.C | 49 +++ .../contracts/cpp26/basic.contract.eval.p4-error.C | 14 + .../contracts/cpp26/basic.contract.eval.p4.C | 27 ++ .../contracts/cpp26/basic.contract.eval.p6.C | 31 ++ .../cpp26/basic.contract.eval.p6.observe.C | 17 + .../contracts/cpp26/basic.contract.eval.p7.3.C | 14 + .../contracts/cpp26/basic.contract.eval.p8.C | 12 + .../contracts/cpp26/basic.scope.contract.p1.C | 42 ++ .../contracts/cpp26/basic.scope.contract.p2.1.C | 11 + .../g++.dg/contracts/cpp26/contract-assert-run.C | 56 +++ .../cpp26/contract-assert-warn-attributes.C | 7 + .../contracts/cpp26/contract-violation-noexcept.C | 112 ++++++ .../contracts/cpp26/contract-violation-noexcept2.C | 38 ++ .../g++.dg/contracts/cpp26/contract_genericize.C | 49 +++ .../g++.dg/contracts/cpp26/contracts-friend1.C | 36 ++ .../contracts/cpp26/contracts-nested-class1.C | 23 ++ .../contracts/cpp26/contracts-nested-class2.C | 38 ++ .../g++.dg/contracts/cpp26/contracts-tmpl-spec2.C | 446 +++++++++++++++++++++ .../g++.dg/contracts/cpp26/dcl.contract.func.p4.C | 138 +++++++ .../g++.dg/contracts/cpp26/dcl.contract.func.p6.C | 21 + .../contracts/cpp26/dcl.contract.func.p7-t1.C | 161 ++++++++ .../contracts/cpp26/dcl.contract.func.p7-t2.C | 157 ++++++++ .../g++.dg/contracts/cpp26/dcl.contract.func.p7.C | 335 ++++++++++++++++ .../g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C | 69 ++++ .../contracts/cpp26/dcl.contract.res.p1-NT.C | 146 +++++++ .../g++.dg/contracts/cpp26/dcl.contract.res.p1.C | 23 ++ .../g++.dg/contracts/cpp26/dcl.contract.res.p2.C | 25 ++ .../g++.dg/contracts/cpp26/debug-and-opt.C | 15 + gcc/testsuite/g++.dg/contracts/cpp26/deferred1.C | 49 +++ .../g++.dg/contracts/cpp26/dependent_contract.C | 32 ++ .../g++.dg/contracts/cpp26/empty-nt-param.C | 42 ++ .../contracts/cpp26/expr.prim.id.unqual.p7-2.C | 65 +++ .../contracts/cpp26/expr.prim.id.unqual.p7.C | 139 +++++++ .../cpp26/function-contract-specifier-seq-error.C | 50 +++ .../cpp26/function-contract-specifier-seq.C | 122 ++++++ .../cpp26/intro.compliance.general.p2.3.4.C | 13 + gcc/testsuite/g++.dg/contracts/cpp26/lambda.C | 5 + .../g++.dg/contracts/cpp26/name_mangling.C | 15 + .../g++.dg/contracts/cpp26/over.call.func.p3.1.C | 36 ++ gcc/testsuite/g++.dg/contracts/cpp26/pr113968.C | 23 ++ gcc/testsuite/g++.dg/contracts/cpp26/src-loc-0.C | 18 + gcc/testsuite/g++.dg/contracts/cpp26/src-loc-1.C | 18 + gcc/testsuite/g++.dg/contracts/cpp26/src-loc-2.C | 20 + .../contracts/cpp26/throwing-violation-handler.cc | 23 ++ .../g++.dg/contracts/cpp26/unused_warning.C | 25 ++ gcc/testsuite/g++.dg/contracts/cpp26/vaargs.C | 35 ++ gcc/testsuite/g++.dg/contracts/cpp2a/check-err.C | 18 + gcc/testsuite/g++.dg/coroutines/pr110871.C | 64 +++ gcc/testsuite/g++.dg/coroutines/pr110872.C | 53 +++ 63 files changed, 3619 insertions(+) diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/BZ121936-workaround-noipa.C b/gcc/testsuite/g++.dg/contracts/cpp26/BZ121936-workaround-noipa.C new file mode 100644 index 000000000000..b818efc0c951 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/BZ121936-workaround-noipa.C @@ -0,0 +1,22 @@ +// check that we do not optimise based on contract_asssert +// { dg-additional-options "-O3 -fcontracts -fcontract-evaluation-semantic=quick_enforce -fdump-tree-optimized " } +// { dg-do compile { target c++26 } } + +[[gnu::used, gnu::noinline]] +inline int* f(int *y) { + contract_assert(y != nullptr); + return y; +} + + +int foo(int *x) { + if (f(x) == nullptr) + { + return 0; + } + return *x; +} + +// Check that the if condition in foo isn't optimised away, we should see the +// nullptr check twice. +// { dg-final { scan-tree-dump-times {if \([^=]+== 0B\)} 2 "optimized" } } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/assertion-statement-errors.C b/gcc/testsuite/g++.dg/contracts/cpp26/assertion-statement-errors.C new file mode 100644 index 000000000000..ef711368404f --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/assertion-statement-errors.C @@ -0,0 +1,41 @@ +// generic assertion-statement parsing checks +// N5008 +// assertion-statement : +// contract_assert attribute-specifier-seq opt ( conditional-expression ) ; +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts" } + +// { dg-prune-output "not declared" } + +static_assert (__cpp_contracts >= 202502L); + +contract_assert f(); // { dg-error "expected unqualified-id before .contract_assert." } + +void f(contract_assert); // { dg-error "expected primary-expression before|declared void" } +struct contract_assert{}; // { dg-error "expected unqualified-id before|expected identifier" } +void contract_assert(); // { dg-error "expected unqualified-id before" } + +void g(){}; +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." } + + i = 7; + + int j = 4; + + 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" } + + contract_assert(true) // { dg-error "expected .;. before .int." } + int k = 4; + + contract_assert(true) // { dg-error "expected .;. before .g." } + g(); + + contract_assert [[deprecated]] (i == 3); // { dg-warning {attributes are ignored on 'contract_assert'} } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/assertion-statement.C b/gcc/testsuite/g++.dg/contracts/cpp26/assertion-statement.C new file mode 100644 index 000000000000..c4ec7f887214 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/assertion-statement.C @@ -0,0 +1,34 @@ +// generic assertion-statement parsing checks +// N5008 +// assertion-statement : +// contract_assert attribute-specifier-seq opt ( conditional-expression ) ; +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts" } + +static_assert (__cpp_contracts >= 202502L); + +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; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p11-observe.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p11-observe.C new file mode 100644 index 000000000000..e97f1200c096 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p11-observe.C @@ -0,0 +1,31 @@ +// N5008 : +// dcl.contract.res/p1 +// If a contract violation occurs in a context that is not manifestly constant-evaluated and the evaluation +// semantic is enforce or observe, the contract-violation handler (6.10.3) is invoked with an lvalue referring to +// n object v of type const std::contracts::contract_violation (17.10.3) containing information about +// the contract violation. Storage for v is allocated in an unspecified manner except as noted in 6.7.6.5.2. The +// lifetime of v persists for the duration of the invocation of the contract-violation handler. +// +// Check the contract violation information in observe semantic +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } + +int f(int i, int j = 1) pre(i > 0) post(r: r < 5) +{ + contract_assert( j > 0); + return i; +} + + +int main(int, char**) +{ + f(5); + f(0,0); +} +// { dg-output "contract violation in function int f.int, int. at .*: r < 5.*(\n|\r\n|\r)" } +// { dg-output ".assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function int f.int, int. at .*: i > 0.*(\n|\r\n|\r)" } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.*(\n|\r\n|\r)" } +// { dg-output "contract violation in function int f.int, int. at .*: j > 0.*(\n|\r\n|\r)" } +// { dg-output ".assertion_kind: assert, semantic: observe, mode: predicate_false, terminating: no.*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p14.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p14.C new file mode 100644 index 000000000000..029a1cef860f --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p14.C @@ -0,0 +1,25 @@ +// N5008 : +// basic.contract.eval/p14 +// Note 10 : If the contract-violation handler returns normally and the evaluation semantic is observe, control flow +// continues normally after the point of evaluation of the contract assertion. — end note +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } + +bool check(int i){ + if (i > 10) + throw 3; + + return true; +} + +void f() pre(check(15)){} + + +int main(int, char**) +{ + + f(); +} + +// { dg-output "contract violation in function void f.. at .*: check.15..*(\n|\r\n|\r)" } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: evaluation_exception: threw an instance of .int., terminating: no.*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-2.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-2.C new file mode 100644 index 000000000000..19144b6b8578 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-2.C @@ -0,0 +1,47 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +void f(int x) noexcept pre(x>1) +{ + try{ + int i = 1; + } + catch(...) {} +} + +int main() +{ + std::set_terminate (my_term); + try + { + f(-42); + } catch (...) {} + // We should not get here + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-3.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-3.C new file mode 100644 index 000000000000..682e55ac18c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-3.C @@ -0,0 +1,58 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 13 : If a contract-violation handler invoked from an assertion-statement (8.8)) exits via an exception, the search +// for a handler continues from the execution of that statement. — end note] +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +void free_f(const int x) { + try { + contract_assert(x>1); + int i = 1; + } + catch(...){} +} + +struct X +{ + void f(const int x) { + try { + contract_assert(x>1); + int i = 1; + } + catch(...){} + } + + virtual void virt_f(const int x) { + try { + contract_assert(x>1); + int i = 1; + } + catch(...){} + } + +}; + +int main() +{ + free_f(-42); + + X x; + x.f(-42); + x.virt_f(-42); + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-4.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-4.C new file mode 100644 index 000000000000..50a59b90b2ed --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-4.C @@ -0,0 +1,55 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a post condition on a member function +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +struct X +{ + void f(const int x) noexcept post(x>1) + { + try{ + int i = 1; + } + catch(...) {} + } +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x; + x.f(-42); + } catch (...) { + } + // We should not get here + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-5.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-5.C new file mode 100644 index 000000000000..b48151f1195b --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-5.C @@ -0,0 +1,52 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a pre condition on a member function +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +struct X +{ + void f(int x) noexcept pre(x>1) { + int i = 1; + } +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x; + x.f(-42); + } catch (...) { + } + + // We should not get here + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-post.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-post.C new file mode 100644 index 000000000000..39fc55ad3ccd --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-post.C @@ -0,0 +1,54 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a post condition on a constructor with function +// try block +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +struct X +{ + + X(const int x) noexcept post(x>1) try + { + int i = 1; + } + catch(...) {} +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x(-42); + } catch (...) { + } + // We should not get here + return 1; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-pre.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-pre.C new file mode 100644 index 000000000000..6297571fdb4e --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF-pre.C @@ -0,0 +1,54 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a pre condition on a constructor with function +// try block +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +struct X +{ + + X(int x) noexcept pre(x>1) try + { + int i = 1; + } + catch(...) {} +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x(-42); + } catch (...) { + } + // We should not get here + return 1; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF2.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF2.C new file mode 100644 index 000000000000..1f55f4cca26b --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF2.C @@ -0,0 +1,57 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a pre condition on a constructor +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +struct X +{ + + X(int x) noexcept pre(x>1) + { + try{ + int i = 1; + } + catch(...) { + } + } +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x(-42); + } catch (...) { + } + // We should not get here + return 1; + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF3.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF3.C new file mode 100644 index 000000000000..605cbc277b4d --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF3.C @@ -0,0 +1,56 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a pre condition on a destructor +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +const int x = -42; + +struct X +{ + + ~X() noexcept pre(x>1) + { + try{} + catch(...){ + } + } +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x; + } catch (...) { + } + // We should not get here + return 1; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF4.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF4.C new file mode 100644 index 000000000000..2b787753caba --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17-SMF4.C @@ -0,0 +1,56 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// +// This tests the behaviour of a pre condition on an implictly noexcept +// destructor +// +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +const int x = -42; + +struct X +{ + + ~X() pre(x>1) try + { + } + catch(...) {} +}; + +int main() +{ + std::set_terminate (my_term); + try + { + X x; + } catch (...) { + } + // We should not get here + return 1; + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17.C new file mode 100644 index 000000000000..0f33722248c6 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p17.C @@ -0,0 +1,49 @@ +// N5008: +// basic.contract.eval/p17 +// If a contract-violation handler invoked from the evaluation of a function contract assertion (9.4.1) exits via +// an exception, the behavior is as if the function body exits via that same exception. +// [Note 12 : A function-try-block (14.1) is the function body when present and thus does not have an opportunity to +// catch the exception. If the function has a non-throwing exception specification, the function std::terminate is +// invoked (14.6.2). — end note] +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <contracts> +#include <exception> +#include <cstdlib> + +struct MyException{}; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + try { throw; } + catch(MyException) { std::exit(0); } +} + + +void handle_contract_violation(const std::contracts::contract_violation& violation) +{ + throw MyException{}; +} + +void f(const int x) noexcept post(x>1) +{ + try{ + int i = 1; + } + catch(...) { + } +} + +int main() +{ + std::set_terminate (my_term); + try + { + f(-42); + } catch (...) { + } + // We should not get here + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p4-error.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p4-error.C new file mode 100644 index 000000000000..1ea188245182 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p4-error.C @@ -0,0 +1,14 @@ +// N5008 : +// basic.contract.eval/p4 +// The evaluation of a contract assertion using the ignore semantic has no effect. +// [Note 2 : The predicate is potentially evaluated (6.3), but not evaluated. — end note] +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=ignore" } + +struct S; +int f(int i, int j = 1) + pre (check(i)) // { dg-error "was not declared in this scope" } +{ + contract_assert ( S{}.member ); // { dg-error "invalid use of incomplete type" } + return i; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p4.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p4.C new file mode 100644 index 000000000000..eb8f2d85ce66 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p4.C @@ -0,0 +1,27 @@ +// N5008 : +// basic.contract.eval/p4 +// The evaluation of a contract assertion using the ignore semantic has no effect. +// [Note 2 : The predicate is potentially evaluated (6.3), but not evaluated. — end note] +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=ignore" } + +#include <contracts> + +int f(int i, int j = 1) + pre (i > 0) + post (r: r < 5) +{ + contract_assert ( j > 0); + return i; +} + +int main(int, char**) +{ + f (0,0); +} + +void +handle_contract_violation (const std::contracts::contract_violation &) +{ + __builtin_abort (); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p6.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p6.C new file mode 100644 index 000000000000..b99d5a8c5162 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p6.C @@ -0,0 +1,31 @@ +// N5008 : +// basic.contract.eval/p6 +// There is an observable checkpoint ([intro.abstract]) C that happens before +// (evaluation of a contract assertion ) A such that any other operation O +// that happens before A also happens before C +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts -O -fdump-tree-original -fdump-tree-optimized -Wno-return-type" } +int f(int& i) +{ + i++; + return i; +} + +int f2 (int x, const int *y) +{ + int b = x + *y; +} + +int main(int ac, char *av[]) +{ + int i = 3; + *av[0] = (char) f(i); + contract_assert (f2(i, &i) > 42); + return i; +} +// Check that +// - observable checkpoint has been added to the contract_assert +// - observable has been removed from the optimized tree + +// { dg-final { scan-tree-dump "__builtin_observable" "original" } } +// { dg-final { scan-tree-dump "_2 = 4" "optimized" } } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p6.observe.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p6.observe.C new file mode 100644 index 000000000000..7f05fd8be1a7 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p6.observe.C @@ -0,0 +1,17 @@ +// N5008 : +// basic.contract.eval/p6 +// There is an observable checkpoint ([intro.abstract]) C that happens before +// (evaluation of a contract assertion ) A such that any other operation O +// that happens before A also happens before C +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe -fdump-tree-gimple " } + + +int main(int ac, char *av[]) +{ + int i = ac; + contract_assert (i == 3); + return i; +} + +// { dg-final { scan-tree-dump "__builtin_observable.*__tu_has_violation.*__builtin_observable" "gimple" } } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p7.3.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p7.3.C new file mode 100644 index 000000000000..6371c586dbaf --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p7.3.C @@ -0,0 +1,14 @@ +// basic.contract.eval/p7.3 +// The evaluation of the predicate is performed in a context that is manifestly +// constant-evaluated ([expr.const]) and the predicate is not a core constant +// expression. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts " } + +extern bool runtime_check (); + +consteval void foo( auto x ) pre ( runtime_check () ) {} +// { dg-error {contract condition is not constant} "" { target *-*-* } .-1 } +int main() { + foo( 42 ); // { dg-error {call to consteval function 'foo<int>\(42\)' is not a constant expression} } +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p8.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p8.C new file mode 100644 index 000000000000..311dfe31f0b7 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.contract.eval.p8.C @@ -0,0 +1,12 @@ +// basic.contract.eval/p8 +// If a contract violation occurs in a context that is manifestly +// constant-evaluated ([expr.const]), and the evaluation semantic is a +// terminating semantic, the program is ill-formed. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts " } + +consteval void foo( auto x ) pre( false ) {} +// { dg-error {contract predicate is false in constant expression} "" { target *-*-* } .-1 } +int main() { + foo( 42 ); // { dg-error {call to consteval function 'foo<int>\(42\)' is not a constant expression} } +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.scope.contract.p1.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.scope.contract.p1.C new file mode 100644 index 000000000000..389d4d03f88d --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.scope.contract.p1.C @@ -0,0 +1,42 @@ +// N5008 +// basic.scope.contract/p1 +// Each contract assertion (6.10) C introduces a contract-assertion scope that includes C. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +// Check that result vars are not visible in function bodies. + +int free_fn (int x) + post (r: r > 5); + +int free_fn (int x) + post (r: r > 5) +{ r += 1; return r;} // { dg-error {'r' was not declared in this scope} } + +struct X { + int fn0 (int x) + post (res: res > 5) + { res = 15; return res; } // { dg-error {'res' was not declared in this scope} } + + int fn1 (int x) + post (res: res > 5); +}; + +int +X::fn1 (int y) + post (ans: ans > 5) + { ans = 15; return 1; } // { dg-error {'ans' was not declared in this scope} } + +template <class T> +int +postcond (T x) post (res: res > 3); + +template <class T> +int +postcond (T x) post (out: out > 3) +{ out = 5; return x; } // { dg-error {'out' was not declared in this scope} } + +int foo (int x) +{ + return postcond (4); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/basic.scope.contract.p2.1.C b/gcc/testsuite/g++.dg/contracts/cpp26/basic.scope.contract.p2.1.C new file mode 100644 index 000000000000..9e1a05b54869 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/basic.scope.contract.p2.1.C @@ -0,0 +1,11 @@ +// N5008 : +// basic.scope.contract/p2.1 +// If a result-name-introducer (9.4.2) that is not name-independent (6.4.1) and whose enclosing postcondition +// assertion is associated with a function F potentially conflicts with a declaration whose target scope is +// - the function parameter scope of F or +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +int bad_mr_shadow (int r) + post (r: r > 5) // { dg-error "contract postcondition result name shadows a function parameter" } + { return r + 1; } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contract-assert-run.C b/gcc/testsuite/g++.dg/contracts/cpp26/contract-assert-run.C new file mode 100644 index 000000000000..760bd36c2753 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contract-assert-run.C @@ -0,0 +1,56 @@ +// 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 { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } + +#include <iostream> +#include <contracts> + +#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 >= 202502L); + +void handle_contract_violation(const std::contracts::contract_violation &violation) { + std::cerr << "custom std::handle_contract_violation called:" + << " " << violation.location().line() + << " " << violation.location().file_name() + << std::endl; + throw -(int)violation.location().line(); +} + +void foo(int x) pre (x>10){}; + +int main() +{ + + 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; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contract-assert-warn-attributes.C b/gcc/testsuite/g++.dg/contracts/cpp26/contract-assert-warn-attributes.C new file mode 100644 index 000000000000..5da1c72ed0a1 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contract-assert-warn-attributes.C @@ -0,0 +1,7 @@ +// { dg-options "-std=c++26 -fcontracts " } + +int foo (int x) +pre [[unlikley]] ( x > 41 ) // { dg-warning "attributes are ignored on function contract specifiers" } +{ + return x + 1; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contract-violation-noexcept.C b/gcc/testsuite/g++.dg/contracts/cpp26/contract-violation-noexcept.C new file mode 100644 index 000000000000..b7203936fe1e --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contract-violation-noexcept.C @@ -0,0 +1,112 @@ +// test that the default contract violation handler can't throw +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +#include <iostream> +#include <exception> +#include <cstdlib> + +// Throws on all overflow and underflow calls. +struct underflow_error: std::exception { }; +struct overflow_error: std::exception { }; +struct positioning_error: std::exception { }; + +template<typename T> +struct fail_buf + : public T +{ + typedef typename T::char_type char_type; + typedef typename T::int_type int_type; + typedef typename T::off_type off_type; + typedef typename T::pos_type pos_type; + +private: + char_type p[2]; + +public: + fail_buf() + { + p[0] = char_type('s'); + p[1] = char_type(); + this->setg(p, p, p + 1); + } + + virtual int_type underflow() + { + throw underflow_error(); + return int_type(); + } + + virtual int_type uflow() + { + throw underflow_error(); + return int_type(); + } + + virtual int_type + overflow(int_type) + { + throw overflow_error(); + return int_type(); + } + + virtual pos_type + seekoff(off_type, std::ios_base::seekdir, std::ios_base::openmode) + { + throw positioning_error(); + return pos_type(off_type(-1)); + } + + virtual pos_type + seekpos(pos_type, std::ios_base::openmode) + { + throw positioning_error(); + return pos_type(off_type(-1)); + } + + virtual int + sync() + { + throw positioning_error(); + return 0; + } +}; + +typedef fail_buf<std::streambuf> fail_streambuf; +std::streambuf* g_buf_to_restore; + +// Test that there is an active exception when we reach the terminate handler. +void my_term() +{ + std::cerr.rdbuf(g_buf_to_restore); // be nice, restore the buf of a global stream + try { throw; } + catch(const underflow_error&) { std::exit(0); } + catch(const overflow_error&) { std::exit(0); } + catch(const positioning_error&) { std::exit(0); } +} + + + +void f(int x) pre(x >= 0) +{ + try{ + int i = 1; + } + catch(...) { + } +} + +int main() +{ + std::set_terminate (my_term); + fail_streambuf buf; + g_buf_to_restore = std::cerr.rdbuf(&buf); + std::cerr.exceptions(std::ios::badbit | std::ios::failbit | std::ios::eofbit); + try + { + f(-42); + } catch (...) { + } + // We should not get here + return 1; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contract-violation-noexcept2.C b/gcc/testsuite/g++.dg/contracts/cpp26/contract-violation-noexcept2.C new file mode 100644 index 000000000000..8f41a4f8e46e --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contract-violation-noexcept2.C @@ -0,0 +1,38 @@ +// test that a noexcept function can't throw even if a violation handler throws +// { dg-do run { target c++26 } } +// { dg-additional-options "-fcontracts" } +// { dg-additional-sources "throwing-violation-handler.cc" } + +#include <exception> + +void f(int x) noexcept pre(x >= 0) +{ +} + +void g(); + +bool f_result = true; + +void my_term() +{ + try { throw; } + catch(int) { __builtin_exit(0); } +} + + +int main() +{ + std::set_terminate (my_term); + try + { + g(); + } catch (...) { + // We should not get here + __builtin_abort(); + } + if (!f_result) + // We should not get here + __builtin_abort(); + // We should not get here + __builtin_abort(); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contract_genericize.C b/gcc/testsuite/g++.dg/contracts/cpp26/contract_genericize.C new file mode 100644 index 000000000000..c34b727b8516 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contract_genericize.C @@ -0,0 +1,49 @@ +// Check that we do not get a crash +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=enforce" } + + +struct Swapper { + static unsigned int swapBytes(); + static bool isFinished(); +}; + + +struct Capacity { + Capacity(int capacity); + bool operator<( int rhs) const; + bool operator>=(int rhs) const; +}; + +template <class CAPACITY, class SWAPPER> +struct Utf32ToUtf8Translator { + + CAPACITY d_capacity; + + Utf32ToUtf8Translator(); + + static void translate(); +}; + +template <class CAPACITY,class SWAPPER> +void Utf32ToUtf8Translator<CAPACITY, SWAPPER>::translate() +{ + Utf32ToUtf8Translator<CAPACITY, SWAPPER> translator; + + unsigned int uc =0; + while (!SWAPPER::isFinished()) { + uc = SWAPPER::swapBytes(); + if (0 != uc) { + } + } + contract_assert( translator.d_capacity >= 1 ); + +} + +void func() +{ + typedef Utf32ToUtf8Translator<Capacity, + Swapper> SwapTranslator; + + SwapTranslator::translate(); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contracts-friend1.C b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-friend1.C new file mode 100644 index 000000000000..110168584b05 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-friend1.C @@ -0,0 +1,36 @@ +// ensure contracts on friend declarations are a complete class context +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } + + +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 void fn0.X. at .*.C:7: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function static void X::fns0.X. at .*.C:9: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function static void X::fns1.X. at .*.C:10: .*(\n|\r\n|\r)" } +// { dg-output "contract violation in function static void X::fns2.X. at .*.C:19: .*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class1.C b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class1.C new file mode 100644 index 000000000000..4dd9c9deb645 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class1.C @@ -0,0 +1,23 @@ +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + +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 ); + + 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}; diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class2.C b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class2.C new file mode 100644 index 000000000000..8de3005b100a --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-nested-class2.C @@ -0,0 +1,38 @@ +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } + +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); +} + diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/contracts-tmpl-spec2.C b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-tmpl-spec2.C new file mode 100644 index 000000000000..3137306ae507 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/contracts-tmpl-spec2.C @@ -0,0 +1,446 @@ +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe -fsigned-char" } +#include <cstdio> + +// template specializations can have differing contracts +template<typename T> +int body(int a) + pre ( a > 0 ) +{ + T t = a * 2.5; + return t; +} + +template<> +int body<double>(int a) + pre ( a > 1 ) +{ + double t = a * 3.3; + return t; +} + +template<typename T> +int none(int a) + pre ( a > 0 ) +{ + return -a; +} + +template<> +int none<double>(int a) + pre ( a > 1 ) +{ + return a - 100; +} + +template<typename T> +int arg0(T t) + pre ( t > 0 ) +{ + return -t - 10; +} + +template<> +int arg0<double>(double t) + pre ( t > 1 ) +{ + return -t + 10; +} + +template<typename T> +int arg1(int a, T t) + pre ( a > 0 ) + pre ( t > 0 ) +{ + return -t * a; +} + +template<> +int arg1<double>(int a, double t) + pre ( a > 1 ) + pre ( t > 1 ) +{ + return -t * a + 17; +} + +template<typename T> +T ret(int a) + pre ( a > 0 ) +{ + return -a; +} + +template<> +double ret<double>(int a) + pre ( a > 1 ) +{ + return -a * 3.3; +} + +// template specializations can have no contracts +template<typename T> +int g1(T t) pre ( t > 0 ) +{ + return (int)t; +} + +template<> +int g1<double>(double t) +{ + return (int)t; +} + +// template specializations can have no contracts in the first decl but add +// them later +template<typename T> +int g2(T t) pre ( t > 0 ) +{ + return (int)t; +} + +template<> +int g2<double>(double t); + +template<> +int g2<double>(double t) + pre ( t < 0 ) +{ + return (int)t; +} + +template<> +int g2<char>(char t) + pre ( t < 'c' ) +{ + return (int)t; +} + +// contracts can be different on the general template, partial and full specs +template<typename T, typename S> +struct G3 +{ + void f(T t, S s) + pre ( t > 0 ) + pre ( s > 0 ) + { + printf ("G3 general T S\n"); + } +}; + +template<typename S> +struct G3<int, S> +{ + void f(int t, S s) pre ( t > 1 ) pre ( s > 1 ) ; +}; + +template<typename S> +void G3<int, S>::f(int t, S s) + pre ( t > 1 ) + pre ( s > 1 ) +{ + printf ("G3 partial int S\n"); +} + +template<> +void G3<int, double>::f(int t, double s) + pre ( t > 2 ) + pre ( s > 2 ) +{ + printf ("G3 full int double\n"); +} + +struct C +{ + bool operator>(int rhs) { return false; } +}; + +// deletes contracts +template<> +void G3<int, C>::f(int t, C s); + +template<> +void G3<int, C>::f(int t, C s) +{ + printf ("G3 full int C\n"); +}; + +// specialized ctors +template<typename T, typename S> +struct G4 +{ + G4(T t, S s) + pre ( t > 0 ) + pre ( s > 0 ) + post ( x > 0 ) + { + printf ("G4 general T S\n"); + return; + } + int x{-1}; +}; + +template<typename S> +struct G4<char, S> +{ + G4(char t, S s) + pre ( t > 'c' ) + pre ( s > 3 ) + post ( x2 > 3 ) + { + printf ("G4 partial char S\n"); + return; + } + int x2{-1}; +}; + +template<> +G4<double, double>::G4(double, double) +{ + printf ("G4 full double double\n"); + return; +} + +template<> +G4<double, char>::G4(double a, char b) + pre ( a > 0 ) + pre ( b > 'b' ) + post ( x > 1 ) +{ + printf ("G4 full double char\n"); + return; +} + +// crossover of template classes and template members ok +template<typename T, typename S> +struct G5 +{ + template<typename P> + void f(T t, S s, P r) + pre ( t > 0 ) + pre ( s > 0 ) + pre ( r > 0 ) + { + printf ("G5 gen T S, f gen R\n"); + } +}; + +template<typename S> +struct G5<char, S> +{ + template<typename R> + void f(char x, S y, R z) + pre ( x > 'z' ) + pre ( y > 1 ) + pre ( z > 1 ) + { + printf ("G5 partial char S, f gen R\n"); + } +}; + +template<> +template<typename Q> +void G5<double, double>::f(double a, double b, Q c) + pre ( a > 2 ) + pre ( b > 2 ) + pre ( c > 2 ) +{ + printf ("G5 full double double, f gen R\n"); +} + +int main(int, char**) +{ + printf("%d\n", body<int>(-1)); + printf("%d\n", body<double>(-1)); + printf("%d\n", none<int>(-1)); + printf("%d\n", none<double>(-1)); + printf("%d\n", arg0(-1)); + printf("%d\n", arg0(-1.0)); + printf("%d\n", arg1(-3, -1)); + printf("%d\n", arg1(-3, -1.0)); + printf("%d\n", (int)ret<int>(-1)); + printf("%d\n", (int)ret<double>(-1)); + printf("%f\n", ret<double>(-1)); + + printf("%d\n", g1(-1)); + printf("%d\n", g1(-1.0)); + + printf("%d\n", g2(-1)); + printf("%d\n", g2(1.0)); + printf("%d\n", g2('d')); + + G3<double, double> g3_gen; + G3<int, int> g3_partial; + G3<int, double> g3_full; + g3_gen.f(-1.0, -1.0); // general + g3_partial.f(-2, -2); // partial spec + g3_full.f(-3, -3.0); // full spec + + G3<char, char> g3_gen2; + G3<int, char> g3_partial2; + g3_gen2.f((char)-1, (char)-1); + g3_partial2.f(-1, (char)-1); + + G3<int, C> g3_full2; + g3_full2.f(5, C{}); + g3_full2.f(-5, C{}); + + G4 g4_gen{-1, -1}; + G4 g4_full1{-1.0, -1.0}; + G4 g4_full2{-1.0, (char)'b'}; + G4 g4_partial{(char)'c', -5}; + + G5<int, int> g5_gen; + g5_gen.f(-1, -1, -2); + g5_gen.f(-1, -1, -2.0); + + G5<char, int> g5_part; + g5_part.f('a', -1, -2); + g5_part.f('a', -1, -2.1); + + G5<double, double> g5_full; + g5_full.f(-1.0, -1.0, -2); + g5_full.f(-1.0, -1.0, -2.1); + return 0; +} + +// { dg-output {contract violation in function int body.int. .with T = int. at .*:9: a > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-2(\n|\r\n|\r)} } +// { dg-output {contract violation in function int body.int. .with T = double. at .*:17: a > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-3(\n|\r\n|\r)} } +// { dg-output {contract violation in function int none.int. .with T = int. at .*:25: a > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {1(\n|\r\n|\r)} } +// { dg-output {contract violation in function int none.int. .with T = double. at .*:32: a > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-101(\n|\r\n|\r)} } +// { dg-output {contract violation in function int arg0.T. .with T = int. at .*:39: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-9(\n|\r\n|\r)} } +// { dg-output {contract violation in function int arg0.T. .with T = double. at .*:46: t > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {11(\n|\r\n|\r)} } +// { dg-output {contract violation in function int arg1.int, T. .with T = int. at .*:53: a > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function int arg1.int, T. .with T = int. at .*:54: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-3(\n|\r\n|\r)} } +// { dg-output {contract violation in function int arg1.int, T. .with T = double. at .*:61: a > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function int arg1.int, T. .with T = double. at .*:62: t > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {14(\n|\r\n|\r)} } +// { dg-output {contract violation in function T ret.int. .with T = int. at .*:69: a > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {1(\n|\r\n|\r)} } +// { dg-output {contract violation in function T ret.int. .with T = double. at .*:76: a > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {contract violation in function T ret.int. .with T = double. at .*:76: a > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {3.300000(\n|\r\n|\r)} } +// { dg-output {contract violation in function int g1.T. .with T = int. at .*:83: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-1(\n|\r\n|\r)} } +// { dg-output {-1(\n|\r\n|\r)} } +// { dg-output {contract violation in function int g2.T. .with T = int. at .*:97: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {-1(\n|\r\n|\r)} } +// { dg-output {contract violation in function int g2.T. .with T = double. at .*:107: t < 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {1(\n|\r\n|\r)} } +// { dg-output {contract violation in function int g2.T. .with T = char. at .*:114: t < 'c'(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G3<T, S>::f.T, S. .with T = double; S = double. at .*:124: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G3<T, S>::f.T, S. .with T = double; S = double. at .*:125: s > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G3 general T S(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G3<int, S>::f.int, S. .with S = int. at .*:139: t > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G3<int, S>::f.int, S. .with S = int. at .*:140: s > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G3 partial int S(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G3<int, S>::f.int, S. .with S = double. at .*:147: t > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G3<int, S>::f.int, S. .with S = double. at .*:148: s > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G3 full int double(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G3<T, S>::f.T, S. .with T = char; S = char. at .*:124: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G3<T, S>::f.T, S. .with T = char; S = char. at .*:125: s > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G3 general T S(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G3<int, S>::f.int, S. .with S = char. at .*:139: t > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G3<int, S>::f.int, S. .with S = char. at .*:140: s > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G3 partial int S(\n|\r\n|\r)} } +// { dg-output {G3 full int C(\n|\r\n|\r)} } +// { dg-output {G3 full int C(\n|\r\n|\r)} } +// { dg-output {contract violation in function G4<T, S>::G4.T, S. .with T = int; S = int. at .*:173: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function G4<T, S>::G4.T, S. .with T = int; S = int. at .*:174: s > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G4 general T S(\n|\r\n|\r)} } +// { dg-output {contract violation in function G4<T, S>::G4.T, S. .with T = int; S = int. at .*:175: x > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G4 full double double(\n|\r\n|\r)} } +// { dg-output {contract violation in function G4<T, S>::G4.T, S. .with T = double; S = char. at .*:206: a > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function G4<T, S>::G4.T, S. .with T = double; S = char. at .*:207: b > 'b'(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G4 full double char(\n|\r\n|\r)} } +// { dg-output {contract violation in function G4<T, S>::G4.T, S. .with T = double; S = char. at .*:208: x > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function G4<char, S>::G4.char, S. .with S = int. at .*:187: t > 'c'(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function G4<char, S>::G4.char, S. .with S = int. at .*:188: s > 3(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G4 partial char S(\n|\r\n|\r)} } +// { dg-output {contract violation in function G4<char, S>::G4.char, S. .with S = int. at .*:189: x2 > 3(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: post, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = int; T = int; S = int. at .*:220: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = int; T = int; S = int. at .*:221: s > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = int; T = int; S = int. at .*:222: r > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G5 gen T S, f gen R(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = double; T = int; S = int. at .*:220: t > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = double; T = int; S = int. at .*:221: s > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = double; T = int; S = int. at .*:222: r > 0(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G5 gen T S, f gen R(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G5<char, S>::f.char, S, R. .with R = int; S = int. at .*:233: x > 'z'(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<char, S>::f.char, S, R. .with R = int; S = int. at .*:234: y > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<char, S>::f.char, S, R. .with R = int; S = int. at .*:235: z > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G5 partial char S, f gen R(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G5<char, S>::f.char, S, R. .with R = double; S = int. at .*:233: x > 'z'(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<char, S>::f.char, S, R. .with R = double; S = int. at .*:234: y > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<char, S>::f.char, S, R. .with R = double; S = int. at .*:235: z > 1(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G5 partial char S, f gen R(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = int; T = double; S = double. at .*:244: a > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = int; T = double; S = double. at .*:245: b > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = int; T = double; S = double. at .*:246: c > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G5 full double double, f gen R(\n|\r\n|\r)} } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = double; T = double; S = double. at .*:244: a > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = double; T = double; S = double. at .*:245: b > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {contract violation in function void G5<T, S>::f.T, S, P. .with P = double; T = double; S = double. at .*:246: c > 2(\n|\r\n|\r)} } +// { dg-output ".assertion_kind: pre, semantic: observe, mode: predicate_false, terminating: no.(\n|\r\n|\r)" } +// { dg-output {G5 full double double, f gen R(\n|\r\n|\r)} } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p4.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p4.C new file mode 100644 index 000000000000..cec6876b667a --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p4.C @@ -0,0 +1,138 @@ +// N5008 : +// A declaration D of a function or function template f that is not a first declaration shall have either no +// function-contract-specifier-seq or the same function-contract-specifier-seq (see below) as any first declaration +// F reachable from D. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + +// allowed to repeat contracts or omit them +int g0(int a) pre (a > 0); +int g0(int a) pre (a > 0 ); +int g0(int a) pre (a > 0); + +int g1(int a) pre (a > 0); +int g1(int a); + +int g2(int a); +int g2(int a) pre (a > 0); // { dg-error "declaration adds contracts" } + +struct G0 +{ + int f(int a); +}; + +int G0::f(int a) pre (a > 0) // { dg-error "declaration adds contracts" } +{ + return -a; +} + +struct G1 +{ + int f(int a) + pre (a > 0); +}; + +int G1::f(int a) + pre ( a > 0 ) // OK. same as the first decl. +{ return 0; } + +int G1::f(int a); // OK not adding + +int G1::f(int a) // { dg-error "different number of contracts" } + pre ( a > 0 ) pre ( a > 0 ); + +int f0(int a) + pre ( a > 0 ); + +int f0(int a) // { dg-error "different number of contracts" } + pre ( a > 0 ) pre ( a > 10 ); + +int f1(int a) + pre ( a > 0 ); +int f1(int a) + pre ( a < 0 ); // { dg-error "mismatched contract" } + +struct Base +{ + virtual int f(int a) // { dg-error "contracts cannot be added to virtual functions" } + pre (a > 0); +}; + +struct Child : Base +{ + int f(int a) // { dg-error "contracts cannot be added to virtual functions" } + pre (a < 0); +}; + +struct F1 +{ + virtual int f(int a); +}; + +int F1::f(int a) // { dg-error "declaration adds contracts" } + pre (a > 0) +{ + return -a; +} + + +struct T1 +{ + void vfun(int m, double n); +}; + +void T1::vfun(int m, double n) + pre ( x < 0 ) // { dg-error "was not declared in this" } +{ +} + +void T1::vfun(int m, double n) // { dg-error "declaration adds contracts" } + pre(true); + +struct Foo { + virtual void f10 (int) // { dg-error "contracts cannot be added to virtual functions" } + pre ( false ) {} +}; + +struct Bar : Foo { + void f10 (int n = 0) override // { dg-error "contracts cannot be added to virtual functions" } + pre ( false ); +}; + +// we currently don't diagnose an error here if the original contract was erroneous. +void Bar::f10(int n) + pre (n >10) {}; // { dg-error "mismatched contract" } + + +struct NonTrivial{ + NonTrivial(){}; + NonTrivial(const NonTrivial&){} + ~NonTrivial(){}; + int x = 0; +}; + +void f(const NonTrivial s) pre(s.x >0); +void f(const NonTrivial g) {}; +void f(const NonTrivial t) pre(t.x >0); + +void g(const NonTrivial t, const NonTrivial t1) pre(t.x >0); +void g(const NonTrivial t, const NonTrivial t1) pre(t1.x >0); // { dg-error "mismatched contract" } +void g(const NonTrivial g, const NonTrivial t1) pre(g.x >0){}; +void g(const NonTrivial t, const NonTrivial t1) pre(t1.x >0); // { dg-error "mismatched contract" } + +template<typename T> +bool somefunc(const T t){ return true;} + +void h(const NonTrivial t, const NonTrivial t1) pre(somefunc(t) == true){}; +void h(const NonTrivial t, const NonTrivial t1) pre(somefunc(t) == true); +void h(const NonTrivial t, const NonTrivial t1) pre(somefunc(t1) == true); // { dg-error "mismatched contract" } + +double sqrt(const double x) + post( r : r >= 0 ); + + +double sqrt(const double x) + post( r : r >= 0) +{ + return x; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p6.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p6.C new file mode 100644 index 000000000000..3f845216b563 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p6.C @@ -0,0 +1,21 @@ +// N5008: +// dcl.contract.func/p6 +// A virtual function (11.7.3), +// TODO : a deleted function (9.6.3), or a function defaulted on its first declaration (9.6.2) +// shall not have a function-contract-specifier-seq. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + +struct Base +{ + virtual int f1() pre(true); // { dg-error "contracts cannot be added to virtual functions" } + virtual int f2(); + +}; +struct Child : Base +{ + int f2() pre(true); // { dg-error "contracts cannot be added to virtual functions" } + +}; + + diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7-t1.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7-t1.C new file mode 100644 index 000000000000..91d3f4a306eb --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7-t1.C @@ -0,0 +1,161 @@ +// N5008: +// dcl.contract.func/p7 +// If the predicate of a postcondition assertion of a function f odr-uses (6.3) a non-reference parameter of f, +// that parameter and the corresponding parameter on all declarations of f shall have const type +// These tests instantiate templates with a non const type. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } +#include <type_traits> +struct NTClass { + NTClass(){}; + ~NTClass(){}; + int x = 1; +}; + +template <typename... ARGS> +bool check(ARGS... args){ return true;} + +namespace checkDeclaration{ + +template <class T> +void ft1 (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)){}; + +template void ft1<int>(int, int&, int*, int const*, int* const); +// { dg-error "value parameter 'i'" "" { target *-*-* } 21 } +// { dg-error "value parameter 'k'" "" { target *-*-* } 21 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 21 } + + +struct PostCond { + + template <class T> + PostCond (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)){}; + + template <class T> + void ft1 (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)){}; + +}; + +template +PostCond::PostCond<int> (int i, int &j, int *k, const int * l, int * const m); +// { dg-error "value parameter 'i'" "" { target *-*-* } 37 } +// { dg-error "value parameter 'k'" "" { target *-*-* } 37 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 37 } + +template +void PostCond::ft1<NTClass>(NTClass, NTClass&, NTClass*, NTClass const*, NTClass* const); +// { dg-error "value parameter 'i'" "" { target *-*-* } 45 } +// { dg-error "value parameter 'k'" "" { target *-*-* } 45 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 45 } + + +template <typename T> +struct PostCondT +{ + + PostCondT (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)){}; + + template <class U> + void f (U i, U &j, U *k, const U * l, U * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)){}; +}; + + +template +PostCondT<int>::PostCondT (int i, int &j, int *k, const int * l, int * const m); +// { dg-error "value parameter 'i'" "" { target *-*-* } 71 } +// { dg-error "value parameter 'k'" "" { target *-*-* } 71 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 71 } + +template +void PostCondT<int>::f<NTClass>(NTClass, NTClass&, NTClass*, NTClass const*, NTClass* const); +// { dg-error "value parameter 'i'" "" { target *-*-* } 79 } +// { dg-error "value parameter 'k'" "" { target *-*-* } 79 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 79 } + +template <typename T> +int f2(const T i[10]) +post(r : r == i[0]){ return 1;}; + +template +int f2<int>(const int i[10]); +// { dg-error "used in a postcondition must be const" "" { target *-*-* } 101 } + +// P3520 +template <typename T> +void f4(std::add_const_t<T> t) post(t > 0){}; //ok +template <typename T> +void f5(T t) post (t > 0){}; + +void test2(){ + f4<int>(1); + f5<int>(1); // { dg-error "used in a postcondition must be const" "" { target *-*-* } 112 } +} +} + + +namespace checkNonOdr{ + +template <class T> +void f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; + +template +void f<int>(int, int*); + +struct PostCond { + template <class T> + void f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; +}; + +template +void PostCond::f<int>(int, int*); + + +template <typename T> +struct PostCondT +{ + void f1 (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; + + template <class U> + void f2 (U i, U *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; +}; + +template +void PostCondT<int>::f1(int, int*); + +template +void PostCondT<int>::f2<NTClass>(NTClass, NTClass*); + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7-t2.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7-t2.C new file mode 100644 index 000000000000..9fc8dd424ba3 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7-t2.C @@ -0,0 +1,157 @@ +// N5008: +// dcl.contract.func/p7 +// If the predicate of a postcondition assertion of a function f odr-uses (6.3) a non-reference parameter of f, +// that parameter and the corresponding parameter on all declarations of f shall have const type +// These tests instantiate templates with a const type. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } +#include <type_traits> +struct NTClass { + NTClass(){}; + ~NTClass(){}; + int x = 1; +}; + +template <typename... ARGS> +bool check(ARGS... args){ return true;} + +namespace checkDeclaration{ + +template <class T> +void ft1 (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)){}; + +template void ft1<const int>(const int, const int&, const int*, int const*, const int* const); +// { dg-error "value parameter 'k'" "" { target *-*-* } 21 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 21 } + + +struct PostCond { + + template <class T> + PostCond (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)){}; + + template <class T> + void ft1 (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)){}; + +}; + +template +PostCond::PostCond<const int> (const int i, const int &j,const int *k, const int * l, const int * const m); +// { dg-error "value parameter 'k'" "" { target *-*-* } 36 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 36 } + +template +void PostCond::ft1<const NTClass>(const NTClass, const NTClass&, const NTClass*, NTClass const*, const NTClass* const); +// { dg-error "value parameter 'k'" "" { target *-*-* } 44 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 44 } + + +template <typename T> +struct PostCondT +{ + + PostCondT (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)){}; + + template <class U> + void f (U i, U &j, U *k, const U * l, U * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)){}; +}; + + +template +PostCondT<const int>::PostCondT (const int i, const int &j, const int *k, const int * l, const int * const m); +// { dg-error "value parameter 'k'" "" { target *-*-* } 68 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 68 } + +template +void PostCondT<const int>::f<const NTClass>(const NTClass, const NTClass&, const NTClass*, NTClass const*, const NTClass* const); +// { dg-error "value parameter 'k'" "" { target *-*-* } 76 } +// { dg-error "value parameter 'l'" "" { target *-*-* } 76 } + +template <typename T> +int f2(const T i[10]) +post(r : r == i[0]){ return 1;}; + +template +int f2<const int>(const int i[10]); +// { dg-error "used in a postcondition must be const" "" { target *-*-* } 96 } + +// P3520 +template <typename T> +void f4(std::add_const_t<T> t) post(t > 0){}; //ok +template <typename T> +void f5(T t) post (t > 0){}; + +void test2(){ + int i[10]; + f4<const int>(1); + f5<const int>(1); +} +} + + +namespace checkNonOdr{ + +template <class T> +void f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; + +template +void f<const int>(const int, const int*); + +struct PostCond { + template <class T> + void f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; +}; + +template +void PostCond::f<const int>(const int, const int*); + + +template <typename T> +struct PostCondT +{ + void f1 (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; + + template <class U> + void f2 (U i, U *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))){}; +}; + +template +void PostCondT<const int>::f1(const int, const int*); + +template +void PostCondT<const int>::f2<const NTClass>(const NTClass, const NTClass*); + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C new file mode 100644 index 000000000000..0d38f85fe056 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.func.p7.C @@ -0,0 +1,335 @@ +// N5008: +// dcl.contract.func/p7 +// If the predicate of a postcondition assertion of a function f odr-uses (6.3) a non-reference parameter of f, +// that parameter and the corresponding parameter on all declarations of f shall have const type +// These tests do not instantiate templates with diagnosable errors +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } +#include <type_traits> +struct NTClass { + NTClass(){}; + ~NTClass(){}; + int x = 1; +}; + +template <typename... ARGS> +bool check(ARGS... args){ return true;} + +namespace checkDeclaration{ + +int f (int i, int &j, int *k, const int * l, int * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)); + +int f (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)); + +template <class T> +void ft1 (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)){}; + + +struct PostCond { + + PostCond (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)); + + template <class T> + PostCond (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)); + + int f (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)); + + template <class T> + int ft1 (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)); +}; + +/* Never instantiated to check errors without an instantiation. */ +template <typename T> +struct PostCondT +{ + + PostCondT (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)); + + PostCondT (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)); + + template <class U> + PostCondT (U i, U &j, U *k, const U * l, U * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)); + + int f (NTClass i, NTClass &j, NTClass *k, const NTClass * l, NTClass * const m) + post ( check (i)) // { dg-error "used in a postcondition must be const" } + post ( check (j)) + post ( check (k)) // { dg-error "used in a postcondition must be const" } + post ( check (l)) // { dg-error "used in a postcondition must be const" } + post ( check (m)); + + int f (T i, T &j, T *k, const T * l, T * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)); + + template <class U> + int f (U i, U &j, U *k, const U * l, U * const m) + post ( check (i)) + post ( check (j)) + post ( check (k)) + post ( check (l)) + post ( check (m)); +}; + + +// P29000R14 [dcl.contract.func]/p7 +int f(const int i[10]) +post(r : r == i[0]); // { dg-error "used in a postcondition must be const" } + +template <typename T> +int f(const T i[10]) +post(r : r == i[0]); + +} + +// P3520 +using const_int_t = const int; +void f3(const_int_t i) post (i > 0); //ok + +template <typename T> +void f4(std::add_const_t<T> t) post(t > 0); //ok + +template <typename T> +void f5(T t) post (t > 0); + + +namespace nonFirstDeclaration +{ + + int f (const NTClass i) + post ( check (i)); + + template <class T> + int f (const T i) + post ( check (i)); + + struct PostCond { + void f (const NTClass i) + post ( check (i)); + + template <class T> + void f (const T i) + post ( check (i)); + }; + + template <typename T> + struct PostCondT + { + void f (const NTClass i) + post ( check (i)); + + void f (const T i) + post ( check (i)); + + template <class U> + void f (const U i) + post ( check (i)); + }; + + int + f (NTClass i); // { dg-error "used in a postcondition must be const" } + + template<class T> + int + f (T i); + + void + PostCond::f (NTClass i){} // { dg-error "used in a postcondition must be const" } + + template<class T> + void + PostCond::f (T i){} + + template<typename T> + void PostCondT<T>::f (NTClass i){} // { dg-error "used in a postcondition must be const" } + + template<typename T> + void PostCondT<T>::f (T i){} + + template<typename T> + template<typename U> + void PostCondT<T>::f (U i){} + +} + +namespace nonFirstDeclarationNegative +{ + + int f (const NTClass i) + post ( check (true)) + pre ( check (i)); + + template <class T> + int f (const T i) + post ( check (true)) + pre ( check (i)); + + struct PostCond { + void f (const NTClass i) + post ( check (true)) + pre ( check (i)); + + template <class T> + void f (const T i) + post ( check (true)) + pre ( check (i)); + }; + + template <typename T> + struct PostCondT + { + void f (const NTClass i) + post ( check (true)) + pre ( check (i)); + + void f (const T i) + post ( check (true)) + pre ( check (i)); + + template <class U> + void f (const U i) + post ( check (true)) + pre ( check (i)); + }; + + int + f (NTClass i); + + template<class T> + int + f (T i); + + void + PostCond::f (NTClass i){} + + template<class T> + void + PostCond::f (T i){} + + template<typename T> + void PostCondT<T>::f (NTClass i){} + + template<typename T> + void PostCondT<T>::f (T i){} + + template<typename T> + template<typename U> + void PostCondT<T>::f (U i){} + +} + +namespace checkNonOdr{ + +int f (int i, int *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + +template <class T> +int f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + +struct PostCond { + + PostCond (int i, int *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + + template <class T> + PostCond (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + int f (int i, int *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + template <class T> + int f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); +}; + +template <typename T> +struct PostCondT +{ + + PostCondT (int i, int *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + template <class U> + PostCondT (U i, U *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + int f (int i, int *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + int f (T i, T *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); + + template <class U> + int f (U i, U *j) + post ( sizeof(i) ) + post ( noexcept(check (&j))); +}; + + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C new file mode 100644 index 000000000000..74bf7d018af9 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-2.C @@ -0,0 +1,69 @@ +// N5008 : +// dcl.contract.res/p1 +// The result-name-introducer of a postcondition-specifier is a declaration. The result-name-introducer introduces +// the identifier as the name of a result binding of the associated function. If a postcondition assertion has a +// result-name-introducer and the return type of the function is cv void, the program is ill-formed. A result +// binding denotes the object or reference returned by invocation of that function. The type of a result binding +// is the return type of its associated function. The optional attribute-specifier-seq of the attributed-identifier in +// the result-name-introducer appertains to the result binding so introduced. +// [Note 1 : An id-expression that names a result binding is a const lvalue (7.5.5.2). — end note] +// +// check that the return value has correct const qualification +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + + +#include <type_traits> + +bool is_constified(int &){ return false;} + +bool is_constified(const int &){ return true;} + + +int f1(int i) + pre (is_constified(i)) + pre (std::is_same<decltype(i), int>::value) + pre (std::is_same<decltype((i)), const int&>::value) + + post (r: is_constified(r)) + post (r: std::is_same<decltype(r), int>::value) + post (r: std::is_same<decltype((r)), const int&>::value) + { return i; }; + +int& f2(int& i) + pre (is_constified(i)) + pre (std::is_same<decltype(i), int&>::value) + pre (std::is_same<decltype((i)), const int&>::value) + + post (r: is_constified(r)) + post (r: std::is_same<decltype(r), int&>::value) + post (r: std::is_same<decltype((r)), const int&>::value) + { + static_assert(std::is_same<decltype(i), int&>::value); + return i; + } + +int* f2(int* i) + pre (std::is_same<decltype(i), int*>::value) + pre (std::is_same<decltype((i)),int * const>::value) + + post (r: std::is_same<decltype(r), int *>::value) + post (r: std::is_same<decltype((r)),int * const>::value) + { return i;} + +int const * f2(int const * i) + pre (std::is_same<decltype(i), int const *>::value) + pre (std::is_same<decltype((i)),int const * const>::value) + + post (r: std::is_same<decltype(r), int const *>::value) + post (r: std::is_same<decltype((r)),int const * const>::value) + { return i;} + +int main() +{ + + int i = 4; + f1(i); + f2(i); + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-NT.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-NT.C new file mode 100644 index 000000000000..5cbfb6117584 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1-NT.C @@ -0,0 +1,146 @@ +// N5008 : +// dcl.contract.res/p1 +// The result-name-introducer of a postcondition-specifier is a declaration. The result-name-introducer introduces +// the identifier as the name of a result binding of the associated function. If a postcondition assertion has a +// result-name-introducer and the return type of the function is cv void, the program is ill-formed. +// +// Various tests with non trivial return value identifier +// +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + + +struct NonTrivial{ + NonTrivial(){}; + NonTrivial(const NonTrivial&){} + ~NonTrivial(){}; + int x = 0; +}; + +template<typename T> +bool check(T t){ + return true; +} +struct S{ + NonTrivial f1(const NonTrivial i) post(r: i.x > 0 ) { return NonTrivial{};} + + template <typename T> + NonTrivial f2(const T i) post(r: i.x > 0 ) { return NonTrivial{};} + + auto f3(const NonTrivial i) post(r: i.x > 0 ) { return NonTrivial{};} + + template <typename T> + T f4(const T i) post(r: i.x > 0 ) { return NonTrivial{};} + + template <typename T> + auto f5(const T i) post(r: i.x > 0 ) { return i;} + + template <typename T> + auto f6(const T i) post(r: check(i) ) { return i;} + + auto f7(const NonTrivial i) post(r: check(r) ) { return i;} + +}; + +template <typename U> +struct S1 +{ + + struct S2 + { + NonTrivial + f1 (const NonTrivial i) post(r: i.x > 0 ) + { return NonTrivial + {}; + } + + template <typename T> + NonTrivial + f2 (const T i) post(r: i.x > 0 ) + { return NonTrivial + {}; + } + + auto + f3 (const NonTrivial i) + post(r: i.x > 0 ) + { return NonTrivial + {};} + + template <typename T> + T + f4 (const T i) + post(r: i.x > 0 ) + { return NonTrivial + {};} + + template <typename T> + auto + f5 (const T i) + post(r: i.x > 0 ) + { return i;} + + }; + + NonTrivial + f1 (const NonTrivial i) + post(r: i.x > 0 ) + { S2 s; + return s.f1(i); + } + + template <typename T> + NonTrivial + f2 (const T i) + post(r: i.x > 0 ) + { S2 s; + return s.f2(i); + } + + auto + f3 (const NonTrivial i) + post(r: i.x > 0 ) + { S2 s; + return s.f3(i); + } + + template <typename T> + T + f4 (const T i) + post(r: i.x > 0 ) + { S2 s; + return s.f4(i); + } + + template <typename T> + auto + f5 (const T i) + post(r: i.x > 0 ) + { S2 s; + return s.f5(i); + } + + }; + + +int main() +{ + S s; + NonTrivial n; + s.f1(NonTrivial{}); + + s.f2(n); + s.f3(n); + s.f4(n); + s.f5(n); + s.f6(n); + s.f7(n); + + + S1<NonTrivial> s1; + s1.f1(n); + s1.f2(n); + s1.f3(n); + s1.f4(n); + s1.f5(n); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1.C new file mode 100644 index 000000000000..7456a86fdf99 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p1.C @@ -0,0 +1,23 @@ +// N5008 : +// dcl.contract.res/p1 +// The result-name-introducer of a postcondition-specifier is a declaration. The result-name-introducer introduces +// the identifier as the name of a result binding of the associated function. If a postcondition assertion has a +// result-name-introducer and the return type of the function is cv void, the program is ill-formed. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + + +void f(const int i) post (r: i > 2); // { dg-error "function does not return a value to test" } + +template <typename T> +void g() post (r: true ){}; // { dg-error "function does not return a value to test" } + +template +void g<int>(); + +struct S{ + + S() post (r : true); // { dg-error "does not return a value to test" } + void f(const int i) post (r: i > 2); // { dg-error "function does not return a value to test" } + ~S() post (r : true); // { dg-error "does not return a value to test" } +}; diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p2.C b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p2.C new file mode 100644 index 000000000000..326ede4d0243 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dcl.contract.res.p2.C @@ -0,0 +1,25 @@ +// N5008 : +// dcl.contract.res/p2 +// When the declared return type of a non-templated function contains a placeholder type, a postcondition- +// specifier with a result-name-introducer shall be present only on a definition. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + + +auto no_deduced_res_types_on_non_defs (int x) // { dg-error "postconditions with deduced result name types must only appear on function definitions" } + pre (x > 1) + post (r: r > 17); + +// ===== + +auto g(auto&) +post (r: r >= 0); // OK, g is a template. + +auto f2() post (r : r > 0) // OK, type of r is deduced below. +{ return 5; } + +template <typename T> +auto f3 () post (r: r > 0); // OK, postcondition instantiated with template + +auto f4 () post (true); // OK, return value not named + diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/debug-and-opt.C b/gcc/testsuite/g++.dg/contracts/cpp26/debug-and-opt.C new file mode 100644 index 000000000000..0d6fa197895a --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/debug-and-opt.C @@ -0,0 +1,15 @@ +// Check that we do not ICE with debug + optimisation. +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe -O -g" } + + +int foo (const int i) + pre (i > 3) +{ + return i; +} + +int main() +{ + foo (1); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/deferred1.C b/gcc/testsuite/g++.dg/contracts/cpp26/deferred1.C new file mode 100644 index 000000000000..165f8eded78c --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/deferred1.C @@ -0,0 +1,49 @@ +// This case should be diagnosed, but we do not handl deferred contracts properly at the moment. +// For now, diagnose that we at least don't accidentally merge the contracts +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -g3" } +#include <cassert> +struct contract +{ + int checked = 0; +}; + +contract a, b; + +bool +checkA () +{ + a.checked++; + return true; +} + +bool +checkB () +{ + b.checked++; + return true; +} + +void +clear_checks () +{ + a.checked = b.checked = 0; + +} + +struct S +{ + friend int f1(S) post (checkB()); + friend int f1(S) pre (checkA()){ return 1;}; +}; + + +int main() +{ + S s; + + clear_checks (); + f1(s); + assert (a.checked > 0); + assert (b.checked == 0); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/dependent_contract.C b/gcc/testsuite/g++.dg/contracts/cpp26/dependent_contract.C new file mode 100644 index 000000000000..e4f35c13b8d1 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/dependent_contract.C @@ -0,0 +1,32 @@ +// check that dependent contract check does not cause an ICE +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } +template <typename ST> +struct S{ + + int* check() const + { + static int i = 6; + return &i; + } + + template <typename T> + T* tcheck(T) const{ + static T t; + return nullptr; + } + + void f() pre(check()) pre(tcheck(1)){ + } + + template <typename T> + void f(T t) pre(check()) pre(tcheck(1)) pre(tcheck<T>(t)){ + } + +}; + +int main() { + S<int> s; + s.f(); + s.f(1); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/empty-nt-param.C b/gcc/testsuite/g++.dg/contracts/cpp26/empty-nt-param.C new file mode 100644 index 000000000000..8ac0d41e0f51 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/empty-nt-param.C @@ -0,0 +1,42 @@ +// check that we do not ICE with an empty nontrivial parameter +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts" } + +struct NTClass { + NTClass(){}; + NTClass(const NTClass&){} + ~NTClass(){}; +}; + +struct Empty {}; + +void f (const NTClass i) pre (true){ +} + +void g (const Empty i) pre (true){ +} + + +struct S { + void f (const NTClass i) + post ( true); + + void g (const Empty i) + post ( true); + +}; + +void +S::f (NTClass i){} + +void +S::g (Empty i){} + +int main(){ + + NTClass n; + f(n); + g(Empty{}); + + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C new file mode 100644 index 000000000000..2614a4b80e87 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7-2.C @@ -0,0 +1,65 @@ +// N5008 [expr.prim.id.unqual]/p7 +// Otherwise, if the unqualified-id appears in the predicate of a contract assertion C (6.10) and the entity is +// (7.1) — a variable declared outside of C of object type T, +// (7.2) — a variable or template parameter declared outside of C of type “reference to T”, or +// (7.3) — a structured binding of type T whose corresponding variable is declared outside of C, +// then the type of the expression is const T +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=enforce -O" } + +struct S{ + S(){}; + S(const S&){} + ~S(){}; + int x = 0; +}; + +int i = 3; +int *pi = &i; +int *pi2 = &i; + + +void f1() pre(i++) {} // { dg-error "increment of read-only location" } +void f2() pre((*pi)++) {}; +void f3() pre(pi2++) {} // { dg-error "increment of read-only location" } + +int a[2] = {1, 2}; +auto [x, y] = a; +auto& [xr, yr] = a; + +void f4() pre(x++) {} // { dg-error "increment of read-only location" } +void f5() pre(xr++) {}; // { dg-error "increment of read-only location" } + +void f7(int x) pre([](int& i) + { + return true; + }(x)){} // { dg-error "discards qualifiers" } +// { dg-error {no match for call to} "" { target *-*-* } .-1 } + + +int g = 0; +struct X { bool m(); }; +struct Y { + int z = 0; + + void f(int i) pre (++g); // { dg-error "increment of read-only location" } + void f2(int i) pre (++i); // { dg-error "increment of read-only location" } + void f3(int* p) pre (++(*p)); // OK + void f4(int& r) pre (++r); // { dg-error "increment of read-only location" } + void f5(X x) pre (x.m()); // { dg-error " argument discards qualifiers" } + void f6(X* px) pre (px->m()); // OK + + template <int N, int& R, int* P> + void tf1() pre(++N); // { dg-error "increment of read-only location" } + // { dg-error {lvalue required as increment operand} "" { target *-*-* } .-1 } + + template <int N, int& R, int* P> + void tf2() pre(++R); // { dg-error "increment of read-only location" } + + template <int N, int& R, int* P> + void tf3() pre(++(*P)); // OK + + int h1() post(r : ++r); // { dg-error "increment of read-only" } + + int& k() post(r : ++r); // { dg-error "increment of read-only" } +}; diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C new file mode 100644 index 000000000000..4549b5460ecf --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/expr.prim.id.unqual.p7.C @@ -0,0 +1,139 @@ +// N5008 [expr.prim.id.unqual]/p7 +// Otherwise, if the unqualified-id appears in the predicate of a contract assertion C (6.10) and the entity is +// (7.1) — a variable declared outside of C of object type T, +// (7.2) — a variable or template parameter declared outside of C of type “reference to T”, or +// (7.3) — a structured binding of type T whose corresponding variable is declared outside of C, +// then the type of the expression is const T +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts" } + + +static_assert (__cpp_contracts >= 202502L); + +int gi = 7; +int &gri = gi; +int *gpi = &gi; + +bool f(int&&){ return true;} +int temporary(){ return 4;} + +void f1(int i) pre(i++); // { dg-error "increment of read-only location" } +void f2(int &i) pre(i++); // { dg-error "increment of read-only location" } +void f3(int *i) pre(i++); // { dg-error "increment of read-only location" } +void f4(int *i) pre((*i)++); // ok, not deep const +void f5(int *i) pre(gi++); // { dg-error "increment of read-only location" } +void f6() pre(f(temporary())); // ok, lifetime started within pre condition + + +struct S{ + + int i; + mutable int mi; + int *pi = &i; + int &ri = i; + int &rmi = mi; + int *pmi = &mi; + + + void f(){ + int array[2] = {1,2}; + auto [sb1, sb2] = array; + + contract_assert(i++); // { dg-error "increment of member" } + contract_assert(mi++); // ok, mutable + contract_assert(sb1++); // { dg-error "increment of read-only location" } + + + contract_assert(ri++); // ok, not deep const + contract_assert(rmi++); // ok, not deep const + + contract_assert(pi++); // { dg-error "increment of member" } + contract_assert((*pi)++); // ok, not deep const + + + contract_assert(pmi++); // { dg-error "increment of member" } + contract_assert((*pmi)++); // ok, not deep const + + contract_assert(gi++); // { dg-error "increment of read-only location" } + contract_assert(gpi++); // { dg-error "increment of read-only location" } + contract_assert((*gpi)++); // ok, not deep const + } + +}; + +template <class T> void perfect_forward(T&& t) pre(++t) {} // { dg-error "increment of read-only" } + +struct S2 +{ + int i = 0; + template <class Self> void perfect_forward(this Self&& self) pre(++self.i) {} // { dg-error "increment of member.*in read-only object" } +}; + +void template_related_tests() +{ + int i = 0; + const int ci = 42; + perfect_forward(i); + perfect_forward(666); + perfect_forward(ci); + perfect_forward((const int&&) ci); + S2 s; + const S2 cs; + s.perfect_forward(); + cs.perfect_forward(); + S2().perfect_forward(); + ((const S2&&)S2()).perfect_forward(); +} + +int n = 0; +struct X { bool m(); }; + +struct Y { + int z = 0; + + void f(int i, int* p, int& r, X x, X* px) + pre (++n) // { dg-error "increment of read-only location" } + pre (++i) // { dg-error "increment of read-only location" } + pre (++(*p)) // OK + pre (++r) // { dg-error "increment of read-only location" } + pre (x.m()) // { dg-error "discards qualifiers" } + pre (px->m()); // OK + + template <int N, int& R, int* P> + void g() + pre(++N) // { dg-error "increment of read-only location" } + // { dg-error {lvalue required as increment operand} "" { target *-*-* } .-1 } + pre(++R) // { dg-error "increment of read-only location" } + pre(++(*P)); // OK + + int& k() + post(r : ++r); // { dg-error "increment of read-only location" } +}; + +int main() +{ + int i; + contract_assert(i++); // { dg-error "increment of read-only location" } + i = 5; + + int& ri = i; + contract_assert(ri++); // { dg-error "increment of read-only location" } + ri = 6; + + contract_assert(gi++); // { dg-error "increment of read-only location" } + contract_assert(gri++); // { dg-error "increment of read-only location" } + + int& rgi = gi; + contract_assert(rgi++); // { dg-error "increment of read-only location" } + rgi = 6; + + + int *pi= &i; + contract_assert(pi++); // { dg-error "increment of read-only location" } + contract_assert((*pi)++); // ok, not deep const + + contract_assert(i == 4 ? i : i ); // ok, no name clash + + + return 0; +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/function-contract-specifier-seq-error.C b/gcc/testsuite/g++.dg/contracts/cpp26/function-contract-specifier-seq-error.C new file mode 100644 index 000000000000..e979ad19e2af --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/function-contract-specifier-seq-error.C @@ -0,0 +1,50 @@ +// generic function-contract-specifier-seq parsing checks +// N5008 +// function-contract-specifier-seq : +// function-contract-specifier function-contract-specifier-seq opt +// function-contract-specifier : +// precondition-specifier +// postcondition-specifier +// precondition-specifier : pre attribute-specifier-seq opt ( conditional-expression ) +// postcondition-specifier : +// post attribute-specifier-seq opt ( result-name-introducer opt conditional-expression ) +// { dg-do compile { target c++26 } } +// { dg-additional-options "-fcontracts" } + +int f(int); +int g1(int a) pre(f(a) > a) pre (true) post(false) pre(f(1) > 4) +{ + int r = a - f(a); + return 2 * r; +} + +int fun(int n) pre (n > 0 ); +void fun2(int n) pre n > 0 ; // { dg-error {expected '\(' before } } + // { dg-error {expected '\)' before ';' token} "" { target *-*-* } .-1 } +void fun2(int n) pre (: n > 0 ]]; // { dg-error } + +int main() +{ + int x; + + contract_assert ( x >= 0 ); + + contract_assert ( x > 0 ? true : false ); + contract_assert ( x < 0 ? true : false ); + + contract_assert ( x = 0 ); // { dg-error {expected '\)' before '=' token} } + // { dg-error {expected .;. before '=' token} "" { target *-*-* } .-1 } + // { dg-error {expected primary-expression before '=' token} "" { target *-*-* } .-2 } + + contract_assert ( y == 0 ); // { dg-error ".y. was not declared in this scope" } + + return 0; +} + +struct Baz +{ + void f(int x) pre(x = 0); // { dg-error "expected conditional-expression" } + void g(int x) pre(x, x); // { dg-error "expected conditional-expression" } + void h(const int x) post(x = 0); // { dg-error "expected conditional-expression" } + void i(const int x) post(x, x); // { dg-error "expected conditional-expression" } +}; diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/function-contract-specifier-seq.C b/gcc/testsuite/g++.dg/contracts/cpp26/function-contract-specifier-seq.C new file mode 100644 index 000000000000..51560268f23e --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/function-contract-specifier-seq.C @@ -0,0 +1,122 @@ +// generic function-contract-specifier-seq parsing checks +// N5008 +// function-contract-specifier-seq : +// function-contract-specifier function-contract-specifier-seq opt +// function-contract-specifier : +// precondition-specifier +// postcondition-specifier +// precondition-specifier : pre attribute-specifier-seq opt ( conditional-expression ) +// postcondition-specifier : +// post attribute-specifier-seq opt ( result-name-introducer opt conditional-expression ) +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } +// { dg-additional-options "-fcontracts" } + +static_assert (__cpp_contracts >= 202502L); + +void f(int i) pre (i>3); + +int g(int j) post (r:r>4) pre (j !=0); + +namespace parsing_basic_test { + int f(const int x) + pre (x >= 0) + post (r: r > x); // r is the return value + + struct A { + bool empty() const noexcept; + void clear() + post (empty()); // return value is optional + }; +} + +namespace parsing_trailing_return_type_test { + auto f() -> int pre(true); + + struct S1 { + auto g() const & noexcept -> int + pre(true); + }; +} + + +namespace parsing_lambda_test { + auto f1 = [] (int x) + pre(x > 0) { return x * x; }; + + auto f2 = [] (int x) -> int + pre(x > 0) { return x * x; }; +} + +namespace parsing_pre_post_names_test { + void f(bool pre, bool post) + pre(pre) pre(post); +} + +namespace parsing_requires_clause_test { + template <typename T> concept C = true; + + template <typename T> + auto g(T x) -> bool + requires C<T> + pre (x > 0); +} + +namespace parsing_ambig1_test { + const bool a = true; + using pre = int; + + auto f() -> pre pre(a); + // pre is the return type, pre(a) the precondition +} + +namespace parsing_ambig2_test { + const bool a = true; + template <typename T> struct pre {}; + using post = int; + + auto g() -> pre<post> pre(a); + // pre<post> is the return type, pre(a) the precondition +} + +namespace parsing_ambig3_test { + constexpr bool a = true; + constexpr bool pre(bool) { return true; } + + template <typename T> + void f1() requires (pre(a)); + // just a requires clause, no postcondition + + using b = bool; + + template <typename T> + void f2() requires ((b)pre(a)); + // just a requires clause, no postcondition + + template <typename T> + void f3() requires (a) pre(a); + // pre(a) is a postcondition +} + +namespace parsing_ambig_test4 { + constexpr int a = 1; + constexpr int b = 2; + constexpr int c = 0; + constexpr int pre(int i) { return i; } + + template <typename T> + void f() requires (a < b > pre(c)); + // just a requires clause, no postcondition + + template <typename T> constexpr bool d = true; + using e = char; + + 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 +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/intro.compliance.general.p2.3.4.C b/gcc/testsuite/g++.dg/contracts/cpp26/intro.compliance.general.p2.3.4.C new file mode 100644 index 000000000000..d0cfc334fa62 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/intro.compliance.general.p2.3.4.C @@ -0,0 +1,13 @@ +// intro.compliance.general/2.34 +// a contract assertion ([basic.contract.eval]) evaluated with a checking +// semantic in a manifestly constant-evaluated context ([expr.const]) +// resulting in a contract violation... +// ... shall issue at least one diagnostic message. +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +consteval void foo( auto x ) pre( false ) {} +// { dg-warning {contract predicate is false in constant expression} "" { target *-*-* } .-1 } +int main() { + foo( 42 ); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/lambda.C b/gcc/testsuite/g++.dg/contracts/cpp26/lambda.C new file mode 100644 index 000000000000..747e325ce93b --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/lambda.C @@ -0,0 +1,5 @@ +// check that we do not crash when capturing a constified entity in a contract assertion lambda +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } +void f(int i, int j) pre( [i, &j](){ return true;} ( )) +{} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/name_mangling.C b/gcc/testsuite/g++.dg/contracts/cpp26/name_mangling.C new file mode 100644 index 000000000000..1e81c1247a1a --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/name_mangling.C @@ -0,0 +1,15 @@ +// check that we do not have conflicts with post condition that includes a return value +// identifier and post condition on an identically named void function with the +// additional parameter that matches the returen value of the first function +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + + +int f() post(r:r>1){ return 2;} +void f(int i) post(true){ } + +int main() { + + f(); + f(2); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/over.call.func.p3.1.C b/gcc/testsuite/g++.dg/contracts/cpp26/over.call.func.p3.1.C new file mode 100644 index 000000000000..381d48d44474 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/over.call.func.p3.1.C @@ -0,0 +1,36 @@ +// N5008 : +// [over.call.func/p3.1 +// if the unqualified function call appears in a precondition assertion of a constructor or a postcondition +// assertion of a destructor and overload resolution selects a non-static member function, the call is +// ill-formed; +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts" } + +struct S{ + bool f(); + bool k; + S() pre(&k); // { dg-error "required when accessing a member" } + ~S()post(&f()); // { dg-error "required when accessing a member" } + S(int i) pre(&(this->k)) + pre([](){ + struct SIn{ + bool i = true; + bool f() const pre(i) { return i;} + SIn() pre(&i){}; // { dg-error "required when accessing a member" } + ; + }; + SIn sin; + return sin.f(); + }()); +}; + +struct S2{ + bool f() const { return true;} + bool i = true; + bool k = true; + S2() pre(&(this->k) != &(this->i)) post(i) {}; + ~S2() pre(f()){}; +}; +int main() +{ +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/pr113968.C b/gcc/testsuite/g++.dg/contracts/cpp26/pr113968.C new file mode 100644 index 000000000000..f173488f70ab --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/pr113968.C @@ -0,0 +1,23 @@ +// check that an invalid contract condition doesn't cause an ICE +// { dg-do compile { target c++26 } } +// { dg-additional-options "-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" } + { + 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/cpp26/src-loc-0.C b/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-0.C new file mode 100644 index 000000000000..025a8fac4590 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-0.C @@ -0,0 +1,18 @@ +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +// Test source location without including <source_location> + +int +foo (int x) + pre ( x > 10 ) +{ + return x - 9; +} + +int main () +{ + foo (9); +} + +// { dg-output "contract violation in function int foo.int. at .*:8: x > 10.*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-1.C b/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-1.C new file mode 100644 index 000000000000..0987d0e62dda --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-1.C @@ -0,0 +1,18 @@ +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } +// Test source location with #include <source_location> +#include <source_location> + +int +foo (int x) + pre ( x > 10 ) +{ + return x - 9; +} + +int main () +{ + foo (9); +} + +// { dg-output "contract violation in function int foo.int. at .*:8: x > 10.*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-2.C b/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-2.C new file mode 100644 index 000000000000..c1ff148f92f1 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/src-loc-2.C @@ -0,0 +1,20 @@ +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe " } + +// Test source location with a late inclusion of <source_location> + +int +foo (int x) + pre ( x > 10 ) +{ + return x - 9; +} + +#include <source_location> + +int main () +{ + foo (9); +} + +// { dg-output "contract violation in function int foo.int. at .*:8: x > 10.*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/throwing-violation-handler.cc b/gcc/testsuite/g++.dg/contracts/cpp26/throwing-violation-handler.cc new file mode 100644 index 000000000000..da0686ebc717 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/throwing-violation-handler.cc @@ -0,0 +1,23 @@ +#include <contracts> + +void handle_contract_violation(const std::contracts::contract_violation&) +{ + throw 666; +} + +/* ODR hack to the hilt - f is actually noexcept, and has a contract. + We do this to avoid the call site knowing that, so that we verify + that the function terminates when it calls the throwing handler + that we use above. */ +int f(int); + +extern bool f_result; + +void g() +{ + try { + f(-42); + } catch (...) { + f_result = false; + } +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/unused_warning.C b/gcc/testsuite/g++.dg/contracts/cpp26/unused_warning.C new file mode 100644 index 000000000000..44d2d9c67bcf --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/unused_warning.C @@ -0,0 +1,25 @@ +// check that we do not get unused warnings for contract check function parameters +// { dg-do compile { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=enforce -Wall -Wextra" } + +struct TimeInterval{ + int i = 4; + void addInterval(int i, int j); +}; + +TimeInterval operator-(const TimeInterval& lhs, const TimeInterval& rhs) + pre(2 != rhs.i); + +inline +TimeInterval operator-(const TimeInterval& lhs, + const TimeInterval& rhs) + +{ + TimeInterval result(lhs); + result.addInterval(-rhs.i, -rhs.i); + return result; +} + +int main(int, char**) { + +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp26/vaargs.C b/gcc/testsuite/g++.dg/contracts/cpp26/vaargs.C new file mode 100644 index 000000000000..abb3ded7c075 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp26/vaargs.C @@ -0,0 +1,35 @@ +// Check contract assertion on a vaarg function +// { dg-do run { target c++23 } } +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=enforce " } +#include <stdio.h> +#include <stdarg.h> +int simple_printf(const char* fmt, ...) pre (fmt != 0) post (r: r > 0) +{ + va_list args; + va_start(args, fmt); + + while (*fmt != '\0') { + if (*fmt == 'd') { + int i = va_arg(args, int); + printf("%d\n", i); + } else if (*fmt == 'c') { + // A 'char' variable will be promoted to 'int' + // A character literal in C is already 'int' by itself + int c = va_arg(args, int); + printf("%c\n", c); + } else if (*fmt == 'f') { + double d = va_arg(args, double); + printf("%f\n", d); + } + ++fmt; + } + + va_end(args); + + return 6; +} + +int main() +{ + simple_printf("dcff", 3, 'a', 1.999, 42.5); +} diff --git a/gcc/testsuite/g++.dg/contracts/cpp2a/check-err.C b/gcc/testsuite/g++.dg/contracts/cpp2a/check-err.C new file mode 100644 index 000000000000..70cfd5d221e0 --- /dev/null +++ b/gcc/testsuite/g++.dg/contracts/cpp2a/check-err.C @@ -0,0 +1,18 @@ +// test that attribute syntax is no longer recognized +// { dg-do compile { target c++20 } } +// { dg-additional-options "-fcontracts" } + +int fun(int n) [[ pre : n > 0 ]]; // { dg-error {expected ']' before ':' token} } +// { dg-warning {'pre' attribute directive ignored} "" { target *-*-* } .-1 } +int fun2(const int n) [[ post : n > 0 ]]; // { dg-error {expected ']' before ':' token} } +// { dg-warning {'post' attribute directive ignored} "" { target *-*-* } .-1 } + +int main() +{ + int x; + + [[assert: x >= 0]]; // { dg-error {expected ']' before ':' token} } + // { dg-warning {attributes at the beginning of statement are ignored} "" { target *-*-* } .-1 } + + return 0; +} diff --git a/gcc/testsuite/g++.dg/coroutines/pr110871.C b/gcc/testsuite/g++.dg/coroutines/pr110871.C new file mode 100644 index 000000000000..803a912bbb2d --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr110871.C @@ -0,0 +1,64 @@ +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } +// { dg-do run { target c++26 } } +// { dg-skip-if "requires hosted libstdc++ for iostream" { ! hostedlib } } + +#include <iostream> +#include <coroutine> + +// In order to test the contract violation diagnostic, we have to set +// -fcontract-continuation-mode=on; this means that the code will emit +// the message below - but before that the contract should have been checked. +void process(int from, int to) +{ + if (from > to) + std::cout << "would have been a disaster!" << std::endl; +} + +template <typename T> +struct generator +{ + struct promise_type + { + template <typename... Args> + promise_type(Args&&... args) { + std::cout << "promise init" << std::endl; + process(args...); + } + + std::suspend_always yield_value(T) { return {}; } + + std::suspend_always initial_suspend() const noexcept { return {}; } + std::suspend_never final_suspend() const noexcept { return {}; } + void unhandled_exception() noexcept {} + + generator<T> get_return_object() noexcept { return {}; } + }; +}; + +namespace std { +template <typename T, typename... Args> +struct coroutine_traits<generator<T>, Args...> +{ + using promise_type = typename generator<T>::promise_type; +}; + +}; + +generator<int> seq(int from, int to) + pre (from <= to) +{ + std::cout << "coro initial" << std::endl; + for (int i = from; i <= to; ++i) { + co_yield i; + std::cout << "coro resumed" << std::endl; + } +} + +int main() { + std::cout << "main initial" << std::endl; + generator<int> s = seq(10, 5); + (void)s; + std::cout << "main continues" << std::endl; +} + +// { dg-output "contract violation in function generator<int> seq.int, int. at .*.C:48: from \<= to.*(\n|\r\n|\r)" } diff --git a/gcc/testsuite/g++.dg/coroutines/pr110872.C b/gcc/testsuite/g++.dg/coroutines/pr110872.C new file mode 100644 index 000000000000..f39872224685 --- /dev/null +++ b/gcc/testsuite/g++.dg/coroutines/pr110872.C @@ -0,0 +1,53 @@ +// { dg-additional-options "-fcontracts -fcontract-evaluation-semantic=observe" } +// NOTE this should switch to run when the changes are resolved. +// { dg-do compile { target c++26 } } +// { dg-skip-if "requires hosted libstdc++ for iostream" { ! hostedlib } } +// { dg-prune-output "during RTL pass: expand" } +// { dg-ice "expand_expr_addr_expr_1" } + +#include <iostream> +#include <coroutine> + + +template <typename T> +struct generator +{ + struct promise_type + { + std::suspend_always yield_value(T) { return {}; } + + std::suspend_always initial_suspend() const noexcept { return {}; } + std::suspend_never final_suspend() const noexcept { return {}; } + void unhandled_exception() noexcept {} + + generator<T> get_return_object() noexcept { return {}; } + }; + + bool is_valid() const { return false; } +}; + +namespace std { +template <typename T, typename... Args> +struct coroutine_traits<generator<T>, Args...> +{ + using promise_type = typename generator<T>::promise_type; +}; + +}; + +generator<int> val(int v) + post (g: g.is_valid()) +{ + std::cout << "coro initial" << std::endl; + co_yield v; + std::cout << "coro resumed" << std::endl; +} + +int main() { + std::cout << "main initial" << std::endl; + generator<int> s = val(1); + (void)s; + std::cout << "main continues" << std::endl; +} + +// { dg-output "contract violation in function generator<int> val.int. at .*.C:36: g.is_valid().*(\n|\r\n|\r)" }
