Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 68 additions & 14 deletions crates/hir-def/src/expr_store/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand All @@ -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(),
Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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> }`
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i was missing this add_definition_to_binding before but I confess that I don't know what it actually does.

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:
Expand Down Expand Up @@ -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),
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions crates/hir-def/src/lang_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,7 @@ language_item_table! { LangItems =>
TryTraitFromOutput, sym::from_output, FunctionId;
TryTraitBranch, sym::branch, FunctionId;
TryTraitFromYeet, sym::from_yeet, FunctionId;
ResidualIntoTryType, sym::into_try_type, FunctionId;

PointerLike, sym::pointer_like, TraitId;

Expand Down
10 changes: 9 additions & 1 deletion crates/hir-ty/src/tests/simple.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2152,10 +2152,11 @@ async fn main() {
let z: core::ops::ControlFlow<(), _> = try { () };
let w = const { 92 };
let t = 'a: { 92 };
let u = try bikeshed core::ops::ControlFlow<(), _> { () };
}
"#,
expect![[r#"
16..193 '{ ...2 }; }': ()
16..256 '{ ...) }; }': ()
26..27 'x': i32
30..43 'unsafe { 92 }': i32
39..41 '92': i32
Expand All @@ -2176,6 +2177,13 @@ async fn main() {
176..177 't': i32
180..190 ''a: { 92 }': i32
186..188 '92': i32
200..201 'u': ControlFlow<(), ()>
204..253 'try bi...{ () }': ControlFlow<(), ()>
204..253 'try bi...{ () }': fn from_output<ControlFlow<(), ()>>(<ControlFlow<(), ()> as Try>::Output) -> ControlFlow<(), ()>
204..253 'try bi...{ () }': ControlFlow<(), ()>
204..253 'try bi...{ () }': ControlFlow<(), ()>
204..253 'try bi...{ () }': ControlFlow<(), ()>
249..251 '()': ()
"#]],
)
}
Expand Down
8 changes: 5 additions & 3 deletions crates/hir-ty/src/tests/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,14 +219,16 @@ fn test() {

#[test]
fn infer_try_block() {
// FIXME: We should test more cases, but it currently doesn't work, since
// our labeled block type inference is broken.
check_types(
r#"
//- minicore: try, option
//- minicore: try, option, result, from
fn test() {
let x: Option<_> = try { Some(2)?; };
//^ Option<()>
let homogeneous = try { Ok::<(), u32>(())?; "hi" };
//^^^^^^^^^^^ Result<&'? str, u32>
let heterogeneous = try bikeshed Result<_, u64> { 1 };
//^^^^^^^^^^^^^ Result<i32, u64>
}
"#,
);
Expand Down
2 changes: 1 addition & 1 deletion crates/ide-assists/src/handlers/convert_closure_to_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ pub(crate) fn convert_closure_to_fn(acc: &mut Assists, ctx: &AssistContext<'_>)
);
}

if block.try_token().is_none()
if block.try_block_modifier().is_none()
&& block.unsafe_token().is_none()
&& block.label().is_none()
&& block.const_token().is_none()
Expand Down
2 changes: 1 addition & 1 deletion crates/ide-assists/src/handlers/extract_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -859,7 +859,7 @@ impl FunctionBody {
ast::BlockExpr(block_expr) => {
let (constness, block) = match block_expr.modifier() {
Some(ast::BlockModifier::Const(_)) => (true, block_expr),
Some(ast::BlockModifier::Try(_)) => (false, block_expr),
Some(ast::BlockModifier::Try { .. }) => (false, block_expr),
Some(ast::BlockModifier::Label(label)) if label.lifetime().is_some() => (false, block_expr),
_ => continue,
};
Expand Down
6 changes: 3 additions & 3 deletions crates/ide-db/src/syntax_helpers/node_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ pub fn is_closure_or_blk_with_modif(expr: &ast::Expr) -> bool {
block_expr.modifier(),
Some(
ast::BlockModifier::Async(_)
| ast::BlockModifier::Try(_)
| ast::BlockModifier::Try { .. }
| ast::BlockModifier::Const(_)
)
)
Expand Down Expand Up @@ -148,7 +148,7 @@ pub fn walk_patterns_in_expr(start: &ast::Expr, cb: &mut dyn FnMut(ast::Pat)) {
block_expr.modifier(),
Some(
ast::BlockModifier::Async(_)
| ast::BlockModifier::Try(_)
| ast::BlockModifier::Try { .. }
| ast::BlockModifier::Const(_)
)
)
Expand Down Expand Up @@ -291,7 +291,7 @@ pub fn for_each_tail_expr(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
match b.modifier() {
Some(
ast::BlockModifier::Async(_)
| ast::BlockModifier::Try(_)
| ast::BlockModifier::Try { .. }
| ast::BlockModifier::Const(_),
) => return cb(expr),

Expand Down
6 changes: 3 additions & 3 deletions crates/ide/src/goto_definition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ pub(crate) fn find_fn_or_blocks(
ast::BlockExpr(blk) => {
match blk.modifier() {
Some(ast::BlockModifier::Async(_)) => blk.syntax().clone(),
Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => blk.syntax().clone(),
Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => blk.syntax().clone(),
_ => continue,
}
},
Expand Down Expand Up @@ -404,8 +404,8 @@ fn nav_for_exit_points(
let blk_in_file = InFile::new(file_id, blk.into());
Some(expr_to_nav(db, blk_in_file, Some(async_tok)))
},
Some(ast::BlockModifier::Try(_)) if token_kind != T![return] => {
let try_tok = blk.try_token()?.text_range();
Some(ast::BlockModifier::Try { .. }) if token_kind != T![return] => {
let try_tok = blk.try_block_modifier()?.try_token()?.text_range();
let blk_in_file = InFile::new(file_id, blk.into());
Some(expr_to_nav(db, blk_in_file, Some(try_tok)))
},
Expand Down
2 changes: 1 addition & 1 deletion crates/ide/src/highlight_related.rs
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ pub(crate) fn highlight_exit_points(
},
ast::BlockExpr(blk) => match blk.modifier() {
Some(ast::BlockModifier::Async(t)) => hl_exit_points(sema, Some(t), blk.into()),
Some(ast::BlockModifier::Try(t)) if token.kind() != T![return] => {
Some(ast::BlockModifier::Try { try_token: t, .. }) if token.kind() != T![return] => {
hl_exit_points(sema, Some(t), blk.into())
},
_ => continue,
Expand Down
2 changes: 1 addition & 1 deletion crates/ide/src/hover/render.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub(super) fn try_expr(
ast::Fn(fn_) => sema.to_def(&fn_)?.ret_type(sema.db),
ast::Item(__) => return None,
ast::ClosureExpr(closure) => sema.type_of_expr(&closure.body()?)?.original,
ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try(_)| ast::BlockModifier::Const(_))) {
ast::BlockExpr(block_expr) => if matches!(block_expr.modifier(), Some(ast::BlockModifier::Async(_) | ast::BlockModifier::Try { .. } | ast::BlockModifier::Const(_))) {
sema.type_of_expr(&block_expr.into())?.original
} else {
continue;
Expand Down
1 change: 1 addition & 0 deletions crates/intern/src/symbol/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,7 @@ define_symbols! {
Into,
into_future,
into_iter,
into_try_type,
IntoFuture,
IntoIter,
IntoIterator,
Expand Down
6 changes: 6 additions & 0 deletions crates/parser/src/grammar/expressions/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -976,11 +976,17 @@ fn break_expr(p: &mut Parser<'_>, r: Restrictions) -> CompletedMarker {
// test try_block_expr
// fn foo() {
// let _ = try {};
// let _ = try bikeshed T<U> {};
// }
fn try_block_expr(p: &mut Parser<'_>, m: Option<Marker>) -> CompletedMarker {
assert!(p.at(T![try]));
let m = m.unwrap_or_else(|| p.start());
let try_modifier = p.start();
p.bump(T![try]);
if p.eat_contextual_kw(T![bikeshed]) {
type_(p);
}
try_modifier.complete(p, TRY_BLOCK_MODIFIER);
if p.at(T!['{']) {
stmt_list(p);
} else {
Expand Down
8 changes: 8 additions & 0 deletions crates/parser/src/syntax_kind/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ pub enum SyntaxKind {
ATT_SYNTAX_KW,
AUTO_KW,
AWAIT_KW,
BIKESHED_KW,
BUILTIN_KW,
CLOBBER_ABI_KW,
DEFAULT_KW,
Expand Down Expand Up @@ -285,6 +286,7 @@ pub enum SyntaxKind {
STRUCT,
TOKEN_TREE,
TRAIT,
TRY_BLOCK_MODIFIER,
TRY_EXPR,
TUPLE_EXPR,
TUPLE_FIELD,
Expand Down Expand Up @@ -458,6 +460,7 @@ impl SyntaxKind {
| STRUCT
| TOKEN_TREE
| TRAIT
| TRY_BLOCK_MODIFIER
| TRY_EXPR
| TUPLE_EXPR
| TUPLE_FIELD
Expand Down Expand Up @@ -596,6 +599,7 @@ impl SyntaxKind {
ASM_KW => "asm",
ATT_SYNTAX_KW => "att_syntax",
AUTO_KW => "auto",
BIKESHED_KW => "bikeshed",
BUILTIN_KW => "builtin",
CLOBBER_ABI_KW => "clobber_abi",
DEFAULT_KW => "default",
Expand Down Expand Up @@ -698,6 +702,7 @@ impl SyntaxKind {
ASM_KW => true,
ATT_SYNTAX_KW => true,
AUTO_KW => true,
BIKESHED_KW => true,
BUILTIN_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
Expand Down Expand Up @@ -788,6 +793,7 @@ impl SyntaxKind {
ASM_KW => true,
ATT_SYNTAX_KW => true,
AUTO_KW => true,
BIKESHED_KW => true,
BUILTIN_KW => true,
CLOBBER_ABI_KW => true,
DEFAULT_KW => true,
Expand Down Expand Up @@ -941,6 +947,7 @@ impl SyntaxKind {
"asm" => ASM_KW,
"att_syntax" => ATT_SYNTAX_KW,
"auto" => AUTO_KW,
"bikeshed" => BIKESHED_KW,
"builtin" => BUILTIN_KW,
"clobber_abi" => CLOBBER_ABI_KW,
"default" => DEFAULT_KW,
Expand Down Expand Up @@ -1112,6 +1119,7 @@ macro_rules ! T_ {
[asm] => { $ crate :: SyntaxKind :: ASM_KW };
[att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW };
[auto] => { $ crate :: SyntaxKind :: AUTO_KW };
[bikeshed] => { $ crate :: SyntaxKind :: BIKESHED_KW };
[builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW };
[clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW };
[default] => { $ crate :: SyntaxKind :: DEFAULT_KW };
Expand Down
Loading