-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Non-capturing BoundsError API #44439
Conversation
This commit changes the semantics of `BoundsError` so that it doesn't capture arrays/tuples passed to its constructor. The change wouldn't improve any performance by itself because the `BoundsError` constructor is mostly called on error paths and thus it is opaque to analyses/optimizers when compiling a caller context. Rather it's supposed to be a building block for broadening the possibility of certain memory optimizations in the future such as copy elision for `ImmutableArray` construction and stack allocation of `Array`s, by allowing us to assume the invariant that primitive indexing operations into array/tuple indexing don't escape it so that our escape analyses can achieve further accuracy. Specifically, when `BoundsError` constructor will now compute the "summary" of its `Array`/`Tuple` argument, rather than capturing it for the later inspection. Assuming `Base.summary(x::Array)` or `Base.summary(x::Tuple)` don't escape `x`, we can assume that `BoundsError` doesn't escape `x`. This change won't appear as breaking in most cases since `showerror` will print the exact same error message as before, but obviously this is technically breaking since we can no longer access to the original arrays/tuples by catching `BoundsError`. I'd say this breaking semantic change would be still acceptable, since I think it's enough if we can know size/type of arrays/tuples from `BoundsError` in most cases. As a last note, I didn't change the semantics for arbitrary user objects, since we still don't have a good infrastructure to tell the compiler some primitive assumptions/rules about user type objects anyway.
A great package to test this out on as well is StaticArrays.jl in particular |
Hmm yeah. It seems to work fine with |
That wouldn't be great. Part of the goal (as I understand it) is to avoid having every throw site depend on the string handling code. As an example we can produce bounds errors on the GPU just find, but creating a string would not work. |
Gotcha. I suppose for now we can limit the scope of this to |
The |
Right, so I guess we do this "kinda summary" in |
Just doing some old PR cleanup |
This PR rebases #43738 and changes the API for
BoundsError
a bit.We export
throw_boundserror
from Base, which by default routes to the sameBoundsError
constructor that we know and love. However, we also implement a newBoundsError
constructor that accepts axes (or whatever equivalent notion of size) and a type rather than an object.Therefore, by defining
throw_boundserror(t::Tuple,...)
,throw_boundserror(s::AbstractString,...)
to give just the size and type info to the newBoundsError
constructor, we avoid capture of these objects, opening us up to be a bit less conservative aboutThrownEscape
in #43800. Package developers can also add their own implementation ofthrow_boundserror
to prevent their objects from being captured byBoundsError
and give more accurate size/type in error messages for their custom types.We also define a new function,
jl_bounds_error_summarized
, which replacesjl_bounds_error
injl_get_nth_field
and similarly avoids the capture of structs viagetfield
/getproperty
.Note: for
Array
, we callBase.axes
from inside of the normalBoundsError
constructor, instead of defining athrow_boundserror(a::AbstractArray,...)
, per #43738 (comment).Co-authored by Shuhei Kadowaki (@aviatesk)