-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
implement catch
expressions
#39849
Labels
C-enhancement
Category: An issue proposing an enhancement or a PR with one.
E-medium
Call for participation: Medium difficulty. Experience needed to fix: Intermediate.
E-mentor
Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
Comments
nikomatsakis
added
E-medium
Call for participation: Medium difficulty. Experience needed to fix: Intermediate.
E-mentor
Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
C-enhancement
Category: An issue proposing an enhancement or a PR with one.
labels
Feb 15, 2017
@cramertj has expressed interest in making these changes. |
Open
12 tasks
Question that @cramertj encountered: https://internals.rust-lang.org/t/grammatical-ambiguity-around-catch-blocks/4807 |
Merged
GuillaumeGomez
added a commit
to GuillaumeGomez/rust
that referenced
this issue
Feb 22, 2017
…tsakis Normalize labeled and unlabeled breaks Part of rust-lang#39849.
bors
added a commit
that referenced
this issue
Feb 24, 2017
Normalize labeled and unlabeled breaks Part of #39849.
eddyb
added a commit
to eddyb/rust
that referenced
this issue
Feb 25, 2017
…tsakis Normalize labeled and unlabeled breaks Part of rust-lang#39849.
eddyb
added a commit
to eddyb/rust
that referenced
this issue
Feb 25, 2017
…tsakis Normalize labeled and unlabeled breaks Part of rust-lang#39849.
frewsxcv
added a commit
to frewsxcv/rust
that referenced
this issue
Mar 8, 2017
…tsakis Add catch {} to AST Part of rust-lang#39849. Builds on rust-lang#39864.
frewsxcv
added a commit
to frewsxcv/rust
that referenced
this issue
Mar 9, 2017
…tsakis Add catch {} to AST Part of rust-lang#39849. Builds on rust-lang#39864.
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Mar 10, 2017
…tsakis Add catch {} to AST Part of rust-lang#39849. Builds on rust-lang#39864.
alexcrichton
added a commit
to alexcrichton/rust
that referenced
this issue
Mar 10, 2017
…tsakis Add catch {} to AST Part of rust-lang#39849. Builds on rust-lang#39864.
3 tasks
frewsxcv
added a commit
to frewsxcv/rust
that referenced
this issue
Mar 21, 2017
…sakis Implement `?` in catch expressions Builds on rust-lang#39921. Final part of rust-lang#39849. r? @nikomatsakis
I believe this can be closed now that #40229 has landed. |
Agreed. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
C-enhancement
Category: An issue proposing an enhancement or a PR with one.
E-medium
Call for participation: Medium difficulty. Experience needed to fix: Intermediate.
E-mentor
Call for participation: This issue has a mentor. Use #t-compiler/help on Zulip for discussion.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
This is a sub-issue of #31436 corresponding to the addition of
catch
expressions. What follows is a rough implementation plan. It consists of four PRs. I'll happily expand and add details upon request.The high-level summary is that it would be nice to implement catch by having the HIR support the ability of a
break
to target arbitrary enclosing blocks. This would not be something exposed in the main language, which still permits only breaking out of loops (but we might choose to do so in the future, that'd require an RFC).Here are some high-level notes on how to go about this. It's a "medium-sized" job, I think. It may be best to begin with some refactorings, as you will see.
Refactoring 1: Normalize how labeled and unlabeled breaks work.
ExprBreak
andExprAgain
variants ofhir::Expr_
reference aOption<Label>
indicating where we should break to'a: loop { ... break 'a }
), it will get translated into a specific node-id (theloop_id
field of theLabel
struct)lower_label()
method of HIR loweringloop { break; }
), then we don't store any label or indicationof where you break to. Instead, each pass must carry a stack of loops along with it. This is annoying and it would be nice to change it, so that every HIR
break
(andcontinue
) already knows precisely where it is going.break
variant toExprBreak(Label, Option<P<Expr>>)
-- the "label", in particular, is no longer optional.Label
struct so that the "name" field is optional, for pretty-printing if nothing elselibrustc_passes/loops.rs
code currently does some sanity check and reports errors for malformedbreak
(e.g.,break
outside of a loop)ExprBreak
andExprAgain
:Some
path that exists (i.e., the code is there to handle the
None
that we are removingliveness
, theloop_scope
stack can just be removed entirely, we won't be using it anymoreWhy do this? This refactoring is an improvement because it consolidates the knowledge of where a
break
goes into one pass: HIR lowering. Right now, that knowledge is repeated in several passes, each of which must maintain a stack of loops. It will still be necessary to keep that stack of loops, but we'll be using it differently. In today's code, we sometimes search the stack for a loop with a given id (labeled break) and sometimes just take the top of the stack (unlabeled break). After this refactoring, we are always searching for the entry with a given id. This is important, because in the next section we are going to add blocks into these stacks, and so if we wanted to keep unlabeled breaks as they are, we'd have to ensure that when we check the top of the stack, we don't find some random block, but only the top-most loop.Next step: add
catch
into ASTThe goal here is just to do the plumbing of parsing
catch
and getting it into the AST.catch
catch
nodes?
is used inside of acatch
node, I would just abort withbug!("unimplemented: ? in catch")
Next step: allow HIR
break
to target blocksThe goal here is to make it possible for breaks to target blocks in HIR. We won't need to change the data structures very much for this to work. Probably it's a good idea to rename the field
loop_id
inhir::Label
to something likenode_id
ortarget_id
, since it won't always be naming a loop anymore.In principle, this change can be landed as an initial PR. Annoyingly, though, since we are not changing the surface syntax, there isn't a good way to test this code without also adding support for
catch
, so we may want to do the two changes together in one PR. Though I'd also be ok to land them separately, or perhaps add some sort of internal testing attributes that enable anonymousbreak
to break out of the innermost block (#[rustc_break_from_block]
).Anyway, we basically don't need to change HIR lowering at all. All the existing breaks are still valid breaks. The only change is that (in the next PR...) HIR lowering will produce new breaks that target blocks and not just loops. So we need to prepare the passes that consume breaks to be prepared for that. These are the same set of passes we saw in the first refactoring: MIR construction, liveness, and CFG construction. As we saw in that first refactoring, each of these passes maintains an internal stack of loops which they already use to search and find the appropriate place that a break should branch to. So the gist of the change is to extend these stacks to include breaks too. Here are some details on each pass:
with_loop_nodes
helper is the one that sets up the targets for breaks and so forth. At present it pushes onto a stack (self.loop_scope
), but that should have been removed in the first refactoring. It also stores into a map (self.break_ln
) with the "place that a break with a given target should go to" (ln
stands for "liveness node").propagate_through_block(blk, succ_ln)
method, we want to therefore recordself.break_ln.insert(node_id, succ_ln)
, assucc_ln
is the liveness node for the successor of the blockLoopScope
entries rather than a map like liveness. We'll need to push onto this for blocks. The relevant function isfn block(&mut self, blk: &hir::Block, pred: CFGIndex) -> CFGIndex
; annoyingly, the way that the CFG construction works is that you are given the predecessor node (pred
) and you return the successor. This means that whenever we process a block, we'll have to create a dummy node (self.add_dummy_node(&[])
) that we can add into theLoopScope
vector to use as the target of blocks. Once we're finished processing the contents of the block, it can branch to this dummy node and then we will return the dummy node.in_loop_scope()
ExprBreak
that targets a block generate an appopriate value; this should work the same way that it does forlet x = loop { break 22 };
.Efficiency note. We may not want to create a bunch of dummy basic blocks for every block. Unfortunately, because MIR and CFG construction works forward, this is sort of hard to avoid. To fix this, I think we will want to extend the HIR for a block to include a boolean indicating whether it is targeted by any breaks, so we can only add the dummy node for those cases where it is needed. This will also mean you have to edit the HAIR, which is a kind of simple, HIR-like representation that exists briefly as we lower to MIR (
src/librustc_mir/hair/
). This change should be fairly straightforward -- in particular, until the next step, NO blocks can be targeted by breaks, so we can just set this value to always be false.The final change: adding
catch
to the HIRWith those pieces in place, we can finally put all the various bits together. Basically when building the HIR for
catch { ... }
, we would do the following:?
, we would construct anExprBreak
targeting this block (with a value), instead of aExprReturn
That's "it"! Then we can write some tests!
The text was updated successfully, but these errors were encountered: