3
3
4
4
use std:: { mem, sync:: Arc } ;
5
5
6
+ use base_db:: CrateId ;
6
7
use either:: Either ;
7
8
use hir_expand:: {
8
9
ast_id_map:: AstIdMap ,
@@ -18,7 +19,7 @@ use rustc_hash::FxHashMap;
18
19
use smallvec:: SmallVec ;
19
20
use syntax:: {
20
21
ast:: {
21
- self , ArrayExprKind , AstChildren , HasArgList , HasLoopBody , HasName , LiteralKind ,
22
+ self , ArrayExprKind , AstChildren , BlockExpr , HasArgList , HasLoopBody , HasName , LiteralKind ,
22
23
SlicePatComponents ,
23
24
} ,
24
25
AstNode , AstPtr , SyntaxNodePtr ,
@@ -36,6 +37,7 @@ use crate::{
36
37
RecordFieldPat , RecordLitField , Statement ,
37
38
} ,
38
39
item_scope:: BuiltinShadowMode ,
40
+ lang_item:: LangItem ,
39
41
path:: { GenericArgs , Path } ,
40
42
type_ref:: { Mutability , Rawness , TypeRef } ,
41
43
AdtId , BlockId , BlockLoc , ModuleDefId , UnresolvedMacro ,
@@ -80,9 +82,11 @@ pub(super) fn lower(
80
82
expander : Expander ,
81
83
params : Option < ( ast:: ParamList , impl Iterator < Item = bool > ) > ,
82
84
body : Option < ast:: Expr > ,
85
+ krate : CrateId ,
83
86
) -> ( Body , BodySourceMap ) {
84
87
ExprCollector {
85
88
db,
89
+ krate,
86
90
source_map : BodySourceMap :: default ( ) ,
87
91
ast_id_map : db. ast_id_map ( expander. current_file_id ) ,
88
92
body : Body {
@@ -96,6 +100,7 @@ pub(super) fn lower(
96
100
_c : Count :: new ( ) ,
97
101
} ,
98
102
expander,
103
+ current_try_block : None ,
99
104
is_lowering_assignee_expr : false ,
100
105
is_lowering_generator : false ,
101
106
}
@@ -107,7 +112,9 @@ struct ExprCollector<'a> {
107
112
expander : Expander ,
108
113
ast_id_map : Arc < AstIdMap > ,
109
114
body : Body ,
115
+ krate : CrateId ,
110
116
source_map : BodySourceMap ,
117
+ current_try_block : Option < LabelId > ,
111
118
is_lowering_assignee_expr : bool ,
112
119
is_lowering_generator : bool ,
113
120
}
@@ -176,8 +183,7 @@ impl ExprCollector<'_> {
176
183
self . source_map . expr_map . insert ( src, id) ;
177
184
id
178
185
}
179
- // desugared exprs don't have ptr, that's wrong and should be fixed
180
- // somehow.
186
+ // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed somehow.
181
187
fn alloc_expr_desugared ( & mut self , expr : Expr ) -> ExprId {
182
188
self . body . exprs . alloc ( expr)
183
189
}
@@ -199,6 +205,10 @@ impl ExprCollector<'_> {
199
205
self . source_map . pat_map . insert ( src, id) ;
200
206
id
201
207
}
208
+ // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
209
+ fn alloc_pat_desugared ( & mut self , pat : Pat ) -> PatId {
210
+ self . body . pats . alloc ( pat)
211
+ }
202
212
fn missing_pat ( & mut self ) -> PatId {
203
213
self . body . pats . alloc ( Pat :: Missing )
204
214
}
@@ -214,6 +224,10 @@ impl ExprCollector<'_> {
214
224
self . source_map . label_map . insert ( src, id) ;
215
225
id
216
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
+ }
217
231
fn make_label ( & mut self , label : Label , src : LabelSource ) -> LabelId {
218
232
let id = self . body . labels . alloc ( label) ;
219
233
self . source_map . label_map_back . insert ( id, src) ;
@@ -251,13 +265,7 @@ impl ExprCollector<'_> {
251
265
self . alloc_expr ( Expr :: Let { pat, expr } , syntax_ptr)
252
266
}
253
267
ast:: Expr :: BlockExpr ( e) => match e. modifier ( ) {
254
- Some ( ast:: BlockModifier :: Try ( _) ) => {
255
- self . collect_block_ ( e, |id, statements, tail| Expr :: TryBlock {
256
- id,
257
- statements,
258
- tail,
259
- } )
260
- }
268
+ Some ( ast:: BlockModifier :: Try ( _) ) => self . collect_try_block ( e) ,
261
269
Some ( ast:: BlockModifier :: Unsafe ( _) ) => {
262
270
self . collect_block_ ( e, |id, statements, tail| Expr :: Unsafe {
263
271
id,
@@ -437,10 +445,7 @@ impl ExprCollector<'_> {
437
445
let expr = self . collect_expr_opt ( e. expr ( ) ) ;
438
446
self . alloc_expr ( Expr :: Await { expr } , syntax_ptr)
439
447
}
440
- ast:: Expr :: TryExpr ( e) => {
441
- let expr = self . collect_expr_opt ( e. expr ( ) ) ;
442
- self . alloc_expr ( Expr :: Try { expr } , syntax_ptr)
443
- }
448
+ ast:: Expr :: TryExpr ( e) => self . collect_try_operator ( syntax_ptr, e) ,
444
449
ast:: Expr :: CastExpr ( e) => {
445
450
let expr = self . collect_expr_opt ( e. expr ( ) ) ;
446
451
let type_ref = Interned :: new ( TypeRef :: from_ast_opt ( & self . ctx ( ) , e. ty ( ) ) ) ;
@@ -601,6 +606,126 @@ impl ExprCollector<'_> {
601
606
} )
602
607
}
603
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
+ /// ```
662
+ fn collect_try_operator ( & mut self , syntax_ptr : AstPtr < ast:: Expr > , e : ast:: TryExpr ) -> ExprId {
663
+ let ( try_branch, cf_continue, cf_break, try_from_residual) = ' if_chain: {
664
+ if let Some ( try_branch) = LangItem :: TryTraitBranch . path ( self . db , self . krate ) {
665
+ if let Some ( cf_continue) = LangItem :: ControlFlowContinue . path ( self . db , self . krate ) {
666
+ if let Some ( cf_break) = LangItem :: ControlFlowBreak . path ( self . db , self . krate ) {
667
+ if let Some ( try_from_residual) =
668
+ LangItem :: TryTraitFromResidual . path ( self . db , self . krate )
669
+ {
670
+ break ' if_chain ( try_branch, cf_continue, cf_break, try_from_residual) ;
671
+ }
672
+ }
673
+ }
674
+ }
675
+ // Some of the needed lang items are missing, so we can't desugar
676
+ return self . alloc_expr ( Expr :: Missing , syntax_ptr) ;
677
+ } ;
678
+ let operand = self . collect_expr_opt ( e. expr ( ) ) ;
679
+ let try_branch = self . alloc_expr ( Expr :: Path ( try_branch) , syntax_ptr. clone ( ) ) ;
680
+ let expr = self . alloc_expr (
681
+ Expr :: Call { callee : try_branch, args : Box :: new ( [ operand] ) , is_assignee_expr : false } ,
682
+ syntax_ptr. clone ( ) ,
683
+ ) ;
684
+ let continue_name = Name :: generate_new_name ( ) ;
685
+ let continue_binding =
686
+ self . alloc_binding ( continue_name. clone ( ) , BindingAnnotation :: Unannotated ) ;
687
+ let continue_bpat =
688
+ self . alloc_pat_desugared ( Pat :: Bind { id : continue_binding, subpat : None } ) ;
689
+ self . add_definition_to_binding ( continue_binding, continue_bpat) ;
690
+ let continue_arm = MatchArm {
691
+ pat : self . alloc_pat_desugared ( Pat :: TupleStruct {
692
+ path : Some ( Box :: new ( cf_continue) ) ,
693
+ args : Box :: new ( [ continue_bpat] ) ,
694
+ ellipsis : None ,
695
+ } ) ,
696
+ guard : None ,
697
+ expr : self . alloc_expr ( Expr :: Path ( Path :: from ( continue_name) ) , syntax_ptr. clone ( ) ) ,
698
+ } ;
699
+ let break_name = Name :: generate_new_name ( ) ;
700
+ let break_binding = self . alloc_binding ( break_name. clone ( ) , BindingAnnotation :: Unannotated ) ;
701
+ let break_bpat = self . alloc_pat_desugared ( Pat :: Bind { id : break_binding, subpat : None } ) ;
702
+ self . add_definition_to_binding ( break_binding, break_bpat) ;
703
+ let break_arm = MatchArm {
704
+ pat : self . alloc_pat_desugared ( Pat :: TupleStruct {
705
+ path : Some ( Box :: new ( cf_break) ) ,
706
+ args : Box :: new ( [ break_bpat] ) ,
707
+ ellipsis : None ,
708
+ } ) ,
709
+ guard : None ,
710
+ expr : {
711
+ let x = self . alloc_expr ( Expr :: Path ( Path :: from ( break_name) ) , syntax_ptr. clone ( ) ) ;
712
+ let callee = self . alloc_expr ( Expr :: Path ( try_from_residual) , syntax_ptr. clone ( ) ) ;
713
+ let result = self . alloc_expr (
714
+ Expr :: Call { callee, args : Box :: new ( [ x] ) , is_assignee_expr : false } ,
715
+ syntax_ptr. clone ( ) ,
716
+ ) ;
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
+ }
723
+ } ,
724
+ } ;
725
+ let arms = Box :: new ( [ continue_arm, break_arm] ) ;
726
+ self . alloc_expr ( Expr :: Match { expr, arms } , syntax_ptr)
727
+ }
728
+
604
729
fn collect_macro_call < F , T , U > (
605
730
& mut self ,
606
731
mcall : ast:: MacroCall ,
@@ -949,16 +1074,24 @@ impl ExprCollector<'_> {
949
1074
. collect ( ) ,
950
1075
}
951
1076
}
952
- ast:: Pat :: LiteralPat ( lit) => {
953
- if let Some ( ast_lit) = lit. literal ( ) {
954
- let expr = Expr :: Literal ( ast_lit. kind ( ) . into ( ) ) ;
1077
+ // FIXME: rustfmt removes this label if it is a block and not a loop
1078
+ ast:: Pat :: LiteralPat ( lit) => ' b: loop {
1079
+ break if let Some ( ast_lit) = lit. literal ( ) {
1080
+ let mut hir_lit: Literal = ast_lit. kind ( ) . into ( ) ;
1081
+ if lit. minus_token ( ) . is_some ( ) {
1082
+ let Some ( h) = hir_lit. negate ( ) else {
1083
+ break ' b Pat :: Missing ;
1084
+ } ;
1085
+ hir_lit = h;
1086
+ }
1087
+ let expr = Expr :: Literal ( hir_lit) ;
955
1088
let expr_ptr = AstPtr :: new ( & ast:: Expr :: Literal ( ast_lit) ) ;
956
1089
let expr_id = self . alloc_expr ( expr, expr_ptr) ;
957
1090
Pat :: Lit ( expr_id)
958
1091
} else {
959
1092
Pat :: Missing
960
- }
961
- }
1093
+ } ;
1094
+ } ,
962
1095
ast:: Pat :: RestPat ( _) => {
963
1096
// `RestPat` requires special handling and should not be mapped
964
1097
// to a Pat. Here we are using `Pat::Missing` as a fallback for
@@ -1063,11 +1196,11 @@ impl From<ast::LiteralKind> for Literal {
1063
1196
FloatTypeWrapper :: new ( lit. float_value ( ) . unwrap_or ( Default :: default ( ) ) ) ,
1064
1197
builtin,
1065
1198
)
1066
- } else if let builtin @ Some ( _) = lit. suffix ( ) . and_then ( BuiltinInt :: from_suffix) {
1067
- Literal :: Int ( lit. value ( ) . unwrap_or ( 0 ) as i128 , builtin)
1068
- } else {
1069
- let builtin = lit. suffix ( ) . and_then ( BuiltinUint :: from_suffix) ;
1199
+ } else if let builtin @ Some ( _) = lit. suffix ( ) . and_then ( BuiltinUint :: from_suffix) {
1070
1200
Literal :: Uint ( lit. value ( ) . unwrap_or ( 0 ) , builtin)
1201
+ } else {
1202
+ let builtin = lit. suffix ( ) . and_then ( BuiltinInt :: from_suffix) ;
1203
+ Literal :: Int ( lit. value ( ) . unwrap_or ( 0 ) as i128 , builtin)
1071
1204
}
1072
1205
}
1073
1206
LiteralKind :: FloatNumber ( lit) => {
0 commit comments