Skip to content

Introduce move expressions (move($expr)) #155023

Open
TaKO8Ki wants to merge 9 commits intorust-lang:mainfrom
TaKO8Ki:move-expr-1
Open

Introduce move expressions (move($expr)) #155023
TaKO8Ki wants to merge 9 commits intorust-lang:mainfrom
TaKO8Ki:move-expr-1

Conversation

@TaKO8Ki
Copy link
Copy Markdown
Member

@TaKO8Ki TaKO8Ki commented Apr 9, 2026

This is an experimental first version of move expressions.

This first version implements it just in plain closures. A support for coroutine closures will be added in follow up pull requests.

RFC: will be added later
Tracking issue: #155050
Project goal:

r? @nikomatsakis

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Apr 9, 2026
@rustbot rustbot added the T-clippy Relevant to the Clippy team. label Apr 9, 2026
@rustbot rustbot added the T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue. label Apr 9, 2026
@rust-lang rust-lang deleted a comment from rust-log-analyzer Apr 9, 2026
@rust-lang rust-lang deleted a comment from rust-log-analyzer Apr 9, 2026
@rust-lang rust-lang deleted a comment from rust-log-analyzer Apr 9, 2026
@TaKO8Ki TaKO8Ki changed the title Move expressions (move($expr)) Introduce move expressions (move($expr)) Apr 9, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@TaKO8Ki TaKO8Ki marked this pull request as ready for review April 16, 2026 12:26
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Apr 16, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 16, 2026

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

The parser was modified, potentially altering the grammar of (stable) Rust
which would be a breaking change.

cc @fmease

Some changes occurred in src/tools/rustfmt

cc @rust-lang/rustfmt

@rustbot rustbot removed the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Apr 16, 2026
Comment thread compiler/rustc_ast_passes/src/feature_gate.rs Outdated
@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Apr 16, 2026
@@ -0,0 +1,12 @@
error[E0658]: `move(expr)` syntax is experimental
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it'd be nice to test more of the edge cases around move vs move(...). Something like this:

let x: bool = true;
let y: bool = true;
let a = move(x) || y; // this should be an error: move(x) outside of closure
let x: bool = true;
let y: bool = true;
let a = || move(x) || y; // this should successfully parse
let x: bool = true;
let y: bool = true;
let a = move[x] || y; // this should error
let x: bool = true;
let y: bool = true;
let a = move || move(x) || y; // this should successfully parse

Trying to think what else might come up. That's all I can think of right now.

View changes since the review

use crate::errors::{InvalidLegacyConstGenericArg, UseConstGenericArg, YieldInClosure};
use crate::{AllowReturnTypeNotation, FnDeclKind, ImplTraitPosition, TryBlockScope};

struct MoveExprOccurrence<'a> {
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment please -- what is this struct for? what are the roles of its fields?

View changes since the review

expr: &'a Expr,
}

struct MoveExprCollector<'a> {
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment please: what does this struct do?

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my preference is to have comments that walk through an example, e.g.,

// Given an expression like this
//
// ```
// || move(foo.bar).clone()
// ```
//
// this will pass walk the AST for the closure and collect `move(foo.bar)` expressions into the vector.


impl<'a> Visitor<'a> for MoveExprCollector<'a> {
fn visit_expr(&mut self, expr: &'a Expr) {
match &expr.kind {
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...then here you can refer back to the example...

// If this is `move` expression (`move(foo.bar)` in the example above) push its id into the vector.
// Else recurse.

or something lik ethat.

View changes since the review

expr: inner,
});
}
ExprKind::Closure(..) | ExprKind::Gen(..) | ExprKind::ConstBlock(..) => {}
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth calling this behavior out too: does not recurse into nested closures or const blocks. What about things like...

|| {
    fn bar() {
        move(x)
    }
}

...I guess that'd be an error, but still, seems like we should stop recursing at items.

View changes since the review

pub explicit_captures: &'hir [ExplicitCapture],
}

#[derive(Debug, Clone, Copy, HashStable_Generic)]
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment, what does this struct represent in terms of Rust source code/

View changes since the review

fn main() {
let s = String::from("hello");
let c = || {
let t = move(s);
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add a test with more than one move

View changes since the review


let _ = euv::ExprUseVisitor::new(&closure_fcx, &mut delegate).consume_body(body);

let explicit_captures = match self.tcx.hir_node(closure_hir_id).expect_expr().kind {
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this paragraph merits a comment

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(...continuing previous store...) so we make it stronger here

@@ -210,6 +210,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {

let _ = euv::ExprUseVisitor::new(&closure_fcx, &mut delegate).consume_body(body);
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pre-existing, but this process ought to have a better comment showing the overall flow.

From what I see here, at the end of this phase, we'll have inferred uses based on the actual actions taken in the closure. But that is not strong enough for move($).clone(), which would introduce a variable $m that is only used by-ref, and we want it to capture $m by value.

View changes since the review

};
for capture in explicit_captures {
let place = closure_fcx.place_for_root_variable(closure_def_id, capture.var_hir_id);
delegate.capture_information.push((
Copy link
Copy Markdown
Contributor

@nikomatsakis nikomatsakis Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have to refresh my memory, I kinda remember there were methods on delegate for like "this place was moved" and so forth-- we are skipping those and directly writing this data structure, is there a helper method we could invoke instead?

View changes since the review

@rust-bors
Copy link
Copy Markdown
Contributor

rust-bors bot commented Apr 16, 2026

☔ The latest upstream changes (presumably #155380) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. T-rustfmt Relevant to the rustfmt team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants