Auto merge of #89841 - cormacrelf:let-else-typed, r=nagisa
Implement let-else type annotations natively Tracking issue: #87335 Fixes #89688, fixes #89807, edit: fixes #89960 as well As explained in https://github.com/rust-lang/rust/issues/89688#issuecomment-940405082, the previous desugaring moved the let-else scrutinee into a dummy variable, which meant if you wanted to refer to it again in the else block, it had moved. This introduces a new hir type, ~~`hir::LetExpr`~~ `hir::Let`, which takes over all the fields of `hir::ExprKind::Let(...)` and adds an optional type annotation. The `hir::Let` is then treated like a `hir::Local` when type checking a function body, specifically: * `GatherLocalsVisitor` overrides a new `Visitor::visit_let_expr` and does pretty much exactly what it does for `visit_local`, assigning a local type to the `hir::Let` ~~(they could be deduplicated but they are right next to each other, so at least we know they're the same)~~ * It reuses the code in `check_decl_local` to typecheck the `hir::Let`, simply returning 'bool' for the expression type after doing that. * ~~`FnCtxt::check_expr_let` passes this local type in to `demand_scrutinee_type`, and then imitates check_decl_local's pattern checking~~ * ~~`demand_scrutinee_type` (the blindest change for me, please give this extra scrutiny) uses this local type instead of of creating a new one~~ * ~~Just realised the `check_expr_with_needs` was passing NoExpectation further down, need to pass the type there too. And apparently this Expectation API already exists.~~ Some other misc notes: * ~~Is the clippy code supposed to be autoformatted? I tried not to give huge diffs but maybe some rustfmt changes simply haven't hit it yet.~~ * in `rustc_ast_lowering/src/block.rs`, I noticed some existing `self.alias_attrs()` calls in `LoweringContext::lower_stmts` seem to be copying attributes from the lowered locals/etc to the statements. Is that right? I'm new at this, I don't know.
This commit is contained in:
commit
dde825db46
46 changed files with 900 additions and 142 deletions
|
@ -20,6 +20,7 @@ macro_rules! arena_types {
|
|||
[] generic_bound: rustc_hir::GenericBound<'tcx>,
|
||||
[] generic_param: rustc_hir::GenericParam<'tcx>,
|
||||
[] expr: rustc_hir::Expr<'tcx>,
|
||||
[] let_expr: rustc_hir::Let<'tcx>,
|
||||
[] expr_field: rustc_hir::ExprField<'tcx>,
|
||||
[] pat_field: rustc_hir::PatField<'tcx>,
|
||||
[] fn_decl: rustc_hir::FnDecl<'tcx>,
|
||||
|
|
|
@ -1160,10 +1160,24 @@ pub struct Arm<'hir> {
|
|||
pub body: &'hir Expr<'hir>,
|
||||
}
|
||||
|
||||
/// 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)]
|
||||
pub struct Let<'hir> {
|
||||
pub hir_id: HirId,
|
||||
pub span: Span,
|
||||
pub pat: &'hir Pat<'hir>,
|
||||
pub ty: Option<&'hir Ty<'hir>>,
|
||||
pub init: &'hir Expr<'hir>,
|
||||
}
|
||||
|
||||
#[derive(Debug, HashStable_Generic)]
|
||||
pub enum Guard<'hir> {
|
||||
If(&'hir Expr<'hir>),
|
||||
// FIXME use ExprKind::Let for this.
|
||||
// FIXME use hir::Let for this.
|
||||
IfLet(&'hir Pat<'hir>, &'hir Expr<'hir>),
|
||||
}
|
||||
|
||||
|
@ -1680,7 +1694,7 @@ pub enum ExprKind<'hir> {
|
|||
///
|
||||
/// 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 Pat<'hir>, &'hir Expr<'hir>, Span),
|
||||
Let(&'hir Let<'hir>),
|
||||
/// An `if` block, with an optional else block.
|
||||
///
|
||||
/// I.e., `if <expr> { <expr> } else { <expr> }`.
|
||||
|
|
|
@ -389,6 +389,9 @@ pub trait Visitor<'v>: Sized {
|
|||
fn visit_expr(&mut self, ex: &'v Expr<'v>) {
|
||||
walk_expr(self, ex)
|
||||
}
|
||||
fn visit_let_expr(&mut self, lex: &'v Let<'v>) {
|
||||
walk_let_expr(self, lex)
|
||||
}
|
||||
fn visit_ty(&mut self, t: &'v Ty<'v>) {
|
||||
walk_ty(self, t)
|
||||
}
|
||||
|
@ -1126,6 +1129,14 @@ pub fn walk_anon_const<'v, V: Visitor<'v>>(visitor: &mut V, constant: &'v AnonCo
|
|||
visitor.visit_nested_body(constant.body);
|
||||
}
|
||||
|
||||
pub fn walk_let_expr<'v, V: Visitor<'v>>(visitor: &mut V, let_expr: &'v Let<'v>) {
|
||||
// match the visit order in walk_local
|
||||
visitor.visit_expr(let_expr.init);
|
||||
visitor.visit_id(let_expr.hir_id);
|
||||
visitor.visit_pat(let_expr.pat);
|
||||
walk_list!(visitor, visit_ty, let_expr.ty);
|
||||
}
|
||||
|
||||
pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>) {
|
||||
visitor.visit_id(expression.hir_id);
|
||||
match expression.kind {
|
||||
|
@ -1172,10 +1183,7 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
|
|||
ExprKind::DropTemps(ref subexpression) => {
|
||||
visitor.visit_expr(subexpression);
|
||||
}
|
||||
ExprKind::Let(ref pat, ref expr, _) => {
|
||||
visitor.visit_expr(expr);
|
||||
visitor.visit_pat(pat);
|
||||
}
|
||||
ExprKind::Let(ref let_expr) => visitor.visit_let_expr(let_expr),
|
||||
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||
visitor.visit_expr(cond);
|
||||
visitor.visit_expr(then);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue