9 Declarations [dcl.dcl]

9.11 Attributes [dcl.attr]

9.11.4 Contract attributes [dcl.attr.contract]

9.11.4.1 Syntax [dcl.attr.contract.syn]

Contract attributes are used to specify preconditions, postconditions, and assertions for functions.
contract-attribute-specifier:
	[ [ expects contract-level : conditional-expression ] ]
	[ [ ensures contract-level identifier : conditional-expression ] ]
	[ [ assert contract-level : conditional-expression ] ]
contract-level:
	default
	audit
	axiom
An ambiguity between a contract-level and an identifier is resolved in favor of contract-level.
It expresses a function's expectation on its arguments and/or the state of other objects using a predicate that is intended to hold upon entry into the function.
The attribute may be applied to the function type of a function declaration.
It expresses a condition that a function should ensure for the return value and/or the state of objects using a predicate that is intended to hold upon exit from the function.
The attribute may be applied to the function type of a function declaration.
A postcondition may introduce an identifier to represent the glvalue result or the prvalue result object of the function.
[Example
:
int f(char * c)
  [[ensures res: res > 0 && c != nullptr]];

int g(double * p)
  [[ensures audit res: res != 0 && p != nullptr && *p <= 0.0]];
end example
]
It expresses a condition that is intended to be satisfied where it appears in a function body.
The attribute may be applied to a null statement ([stmt.expr]).
An assertion is checked by evaluating its predicate as part of the evaluation of the null statement it applies to.
Preconditions, postconditions, and assertions are collectively called contracts.
The conditional-expression in a contract is contextually converted to bool ([conv]); the converted expression is called the predicate of the contract.
[Note
:
The predicate of a contract is potentially evaluated ([basic.def.odr]).
end note
]
The only side effects of a predicate that are allowed in a contract-attribute-specifier are modifications of non-volatile objects whose lifetime began and ended within the evaluation of the predicate.
An evaluation of a predicate that exits via an exception invokes the function std​::​terminate ([except.terminate]).
The behavior of any other side effect is undefined.
[Example
:
void push(int x, queue & q)
  [[expects: !q.full()]]
  [[ensures: !q.empty()]]
{
  /* ... */
  [[assert: q.is_valid()]];
  /* ... */
}

int min = -42;
constexpr int max = 42;

constexpr int g(int x)
  [[expects: min <= x]]                         // error
  [[expects: x < max]]                          // OK
{
  /* ... */
  [[assert: 2*x < max]];
  [[assert: ++min > 0]];                        // undefined behavior
  /* ... */
}
end example
]

9.11.4.2 Contract conditions [dcl.attr.contract.cond]

A contract condition is a precondition or a postcondition.
The first declaration of a function shall specify all contract conditions (if any) of the function.
Subsequent declarations shall either specify no contract conditions or the same list of contract conditions; no diagnostic is required if corresponding conditions will always evaluate to the same value.
The list of contract conditions of a function shall be the same if the declarations of that function appear in different translation units; no diagnostic required.
If a friend declaration is the first declaration of the function in a translation unit and has a contract condition, the declaration shall be a definition and shall be the only declaration of the function in the translation unit.
Two lists of contract conditions are the same if they consist of the same contract conditions in the same order.
Two contract conditions are the same if their contract levels are the same and their predicates are the same.
Two predicates contained in contract-attribute-specifiers are the same if they would satisfy the one-definition rule ([basic.def.odr]) were they to appear in function definitions, except for renaming of parameters, return value identifiers (if any), and template parameters.
[Note
:
A function pointer cannot include contract conditions.
[Example
:
typedef int (*fpt)() [[ensures r: r != 0]];     // error: contract condition not on a function declaration

int g(int x)
  [[expects: x >= 0]]
  [[ensures r: r > x]]
{
  return x+1;
}

int (*pf)(int) = g;                             // OK
int x = pf(5);                                  // contract conditions of g are checked
end example
]
end note
]
The predicate of a contract condition has the same semantic restrictions as if it appeared as the first expression-statement in the body of the function it applies to.
Additional access restrictions apply to names appearing in a contract condition of a member function of class C:
  • Friendship is not considered ([class.friend]).
  • For a contract condition of a public member function, no member of C or of an enclosing class of C is accessible unless it is a public member of C, or a member of a base class accessible as a public member of C ([class.access.base]).
  • For a contract condition of a protected member function, no member of C or of an enclosing class of C is accessible unless it is a public or protected member of C, or a member of a base class accessible as a public or protected member of C.
For names appearing in a contract condition of a non-member function, friendship is not considered.
[Example
:
class X {
public:
  int v() const;
  void f() [[expects: x > 0]];                  // error: x is private
  void g() [[expects: v() > 0]];                // OK
  friend void r(int z) [[expects: z > 0]];      // OK
  friend void s(int z) [[expects: z > x]];      // error: x is private
protected:
  int w();
  void h() [[expects: x > 0]];                  // error: x is private
  void i() [[ensures: y > 0]];                  // OK
  void j() [[ensures: w() > 0]];                // OK
  int y;
private:
  void k() [[expects: x > 0]];                  // OK
  int x;
};

class Y : public X {
public:
  void a() [[expects: v() > 0]];                // OK
  void b() [[ensures: w() > 0]];                // error: w is protected
protected:
  void c() [[expects: w() > 0]];                // OK
};
end example
]
A precondition is checked by evaluating its predicate immediately before starting evaluation of the function body.
[Note
:
The function body includes the function-try-block and the ctor-initializer.
end note
]
A postcondition is checked by evaluating its predicate immediately before returning control to the caller of the function.
[Note
:
The lifetime of local variables and temporaries has ended.
Exiting via an exception or via longjmp ([csetjmp.syn]) is not considered returning control to the caller of the function.
end note
]
If a function has multiple preconditions, their evaluation (if any) will be performed in the order they appear lexically.
If a function has multiple postconditions, their evaluation (if any) will be performed in the order they appear lexically.
[Example
:
void f(int * p)
  [[expects: p != nullptr]]                     // #1
  [[ensures: *p == 1]]                          // #3
  [[expects: *p == 0]]                          // #2
{
  *p = 1;
}
end example
]
If a postcondition odr-uses ([basic.def.odr]) a parameter in its predicate and the function body makes direct or indirect modifications of the value of that parameter, the behavior is undefined.
[Example
:
int f(int x)
  [[ensures r: r == x]]
{
  return ++x;                   // undefined behavior
}

int g(int * p)
  [[ensures r: p != nullptr]]
{
  *p = 42;                      // OK, p is not modified
}

int h(int x)
  [[ensures r: r == x]]
{
  potentially_modify(x);        // undefined behavior if x is modified
  return x;
}
end example
]

9.11.4.3 Checking contracts [dcl.attr.contract.check]

If the contract-level of a contract-attribute-specifier is absent, it is assumed to be default.
[Note
:
A default contract-level is expected to be used for those contracts where the cost of run-time checking is assumed to be small (or at least not expensive) compared to the cost of executing the function.
An audit contract-level is expected to be used for those contracts where the cost of run-time checking is assumed to be large (or at least significant) compared to the cost of executing the function.
An axiom contract-level is expected to be used for those contracts that are formal comments and are not evaluated at run-time.
end note
]
[Note
:
Multiple contract conditions may be applied to a function type with the same or different contract-levels.
[Example
:
int z;

bool is_prime(int k);

void f(int x)
  [[expects: x > 0]]
  [[expects audit: is_prime(x)]]
  [[ensures: z > 10]]
{
  /* ... */
}
end example
]
end note
]
A translation may be performed with one of the following build levels: off, default, or audit.
A translation with build level set to off performs no checking for any contract.
A translation with build level set to default performs checking for default contracts.
A translation with build level set to audit performs checking for default and audit contracts.
If no build level is explicitly selected, the build level is default.
The mechanism for selecting the build level is implementation-defined.
The translation of a program consisting of translation units where the build level is not the same in all translation units is conditionally-supported.
There should be no programmatic way of setting, modifying, or querying the build level of a translation unit.
During constant expression evaluation ([expr.const]), only predicates of checked contracts are evaluated.
In other contexts, it is unspecified whether the predicate for a contract that is not checked under the current build level is evaluated; if the predicate of such a contract would evaluate to false, the behavior is undefined.
The violation handler of a program is a function of type “noexcept function of (lvalue reference to const std​::​contract_­violation) returning void”, and is specified in an implementation-defined manner.
The violation handler is invoked when the predicate of a checked contract evaluates to false (called a contract violation).
There should be no programmatic way of setting or modifying the violation handler.
It is implementation-defined how the violation handler is established for a program and how the std​::​contract_­violation ([support.contract.cviol]) argument value is set, except as specified below.
If a precondition is violated, the source location of the violation is implementation-defined.
[Note
:
Implementations are encouraged but not required to report the caller site.
end note
]
If a postcondition is violated, the source location of the violation is the source location of the function definition.
If an assertion is violated, the source location of the violation is the source location of the statement to which the assertion is applied.
If a user-provided violation handler exits by throwing an exception and a contract is violated on a call to a function with a non-throwing exception specification, then the behavior is as if the exception escaped the function body.
[Note
:
The function std​::​terminate is invoked ([except.terminate]).
end note
]
[Example
:
void f(int x) noexcept [[expects: x > 0]];

void g() {
  f(0);                                         // std​::​terminate() if violation handler throws
  /* ... */
}
end example
]
A translation may be performed with one of the following violation continuation modes: off or on.
A translation with violation continuation mode set to off terminates execution by invoking the function std​::​terminate ([except.terminate]) after completing the execution of the violation handler.
A translation with a violation continuation mode set to on continues execution after completing the execution of the violation handler.
If no continuation mode is explicitly selected, the default continuation mode is off.
[Note
:
A continuation mode set to on provides the opportunity to install a logging handler to instrument a pre-existing code base and fix errors before enforcing checks.
end note
]
[Example
:
void f(int x) [[expects: x > 0]];

void g() {
  f(0);         // std​::​terminate() after handler if continuation mode is off;
                // proceeds after handler if continuation mode is on
  /* ... */
}
end example
]