Virtual function contracts need the caller and the implementation
P3097R0 proposes checking the interface used by a caller alongside the dynamically selected override. Its design avoids inheriting every assertion across an entire class hierarchy.
A virtual call has two relevant declarations. The caller sees an interface through a particular static type, while dispatch selects an implementation from the object's dynamic type. P3097R0, dated 12 April, proposes contract assertions for both declarations. Timur Doumler, Joshua Berne and Gašper Ažman use that split to address cases that automatic inheritance of assertions handles poorly. The paper is a proposal for the evolving contracts facility, with no claim of adoption in published C++.
Large C++ codebases can contain several generations of interface conventions at once. That is part of what makes the language costly to read in my view. A contracts facility should let an interface state its own requirements without making every call depend on assertions scattered through unrelated ancestors.
The static interface describes what the caller uses
The paper starts from the difference between an interface and its implementation. A caller using a base reference must satisfy that interface's preconditions and can rely on its postconditions. The selected override also has requirements its body assumes. Checking only the override can omit the agreement visible to the caller.
Conversely, checking every ancestor can impose assertions belonging to interfaces that this invocation did not use. A direct call through a derived interface may intentionally accept a wider input domain. An inherited restriction could reject that valid use merely because an ancestor describes a narrower domain. The proposal treats the declaration used for the call as relevant information.
This distinction is useful for maintenance because adding an assertion to a base should not silently change every direct use of a derived interface. The derived implementation may have been written with a different documented behavior. A contracts design needs to specify which agreement each invocation is being checked against.
The proposed order follows the invocation
P3097R0 checks the statically selected interface preconditions first, then the dynamically selected implementation preconditions. It runs the implementation body, checks that implementation's postconditions, and finally checks the interface postconditions. These are the two sources of assertions for the invocation, even when the hierarchy has additional intermediate classes.
When the static and dynamic declarations coincide, the proposal permits checking that declaration's assertions once. A qualified call that bypasses virtual dispatch uses only the called declaration. These distinctions keep the checking model tied to the actual invocation, rather than treating membership in a hierarchy as sufficient reason to run an assertion.
The earlier P2894R1 discusses implementation-selected checking semantics for contracts. Selecting which assertions belong to a call is a separate question from choosing their checking policy.
The mechanism does not prove that an override is substitutable for its base for every possible argument. It checks the assertions selected for a particular call. That is a different guarantee from a compile-time proof over arbitrary predicates. The authors explicitly reject requiring such proofs as a practical general solution for C++.
For an API author, this means the predicates still need to describe the intended contract. A narrower override can produce a detected violation when used through an incompatible base call. Detection exposes the disagreement. It does not transform that disagreement into correct polymorphic behavior.
Multiple inheritance exposes the problem with global combinations
The paper gives an example with separate interfaces for even and odd integer inputs. One promises an even result for an even input, while the other promises an odd result for an odd input. An identity implementation can satisfy both. Requiring every call to satisfy both result predicates would instead demand an impossible result.
The invocation-specific approach preserves the relevant agreement. A call through the even-input interface checks its corresponding promise. A call through the odd-input interface checks the other one. The identity implementation does not need to produce a value that meets both mutually exclusive postconditions at once.
This is a useful test case because the implementation itself is uncomplicated. The difficulty comes entirely from choosing the assertions that apply. A design that rejects it has encoded too broad an interpretation of the hierarchy, even though the concrete behavior can satisfy each interface used appropriately.
I favor the paper's focus on an actual invocation. It gives reviewers a specific interface to examine and a specific implementation whose assumptions matter. The remaining design work concerns how reliably and efficiently that relationship can be represented in existing C++ implementations.
Implementation strategy is part of the proposal
P3097R0 discusses caller-side and callee-side checking, thunks and additional virtual-table entries. It also considers ABI stability. Those are material questions for a facility intended to work across separately compiled libraries. The source-level rule must remain implementable when the caller does not know which override dispatch will select.
The proposal's central contribution is a bounded rule for choosing assertions. It does not automatically inherit assertions into overrides, and it does not aggregate every predicate in the inheritance tree. It selects the interface used by the call and the implementation actually invoked. That lets each declaration describe its own agreement while making a mismatch observable on the path where it matters.