It is an interesting direction. Probably needs (in my personal humble opinion) implementation and usage experience first.

 

Some things I noticed:

 

 

1) What does non-recursing mean?

 

If A calls B and B calls A, is A recursing?

 

 

2) It is part of the type? inferred where definitions are visible? That is dangerous for types suddenly changing depending on the definition. What if some code sees the definition and other code only the declaration? ODR violation?

 

 

3) One can on purpose declare a function, which calls (and does not catch) throwing functions as noexcept. Either you are convinced, that the exception does not happen in practice or you are happy with the process terminating in case of an exception.

 

 

4) nonallocating could be important relative to a certain heap, other (e.g. thread-local) heaps may be okay. nonblocking could be in relation to certain other threads or by certain mutexes, others may be okay. non-recursing may be okay for a certain function (with bounded recursions) and not okay for other functions.

 

 

5) What about pure functions? Even pure functions can have runtime parameters, so the constexpr variants could not be enough.

 

 

 

There was one other previous instance beside noexcept: volatile member functions; and another current one: const member functions
 

Beside your mentioned prior art, I see contracts as some kind of weak prior art. But it does not say something about the results, but about runtime behavior.



 

-----Ursprüngliche Nachricht-----
Von: Michael Galuszka via Std-Proposals <std-proposals@lists.isocpp.org>
Gesendet: So 07.06.2026 22:42
Betreff: [std-proposals] Float the idea: First-class effect annotations and resource contracts for C++
An: std-proposals@lists.isocpp.org;
CC: Michael Galuszka <galuszka.michael@googlemail.com>;
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@googlemail.com>
-- 
 Std-Proposals mailing list
 Std-Proposals@lists.isocpp.org
 https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals