September 3, 2025·4 min read·engineeringcppcontractslinkingstandards

Mixed contract semantics challenge C++ component boundaries

P3835 shows how separately compiled users of an inline definition can supply different contract policies. The objection concerns the guarantee a library can make about its own checks.

A library can be compiled with terminating contract checks while a client compiles the same inline function with those checks ignored. If the final program uses the client's emitted definition for a library call, the library's build flags have not secured the behavior its author expected. That is the concrete example behind September's renewed objection to the C++26 contracts design.

P3835R0, dated September 3, is by John Spicer, Ville Voutilainen, and Jose Daniel Garcia Sanchez. The authors argue that the draft contracts facility cannot ensure a consistent evaluation semantic for a component. Their conclusion is a request to remove the feature until that concern is resolved. It is a position in an ongoing standards debate, not a report that contracts have been removed.

The adopted P2900R14 design provides configurable semantics for contract assertions. The February meeting report identifies that revision as the adoption baseline. P3835's introduction refers to an earlier revision, but the component-boundary argument can be examined through its actual example without repeating that historical mismatch.

One header can participate in two build policies

The example begins with an inline definition in a header. Its body contains a contract assertion about its argument. This is C++26 draft syntax and requires an implementation of the contracts facility.

inline void require_positive(int value)
{
    contract_assert(value > 0);
}

The library includes that header and calls the function with zero. It builds using quick-enforce semantics, so the author expects the violated assertion to terminate execution. A client includes the same header but builds using ignore semantics, and calls the function with a valid argument before invoking the library.

The header's source definition is the same in both places. Its compilation policy is different. That distinction is the important part of the example. Source identity does not by itself make the generated checking behavior identical when the facility allows an external build choice.

P3835 explains that an out-of-line call from the library can use the function definition emitted by the client's compilation. In that case the assertion is ignored. The library's quick-enforce setting therefore does not establish that every relevant call in the final program uses the checking behavior its author selected.

Inlining is an observable dependency of the expectation

Inlining the function body at the library's call site can retain the library compilation's chosen behavior. Leaving an out-of-line call can expose the definition-selection issue described by the paper. The authors' complaint is that the programmer cannot rely on the desired semantic for all the calls simply by compiling the library component with one option.

That makes this more than a question of whether an application intentionally disables checks in its own code. A library author can make a decision about checks in the library and still lack the guarantee that decision appears to provide. The same inline entity spans the supposed policy boundary.

I find this concern substantial because C++ abstractions are often distributed through headers and templates. A build flag attached to one component is an incomplete description if another component can supply behavior used by its calls. The guarantee should name the final linked program's rules, not stop at the compiler invocation that produced one object file.

The paper says the issue extends to template definitions as well as ordinary inline functions. Those definitions are routinely instantiated in clients. The problem consequently cannot be dismissed as an unusual header layout limited to the particular short example.

A label would attach policy to the assertion

The authors describe a need for a label-like mechanism that ties semantics to components. They refer to implementation-specific attributes in the experimental Clang work and explain that a common vendor agreement had not emerged. That is evidence of a deployment need, not a portable standard solution already available in the adopted draft.

An attribute attached to the declaration or assertion could carry information with the source definition wherever it is compiled. Whether that mechanism is sufficient depends on its actual specification. P3835 does not provide complete replacement wording, and its argument should not be mistaken for an implemented label design.

The paper also rejects relying on specialized linker knowledge as a satisfactory general answer. A workaround for two small files may not scale to numerous functions and independently built components. The objection concerns the usability of the guarantee as well as the existence of a possible toolchain-specific intervention.

Modules do not automatically settle the semantic choice

P3835 treats the problem as orthogonal to modules. Changing how declarations and definitions are distributed does not by itself provide the missing way to bind a checking semantic. Inline functions and templates still need an account of which policy governs their compiled use.

The paper's module discussion is cautious. It does not claim that every module implementation emits identical code in every case. Its point is that adopting modules is not a specified replacement for the missing semantic-control mechanism. The required guarantee must come from rules that actually constrain the evaluation choice.

This is distinct from January's concern about undefined behavior inside a predicate. Even a well-defined expression such as comparing an integer with zero can expose the component problem. Fixing arithmetic inside predicates would not determine which compiled checking policy is used for the shared inline definition. The same separation applies to a violation handler. Configuring how a detected violation is reported cannot restore a check that the selected definition never evaluates. Detection and handling remain separate parts of the intended behavior, and the example fails at the detection stage.

I still see value in standard syntax for requirements, but a component author needs to know which promise that syntax supports. P3835 supplies a concrete counterexample to assuming that the library's terminating build policy governs every call originating there. Its challenge to the committee is to resolve that expectation at the source and program-composition boundary, where the same function can be compiled under more than one policy.