@@ -19,7 +19,7 @@ use rustc_hash::FxHashMap;
19
19
use smallvec:: SmallVec ;
20
20
use syntax:: {
21
21
ast:: {
22
- self , ArrayExprKind , AstChildren , HasArgList , HasLoopBody , HasName , LiteralKind ,
22
+ self , ArrayExprKind , AstChildren , BlockExpr , HasArgList , HasLoopBody , HasName , LiteralKind ,
23
23
SlicePatComponents ,
24
24
} ,
25
25
AstNode , AstPtr , SyntaxNodePtr ,
@@ -100,6 +100,7 @@ pub(super) fn lower(
100
100
_c : Count :: new ( ) ,
101
101
} ,
102
102
expander,
103
+ current_try_block : None ,
103
104
is_lowering_assignee_expr : false ,
104
105
is_lowering_generator : false ,
105
106
}
@@ -113,6 +114,7 @@ struct ExprCollector<'a> {
113
114
body : Body ,
114
115
krate : CrateId ,
115
116
source_map : BodySourceMap ,
117
+ current_try_block : Option < LabelId > ,
116
118
is_lowering_assignee_expr : bool ,
117
119
is_lowering_generator : bool ,
118
120
}
@@ -222,6 +224,10 @@ impl ExprCollector<'_> {
222
224
self . source_map . label_map . insert ( src, id) ;
223
225
id
224
226
}
227
+ // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
228
+ fn alloc_label_desugared ( & mut self , label : Label ) -> LabelId {
229
+ self . body . labels . alloc ( label)
230
+ }
225
231
fn make_label ( & mut self , label : Label , src : LabelSource ) -> LabelId {
226
232
let id = self . body . labels . alloc ( label) ;
227
233
self . source_map . label_map_back . insert ( id, src) ;
@@ -259,13 +265,7 @@ impl ExprCollector<'_> {
259
265
self . alloc_expr ( Expr :: Let { pat, expr } , syntax_ptr)
260
266
}
261
267
ast:: Expr :: BlockExpr ( e) => match e. modifier ( ) {
262
- Some ( ast:: BlockModifier :: Try ( _) ) => {
263
- self . collect_block_ ( e, |id, statements, tail| Expr :: TryBlock {
264
- id,
265
- statements,
266
- tail,
267
- } )
268
- }
268
+ Some ( ast:: BlockModifier :: Try ( _) ) => self . collect_try_block ( e) ,
269
269
Some ( ast:: BlockModifier :: Unsafe ( _) ) => {
270
270
self . collect_block_ ( e, |id, statements, tail| Expr :: Unsafe {
271
271
id,
@@ -606,6 +606,59 @@ impl ExprCollector<'_> {
606
606
} )
607
607
}
608
608
609
+ /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`,
610
+ /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }`
611
+ /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator.
612
+ fn collect_try_block ( & mut self , e : BlockExpr ) -> ExprId {
613
+ let Some ( try_from_output) = LangItem :: TryTraitFromOutput . path ( self . db , self . krate ) else {
614
+ return self . alloc_expr_desugared ( Expr :: Missing ) ;
615
+ } ;
616
+ let prev_try_block = self . current_try_block . take ( ) ;
617
+ self . current_try_block =
618
+ Some ( self . alloc_label_desugared ( Label { name : Name :: generate_new_name ( ) } ) ) ;
619
+ let expr_id = self . collect_block ( e) ;
620
+ let callee = self . alloc_expr_desugared ( Expr :: Path ( try_from_output) ) ;
621
+ let Expr :: Block { label, tail, .. } = & mut self . body . exprs [ expr_id] else {
622
+ unreachable ! ( "It is the output of collect block" ) ;
623
+ } ;
624
+ * label = self . current_try_block ;
625
+ let next_tail = match * tail {
626
+ Some ( tail) => self . alloc_expr_desugared ( Expr :: Call {
627
+ callee,
628
+ args : Box :: new ( [ tail] ) ,
629
+ is_assignee_expr : false ,
630
+ } ) ,
631
+ None => {
632
+ let unit = self . alloc_expr_desugared ( Expr :: Tuple {
633
+ exprs : Box :: new ( [ ] ) ,
634
+ is_assignee_expr : false ,
635
+ } ) ;
636
+ self . alloc_expr_desugared ( Expr :: Call {
637
+ callee,
638
+ args : Box :: new ( [ unit] ) ,
639
+ is_assignee_expr : false ,
640
+ } )
641
+ }
642
+ } ;
643
+ let Expr :: Block { tail, .. } = & mut self . body . exprs [ expr_id] else {
644
+ unreachable ! ( "It is the output of collect block" ) ;
645
+ } ;
646
+ * tail = Some ( next_tail) ;
647
+ self . current_try_block = prev_try_block;
648
+ expr_id
649
+ }
650
+
651
+ /// Desugar `ast::TryExpr` from: `<expr>?` into:
652
+ /// ```ignore (pseudo-rust)
653
+ /// match Try::branch(<expr>) {
654
+ /// ControlFlow::Continue(val) => val,
655
+ /// ControlFlow::Break(residual) =>
656
+ /// // If there is an enclosing `try {...}`:
657
+ /// break 'catch_target Try::from_residual(residual),
658
+ /// // Otherwise:
659
+ /// return Try::from_residual(residual),
660
+ /// }
661
+ /// ```
609
662
fn collect_try_operator ( & mut self , syntax_ptr : AstPtr < ast:: Expr > , e : ast:: TryExpr ) -> ExprId {
610
663
let ( try_branch, cf_continue, cf_break, try_from_residual) = ' if_chain: {
611
664
if let Some ( try_branch) = LangItem :: TryTraitBranch . path ( self . db , self . krate ) {
@@ -628,7 +681,9 @@ impl ExprCollector<'_> {
628
681
Expr :: Call { callee : try_branch, args : Box :: new ( [ operand] ) , is_assignee_expr : false } ,
629
682
syntax_ptr. clone ( ) ,
630
683
) ;
631
- let continue_binding = self . alloc_binding ( name ! [ v1] , BindingAnnotation :: Unannotated ) ;
684
+ let continue_name = Name :: generate_new_name ( ) ;
685
+ let continue_binding =
686
+ self . alloc_binding ( continue_name. clone ( ) , BindingAnnotation :: Unannotated ) ;
632
687
let continue_bpat =
633
688
self . alloc_pat_desugared ( Pat :: Bind { id : continue_binding, subpat : None } ) ;
634
689
self . add_definition_to_binding ( continue_binding, continue_bpat) ;
@@ -639,9 +694,10 @@ impl ExprCollector<'_> {
639
694
ellipsis : None ,
640
695
} ) ,
641
696
guard : None ,
642
- expr : self . alloc_expr ( Expr :: Path ( Path :: from ( name ! [ v1 ] ) ) , syntax_ptr. clone ( ) ) ,
697
+ expr : self . alloc_expr ( Expr :: Path ( Path :: from ( continue_name ) ) , syntax_ptr. clone ( ) ) ,
643
698
} ;
644
- let break_binding = self . alloc_binding ( name ! [ v1] , BindingAnnotation :: Unannotated ) ;
699
+ let break_name = Name :: generate_new_name ( ) ;
700
+ let break_binding = self . alloc_binding ( break_name. clone ( ) , BindingAnnotation :: Unannotated ) ;
645
701
let break_bpat = self . alloc_pat_desugared ( Pat :: Bind { id : break_binding, subpat : None } ) ;
646
702
self . add_definition_to_binding ( break_binding, break_bpat) ;
647
703
let break_arm = MatchArm {
@@ -652,13 +708,18 @@ impl ExprCollector<'_> {
652
708
} ) ,
653
709
guard : None ,
654
710
expr : {
655
- let x = self . alloc_expr ( Expr :: Path ( Path :: from ( name ! [ v1 ] ) ) , syntax_ptr. clone ( ) ) ;
711
+ let x = self . alloc_expr ( Expr :: Path ( Path :: from ( break_name ) ) , syntax_ptr. clone ( ) ) ;
656
712
let callee = self . alloc_expr ( Expr :: Path ( try_from_residual) , syntax_ptr. clone ( ) ) ;
657
713
let result = self . alloc_expr (
658
714
Expr :: Call { callee, args : Box :: new ( [ x] ) , is_assignee_expr : false } ,
659
715
syntax_ptr. clone ( ) ,
660
716
) ;
661
- self . alloc_expr ( Expr :: Return { expr : Some ( result) } , syntax_ptr. clone ( ) )
717
+ if let Some ( label) = self . current_try_block {
718
+ let label = Some ( self . body . labels [ label] . name . clone ( ) ) ;
719
+ self . alloc_expr ( Expr :: Break { expr : Some ( result) , label } , syntax_ptr. clone ( ) )
720
+ } else {
721
+ self . alloc_expr ( Expr :: Return { expr : Some ( result) } , syntax_ptr. clone ( ) )
722
+ }
662
723
} ,
663
724
} ;
664
725
let arms = Box :: new ( [ continue_arm, break_arm] ) ;
0 commit comments