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!