C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Canonical State Enforcement

From: Robin Savonen Söderholm <robinsavonensoderholm_at_[hidden]>
Date: Thu, 12 Feb 2026 09:14:49 +0100
I like this tag-idea, as it could be used for real time safety and probably
other guarantees as well.

// Robin

On Thu, Feb 12, 2026, 08:46 David Brown via Std-Proposals <
std-proposals_at_[hidden]> wrote:

>
>
> On 12/02/2026 08:07, Steve Weinrich via Std-Proposals wrote:
> > Many years ago we had a similar issue with threads. We wanted a means
> > to keep the data that could be accessed from a thread declaratively
> > distinct from the non-thread data. We ended up declaring a class to
> > represent a thread and used a nested class to represent that threads
> > specific data. I am wondering if a similar approach would work here
> > without any language extension?
> >
>
> I had a related idea for adding "tags" to functions to help control
> which functions are allowed to call which other functions. My interest
> was primarily for embedded systems - so tags would be for things like
> "interrupts disabled" or "in ram" (you don't want your flash programming
> code accidentally calling functions that are in flash!). The same
> concept would work just as well for controlling functions that can be
> used with different threads, and for canon/non-canon functions.
>
> I filed it as a feature request for gcc, as a function attribute, but
> nothing came of it: <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88391>
>
>
> I had not looked at marking data in any way here - maybe that would be
> necessary, but it is also possible that that could be handled within the
> existing type system. But I think a critical point is that if any
> related checking system is added to C++, it would have to be with
> user-defined tags. There is no way the standard is going to add extra
> keywords to support a small niche market (lots of people are involved in
> writing games, but very few work in the heart of high-performance game
> engines). But the concept could be widened to be of use to a much
> larger audience by making the tags user-definable.
>
> The core of my feature request was:
>
>
> My suggestions for function attributes are:
>
> 1. __attribute__((tagged("tagname")))
>
> Mark a function as being tagged with "tagname" (or multiple tag names).
>
> 2. __attribute__((caller_tag("tagname")))
>
> Mark a function as only being callable by a function with this tag.
>
> 3. __attribute__((callee_tag("tagname")))
>
> Mark a function as only being allowed to call functions with this tag.
>
> 4. __attribute__((caller_notag("tagname")))
>
> Mark a function as not being callable by a function with this tag.
>
> 5. __attribute__((callee_notag("tagname")))
>
> Mark a function as not being allowed to call functions with this tag.
>
>
> There would also need to be a statement attribute:
>
> __attribute__((tag("tagname")))
>
> Mark the statement as having this tag (and therefore being allowed to
> call matching "caller_tag" functions, and not allowed to call matching
> "caller_notag" functions).
>
>
> mvh.,
>
> David
>
>
>
>
>
> > *From:*Std-Proposals <std-proposals-bounces_at_[hidden]> *On
> Behalf
> > Of *mika.koivuranta via Std-Proposals
> > *Sent:* Wednesday, February 11, 2026 7:49 AM
> > *To:* std-proposals_at_[hidden]
> > *Cc:* mika.koivuranta <mika.koivuranta_at_[hidden]>
> > *Subject:* [std-proposals] Canonical State Enforcement
> >
> > Following proposal is in markdown format:
> >
> >
> > # Canonical State Enforcement
> >
> > ## 1. Abstract
> >
> > This paper proposes a C++ standard for a compile-time language feature
> > for expressing and enforcing *canon state* in C++. The proposal
> > introduces the `canon`, `noncanon` and `input` specifiers for variables
> > and functions, enabling compile-time catching of class of bugs by
> > splitting the program into two separate states, which C++26 currently
> > cannot express. The feature is purely opt-in, and imposes no runtime
> > overhead.
> >
> > ## 2. Motivation
> >
> > A common class of errors in real-world C++ programs -- particularly in
> > games, simulations, and interactive systems -- arises from unintended
> > mutation of deterministic/authoritative state from outside contexts.
> > These defects manifest as framerate dependent behavior, nondeterministic
> > simulations, and/or desynchronization in networked or replayed systems.
> >
> > There are common mitigating solutions -- namely `deltatime` and fixed-
> > timestep execution -- but they are purely social: They rely on no
> > developer mistakenly updating or using specific state within wrong
> > contexts. Contexts, which they can only know by carefully examining the
> > call tree of the entire program.
> >
> > ```cpp
> >
> > void Move(float velocity)
> >
> > {
> >
> > this.position += velocity * GetDeltaTime();
> >
> > // oh no! GetDeltaTime() returns a value modified each frame!
> >
> > // Now this summation's accuracy is dependent on framerate!
> >
> > // Move(float Velocity) and this.position have both become noncanon!
> >
> > }
> >
> > ```
> >
> > In this example, the `Move(float velocity)` function itself can be used
> > as a side effect of a more complicated control flow. Thanks to the
> > addition of wall-clock dependent time, `Move(float Velocity)` should no
> > longer be called from, for example, deterministic physics simulations.
> > However, the c++ standard has no enforcement for this: Only social
> > discipline can prevent this class of bugs in C++ 26 and below.
> >
> > Just like there is some state which should never be modified (`const`),
> > there is some state which should never be modified non-canonically
> > (`canon`). Underlying both of these specifiers is social discipline made
> > manifest.
> >
> > ## 3. Definitions
> >
> > **Program state** refers to all memory whose contents influence program
> > behavior.
> >
> > **Canon behavior** is a function of its prior canon state and optionally
> > of explicit user inputs.
> >
> > **Canon state** is program state whose mutation is intended to be
> > deterministic: Given identical inputs, it should evolve identically
> > across executions based on only canon state itself, and independent of
> > factors such as hardware and rendering speed.
> >
> > **Non-canon state** is program state whose mutation is allowed to depend
> > on canon, non-canon and input factors.
> >
> > ## 4. Overview of the Approach
> >
> > The proposal introduces:
> >
> > * Three mutually exclusive specifiers of canonicality: `canon`,
> > `noncanon` and `input`. These may appear in variable declarations and
> > function declarations.
> >
> > * Canonicality as a property of variables and behaviors (contexts)
> >
> > * Rules governing mutation, assignment, operations, and call graphs
> >
> > * A default behavior for unannotated functions to preserve backwards
> > compatibility
> >
> > ```mermaid
> >
> > graph TD
> >
> > A[[canon main]] --> B[<noncanon> frame update]
> >
> > B ----> C(<noncanon> render)
> >
> > B ----> H[<noncanon> update inputs]
> >
> > B ---> E(<anonymous> std::memcpy)
> >
> > B --> D[<noncanon> particle effects]
> >
> > A --> F[[<canon> tick update]]
> >
> > F --> D
> >
> > F --> E
> >
> > I --> H
> >
> > F ----> I[[<canon> apply inputs to simulation]]
> >
> > F ---> G[[<canon> physics simulation]]
> >
> > ```
> >
> > ## 5. Implementation
> >
> > ### 5.1. Program State
> >
> > #### 5.1.1 State Rules
> >
> > 1. *Canon* state **may only** be modified and assigned within *canon*
> > contexts
> >
> > 2. *Canon* state **may only** be initialized and assigned to *canon* and
> > *input* states
> >
> > 3. *Non-canon* state **may** be initialized and assigned to *canon*,
> > *non-canon*, and *input* states
> >
> > 4. Any operation involving **any** *non-canon* operands always yields a
> > *non-canon* result (with exception of rule 12)
> >
> > 5. Any operation involving **only** *canon* operands always yields a
> > *canon* result (with exception of rule 12)
> >
> > #### 5.1.2 Declaration of State Canonicality
> >
> > Variable canonicality may be declared as:
> >
> > ```cpp
> >
> > float a = 1; // non-specified variables default to the canonicality of
> > the context (rule 8)
> >
> > canon float c = 2; // canon variable
> >
> > noncanon float nc = 3; // noncanon variable
> >
> > canon noncanon float b; // error: unknown specifier
> >
> > canon float d = c; // allowed
> >
> > noncanon float e = c // allowed
> >
> > canon float f = nc // error: canon state cannot be assigned from a
> > noncanon state (rule 2)
> >
> > noncanon float g = nc // allowed
> >
> > noncanon float h = c + nc; // allowed; yields in a noncanon result (rule
> 4)
> >
> > noncanon bool i = c > nc; // allowed; yields in a noncanon result (rule
> 4)
> >
> > canon float j = c + d; // allowed; yields in a canon result (rule 5)
> >
> > ```
> >
> > These rules impose a strict divide in the state of the program:
> > Declaring `canon float c` , for example, would impose a program-wide
> > restriction to the writing of `c`: For example, frame-dependent behavior
> > is restricted at standard level, from accidentally modifying, the state
> > of `c`.
> >
> > However, that is only possible if all frame-dependent contexts are
> > defined as non-canon:
> >
> > ### 5.2. Contexts
> >
> > #### 5.2.1 Context Rules
> >
> > 6. *Non-canon*, and *canonically anonymous* contexts **may only** call
> > *non-canon* and *canonically anonymous* functions
> >
> > 7. *Canon* contexts **may** call *canon*, *non-canon*, and *canonically
> > anonymous* functions
> >
> > 8. Non-specified variables declared within a context inherit the
> > context's canonicality. If that canonicality is *canonically anonymous*,
> > they are *non-canon*
> >
> > 9. `switch`, `if` , `for` and `while` statement bodies' context is
> > *non-canon* if their condition is *non-canon* or *input*, or if the
> > condition is called from a *non-canon* context
> >
> > #### 5.2.2 Declaration of Context Canonicality
> >
> > Function canonicality may be declared as:
> >
> > ```cpp
> >
> > // note: non-specified argument types default to the global context,
> > which is noncanon:
> >
> > // void y(float nc) canon
> >
> > // is equilevent to declaration of l()
> >
> > // canon functions:
> >
> > void j() canon // allowed
> >
> > void k(canon float c) canon; // allowed
> >
> > void l(noncanon float nc) canon; // allowed
> >
> > canon float n(canon float c) canon; // allowed
> >
> > noncanon float m(canon float c) canon; // allowed
> >
> > canon float o(noncanon float nc) canon; // allowed
> >
> > noncanon float p(noncanon float nc) canon; // allowed
> >
> > // noncanon functions:
> >
> > void q() noncanon // allowed
> >
> > void r(canon float c) noncanon; // error: modification of canon state is
> > allowed only from strictly canon contexts (rule 1)
> >
> > void s(noncanon float nc) noncanon; // allowed
> >
> > canon float t(canon float c) noncanon; // error: assignment of canon
> > state is allowed only from strictly canon contexts (rule 1)
> >
> > noncanon float u(canon float c) noncanon; // error: argument must be
> > const (rule 1)
> >
> > canon float v(noncanon float nc) noncanon; // error: assignment of canon
> > state is allowed only from strictly canon contexts (rule 1)
> >
> > noncanon float w(noncanon float nc) noncanon; // allowed
> >
> > noncanon float example = 1;
> >
> > void x() canon
> >
> > {
> >
> > j(); // allowed (rule 7)
> >
> > q(); // allowed (rule 7)
> >
> > if (example >= 5) // condition is non-canon; statement context is non-
> > canon (rule 9)
> >
> > {
> >
> > example -= 4;
> >
> > j(); // error: cannot call canon functions in noncanon contexts (rule 6)
> >
> > q(); // allowed (rule 6)
> >
> > }
> >
> > }
> >
> > ```
> >
> > These rules establish a unidirectional flow of authority: Canon behavior
> > may observe and mutate any state. Non-canon behavior may observe canon
> > state and use it to calculate its own behavior, but is not allowed to
> > mutate the canon state.
> >
> > It is good to bear in mind that calling a `canon` function is equivalent
> > to advancing authoritative state. (e.g. Server, physics simulation, or
> > the text on a document)
> >
> > Allowing non-canon contexts to call `canon` functions allows the context
> > to possibly by modify canon state, resulting to it being, at least
> > partly, framerate or hardware dependent. This is exactly the developer
> > mistake which canonicality was proposed to prevent, which is why such
> > calls aren't allowed in this proposed feature.
> >
> > #### 5.2.3. Canonically Anonymous Functions
> >
> > Functions declared without either `canon` or `noncanon` are considered
> > *canonically anonymous*. Canonically anonymous is a behavior
> > classification which allows helper functions with side effects tied to
> > its arguments (such as `std::memcpy`) to be called from any context.
> >
> > Canonically anonymous functions act as helpers in any context, analogous
> > to how `constexpr` functions may execute in both constant and runtime
> > contexts.
> >
> > 10. *Canonically anonymous* functions' canonically non-specified
> > arguments **may** be called with either canon or non-canon variables
> >
> > 11. *Canonically anonymous* functions **may** be called with non-const
> > canon arguments, **only** if the context calling it is also canon
> >
> > In short, a compiler only needs to consider the canonicality of
> > canonically anonymous functions when such function is called with canon
> > or non-canon arguments. In that case, the compiler only needs to check
> > whether the function's arguments are `const` or copied, and from those
> > which are not, whether the given values are `canon` (or assignable to
> > canon), and if they are, whether the call context is also canon, and if
> > it is not, then a compilation error has occurred. This allows
> > canonicality to be fully opt-in feature, and allows programs designed
> > with canonicality in mind to interface with libraries which otherwise
> > lack canonical considerations.
> >
> > #### 5.2.4. Declaration of Canonical Anonymity
> >
> > ```cpp
> >
> > // canonically anonymous functions:
> >
> > void c(float const &c); // allowed
> >
> > void b(float &c); // canonicality of this function and its argument
> > depends on
> >
> > // whether this function is called from canon or noncanon contexts;
> >
> > // when called from canon context, allowed
> >
> > // when called from noncanon context, allowed with noncanon or const
> > canon arguments, otherwise
> >
> > // error: argument must be const, or function must be canon (rule 11)
> >
> > void d(canon float &c); // error: argument must be const, or function
> > must be canon (rule 11)
> >
> > void e(noncanon float &nc); // allowed
> >
> > canon float f(canon float &c); // error: assignment of canon state is
> > allowed only from strictly canon contexts (rule 2)
> >
> > noncanon float g(canon float &c); // error: argument must be const, or
> > function must be canon (rule 11)
> >
> > canon float h(noncanon float &nc); // error: assignment of canon value
> > is allowed only from strictly canon contexts (rule 2)
> >
> > noncanon float i(noncanon float &nc); // allowed
> >
> > ```
> >
> > ### 5.3. Input
> >
> > Canon state is defined as that which isn't affected by sources outside
> > canon state itself.
> >
> > According to this definition, user input and hardware checks would be
> > understood as non-canon. If the standard is made to enforce that type of
> > strict canonicality, the return types of `std::cin` and `std::fstream`
> > would gain the `non-canon` specifier. However, this results in canon
> > contexts with no user interaction aside from opening and closing them.
> >
> > In order to preserve meaningful computing in canon contexts, the user
> > themselves must be understood as a non-canon, yet strictly self-
> > deterministic part of the program. For the rest of the paper, *input
> > state* will be defined as the part of the program which that conceptual
> > user precedes over.
> >
> > A basic example of input is:
> >
> > ```cpp
> >
> > input bool buttonPressed;
> >
> > PushBox(canon float force) canon;
> >
> > void Tick() canon
> >
> > {
> >
> > // (imagine a non-blocking input check here)
> >
> > if (buttonPressed)
> >
> > {
> >
> > PushBox(200);
> >
> > }
> >
> > }
> >
> > ```
> >
> > In the above example, the user input is a simple Boolean truth; Whether
> > a button is pressed or not. The canonicality of such input can be easily
> > replaced with a canon.
> >
> > However, in the following example, such replacement cannot be easily
> made:
> >
> > ```cpp
> >
> > noncanon float buttonPressTime;
> >
> > PushBox(canon float force) canon;
> >
> > void Frame(deltaTime) noncanon
> >
> > {
> >
> > // (imagine a non-blocking input check here)
> >
> > if(buttonPressed)
> >
> > {
> >
> > buttonPressTime += deltaTime;
> >
> > }
> >
> > }
> >
> > void Tick() canon
> >
> > {
> >
> > PushBox(buttonPressTime); // error: function only takes canon arguments
> >
> > }
> >
> > ```
> >
> > In the above example, the user input is the **amount of time a key was
> > pressed**. Such input's accuracy is based on input sampling rate, and so
> > must be non-canon. This disqualifies it as a legal argument for
> > `PushBox(canon float force) canon` function.
> >
> > This is the use case of the `input` specifier: It designates program
> > state whose values originate from sources external to the program’s
> > canon state, but which are nevertheless considered deterministic or
> > authoritative.
> >
> > As such, input state represents a conceptual *user*, such as keyboard
> > input, network messages, sensor readings, or file contents. While, for
> > example, framerate is also an external effect, it is not be considered
> > part of the user's intent, and therefore is not of input state.
> >
> > #### 5.3.1 Input Rules
> >
> > 12. Any operation involving **any** input operands always yields an
> > input result (this precedes over rule 4)
> >
> > 13. *Input* state **may** be initialized and assigned to *canon*, *non-
> > canon*, and *input* states
> >
> > These rules implicitly specify following behavior: Input state, for all
> > intents and purposes, acts the same as non-canon state, with one key
> > difference: Within canon behavior, canon state is not allowed to be
> > assigned to non-canon state, but canon state is allowed to be assigned
> > to input state, as per rules 1 and 2.
> >
> > In this sense, input can be understood as a way to "cast" non-canon
> > state to canon state. This, sadly allows for a class of developer
> > mistakes, wherein the developer adds `input` specifier overzealously to
> > state which is not part of the user, or modifies input state in a way
> > which is not part of the user's input.
> >
> > However, this class of mistakes are clearly marked with the input
> > specifier and sourced by conscious disobedience to the standard, which
> > -- in the author's opinion -- are an acceptable replacement to often
> > hidden class of mistakes sourced from accidental disobedience to a
> > specific codebase's social rules, caused canonical unsafety of the C++
> > 26 standard.
> >
> > Note that there are no mentions of input contexts in this proposal: A
> > theoretical input context would be logically equilevent to non-canon
> > context.
> >
> > #### 5.3.2 Chart of Operations Between Canonical Types
> >
> > | Operand type: | Canon | Non-canon | Input
> >
> >
> |:--------|--------:|------------:|--------------:|--------------:|--------------:|
> >
> > | **Canon** | Canon | Non-canon| Input |
> >
> > | **Non-canon** | Non-canon | Non-canon | Input |
> >
> > | **Input** | Input | Input | Input |
> >
> > #### 5.2.3 Declaration of Input Canonicality
> >
> > ```cpp
> >
> > input bool buttonPressed; // allowed
> >
> > input float buttonPressTime; // allowed
> >
> > void InputFunction() input; // allowed
> >
> > canon float c;
> >
> > void Frame(float deltaTime) noncanon
> >
> > {
> >
> > buttonPressTime += deltaTime; // error: input state may not be
> > modified in non-canon contexts
> >
> > noncanon float nc;
> >
> > std::cin >> nc; // allowed; noncanon state may be assigned to input state
> >
> > std::cin >> buttonPressed; // error: input state may not be modified in
> > non-canon contexts
> >
> > if (buttonPressed) // condition is input; statement context is non-canon
> >
> > {
> >
> > buttonPressTime += deltaTime; // allowed
> >
> > nc = buttonPressTime; // allowed
> >
> > c = buttonPressTime; // error: cannot modify canon state in non-canon
> > context
> >
> > }
> >
> > }
> >
> > ```
> >
> > ### 5.4. Classes
> >
> > Canonicality is not a property of class types. Instead, data members and
> > member functions must be declared `canon` or `noncanon` individually. To
> > declare a fully canon class, one must simply declare all of its members
> > as canon.
> >
> > ### 5.5. Templates
> >
> > Template declarations inherit the canonicality of their primary
> > declaration. All instantiations of a given template share the same
> > canonicality.
> >
> > ## 6. C++ Code Example
> >
> > ```cpp
> >
> > canon float time;
> >
> > float frameTimer = 0.1; // global scope defaults to noncanon
> >
> > canon const float tickTimer = 0.2;
> >
> > void sideEffect(float& arg) // canonically anonymous function: Can be
> > called in canon and noncanon as long as arguments arent canonically
> illegal
> >
> > {
> >
> > arg = 5;
> >
> > }
> >
> > template <typename T>
> >
> > T CalculateNewTime(T time) // canonically anonymous function: Can be
> > called in canon and noncanon as long as arguments arent canonically
> illegal
> >
> > {
> >
> > return 0.1;
> >
> > }
> >
> > struct Foo // cannot declare canon or noncanon on structs/classes
> >
> > {
> >
> > float a = 0; // global scope defaults to noncanon
> >
> > noncanon float b = 1;
> >
> > canon float c = 2;
> >
> > Foo() {}
> >
> > Foo(float nc) : c(nc) {} // error: cannot use type "noncanon float"
> > to set the value of type "canon float"
> >
> > Foo(canon float nc) : c(nc) {} // allowed
> >
> > };
> >
> > void Frame() noncanon // noncanon function: Can call noncanon and anon
> > functions
> >
> > {
> >
> > noncanon float newTime = time; // allowed
> >
> > canon float* p; // allowed
> >
> > p = &newTime; // error: cannot modify canon state within noncanon
> > behaviour
> >
> > noncanon float* b; // allowed
> >
> > b = &time; // error: cannot use type "canon float*" to set the
> > value of type "noncanon float*"
> >
> > time = 0; // error: cannot modify canon state within noncanon
> behaviour
> >
> > Foo* foo = new Foo(); // TODO: Should default initialization of
> > canon members be allowed in noncanon contexts?
> >
> > Foo* foo = new Foo(2); // error: cannot modify canon state "canon
> > float c" in noncanon behaviour
> >
> > // note: literal "2" defaults to context's canonicality, which in
> > this case is noncanon
> >
> > foo->a = foo->c + 3; // allowed
> >
> > foo->c = 3; // error: cannot modify canon state within noncanon
> > behaviour
> >
> > sideEffect(time); // error: canonically anonymous function cannot
> > take non-const canon arguments while called from a non-canon context
> >
> > sideEffect(newTime); // allowed
> >
> > if (newTime == time) // any operation involving any noncanon
> > operands always yields a noncanon result; The condition is noncanon ->
> > the statement context is noncanon
> >
> > {
> >
> > p = &newTime; // error: cannot modify canon state within
> > noncanon behaviour
> >
> > }
> >
> > if (time == 0) // any operation with only canon operands always
> > yields a canon result; The condition is canon, but is in non-canon
> > context -> the statement context is non-canon
> >
> > {
> >
> > p = &newTime; // error: cannot modify canon state within
> > noncanon behaviour
> >
> > }
> >
> > }
> >
> > void Tick() canon // canon function: Can call canon, noncanon and anon
> > functions
> >
> > {
> >
> > noncanon float newTime = time; // allowed
> >
> > canon float* p; // allowed
> >
> > p = &newTime; // allowed
> >
> > noncanon float* b; // allowed
> >
> > b = &time; // error: cannot use type "canon float*" to set the
> > value of type "noncanon float*"
> >
> > time = 0; // allowed
> >
> > Foo* foo = new Foo(); // allowed
> >
> > Foo* foo = new Foo(2); // allowed
> >
> > // note: literal "2" defaults to context's canonicality, which in
> > this case is canon
> >
> > foo->a = foo->c + 3; // allowed
> >
> > foo->c = 3; // allowed
> >
> > sideEffect(time); // allowed
> >
> > sideEffect(newTime); // allowed
> >
> > if (newTime == time) // any operation involving any noncano
> > operands always yields a noncanonresult; The condition is noncanon ->
> > the statement context is noncanon
> >
> > {
> >
> > p = &newTime; // error: cannot modify canon state within
> > noncanon behaviour
> >
> > }
> >
> > if (time == 0) // any operation with only canon operands always
> > yields a canon result; The condition is canon -> the statment context is
> > canon
> >
> > {
> >
> > p = &newTime; // allowed
> >
> > }
> >
> > }
> >
> > int main() canon // canon function: Can call canon, noncanon and anon
> > functions
> >
> > {
> >
> > float tickTime = 0; // defaults to canon, since defined within
> > canon context
> >
> > while (true)
> >
> > {
> >
> > time = CalculateNewTime<float>(time); // canonically anonymous
> > called with non-const canon state in canon behaviour -> allowed!
> >
> > if (time >= frameTimer) // any operation with non-canon
> > operands always yields a non-canon result; condition is non-canon ->
> > statement context is non-canon
> >
> > {
> >
> > Frame(); // noncanon function call
> >
> > }
> >
> > tickTime += time; // modification of canon allowed in canon
> > function
> >
> > while (tickTime >= tickTimer) // any operation between only
> > canon operands always yields a canon result; condition is canon, and is
> > in canon context -> statement context is canon
> >
> > {
> >
> > tickTime -= tickTimer;
> >
> > Tick(); // canon function call
> >
> > }
> >
> > }
> >
> > }
> >
> > ```
> >
> > ## 7. Backwards Compatibility
> >
> > Due to the proposed anonymous canonicality , the behavior of programs
> > which do not use canonicality (`canon`, `noncanon` and `input`
> > specifiers) would be unaffected. Canonicality is entirely opt-in and
> > introduces no changes to the building and running of existing programs
> > and libraries, except in case of macros using the names of the
> > specifiers proposed in this paper.
> >
> > ## 8. Issues
> >
> > The most common argument against any standard addition is that a
> > programmer could reasonably design it themselves with libraries, or with
> > wrappers to another language. But in the case of canonization, this
> > argument becomes an expectation for each compiler to have their own
> > specification for canonicality, or for them to leave standard C++ to be
> > fundamentally canonically unsafe. Both choices result in complete
> > developer uncertainty, which seems an unnecessary and harsh punishment
> > for simply using C++ for any low-level code.
> >
> > ### 8.1 Why not use types?
> >
> > Types can express canonized variables; `canon_float`, `noncanon_float`
> > and `input_float`can express the full canonicality of a `float` type,
> > even though it essentially quadruples all primitive types.
> >
> > However, types cannot express canonized functions: `canon_float
> > function()` is not equivalent to `float function() canon`. Types also
> > cannot specify canonically anonymous functions.
> >
> > ### 8.2 Why introduce canonicality to the whole standard?
> >
> > Existing approaches rely on social discipline: Conventions which prevent
> > the bug, or laborious manual checks or code reviews to catch the bug.
> > This proposal provides compile-time enforcement comparable in spirit to
> > `const` or `static_assert`, which also exist to apply important code
> > conventions to the language itself.
> >
> > While canonization mainly helps in physics simulations, any program with
> > authoritative, deterministic state would only gain from canonization: A
> > library which allows online editing of a text-file, or handling of user
> > input, for example, can only safeguard their state by hiding it from the
> > user completely (in a nameless namespace, or by making the state
> > private). However, canonization allows for internal state to be marked,
> > and its modification enforced to the exact contexts where such
> > modification is allowed.
> >
> > Canonization also offers a solution to functions and variables which are
> > intended to be deterministic: Pseudo-random number generators, timers
> > and physics objects often allow modification at runtime, which runs the
> > risk of a developer mistakenly modifying them non-deterministically.
> > With canonization, programmers may explicitly declare functions as
> > deterministic, by using the `canon` specifier.
> >
> > In short, canonicality should be used in any case where the **mutability
> > must be limited to specifically deterministic behavior**.
> >
> > ### 8.3 How does canonicality affect the standard library?
> >
> > #### 8.3.1 Canon
> >
> > Canonicality is defined through the developer's intent, and so shouldn't
> > be added to the standard library. Canon specifier is also opt-in, and so
> > shouldn't be forced upon the developer. Furthermore, canon specifier
> > breaks backwards combability in libraries it is used in.
> >
> > It is important to let the user be free to define the state which they
> > intend enforce as canon.
> >
> > #### 8.3.2 Canonically Anonymous
> >
> > Standard helpers, such as:
> >
> > ```cp
> >
> > std::sort
> >
> > std::copy
> >
> > std::transform
> >
> > std::accumulate
> >
> > std::memcpy
> >
> > std::memmove
> >
> > ```
> >
> > and pure functions such as:
> >
> > ```cpp
> >
> > std::sin
> >
> > std::cos
> >
> > std::sqrt
> >
> > std::min
> >
> > std::max
> >
> > std::mt19937
> >
> > ```
> >
> > Should stay canonically anonymous, since their return types and side
> > effects depend on call context, and such are allowed in every context.
> >
> > #### 8.3.4 Non-canon
> >
> > Non-input reading functions based on time or environment, such as:
> >
> > ```cpp
> >
> > std::chrono::system_clock::now()
> >
> > std::clock()
> >
> > std::random_device()
> >
> > std::getenv()
> >
> > ```
> >
> > Are definitionally non-canon, since they depend on state outside of the
> > program. The functions and their return types should both be non-canon.
> >
> > Note that the user is free to assign any of these into canon or input
> > state, choose they disagree with what makes up the conceptual user
> > within their programs.
> >
> > #### 8.3.3 Input
> >
> > All of the standard library's input-reading functions, such as:
> >
> > ```cpp
> >
> > std::cin
> >
> > std::ifstream
> >
> > std::filesystem::last_write_time
> >
> > ```
> >
> > Should specify their return types as `input`. The functions themselves
> > should be non-canon. Streams are not necessarily input and should stay
> > with their default context-dependent canonicality.
> >
> > ### 8.4 Why not enforce deterministic while loops?
> >
> > This is possible: `while` and `for` loops can be checked at compile-time
> > for determinism:
> >
> > ```cpp
> >
> > const int forever = true; // const
> >
> > while (forever) // non-deterministic, runs based on execution speed,
> > statement context should be non-canon
> >
> > {
> >
> > //...
> >
> > }
> >
> > ```
> >
> > However, this is a form of the unsolvable halting problem, and therefore
> > one's work on this feature will never be finished. However, some trivial
> > non-halting contexts, such as bare while loops, could be enforced within
> > the standard as strictly non-canon contexts. though such algorithms are
> > outside the scope of this paper.
> >
> > Another solution is to handle `for` and `while` loops as non-canon
> > contexts by default. This, however, limits the possible behaviors of
> > canon behavior. Instead, allowing `canon` and `noncanon` specifiers to
> > apply to `for` and `while` statements themselves gives the control fully
> > to the developer, though again, such feature is outside the scope of
> > this paper.
> >
> > ### 8.5 Open issues:
> >
> > * Should the specifiers `canon`, `noncanon` and `input` be attributes?
> >
> > * Names of the specifiers `canon`, `noncanon` and `input` should
> > probably be changed for sake of brevity
> >
> > * Exact error wording is unfinished
> >
> > * Should we allow initialization of canon members within non-canon
> behavior?
> >
> > * `goto` might allow contexts to break out canon behavior, but is that
> > a real problem?
> >
> > ## 9. Conclusion
> >
> > Canon state exists implicitly in all programs but is not representable
> > or enforceable in current C++. This proposal introduces changes to the
> > standard library, as well as a minimal compile-time, opt-in feature
> > which expresses and enforces authority boundaries at compile time,
> > preventing a large class of common bugs without a significant cost.
> >
> >
>
> --
> Std-Proposals mailing list
> Std-Proposals_at_[hidden]
> https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals
>

Received on 2026-02-12 08:15:16