The range and flexibility of available choices of
evaluation semantics depends on the implementation
and need not allow all four evaluation semantics as possibilities.
The evaluation semantics can differ
for different evaluations of the same contract assertion,
including evaluations during constant evaluation.
Recommended practice: An implementation should provide
the option to translate a program
such that all evaluations of contract assertions use the ignore semantic
as well as
the option to translate a program
such that all evaluations of contract assertions use the enforce semantic.
By default,
evaluations of contract assertions should use the enforce semantic.
To determine whether a predicate would evaluate
to true or false,
an alternative evaluation
that produces the same value as the predicate
but has no side effects
can occur.
[Example 1: struct S {mutableint g =5;
} s;
void f()
pre(( s.g++, false)); // #1void g(){
f(); // Increment of s.g might not occur, even if #1 uses a checking semantic.} — end example]
There is an observable checkpoint ([intro.abstract]) C
that happens before A
such that any other operation O
that happens before A
also happens before C.
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.
If B is true,
no contract violation occurs and
control flow continues normally
after the point of evaluation of the contract assertion.
The evaluation of the predicate
can fail to produce a value
without causing a contract violation,
for example,
by calling longjmp ([csetjmp.syn])
or terminating the program.
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.
Different evaluation semantics
chosen for the same contract assertion
in different translation units
can result in
violations of the one-definition rule ([basic.def.odr])
when a contract assertion has side effects
that alter the value produced by a constant expression.
[Example 2: constexprint f(int i){contract_assert((++const_cast<int&>(i), true));
return i;
}inlinevoid g(){int a[f(1)]; // size dependent on the evaluation semantic of contract_assert above} — end example]
Performing the actions of
std::terminate or std::abort
without actually making a library call
is a conforming implementation of
contract-termination ([intro.abstract]).
If a contract violation occurs
in a context that is not manifestly constant-evaluated
and the evaluation semantic is quick-enforce,
the program is contract-terminated.
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 ([basic.contract.handler])
is invoked with an lvalue referring to
an object v
of type const std::contracts::contract_violation ([support.contract.violation])
containing information about the contract violation.
If the contract violation occurred
because the evaluation of the predicate
exited via an exception,
the contract-violation handler is invoked
from within an active implicit handler
for that exception ([except.handle]).
If the contract-violation handler
returns normally
and the evaluation semantic is observe,
that implicit handler
is no longer considered active.
If the contract-violation handler
returns normally
and the evaluation semantic is enforce,
the program is contract-terminated;
if violation occurred
as the result of an uncaught exception
from the evaluation of the predicate,
the implicit handler
remains active when contract termination occurs.
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.
There is an observable checkpoint ([intro.abstract]) C
that happens after the contract-violation handler returns normally
such that any other operation O
that happens after the contract-violation handler returns
also happens after C.
The terminating semantics terminate the program
if execution would otherwise continue normally
past a contract violation:
the enforce semantic provides the opportunity to
log information about the contract violation
before terminating the program
or to throw an exception to avoid termination,
and the quick-enforce semantic is intended
to terminate the program as soon as possible
as well as
to minimize the impact of contract checks
on the generated code size.
Conversely,
the observe semantic
provides the opportunity to
log information about the contract violation
without having to terminate the program.
If a contract-violation handler
invoked from the evaluation of a function contract assertion ([dcl.contract.func])
exits via an exception,
the behavior is as if
the function body exits via that same exception.
If a contract-violation handler
invoked from an assertion-statement ([stmt.contract.assert]))
exits via an exception,
the search for a handler
continues from the execution of that statement.
Evaluate each element of S such that,
if a contract assertion A
precedes a contract assertion B
in S,
then the evaluation of A
is sequenced before
the evaluation of B.
[Example 3: void f(int i){contract_assert(i >0); // #1contract_assert(i <10); // #2// valid sequence of evaluations: #1 #2// valid sequence of evaluations: #1 #1 #2 #2// valid sequence of evaluations: #1 #2 #1 #2// valid sequence of evaluations: #1 #2 #2 #1// invalid sequence of evaluations: #2 #1} — end example]