add move(expr) syntax

This commit is contained in:
Takayuki Maeda
2026-04-09 21:14:27 +09:00
parent f62d5368a2
commit 98348de63a
13 changed files with 77 additions and 4 deletions
+3
View File
@@ -1594,6 +1594,7 @@ fn prefix_attrs_precedence(attrs: &AttrVec) -> ExprPrecedence {
// need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
// but we need to print `(let _ = a) < b` as-is with parens.
| ExprKind::Let(..)
| ExprKind::Move(..)
| ExprKind::Unary(..) => ExprPrecedence::Prefix,
// Need parens if and only if there are prefix attributes.
@@ -1763,6 +1764,8 @@ pub enum ExprKind {
Binary(BinOp, Box<Expr>, Box<Expr>),
/// A unary operation (e.g., `!x`, `*x`).
Unary(UnOp, Box<Expr>),
/// A `move(expr)` expression.
Move(Box<Expr>, Span),
/// A literal (e.g., `1`, `"foo"`).
Lit(token::Lit),
/// A cast (e.g., `foo as f64`).
+2
View File
@@ -108,6 +108,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
Assign(e, _, _)
| AssignOp(_, e, _)
| Await(e, _)
| Move(e, _)
| Use(e, _)
| Binary(_, e, _)
| Call(e, _)
@@ -183,6 +184,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
| Ret(Some(e))
| Unary(_, e)
| Yeet(Some(e))
| Move(e, _)
| Become(e) => {
expr = e;
}
+3 -1
View File
@@ -1024,7 +1024,9 @@ pub fn walk_fn<$($lt,)? V: $Visitor$(<$lt>)?>(vis: &mut V, kind: FnKind<$($lt)?
visit_visitable!($($mut)? vis, block, opt_label),
ExprKind::Gen(capt, body, kind, decl_span) =>
visit_visitable!($($mut)? vis, capt, body, kind, decl_span),
ExprKind::Await(expr, span) | ExprKind::Use(expr, span) =>
ExprKind::Await(expr, span)
| ExprKind::Move(expr, span)
| ExprKind::Use(expr, span) =>
visit_visitable!($($mut)? vis, expr, span),
ExprKind::Assign(lhs, rhs, span) =>
visit_visitable!($($mut)? vis, lhs, rhs, span),
@@ -136,6 +136,13 @@ pub(crate) struct ClosureCannotBeStatic {
pub fn_decl_span: Span,
}
#[derive(Diagnostic)]
#[diag("`move(expr)` is only supported in plain closures")]
pub(crate) struct MoveExprOnlyInPlainClosures {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag("functional record updates are not allowed in destructuring assignments")]
pub(crate) struct FunctionalRecordUpdateDestructuringAssignment {
+14 -2
View File
@@ -19,8 +19,8 @@
use super::errors::{
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, ClosureCannotBeStatic,
CoroutineTooManyParameters, FunctionalRecordUpdateDestructuringAssignment,
InclusiveRangeWithNoEnd, MatchArmWithNoBody, NeverPatternWithBody, NeverPatternWithGuard,
UnderscoreExprLhsAssign,
InclusiveRangeWithNoEnd, MatchArmWithNoBody, MoveExprOnlyInPlainClosures, NeverPatternWithBody,
NeverPatternWithGuard, UnderscoreExprLhsAssign,
};
use super::{
GenericArgsMode, ImplTraitContext, LoweringContext, ParamMode, ResolverAstLoweringExt,
@@ -211,6 +211,18 @@ pub(super) fn lower_expr_mut(&mut self, e: &Expr) -> hir::Expr<'hir> {
},
),
ExprKind::Await(expr, await_kw_span) => self.lower_expr_await(*await_kw_span, expr),
ExprKind::Move(_, move_kw_span) => {
if !self.tcx.features().move_expr() {
return self.expr_err(
*move_kw_span,
self.dcx().span_delayed_bug(*move_kw_span, "invalid move(expr)"),
);
}
self.dcx().emit_err(MoveExprOnlyInPlainClosures { span: *move_kw_span });
hir::ExprKind::Err(
self.dcx().span_delayed_bug(*move_kw_span, "invalid move(expr)"),
)
}
ExprKind::Use(expr, use_kw_span) => self.lower_expr_use(*use_kw_span, expr),
ExprKind::Closure(Closure {
binder,
@@ -352,6 +352,9 @@ fn visit_expr(&mut self, e: &'a ast::Expr) {
}
_ => (),
},
ast::ExprKind::Move(_, move_kw_span) => {
gate!(&self, move_expr, move_kw_span, "`move(expr)` syntax is experimental");
}
_ => {}
}
visit::walk_expr(self, e)
@@ -630,6 +630,11 @@ pub(super) fn print_expr_outer_attr_style(
);
self.word(".await");
}
ast::ExprKind::Move(expr, _) => {
self.word("move(");
self.print_expr(expr, FixupContext::default());
self.word(")");
}
ast::ExprKind::Use(expr, _) => {
self.print_expr_cond_paren(
expr,
@@ -248,6 +248,9 @@ fn manage_cond_expr(&mut self, expr: &mut Box<Expr>) {
self.manage_cond_expr(arg);
}
}
ExprKind::Move(local_expr, _) => {
self.manage_cond_expr(local_expr);
}
ExprKind::Path(_, Path { segments, .. }) if let [path_segment] = &segments[..] => {
let path_ident = path_segment.ident;
self.manage_initial_capture(expr, path_ident);
+17
View File
@@ -564,6 +564,12 @@ macro_rules! make_it {
token::Ident(..) if this.token.is_keyword(kw::Box) => {
make_it!(this, attrs, |this, _| this.parse_expr_box(lo))
}
token::Ident(..)
if this.token.is_keyword(kw::Move)
&& this.look_ahead(1, |t| *t == token::OpenParen) =>
{
make_it!(this, attrs, |this, _| this.parse_expr_move(lo))
}
token::Ident(..) if this.may_recover() && this.is_mistaken_not_ident_negation() => {
make_it!(this, attrs, |this, _| this.recover_not_expr(lo))
}
@@ -607,6 +613,16 @@ fn parse_expr_box(&mut self, box_kw: Span) -> PResult<'a, (Span, ExprKind)> {
Ok((span, ExprKind::Err(guar)))
}
fn parse_expr_move(&mut self, move_kw: Span) -> PResult<'a, (Span, ExprKind)> {
self.bump();
self.psess.gated_spans.gate(sym::move_expr, move_kw);
self.expect(exp!(OpenParen))?;
let expr = self.parse_expr()?;
self.expect(exp!(CloseParen))?;
let span = move_kw.to(self.prev_token.span);
Ok((span, ExprKind::Move(expr, move_kw)))
}
fn is_mistaken_not_ident_negation(&self) -> bool {
let token_cannot_continue_expr = |t: &Token| match t.uninterpolate().kind {
// These tokens can start an expression after `!`, but
@@ -4387,6 +4403,7 @@ fn find_let_some(expr: &Expr) -> Option<&Expr> {
}
ExprKind::Unary(_, _)
| ExprKind::Await(_, _)
| ExprKind::Move(_, _)
| ExprKind::Use(_, _)
| ExprKind::AssignOp(_, _, _)
| ExprKind::Range(_, _, _)
+1 -1
View File
@@ -657,7 +657,7 @@ fn visit_expr(&mut self, e: &'v ast::Expr) {
(self, e, e.kind, None, ast, Expr, ExprKind),
[
Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
If, While, ForLoop, Loop, Match, Closure, Block, Await, Use, TryBlock, Assign,
If, While, ForLoop, Loop, Match, Closure, Block, Await, Move, Use, TryBlock, Assign,
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy
@@ -0,0 +1,4 @@
fn main() {
let _ = || move(2);
//~^ ERROR `move(expr)` syntax is experimental
}
@@ -0,0 +1,12 @@
error[E0658]: `move(expr)` syntax is experimental
--> $DIR/feature-gate-move_expr.rs:2:16
|
LL | let _ = || move(2);
| ^^^^
|
= help: add `#![feature(move_expr)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.
+3
View File
@@ -3,6 +3,7 @@
//@ compile-flags: --test
#![allow(incomplete_features)]
#![allow(unused_features)]
#![feature(auto_traits)]
#![feature(box_patterns)]
#![feature(const_block_items)]
@@ -11,6 +12,7 @@
#![feature(decl_macro)]
#![feature(macro_guard_matcher)]
#![feature(more_qualified_paths)]
#![feature(move_expr)]
#![feature(never_patterns)]
#![feature(specialization)]
#![feature(trait_alias)]
@@ -110,6 +112,7 @@ fn test_expr() {
c1!(expr, [ *expr ], "*expr");
c1!(expr, [ !expr ], "!expr");
c1!(expr, [ -expr ], "-expr");
c1!(expr, [ move(expr) ], "move(expr)");
// ExprKind::Lit
c1!(expr, [ 'x' ], "'x'");