This does seem questionable – consider
julia> fib(n) = n < 0 ? 1 : fib(n-1) + fib(n-2)
julia> fub(x) = x ? fib(5) : 0
We want that speculatively executed!
julia> fib(n) = n < 0 ? 1 : fib(n-1) + fib(n-2)
julia> fub(x) = x ? fib(1000) : 0
No way do we want to execute that speculatively, that wouldn’t terminate until heat death!
For speculative execution, we really want to specialize the effects on constants. We don’t care about the effects of fib(n::Int) (which arguably correctly infers as maybe-not-terminating), we only care about the effects of fib(5) (and we only care about that until we spot the first side-effect / inconsistency!).
That is a very valid point: It can be fine to have code containing bounds-check violations that are valid, intended, caught and handled, a la
julia> function foo(a)
try
return a[100]
catch
0
end
end
foo (generic function with 1 method)
julia> foo([])
0
Similar as global --fast-math had to be removed.
If I understand the digression on speculative execution right, then the issue is that Core.Compiler is now such code, due to internal implementation details, that gets miscompiled under a naive --check-bounds=no regime.
So the change is that: Before, only a subset/dialect of julia ran correctly under --check-bounds=no (albeit with changed semantics), and this subset has now become empty?
For that, I would consider a system that allows specific modules to declare that their bounds-checks must not be removed even if --check-bounds=no. In order to make the subset/dialect of julia that is --check-bounds=no-compatible nonempty, it is sufficient to hard-code Core.Compiler? (but I guess a compile-time option in some header file would be more convenient, or even a @insist_on_boundschecks module ... end macro)
As long as --check-bounds=yes remains part of the language, the obvious consequence of removing --check-bounds=no is that everybody is incentivized to add @inbounds everywhere, and document that their package should be run under --checkbounds=yes for testing.
This is stupid!