pubenumDesugaringKind { /// We desugar `if c { i } else { e }` to `match $ExprKind::Use(c) { true => i, _ => e }`. /// However, we do not want to blame `c` for unreachability but rather say that `i` /// is unreachable. This desugaring kind allows us to avoid blaming `c`. /// This also applies to `while` loops. CondTemporary, QuestionMark, TryBlock, /// Desugaring of an `impl Trait` in return type position /// to an `type Foo = impl Trait;` and replacing the /// `impl Trait` with `Foo`. OpaqueTy, Async, Await, ForLoop, LetElse, WhileLoop, }
先不考虑Async和Await,我们来一个个说其他的
CondTemporary
这部分都在src/expr.rs中
我们先来看一下它的调用位置,发现是在manage_let_cond这个函数中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond` // in a temporary block. fnmanage_let_cond(&mutself, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> { fnhas_let_expr<'hir>(expr: &'hir hir::Expr<'hir>) -> bool { match expr.kind { hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs), hir::ExprKind::Let(..) => true, _ => false, } } if has_let_expr(cond) { cond } else { let reason = DesugaringKind::CondTemporary; let span_block = self.mark_span_with_reason(reason, cond.span, None); self.expr_drop_temps(span_block, cond, AttrVec::new()) } }
/// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`. /// /// In terms of drop order, it has the same effect as wrapping `expr` in /// `{ let _t = $expr; _t }` but should provide better compile-time performance. /// /// The drop order can be important in e.g. `if expr { .. }`. pub(super) fnexpr_drop_temps( &mutself, span: Span, expr: &'hir hir::Expr<'hir>, attrs: AttrVec, ) -> &'hir hir::Expr<'hir> { self.arena.alloc(self.expr_drop_temps_mut(span, expr, attrs)) }
/// A `match` block, with a source that indicates whether or not it is /// the result of a desugaring, and if so, which kind. Match(&'hir Expr<'hir>, &'hir [Arm<'hir>], MatchSource)
根据注释的内容看上去分为三个部分
Try::branch()
非常直接的操作,直接lower传进来的sub_expr
1 2 3 4 5 6 7 8 9 10 11 12
// `Try::branch(<expr>)` let scrutinee = { // expand <expr> let sub_expr = self.lower_expr_mut(sub_expr);
let attr = { // `allow(unreachable_code)` let allow = { let allow_ident = Ident::new(sym::allow, self.lower_span(span)); let uc_ident = Ident::new(sym::unreachable_code, self.lower_span(span)); let uc_nested = attr::mk_nested_word_item(uc_ident); attr::mk_list_item(allow_ident, vec![uc_nested]) }; attr::mk_attr_outer(allow) }; let attrs = vec![attr];
TryBlock
在lower_expr_try_block中被用到
做了什么
这里的注释解释的比较清楚了,我就不再赘述
1 2 3
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`, /// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_output(()) }` /// and save the block id to use it as a break target for desugaring of the `?` operator.
// Final expression of the block (if present) or `()` with span at the end of block let (try_span, tail_expr) = ifletSome(expr) = block.expr.take() { ( this.mark_span_with_reason( DesugaringKind::TryBlock, expr.span, this.allow_try_trait.clone(), ), expr, ) } else { let try_span = this.mark_span_with_reason( DesugaringKind::TryBlock, this.sess.source_map().end_point(body.span), this.allow_try_trait.clone(), );
(try_span, this.expr_unit(try_span)) };
let ok_wrapped_span = this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None);
/// Context of `impl Trait` in code, which determines whether it is allowed in an HIR subtree, /// and if so, what meaning it has. #[derive(Debug)] enumImplTraitContext<'b, 'a> { /// Treat `impl Trait` as shorthand for a new universal generic parameter. /// Example: `fn foo(x: impl Debug)`, where `impl Debug` is conceptually /// equivalent to a fresh universal parameter like `fn foo<T: Debug>(x: T)`. /// /// Newly generated parameters should be inserted into the given `Vec`. Universal(&'bmutVec<hir::GenericParam<'a>>, LocalDefId),
/// Treat `impl Trait` as shorthand for a new opaque type. /// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually /// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`. /// ReturnPositionOpaqueTy { /// `DefId` for the parent function, used to look up necessary /// information later. fn_def_id: LocalDefId, /// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn, origin: hir::OpaqueTyOrigin, }, /// Impl trait in type aliases. TypeAliasesOpaqueTy { /// Set of lifetimes that this opaque type can capture, if it uses /// them. This includes lifetimes bound since we entered this context. /// For example: /// /// ``` /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>; /// ``` /// /// Here the inner opaque type captures `'a` because it uses it. It doesn't /// need to capture `'b` because it already inherits the lifetime /// parameter from `A`. // FIXME(impl_trait): but `required_region_bounds` will ICE later // anyway. capturable_lifetimes: &'bmut FxHashSet<hir::LifetimeName>, }, /// `impl Trait` is not accepted in this position. Disallowed(ImplTraitPosition), }
letmut expr = None; whilelet [s, tail @ ..] = ast_stmts { match s.kind { StmtKind::Local(ref local) => { let hir_id = self.lower_node_id(s.id); match &local.kind { LocalKind::InitElse(init, els) => { let e = self.lower_let_else(hir_id, local, init, els, tail); expr = Some(e); // remaining statements are in let-else expression break;
注意这里的break
来看一下InitElse
1 2 3 4 5 6
pubenumLocalKind { ... /// Local declaration with an initializer and an `else` clause. /// Example: `let Some(x) = y else { return };` InitElse(P<Expr>, P<Block>), }
/// Represents a `let <pat>[: <ty>] = <expr>` expression (not a Local), occurring in an `if-let` or /// `let-else`, evaluating to a boolean. Typically the pattern is refutable. /// /// In an if-let, imagine it as `if (let <pat> = <expr>) { ... }`; in a let-else, it is part of the /// desugaring to if-let. Only let-else supports the type annotation at present. #[derive(Debug, HashStable_Generic)] pubstructLet<'hir> { pub hir_id: HirId, pub span: Span, pub pat: &'hir Pat<'hir>, pub ty: Option<&'hir Ty<'hir>>, pub init: &'hir Expr<'hir>, }
pubenumExprKind<'hir> { ... /// A `let $pat = $expr` expression. /// /// These are not `Local` and only occur as expressions. /// The `let Some(x) = foo()` in `if let Some(x) = foo()` is an example of `Let(..)`. Let(&'hir Let<'hir>), ... }
then_expr
1 2 3 4 5
let then_expr = { let (stmts, expr) = self.lower_stmts(tail); let block = self.block_all(span, stmts, expr); self.arena.alloc(self.expr_block(block, AttrVec::new())) };
let else_expr = { let block = self.lower_block(els, false); self.arena.alloc(self.expr_block(block, AttrVec::new())) };
这里将传进来的els(InitElse的else block)lower到了一个block
实际做了什么转换
单个看起来可能不够直观,将三个部分组合起来的话这个逻辑就是
cond中创建了一个expr bind
true:将后面的stmts lower到一个新的block中(因此外面需要break)
false:将els的部分lower到block
false为什么不lower tail
像我一样不了解这里语法的情况会觉得false的行为很奇怪,false就不走tail了吗
于是我就写了这样的一个用例
1 2 3 4 5 6 7
#![feature(let_else)] fnmain() { let y:Option<i32> = None; letSome(x) = y else { println!("fail") }; println!("test"); }
直接报了编译错误,else中的内容是要强制从当前函数返回才行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
error[E0308]: `else` clause of `let...else` does not diverge --> src/main.rs:4:26 | 4 | letSome(x) = y else { | __________________________^ 5 | | println!("fail") }; | |__________________________^ expected `!`, found `()` | = note: expected type `!` foundtype `()` = help: try adding a diverging expression, such as `return` or `panic!(..)` = help: ...or use `match` instead of `let...else`
For more information about this error, try `rustc --explain E0308`. error: could not compile `playground` due to previous error
// We desugar: `'label: while $cond $body` into: // // ``` // 'label: loop { // if { let _t = $cond; _t } { // $body // } // else { // break; // } // } // ``` // // Wrap in a construct equivalent to `{ let _t = $cond; _t }` // to preserve drop semantics since `while $cond { ... }` does not // let temporaries live outside of `cond`.