-
-
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
Improve error messages in abstractarray.jl #11797
Conversation
@mbauman @carnaval @timholy I know you weren't fans of #11792 (which I didn't like either, but felt driven to it by a desire for better coverage information), could you please look at this PR on it's own merits? Also, I wanted to also see about adding more testing, because the coverage results I've been getting have been rather dismal. I'm not sure if it is because simply more tests are needed, or because the instructions for getting accurate coverage information require deleting If you wonder why I picked |
I agree some of those lines are ridiculously long (e.g., https://github.com/JuliaLang/julia/pull/11797/files#diff-2264bb51acec4e7e2219a3cb1c733651L65), and restructuring is a definite gain in readability. And I like that you've added arguments to some But: I really don't like that there is a change in semantics in some places. I don't like all the replacements from I also find some of these rearrangements much less readable.
There are other very short lines that were very readable before and now are harder for me to parse. I'm sure I would get used to it eventually, but if we're judging this independently from code coverage changes I don't see the purpose. Finally, I frankly don't like spending time on this. I know you want everything in Julia to be done in the best way possible, but my time here is donated. Discussions like this are taxing and, to my mind, relatively unproductive. There are much better uses of all of our time than surface cosmetics and vetting blind changes. |
The splitting up of some of the tests was in order to be able to get good error messages. |
Perhaps I misunderstood your request to view this PR on its own merits. And I'm quite certain I'm having trouble distancing this from #11792. When I looked through the diff, I saw lots of surface syntax changes, changes in semantics, and what looked to be blind usage of a quirky performance trick. Just like I said in #11792, if this is accompanied by an active push to improve testing coverage, I would definitely view this in a different light (I don't mean the same PR, just ongoing work). Or if this were just the improvements in error messages, again, I'd view that differently. |
I've been running the coverage tests on this, since I started (with the problem of the results may not be good because this is part of inference.dylib and I can't delete that like sys.dylib), and I wanted to start with this because this seems like such an important piece of work. I take all your points about not wanting stuff that might adversely affect performance, and if the GC issue is going to be resolved soon, like the coverage issue, then it just boils down to adding more information to the errors, adding more tests, |
This now only has the improved error messages, and a couple of lines with added whitespace where it was over the 92 character limit. |
Bump: @mbauman just wondering if you'd have a chance to look at this, thanks! |
Yes, this looks much better. I've been meaning to get to running a few cursory performance checks, but I think this should be good to go once I get the chance. Sorry I haven't gotten to it yet. |
throw(ArgumentError("source and destination must have same size")) | ||
length(ir_dest) != length(ir_src) && | ||
throw(ArgumentError("source ($(length(ir_src))) and destination ($(length(ir_dest))) must have same size")) | ||
throw(ArgumentError("source ($(length(jr_src))) and destination ($(length(jr_dest))) must have same size")) |
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.
The error message seems wrong.
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.
What seems wrong to you? It is the same as the comparision, in this case, ir_dest compared to ir_src, and jr_dest compared to jr_src.
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.
Well, the error message would be sth like
ArgumentError: source (2) and destination (3) must have same size
but 2
and 3
are not the source or destination.
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.
I think a common pattern is to write source and destination must have the same size (got X and Y)
NP! Thanks! |
@@ -676,7 +696,7 @@ function hcat{T}(A::AbstractVecOrMat{T}...) | |||
for j = 1:nargs | |||
Aj = A[j] | |||
if size(Aj, 1) != nrows | |||
throw(ArgumentError("number of rows must match")) | |||
throw(ArgumentError("number of rows of A[$j] ($(size(Aj,1))) must match $nrows")) |
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.
Better to avoid the A
in the error message since it doesn't mean anything to the user of the function. Printing the object is probably too much, just print out the index of the argument is probably ok.
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.
Same here, I think you can say number of rows of arrays must match (got $(map(x->size(x, 1), A)))
.
Great idea! |
I hope I've addressed all (but one) of the great review comments, thanks very much @nalimilan and @yuyichao for your time! |
@@ -62,7 +62,11 @@ ndims{T<:AbstractArray}(::Type{T}) = ndims(super(T)) | |||
length(t::AbstractArray) = prod(size(t))::Int | |||
endof(a::AbstractArray) = length(a) | |||
first(a::AbstractArray) = a[1] | |||
first(a) = (s = start(a); done(a, s) ? throw(ArgumentError("collection must be non-empty")) : next(a, s)[1]) | |||
function first(arr) |
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.
This is rather pedantic, I know, but I'd prefer the argument here to remain as a
and not arr
. This definition isn't just for arrays — it's defined for all iterables. Naming it arr
strongly implies that it's just for arrays. If you really want to steer away from the one-letter name, itr
would be better.
Similarly, s
is commonly used as the iterable's state throughout Julia. str
reads as string to me.
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.
state then?
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.
Sure, that'd be great.
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.
That's fixed now, you're right.
To me, the a
also strongly implied it was for arrays, so I do think itr
is clearer,
and given that I've spent most all of my time looking at string handle code, where s
reads as string
and not state
, you can see why I like a little clearer names.
(I know you know exactly what you meant, but the idea is to make things clearer for somebody new to this part of the code)
end | ||
st = start(src) | ||
for j = 1:(soffs-1) | ||
done(src, st) && throw(BoundsError()) | ||
done(src, st) && throw(BoundsError(src, j)) |
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.
I'm not entirely sure about this. In this method, src
is a general iterable and not an abstract array (there are later specializations for abstract array sources). Are there other places where we use bounds errors for iterables (that aren't arrays) terminating earlier than expected? If not, a more accurate message might be something along the lines of: throw(ArgumentError("source has fewer elements than required, expected at least $soffs, got $(j-1)"))
.
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.
OK, I'll fix those up after lunch! Thanks!
@@ -690,7 +710,7 @@ function hcat{T}(A::AbstractVecOrMat{T}...) | |||
for j = 1:nargs | |||
Aj = A[j] | |||
if size(Aj, 1) != nrows | |||
throw(ArgumentError("number of rows must match")) | |||
throw(ArgumentError("number of rows of arrays must match (got $(map(x->size(x,1), A)))")) |
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.
Maybe "number of rows in each array must match (…)"?
Besides a few very minor comments, this looks good. No perf regressions that I can measure, either. Thanks, Scott! |
6442a56
to
e833fb9
Compare
The error type changing based upon the type of the source does seem unfortunate, especially in the context of that test file. It is technically more accurate, but I wonder if it'll just be a headache for folks to handle (e.g., if you know that the source might not have enough elements and want to catch the error, this will make your life very difficult). |
throw(BoundsError()) | ||
dmax > length(dest) && throw(BoundsError(dest, dmax)) | ||
doffs < 1 && throw(BoundsError(dest, doffs)) | ||
throw(BoundsError(src, soffs)) |
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.
Your tests will be a little more sane if this also throws an ArgumentError
. But I'm not totally convinced that this is the way to go.
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.
This needs to be consistent. Either we should change this to be an ArgumentError
here, too, or we should revert the other back to a BoundsError
. I don't really have strong feelings either way, but we gotta do one or the other.
Ok, this looks clean now, thanks so much for all your time reviewing!!! |
I've rebased this (because of the other tests being added for abstractarrays), so now it can again me merged. Thanks again for the reviewing! |
We still need to resolve #11797 (comment) before this can be merged. |
Ah, sorry, I missed that one (away disconnecting with family this weekend!). |
I've been digging this, and unfortunately, it's a bit of a rathole. The |
It's not that bad, is it? I think it's just lines 242 and 276. All the other bounds errors are called on an AbstractArray ( |
Ok, thanks, I'll take care of that when I get home |
All updated, rebased, tests simpler (and passed), anything else? |
LGTM. Squash it, and then this should be good to merge. |
Updated messages based on review Update variable names per Matt Updated tests to handle ArgumentError instead of BoundsError when passed iterable More specific exceptions for test_throws Fix dangling right parenthesis Update non-abstract array error messages and tests
Done! |
Improve error messages in abstractarray.jl
This change improves the error messages for
AbstractArray
, and also (until one of the genius Julians fixes it, apparently quite soon) improves the coverage information.