Date: Sun, 7 Jun 2026 22:41:46 +0200
Hi all,
Following the "Float the idea" step on isocpp.org, I'd like to gauge
interest in a direction before writing the formal R0.
Now that C++26 is finalized — shipping both Contracts (P2900) and
Reflection (P2996), already in GCC and Clang trunk — the two carriers that
Layers 2 and 3 below build on exist for the first time. This direction
therefore targets the C++29 timeframe, whose focus on safety makes it a
natural fit.
The gap
C++ carries exactly one aspect of a function's dynamic behaviour in its
type: noexcept. Beyond that, the things real-time, embedded, and middleware
code care most about at an interface boundary — does this function
allocate? block? recurse? bounded stack? bounded time on this hardware? —
live outside the language: in comments, in MISRA-style prohibitions, or in
external WCET tools that re-derive information the programmer already knew
but couldn't express, and that can drift from the code.
The direction
I think the right structure is a small, layered program, with each layer
independently shippable:
- Layer 1 — Effect sets. Generalize noexcept to a closed, orthogonal
family of static effect guarantees: nonallocating, nonblocking,
nonrecursing (plus nonthrowing ≡ noexcept). Part of the function type
(sound across indirect/virtual calls); inferred where definitions are
visible, declared at API boundaries; discharge at well-defined region
boundaries (catch-all, arena allocators). Fully static, fully decidable.
This is what I would propose first.
- Layer 2 — Quantitative resource contracts. Extend P2900 Contracts with
resource predicates (budget(stack).peak <= 4_KiB, budget(heap_bytes).delta
== 0, budget(time).peak <= 50us) over an explicit platform/cost model.
Three discharge modes — prove (within a decidable fragment), assume +
monitor, ignore — mapping onto the four P2900 semantics. The language
standardizes the vocabulary and the discharge framework, not a WCET
algorithm.
- Layer 3 — Reflective manifest + runtime monitoring. Use P2996
Reflection to read declared envelopes at compile time, emit a
cross-module-checkable resource manifest, and inject runtime probes that
verify actual execution against the declared envelope. Almost entirely a
library on top of P2996/P2900. Useful even with only Layer 1.
Layers 2 and 3 are independent of each other; both depend only on Layer 1
having declared something.
Alternatives considered
The common thread: none of these can publish a composable, interface-level
guarantee that callers can check across module boundaries. That is the gap
this program fills.
- C++ Profiles. A profile is a set of rules a conforming compiler
enforces at compile time (it may inject runtime checks) — not merely
"turning constructs off". But a profile is a property of a translation, not
part of an interface: it constrains what a piece of code may do
(author-facing), whereas an effect/resource envelope publishes what callers
may rely on (interface-facing). The two are complementary, not opposed —
they share the principle of in-compiler, compile-time enforcement, but
point the statement in opposite directions. A library under a bounds
profile can become safer internally, yet still cannot tell its clients "I
don't allocate on this path, I cost ≤ N µs on this hardware", and clients
cannot mechanically check that.
- MISRA C++ / AUTOSAR C++14 / JSF. Constraint regimes: prohibitions,
useful in certification, external to the language and non-publishing.
- Purely external WCET / static analysis (aiT, OTAWA, RapiTime).
Reconstructs information after the fact, decoupled from the code, so it can
drift; nothing checkable at the interface.
- P3271 "function usage types" (Lippincott), a committee-encouraged
C++29-timeframe direction. It addresses the same indirect-call concern but
keeps the property off the function's own type (it lives on the
pointer/reference type) to allow backwards-compatible contractualization.
That is a genuinely different tradeoff from Layer 1, which binds the effect
to the function type for a published, definition-site, everywhere-visible
guarantee. I see them as complementary rather than competing — a usage type
could even be expressed over effect-bearing function types — and I'd
welcome views on how the two should relate.
- Status quo / noexcept only. noexcept already carries one intensional
effect in the type; this works less well as RT/middleware ABI surfaces
multiply and need more than the throwing effect.
Prior art
- noexcept (P0012, part of the type since C++17) — the precedent and the
Layer 1 blueprint.
- Clang function effects: [[clang::nonblocking]],
[[clang::nonallocating]] — an implementation existence proof for Layer 1,
used in real-time audio.
- Ada pragma Restrictions, Ravenscar/Jorvik — the gold standard for this
in certified RT.
- must_not_suspend (other languages) — precedent for suspension as an
effect.
What I'd like feedback on
1. Is SG14/EWG broadly receptive to Layer 1 as a generalization of noexcept
— that is, an effect specifier that is part of the function type, not an
attribute?
2. Is there interest in attaching resource contracts to P2900's grammar,
with the three-mode discharge model — accepting that the language would not
compute WCET, only standardize the vocabulary and the discharge framework?
3. Should the manifest + monitoring track be pursued separately via P2996
(SG7 / LEWG), so it doesn't need to wait on Layer 2?
I have draft papers for each layer plus a manifest schema and a worked
example, but I'd rather hear the room first.
This comes from several years working on real-time projects across
security, automation, and 3D-camera vision. The recurring frustration was
the same across all of them: whether the system actually met its real-time
target depended on things the interface couldn't express — whether a call
allocated, blocked, or stayed within a time budget on the target hardware.
The function signatures told us what was computed but almost nothing about
the dynamic behaviour that decided whether the result was correct in time.
That gap is what this direction is meant to close.
Thanks,
— Michael Galuszka <galuszka.michael_at_[hidden]>
Following the "Float the idea" step on isocpp.org, I'd like to gauge
interest in a direction before writing the formal R0.
Now that C++26 is finalized — shipping both Contracts (P2900) and
Reflection (P2996), already in GCC and Clang trunk — the two carriers that
Layers 2 and 3 below build on exist for the first time. This direction
therefore targets the C++29 timeframe, whose focus on safety makes it a
natural fit.
The gap
C++ carries exactly one aspect of a function's dynamic behaviour in its
type: noexcept. Beyond that, the things real-time, embedded, and middleware
code care most about at an interface boundary — does this function
allocate? block? recurse? bounded stack? bounded time on this hardware? —
live outside the language: in comments, in MISRA-style prohibitions, or in
external WCET tools that re-derive information the programmer already knew
but couldn't express, and that can drift from the code.
The direction
I think the right structure is a small, layered program, with each layer
independently shippable:
- Layer 1 — Effect sets. Generalize noexcept to a closed, orthogonal
family of static effect guarantees: nonallocating, nonblocking,
nonrecursing (plus nonthrowing ≡ noexcept). Part of the function type
(sound across indirect/virtual calls); inferred where definitions are
visible, declared at API boundaries; discharge at well-defined region
boundaries (catch-all, arena allocators). Fully static, fully decidable.
This is what I would propose first.
- Layer 2 — Quantitative resource contracts. Extend P2900 Contracts with
resource predicates (budget(stack).peak <= 4_KiB, budget(heap_bytes).delta
== 0, budget(time).peak <= 50us) over an explicit platform/cost model.
Three discharge modes — prove (within a decidable fragment), assume +
monitor, ignore — mapping onto the four P2900 semantics. The language
standardizes the vocabulary and the discharge framework, not a WCET
algorithm.
- Layer 3 — Reflective manifest + runtime monitoring. Use P2996
Reflection to read declared envelopes at compile time, emit a
cross-module-checkable resource manifest, and inject runtime probes that
verify actual execution against the declared envelope. Almost entirely a
library on top of P2996/P2900. Useful even with only Layer 1.
Layers 2 and 3 are independent of each other; both depend only on Layer 1
having declared something.
Alternatives considered
The common thread: none of these can publish a composable, interface-level
guarantee that callers can check across module boundaries. That is the gap
this program fills.
- C++ Profiles. A profile is a set of rules a conforming compiler
enforces at compile time (it may inject runtime checks) — not merely
"turning constructs off". But a profile is a property of a translation, not
part of an interface: it constrains what a piece of code may do
(author-facing), whereas an effect/resource envelope publishes what callers
may rely on (interface-facing). The two are complementary, not opposed —
they share the principle of in-compiler, compile-time enforcement, but
point the statement in opposite directions. A library under a bounds
profile can become safer internally, yet still cannot tell its clients "I
don't allocate on this path, I cost ≤ N µs on this hardware", and clients
cannot mechanically check that.
- MISRA C++ / AUTOSAR C++14 / JSF. Constraint regimes: prohibitions,
useful in certification, external to the language and non-publishing.
- Purely external WCET / static analysis (aiT, OTAWA, RapiTime).
Reconstructs information after the fact, decoupled from the code, so it can
drift; nothing checkable at the interface.
- P3271 "function usage types" (Lippincott), a committee-encouraged
C++29-timeframe direction. It addresses the same indirect-call concern but
keeps the property off the function's own type (it lives on the
pointer/reference type) to allow backwards-compatible contractualization.
That is a genuinely different tradeoff from Layer 1, which binds the effect
to the function type for a published, definition-site, everywhere-visible
guarantee. I see them as complementary rather than competing — a usage type
could even be expressed over effect-bearing function types — and I'd
welcome views on how the two should relate.
- Status quo / noexcept only. noexcept already carries one intensional
effect in the type; this works less well as RT/middleware ABI surfaces
multiply and need more than the throwing effect.
Prior art
- noexcept (P0012, part of the type since C++17) — the precedent and the
Layer 1 blueprint.
- Clang function effects: [[clang::nonblocking]],
[[clang::nonallocating]] — an implementation existence proof for Layer 1,
used in real-time audio.
- Ada pragma Restrictions, Ravenscar/Jorvik — the gold standard for this
in certified RT.
- must_not_suspend (other languages) — precedent for suspension as an
effect.
What I'd like feedback on
1. Is SG14/EWG broadly receptive to Layer 1 as a generalization of noexcept
— that is, an effect specifier that is part of the function type, not an
attribute?
2. Is there interest in attaching resource contracts to P2900's grammar,
with the three-mode discharge model — accepting that the language would not
compute WCET, only standardize the vocabulary and the discharge framework?
3. Should the manifest + monitoring track be pursued separately via P2996
(SG7 / LEWG), so it doesn't need to wait on Layer 2?
I have draft papers for each layer plus a manifest schema and a worked
example, but I'd rather hear the room first.
This comes from several years working on real-time projects across
security, automation, and 3D-camera vision. The recurring frustration was
the same across all of them: whether the system actually met its real-time
target depended on things the interface couldn't express — whether a call
allocated, blocked, or stayed within a time budget on the target hardware.
The function signatures told us what was computed but almost nothing about
the dynamic behaviour that decided whether the result was correct in time.
That gap is what this direction is meant to close.
Thanks,
— Michael Galuszka <galuszka.michael_at_[hidden]>
Received on 2026-06-07 20:41:54
