-
-
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
RFC: Safer, extensible ﹫inbounds #8227
Conversation
I'm amazed you managed to reimplement that part of the expansion pass. Having that code duplicated is not good. It probably would have been easier to replace I think this is also a sign that this macro is slightly sketchy (admittedly the existing
So I think there is still something subtly unsatisfying about this approach. |
Yes, modifying the front-end is probably a better solution. Maybe we could have a This macro doesn't actually replace any identifiers; it just expands let getindex(x) = 2x
@inbounds y = x[1]
end would end up calling |
It would be great if this pattern could be easily applied to other functions, as this sort of problem pops up in other cases. Two that come to mind:
|
4516065
to
457ce1b
Compare
Here's another crack at changing how |
457ce1b
to
c81021a
Compare
c81021a
to
7cb11d5
Compare
👍. I can't review the Scheme code changes, but I have no complaints about the Julia code. Looking forward to having |
@@ -342,8 +342,9 @@ end | |||
## Indexing: getindex ## | |||
|
|||
function unsafe_bitgetindex(Bc::Vector{Uint64}, i::Int) | |||
return (Bc[@_div64(i-1)+1] & (uint64(1)<<@_mod64(i-1))) != 0 | |||
return @inbounds (Bc[@_div64(i-1)+1] & (uint64(1)<<@_mod64(i-1))) != 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's great if this does no longer cause the function to return nothing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup:
julia> x = [1, 2];
julia> @inbounds x[1]
1
what if these were to go into a special module. I may need to write a longer Julep for this, but here's my basic idea for it: Instead of module Base
baremodule Unsafe
getindex(x...) = Base.getindex(x...)
setindex!(x...) = Base.setindex!(x...)
# unsafe versions go here
end
# the existing definitions remain here
end And perhaps add a few new intrinsics to do the unsafe operations (or perhaps just enhance If the user wanted, they could then access these directly, or with an But in typical usage, it would just But really, ideally, the following could probably be made just as efficient, without too much effort: macro inbounds(expr)
quote
let getindex = Base.Unsafe.getindex, setindex! = Base.Unsafe.setindex!
$(esc(expr))
end
end
end Which would be really sweet, since it doesn't require any special handling. |
@vtjnash That's an interesting approach. If we set a flag we could presumably choose to substitute |
A slightly-less contrived example is: module MyModule
global getindex
getindex(x...) = ...
function f()
@inline x[1] = 2
end
end But what I perhaps like most about the module MyModule
macro inbounds(expr...)
:( let getindex = my_getindex; :(esc(expr)); nothing; end )
end
# now @inbounds is something different
end p.s. I might as well mention this too: I think the module approach also generalizes really nicely for other checked/unchecked operations (cough_arithmetic_cough). So you could rewrite the default arithmetic in Base to be always do checked math, and then have an p.p.s I think p.p.s. while my approach may seems similar, note that it would have significantly behavior in the following case, which may not be entirely desirable: function f()
@inbounds map(getindex, [[1],[2],[3]])
end But then again, having the following operations calling different functions is perhaps a bit odd too: function f()
@inbounds begin
let gi = getindex
getindex(x) == gi(x) == x[]
end
end
end p.p.p.s. I corrected my above |
A problem with the @inbounds x = a[1]
x # undefined Also, I don't think we want |
So given the scoping issue with
|
Options two and three are the same. I'll let Jeff make the final call. |
@vtjnash At least as I'm envisioning them, they're not quite the same: option 2 would only substitute functions for functions, while option 3 would work on at the level of identifiers. Option 2 would avoid the hygiene issue (we could substitute |
2ef98c5
to
0388647
Compare
Bump. Now that we have most of the other critical elements of #7941, this (in my opinion) is the key outstanding issue. It's not obvious to me that one wants to turn on |
Given the interest in moving towards finalizing 0.4, @JeffBezanson, input/code review is needed here. In my opinion this is the main obstacle for returning views from indexing in 0.4. Most of the work is already done: in addition to the SubArray and multidimensional indexing revamp, there's #9150 waiting in the wings. But we need to make a couple of decisions before we can move forward. Two comments:
|
Some more thoughts:
|
Moving the discussion here, referencing #10525 (comment):
Couldn't we have abstract BoundsCheck
immutable BoundsCheckOn <: BoundsCheck; end
immutable BoundsCheckOff <: BoundsCheck; end
getindex(::BoundsCheck, x, I...) = getindex(x, I...) Then users can opt into getting bounds check information when defining their own methods. It makes calling
I was thinking of that part as solved by the code you've already written here. :) |
@mbauman With the fallback, it seems that any method defined as |
The reason I liked having this as an aspect of multiple dispatch is not for defining different methods, but rather as a way to allow the compiler to elide bounds checks. Bool(::BoundsCheckOn) = true
Bool(::BoundsCheckOff) = false
getindex(b::BoundsCheck, x::AbstractArray, I...) = _getindex(b, linearindexing(x), x, I...)
function _getindex(b::BoundsCheck, ::LinearFast, x, I::Union(Real, AbstractArray, Colon)...)
Bool(b) && checkbounds(x, I...) # I initially tried isa(b, BoundsCheckOn), but of course that doesn't work
… # compute i::Int from I...
getindex(BoundsCheckOff(), x, i) # If the user hasn't defined this, it will fall back to the next method
end
_getindex(::BoundsCheck, ::LinearFast, x, I::Int) = getindex(x, I) # And now this will throw the appropriate no method error! Edit: |
Oooh, right you are. This will cause a strange middle-ground where |
This implements my proposal from #7799 (comment).
unsafe_getindex
/unsafe_setindex!
providegetindex
/setindex!
functionality without bounds checks, and@inbounds
rewrites the AST to call the unsafe versions of these functions.@inbounds
only affects the expression it's applied to, not all inlined function calls.@boundscheck
still works and provides the old behavior in case there's a really good reason to use it. As a bonus, this variant of@inbounds
could be made to pass through the value of the enclosed expression,although at the moment I don't think the behavior is as expected for.setindex!
expressionsThis would have been a lot easier if it didn't require reimplementing the expansion of expressions containing
ref
intogetindex
/setindex!
in Julia beforearray.jl
is loaded. It may be a better idea to adjust the femtolisp code so that it can perform this expansion without expanding anything else. Being able to rewritegetindex
/setindex!
with a macro may also be useful for the transition to arrays as views.