Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/expr_store/lower.rs')
| -rw-r--r-- | crates/hir-def/src/expr_store/lower.rs | 82 |
1 files changed, 68 insertions, 14 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs index 4fbf6d9517..701586c258 100644 --- a/crates/hir-def/src/expr_store/lower.rs +++ b/crates/hir-def/src/expr_store/lower.rs @@ -426,7 +426,7 @@ pub struct ExprCollector<'db> { /// and we need to find the current definition. So we track the number of definitions we saw. current_block_legacy_macro_defs_count: FxHashMap<Name, usize>, - current_try_block_label: Option<LabelId>, + current_try_block: Option<TryBlock>, label_ribs: Vec<LabelRib>, unowned_bindings: Vec<BindingId>, @@ -472,6 +472,13 @@ enum Awaitable { No(&'static str), } +enum TryBlock { + // `try { ... }` + Homogeneous { label: LabelId }, + // `try bikeshed Ty { ... }` + Heterogeneous { label: LabelId }, +} + #[derive(Debug, Default)] struct BindingList { map: FxHashMap<(Name, HygieneId), BindingId>, @@ -532,7 +539,7 @@ impl<'db> ExprCollector<'db> { lang_items: OnceCell::new(), store: ExpressionStoreBuilder::default(), expander, - current_try_block_label: None, + current_try_block: None, is_lowering_coroutine: false, label_ribs: Vec::new(), unowned_bindings: Vec::new(), @@ -1069,7 +1076,9 @@ impl<'db> ExprCollector<'db> { self.alloc_expr(Expr::Let { pat, expr }, syntax_ptr) } ast::Expr::BlockExpr(e) => match e.modifier() { - Some(ast::BlockModifier::Try(_)) => self.desugar_try_block(e), + Some(ast::BlockModifier::Try { try_token: _, bikeshed_token: _, result_type }) => { + self.desugar_try_block(e, result_type) + } Some(ast::BlockModifier::Unsafe(_)) => { self.collect_block_(e, |id, statements, tail| Expr::Unsafe { id, @@ -1344,7 +1353,7 @@ impl<'db> ExprCollector<'db> { .map(|it| this.lower_type_ref_disallow_impl_trait(it)); let prev_is_lowering_coroutine = mem::take(&mut this.is_lowering_coroutine); - let prev_try_block_label = this.current_try_block_label.take(); + let prev_try_block = this.current_try_block.take(); let awaitable = if e.async_token().is_some() { Awaitable::Yes @@ -1369,7 +1378,7 @@ impl<'db> ExprCollector<'db> { let capture_by = if e.move_token().is_some() { CaptureBy::Value } else { CaptureBy::Ref }; this.is_lowering_coroutine = prev_is_lowering_coroutine; - this.current_try_block_label = prev_try_block_label; + this.current_try_block = prev_try_block; this.alloc_expr( Expr::Closure { args: args.into(), @@ -1686,11 +1695,15 @@ impl<'db> ExprCollector<'db> { /// Desugar `try { <stmts>; <expr> }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(<expr>) }`, /// `try { <stmts>; }` into `'<new_label>: { <stmts>; ::std::ops::Try::from_output(()) }` /// and save the `<new_label>` to use it as a break target for desugaring of the `?` operator. - fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { + fn desugar_try_block(&mut self, e: BlockExpr, result_type: Option<ast::Type>) -> ExprId { let try_from_output = self.lang_path(self.lang_items().TryTraitFromOutput); let label = self.generate_new_name(); let label = self.alloc_label_desugared(Label { name: label }, AstPtr::new(&e).wrap_right()); - let old_label = self.current_try_block_label.replace(label); + let try_block_info = match result_type { + Some(_) => TryBlock::Heterogeneous { label }, + None => TryBlock::Homogeneous { label }, + }; + let old_try_block = self.current_try_block.replace(try_block_info); let ptr = AstPtr::new(&e).upcast(); let (btail, expr_id) = self.with_labeled_rib(label, HygieneId::ROOT, |this| { @@ -1720,8 +1733,38 @@ impl<'db> ExprCollector<'db> { unreachable!("block was lowered to non-block"); }; *tail = Some(next_tail); - self.current_try_block_label = old_label; - expr_id + self.current_try_block = old_try_block; + match result_type { + Some(ty) => { + // `{ let <name>: <ty> = <expr>; <name> }` + let name = self.generate_new_name(); + let type_ref = self.lower_type_ref_disallow_impl_trait(ty); + let binding = self.alloc_binding( + name.clone(), + BindingAnnotation::Unannotated, + HygieneId::ROOT, + ); + let pat = self.alloc_pat_desugared(Pat::Bind { id: binding, subpat: None }); + self.add_definition_to_binding(binding, pat); + let tail_expr = + self.alloc_expr_desugared_with_ptr(Expr::Path(Path::from(name)), ptr); + self.alloc_expr_desugared_with_ptr( + Expr::Block { + id: None, + statements: Box::new([Statement::Let { + pat, + type_ref: Some(type_ref), + initializer: Some(expr_id), + else_branch: None, + }]), + tail: Some(tail_expr), + label: None, + }, + ptr, + ) + } + None => expr_id, + } } /// Desugar `ast::WhileExpr` from: `[opt_ident]: while <cond> <body>` into: @@ -1863,6 +1906,8 @@ impl<'db> ExprCollector<'db> { /// ControlFlow::Continue(val) => val, /// ControlFlow::Break(residual) => /// // If there is an enclosing `try {...}`: + /// break 'catch_target Residual::into_try_type(residual), + /// // If there is an enclosing `try bikeshed Ty {...}`: /// break 'catch_target Try::from_residual(residual), /// // Otherwise: /// return Try::from_residual(residual), @@ -1873,7 +1918,6 @@ impl<'db> ExprCollector<'db> { let try_branch = self.lang_path(lang_items.TryTraitBranch); let cf_continue = self.lang_path(lang_items.ControlFlowContinue); let cf_break = self.lang_path(lang_items.ControlFlowBreak); - let try_from_residual = self.lang_path(lang_items.TryTraitFromResidual); let operand = self.collect_expr_opt(e.expr()); let try_branch = self.alloc_expr(try_branch.map_or(Expr::Missing, Expr::Path), syntax_ptr); let expr = self @@ -1910,13 +1954,23 @@ impl<'db> ExprCollector<'db> { guard: None, expr: { let it = self.alloc_expr(Expr::Path(Path::from(break_name)), syntax_ptr); - let callee = self - .alloc_expr(try_from_residual.map_or(Expr::Missing, Expr::Path), syntax_ptr); + let convert_fn = match self.current_try_block { + Some(TryBlock::Homogeneous { .. }) => { + self.lang_path(lang_items.ResidualIntoTryType) + } + Some(TryBlock::Heterogeneous { .. }) | None => { + self.lang_path(lang_items.TryTraitFromResidual) + } + }; + let callee = + self.alloc_expr(convert_fn.map_or(Expr::Missing, Expr::Path), syntax_ptr); let result = self.alloc_expr(Expr::Call { callee, args: Box::new([it]) }, syntax_ptr); self.alloc_expr( - match self.current_try_block_label { - Some(label) => Expr::Break { expr: Some(result), label: Some(label) }, + match self.current_try_block { + Some( + TryBlock::Heterogeneous { label } | TryBlock::Homogeneous { label }, + ) => Expr::Break { expr: Some(result), label: Some(label) }, None => Expr::Return { expr: Some(result) }, }, syntax_ptr, |