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:

  1. 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
    
  2. Install the ericwf-clang-contracts compiler:

    ./bin/ce_install --enable nightly install 'compilers/c++/nightly/clang ericwf-contracts-trunk'
    
  3. The compiler will be available at /opt/compiler-explorer/clang-ericwf-contracts-trunk/bin/clang++

  4. 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 program

  • enforce - Contract assertions are evaluated and violations terminate the program

  • quick_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:

  1. Exact Match: The most specific match always wins

  2. Parent Inheritance: If no exact match exists, the nearest parent group’s settings apply

  3. 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 parent mylib)

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();
}

Indices and tables