Rollup merge of #141876 - compiler-errors:missing-let-ty, r=SparrowLii

Don't declare variables in `ExprKind::Let` in invalid positions

Handle `let` expressions in invalid positions specially during resolve in order to avoid making destructuring-assignment expressions that reference (invalid) variables that have not yet been delcared yet.

See further explanation in test and comment in the source.

Fixes rust-lang/rust#141844
This commit is contained in:
Matthias Krüger 2025-06-03 07:03:44 +02:00 committed by GitHub
commit 69ebe39cea
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 3 deletions

View file

@ -2370,6 +2370,10 @@ impl Expr<'_> {
// Lang item paths cannot currently be local variables or statics.
ExprKind::Path(QPath::LangItem(..)) => false,
// Suppress errors for bad expressions.
ExprKind::Err(_guar)
| ExprKind::Let(&LetExpr { recovered: ast::Recovered::Yes(_guar), .. }) => true,
// Partially qualified paths in expressions can only legally
// refer to associated items which are always rvalues.
ExprKind::Path(QPath::TypeRelative(..))
@ -2401,8 +2405,7 @@ impl Expr<'_> {
| ExprKind::Binary(..)
| ExprKind::Yield(..)
| ExprKind::Cast(..)
| ExprKind::DropTemps(..)
| ExprKind::Err(_) => false,
| ExprKind::DropTemps(..) => false,
}
}

View file

@ -4898,11 +4898,28 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.resolve_expr(e, Some(expr));
}
ExprKind::Let(ref pat, ref scrutinee, _, _) => {
ExprKind::Let(ref pat, ref scrutinee, _, Recovered::No) => {
self.visit_expr(scrutinee);
self.resolve_pattern_top(pat, PatternSource::Let);
}
ExprKind::Let(ref pat, ref scrutinee, _, Recovered::Yes(_)) => {
self.visit_expr(scrutinee);
// This is basically a tweaked, inlined `resolve_pattern_top`.
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
self.resolve_pattern(pat, PatternSource::Let, &mut bindings);
// We still collect the bindings in this `let` expression which is in
// an invalid position (and therefore shouldn't declare variables into
// its parent scope). To avoid unnecessary errors though, we do just
// reassign the resolutions to `Res::Err`.
for (_, bindings) in &mut bindings {
for (_, binding) in bindings {
*binding = Res::Err;
}
}
self.apply_pattern_bindings(bindings);
}
ExprKind::If(ref cond, ref then, ref opt_else) => {
self.with_rib(ValueNS, RibKind::Normal, |this| {
let old = this.diag_metadata.in_if_condition.replace(cond);

View file

@ -0,0 +1,13 @@
// Regression test for <https://github.com/rust-lang/rust/issues/141844>.
fn main() {
// The following expression gets desugared into something like:
// ```
// let (lhs,) = x; (let x = 1) = lhs;
// ```
// This used to ICE since we haven't yet declared the type for `x` when
// checking the first desugared statement, whose RHS resolved to `x` since
// in the AST, the `let` expression was visited first.
(let x = 1,) = x;
//~^ ERROR expected expression, found `let` statement
}

View file

@ -0,0 +1,10 @@
error: expected expression, found `let` statement
--> $DIR/bad-let-in-destructure.rs:11:4
|
LL | (let x = 1,) = x;
| ^^^
|
= note: only supported directly in conditions of `if` and `while` expressions
error: aborting due to 1 previous error