-
Notifications
You must be signed in to change notification settings - Fork 2k
Implement the new homogeneous & heterogeneous try blocks #21572
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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> }` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Technically in the MIR forcing a type is not the same as introducing a new local variable, but our MIR is horribly broken anyway and I believe in typeck the effect should be the same.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct of course, but I don't think we have a construct for type ascription in an expression (which is what rustc uses) :') |
||
| 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); | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think i was missing this |
||
| 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, | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.