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

melt the ICE when lowering an impossible range #32267

Merged
merged 4 commits into from
Mar 29, 2016

Conversation

durka
Copy link
Contributor

@durka durka commented Mar 15, 2016

Emit a fatal error instead of panicking when HIR lowering encounters a range with no end point.

This involved adding a method to wire up LoweringContext::span_fatal.

Fixes #32245 (cc @nodakai).

r? @nrc

@durka
Copy link
Contributor Author

durka commented Mar 15, 2016

Should I define an error code and use span_fatal_with_code instead?

@alexcrichton
Copy link
Member

"melt the ICE" -- this is an amazing phrase

🍧

@durka
Copy link
Contributor Author

durka commented Mar 15, 2016

Also, if span_fatal is for things that shouldn't happen... you can write a lone ... no problem and it doesn't get caught until here. (Whereas a... cannot be produced without a broken syntax extension.) So should I try to catch lone ... in the parser as well, so that it is recoverable?

@nrc
Copy link
Member

nrc commented Mar 16, 2016

Should I define an error code and use span_fatal_with_code instead?

Ideally yes, but I don't think it is essential.

Also, if span_fatal is for things that shouldn't happen... you can write a lone ... no problem and it doesn't get caught until here. (Whereas a... cannot be produced without a broken syntax extension.) So should I try to catch lone ... in the parser as well, so that it is recoverable?

span_fatal is for things we can't recover from, rather than things that shouldn't happen. It's nice if those two things align, since unrecoverable errors are not great for the user, so should be rare.

I think we should try and catch a lone ... before here. If we can do so in the parser then that would be good. (Note that since we can recover in the parser, we won't abort compilation until lowering and we shouldn't report the error twice. Usually we have some mechanism for that - e.g., we don't emit errors that involve TyErr, but in this case we might have to do something ad hoc).

@@ -336,6 +336,10 @@ impl NodeIdAssigner for Session {
fn peek_node_id(&self) -> NodeId {
self.next_node_id.get().checked_add(1).unwrap()
}

fn span_fatal(&self, sp: MultiSpan, msg: &str) -> ! {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NodeIdAssigner could have a GetDiagnostic method returning Option, that avoids having error methods for every kind of error in NodeIdAssigner.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or just a Diagnostic and panic if it can't be done.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm so bring in DiagnosticBuilder from libsyntax?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

syntax::errors::Handler (sorry, naming has changed somewhat); and GetHandler.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (except this code path is now untested because the parse error was made fatal).

@durka
Copy link
Contributor Author

durka commented Mar 16, 2016

I think we should try and catch a lone ... before here. If we can do so in the parser then that would be good. (Note that since we can recover in the parser, we won't abort compilation until lowering and we shouldn't report the error twice. Usually we have some mechanism for that - e.g., we don't emit errors that involve TyErr, but in this case we might have to do something ad hoc).

I was wondering about this. What does the parser leave behind in the AST when it recovers? Does it just skip the line of code? I tried to inspect the AST, but apparently semicolon recovery isn't enough to get to --pretty=expanded.

@nrc
Copy link
Member

nrc commented Mar 16, 2016

In the AST there isn't an 'error' node. We only recover if we can make a valid AST.

..1;
0..1;

...; //~ERROR inclusive range with no end
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused. We get to this error during lowering, so I assumed the one on the next line (which is a parse error) was recovered. But what valid AST could be left behind?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A range with neither start nor end - the AST doesn't have to be well-formed (for some definition), it just must be constructable. We don't get the lowering error there because the first error was fatal. I suspect if you have a test case with just that line, you would get both a parsing and lowering error.

@durka
Copy link
Contributor Author

durka commented Mar 16, 2016

in this case we might have to do something ad hoc

Here's my plan (can't work on it until tonight): add a field to ast::RangeLimits (perhaps renamed to ast::RangeKind) which the parser sets if it emits an error while constructing a bad range. I can set this field and emit the error in mk_range. Then in HIR lowering we will emit an error if this field isn't set, but if it is... abort silently? This is still a bad experience if a syntax extension sets the field by mistake though.

Anyway, this situation will probably go away when @aturon and I get around to writing that RFC to change the syntax and allow all types of ranges :)

@durka
Copy link
Contributor Author

durka commented Mar 17, 2016

I added the parse error. Haven't implemented the NodeIdAssigner change yet.

@durka
Copy link
Contributor Author

durka commented Mar 17, 2016

OK, ready for review.

@@ -336,6 +336,10 @@ impl NodeIdAssigner for Session {
fn peek_node_id(&self) -> NodeId {
self.next_node_id.get().checked_add(1).unwrap()
}

fn diagnostic(&self) -> &errors::Handler {
self.diagnostic()
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Um. This looks... weird. Did I introduce an infinite recursion (I didn't see a warning) or it's OK because inherent fns take precedence over trait fns?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, inherent has precedence.

and looking more at this has made me realized that one cannot resort to UFCS as a way to try to "clarify" that the inherent method is what is intended. There's no way to say "I want an inherent impl and only an inherent impl", at least not via UFCS...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no way to say "I want an inherent impl and only an inherent impl"

That seems good. Then you can factor out an inherent method into a trait method without a breaking change, is that true?

@@ -140,6 +141,11 @@ impl<'a, 'hir> LoweringContext<'a> {
result
}
}

// panics if this LoweringContext's NodeIdAssigner is not a Session
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bit about being a Session is too much detail, one could imagine implementing NodeIdAssigner with something that is not a Session but can still provide a diagnostic (also, nit, comments should start with a capital and end with a period).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@nrc
Copy link
Member

nrc commented Mar 18, 2016

looks good, r+ with the minor comments addressed

@durka
Copy link
Contributor Author

durka commented Mar 18, 2016

2/3 nits addressed, 1/3 nits quibbled with.

@durka
Copy link
Contributor Author

durka commented Mar 23, 2016

Should be good to go now.

@bors
Copy link
Contributor

bors commented Mar 23, 2016

☔ The latest upstream changes (presumably #32454) made this pull request unmergeable. Please resolve the merge conflicts.

@nrc
Copy link
Member

nrc commented Mar 24, 2016

r+, but needs a rebase

End-less ranges (`a...`) don't parse but bad syntax extensions could
conceivably produce them. Unbounded ranges (`...`) do parse and are
caught here.

The other panics in HIR lowering are all for unexpanded macros, which
cannot be constructed by bad syntax extensions.
@durka
Copy link
Contributor Author

durka commented Mar 24, 2016

Guh. The untry conversion broke a bunch of alignment in libsyntax. Don't know why y'all let that through without reformatting.

Now it is impossible for `...` or `a...` to reach HIR lowering
without a rogue syntax extension in play.
@durka
Copy link
Contributor Author

durka commented Mar 24, 2016

Rebased.

.map(|x|{
hi = x.span.hi;
x
})?)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the record, I think this is much harder to read than the old version with try!. You have to parse the entire expression backwards from ? to see what it applies to.

@nrc
Copy link
Member

nrc commented Mar 28, 2016

@bors: r+

@bors
Copy link
Contributor

bors commented Mar 28, 2016

📌 Commit 861644f has been approved by nrc

@bors
Copy link
Contributor

bors commented Mar 28, 2016

⌛ Testing commit 861644f with merge 44a77f6...

bors added a commit that referenced this pull request Mar 28, 2016
melt the ICE when lowering an impossible range

Emit a fatal error instead of panicking when HIR lowering encounters a range with no `end` point.

This involved adding a method to wire up `LoweringContext::span_fatal`.

Fixes #32245 (cc @nodakai).

r? @nrc
@bors bors merged commit 861644f into rust-lang:master Mar 29, 2016
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.

6 participants