Rollup merge of #78748 - fanzier:tuple-assignment, r=petrochenkov
Implement destructuring assignment for tuples This is the first step towards implementing destructuring assignment (RFC: https://github.com/rust-lang/rfcs/pull/2909, tracking issue: #71126). This PR is the first part of #71156, which was split up to allow for easier review. Quick summary: This change allows destructuring the LHS of an assignment if it's a (possibly nested) tuple. It is implemented via a desugaring (AST -> HIR lowering) as follows: ```rust (a,b) = (1,2) ``` ... becomes ... ```rust { let (lhs0,lhs1) = (1,2); a = lhs0; b = lhs1; } ``` Thanks to `@varkor` who helped with the implementation, particularly around default binding modes. r? `@petrochenkov`
This commit is contained in:
commit
abaa78baeb
23 changed files with 395 additions and 86 deletions
|
@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_span::hygiene::ForLoopLoc;
|
use rustc_span::hygiene::ForLoopLoc;
|
||||||
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
|
use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned};
|
||||||
use rustc_span::symbol::{sym, Ident, Symbol};
|
use rustc_span::symbol::{sym, Ident, Symbol};
|
||||||
|
@ -146,7 +147,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
|
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
|
||||||
}
|
}
|
||||||
ExprKind::Assign(ref el, ref er, span) => {
|
ExprKind::Assign(ref el, ref er, span) => {
|
||||||
hir::ExprKind::Assign(self.lower_expr(el), self.lower_expr(er), span)
|
self.lower_expr_assign(el, er, span, e.span)
|
||||||
}
|
}
|
||||||
ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
|
ExprKind::AssignOp(op, ref el, ref er) => hir::ExprKind::AssignOp(
|
||||||
self.lower_binop(op),
|
self.lower_binop(op),
|
||||||
|
@ -840,6 +841,134 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Destructure the LHS of complex assignments.
|
||||||
|
/// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`.
|
||||||
|
fn lower_expr_assign(
|
||||||
|
&mut self,
|
||||||
|
lhs: &Expr,
|
||||||
|
rhs: &Expr,
|
||||||
|
eq_sign_span: Span,
|
||||||
|
whole_span: Span,
|
||||||
|
) -> hir::ExprKind<'hir> {
|
||||||
|
// Return early in case of an ordinary assignment.
|
||||||
|
fn is_ordinary(lhs: &Expr) -> bool {
|
||||||
|
match &lhs.kind {
|
||||||
|
ExprKind::Tup(..) => false,
|
||||||
|
ExprKind::Paren(e) => {
|
||||||
|
match e.kind {
|
||||||
|
// We special-case `(..)` for consistency with patterns.
|
||||||
|
ExprKind::Range(None, None, RangeLimits::HalfOpen) => false,
|
||||||
|
_ => is_ordinary(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_ordinary(lhs) {
|
||||||
|
return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span);
|
||||||
|
}
|
||||||
|
if !self.sess.features_untracked().destructuring_assignment {
|
||||||
|
feature_err(
|
||||||
|
&self.sess.parse_sess,
|
||||||
|
sym::destructuring_assignment,
|
||||||
|
eq_sign_span,
|
||||||
|
"destructuring assignments are unstable",
|
||||||
|
)
|
||||||
|
.span_label(lhs.span, "cannot assign to this expression")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut assignments = vec![];
|
||||||
|
|
||||||
|
// The LHS becomes a pattern: `(lhs1, lhs2)`.
|
||||||
|
let pat = self.destructure_assign(lhs, eq_sign_span, &mut assignments);
|
||||||
|
let rhs = self.lower_expr(rhs);
|
||||||
|
|
||||||
|
// Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`.
|
||||||
|
let destructure_let = self.stmt_let_pat(
|
||||||
|
ThinVec::new(),
|
||||||
|
whole_span,
|
||||||
|
Some(rhs),
|
||||||
|
pat,
|
||||||
|
hir::LocalSource::AssignDesugar(eq_sign_span),
|
||||||
|
);
|
||||||
|
|
||||||
|
// `a = lhs1; b = lhs2;`.
|
||||||
|
let stmts = self
|
||||||
|
.arena
|
||||||
|
.alloc_from_iter(std::iter::once(destructure_let).chain(assignments.into_iter()));
|
||||||
|
|
||||||
|
// Wrap everything in a block.
|
||||||
|
hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert the LHS of a destructuring assignment to a pattern.
|
||||||
|
/// Each sub-assignment is recorded in `assignments`.
|
||||||
|
fn destructure_assign(
|
||||||
|
&mut self,
|
||||||
|
lhs: &Expr,
|
||||||
|
eq_sign_span: Span,
|
||||||
|
assignments: &mut Vec<hir::Stmt<'hir>>,
|
||||||
|
) -> &'hir hir::Pat<'hir> {
|
||||||
|
match &lhs.kind {
|
||||||
|
// Tuples.
|
||||||
|
ExprKind::Tup(elements) => {
|
||||||
|
let (pats, rest) =
|
||||||
|
self.destructure_sequence(elements, "tuple", eq_sign_span, assignments);
|
||||||
|
let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0));
|
||||||
|
return self.pat_without_dbm(lhs.span, tuple_pat);
|
||||||
|
}
|
||||||
|
ExprKind::Paren(e) => {
|
||||||
|
// We special-case `(..)` for consistency with patterns.
|
||||||
|
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
|
||||||
|
let tuple_pat = hir::PatKind::Tuple(&[], Some(0));
|
||||||
|
return self.pat_without_dbm(lhs.span, tuple_pat);
|
||||||
|
} else {
|
||||||
|
return self.destructure_assign(e, eq_sign_span, assignments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// Treat all other cases as normal lvalue.
|
||||||
|
let ident = Ident::new(sym::lhs, lhs.span);
|
||||||
|
let (pat, binding) = self.pat_ident(lhs.span, ident);
|
||||||
|
let ident = self.expr_ident(lhs.span, ident, binding);
|
||||||
|
let assign = hir::ExprKind::Assign(self.lower_expr(lhs), ident, eq_sign_span);
|
||||||
|
let expr = self.expr(lhs.span, assign, ThinVec::new());
|
||||||
|
assignments.push(self.stmt_expr(lhs.span, expr));
|
||||||
|
pat
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Destructure a sequence of expressions occurring on the LHS of an assignment.
|
||||||
|
/// Such a sequence occurs in a tuple (struct)/slice.
|
||||||
|
/// Return a sequence of corresponding patterns, and the index and the span of `..` if it
|
||||||
|
/// exists.
|
||||||
|
/// Each sub-assignment is recorded in `assignments`.
|
||||||
|
fn destructure_sequence(
|
||||||
|
&mut self,
|
||||||
|
elements: &[AstP<Expr>],
|
||||||
|
ctx: &str,
|
||||||
|
eq_sign_span: Span,
|
||||||
|
assignments: &mut Vec<hir::Stmt<'hir>>,
|
||||||
|
) -> (&'hir [&'hir hir::Pat<'hir>], Option<(usize, Span)>) {
|
||||||
|
let mut rest = None;
|
||||||
|
let elements =
|
||||||
|
self.arena.alloc_from_iter(elements.iter().enumerate().filter_map(|(i, e)| {
|
||||||
|
// Check for `..` pattern.
|
||||||
|
if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind {
|
||||||
|
if let Some((_, prev_span)) = rest {
|
||||||
|
self.ban_extra_rest_pat(e.span, prev_span, ctx);
|
||||||
|
} else {
|
||||||
|
rest = Some((i, e.span));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(self.destructure_assign(e, eq_sign_span, assignments))
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
(elements, rest)
|
||||||
|
}
|
||||||
|
|
||||||
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
|
/// Desugar `<start>..=<end>` into `std::ops::RangeInclusive::new(<start>, <end>)`.
|
||||||
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
|
fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> {
|
||||||
let e1 = self.lower_expr_mut(e1);
|
let e1 = self.lower_expr_mut(e1);
|
||||||
|
|
|
@ -2531,6 +2531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
hir_id,
|
hir_id,
|
||||||
kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
|
kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None),
|
||||||
span,
|
span,
|
||||||
|
default_binding_modes: true,
|
||||||
}),
|
}),
|
||||||
hir_id,
|
hir_id,
|
||||||
)
|
)
|
||||||
|
@ -2541,7 +2542,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
|
fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
|
||||||
self.arena.alloc(hir::Pat { hir_id: self.next_id(), kind, span })
|
self.arena.alloc(hir::Pat {
|
||||||
|
hir_id: self.next_id(),
|
||||||
|
kind,
|
||||||
|
span,
|
||||||
|
default_binding_modes: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pat_without_dbm(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
|
||||||
|
self.arena.alloc(hir::Pat {
|
||||||
|
hir_id: self.next_id(),
|
||||||
|
kind,
|
||||||
|
span,
|
||||||
|
default_binding_modes: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ty_path(
|
fn ty_path(
|
||||||
|
|
|
@ -273,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
|
|
||||||
/// Construct a `Pat` with the `HirId` of `p.id` lowered.
|
/// Construct a `Pat` with the `HirId` of `p.id` lowered.
|
||||||
fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
|
fn pat_with_node_id_of(&mut self, p: &Pat, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
|
||||||
self.arena.alloc(hir::Pat { hir_id: self.lower_node_id(p.id), kind, span: p.span })
|
self.arena.alloc(hir::Pat {
|
||||||
|
hir_id: self.lower_node_id(p.id),
|
||||||
|
kind,
|
||||||
|
span: p.span,
|
||||||
|
default_binding_modes: true,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
|
/// Emit a friendly error for extra `..` patterns in a tuple/tuple struct/slice pattern.
|
||||||
fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
|
crate fn ban_extra_rest_pat(&self, sp: Span, prev_sp: Span, ctx: &str) {
|
||||||
self.diagnostic()
|
self.diagnostic()
|
||||||
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
|
.struct_span_err(sp, &format!("`..` can only be used once per {} pattern", ctx))
|
||||||
.span_label(sp, &format!("can only be used once per {} pattern", ctx))
|
.span_label(sp, &format!("can only be used once per {} pattern", ctx))
|
||||||
|
|
|
@ -610,6 +610,9 @@ declare_features! (
|
||||||
/// Allows unsized fn parameters.
|
/// Allows unsized fn parameters.
|
||||||
(active, unsized_fn_params, "1.49.0", Some(48055), None),
|
(active, unsized_fn_params, "1.49.0", Some(48055), None),
|
||||||
|
|
||||||
|
/// Allows the use of destructuring assignments.
|
||||||
|
(active, destructuring_assignment, "1.49.0", Some(71126), None),
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// feature-group-end: actual feature gates
|
// feature-group-end: actual feature gates
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
|
@ -732,6 +732,9 @@ pub struct Pat<'hir> {
|
||||||
pub hir_id: HirId,
|
pub hir_id: HirId,
|
||||||
pub kind: PatKind<'hir>,
|
pub kind: PatKind<'hir>,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
// Whether to use default binding modes.
|
||||||
|
// At present, this is false only for destructuring assignment.
|
||||||
|
pub default_binding_modes: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pat<'_> {
|
impl Pat<'_> {
|
||||||
|
@ -1680,6 +1683,9 @@ pub enum LocalSource {
|
||||||
AsyncFn,
|
AsyncFn,
|
||||||
/// A desugared `<expr>.await`.
|
/// A desugared `<expr>.await`.
|
||||||
AwaitDesugar,
|
AwaitDesugar,
|
||||||
|
/// A desugared `expr = expr`, where the LHS is a tuple, struct or array.
|
||||||
|
/// The span is that of the `=` sign.
|
||||||
|
AssignDesugar(Span),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Hints at the original code for a `match _ { .. }`.
|
/// Hints at the original code for a `match _ { .. }`.
|
||||||
|
|
|
@ -69,6 +69,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
|
||||||
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
|
hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None),
|
||||||
hir::LocalSource::AsyncFn => ("async fn binding", None),
|
hir::LocalSource::AsyncFn => ("async fn binding", None),
|
||||||
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
|
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
|
||||||
|
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
|
||||||
};
|
};
|
||||||
self.check_irrefutable(&loc.pat, msg, sp);
|
self.check_irrefutable(&loc.pat, msg, sp);
|
||||||
self.check_patterns(&loc.pat);
|
self.check_patterns(&loc.pat);
|
||||||
|
|
|
@ -434,6 +434,7 @@ symbols! {
|
||||||
deref_mut,
|
deref_mut,
|
||||||
deref_target,
|
deref_target,
|
||||||
derive,
|
derive,
|
||||||
|
destructuring_assignment,
|
||||||
diagnostic,
|
diagnostic,
|
||||||
direct,
|
direct,
|
||||||
discriminant_kind,
|
discriminant_kind,
|
||||||
|
|
|
@ -718,39 +718,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_destructuring_place_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> bool {
|
|
||||||
match &expr.kind {
|
|
||||||
ExprKind::Array(comps) | ExprKind::Tup(comps) => {
|
|
||||||
comps.iter().all(|e| self.is_destructuring_place_expr(e))
|
|
||||||
}
|
|
||||||
ExprKind::Struct(_path, fields, rest) => {
|
|
||||||
rest.as_ref().map(|e| self.is_destructuring_place_expr(e)).unwrap_or(true)
|
|
||||||
&& fields.iter().all(|f| self.is_destructuring_place_expr(&f.expr))
|
|
||||||
}
|
|
||||||
_ => expr.is_syntactic_place_expr(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn check_lhs_assignable(
|
pub(crate) fn check_lhs_assignable(
|
||||||
&self,
|
&self,
|
||||||
lhs: &'tcx hir::Expr<'tcx>,
|
lhs: &'tcx hir::Expr<'tcx>,
|
||||||
err_code: &'static str,
|
err_code: &'static str,
|
||||||
expr_span: &Span,
|
expr_span: &Span,
|
||||||
) {
|
) {
|
||||||
if !lhs.is_syntactic_place_expr() {
|
if lhs.is_syntactic_place_expr() {
|
||||||
// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
|
return;
|
||||||
let mut err = self.tcx.sess.struct_span_err_with_code(
|
|
||||||
*expr_span,
|
|
||||||
"invalid left-hand side of assignment",
|
|
||||||
DiagnosticId::Error(err_code.into()),
|
|
||||||
);
|
|
||||||
err.span_label(lhs.span, "cannot assign to this expression");
|
|
||||||
if self.is_destructuring_place_expr(lhs) {
|
|
||||||
err.note("destructuring assignments are not currently supported");
|
|
||||||
err.note("for more information, see https://github.com/rust-lang/rfcs/issues/372");
|
|
||||||
}
|
|
||||||
err.emit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: Make this use SessionDiagnostic once error codes can be dynamically set.
|
||||||
|
let mut err = self.tcx.sess.struct_span_err_with_code(
|
||||||
|
*expr_span,
|
||||||
|
"invalid left-hand side of assignment",
|
||||||
|
DiagnosticId::Error(err_code.into()),
|
||||||
|
);
|
||||||
|
err.span_label(lhs.span, "cannot assign to this expression");
|
||||||
|
err.emit();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type check assignment expression `expr` of form `lhs = rhs`.
|
/// Type check assignment expression `expr` of form `lhs = rhs`.
|
||||||
|
|
|
@ -270,6 +270,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
///
|
///
|
||||||
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
|
/// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
|
||||||
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
|
fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
|
||||||
|
// When we perform destructuring assignment, we disable default match bindings, which are
|
||||||
|
// unintuitive in this context.
|
||||||
|
if !pat.default_binding_modes {
|
||||||
|
return AdjustMode::Reset;
|
||||||
|
}
|
||||||
match &pat.kind {
|
match &pat.kind {
|
||||||
// Type checking these product-like types successfully always require
|
// Type checking these product-like types successfully always require
|
||||||
// that the expected type be of those types and not reference types.
|
// that the expected type be of those types and not reference types.
|
||||||
|
|
|
@ -577,7 +577,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> {
|
||||||
fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) {
|
fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) {
|
||||||
debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat);
|
debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat);
|
||||||
ignore_err!(self.with_mc(|mc| {
|
ignore_err!(self.with_mc(|mc| {
|
||||||
mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id }| {
|
mc.cat_pattern(discr_cmt, root_pat, |sub_cmt, hir::Pat { kind, span, hir_id, .. }| {
|
||||||
// `ref x` pattern
|
// `ref x` pattern
|
||||||
if let PatKind::Binding(..) = kind {
|
if let PatKind::Binding(..) = kind {
|
||||||
if let Some(ty::BindByReference(mutbl)) =
|
if let Some(ty::BindByReference(mutbl)) =
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
1 = 2; //~ ERROR invalid left-hand side of assignment
|
1 = 2; //~ ERROR invalid left-hand side of assignment
|
||||||
1 += 2; //~ ERROR invalid left-hand side of assignment
|
1 += 2; //~ ERROR invalid left-hand side of assignment
|
||||||
(1, 2) = (3, 4); //~ ERROR invalid left-hand side of assignment
|
(1, 2) = (3, 4); //~ ERROR destructuring assignments are unstable
|
||||||
|
//~| ERROR invalid left-hand side of assignment
|
||||||
|
//~| ERROR invalid left-hand side of assignment
|
||||||
|
|
||||||
let (a, b) = (1, 2);
|
let (a, b) = (1, 2);
|
||||||
(a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
|
(a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
|
||||||
|
|
||||||
None = Some(3); //~ ERROR invalid left-hand side of assignment
|
None = Some(3); //~ ERROR invalid left-hand side of assignment
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,25 @@
|
||||||
|
error[E0658]: destructuring assignments are unstable
|
||||||
|
--> $DIR/bad-expr-lhs.rs:4:12
|
||||||
|
|
|
||||||
|
LL | (1, 2) = (3, 4);
|
||||||
|
| ------ ^
|
||||||
|
| |
|
||||||
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
|
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
|
||||||
|
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: destructuring assignments are unstable
|
||||||
|
--> $DIR/bad-expr-lhs.rs:9:12
|
||||||
|
|
|
||||||
|
LL | (a, b) = (3, 4);
|
||||||
|
| ------ ^
|
||||||
|
| |
|
||||||
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
|
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
|
||||||
|
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/bad-expr-lhs.rs:2:7
|
--> $DIR/bad-expr-lhs.rs:2:7
|
||||||
|
|
|
|
||||||
|
@ -18,30 +40,27 @@ error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/bad-expr-lhs.rs:4:12
|
--> $DIR/bad-expr-lhs.rs:4:12
|
||||||
|
|
|
|
||||||
LL | (1, 2) = (3, 4);
|
LL | (1, 2) = (3, 4);
|
||||||
| ------ ^
|
| - ^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/bad-expr-lhs.rs:7:12
|
--> $DIR/bad-expr-lhs.rs:4:12
|
||||||
|
|
|
|
||||||
LL | (a, b) = (3, 4);
|
LL | (1, 2) = (3, 4);
|
||||||
| ------ ^
|
| - ^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/bad-expr-lhs.rs:9:10
|
--> $DIR/bad-expr-lhs.rs:11:10
|
||||||
|
|
|
|
||||||
LL | None = Some(3);
|
LL | None = Some(3);
|
||||||
| ---- ^
|
| ---- ^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
||||||
error: aborting due to 5 previous errors
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0067, E0070.
|
Some errors have detailed explanations: E0067, E0070, E0658.
|
||||||
For more information about an error, try `rustc --explain E0067`.
|
For more information about an error, try `rustc --explain E0067`.
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
#![feature(destructuring_assignment)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut x = &0;
|
||||||
|
let mut y = &0;
|
||||||
|
(x, y) = &(1, 2); //~ ERROR mismatched types
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/default-match-bindings-forbidden.rs:6:5
|
||||||
|
|
|
||||||
|
LL | (x, y) = &(1, 2);
|
||||||
|
| ^^^^^^ ------- this expression has type `&({integer}, {integer})`
|
||||||
|
| |
|
||||||
|
| expected reference, found tuple
|
||||||
|
|
|
||||||
|
= note: expected type `&({integer}, {integer})`
|
||||||
|
found tuple `(_, _)`
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -3,23 +3,24 @@ struct S { x: u8, y: u8 }
|
||||||
fn main() {
|
fn main() {
|
||||||
let (a, b) = (1, 2);
|
let (a, b) = (1, 2);
|
||||||
|
|
||||||
(a, b) = (3, 4); //~ ERROR invalid left-hand side of assignment
|
(a, b) = (3, 4); //~ ERROR destructuring assignments are unstable
|
||||||
(a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment
|
(a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment
|
||||||
//~^ ERROR binary assignment operation `+=` cannot be applied
|
//~| ERROR binary assignment operation `+=` cannot be applied
|
||||||
|
|
||||||
[a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment
|
[a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment
|
||||||
[a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment
|
[a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment
|
||||||
//~^ ERROR binary assignment operation `+=` cannot be applied
|
//~| ERROR binary assignment operation `+=` cannot be applied
|
||||||
|
|
||||||
let s = S { x: 3, y: 4 };
|
let s = S { x: 3, y: 4 };
|
||||||
|
|
||||||
S { x: a, y: b } = s; //~ ERROR invalid left-hand side of assignment
|
S { x: a, y: b } = s; //~ ERROR invalid left-hand side of assignment
|
||||||
S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment
|
S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment
|
||||||
//~^ ERROR binary assignment operation `+=` cannot be applied
|
//~| ERROR binary assignment operation `+=` cannot be applied
|
||||||
|
|
||||||
S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment
|
S { x: a, ..s } = S { x: 3, y: 4 };
|
||||||
|
//~^ ERROR invalid left-hand side of assignment
|
||||||
|
|
||||||
let c = 3;
|
let c = 3;
|
||||||
|
|
||||||
((a, b), c) = ((3, 4), 5); //~ ERROR invalid left-hand side of assignment
|
((a, b), c) = ((3, 4), 5); //~ ERROR destructuring assignments are unstable
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0658]: destructuring assignments are unstable
|
||||||
--> $DIR/note-unsupported.rs:6:12
|
--> $DIR/note-unsupported.rs:6:12
|
||||||
|
|
|
|
||||||
LL | (a, b) = (3, 4);
|
LL | (a, b) = (3, 4);
|
||||||
|
@ -6,8 +6,19 @@ LL | (a, b) = (3, 4);
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error[E0658]: destructuring assignments are unstable
|
||||||
|
--> $DIR/note-unsupported.rs:25:17
|
||||||
|
|
|
||||||
|
LL | ((a, b), c) = ((3, 4), 5);
|
||||||
|
| ----------- ^
|
||||||
|
| |
|
||||||
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
|
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
|
||||||
|
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
|
||||||
|
|
||||||
error[E0368]: binary assignment operation `+=` cannot be applied to type `({integer}, {integer})`
|
error[E0368]: binary assignment operation `+=` cannot be applied to type `({integer}, {integer})`
|
||||||
--> $DIR/note-unsupported.rs:7:5
|
--> $DIR/note-unsupported.rs:7:5
|
||||||
|
@ -24,9 +35,6 @@ LL | (a, b) += (3, 4);
|
||||||
| ------ ^^
|
| ------ ^^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/note-unsupported.rs:10:12
|
--> $DIR/note-unsupported.rs:10:12
|
||||||
|
@ -35,9 +43,6 @@ LL | [a, b] = [3, 4];
|
||||||
| ------ ^
|
| ------ ^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]`
|
error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]`
|
||||||
--> $DIR/note-unsupported.rs:11:5
|
--> $DIR/note-unsupported.rs:11:5
|
||||||
|
@ -54,9 +59,6 @@ LL | [a, b] += [3, 4];
|
||||||
| ------ ^^
|
| ------ ^^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/note-unsupported.rs:16:22
|
--> $DIR/note-unsupported.rs:16:22
|
||||||
|
@ -65,9 +67,6 @@ LL | S { x: a, y: b } = s;
|
||||||
| ---------------- ^
|
| ---------------- ^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0368]: binary assignment operation `+=` cannot be applied to type `S`
|
error[E0368]: binary assignment operation `+=` cannot be applied to type `S`
|
||||||
--> $DIR/note-unsupported.rs:17:5
|
--> $DIR/note-unsupported.rs:17:5
|
||||||
|
@ -86,9 +85,6 @@ LL | S { x: a, y: b } += s;
|
||||||
| ---------------- ^^
|
| ---------------- ^^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
error[E0070]: invalid left-hand side of assignment
|
||||||
--> $DIR/note-unsupported.rs:20:21
|
--> $DIR/note-unsupported.rs:20:21
|
||||||
|
@ -97,22 +93,8 @@ LL | S { x: a, ..s } = S { x: 3, y: 4 };
|
||||||
| --------------- ^
|
| --------------- ^
|
||||||
| |
|
| |
|
||||||
| cannot assign to this expression
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error[E0070]: invalid left-hand side of assignment
|
|
||||||
--> $DIR/note-unsupported.rs:24:17
|
|
||||||
|
|
|
||||||
LL | ((a, b), c) = ((3, 4), 5);
|
|
||||||
| ----------- ^
|
|
||||||
| |
|
|
||||||
| cannot assign to this expression
|
|
||||||
|
|
|
||||||
= note: destructuring assignments are not currently supported
|
|
||||||
= note: for more information, see https://github.com/rust-lang/rfcs/issues/372
|
|
||||||
|
|
||||||
error: aborting due to 11 previous errors
|
error: aborting due to 11 previous errors
|
||||||
|
|
||||||
Some errors have detailed explanations: E0067, E0070, E0368.
|
Some errors have detailed explanations: E0067, E0070, E0368, E0658.
|
||||||
For more information about an error, try `rustc --explain E0067`.
|
For more information about an error, try `rustc --explain E0067`.
|
||||||
|
|
37
src/test/ui/destructuring-assignment/tuple_destructure.rs
Normal file
37
src/test/ui/destructuring-assignment/tuple_destructure.rs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(destructuring_assignment)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (mut a, mut b);
|
||||||
|
(a, b) = (0, 1);
|
||||||
|
assert_eq!((a, b), (0, 1));
|
||||||
|
(b, a) = (a, b);
|
||||||
|
assert_eq!((a, b), (1, 0));
|
||||||
|
(a, .., b) = (1, 2);
|
||||||
|
assert_eq!((a, b), (1, 2));
|
||||||
|
(.., a) = (1, 2);
|
||||||
|
assert_eq!((a, b), (2, 2));
|
||||||
|
(..) = (3, 4);
|
||||||
|
assert_eq!((a, b), (2, 2));
|
||||||
|
(b, ..) = (5, 6, 7);
|
||||||
|
assert_eq!(b, 5);
|
||||||
|
|
||||||
|
// Test for a non-Copy type (String):
|
||||||
|
let (mut c, mut d);
|
||||||
|
(c, d) = ("c".to_owned(), "d".to_owned());
|
||||||
|
assert_eq!(c, "c");
|
||||||
|
assert_eq!(d, "d");
|
||||||
|
(d, c) = (c, d);
|
||||||
|
assert_eq!(c, "d");
|
||||||
|
assert_eq!(d, "c");
|
||||||
|
|
||||||
|
// Test nesting/parentheses:
|
||||||
|
((a, b)) = (0, 1);
|
||||||
|
assert_eq!((a, b), (0, 1));
|
||||||
|
(((a, b)), (c)) = ((2, 3), d);
|
||||||
|
assert_eq!((a, b), (2, 3));
|
||||||
|
assert_eq!(c, "c");
|
||||||
|
((a, .., b), .., (..)) = ((4, 5), ());
|
||||||
|
assert_eq!((a, b), (4, 5));
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
#![feature(destructuring_assignment)]
|
||||||
|
|
||||||
|
const C: i32 = 1;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let (mut a, mut b);
|
||||||
|
(a, .., b, ..) = (0, 1); //~ ERROR `..` can only be used once per tuple pattern
|
||||||
|
(a, a, b) = (1, 2); //~ ERROR mismatched types
|
||||||
|
(C, ..) = (0,1); //~ ERROR invalid left-hand side of assignment
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
error: `..` can only be used once per tuple pattern
|
||||||
|
--> $DIR/tuple_destructure_fail.rs:7:16
|
||||||
|
|
|
||||||
|
LL | (a, .., b, ..) = (0, 1);
|
||||||
|
| -- ^^ can only be used once per tuple pattern
|
||||||
|
| |
|
||||||
|
| previously used here
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/tuple_destructure_fail.rs:8:5
|
||||||
|
|
|
||||||
|
LL | (a, a, b) = (1, 2);
|
||||||
|
| ^^^^^^^^^ ------ this expression has type `({integer}, {integer})`
|
||||||
|
| |
|
||||||
|
| expected a tuple with 2 elements, found one with 3 elements
|
||||||
|
|
|
||||||
|
= note: expected type `({integer}, {integer})`
|
||||||
|
found tuple `(_, _, _)`
|
||||||
|
|
||||||
|
error[E0070]: invalid left-hand side of assignment
|
||||||
|
--> $DIR/tuple_destructure_fail.rs:9:13
|
||||||
|
|
|
||||||
|
LL | (C, ..) = (0,1);
|
||||||
|
| - ^
|
||||||
|
| |
|
||||||
|
| cannot assign to this expression
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0070, E0308.
|
||||||
|
For more information about an error, try `rustc --explain E0070`.
|
|
@ -0,0 +1,23 @@
|
||||||
|
// run-pass
|
||||||
|
|
||||||
|
#![feature(destructuring_assignment)]
|
||||||
|
|
||||||
|
#![warn(unused_assignments)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut a;
|
||||||
|
// Assignment occurs left-to-right.
|
||||||
|
// However, we emit warnings when this happens, so it is clear that this is happening.
|
||||||
|
(a, a) = (0, 1); //~ WARN value assigned to `a` is never read
|
||||||
|
assert_eq!(a, 1);
|
||||||
|
|
||||||
|
// We can't always tell when a variable is being assigned to twice, which is why we don't try
|
||||||
|
// to emit an error, which would be fallible.
|
||||||
|
let mut x = 1;
|
||||||
|
(*foo(&mut x), *foo(&mut x)) = (5, 6);
|
||||||
|
assert_eq!(x, 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo<'a>(x: &'a mut u32) -> &'a mut u32 {
|
||||||
|
x
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
warning: value assigned to `a` is never read
|
||||||
|
--> $DIR/warn-unused-duplication.rs:11:6
|
||||||
|
|
|
||||||
|
LL | (a, a) = (0, 1);
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/warn-unused-duplication.rs:5:9
|
||||||
|
|
|
||||||
|
LL | #![warn(unused_assignments)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
= help: maybe it is overwritten before being read?
|
||||||
|
|
||||||
|
warning: 1 warning emitted
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() {
|
||||||
|
let (a, b) = (0, 1);
|
||||||
|
(a, b) = (2, 3); //~ ERROR destructuring assignments are unstable
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
error[E0658]: destructuring assignments are unstable
|
||||||
|
--> $DIR/feature-gate-destructuring_assignment.rs:3:12
|
||||||
|
|
|
||||||
|
LL | (a, b) = (2, 3);
|
||||||
|
| ------ ^
|
||||||
|
| |
|
||||||
|
| cannot assign to this expression
|
||||||
|
|
|
||||||
|
= note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
|
||||||
|
= help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
Loading…
Add table
Add a link
Reference in a new issue