Skip to content
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

smarter BoundsError reporting #9534

Merged
merged 3 commits into from
Jan 2, 2015
Merged

smarter BoundsError reporting #9534

merged 3 commits into from
Jan 2, 2015

Conversation

vtjnash
Copy link
Sponsor Member

@vtjnash vtjnash commented Jan 1, 2015

BoundsError() made me sad (so I fixed it)

this adds argument information to intrinsic bounds checking operations, optimized to have no added impact on the runtime cost, in the common case of not having an error

lots of examples (because there's a lot of cases this handles):

julia> a,b,c = 1,2
ERROR: BoundsError(
  of (1,2)
  at index [3]
  )
 in indexed_next at /Volumes/Lion/Users/jameson/Documents/julia/usr/lib/julia/sys.dylib

julia> f(::Any, ::Any, ::Any, args...) = Any
f (generic function with 1 method)

julia> invoke(f, (Any, Any, Any), 1, 1, 1)
ERROR: BoundsError(
  of (Int64,Int64...)
  at index [3]
  )
 in typeinf at ./inference.jl:1393
 in typeinf_ext at ./inference.jl:1242

julia> [][1,2]
ERROR: BoundsError(
  of 0-element Array{Any,1}
  at index [1,2]
  )
 in getindex at no file

julia> [1,2][10]
ERROR: BoundsError(
  of 2-element Array{Int64,1}:
 1
 2
  at index [10]
  )
 in getindex at /Volumes/Lion/Users/jameson/Documents/julia/usr/lib/julia/sys.dylib

julia> [1,2][10,10]
ERROR: BoundsError(
  of 2-element Array{Int64,1}:
 1
 2
  at index [10,10]
  )
 in getindex at no file

julia> f(x) = (a=1+2im; a.(x))
f (generic function with 2 methods)

julia> f(4)
ERROR: BoundsError(
  of 1 + 2im
  at index [4]
  )
 in f at no file

julia> f(x) = (a=(1,2.,""); a[x])
f (generic function with 2 methods)

julia> f(4)
ERROR: BoundsError(
  of (1,2.0,"")
  at index [4]
  )
 in f at no file

julia> f(x) = (a=(1,2.); a[x])
f (generic function with 2 methods)

julia> f(4)
ERROR: BoundsError(
  of (1,2.0)
  at index [4]
  )
 in f at no file

julia> f(x) = (a=(1,2,4,6,7); a[x])
f (generic function with 2 methods)

julia> f(30)
ERROR: BoundsError(
  of (1,2,4,6,7)
  at index [30]
  )
 in f at no file

julia> f(x) = (a=IOBuffer(); a.(x) = 3)
f (generic function with 2 methods)

julia> f(30)
ERROR: BoundsError(
  of IOBuffer
  at index [30]
  )
 in f at no file

julia> f(x) = (a=IOBuffer(); a.(x))
f (generic function with 2 methods)

julia> f(30)
ERROR: BoundsError(
  of IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
  at index [30]
  )
 in f at no file

julia> f() = (a=IOBuffer(); a.(100))
f (generic function with 3 methods)

julia> f()
ERROR: BoundsError(
  of IOBuffer(data=UInt8[...], readable=true, writable=true, seekable=true, append=false, size=0, maxsize=Inf, ptr=1, mark=-1)
  at index [100]
  )
 in f at no file

julia> f() = (a=1+2im; a.(100))
f (generic function with 3 methods)

julia> f()
ERROR: BoundsError(
  of 1 + 2im
  at index [100]
  )
 in f at no file

julia> f() = (a=(1,2,3); a[100])
f (generic function with 3 methods)

julia> f()
ERROR: BoundsError(
  of (1,2,3)
  at index [100]
  )
 in f at no file

julia> f() = (a=[1,2,3]; a[100])
f (generic function with 3 methods)

julia> f()
ERROR: BoundsError(
  of 3-element Array{Int64,1}:
 1
 2
 3
  at index [100]
  )
 in f at no file

julia> [][]
ERROR: BoundsError(
  of 0-element Array{Any,1}
  at index [1]
  )
 in getindex at no file

julia> (1,2,3)[100]
ERROR: BoundsError(
  of (1,2,3)
  at index [100]
  )
 in getindex at /Volumes/Lion/Users/jameson/Documents/julia/usr/lib/julia/sys.dylib

julia> (1,2,3.)[100]
ERROR: BoundsError(
  of (1,2,3.0)
  at index [100]
  )
 in getindex at /Volumes/Lion/Users/jameson/Documents/julia/usr/lib/julia/sys.dylib

julia> (1,2,3.,"4")[100]
ERROR: BoundsError(
  of (1,2,3.0,"4")
  at index [100]
  )
 in getindex at /Volumes/Lion/Users/jameson/Documents/julia/usr/lib/julia/sys.dylib

julia> (1+2im).(100)
ERROR: BoundsError(
  of 1 + 2im
  at index [100]
  )

julia> error(BoundsError())
ERROR: BoundsError()
 in error at no file

julia> error(BoundsError(Int))
ERROR: BoundsError(
  of Int64
  )
 in error at no file

julia> error(BoundsError(Int, 10))
ERROR: BoundsError(
  of Int64
  at index [10]
  )
 in error at no file

julia> error(BoundsError(Int, (:a,)))
ERROR: BoundsError(
  of Int64
  at index [a]
  )
 in error at no file

julia> f(a,b,c...) = c[a]
f (generic function with 1 method)

julia> f(1000,2,3,4,5,6)
ERROR: BoundsError(
  of (3,4,5,6)
  at index [1000]
  )

ref #9520
ref #6297
ref #7978
ref #4744

this adds argument information to intrinsic bounds checking operations, optimized to have a no added impact on the runtime cost in the common case of not having an error
@vtjnash vtjnash changed the title smarter bounds checking smarter BoundsError reporting Jan 1, 2015
@nalimilan
Copy link
Member

Wow, this is a really incredible start for the new year! This is a major improvement IMHO.

One small suggestion: maybe to make the error even more explicit, say

ERROR: BoundsError(
  attempt to access X
  at index [Y]
  )

@timholy
Copy link
Sponsor Member

timholy commented Jan 1, 2015

💯

@dhoegh
Copy link
Contributor

dhoegh commented Jan 1, 2015

+1 for the wording suggested by @nalimilan, I find it more explicit and easy to understand.

@Ismael-VC
Copy link
Contributor

+1 for explicit and readable, cool!

On Thu, Jan 1, 2015 at 8:16 AM, Daniel Høegh [email protected]
wrote:

+1 for the wording suggested by @nalimilan https://github.com/nalimilan,
I find it more explicit and easy to understand.


Reply to this email directly or view it on GitHub
#9534 (comment).

@chrisvoncsefalvay
Copy link

<3 this.

@tknopp
Copy link
Contributor

tknopp commented Jan 1, 2015

+10000

This can only be beaten by package precompilation Jameson ;-)

@timholy
Copy link
Sponsor Member

timholy commented Jan 1, 2015

We should all get together and buy Jameson his own tropical island for Christmas (as long as it has really good WiFi).

@vtjnash vtjnash merged commit 59d221e into master Jan 2, 2015
@joehuchette
Copy link
Member

This is a fantastic change, but the printing maybe should be tweaked a bit:

a = rand(100); a[101]

gives
screenshot 2015-01-02 00 42 06

@timholy
Copy link
Sponsor Member

timholy commented Jan 2, 2015

@joehuchette, the printing could be tackled by anyone, that's something that doesn't need Jameson's wizard skills. Care to take a crack yourself? See the implementation of show(io::IO, be::BoundsError) in base/replutil.c.

@JeffBezanson
Copy link
Sponsor Member

Yes the printing needs to be fixed. There is no point in showing syntax-style BoundsError( ... ) if there is free-form english inside the parentheses.

@yuyichao
Copy link
Contributor

yuyichao commented Jan 4, 2015

I see getfield(1 + 2im, 3) in the test. Is there a reason to leave getindex(1 + 2im, 3) (as well as other use of BoundError() in the base module)?

Also given that the help string is

help?> getfield
Base.getfield(value, name::Symbol)

Why is a non-symbol name even allowed?

IMHO, it is super confusing right now if for the following error is raised

julia> getfield([1], 1)
ERROR: BoundsError: attempt to access 1-element Array{Int64,1}:
 1
  at index [1]

Which looks like (just from the error) julia raise a error on [1][1].....

Edit: Ok. I see that number index is used to access field in a composite type. Nevertheless, the error message above is still confusing. It's probably better to change the doc string of getfield to include this and have a slightly better error message as well.

@vtjnash
Copy link
Sponsor Member Author

vtjnash commented Jan 4, 2015

since getfield and getindex throw exactly the same error, there wasn't much I could do about the error message. hopefully, that will usually be available from context when reading the source code.

getfield (x[n]) and getindex (x.(n)) are quite different in how they operate:
getindex(x::Numeric, n) returns the number x (for n = 1), while
getfield(x, n) returns the n-th field of the object x (numbered in order of their definition in the object)

@yuyichao
Copy link
Contributor

yuyichao commented Jan 4, 2015

Maybe adding a flag to indicate the context? (getindex/getfield, maybe also another one for iterator?)

@nalimilan
Copy link
Member

Just wondering: wouldn't it be better to print the index first, and the array after? When arrays are long, the index is hard to find. For example (from #9607):

julia-0.4> b[4] = true
ERROR: BoundsError: attempt to access 3-element BitArray{1}:
  true
  true
 false
  at index [4]

vs. something like

julia-0.4> b[4] = true
ERROR: BoundsError: attempt to access
  index [4]
of 3-element BitArray{1}:
  true
  true
 false

What do you think?

@timholy
Copy link
Sponsor Member

timholy commented Jan 5, 2015

When the underlying array is 100x100x2500, the index (if it comes first) will disappear from the console's history. But in any event we probably want to switch to printing a summary when the array gets large.

@jakebolewski
Copy link
Member

I don't see how printing out the value is useful. The Type of the value, it's size / length and the out-of-bounds index requested should be all you need.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet