---
title: "How to decouple UB-checks from Contracts"
document: P4347R0
date: 2026-08-14
audience: EWG
reply-to:
  - "Ville Voutilainen <ville.voutilainen@gmail.com>"
---

## Abstract

This paper explains how to decouple UB-checks from Contracts. Because multiple people have expressed a desire to look at that. Further, this paper explains how to do that without redefining Undefined Behavior. This paper focuses on *how*. This paper doesn't focus on *why*.

## The approach

Let's dive right into it. The first thing is to remove the UB-redefining. Do not introduce "Unconstrained Behavior", do not make Undefined Behavior refer to it.

Then, introduce the notion of implementation-defined UB-checks. Specify that

- if the check fails, it's implementation-defined whether the situation is diagnosed, and
- it's implementation-defined whether the program is terminated, and
- it's implementation-defined whether a replacement value is used.

Then, modify the cases where the language says that the behavior is undefined to state that

- if an implementation-defined UB-check is enabled (by an implementation-defined mechanism), perform the check
- if such a check is not enabled, the behavior is undefined

## Is this different for programmers?

No. One can envision that an implementation that would want to implement both P2900 and P3100 would allow enabling them separately, or in combination.

That separate enabling can also enable this approach, performing exactly the same checks as P3100 performs in a mode where its checks are enabled.

That combination-enabling can enable P2900 and the same checks for this approach as P3100 would enable.

How contract checks are controlled, and which P2900 semantics are chosen, is completely implementation-defined. Which semantics are even supported is implementation-defined. Which P3100 checks are supported or enabled is implementation-defined.

From that, it's a relatively short distance to a specification that specifies the same practical end result in terms of implementation-defined behavior instead of attempting to reuse the P2900 functionality.

The implementation-defined behavior isn't tied to P2900. But it can be, if the implementation so chooses. It can choose something different, or something beyond. An implementation can choose that its runtime diagnostic means invoking the contract violation handler, and it can decide to provide exactly the P2900 selection of semantics. It can provide more.

With P2900 and P3100, an implementation can already provide a subset of either.

## Example

> If a deallocation function terminates by throwing an exception,
> 
> then
> 
> - <ins>if an implementation-defined check is enabled, perform that check</ins>
> - <ins>otherwise, </ins>the behavior is undefined

Another example:

> The behavior of evaluating an arithmetic expression is undefined if
> 
> If
> 
> the mathematical result
> 
> of evaluating an arithmetic expression
> 
> is neither
> 
> - in the range of representable values for its type nor
> - a negative infinity, positive infinity, or NaN that is among the values of the type<del>.</del>
> 
> then
> 
> - <ins>if an implementation-defined check is enabled, perform that check</ins>
> - <ins>otherwise, the behavior is undefined.</ins>
