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.rs82
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,