Date: Thu, 5 Mar 2026 14:25:58 +0000
On 05/03/2026 13:54, Marcin Jaczewski wrote:
> czw., 5 mar 2026 o 13:41 Jonathan Grant <jg_at_[hidden]> napisał(a):
>>
>>
>>
>> On 03/03/2026 12:10, Marcin Jaczewski wrote:> wt., 3 mar 2026 o 12:57 Jonathan Grant <jg_at_[hidden]> napisał(a):
>>>>
>>>>
>>>>
>>>> On 03/03/2026 11:19, Marcin Jaczewski wrote:
>>>>> wt., 3 mar 2026 o 11:41 Jonathan Grant <jg_at_[hidden]> napisał(a):
>>>>>>
>>>>>>
>>>>>>> On Sun, 22 Feb 2026, 11:26 Marcin Jaczewski via Std-Proposals, <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>>>>>>>
>>>>>> <....>>
>>>>>>> Beside preconditions are visible on both sides of function call even if not
>>>>>>> inlined, this means you do not need to rely on opimalzier to do this job
>>>>>>> as you put enough preconditions to make all checks local.
>>>>>>
>>>>>> Martin may I ask, re inlining, if a function has an attribute [[gnu::noinline]] in my tests I found the constant is not propagated, so the Optimizer fails to identify the constraint is met. Is there any solution?
>>>>>>
>>>>>> [[gnu::noinline]]
>>>>>> int get_int() { return 10; }
>>>>>>
>>>>>> int x = get_int();
>>>>>> compile_assert(x < 15, "x must be < 15");
>>>>>>
>>>>>> If I changed the compile_assert() to call a [[deprecated]] function, the obj file would be produced, but it would then rely upon the linker removing the compile_assert() still there.
>>>>>>
>>>>>> There may be some places where it's difficult to place a compile_assert().
>>>>>>
>>>>>
>>>>> I mean THE C++ preconditions like:
>>>>>
>>>>> ```
>>>>> size_t foo(size_t x) pre(x < 10) post(z: z < x);
>>>>> ```
>>>>>
>>>>> as you see no function body posted there but you know that result will
>>>>> be less than given x.
>>>>> We could ask the compiler to solve them statically and the programmer
>>>>> can add more constatians
>>>>> to make it possible for dumb compiler. No need for optimization levels
>>>>> only constexpr calcations.
>>>>
>>>> Hi Marcin
>>>>
>>>> Ok I see, so the caller() call fail with a compile error if static analysis finds it's x is >= 10 before the call to foo(x)?
>>>> then caller() can also static analysis check return z < x. These would force both functions to put checks that their values are within bounds and handle it safely I expect?
>>>>
>>>
>>> Close, right now it's only runtime checks but it can have enough
>>> information that some compile checks
>>> can be done.
>>
>> My concern would be that a pre() I expect to run at at compile time may silently degrade to be be a runtime assert().
>> I like having a separate name compile_assert(), maybe it could be an attribute eg
>> [[compiler]] pre();
>> [[runtime]] pre();
>>
>> Although that starts to look complicated.
>>
>>>> I wrote out below, where I show the lines the programmer needed to add:
>>>>
>>>> size_t foo(size_t x)
>>>> {
>>>> size_t result = get_network(x);
>>>>
>>>> // this line added to satisfy build constraint.
>>>> if(result >= x) throw std::runtime_error;
>>>> return result;
>>>> }
>>>>
>>>> void caller()
>>>> {
>>>> size_t x = get_val();
>>>>
>>>> // this line added to pass constraint.
>>>> if(x >= 10) throw std::runtime_error;
>>>>
>>>> size_t z = foo(x);
>>>>
>>>> std::cout << z;
>>>> }
>>>>
>>>
>>> Yes, we could require that the programmer have explicit checks for
>>> this too, but `get_val` has some
>>> constraints on its return value that would make this check not needed,
>>> but if the function has
>>> changed (like return bigger range) then the compiler could fail to compile it.
>>>
>>> I think we could require the compiler to do basic folding like `x <
>>> 10` and `z < x` and conclude that
>>> `z < 10`.
>>
>> My thought was static analysis is more about proving the program is correct and safe. The optimizer does constant propagation.
>> I know I hijacked the optimizer to find risky branches, and put a function call in the unsafe branch location, so I get a build error when the function is missing or [[gnu:error]].
>>
>> How would these pre(), post() differ from the proposed compile_assert() reliance upon the Optimizer? I think static analysis would also require a control-flow-graph, and many compilers don't have a static analyser. I recall David Malcolm added some GCC. I noticed eg nonnull attribute doesn't get propagated fully yet.
>>
>
> Because they do not need an optimizer and inlining? Only thing is
> constexpr calculation that is always required for any C++ compiler in
> any mode past C++11.
> If we stick to only basic operations like `<,>,!=,==` compiler can
> change `pre(x < 10)` to `temp_x = MAX_INT; temp_x = std::min(temp_x,
> 10 - 1);` and then put this
> `temp_x` to the next precondition. And all this can be done locally
> and do not need to know the external part of the codebase.
> And at some point put this `temp_x` to `static_assert` to check if the
> result still makes sense and not something like `temp_x < temp_x`.
> All compilers should have the same output but if users do not provide
> enough data (or format that compiler can recognize) then we will have
> compiler error in every compiler.
> We can start with very basic operations like comparing with fixed
> values but in further standard we could expand to more complex cases
> like we did with constexpr .
That sounds good. Would it need to do some sort of control flow analysis, rather like the inlining the optimizer does?
Kind regards
Jonathan
> czw., 5 mar 2026 o 13:41 Jonathan Grant <jg_at_[hidden]> napisał(a):
>>
>>
>>
>> On 03/03/2026 12:10, Marcin Jaczewski wrote:> wt., 3 mar 2026 o 12:57 Jonathan Grant <jg_at_[hidden]> napisał(a):
>>>>
>>>>
>>>>
>>>> On 03/03/2026 11:19, Marcin Jaczewski wrote:
>>>>> wt., 3 mar 2026 o 11:41 Jonathan Grant <jg_at_[hidden]> napisał(a):
>>>>>>
>>>>>>
>>>>>>> On Sun, 22 Feb 2026, 11:26 Marcin Jaczewski via Std-Proposals, <std-proposals_at_[hidden] <mailto:std-proposals_at_[hidden]>> wrote:
>>>>>>>
>>>>>> <....>>
>>>>>>> Beside preconditions are visible on both sides of function call even if not
>>>>>>> inlined, this means you do not need to rely on opimalzier to do this job
>>>>>>> as you put enough preconditions to make all checks local.
>>>>>>
>>>>>> Martin may I ask, re inlining, if a function has an attribute [[gnu::noinline]] in my tests I found the constant is not propagated, so the Optimizer fails to identify the constraint is met. Is there any solution?
>>>>>>
>>>>>> [[gnu::noinline]]
>>>>>> int get_int() { return 10; }
>>>>>>
>>>>>> int x = get_int();
>>>>>> compile_assert(x < 15, "x must be < 15");
>>>>>>
>>>>>> If I changed the compile_assert() to call a [[deprecated]] function, the obj file would be produced, but it would then rely upon the linker removing the compile_assert() still there.
>>>>>>
>>>>>> There may be some places where it's difficult to place a compile_assert().
>>>>>>
>>>>>
>>>>> I mean THE C++ preconditions like:
>>>>>
>>>>> ```
>>>>> size_t foo(size_t x) pre(x < 10) post(z: z < x);
>>>>> ```
>>>>>
>>>>> as you see no function body posted there but you know that result will
>>>>> be less than given x.
>>>>> We could ask the compiler to solve them statically and the programmer
>>>>> can add more constatians
>>>>> to make it possible for dumb compiler. No need for optimization levels
>>>>> only constexpr calcations.
>>>>
>>>> Hi Marcin
>>>>
>>>> Ok I see, so the caller() call fail with a compile error if static analysis finds it's x is >= 10 before the call to foo(x)?
>>>> then caller() can also static analysis check return z < x. These would force both functions to put checks that their values are within bounds and handle it safely I expect?
>>>>
>>>
>>> Close, right now it's only runtime checks but it can have enough
>>> information that some compile checks
>>> can be done.
>>
>> My concern would be that a pre() I expect to run at at compile time may silently degrade to be be a runtime assert().
>> I like having a separate name compile_assert(), maybe it could be an attribute eg
>> [[compiler]] pre();
>> [[runtime]] pre();
>>
>> Although that starts to look complicated.
>>
>>>> I wrote out below, where I show the lines the programmer needed to add:
>>>>
>>>> size_t foo(size_t x)
>>>> {
>>>> size_t result = get_network(x);
>>>>
>>>> // this line added to satisfy build constraint.
>>>> if(result >= x) throw std::runtime_error;
>>>> return result;
>>>> }
>>>>
>>>> void caller()
>>>> {
>>>> size_t x = get_val();
>>>>
>>>> // this line added to pass constraint.
>>>> if(x >= 10) throw std::runtime_error;
>>>>
>>>> size_t z = foo(x);
>>>>
>>>> std::cout << z;
>>>> }
>>>>
>>>
>>> Yes, we could require that the programmer have explicit checks for
>>> this too, but `get_val` has some
>>> constraints on its return value that would make this check not needed,
>>> but if the function has
>>> changed (like return bigger range) then the compiler could fail to compile it.
>>>
>>> I think we could require the compiler to do basic folding like `x <
>>> 10` and `z < x` and conclude that
>>> `z < 10`.
>>
>> My thought was static analysis is more about proving the program is correct and safe. The optimizer does constant propagation.
>> I know I hijacked the optimizer to find risky branches, and put a function call in the unsafe branch location, so I get a build error when the function is missing or [[gnu:error]].
>>
>> How would these pre(), post() differ from the proposed compile_assert() reliance upon the Optimizer? I think static analysis would also require a control-flow-graph, and many compilers don't have a static analyser. I recall David Malcolm added some GCC. I noticed eg nonnull attribute doesn't get propagated fully yet.
>>
>
> Because they do not need an optimizer and inlining? Only thing is
> constexpr calculation that is always required for any C++ compiler in
> any mode past C++11.
> If we stick to only basic operations like `<,>,!=,==` compiler can
> change `pre(x < 10)` to `temp_x = MAX_INT; temp_x = std::min(temp_x,
> 10 - 1);` and then put this
> `temp_x` to the next precondition. And all this can be done locally
> and do not need to know the external part of the codebase.
> And at some point put this `temp_x` to `static_assert` to check if the
> result still makes sense and not something like `temp_x < temp_x`.
> All compilers should have the same output but if users do not provide
> enough data (or format that compiler can recognize) then we will have
> compiler error in every compiler.
> We can start with very basic operations like comparing with fixed
> values but in further standard we could expand to more complex cases
> like we did with constexpr .
That sounds good. Would it need to do some sort of control flow analysis, rather like the inlining the optimizer does?
Kind regards
Jonathan
Received on 2026-03-05 14:26:03
