Merge pull request #21572 from goffrie/try
Implement the new homogeneous & heterogeneous try blocks
This commit is contained in:
commit
ed58edc701
21 changed files with 238 additions and 37 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 '()': ()
|
||||
"#]],
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
}
|
||||
"#,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(_)
|
||||
)
|
||||
)
|
||||
|
|
@ -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(_)
|
||||
)
|
||||
)
|
||||
|
|
@ -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),
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
},
|
||||
|
|
@ -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)))
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -285,6 +285,7 @@ define_symbols! {
|
|||
Into,
|
||||
into_future,
|
||||
into_iter,
|
||||
into_try_type,
|
||||
IntoFuture,
|
||||
IntoIter,
|
||||
IntoIterator,
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@ pub enum SyntaxKind {
|
|||
ATT_SYNTAX_KW,
|
||||
AUTO_KW,
|
||||
AWAIT_KW,
|
||||
BIKESHED_KW,
|
||||
BUILTIN_KW,
|
||||
CLOBBER_ABI_KW,
|
||||
DEFAULT_KW,
|
||||
|
|
@ -285,6 +286,7 @@ pub enum SyntaxKind {
|
|||
STRUCT,
|
||||
TOKEN_TREE,
|
||||
TRAIT,
|
||||
TRY_BLOCK_MODIFIER,
|
||||
TRY_EXPR,
|
||||
TUPLE_EXPR,
|
||||
TUPLE_FIELD,
|
||||
|
|
@ -458,6 +460,7 @@ impl SyntaxKind {
|
|||
| STRUCT
|
||||
| TOKEN_TREE
|
||||
| TRAIT
|
||||
| TRY_BLOCK_MODIFIER
|
||||
| TRY_EXPR
|
||||
| TUPLE_EXPR
|
||||
| TUPLE_FIELD
|
||||
|
|
@ -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",
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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,
|
||||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ SOURCE_FILE
|
|||
WHITESPACE " "
|
||||
EXPR_STMT
|
||||
BLOCK_EXPR
|
||||
TRY_KW "try"
|
||||
TRY_BLOCK_MODIFIER
|
||||
TRY_KW "try"
|
||||
WHITESPACE " "
|
||||
LITERAL
|
||||
INT_NUMBER "92"
|
||||
|
|
|
|||
|
|
@ -21,7 +21,42 @@ SOURCE_FILE
|
|||
EQ "="
|
||||
WHITESPACE " "
|
||||
BLOCK_EXPR
|
||||
TRY_KW "try"
|
||||
TRY_BLOCK_MODIFIER
|
||||
TRY_KW "try"
|
||||
WHITESPACE " "
|
||||
STMT_LIST
|
||||
L_CURLY "{"
|
||||
R_CURLY "}"
|
||||
SEMICOLON ";"
|
||||
WHITESPACE "\n "
|
||||
LET_STMT
|
||||
LET_KW "let"
|
||||
WHITESPACE " "
|
||||
WILDCARD_PAT
|
||||
UNDERSCORE "_"
|
||||
WHITESPACE " "
|
||||
EQ "="
|
||||
WHITESPACE " "
|
||||
BLOCK_EXPR
|
||||
TRY_BLOCK_MODIFIER
|
||||
TRY_KW "try"
|
||||
WHITESPACE " "
|
||||
BIKESHED_KW "bikeshed"
|
||||
WHITESPACE " "
|
||||
PATH_TYPE
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "T"
|
||||
GENERIC_ARG_LIST
|
||||
L_ANGLE "<"
|
||||
TYPE_ARG
|
||||
PATH_TYPE
|
||||
PATH
|
||||
PATH_SEGMENT
|
||||
NAME_REF
|
||||
IDENT "U"
|
||||
R_ANGLE ">"
|
||||
WHITESPACE " "
|
||||
STMT_LIST
|
||||
L_CURLY "{"
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
fn foo() {
|
||||
let _ = try {};
|
||||
let _ = try bikeshed T<U> {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -472,8 +472,11 @@ RefExpr =
|
|||
TryExpr =
|
||||
Attr* Expr '?'
|
||||
|
||||
TryBlockModifier =
|
||||
'try' ('bikeshed' Type)?
|
||||
|
||||
BlockExpr =
|
||||
Attr* Label? ('try' | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList
|
||||
Attr* Label? (TryBlockModifier | 'unsafe' | ('async' 'move'?) | ('gen' 'move'?) | 'const') StmtList
|
||||
|
||||
PrefixExpr =
|
||||
Attr* op:('-' | '!' | '*') Expr
|
||||
|
|
|
|||
|
|
@ -375,7 +375,11 @@ impl ast::Literal {
|
|||
pub enum BlockModifier {
|
||||
Async(SyntaxToken),
|
||||
Unsafe(SyntaxToken),
|
||||
Try(SyntaxToken),
|
||||
Try {
|
||||
try_token: SyntaxToken,
|
||||
bikeshed_token: Option<SyntaxToken>,
|
||||
result_type: Option<ast::Type>,
|
||||
},
|
||||
Const(SyntaxToken),
|
||||
AsyncGen(SyntaxToken),
|
||||
Gen(SyntaxToken),
|
||||
|
|
@ -394,7 +398,13 @@ impl ast::BlockExpr {
|
|||
})
|
||||
.or_else(|| self.async_token().map(BlockModifier::Async))
|
||||
.or_else(|| self.unsafe_token().map(BlockModifier::Unsafe))
|
||||
.or_else(|| self.try_token().map(BlockModifier::Try))
|
||||
.or_else(|| {
|
||||
let modifier = self.try_block_modifier()?;
|
||||
let try_token = modifier.try_token()?;
|
||||
let bikeshed_token = modifier.bikeshed_token();
|
||||
let result_type = modifier.ty();
|
||||
Some(BlockModifier::Try { try_token, bikeshed_token, result_type })
|
||||
})
|
||||
.or_else(|| self.const_token().map(BlockModifier::Const))
|
||||
.or_else(|| self.label().map(BlockModifier::Label))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -323,6 +323,8 @@ impl BlockExpr {
|
|||
#[inline]
|
||||
pub fn stmt_list(&self) -> Option<StmtList> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn try_block_modifier(&self) -> Option<TryBlockModifier> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn async_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![async]) }
|
||||
#[inline]
|
||||
pub fn const_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![const]) }
|
||||
|
|
@ -331,8 +333,6 @@ impl BlockExpr {
|
|||
#[inline]
|
||||
pub fn move_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![move]) }
|
||||
#[inline]
|
||||
pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
|
||||
#[inline]
|
||||
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
|
||||
}
|
||||
pub struct BoxPat {
|
||||
|
|
@ -1630,6 +1630,19 @@ impl Trait {
|
|||
#[inline]
|
||||
pub fn unsafe_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![unsafe]) }
|
||||
}
|
||||
pub struct TryBlockModifier {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
impl TryBlockModifier {
|
||||
#[inline]
|
||||
pub fn ty(&self) -> Option<Type> { support::child(&self.syntax) }
|
||||
#[inline]
|
||||
pub fn bikeshed_token(&self) -> Option<SyntaxToken> {
|
||||
support::token(&self.syntax, T![bikeshed])
|
||||
}
|
||||
#[inline]
|
||||
pub fn try_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![try]) }
|
||||
}
|
||||
pub struct TryExpr {
|
||||
pub(crate) syntax: SyntaxNode,
|
||||
}
|
||||
|
|
@ -6320,6 +6333,38 @@ impl fmt::Debug for Trait {
|
|||
f.debug_struct("Trait").field("syntax", &self.syntax).finish()
|
||||
}
|
||||
}
|
||||
impl AstNode for TryBlockModifier {
|
||||
#[inline]
|
||||
fn kind() -> SyntaxKind
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
TRY_BLOCK_MODIFIER
|
||||
}
|
||||
#[inline]
|
||||
fn can_cast(kind: SyntaxKind) -> bool { kind == TRY_BLOCK_MODIFIER }
|
||||
#[inline]
|
||||
fn cast(syntax: SyntaxNode) -> Option<Self> {
|
||||
if Self::can_cast(syntax.kind()) { Some(Self { syntax }) } else { None }
|
||||
}
|
||||
#[inline]
|
||||
fn syntax(&self) -> &SyntaxNode { &self.syntax }
|
||||
}
|
||||
impl hash::Hash for TryBlockModifier {
|
||||
fn hash<H: hash::Hasher>(&self, state: &mut H) { self.syntax.hash(state); }
|
||||
}
|
||||
impl Eq for TryBlockModifier {}
|
||||
impl PartialEq for TryBlockModifier {
|
||||
fn eq(&self, other: &Self) -> bool { self.syntax == other.syntax }
|
||||
}
|
||||
impl Clone for TryBlockModifier {
|
||||
fn clone(&self) -> Self { Self { syntax: self.syntax.clone() } }
|
||||
}
|
||||
impl fmt::Debug for TryBlockModifier {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TryBlockModifier").field("syntax", &self.syntax).finish()
|
||||
}
|
||||
}
|
||||
impl AstNode for TryExpr {
|
||||
#[inline]
|
||||
fn kind() -> SyntaxKind
|
||||
|
|
@ -9979,6 +10024,11 @@ impl std::fmt::Display for Trait {
|
|||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for TryBlockModifier {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
}
|
||||
}
|
||||
impl std::fmt::Display for TryExpr {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
std::fmt::Display::fmt(self.syntax(), f)
|
||||
|
|
|
|||
|
|
@ -953,6 +953,9 @@ pub mod ops {
|
|||
#[lang = "from_residual"]
|
||||
fn from_residual(residual: R) -> Self;
|
||||
}
|
||||
pub const trait Residual<O>: Sized {
|
||||
type TryType: [const] Try<Output = O, Residual = Self>;
|
||||
}
|
||||
#[lang = "Try"]
|
||||
pub trait Try: FromResidual<Self::Residual> {
|
||||
type Output;
|
||||
|
|
@ -962,6 +965,12 @@ pub mod ops {
|
|||
#[lang = "branch"]
|
||||
fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
|
||||
}
|
||||
#[lang = "into_try_type"]
|
||||
pub const fn residual_into_try_type<R: [const] Residual<O>, O>(
|
||||
r: R,
|
||||
) -> <R as Residual<O>>::TryType {
|
||||
FromResidual::from_residual(r)
|
||||
}
|
||||
|
||||
impl<B, C> Try for ControlFlow<B, C> {
|
||||
type Output = C;
|
||||
|
|
@ -985,6 +994,10 @@ pub mod ops {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, C> Residual<C> for ControlFlow<B, Infallible> {
|
||||
type TryType = ControlFlow<B, C>;
|
||||
}
|
||||
// region:option
|
||||
impl<T> Try for Option<T> {
|
||||
type Output = T;
|
||||
|
|
@ -1008,6 +1021,10 @@ pub mod ops {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> const Residual<T> for Option<Infallible> {
|
||||
type TryType = Option<T>;
|
||||
}
|
||||
// endregion:option
|
||||
// region:result
|
||||
// region:from
|
||||
|
|
@ -1037,10 +1054,14 @@ pub mod ops {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E> const Residual<T> for Result<Infallible, E> {
|
||||
type TryType = Result<T, E>;
|
||||
}
|
||||
// endregion:from
|
||||
// endregion:result
|
||||
}
|
||||
pub use self::try_::{ControlFlow, FromResidual, Try};
|
||||
pub use self::try_::{ControlFlow, FromResidual, Residual, Try};
|
||||
// endregion:try
|
||||
|
||||
// region:add
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ const RESERVED: &[&str] = &[
|
|||
// keywords that are keywords only in specific parse contexts
|
||||
#[doc(alias = "WEAK_KEYWORDS")]
|
||||
const CONTEXTUAL_KEYWORDS: &[&str] =
|
||||
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe"];
|
||||
&["macro_rules", "union", "default", "raw", "dyn", "auto", "yeet", "safe", "bikeshed"];
|
||||
// keywords we use for special macro expansions
|
||||
const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
|
||||
"asm",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue