Using Clang Contracts¶
This document specifies how to use C++ Contracts in Clang, including how to install the compiler and enable contract checking.
See https://godbolt.org/z/azdnqxn87 for a live example.
Installing¶
The easiest way to get started with Clang Contracts is to use pre-built binaries. You can download and install Clang and libc++ from compiler explorer.
To install the contracts-enabled Clang compiler using compiler-explorer-infra:
Clone the compiler-explorer-infra repository:
git clone https://github.com/compiler-explorer/infra.git cd infra make ce # Needed to initially set up the environment
Install the ericwf-clang-contracts compiler:
./bin/ce_install --enable nightly install 'compilers/c++/nightly/clang ericwf-contracts-trunk'
The compiler will be available at
/opt/compiler-explorer/clang-ericwf-contracts-trunk/bin/clang++
You can also use the online version at Compiler Explorer by selecting the “ericwf-clang-contracts” compiler from the compiler list.
Required Flags¶
The following flags are required to enable contract checking in Clang:
-fcontracts
Enables C++ contracts support. This flag must be specified to compile code that uses contracts syntax (
pre
,post
,contract_assert
). Without this flag, contract declarations will result in compilation errors.-stdlib=libc++
Use the libc++ standard library implementation. This is required when using contracts because the contracts implementation is integrated with libc++. The standard library components like
std::contracts::contract_violation
and violation handler functions are provided by libc++.-fcontract-evaluation-semantic=<value>
Controls how contracts are evaluated. Valid values are:
ignore
- Contract assertions are not evaluated (contracts are disabled)observe
- Contract assertions are evaluated but violations do not terminate the programenforce
- Contract assertions are evaluated and violations terminate the programquick_enforce
- Like enforce, but uses optimized evaluation that may skip some checks
Optional Flags¶
The following flags control additional aspects of contract checking:
-fcontract-group-evaluation-semantic=<group>=<semantic>
Allows fine-grained control over contract evaluation for specific groups. For example:
-fcontract-group-evaluation-semantic=std=quick_enforce
sets standard library contracts to quick_enforce mode, while-fcontract-group-evaluation-semantic=mylib=observe
sets custom “mylib” group contracts to observe mode.
Contract Groups¶
The [[clang::contract_group("name")]]
attribute allows you to organize contracts into
named groups, enabling fine-grained control over which contracts are evaluated.
Basic Usage¶
You can assign contracts to groups using the attribute:
contract_assert [[clang::contract_group("mylib")]] (x > 0);
contract_assert [[clang::contract_group("debug")]] (invariant_holds());
Hierarchical Group Names¶
Contract groups support a dot-delimited hierarchy system that allows for precise control over evaluation semantics. Groups are organized in a tree structure where more specific groups inherit settings from their parent groups, but can override them.
Group Matching Rules:
Exact Match: The most specific match always wins
Parent Inheritance: If no exact match exists, the nearest parent group’s settings apply
Default Fallback: If no group matches, the default evaluation semantic applies
Examples of hierarchical matching:
// Group hierarchy: mylib -> mylib.debug -> mylib.debug.verbose
contract_assert [[clang::contract_group("mylib")]] (basic_check());
contract_assert [[clang::contract_group("mylib.debug")]] (debug_check());
contract_assert [[clang::contract_group("mylib.debug.verbose")]] (detailed_check());
contract_assert [[clang::contract_group("mylib.performance")]] (perf_check());
With these compiler flags:
# Set base mylib group to observe
-fcontract-group-evaluation-semantic=mylib=observe
# Override debug subgroup to enforce
-fcontract-group-evaluation-semantic=mylib.debug=enforce
# Override specific verbose group to ignore
-fcontract-group-evaluation-semantic=mylib.debug.verbose=ignore
The evaluation semantics would be:
mylib
contracts: observe (exact match)mylib.debug
contracts: enforce (exact match, overrides parent)mylib.debug.verbose
contracts: ignore (exact match, overrides parent)mylib.performance
contracts: observe (inherits from parentmylib
)
Multiple Group Specifications¶
You can specify multiple group settings in a single command:
-fcontract-group-evaluation-semantic=std=quick_enforce,mylib=observe,mylib.debug=ignore
Examples¶
// Use -std=c++23 -fcontracts -stdlib=libc++
//
// Change the evaluation semantic using
// -fcontract-evaluation-semantic=<ignore|observe|enforce|quick_enforce>
//
// Change evaluation in the standard library using:
// -fcontract-group-evaluation-semantic=std=<...>@
//
#include <string_view>
#include <iostream>
#include <contracts>
int f(const int x)
pre(x != 0)
post(r : r != x) {
return x + 1;
}
void try_contract_groups() {
// Turn on all "mylib" assertions using.
// -fcontract-group-evaluation-semantic=mylib=enforce
// Disable the debug group with
// -fcontract-group-evaluation-semantic=mylib=enforce,mylib.debug=ignore
// Or change "mylib.other" to quick_enforce with
// -fcontract-group-evaluation-semantic=mylib.hardening=quick_enforce
//
// Currently the flag is -fcontract-group-evaluation-semantic=mylib=observe
contract_assert [[clang::contract_group("mylib")]] (true);
contract_assert [[clang::contract_group("mylib.debug")]] (false && "mylib.debug");
contract_assert [[clang::contract_group("mylib.other")]] (false && "mylib.other");
}
void stdlib_using_contracts() {
std::cerr << "stdlib_using_contracts is about to quick_enforce and trap!" << std::endl;
std::string_view sv;
(void)sv.back();
}
// Try overriding the violation handler
void handle_contract_violation(std::contracts::contract_violation const& violation) {
if (violation.semantic() == std::contracts::evaluation_semantic::observe) {
std::cerr << violation.location().function_name() << ":"
<< violation.location().line()
<< ": observing violation my way: "
<< violation.comment()
<< std::endl;
return;
}
std::contracts::invoke_default_contract_violation_handler(violation);
}
int main() {
int r = f(1);
try_contract_groups();
stdlib_using_contracts();
}