rust-lang/rust · #164076

Fix ICE when move expressions reference out-of-scope local bindings

TaKO8Ki · merged Oct 15, 202612 files · 451 + / 22 −
compiler/rustc_resolve/src/diagnostics/impls.rs11 + / 0 −
@@ -1436,6 +1436,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {             ResolutionError::BindingInNeverPattern => {                 self.dcx().create_err(diagnostics::BindingInNeverPattern { span })             }+            ResolutionError::CannotUseInnerLocalInMoveExpr {+                move_expr_span,+                ident,+                scope_span,+            } => self.dcx().create_err(diagnostics::CannotUseInnerLocalInMoveExpr {+                span,+                ident,+                binding_span: ident.span,+                move_expr_span,+                scope_span,+            }),         }     } 
compiler/rustc_resolve/src/diagnostics/mod.rs14 + / 0 −
@@ -525,6 +525,20 @@ pub(crate) struct BindingInNeverPattern {     pub(crate) span: Span, } +#[derive(Diagnostic)]+#[diag("cannot use `{$ident}` in this move expression")]+pub(crate) struct CannotUseInnerLocalInMoveExpr {+    #[primary_span]+    pub(crate) span: Span,+    pub(crate) ident: Ident,+    #[label("`{$ident}` is defined here")]+    pub(crate) binding_span: Span,+    #[label("this expression is evaluated outside that scope")]+    pub(crate) move_expr_span: Span,+    #[label("`{$ident}` is defined within this scope")]+    pub(crate) scope_span: Span,+}+ #[derive(Diagnostic)] #[diag("duplicate definitions with name `{$name}`:", code = E0201)] pub(crate) struct TraitImplDuplicate {
compiler/rustc_resolve/src/ident.rs21 + / 0 −
@@ -1511,9 +1511,28 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {                  for rib in ribs {                     match rib.kind {+                        // `first_rib` marks the start of the closure or coroutine scope.+                        // The initializer is evaluated outside that scope, so bindings defined+                        // inside it are unavailable, while outer bindings remain accessible.+                        RibKind::MoveExpr { first_rib, move_expr_span, scope_span }+                            if rib_index >= first_rib =>+                        {+                            if let Some(span) = finalize {+                                self.report_error(+                                    span,+                                    CannotUseInnerLocalInMoveExpr {+                                        move_expr_span,+                                        ident: original_rib_ident_def,+                                        scope_span,+                                    },+                                );+                            }+                            return Res::Err;+                        }                         RibKind::Normal                         | RibKind::Block(..)                         | RibKind::FnOrCoroutine+                        | RibKind::MoveExpr { .. }                         | RibKind::Module(..)                         | RibKind::MacroDefinition(..)                         | RibKind::ForwardGenericParamBan(_) => {@@ -1610,6 +1629,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {                         RibKind::Normal                         | RibKind::Block(..)                         | RibKind::FnOrCoroutine+                        | RibKind::MoveExpr { .. }                         | RibKind::Module(..)                         | RibKind::MacroDefinition(..)                         | RibKind::InlineAsmSym@@ -1717,6 +1737,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {                         RibKind::Normal                         | RibKind::Block(..)                         | RibKind::FnOrCoroutine+                        | RibKind::MoveExpr { .. }                         | RibKind::Module(..)                         | RibKind::MacroDefinition(..)                         | RibKind::InlineAsmSym
compiler/rustc_resolve/src/late.rs74 + / 13 −
@@ -219,6 +219,12 @@ pub(crate) enum RibKind<'ra> {     /// We passed through a function, closure or coroutine signature. Disallow labels.     FnOrCoroutine, +    MoveExpr {+        first_rib: usize,+        move_expr_span: Span,+        scope_span: Span,+    },+     /// We passed through an item scope. Disallow upvars.     Item(HasGenericParams, DefKind), @@ -264,6 +270,7 @@ impl RibKind<'_> {             RibKind::Normal             | RibKind::Block(..)             | RibKind::FnOrCoroutine+            | RibKind::MoveExpr { .. }             | RibKind::ConstantItem(..)             | RibKind::Module(_)             | RibKind::MacroDefinition(_)@@ -840,6 +847,8 @@ struct LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {     /// The current set of local scopes for types and values.     ribs: PerNS<Vec<Rib<'ra>>>, +    move_expr_scopes: Vec<Option<(usize, Span)>>,+     /// Previous popped `rib`, only used for diagnostic.     last_block_rib: Option<Rib<'ra>>, @@ -901,7 +910,9 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc         // Always report errors in items we just entered.         let old_ignore = replace(&mut self.in_func_body, false);         with_owner(self, item.id, |this| {-            this.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item))+            this.with_move_expr_scope(None, |this| {+                this.with_lifetime_rib(LifetimeRibKind::Item, |this| this.resolve_item(item))+            })         });         self.in_func_body = old_ignore;         self.diag_metadata.current_item = prev;@@ -1617,6 +1628,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {                 type_ns: vec![Rib::new(start_rib_kind)],                 macro_ns: vec![Rib::new(start_rib_kind)],             },+            move_expr_scopes: Vec::new(),             last_block_rib: None,             label_ribs: Vec::new(),             lifetime_ribs: Vec::new(),@@ -1728,6 +1740,39 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {         }     } +    fn with_move_expr_scope<T>(+        &mut self,+        scope_span: Option<Span>,+        work: impl FnOnce(&mut Self) -> T,+    ) -> T {+        if !self.r.features.move_expr() {+            return work(self);+        }+        let scope = scope_span.map(|span| (self.ribs[ValueNS].len(), span));+        self.move_expr_scopes.push(scope);+        let result = work(self);+        self.move_expr_scopes.pop();+        result+    }++    fn with_move_expr_initializer<T>(+        &mut self,+        move_expr_span: Span,+        work: impl FnOnce(&mut Self) -> T,+    ) -> T {+        let Some(Some((first_rib, scope_span))) = self.move_expr_scopes.last().copied() else {+            return work(self);+        };+        self.move_expr_scopes.pop();+        let result = self.with_rib(+            ValueNS,+            RibKind::MoveExpr { first_rib, move_expr_span, scope_span },+            work,+        );+        self.move_expr_scopes.push(Some((first_rib, scope_span)));+        result+    }+     fn visit_generic_params(&mut self, params: &'ast [GenericParam], add_self_upper: bool) {         // For type parameter defaults, we have to ban access         // to following type parameters, as the GenericArgs can only@@ -5566,30 +5611,46 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {             ExprKind::Type(ref _type_expr, ref _ty) => {                 visit::walk_expr(self, expr);             }+            ExprKind::Move(..) => {+                self.with_move_expr_initializer(expr.span, |this| visit::walk_expr(this, expr));+            }             // For closures, RibKind::FnOrCoroutine is added in visit_fn             ExprKind::Closure(ast::Closure {                 binder: ClosureBinder::For { ref generic_params, span },+                fn_decl_span,                 ..             }) => {-                self.with_generic_param_rib(-                    generic_params,-                    RibKind::Normal,-                    expr.id,-                    LifetimeBinderKind::Closure,-                    span,-                    |this| visit::walk_expr(this, expr),-                );+                self.with_move_expr_scope(Some(fn_decl_span), |this| {+                    this.with_generic_param_rib(+                        generic_params,+                        RibKind::Normal,+                        expr.id,+                        LifetimeBinderKind::Closure,+                        span,+                        |this| visit::walk_expr(this, expr),+                    );+                });+            }+            ExprKind::Closure(ref closure) => {+                self.with_move_expr_scope(Some(closure.fn_decl_span), |this| {+                    visit::walk_expr(this, expr)+                });             }-            ExprKind::Closure(..) => visit::walk_expr(self, expr),-            ExprKind::Gen(..) => {-                self.with_label_rib(RibKind::FnOrCoroutine, |this| visit::walk_expr(this, expr));+            ExprKind::Gen(_, _, _, decl_span) => {+                self.with_move_expr_scope(Some(decl_span), |this| {+                    this.with_label_rib(RibKind::FnOrCoroutine, |this| {+                        visit::walk_expr(this, expr)+                    });+                });             }             ExprKind::Repeat(ref elem, ref ct) => {                 self.visit_expr(elem);                 self.resolve_anon_const(ct, AnonConstKind::ConstArg(IsRepeatExpr::Yes));             }             ExprKind::ConstBlock(ref ct) => {-                self.resolve_anon_const(ct, AnonConstKind::InlineConst);+                self.with_move_expr_scope(None, |this| {+                    this.resolve_anon_const(ct, AnonConstKind::InlineConst);+                });             }             ExprKind::Index(ref elem, ref idx, _) => {                 self.resolve_expr(elem, Some(expr));
compiler/rustc_resolve/src/lib.rs24 + / 5 −
@@ -262,7 +262,10 @@ enum ResolutionError<'ra> {     /// Error E0416: identifier is bound more than once in the same pattern.     IdentifierBoundMoreThanOnceInSamePattern(Ident),     /// Error E0426: use of undeclared label.-    UndeclaredLabel { name: Symbol, suggestion: Option<LabelSuggestion> },+    UndeclaredLabel {+        name: Symbol,+        suggestion: Option<LabelSuggestion>,+    },     /// Error E0433: failed to resolve.     FailedToResolve {         segment: Symbol,@@ -273,7 +276,9 @@ enum ResolutionError<'ra> {         message: String,     },     /// Error E0434: can't capture dynamic environment in a fn item.-    CannotCaptureDynamicEnvironmentInFnItem { suggest_closure: bool },+    CannotCaptureDynamicEnvironmentInFnItem {+        suggest_closure: bool,+    },     /// Error E0435: attempt to use a non-constant value in a constant.     AttemptToUseNonConstantValueInConstant {         ident: Ident,@@ -296,7 +301,9 @@ enum ResolutionError<'ra> {     // FIXME(generic_const_parameter_types): This should give custom output specifying it's only     // problematic to use *forward declared* parameters when the feature is enabled.     /// ERROR E0770: the type of const parameters must not depend on other generic parameters.-    ParamInTyOfConstParam { name: Symbol },+    ParamInTyOfConstParam {+        name: Symbol,+    },     /// cannot use self in const param     SelfInConstParam,     /// generic parameters must not be used inside const evaluations.@@ -310,11 +317,18 @@ enum ResolutionError<'ra> {     /// generic parameters must not be used inside enum discriminants.     ///     /// This error is emitted even with `generic_const_exprs`.-    ParamInEnumDiscriminant { name: Symbol, param_kind: ParamKindInEnumDiscriminant },+    ParamInEnumDiscriminant {+        name: Symbol,+        param_kind: ParamKindInEnumDiscriminant,+    },     /// Error E0735: generic parameters with a default cannot use `Self`     ForwardDeclaredSelf(ForwardGenericParamBanReason),     /// Error E0767: use of unreachable label-    UnreachableLabel { name: Symbol, definition_span: Span, suggestion: Option<LabelSuggestion> },+    UnreachableLabel {+        name: Symbol,+        definition_span: Span,+        suggestion: Option<LabelSuggestion>,+    },     /// Error E0323, E0324, E0325: mismatch between trait item and impl item.     TraitImplMismatch {         name: Ident,@@ -339,6 +353,11 @@ enum ResolutionError<'ra> {     LowercaseSelf,     /// A never pattern has a binding.     BindingInNeverPattern,+    CannotUseInnerLocalInMoveExpr {+        move_expr_span: Span,+        ident: Ident,+        scope_span: Span,+    }, }  #[derive(Debug)]
tests/ui/move-expr/closure-local-bindings.rsadded77 + / 0 −
@@ -0,0 +1,77 @@+//@ check-fail+//@ edition: 2024++#![allow(incomplete_features)]+#![feature(gen_blocks, move_expr)]++macro_rules! move_value {+    ($value:expr) => { move($value) };+}++fn main() {+    let _ = |x: i32| move(x);+    //~^ ERROR cannot use `x` in this move expression++    let _ = |(x, _): (i32, i32)| move(x);+    //~^ ERROR cannot use `x` in this move expression++    let x = 1;+    let _ = || {+        let x = 2;+        move(x);+        //~^ ERROR cannot use `x` in this move expression+    };++    let _ = || {+        for x in [1] {+            move(x);+            //~^ ERROR cannot use `x` in this move expression+        }+    };++    let _ = || {+        if let Some(x) = Some(1) {+            move(x);+            //~^ ERROR cannot use `x` in this move expression+        }+    };++    let _ = || {+        let x = 1;+        || move(move(x))+        //~^ ERROR cannot use `x` in this move expression+    };++    let _ = || || move({+        let x = 1;+        move(x)+        //~^ ERROR cannot use `x` in this move expression+    });++    let _ = || {+        let x = 1;+        move_value!(x);+        //~^ ERROR cannot use `x` in this move expression+    };++    let _ = async |x: i32| move(x);+    //~^ ERROR cannot use `x` in this move expression++    let _ = async {+        let x = 1;+        move(x);+        //~^ ERROR cannot use `x` in this move expression+    };++    let _ = gen {+        let x = 1;+        yield move(x);+        //~^ ERROR cannot use `x` in this move expression+    };++    let _ = || {+        let x = 1;+        move(|| x)+        //~^ ERROR cannot use `x` in this move expression+    };+}
tests/ui/move-expr/closure-local-bindings.stderradded125 + / 0 −
@@ -0,0 +1,125 @@+error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:12:27+   |+LL |     let _ = |x: i32| move(x);+   |             -------- -----^-+   |             ||       |+   |             ||       this expression is evaluated outside that scope+   |             |`x` is defined here+   |             `x` is defined within this scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:15:39+   |+LL |     let _ = |(x, _): (i32, i32)| move(x);+   |             -------------------- -----^-+   |             | |                  |+   |             | |                  this expression is evaluated outside that scope+   |             | `x` is defined here+   |             `x` is defined within this scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:21:14+   |+LL |     let _ = || {+   |             -- `x` is defined within this scope+LL |         let x = 2;+   |             - `x` is defined here+LL |         move(x);+   |         -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:27:18+   |+LL |     let _ = || {+   |             -- `x` is defined within this scope+LL |         for x in [1] {+   |             - `x` is defined here+LL |             move(x);+   |             -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:34:18+   |+LL |     let _ = || {+   |             -- `x` is defined within this scope+LL |         if let Some(x) = Some(1) {+   |                     - `x` is defined here+LL |             move(x);+   |             -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:41:22+   |+LL |     let _ = || {+   |             -- `x` is defined within this scope+LL |         let x = 1;+   |             - `x` is defined here+LL |         || move(move(x))+   |                 -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:47:14+   |+LL |     let _ = || || move({+   |             -- `x` is defined within this scope+LL |         let x = 1;+   |             - `x` is defined here+LL |         move(x)+   |         -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:53:21+   |+LL |     ($value:expr) => { move($value) };+   |                        ------------ this expression is evaluated outside that scope+...+LL |     let _ = || {+   |             -- `x` is defined within this scope+LL |         let x = 1;+   |             - `x` is defined here+LL |         move_value!(x);+   |                     ^++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:57:33+   |+LL |     let _ = async |x: i32| move(x);+   |             -------------- -----^-+   |             |      |       |+   |             |      |       this expression is evaluated outside that scope+   |             |      `x` is defined here+   |             `x` is defined within this scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:62:14+   |+LL |     let _ = async {+   |             ----- `x` is defined within this scope+LL |         let x = 1;+   |             - `x` is defined here+LL |         move(x);+   |         -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:68:20+   |+LL |     let _ = gen {+   |             --- `x` is defined within this scope+LL |         let x = 1;+   |             - `x` is defined here+LL |         yield move(x);+   |               -----^- this expression is evaluated outside that scope++error: cannot use `x` in this move expression+  --> $DIR/closure-local-bindings.rs:74:17+   |+LL |     let _ = || {+   |             -- `x` is defined within this scope+LL |         let x = 1;+   |             - `x` is defined here+LL |         move(|| x)+   |         --------^- this expression is evaluated outside that scope++error: aborting due to 12 previous errors+
tests/ui/move-expr/closure-local-uninitialized.rsadded13 + / 0 −
@@ -0,0 +1,13 @@+//@ check-fail+// Regression test for https://github.com/rust-lang/rust/issues/156509++#![allow(incomplete_features)]+#![feature(move_expr)]++fn main() {+    let _c = || {+        let x;+        move(x);+        //~^ ERROR cannot use `x` in this move expression+    };+}
tests/ui/move-expr/closure-local-uninitialized.stderradded12 + / 0 −
@@ -0,0 +1,12 @@+error: cannot use `x` in this move expression+  --> $DIR/closure-local-uninitialized.rs:10:14+   |+LL |     let _c = || {+   |              -- `x` is defined within this scope+LL |         let x;+   |             - `x` is defined here+LL |         move(x);+   |         -----^- this expression is evaluated outside that scope++error: aborting due to 1 previous error+
tests/ui/move-expr/move-initializer-local-bindings.rsadded66 + / 0 −
@@ -0,0 +1,66 @@+//@ run-pass++#![allow(incomplete_features, unused_variables)]+#![feature(move_expr)]++fn main() {+    let c = || move({+        let x = 3;+        x+    });+    assert_eq!(c(), 3);++    let c = || move({+        let x = 4;+        (|| x)()+    });+    assert_eq!(c(), 4);++    let outer = || {+        let x = 5;+        || move(x)+    };+    assert_eq!(outer()(), 5);++    let outer = || {+        let x = 6;+        || move({+            let y = 7;+            || move(y + x)+        })+    };+    assert_eq!(outer()()(), 13);++    let c = || move({+        fn identity(x: i32) -> i32 {+            x+        }+        identity(8)+    });+    assert_eq!(c(), 8);++    let c = || move(const { 9 });+    assert_eq!(c(), 9);++    let x = 10;+    let c = || move(x);+    let d = || move(x + 1);+    assert_eq!(c(), 10);+    assert_eq!(d(), 11);++    let c = || {+        let x = 12;+        move({+            let x = 13;+            x+        })+    };+    assert_eq!(c(), 13);++    let c = |x: i32| move({+        let x;+        x = 14;+        x+    });+    assert_eq!(c(0), 14);+}
tests/ui/move-expr/name-resolution.rs2 + / 4 −
@@ -1,13 +1,11 @@-//@ check-pass-//@ ignore-test (#155050): currently ICEs instead of reporting a name-resolution error-// FIXME(TaKO8Ki): Remove this ignore once closure-local names in `move(expr)` produce a real-// diagnostic instead of hitting the current `Res::Err` ICE path.+//@ check-fail #![allow(incomplete_features)] #![feature(move_expr)]  fn main() {     let _c = || {         let x = 3;         move(x);+        //~^ ERROR cannot use `x` in this move expression     }; }
tests/ui/move-expr/name-resolution.stderradded12 + / 0 −
@@ -0,0 +1,12 @@+error: cannot use `x` in this move expression+  --> $DIR/name-resolution.rs:8:14+   |+LL |     let _c = || {+   |              -- `x` is defined within this scope+LL |         let x = 3;+   |             - `x` is defined here+LL |         move(x);+   |         -----^- this expression is evaluated outside that scope++error: aborting due to 1 previous error+