On Tue, Sep 09, 2025 at 03:30:07PM +0200, Michael Matz wrote:
> But it would also be an invalid transformation of the compiler.  So I 
> can't quite see the worry.  The second check ("*p > 5") is conditional on 
> the first one:
> 
>   if (!p)
>     fail()
>   if (*p > 5)
>     fail2()
> 
> The *p access would only be unconditional if fail() can fall-through.  
> 
> Two cases:
> (a) the spec of contracts says that later contracts may depend on earlier 
>     contracts to have succeeded (dependend contracts): the fail() must not 
>     fall through, that would be an invalid implementation of contracts, 
>     and the compiler removing the first !p check would be buggy
> (b) the spec of contracts say that all of them are independend: then 
>     fail() can fall through and the compiler may indeed remove the !p 
>     check (if fail() itself doesn't have any interesting side-effects).
>     But then the author who wrote those two contracts independendly, where 
>     in reality one depends on the other, was wrong.
> 
> In both cases I don't see how an observably_checkpoint facility would 
> help.
> 
> So, again, a precise spec of the supposed semantics of the builtin are 
> required.  Up to now it seems fairly fuzzy and all cases I've seen in this 
> thread are trying to work around clear compiler or user bugs.
> 
> So, what is or isn't guaranteed by the builtin?

I think some pedantic readings of the standards suggest that if a program
invokes UB, it is invalid as whole and can do anything even before the point
of the UB.  Which I think is true, but the compiler can't assume code
invokes UB if it calls something it doesn't know anything about (because
it could loop forever or exit or throw or setjmp out of the UB invoking
function etc. and so never actually invoke the UB) before the UB is
encountered.
So any movement of UB across such points is a compiler bug, while moving it
across some statements which certainly won't result in the later UB not
being invoked should be fine.

        Jakub

Reply via email to