diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a6ac056b93b5e..1f2aba2b27e68 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -9,6 +9,7 @@ use rustc_data_structures::thin_vec::ThinVec; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::Res; +use rustc_session::parse::feature_err; use rustc_span::hygiene::ForLoopLoc; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; 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) } 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( 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 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], + ctx: &str, + eq_sign_span: Span, + assignments: &mut Vec>, + ) -> (&'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 `..=` into `std::ops::RangeInclusive::new(, )`. fn lower_expr_range_closed(&mut self, span: Span, e1: &Expr, e2: &Expr) -> hir::ExprKind<'hir> { let e1 = self.lower_expr_mut(e1); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 599599f415f1c..af2f96d5e6253 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -2531,6 +2531,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { hir_id, kind: hir::PatKind::Binding(bm, hir_id, ident.with_span_pos(span), None), span, + default_binding_modes: true, }), 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> { - 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( diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index a1cbcde1f4291..e4e7b24d29e52 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -273,11 +273,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { /// 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> { - 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. - 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() .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)) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ad926a810e6bf..84114fc773533 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -610,6 +610,9 @@ declare_features! ( /// Allows unsized fn parameters. (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 // ------------------------------------------------------------------------- diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b9ec18688c5f2..6767041ecee83 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -732,6 +732,9 @@ pub struct Pat<'hir> { pub hir_id: HirId, pub kind: PatKind<'hir>, 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<'_> { @@ -1680,6 +1683,9 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. 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 _ { .. }`. diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 30b700a1d4f63..04d456936eba6 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -69,6 +69,7 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> { hir::LocalSource::ForLoopDesugar => ("`for` loop binding", None), hir::LocalSource::AsyncFn => ("async fn 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_patterns(&loc.pat); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 1a6c45b6c80d2..2324dba80f543 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -434,6 +434,7 @@ symbols! { deref_mut, deref_target, derive, + destructuring_assignment, diagnostic, direct, discriminant_kind, diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 324aa1a66a6d5..af19ad08c1d08 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -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( &self, lhs: &'tcx hir::Expr<'tcx>, err_code: &'static str, expr_span: &Span, ) { - if !lhs.is_syntactic_place_expr() { - // 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"); - 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(); + if lhs.is_syntactic_place_expr() { + return; } + + // 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`. diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs index 53bc2069b76ce..d0e6edfd9db30 100644 --- a/compiler/rustc_typeck/src/check/pat.rs +++ b/compiler/rustc_typeck/src/check/pat.rs @@ -270,6 +270,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// /// 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) -> 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 { // Type checking these product-like types successfully always require // that the expected type be of those types and not reference types. diff --git a/compiler/rustc_typeck/src/check/regionck.rs b/compiler/rustc_typeck/src/check/regionck.rs index ba0f22513a146..7b31b9f3915f4 100644 --- a/compiler/rustc_typeck/src/check/regionck.rs +++ b/compiler/rustc_typeck/src/check/regionck.rs @@ -577,7 +577,7 @@ impl<'a, 'tcx> RegionCtxt<'a, 'tcx> { fn link_pattern(&self, discr_cmt: PlaceWithHirId<'tcx>, root_pat: &hir::Pat<'_>) { debug!("link_pattern(discr_cmt={:?}, root_pat={:?})", discr_cmt, root_pat); 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 if let PatKind::Binding(..) = kind { if let Some(ty::BindByReference(mutbl)) = diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs index d7cf1b7700514..39536f12e3bb5 100644 --- a/src/test/ui/bad/bad-expr-lhs.rs +++ b/src/test/ui/bad/bad-expr-lhs.rs @@ -1,10 +1,12 @@ fn main() { 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); - (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 } diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr index a195e1054d099..d4b2193d09fc2 100644 --- a/src/test/ui/bad/bad-expr-lhs.stderr +++ b/src/test/ui/bad/bad-expr-lhs.stderr @@ -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 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 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + error[E0070]: invalid left-hand side of assignment --> $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 | LL | (1, 2) = (3, 4); - | ------ ^ - | | - | cannot assign to this expression + | - ^ + | | + | cannot assign to this expression error[E0070]: invalid left-hand side of assignment - --> $DIR/bad-expr-lhs.rs:7:12 - | -LL | (a, b) = (3, 4); - | ------ ^ - | | - | cannot assign to this expression + --> $DIR/bad-expr-lhs.rs:4:12 | - = note: destructuring assignments are not currently supported - = note: for more information, see https://github.com/rust-lang/rfcs/issues/372 +LL | (1, 2) = (3, 4); + | - ^ + | | + | cannot assign to this expression 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); | ---- ^ | | | 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`. diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs new file mode 100644 index 0000000000000..adecd0ff291f9 --- /dev/null +++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs @@ -0,0 +1,7 @@ +#![feature(destructuring_assignment)] + +fn main() { + let mut x = &0; + let mut y = &0; + (x, y) = &(1, 2); //~ ERROR mismatched types +} diff --git a/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr new file mode 100644 index 0000000000000..e6161fdfa2441 --- /dev/null +++ b/src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr @@ -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`. diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index 876c9efea2647..e0cb9dc9158e2 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -3,23 +3,24 @@ struct S { x: u8, y: u8 } fn main() { 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 - //~^ 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 - //~^ ERROR binary assignment operation `+=` cannot be applied + //~| ERROR binary assignment operation `+=` cannot be applied 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 - //~^ 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; - ((a, b), c) = ((3, 4), 5); //~ ERROR invalid left-hand side of assignment + ((a, b), c) = ((3, 4), 5); //~ ERROR destructuring assignments are unstable } diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index d4e25930d22d7..c5543fab825eb 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -1,4 +1,4 @@ -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:6:12 | LL | (a, b) = (3, 4); @@ -6,8 +6,19 @@ LL | (a, b) = (3, 4); | | | 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 + = note: see issue #71126 for more information + = 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 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})` --> $DIR/note-unsupported.rs:7:5 @@ -24,9 +35,6 @@ LL | (a, b) += (3, 4); | ------ ^^ | | | 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:10:12 @@ -35,9 +43,6 @@ LL | [a, b] = [3, 4]; | ------ ^ | | | 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]` --> $DIR/note-unsupported.rs:11:5 @@ -54,9 +59,6 @@ LL | [a, b] += [3, 4]; | ------ ^^ | | | 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:16:22 @@ -65,9 +67,6 @@ LL | S { x: a, y: b } = s; | ---------------- ^ | | | 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` --> $DIR/note-unsupported.rs:17:5 @@ -86,9 +85,6 @@ LL | S { x: a, y: b } += s; | ---------------- ^^ | | | 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:20:21 @@ -97,22 +93,8 @@ LL | S { x: a, ..s } = S { x: 3, y: 4 }; | --------------- ^ | | | 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 -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`. diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs new file mode 100644 index 0000000000000..16aafc4693f3f --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -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)); +} diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs new file mode 100644 index 0000000000000..b76f4968e6249 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs @@ -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 +} diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr new file mode 100644 index 0000000000000..a60e1cb1eec62 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -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`. diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.rs b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs new file mode 100644 index 0000000000000..c1c5c2cd3cebb --- /dev/null +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs @@ -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 +} diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr new file mode 100644 index 0000000000000..b87ef6f1571c1 --- /dev/null +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr @@ -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 + diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs new file mode 100644 index 0000000000000..e7801f0e8ec2b --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs @@ -0,0 +1,4 @@ +fn main() { + let (a, b) = (0, 1); + (a, b) = (2, 3); //~ ERROR destructuring assignments are unstable +} diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr new file mode 100644 index 0000000000000..62e71decb32a0 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr @@ -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 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`.