33
44use std:: { mem, sync:: Arc } ;
55
6+ use base_db:: CrateId ;
67use either:: Either ;
78use hir_expand:: {
89 ast_id_map:: AstIdMap ,
@@ -18,7 +19,7 @@ use rustc_hash::FxHashMap;
1819use smallvec:: SmallVec ;
1920use syntax:: {
2021 ast:: {
21- self , ArrayExprKind , AstChildren , HasArgList , HasLoopBody , HasName , LiteralKind ,
22+ self , ArrayExprKind , AstChildren , BlockExpr , HasArgList , HasLoopBody , HasName , LiteralKind ,
2223 SlicePatComponents ,
2324 } ,
2425 AstNode , AstPtr , SyntaxNodePtr ,
@@ -36,6 +37,7 @@ use crate::{
3637 RecordFieldPat , RecordLitField , Statement ,
3738 } ,
3839 item_scope:: BuiltinShadowMode ,
40+ lang_item:: LangItem ,
3941 path:: { GenericArgs , Path } ,
4042 type_ref:: { Mutability , Rawness , TypeRef } ,
4143 AdtId , BlockId , BlockLoc , ModuleDefId , UnresolvedMacro ,
@@ -80,9 +82,11 @@ pub(super) fn lower(
8082 expander : Expander ,
8183 params : Option < ( ast:: ParamList , impl Iterator < Item = bool > ) > ,
8284 body : Option < ast:: Expr > ,
85+ krate : CrateId ,
8386) -> ( Body , BodySourceMap ) {
8487 ExprCollector {
8588 db,
89+ krate,
8690 source_map : BodySourceMap :: default ( ) ,
8791 ast_id_map : db. ast_id_map ( expander. current_file_id ) ,
8892 body : Body {
@@ -96,6 +100,7 @@ pub(super) fn lower(
96100 _c : Count :: new ( ) ,
97101 } ,
98102 expander,
103+ current_try_block : None ,
99104 is_lowering_assignee_expr : false ,
100105 is_lowering_generator : false ,
101106 }
@@ -107,7 +112,9 @@ struct ExprCollector<'a> {
107112 expander : Expander ,
108113 ast_id_map : Arc < AstIdMap > ,
109114 body : Body ,
115+ krate : CrateId ,
110116 source_map : BodySourceMap ,
117+ current_try_block : Option < LabelId > ,
111118 is_lowering_assignee_expr : bool ,
112119 is_lowering_generator : bool ,
113120}
@@ -176,8 +183,7 @@ impl ExprCollector<'_> {
176183 self . source_map . expr_map . insert ( src, id) ;
177184 id
178185 }
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.
181187 fn alloc_expr_desugared ( & mut self , expr : Expr ) -> ExprId {
182188 self . body . exprs . alloc ( expr)
183189 }
@@ -199,6 +205,10 @@ impl ExprCollector<'_> {
199205 self . source_map . pat_map . insert ( src, id) ;
200206 id
201207 }
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+ }
202212 fn missing_pat ( & mut self ) -> PatId {
203213 self . body . pats . alloc ( Pat :: Missing )
204214 }
@@ -214,6 +224,10 @@ impl ExprCollector<'_> {
214224 self . source_map . label_map . insert ( src, id) ;
215225 id
216226 }
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+ }
217231 fn make_label ( & mut self , label : Label , src : LabelSource ) -> LabelId {
218232 let id = self . body . labels . alloc ( label) ;
219233 self . source_map . label_map_back . insert ( id, src) ;
@@ -251,13 +265,7 @@ impl ExprCollector<'_> {
251265 self . alloc_expr ( Expr :: Let { pat, expr } , syntax_ptr)
252266 }
253267 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) ,
261269 Some ( ast:: BlockModifier :: Unsafe ( _) ) => {
262270 self . collect_block_ ( e, |id, statements, tail| Expr :: Unsafe {
263271 id,
@@ -437,10 +445,7 @@ impl ExprCollector<'_> {
437445 let expr = self . collect_expr_opt ( e. expr ( ) ) ;
438446 self . alloc_expr ( Expr :: Await { expr } , syntax_ptr)
439447 }
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) ,
444449 ast:: Expr :: CastExpr ( e) => {
445450 let expr = self . collect_expr_opt ( e. expr ( ) ) ;
446451 let type_ref = Interned :: new ( TypeRef :: from_ast_opt ( & self . ctx ( ) , e. ty ( ) ) ) ;
@@ -601,6 +606,126 @@ impl ExprCollector<'_> {
601606 } )
602607 }
603608
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+
604729 fn collect_macro_call < F , T , U > (
605730 & mut self ,
606731 mcall : ast:: MacroCall ,
@@ -949,16 +1074,24 @@ impl ExprCollector<'_> {
9491074 . collect ( ) ,
9501075 }
9511076 }
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) ;
9551088 let expr_ptr = AstPtr :: new ( & ast:: Expr :: Literal ( ast_lit) ) ;
9561089 let expr_id = self . alloc_expr ( expr, expr_ptr) ;
9571090 Pat :: Lit ( expr_id)
9581091 } else {
9591092 Pat :: Missing
960- }
961- }
1093+ } ;
1094+ } ,
9621095 ast:: Pat :: RestPat ( _) => {
9631096 // `RestPat` requires special handling and should not be mapped
9641097 // to a Pat. Here we are using `Pat::Missing` as a fallback for
@@ -1063,11 +1196,11 @@ impl From<ast::LiteralKind> for Literal {
10631196 FloatTypeWrapper :: new ( lit. float_value ( ) . unwrap_or ( Default :: default ( ) ) ) ,
10641197 builtin,
10651198 )
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) {
10701200 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)
10711204 }
10721205 }
10731206 LiteralKind :: FloatNumber ( lit) => {
0 commit comments