From a8c784cd0900cf5b34fdfd4de030ce5f57d15022 Mon Sep 17 00:00:00 2001 From: varkor Date: Sun, 22 Dec 2019 23:00:53 +0000 Subject: [PATCH 01/28] Add `destructuring_assignment` feature gate --- compiler/rustc_feature/src/active.rs | 3 + compiler/rustc_span/src/symbol.rs | 1 + compiler/rustc_typeck/src/check/expr.rs | 43 ++++++--- src/test/ui/bad/bad-expr-lhs.rs | 2 +- src/test/ui/bad/bad-expr-lhs.stderr | 11 +-- .../note-unsupported.rs | 16 ++-- .../note-unsupported.stderr | 92 +++++++------------ .../feature-gate-destructuring_assignment.rs | 4 + ...ature-gate-destructuring_assignment.stderr | 11 +++ 9 files changed, 97 insertions(+), 86 deletions(-) create mode 100644 src/test/ui/feature-gates/feature-gate-destructuring_assignment.rs create mode 100644 src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index ad926a810e6bf..0966f85174830 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", None, None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- 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..256970c7b7e6b 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -39,6 +39,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility}; +use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -737,19 +738,37 @@ impl<'a, 'tcx> FnCtxt<'a, '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"); + if lhs.is_syntactic_place_expr() { + return; + } + + let da = self.is_destructuring_place_expr(lhs); + match (da, self.tcx.features().destructuring_assignment) { + // Valid destructuring assignment. + (true, true) => {} + + // Destructuring assignment, but the feature is not enabled. + (true, false) => { + feature_err( + &self.tcx.sess.parse_sess, + sym::destructuring_assignment, + *expr_span, + "destructuring assignments are unstable", + ) + .emit(); + } + + // Invalid assignment. + (false, _) => { + // 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(); } - err.emit(); } } diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs index d7cf1b7700514..93b1c39c076b3 100644 --- a/src/test/ui/bad/bad-expr-lhs.rs +++ b/src/test/ui/bad/bad-expr-lhs.rs @@ -4,7 +4,7 @@ fn main() { (1, 2) = (3, 4); //~ 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..f7d92c160abb3 100644 --- a/src/test/ui/bad/bad-expr-lhs.stderr +++ b/src/test/ui/bad/bad-expr-lhs.stderr @@ -22,16 +22,13 @@ LL | (1, 2) = (3, 4); | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/bad-expr-lhs.rs:7:12 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0070]: invalid left-hand side of assignment --> $DIR/bad-expr-lhs.rs:9:10 @@ -43,5 +40,5 @@ LL | None = Some(3); error: aborting due to 5 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/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index 876c9efea2647..9b284b117d312 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -3,23 +3,23 @@ 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 invalid left-hand side of assignment + (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable + (a, b) += (3, 4); //~ ERROR destructuring assignments are unstable //~^ 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 destructuring assignments are unstable + [a, b] += [3, 4]; //~ ERROR destructuring assignments are unstable //~^ 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 + S { x: a, y: b } = s; //~ ERROR destructuring assignments are unstable + S { x: a, y: b } += s; //~ ERROR destructuring assignments are unstable //~^ 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 destructuring assignments are unstable 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..a8b5b26818519 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -1,13 +1,10 @@ -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:6:12 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:6:12 | 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 + = 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 @@ -17,27 +14,21 @@ LL | (a, b) += (3, 4); | | | cannot use `+=` on type `({integer}, {integer})` -error[E0067]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:7:12 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:7:12 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:10:12 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:10:12 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]` --> $DIR/note-unsupported.rs:11:5 @@ -47,27 +38,21 @@ LL | [a, b] += [3, 4]; | | | cannot use `+=` on type `[{integer}; 2]` -error[E0067]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:11:12 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:11:12 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:16:22 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:16:22 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0368]: binary assignment operation `+=` cannot be applied to type `S` --> $DIR/note-unsupported.rs:17:5 @@ -79,40 +64,31 @@ LL | S { x: a, y: b } += s; | = note: an implementation of `std::ops::AddAssign` might be missing for `S` -error[E0067]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:17:22 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:17:22 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:20:21 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.rs:20:21 | 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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error[E0070]: invalid left-hand side of assignment - --> $DIR/note-unsupported.rs:24:17 +error[E0658]: destructuring assignments are unstable + --> $DIR/destructuring-assignment.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 + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error: aborting due to 11 previous errors -Some errors have detailed explanations: E0067, E0070, E0368. -For more information about an error, try `rustc --explain E0067`. +Some errors have detailed explanations: E0368, E0658. +For more information about an error, try `rustc --explain E0368`. 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..198712a21f843 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr @@ -0,0 +1,11 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/feature-gate-destructuring_assignment.rs:3:12 + | +LL | (a, b) = (2, 3); + | ^ + | + = 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`. From fc4fd1096339d889aee0acf9c82e201951af774d Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Thu, 9 Apr 2020 01:10:17 +0200 Subject: [PATCH 02/28] Allow recursive tuples on the LHS of assignments --- compiler/rustc_ast_lowering/src/expr.rs | 88 ++++++++++++++++++- compiler/rustc_hir/src/hir.rs | 3 + .../src/thir/pattern/check_match.rs | 1 + compiler/rustc_typeck/src/check/expr.rs | 50 ++--------- 4 files changed, 96 insertions(+), 46 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index a6ac056b93b5e..9cfa77fa1ace4 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -146,7 +146,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), @@ -163,6 +163,16 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Range(ref e1, ref e2, lims) => { self.lower_expr_range(e.span, e1.as_deref(), e2.as_deref(), lims) } + ExprKind::Underscore => { + self.sess + .struct_span_err( + e.span, + "expected expression, found reserved identifier `_`", + ) + .span_label(e.span, "expected expression") + .emit(); + hir::ExprKind::Err + } ExprKind::Path(ref qself, ref path) => { let qpath = self.lower_qpath( e.id, @@ -186,8 +196,18 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::InlineAsm(ref asm) => self.lower_expr_asm(e.span, asm), ExprKind::LlvmInlineAsm(ref asm) => self.lower_expr_llvm_asm(asm), - ExprKind::Struct(ref path, ref fields, ref maybe_expr) => { - let maybe_expr = maybe_expr.as_ref().map(|x| self.lower_expr(x)); + ExprKind::Struct(ref path, ref fields, ref rest) => { + let rest = match rest { + StructRest::Base(e) => Some(self.lower_expr(e)), + StructRest::Rest(sp) => { + self.sess + .struct_span_err(*sp, "base expression required after `..`") + .span_label(*sp, "add a base expression here") + .emit(); + Some(&*self.arena.alloc(self.expr_err(*sp))) + } + StructRest::None => None, + }; hir::ExprKind::Struct( self.arena.alloc(self.lower_qpath( e.id, @@ -197,7 +217,7 @@ impl<'hir> LoweringContext<'_, 'hir> { ImplTraitContext::disallowed(), )), self.arena.alloc_from_iter(fields.iter().map(|x| self.lower_field(x))), - maybe_expr, + rest, ) } ExprKind::Yield(ref opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()), @@ -840,6 +860,66 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } + /// 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> { + let mut assignments = Vec::new(); + + // 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. + /// Along the way, introduce additional assignments in the parameter assignments. + fn destructure_assign( + &mut self, + lhs: &Expr, + eq_sign_span: Span, + assignments: &mut Vec>, + ) -> &'hir hir::Pat<'hir> { + match &lhs.kind { + ExprKind::Tup(elements) => { + let pats = self.arena.alloc_from_iter( + elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), + ); + let tuple_pat = hir::PatKind::Tuple(pats, None); + self.pat(lhs.span, tuple_pat) + } + _ => { + 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 + } + } + } + /// 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_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b9ec18688c5f2..e90620f4e7299 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1680,6 +1680,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 for 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_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 256970c7b7e6b..af19ad08c1d08 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -39,7 +39,6 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; use rustc_middle::ty::Ty; use rustc_middle::ty::TypeFoldable; use rustc_middle::ty::{AdtKind, Visibility}; -use rustc_session::parse::feature_err; use rustc_span::hygiene::DesugaringKind; use rustc_span::source_map::Span; use rustc_span::symbol::{kw, sym, Ident, Symbol}; @@ -719,19 +718,6 @@ 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>, @@ -742,34 +728,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return; } - let da = self.is_destructuring_place_expr(lhs); - match (da, self.tcx.features().destructuring_assignment) { - // Valid destructuring assignment. - (true, true) => {} - - // Destructuring assignment, but the feature is not enabled. - (true, false) => { - feature_err( - &self.tcx.sess.parse_sess, - sym::destructuring_assignment, - *expr_span, - "destructuring assignments are unstable", - ) - .emit(); - } - - // Invalid assignment. - (false, _) => { - // 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(); - } - } + // 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`. From 397e91417d1ccd1833942487279077ef7f6559fe Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Thu, 9 Apr 2020 20:39:02 +0200 Subject: [PATCH 03/28] Handle slices and structs as well --- compiler/rustc_ast_lowering/src/expr.rs | 69 +++++++++++++++++++++---- 1 file changed, 59 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9cfa77fa1ace4..1da6fc2a3a69e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -900,24 +900,73 @@ impl<'hir> LoweringContext<'_, 'hir> { eq_sign_span: Span, assignments: &mut Vec>, ) -> &'hir hir::Pat<'hir> { + // TODO: Handle `..` and `_` match &lhs.kind { + // slices: + ExprKind::Array(elements) => { + let pats = self.arena.alloc_from_iter( + elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), + ); + let slice_pat = hir::PatKind::Slice(pats, None, &[]); + return self.pat(lhs.span, slice_pat); + } + // tuple structs: + ExprKind::Call(callee, args) => { + if let ExprKind::Path(qself, path) = &callee.kind { + let pats = self.arena.alloc_from_iter( + args.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), + ); + let qpath = self.lower_qpath( + callee.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let tuple_struct_pat = hir::PatKind::TupleStruct(qpath, pats, None); + return self.pat(lhs.span, tuple_struct_pat); + } + } + // structs: + ExprKind::Struct(path, fields, _rest) => { + let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { + let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); + hir::FieldPat { + hir_id: self.next_id(), + ident: f.ident, + pat, + is_shorthand: f.is_shorthand, + span: f.span, + } + })); + let qpath = self.lower_qpath( + lhs.id, + &None, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + let struct_pat = hir::PatKind::Struct(qpath, field_pats, false); + return self.pat(lhs.span, struct_pat); + } + // tuples: ExprKind::Tup(elements) => { let pats = self.arena.alloc_from_iter( elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), ); let tuple_pat = hir::PatKind::Tuple(pats, None); - self.pat(lhs.span, tuple_pat) - } - _ => { - 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 + return self.pat(lhs.span, tuple_pat); } + _ => {} } + // 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 } /// Desugar `..=` into `std::ops::RangeInclusive::new(, )`. From 7b10eec11c213158bfa31623190522ef3079a644 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Thu, 9 Apr 2020 22:39:05 +0200 Subject: [PATCH 04/28] Handle `..` except in structs --- compiler/rustc_ast_lowering/src/expr.rs | 61 +++++++++++++++++++------ compiler/rustc_ast_lowering/src/pat.rs | 2 +- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 1da6fc2a3a69e..28e254e244708 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -900,22 +900,25 @@ impl<'hir> LoweringContext<'_, 'hir> { eq_sign_span: Span, assignments: &mut Vec>, ) -> &'hir hir::Pat<'hir> { - // TODO: Handle `..` and `_` + // TODO: Handle `_`, requires changes to the parser match &lhs.kind { // slices: ExprKind::Array(elements) => { - let pats = self.arena.alloc_from_iter( - elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), - ); - let slice_pat = hir::PatKind::Slice(pats, None, &[]); + let (pats, rest) = + self.destructure_sequence(elements, "slice", eq_sign_span, assignments); + let slice_pat = if let Some((i, span)) = rest { + let (before, after) = pats.split_at(i); + hir::PatKind::Slice(before, Some(self.pat(span, hir::PatKind::Wild)), after) + } else { + hir::PatKind::Slice(pats, None, &[]) + }; return self.pat(lhs.span, slice_pat); } // tuple structs: ExprKind::Call(callee, args) => { if let ExprKind::Path(qself, path) = &callee.kind { - let pats = self.arena.alloc_from_iter( - args.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), - ); + let (pats, rest) = + self.destructure_sequence(args, "tuple struct", eq_sign_span, assignments); let qpath = self.lower_qpath( callee.id, qself, @@ -923,12 +926,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - let tuple_struct_pat = hir::PatKind::TupleStruct(qpath, pats, None); + let tuple_struct_pat = + hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); return self.pat(lhs.span, tuple_struct_pat); } } // structs: - ExprKind::Struct(path, fields, _rest) => { + // TODO: support `..` here, requires changes to the parser + ExprKind::Struct(path, fields, rest) => { let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); hir::FieldPat { @@ -951,10 +956,9 @@ impl<'hir> LoweringContext<'_, 'hir> { } // tuples: ExprKind::Tup(elements) => { - let pats = self.arena.alloc_from_iter( - elements.iter().map(|e| self.destructure_assign(e, eq_sign_span, assignments)), - ); - let tuple_pat = hir::PatKind::Tuple(pats, None); + 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(lhs.span, tuple_pat); } _ => {} @@ -969,6 +973,35 @@ impl<'hir> LoweringContext<'_, 'hir> { 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 span of `..`, if any. + /// Along the way, introduce additional assignments in the parameter `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/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index a1cbcde1f4291..18790956b0670 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -277,7 +277,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } /// 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)) From 78238974245c659bf6d37c37bc586b6442c89d12 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Fri, 10 Apr 2020 23:24:23 +0200 Subject: [PATCH 05/28] Fix tidy --- compiler/rustc_ast_lowering/src/expr.rs | 6 +++--- compiler/rustc_feature/src/active.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 28e254e244708..8a38cc07d699f 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -900,7 +900,7 @@ impl<'hir> LoweringContext<'_, 'hir> { eq_sign_span: Span, assignments: &mut Vec>, ) -> &'hir hir::Pat<'hir> { - // TODO: Handle `_`, requires changes to the parser + // FIXME: Handle `_`, requires changes to the parser match &lhs.kind { // slices: ExprKind::Array(elements) => { @@ -932,8 +932,8 @@ impl<'hir> LoweringContext<'_, 'hir> { } } // structs: - // TODO: support `..` here, requires changes to the parser - ExprKind::Struct(path, fields, rest) => { + // FIXME: support `..` here, requires changes to the parser + ExprKind::Struct(path, fields, _rest) => { let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); hir::FieldPat { diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 0966f85174830..8818ee2737729 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -611,7 +611,7 @@ declare_features! ( (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows the use of destructuring assignments. - (active, destructuring_assignment, "1.49.0", None, None), + (active, destructuring_assignment, "1.49.0", Some(372), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates From 475cd1e15cd6574917d7c850dfcab82ca3231849 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Fri, 10 Apr 2020 23:49:21 +0200 Subject: [PATCH 06/28] Handle ordinary assignments as before --- compiler/rustc_ast_lowering/src/expr.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 8a38cc07d699f..ae01cae8afda9 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -860,7 +860,8 @@ impl<'hir> LoweringContext<'_, 'hir> { }) } - /// Lower `(a,b) = t` to `{ let (lhs1,lhs2) = t; a = lhs1; b = lhs2; }`. + /// 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, @@ -868,6 +869,19 @@ impl<'hir> LoweringContext<'_, 'hir> { eq_sign_span: Span, whole_span: Span, ) -> hir::ExprKind<'hir> { + // Return early in case of an ordinary assignment. + match lhs.kind { + ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => { + } + _ => { + return hir::ExprKind::Assign( + self.lower_expr(lhs), + self.lower_expr(rhs), + eq_sign_span, + ); + } + } + let mut assignments = Vec::new(); // The LHS becomes a pattern: `(lhs1, lhs2)` From a6562bdcc8444afb470b1db3ed6ac1923e885383 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Sat, 11 Apr 2020 01:25:47 +0200 Subject: [PATCH 07/28] Only lower tuple structs if in fact a constructor --- compiler/rustc_ast_lowering/src/expr.rs | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ae01cae8afda9..e653ad9862313 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -8,7 +8,7 @@ use rustc_data_structures::stack::ensure_sufficient_stack; 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_hir::def::{DefKind, Res}; use rustc_span::hygiene::ForLoopLoc; use rustc_span::source_map::{respan, DesugaringKind, Span, Spanned}; use rustc_span::symbol::{sym, Ident, Symbol}; @@ -940,9 +940,21 @@ impl<'hir> LoweringContext<'_, 'hir> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - let tuple_struct_pat = - hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); - return self.pat(lhs.span, tuple_struct_pat); + match qpath { + hir::QPath::Resolved( + _, + hir::Path { res: Res::Def(DefKind::Ctor(..), _), .. }, + ) => { + // Destructure like a tuple struct since the path is in fact a constructor. + let tuple_struct_pat = + hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); + return self.pat(lhs.span, tuple_struct_pat); + } + _ => { + // If the path is not a constructor, lower as an ordinary LHS. + // Typecheck will report an error later. + } + } } } // structs: From 5e39caa965638876e9d084f81b7577f94d361fd9 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Sat, 11 Apr 2020 01:49:06 +0200 Subject: [PATCH 08/28] Disallow `.. rest` in struct on LHS of an assignment --- compiler/rustc_ast_lowering/src/expr.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index e653ad9862313..9e160ea83dcae 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -871,8 +871,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::ExprKind<'hir> { // Return early in case of an ordinary assignment. match lhs.kind { - ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => { - } + ExprKind::Array(..) + | ExprKind::Call(..) + | ExprKind::Struct(_, _, None) + | ExprKind::Tup(..) => {} _ => { return hir::ExprKind::Assign( self.lower_expr(lhs), @@ -959,7 +961,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } // structs: // FIXME: support `..` here, requires changes to the parser - ExprKind::Struct(path, fields, _rest) => { + ExprKind::Struct(path, fields, None) => { let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); hir::FieldPat { From 330b1811084d9bc23f1a86184ffaf7402058f31c Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Sat, 11 Apr 2020 02:23:57 +0200 Subject: [PATCH 09/28] Check for constructors in early return condition --- compiler/rustc_ast_lowering/src/expr.rs | 37 +++++++++++++++++-------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 9e160ea83dcae..ae94eb51f7cc6 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -870,18 +870,33 @@ impl<'hir> LoweringContext<'_, 'hir> { whole_span: Span, ) -> hir::ExprKind<'hir> { // Return early in case of an ordinary assignment. - match lhs.kind { - ExprKind::Array(..) - | ExprKind::Call(..) - | ExprKind::Struct(_, _, None) - | ExprKind::Tup(..) => {} - _ => { - return hir::ExprKind::Assign( - self.lower_expr(lhs), - self.lower_expr(rhs), - eq_sign_span, - ); + let is_ordinary = match &lhs.kind { + ExprKind::Array(..) | ExprKind::Struct(_, _, None) | ExprKind::Tup(..) => false, + ExprKind::Call(callee, ..) => { + // Check for tuple struct constructor. + if let ExprKind::Path(qself, path) = &callee.kind { + let qpath = self.lower_qpath( + callee.id, + qself, + path, + ParamMode::Optional, + ImplTraitContext::disallowed(), + ); + match qpath { + hir::QPath::Resolved( + _, + hir::Path { res: Res::Def(DefKind::Ctor(..), _), .. }, + ) => false, + _ => true, + } + } else { + true + } } + _ => true, + }; + if is_ordinary { + return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span); } let mut assignments = Vec::new(); From 27e43ae09264e0d479b1a6fd323ee560b8a7364a Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Sat, 11 Apr 2020 02:34:40 +0200 Subject: [PATCH 10/28] Add tests --- src/test/ui/bad/bad-expr-lhs.rs | 4 +- src/test/ui/bad/bad-expr-lhs.stderr | 32 +++++++-- .../nested_destructure.rs | 17 +++++ .../note-unsupported.rs | 14 ++-- .../note-unsupported.stderr | 72 +++++++++++-------- .../slice_destructure.rs | 11 +++ .../slice_destructure_fail.rs | 7 ++ .../slice_destructure_fail.stderr | 17 +++++ .../struct_destructure.rs | 13 ++++ .../struct_destructure_fail.rs | 11 +++ .../struct_destructure_fail.stderr | 9 +++ .../tuple_destructure.rs | 11 +++ .../tuple_destructure_fail.rs | 7 ++ .../tuple_destructure_fail.stderr | 22 ++++++ .../tuple_struct_destructure.rs | 13 ++++ .../tuple_struct_destructure_fail.rs | 17 +++++ .../tuple_struct_destructure_fail.stderr | 29 ++++++++ ...ature-gate-destructuring_assignment.stderr | 5 +- 18 files changed, 267 insertions(+), 44 deletions(-) create mode 100644 src/test/ui/destructuring-assignment/nested_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/slice_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/slice_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/slice_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/struct_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/struct_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/struct_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/tuple_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr create mode 100644 src/test/ui/destructuring-assignment/tuple_struct_destructure.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs create mode 100644 src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr diff --git a/src/test/ui/bad/bad-expr-lhs.rs b/src/test/ui/bad/bad-expr-lhs.rs index 93b1c39c076b3..39536f12e3bb5 100644 --- a/src/test/ui/bad/bad-expr-lhs.rs +++ b/src/test/ui/bad/bad-expr-lhs.rs @@ -1,7 +1,9 @@ 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 destructuring assignments are unstable diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr index f7d92c160abb3..cb3c12384d16f 100644 --- a/src/test/ui/bad/bad-expr-lhs.stderr +++ b/src/test/ui/bad/bad-expr-lhs.stderr @@ -14,31 +14,53 @@ LL | 1 += 2; | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment +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 #372 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:4:12 + | +LL | (1, 2) = (3, 4); + | - ^ + | | + | cannot assign to this expression + +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 error[E0658]: destructuring assignments are unstable - --> $DIR/bad-expr-lhs.rs:7:12 + --> $DIR/bad-expr-lhs.rs:9:12 | LL | (a, b) = (3, 4); - | ^ + | ------ ^ + | | + | cannot assign to this expression | + = note: see issue #372 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: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, E0658. For more information about an error, try `rustc --explain E0067`. diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs new file mode 100644 index 0000000000000..393dfc16c0a1c --- /dev/null +++ b/src/test/ui/destructuring-assignment/nested_destructure.rs @@ -0,0 +1,17 @@ +// run-pass + +#![feature(destructuring_assignment)] + +struct Struct { + a: S, + b: T, +} + +struct TupleStruct(S, T); + +fn main() { + let (a, b, c, d); + Struct { a: TupleStruct((a, b), c), b: [d] } = + Struct { a: TupleStruct((0, 1), 2), b: [3] }; + assert_eq!((a, b, c, d), (0, 1, 2, 3)); +} diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index 9b284b117d312..fd1f3afa549de 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -4,20 +4,20 @@ fn main() { let (a, b) = (1, 2); (a, b) = (3, 4); //~ ERROR destructuring assignments are unstable - (a, b) += (3, 4); //~ ERROR destructuring assignments are unstable - //~^ ERROR binary assignment operation `+=` cannot be applied + (a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment + //~| ERROR binary assignment operation `+=` cannot be applied [a, b] = [3, 4]; //~ ERROR destructuring assignments are unstable - [a, b] += [3, 4]; //~ ERROR destructuring assignments are unstable - //~^ ERROR binary assignment operation `+=` cannot be applied + [a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment + //~| ERROR binary assignment operation `+=` cannot be applied let s = S { x: 3, y: 4 }; S { x: a, y: b } = s; //~ ERROR destructuring assignments are unstable - S { x: a, y: b } += s; //~ ERROR destructuring assignments are unstable - //~^ ERROR binary assignment operation `+=` cannot be applied + S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment + //~| ERROR binary assignment operation `+=` cannot be applied - S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR destructuring assignments are unstable + S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR invalid left-hand side of assignment let c = 3; diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index a8b5b26818519..3c87226d06817 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -1,9 +1,12 @@ error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:6:12 + --> $DIR/note-unsupported.rs:6:12 | LL | (a, b) = (3, 4); - | ^ + | ------ ^ + | | + | cannot assign to this expression | + = note: see issue #372 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})` @@ -14,20 +17,23 @@ LL | (a, b) += (3, 4); | | | cannot use `+=` on type `({integer}, {integer})` -error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:7:12 +error[E0067]: invalid left-hand side of assignment + --> $DIR/note-unsupported.rs:7:12 | LL | (a, b) += (3, 4); - | ^^ - | - = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + | ------ ^^ + | | + | cannot assign to this expression error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:10:12 + --> $DIR/note-unsupported.rs:10:12 | LL | [a, b] = [3, 4]; - | ^ + | ------ ^ + | | + | cannot assign to this expression | + = note: see issue #372 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}; 2]` @@ -38,20 +44,23 @@ LL | [a, b] += [3, 4]; | | | cannot use `+=` on type `[{integer}; 2]` -error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:11:12 +error[E0067]: invalid left-hand side of assignment + --> $DIR/note-unsupported.rs:11:12 | LL | [a, b] += [3, 4]; - | ^^ - | - = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + | ------ ^^ + | | + | cannot assign to this expression error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:16:22 + --> $DIR/note-unsupported.rs:16:22 | LL | S { x: a, y: b } = s; - | ^ + | ---------------- ^ + | | + | cannot assign to this expression | + = note: see issue #372 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0368]: binary assignment operation `+=` cannot be applied to type `S` @@ -64,31 +73,34 @@ LL | S { x: a, y: b } += s; | = note: an implementation of `std::ops::AddAssign` might be missing for `S` -error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:17:22 +error[E0067]: invalid left-hand side of assignment + --> $DIR/note-unsupported.rs:17:22 | LL | S { x: a, y: b } += s; - | ^^ - | - = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + | ---------------- ^^ + | | + | cannot assign to this expression -error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:20:21 +error[E0070]: invalid left-hand side of assignment + --> $DIR/note-unsupported.rs:20:21 | LL | S { x: a, ..s } = S { x: 3, y: 4 }; - | ^ - | - = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + | --------------- ^ + | | + | cannot assign to this expression error[E0658]: destructuring assignments are unstable - --> $DIR/destructuring-assignment.rs:24:17 + --> $DIR/note-unsupported.rs:24:17 | LL | ((a, b), c) = ((3, 4), 5); - | ^ + | ----------- ^ + | | + | cannot assign to this expression | + = note: see issue #372 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error: aborting due to 11 previous errors -Some errors have detailed explanations: E0368, E0658. -For more information about an error, try `rustc --explain 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/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs new file mode 100644 index 0000000000000..630f16be1a1c4 --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + [a, b] = [0, 1]; + assert_eq!((a,b), (0,1)); + [a, .., b] = [1,2]; + assert_eq!((a,b), (1,2)); +} diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs new file mode 100644 index 0000000000000..6efea32175f62 --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs @@ -0,0 +1,7 @@ +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern + [a, a, b] = [1,2]; //~ ERROR pattern requires 3 elements but array has 2 +} diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr new file mode 100644 index 0000000000000..596ff23c13d0c --- /dev/null +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -0,0 +1,17 @@ +error: `..` can only be used once per slice pattern + --> $DIR/slice_destructure_fail.rs:5:14 + | +LL | [a, .., b, ..] = [0, 1]; + | -- ^^ can only be used once per slice pattern + | | + | previously used here + +error[E0527]: pattern requires 3 elements but array has 2 + --> $DIR/slice_destructure_fail.rs:6:3 + | +LL | [a, a, b] = [1,2]; + | ^^^^^^^^^ expected 2 elements + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0527`. diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs new file mode 100644 index 0000000000000..bb1c12455c4bf --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(destructuring_assignment)] +struct Struct { + a: S, + b: T, +} + +fn main() { + let (a, b); + Struct { a, b } = Struct { a: 0, b: 1 }; + assert_eq!((a, b), (0, 1)); +} diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs new file mode 100644 index 0000000000000..b3261f054a890 --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -0,0 +1,11 @@ +#![feature(destructuring_assignment)] +struct Struct { + a: S, + b: T, +} + +fn main() { + let (mut a, b); + let mut c; + Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c` +} diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr new file mode 100644 index 0000000000000..c4099574d01b4 --- /dev/null +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -0,0 +1,9 @@ +error[E0026]: struct `Struct` does not have a field named `c` + --> $DIR/struct_destructure_fail.rs:10:20 + | +LL | Struct { a, b, c } = Struct { a: 0, b: 1 }; + | ^ struct `Struct` does not have this field + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0026`. 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..297fae83592c2 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -0,0 +1,11 @@ +// run-pass + +#![feature(destructuring_assignment)] + +fn main() { + let (mut a, mut b); + (a, b) = (0, 1); + assert_eq!((a,b), (0,1)); + (a, .., b) = (1,2); + assert_eq!((a,b), (1,2)); +} 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..975e5b6e5cac0 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs @@ -0,0 +1,7 @@ +#![feature(destructuring_assignment)] + +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 +} 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..1b750ab9cd74d --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -0,0 +1,22 @@ +error: `..` can only be used once per tuple pattern + --> $DIR/tuple_destructure_fail.rs:5: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:6: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 tuple `({integer}, {integer})` + found tuple `(_, _, _)` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs new file mode 100644 index 0000000000000..5dc93c4022618 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs @@ -0,0 +1,13 @@ +// run-pass + +#![feature(destructuring_assignment)] + +struct TupleStruct(S, T); + +fn main() { + let (mut a, mut b); + TupleStruct(a, b) = TupleStruct(0, 1); + assert_eq!((a,b), (0,1)); + TupleStruct(a, .., b) = TupleStruct(1,2); + assert_eq!((a,b), (1,2)); +} diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs new file mode 100644 index 0000000000000..f855323f26944 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs @@ -0,0 +1,17 @@ +#![feature(destructuring_assignment)] + +struct TupleStruct(S, T); + +fn main() { + let (mut a, mut b); + TupleStruct(a, .., b, ..) = TupleStruct(0, 1); + //~^ ERROR `..` can only be used once per tuple struct pattern + TupleStruct(a, a, b) = TupleStruct(1,2); + //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields + // Check if `test` is recognized as not a tuple struct but a function call: + test() = TupleStruct(0,0) //~ ERROR invalid left-hand side of assignment +} + +fn test() -> TupleStruct { + TupleStruct(0, 0) +} diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr new file mode 100644 index 0000000000000..7c6a12ac75937 --- /dev/null +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -0,0 +1,29 @@ +error: `..` can only be used once per tuple struct pattern + --> $DIR/tuple_struct_destructure_fail.rs:7:27 + | +LL | TupleStruct(a, .., b, ..) = TupleStruct(0, 1); + | -- ^^ can only be used once per tuple struct pattern + | | + | previously used here + +error[E0023]: this pattern has 3 fields, but the corresponding tuple struct has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:9:5 + | +LL | struct TupleStruct(S, T); + | ------------------------------- tuple struct defined here +... +LL | TupleStruct(a, a, b) = TupleStruct(1,2); + | ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3 + +error[E0070]: invalid left-hand side of assignment + --> $DIR/tuple_struct_destructure_fail.rs:12:12 + | +LL | test() = TupleStruct(0,0) + | ------ ^ + | | + | cannot assign to this expression + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0023, E0070. +For more information about an error, try `rustc --explain E0023`. diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr index 198712a21f843..a890dc6e3c6c9 100644 --- a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr @@ -2,8 +2,11 @@ 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 #372 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error: aborting due to previous error From f54533b9837db9e7a65ab8aac9ddd3cb0e5636fa Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Tue, 14 Apr 2020 00:12:00 +0200 Subject: [PATCH 11/28] Allow `_` as an expression --- compiler/rustc_ast/src/ast.rs | 3 ++ compiler/rustc_ast/src/mut_visit.rs | 1 + compiler/rustc_ast/src/visit.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 1 + compiler/rustc_parse/src/parser/expr.rs | 2 + src/test/ui/issues/issue-34334.rs | 6 +++ src/test/ui/issues/issue-34334.stderr | 48 +++++++++++++++++-- 7 files changed, 59 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f13d67b9c1584..198866f73388b 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1192,6 +1192,7 @@ impl Expr { ExprKind::Field(..) => ExprPrecedence::Field, ExprKind::Index(..) => ExprPrecedence::Index, ExprKind::Range(..) => ExprPrecedence::Range, + ExprKind::Underscore => ExprPrecedence::Path, ExprKind::Path(..) => ExprPrecedence::Path, ExprKind::AddrOf(..) => ExprPrecedence::AddrOf, ExprKind::Break(..) => ExprPrecedence::Break, @@ -1314,6 +1315,8 @@ pub enum ExprKind { Index(P, P), /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`). Range(Option>, Option>, RangeLimits), + /// An underscore. + Underscore, /// Variable reference, possibly containing `::` and/or type /// parameters (e.g., `foo::bar::`). diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index fe9ad58c9ac84..e951b60daea8d 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1218,6 +1218,7 @@ pub fn noop_visit_expr( visit_opt(e1, |e1| vis.visit_expr(e1)); visit_opt(e2, |e2| vis.visit_expr(e2)); } + ExprKind::Underscore => {} ExprKind::Path(qself, path) => { vis.visit_qself(qself); vis.visit_path(path); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2ab6667ac3cf1..419093ae4f541 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -807,6 +807,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { walk_list!(visitor, visit_expr, start); walk_list!(visitor, visit_expr, end); } + ExprKind::Underscore => {} ExprKind::Path(ref maybe_qself, ref path) => { if let Some(ref qself) = *maybe_qself { visitor.visit_ty(&qself.ty); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 9fcba90244330..26f0e05109b19 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -2069,6 +2069,7 @@ impl<'a> State<'a> { self.print_expr_maybe_paren(e, fake_prec); } } + ast::ExprKind::Underscore => self.s.word("_"), ast::ExprKind::Path(None, ref path) => self.print_path(path, true, 0), ast::ExprKind::Path(Some(ref qself), ref path) => self.print_qpath(path, qself, true), ast::ExprKind::Break(opt_label, ref opt_expr) => { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index c2a13d4b0dec1..ccdb32fc3d2e0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1116,6 +1116,8 @@ impl<'a> Parser<'a> { } else { self.parse_lit_expr(attrs) } + } else if self.eat_keyword(kw::Underscore) { + Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) } else { self.parse_lit_expr(attrs) } diff --git a/src/test/ui/issues/issue-34334.rs b/src/test/ui/issues/issue-34334.rs index bf2d091a01e95..4cd93a3e84cdd 100644 --- a/src/test/ui/issues/issue-34334.rs +++ b/src/test/ui/issues/issue-34334.rs @@ -1,6 +1,12 @@ fn main () { let sr: Vec<(u32, _, _) = vec![]; //~^ ERROR expected one of `,` or `>`, found `=` + //~| ERROR expected value, found struct `Vec` + //~| ERROR expected value, found builtin type `u32` + //~| ERROR mismatched types + //~| ERROR invalid left-hand side of assignment + //~| ERROR expected expression, found reserved identifier `_` + //~| ERROR expected expression, found reserved identifier `_` let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); //~^ ERROR a value of type `Vec<(u32, _, _)>` cannot be built } diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr index c10a41443050a..d4333c6211a80 100644 --- a/src/test/ui/issues/issue-34334.stderr +++ b/src/test/ui/issues/issue-34334.stderr @@ -6,14 +6,56 @@ LL | let sr: Vec<(u32, _, _) = vec![]; | | | while parsing the type for `sr` -error[E0277]: a value of type `Vec<(u32, _, _)>` cannot be built from an iterator over elements of type `()` - --> $DIR/issue-34334.rs:4:87 +error[E0423]: expected value, found struct `Vec` + --> $DIR/issue-34334.rs:2:13 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^^^ did you mean `Vec { /* fields */ }`? + +error[E0423]: expected value, found builtin type `u32` + --> $DIR/issue-34334.rs:2:18 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^^^ not a value + +error: expected expression, found reserved identifier `_` + --> $DIR/issue-34334.rs:2:23 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^ expected expression + +error: expected expression, found reserved identifier `_` + --> $DIR/issue-34334.rs:2:26 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^ expected expression + +error[E0308]: mismatched types + --> $DIR/issue-34334.rs:2:31 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | ^^^^^^ expected `bool`, found struct `std::vec::Vec` + | + = note: expected type `bool` + found struct `std::vec::Vec<_>` + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0070]: invalid left-hand side of assignment + --> $DIR/issue-34334.rs:2:29 + | +LL | let sr: Vec<(u32, _, _) = vec![]; + | --------------- ^ + | | + | cannot assign to this expression + +error[E0599]: no method named `iter` found for unit type `()` in the current scope + --> $DIR/issue-34334.rs:10:36 | LL | let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); | ^^^^^^^ value of type `Vec<(u32, _, _)>` cannot be built from `std::iter::Iterator` | = help: the trait `FromIterator<()>` is not implemented for `Vec<(u32, _, _)>` -error: aborting due to 2 previous errors +error: aborting due to 8 previous errors For more information about this error, try `rustc --explain E0277`. From 3631ab523e97345a416e2e3a1ebcaf56f12794ae Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Tue, 14 Apr 2020 00:41:15 +0200 Subject: [PATCH 12/28] Allow `_` on the LHS of an assignment to discard a value --- compiler/rustc_ast_lowering/src/expr.rs | 9 +++++++-- .../nested_destructure.rs | 3 +++ .../slice_destructure.rs | 2 ++ .../slice_destructure_fail.rs | 1 + .../slice_destructure_fail.stderr | 8 +++++++- .../struct_destructure.rs | 6 +++++- .../struct_destructure_fail.rs | 2 ++ .../struct_destructure_fail.stderr | 19 +++++++++++++++++-- .../tuple_destructure.rs | 2 ++ .../tuple_destructure_fail.rs | 1 + .../tuple_destructure_fail.stderr | 13 ++++++++++++- .../tuple_struct_destructure.rs | 2 ++ .../tuple_struct_destructure_fail.rs | 4 +++- .../tuple_struct_destructure_fail.stderr | 13 +++++++++++-- 14 files changed, 75 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index ae94eb51f7cc6..791fad7e36a8c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -871,7 +871,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::ExprKind<'hir> { // Return early in case of an ordinary assignment. let is_ordinary = match &lhs.kind { - ExprKind::Array(..) | ExprKind::Struct(_, _, None) | ExprKind::Tup(..) => false, + ExprKind::Array(..) + | ExprKind::Struct(_, _, None) + | ExprKind::Tup(..) + | ExprKind::Underscore => false, ExprKind::Call(callee, ..) => { // Check for tuple struct constructor. if let ExprKind::Path(qself, path) = &callee.kind { @@ -931,8 +934,10 @@ impl<'hir> LoweringContext<'_, 'hir> { eq_sign_span: Span, assignments: &mut Vec>, ) -> &'hir hir::Pat<'hir> { - // FIXME: Handle `_`, requires changes to the parser match &lhs.kind { + ExprKind::Underscore => { + return self.pat(lhs.span, hir::PatKind::Wild); + } // slices: ExprKind::Array(elements) => { let (pats, rest) = diff --git a/src/test/ui/destructuring-assignment/nested_destructure.rs b/src/test/ui/destructuring-assignment/nested_destructure.rs index 393dfc16c0a1c..0d45ff7da7249 100644 --- a/src/test/ui/destructuring-assignment/nested_destructure.rs +++ b/src/test/ui/destructuring-assignment/nested_destructure.rs @@ -14,4 +14,7 @@ fn main() { Struct { a: TupleStruct((a, b), c), b: [d] } = Struct { a: TupleStruct((0, 1), 2), b: [3] }; assert_eq!((a, b, c, d), (0, 1, 2, 3)); + + // unnested underscore: just discard + _ = 1; } diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs index 630f16be1a1c4..789feb1c2baaf 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -8,4 +8,6 @@ fn main() { assert_eq!((a,b), (0,1)); [a, .., b] = [1,2]; assert_eq!((a,b), (1,2)); + [_, a] = [1,2]; + assert_eq!((a,b), (2,2)); } diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs index 6efea32175f62..b5d86fa0030ac 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs @@ -4,4 +4,5 @@ fn main() { let (mut a, mut b); [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern [a, a, b] = [1,2]; //~ ERROR pattern requires 3 elements but array has 2 + [_] = [1,2]; //~ ERROR pattern requires 1 element but array has 2 } diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr index 596ff23c13d0c..98d06868ce1ec 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -12,6 +12,12 @@ error[E0527]: pattern requires 3 elements but array has 2 LL | [a, a, b] = [1,2]; | ^^^^^^^^^ expected 2 elements -error: aborting due to 2 previous errors +error[E0527]: pattern requires 1 element but array has 2 + --> $DIR/slice_destructure_fail.rs:7:3 + | +LL | [_] = [1,2]; + | ^^^ expected 2 elements + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0527`. diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs index bb1c12455c4bf..ae7e956cba6c4 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -7,7 +7,11 @@ struct Struct { } fn main() { - let (a, b); + let (mut a, mut b); Struct { a, b } = Struct { a: 0, b: 1 }; assert_eq!((a, b), (0, 1)); + Struct { a: b, b: a } = Struct { a: 1, b: 2}; + assert_eq!((a,b), (2,1)); + Struct { a: _, b } = Struct { a: 1, b: 2 }; + assert_eq!((a,b), (2,2)); } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs index b3261f054a890..2bfb122ff6921 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -8,4 +8,6 @@ fn main() { let (mut a, b); let mut c; Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c` + Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b` + //~| ERROR expected identifier, found reserved identifier `_` } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index c4099574d01b4..b7f6d87acfdb0 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -1,9 +1,24 @@ +error: expected identifier, found reserved identifier `_` + --> $DIR/struct_destructure_fail.rs:11:17 + | +LL | Struct { a, _ } = Struct { a: 1, b: 2 }; + | ------ ^ expected identifier, found reserved identifier + | | + | while parsing this struct + error[E0026]: struct `Struct` does not have a field named `c` --> $DIR/struct_destructure_fail.rs:10:20 | LL | Struct { a, b, c } = Struct { a: 0, b: 1 }; | ^ struct `Struct` does not have this field -error: aborting due to previous error +error[E0027]: pattern does not mention field `b` + --> $DIR/struct_destructure_fail.rs:11:5 + | +LL | Struct { a, _ } = Struct { a: 1, b: 2 }; + | ^^^^^^^^^^^^^^^ missing field `b` + +error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0026`. +Some errors have detailed explanations: E0026, E0027. +For more information about an error, try `rustc --explain E0026`. diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs index 297fae83592c2..80323cfa606c2 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -8,4 +8,6 @@ fn main() { assert_eq!((a,b), (0,1)); (a, .., b) = (1,2); assert_eq!((a,b), (1,2)); + (_, a) = (1,2); + assert_eq!((a,b), (2,2)); } diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs index 975e5b6e5cac0..a2b22c29ca086 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs @@ -4,4 +4,5 @@ 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 + (_,) = (1,2); //~ ERROR mismatched types } diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr index 1b750ab9cd74d..a117b6145e34a 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -17,6 +17,17 @@ LL | (a, a, b) = (1,2); = note: expected tuple `({integer}, {integer})` found tuple `(_, _, _)` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/tuple_destructure_fail.rs:7:5 + | +LL | (_,) = (1,2); + | ^^^^ ----- this expression has type `({integer}, {integer})` + | | + | expected a tuple with 2 elements, found one with 1 element + | + = note: expected tuple `({integer}, {integer})` + found tuple `(_,)` + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs index 5dc93c4022618..6f0af6b781d58 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs @@ -10,4 +10,6 @@ fn main() { assert_eq!((a,b), (0,1)); TupleStruct(a, .., b) = TupleStruct(1,2); assert_eq!((a,b), (1,2)); + TupleStruct(_, a) = TupleStruct(2,2); + assert_eq!((a,b), (2,2)); } diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs index f855323f26944..2f71a4d479d89 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs @@ -9,7 +9,9 @@ fn main() { TupleStruct(a, a, b) = TupleStruct(1,2); //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields // Check if `test` is recognized as not a tuple struct but a function call: - test() = TupleStruct(0,0) //~ ERROR invalid left-hand side of assignment + test() = TupleStruct(0,0); //~ ERROR invalid left-hand side of assignment + TupleStruct(_) = TupleStruct(1,2); + //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields } fn test() -> TupleStruct { diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr index 7c6a12ac75937..9bf6a9cc29167 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -18,12 +18,21 @@ LL | TupleStruct(a, a, b) = TupleStruct(1,2); error[E0070]: invalid left-hand side of assignment --> $DIR/tuple_struct_destructure_fail.rs:12:12 | -LL | test() = TupleStruct(0,0) +LL | test() = TupleStruct(0,0); | ------ ^ | | | cannot assign to this expression -error: aborting due to 3 previous errors +error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 fields + --> $DIR/tuple_struct_destructure_fail.rs:13:5 + | +LL | struct TupleStruct(S, T); + | ------------------------------- tuple struct defined here +... +LL | TupleStruct(_) = TupleStruct(1,2); + | ^^^^^^^^^^^^^^ expected 2 fields, found 1 + +error: aborting due to 4 previous errors Some errors have detailed explanations: E0023, E0070. For more information about an error, try `rustc --explain E0023`. From 1f65f5d78fb25cc0785003182c713c6b9586083f Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Tue, 14 Apr 2020 02:48:36 +0200 Subject: [PATCH 13/28] Allow `..` in struct expressions on the LHS of assignments --- compiler/rustc_ast_lowering/src/expr.rs | 26 ++++++++++++++++--- compiler/rustc_parse/src/parser/expr.rs | 10 +++++++ .../note-unsupported.rs | 3 ++- .../note-unsupported.stderr | 17 +++++++++--- .../struct_destructure.rs | 2 ++ .../struct_destructure_fail.rs | 2 ++ .../struct_destructure_fail.stderr | 14 +++++++--- 7 files changed, 61 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 791fad7e36a8c..0785c09a6970c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -872,7 +872,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Return early in case of an ordinary assignment. let is_ordinary = match &lhs.kind { ExprKind::Array(..) - | ExprKind::Struct(_, _, None) + | ExprKind::Struct(..) | ExprKind::Tup(..) | ExprKind::Underscore => false, ExprKind::Call(callee, ..) => { @@ -980,8 +980,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } // structs: - // FIXME: support `..` here, requires changes to the parser - ExprKind::Struct(path, fields, None) => { + ExprKind::Struct(path, fields, base) => { let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); hir::FieldPat { @@ -999,7 +998,26 @@ impl<'hir> LoweringContext<'_, 'hir> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - let struct_pat = hir::PatKind::Struct(qpath, field_pats, false); + let fields_omitted = match base { + None => false, + Some(e) => { + match e.kind { + ExprKind::Underscore => {} + _ => self + .sess + .struct_span_err(e.span, "base expression not allowed here") + .span_suggestion( + e.span, + "consider removing this", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit(), + } + true + } + }; + let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); return self.pat(lhs.span, struct_pat); } // tuples: diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index ccdb32fc3d2e0..078f343e72a97 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2104,6 +2104,16 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; + // If the struct ends in `.. }`, treat it like `.. _ }`. + // AST lowering will then report an error if it's not on the LHS of an assignment. + if self.token == token::CloseDelim(token::Brace) { + base = Some(self.mk_expr( + self.prev_token.span, + ExprKind::Underscore, + AttrVec::new(), + )); + break; + } match self.parse_expr() { Ok(e) => base = Some(e), Err(mut e) if recover => { diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index fd1f3afa549de..394157531aa8f 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -17,7 +17,8 @@ fn main() { S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment //~| 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 base expression not allowed + //~| ERROR destructuring assignments are unstable let c = 3; diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index 3c87226d06817..749f97e6c8614 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -1,3 +1,9 @@ +error: base expression not allowed here + --> $DIR/note-unsupported.rs:20:17 + | +LL | S { x: a, ..s } = S { x: 3, y: 4 }; + | ^ help: consider removing this + error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:6:12 | @@ -81,16 +87,19 @@ LL | S { x: a, y: b } += s; | | | cannot assign to this expression -error[E0070]: invalid left-hand side of assignment +error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:20:21 | LL | S { x: a, ..s } = S { x: 3, y: 4 }; | --------------- ^ | | | cannot assign to this expression + | + = note: see issue #372 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0658]: destructuring assignments are unstable - --> $DIR/note-unsupported.rs:24:17 + --> $DIR/note-unsupported.rs:25:17 | LL | ((a, b), c) = ((3, 4), 5); | ----------- ^ @@ -100,7 +109,7 @@ LL | ((a, b), c) = ((3, 4), 5); = note: see issue #372 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable -error: aborting due to 11 previous errors +error: aborting due to 12 previous errors -Some errors have detailed explanations: E0067, E0070, E0368, E0658. +Some errors have detailed explanations: E0067, E0368, E0658. For more information about an error, try `rustc --explain E0067`. diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs index ae7e956cba6c4..fcc3b019a6dff 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -14,4 +14,6 @@ fn main() { assert_eq!((a,b), (2,1)); Struct { a: _, b } = Struct { a: 1, b: 2 }; assert_eq!((a,b), (2,2)); + Struct { a, .. } = Struct { a: 1, b: 3}; + assert_eq!((a,b), (1,2)); } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs index 2bfb122ff6921..ed4f799252905 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -7,7 +7,9 @@ struct Struct { fn main() { let (mut a, b); let mut c; + let d = Struct { a: 0, b: 1 }; Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c` Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b` //~| ERROR expected identifier, found reserved identifier `_` + Struct { a, ..d } = Struct { a: 1, b: 2 }; //~ ERROR base expression not allowed here } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index b7f6d87acfdb0..d141bdbae54ef 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -1,24 +1,30 @@ error: expected identifier, found reserved identifier `_` - --> $DIR/struct_destructure_fail.rs:11:17 + --> $DIR/struct_destructure_fail.rs:12:17 | LL | Struct { a, _ } = Struct { a: 1, b: 2 }; | ------ ^ expected identifier, found reserved identifier | | | while parsing this struct +error: base expression not allowed here + --> $DIR/struct_destructure_fail.rs:14:19 + | +LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; + | ^ help: consider removing this + error[E0026]: struct `Struct` does not have a field named `c` - --> $DIR/struct_destructure_fail.rs:10:20 + --> $DIR/struct_destructure_fail.rs:11:20 | LL | Struct { a, b, c } = Struct { a: 0, b: 1 }; | ^ struct `Struct` does not have this field error[E0027]: pattern does not mention field `b` - --> $DIR/struct_destructure_fail.rs:11:5 + --> $DIR/struct_destructure_fail.rs:12:5 | LL | Struct { a, _ } = Struct { a: 1, b: 2 }; | ^^^^^^^^^^^^^^^ missing field `b` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0026, E0027. For more information about an error, try `rustc --explain E0026`. From 2415a6411a21fcab4c0ce895a1766082f525af6e Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Tue, 14 Apr 2020 10:44:45 +0200 Subject: [PATCH 14/28] Improve error message for struct expression missing a base expression after `..` --- compiler/rustc_parse/src/parser/expr.rs | 2 +- .../destructuring-assignment/struct_destructure_fail.rs | 1 + .../struct_destructure_fail.stderr | 8 +++++++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 078f343e72a97..f6ccca0eb8894 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2108,7 +2108,7 @@ impl<'a> Parser<'a> { // AST lowering will then report an error if it's not on the LHS of an assignment. if self.token == token::CloseDelim(token::Brace) { base = Some(self.mk_expr( - self.prev_token.span, + self.prev_token.span.shrink_to_hi(), ExprKind::Underscore, AttrVec::new(), )); diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs index ed4f799252905..e31a545837f43 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -12,4 +12,5 @@ fn main() { Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b` //~| ERROR expected identifier, found reserved identifier `_` Struct { a, ..d } = Struct { a: 1, b: 2 }; //~ ERROR base expression not allowed here + Struct { a, .. }; //~ ERROR base expression required after `..` } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index d141bdbae54ef..34c26eb383c10 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -12,6 +12,12 @@ error: base expression not allowed here LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; | ^ help: consider removing this +error: base expression required after `..` + --> $DIR/struct_destructure_fail.rs:15:19 + | +LL | Struct { a, .. }; + | ^ add the base expression here + error[E0026]: struct `Struct` does not have a field named `c` --> $DIR/struct_destructure_fail.rs:11:20 | @@ -24,7 +30,7 @@ error[E0027]: pattern does not mention field `b` LL | Struct { a, _ } = Struct { a: 1, b: 2 }; | ^^^^^^^^^^^^^^^ missing field `b` -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors Some errors have detailed explanations: E0026, E0027. For more information about an error, try `rustc --explain E0026`. From 9b387509806310290c897f0ab5d8e98c853443b2 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 14 Apr 2020 15:55:06 +0100 Subject: [PATCH 15/28] Adjust comments and diagnostics --- compiler/rustc_ast/src/ast.rs | 4 +-- compiler/rustc_ast_lowering/src/expr.rs | 32 +++++++++++-------- compiler/rustc_feature/src/active.rs | 2 ++ compiler/rustc_hir/src/hir.rs | 4 +-- .../note-unsupported.rs | 3 +- .../note-unsupported.stderr | 6 ++-- .../slice_destructure.rs | 12 ++++--- .../slice_destructure_fail.rs | 4 +-- .../slice_destructure_fail.stderr | 4 +-- .../struct_destructure.rs | 12 ++++--- .../struct_destructure_fail.rs | 3 +- .../struct_destructure_fail.stderr | 6 ++-- .../tuple_destructure.rs | 15 ++++++--- .../tuple_destructure_fail.rs | 4 +-- .../tuple_destructure_fail.stderr | 8 ++--- .../tuple_struct_destructure.rs | 12 ++++--- .../tuple_struct_destructure_fail.rs | 6 ++-- .../tuple_struct_destructure_fail.stderr | 6 ++-- .../warn-unused-duplication.rs | 13 ++++++++ .../warn-unused-duplication.stderr | 13 ++++++++ 20 files changed, 108 insertions(+), 61 deletions(-) create mode 100644 src/test/ui/destructuring-assignment/warn-unused-duplication.rs create mode 100644 src/test/ui/destructuring-assignment/warn-unused-duplication.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 198866f73388b..9702def6e4d7c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1313,9 +1313,9 @@ pub enum ExprKind { Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). Index(P, P), - /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`). + /// A range (e.g., `1..2`, `1..`, `..2`, `1..=2`, `..=2`; and `..` in destructuring assingment). Range(Option>, Option>, RangeLimits), - /// An underscore. + /// An underscore, used in destructuring assignment to ignore a value. Underscore, /// Variable reference, possibly containing `::` and/or type diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 0785c09a6970c..af9f337a7df2c 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -861,7 +861,7 @@ 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; }`. + /// For instance, lower `(a, b) = t` to `{ let (lhs1, lhs2) = t; a = lhs1; b = lhs2; }`. fn lower_expr_assign( &mut self, lhs: &Expr, @@ -902,13 +902,13 @@ impl<'hir> LoweringContext<'_, 'hir> { return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span); } - let mut assignments = Vec::new(); + let mut assignments = vec![]; - // The LHS becomes a pattern: `(lhs1, lhs2)` + // 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`. + // Introduce a `let` for destructuring: `let (lhs1, lhs2) = t`. let destructure_let = self.stmt_let_pat( ThinVec::new(), whole_span, @@ -927,7 +927,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } /// Convert the LHS of a destructuring assignment to a pattern. - /// Along the way, introduce additional assignments in the parameter assignments. + /// Each sub-assignment is recorded in `assignments`. fn destructure_assign( &mut self, lhs: &Expr, @@ -935,10 +935,11 @@ impl<'hir> LoweringContext<'_, 'hir> { assignments: &mut Vec>, ) -> &'hir hir::Pat<'hir> { match &lhs.kind { + // Underscore pattern. ExprKind::Underscore => { return self.pat(lhs.span, hir::PatKind::Wild); } - // slices: + // Slice patterns. ExprKind::Array(elements) => { let (pats, rest) = self.destructure_sequence(elements, "slice", eq_sign_span, assignments); @@ -950,7 +951,7 @@ impl<'hir> LoweringContext<'_, 'hir> { }; return self.pat(lhs.span, slice_pat); } - // tuple structs: + // Tuple structs. ExprKind::Call(callee, args) => { if let ExprKind::Path(qself, path) = &callee.kind { let (pats, rest) = @@ -979,7 +980,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } } - // structs: + // Structs. ExprKind::Struct(path, fields, base) => { let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); @@ -1005,10 +1006,14 @@ impl<'hir> LoweringContext<'_, 'hir> { ExprKind::Underscore => {} _ => self .sess - .struct_span_err(e.span, "base expression not allowed here") + .struct_span_err( + e.span, + "functional record updates are not allowed in destructuring \ + assignments", + ) .span_suggestion( e.span, - "consider removing this", + "consider removing the trailing pattern", String::new(), rustc_errors::Applicability::MachineApplicable, ) @@ -1020,7 +1025,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); return self.pat(lhs.span, struct_pat); } - // tuples: + // Tuples. ExprKind::Tup(elements) => { let (pats, rest) = self.destructure_sequence(elements, "tuple", eq_sign_span, assignments); @@ -1041,8 +1046,9 @@ impl<'hir> LoweringContext<'_, 'hir> { /// 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 span of `..`, if any. - /// Along the way, introduce additional assignments in the parameter `assignments`. + /// 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], diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 8818ee2737729..e4071e182dfd5 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -611,6 +611,8 @@ declare_features! ( (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows the use of destructuring assignments. + /// The current issue reference refers to the rust-lang/rfcs issue. This will be changed to a + /// valid rust-lang/rust issue when the accompanying RFC is accepted. (active, destructuring_assignment, "1.49.0", Some(372), None), // ------------------------------------------------------------------------- diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index e90620f4e7299..b5f652f30fbb5 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1680,8 +1680,8 @@ pub enum LocalSource { AsyncFn, /// A desugared `.await`. AwaitDesugar, - /// A desugared expr = expr where the LHS is a tuple, struct or array. - /// The span is for the `=` sign. + /// A desugared `expr = expr`, where the LHS is a tuple, struct or array. + /// The span is that of the `=` sign. AssignDesugar(Span), } diff --git a/src/test/ui/destructuring-assignment/note-unsupported.rs b/src/test/ui/destructuring-assignment/note-unsupported.rs index 394157531aa8f..249fba7f920bc 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.rs +++ b/src/test/ui/destructuring-assignment/note-unsupported.rs @@ -17,7 +17,8 @@ fn main() { S { x: a, y: b } += s; //~ ERROR invalid left-hand side of assignment //~| ERROR binary assignment operation `+=` cannot be applied - S { x: a, ..s } = S { x: 3, y: 4 }; //~ ERROR base expression not allowed + S { x: a, ..s } = S { x: 3, y: 4 }; + //~^ ERROR functional record updates are not allowed in destructuring assignments //~| ERROR destructuring assignments are unstable let c = 3; diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index 749f97e6c8614..bcf7c5d29189a 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -1,8 +1,8 @@ -error: base expression not allowed here +error: functional record updates are not allowed in destructuring assignments --> $DIR/note-unsupported.rs:20:17 | LL | S { x: a, ..s } = S { x: 3, y: 4 }; - | ^ help: consider removing this + | ^ help: consider removing the trailing pattern error[E0658]: destructuring assignments are unstable --> $DIR/note-unsupported.rs:6:12 @@ -99,7 +99,7 @@ LL | S { x: a, ..s } = S { x: 3, y: 4 }; = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0658]: destructuring assignments are unstable - --> $DIR/note-unsupported.rs:25:17 + --> $DIR/note-unsupported.rs:26:17 | LL | ((a, b), c) = ((3, 4), 5); | ----------- ^ diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs index 789feb1c2baaf..1dbc720b435fd 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -5,9 +5,11 @@ fn main() { let (mut a, mut b); [a, b] = [0, 1]; - assert_eq!((a,b), (0,1)); - [a, .., b] = [1,2]; - assert_eq!((a,b), (1,2)); - [_, a] = [1,2]; - assert_eq!((a,b), (2,2)); + assert_eq!((a, b), (0, 1)); + let c; + [a, .., b, c] = [1, 2, 3, 4, 5]; + assert_eq!((a, b, c), (1, 4, 5)); + [_, a, _] = [1, 2, 3]; + assert_eq!((a, b), (2, 4)); + [..] = [1, 2, 3]; } diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs index b5d86fa0030ac..90d93892f7f22 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.rs @@ -3,6 +3,6 @@ fn main() { let (mut a, mut b); [a, .., b, ..] = [0, 1]; //~ ERROR `..` can only be used once per slice pattern - [a, a, b] = [1,2]; //~ ERROR pattern requires 3 elements but array has 2 - [_] = [1,2]; //~ ERROR pattern requires 1 element but array has 2 + [a, a, b] = [1, 2]; //~ ERROR pattern requires 3 elements but array has 2 + [_] = [1, 2]; //~ ERROR pattern requires 1 element but array has 2 } diff --git a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr index 98d06868ce1ec..cc412c72df51d 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/slice_destructure_fail.stderr @@ -9,13 +9,13 @@ LL | [a, .., b, ..] = [0, 1]; error[E0527]: pattern requires 3 elements but array has 2 --> $DIR/slice_destructure_fail.rs:6:3 | -LL | [a, a, b] = [1,2]; +LL | [a, a, b] = [1, 2]; | ^^^^^^^^^ expected 2 elements error[E0527]: pattern requires 1 element but array has 2 --> $DIR/slice_destructure_fail.rs:7:3 | -LL | [_] = [1,2]; +LL | [_] = [1, 2]; | ^^^ expected 2 elements error: aborting due to 3 previous errors diff --git a/src/test/ui/destructuring-assignment/struct_destructure.rs b/src/test/ui/destructuring-assignment/struct_destructure.rs index fcc3b019a6dff..2bcbd9d0d742e 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure.rs @@ -10,10 +10,12 @@ fn main() { let (mut a, mut b); Struct { a, b } = Struct { a: 0, b: 1 }; assert_eq!((a, b), (0, 1)); - Struct { a: b, b: a } = Struct { a: 1, b: 2}; - assert_eq!((a,b), (2,1)); + Struct { a: b, b: a } = Struct { a: 1, b: 2 }; + assert_eq!((a,b), (2, 1)); Struct { a: _, b } = Struct { a: 1, b: 2 }; - assert_eq!((a,b), (2,2)); - Struct { a, .. } = Struct { a: 1, b: 3}; - assert_eq!((a,b), (1,2)); + assert_eq!((a, b), (2, 2)); + Struct { a, .. } = Struct { a: 1, b: 3 }; + assert_eq!((a, b), (1, 2)); + Struct { .. } = Struct { a: 1, b: 4 }; + assert_eq!((a, b), (1, 2)); } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs index e31a545837f43..4aa327b61f497 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs @@ -11,6 +11,7 @@ fn main() { Struct { a, b, c } = Struct { a: 0, b: 1 }; //~ ERROR does not have a field named `c` Struct { a, _ } = Struct { a: 1, b: 2 }; //~ ERROR pattern does not mention field `b` //~| ERROR expected identifier, found reserved identifier `_` - Struct { a, ..d } = Struct { a: 1, b: 2 }; //~ ERROR base expression not allowed here + Struct { a, ..d } = Struct { a: 1, b: 2 }; + //~^ ERROR functional record updates are not allowed in destructuring assignments Struct { a, .. }; //~ ERROR base expression required after `..` } diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index 34c26eb383c10..30e435bbda6ce 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -6,14 +6,14 @@ LL | Struct { a, _ } = Struct { a: 1, b: 2 }; | | | while parsing this struct -error: base expression not allowed here +error: functional record updates are not allowed in destructuring assignments --> $DIR/struct_destructure_fail.rs:14:19 | LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; - | ^ help: consider removing this + | ^ help: consider removing the trailing expression error: base expression required after `..` - --> $DIR/struct_destructure_fail.rs:15:19 + --> $DIR/struct_destructure_fail.rs:16:19 | LL | Struct { a, .. }; | ^ add the base expression here diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs index 80323cfa606c2..748144e9f2b9e 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -5,9 +5,14 @@ fn main() { let (mut a, mut b); (a, b) = (0, 1); - assert_eq!((a,b), (0,1)); - (a, .., b) = (1,2); - assert_eq!((a,b), (1,2)); - (_, a) = (1,2); - assert_eq!((a,b), (2,2)); + 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)); + // The following currently does not work, but should. + // (..) = (3, 4); + // assert_eq!((a, b), (2, 2)); } diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs index a2b22c29ca086..a98d73b632da4 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.rs @@ -3,6 +3,6 @@ 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 - (_,) = (1,2); //~ ERROR mismatched types + (a, a, b) = (1, 2); //~ ERROR mismatched types + (_,) = (1, 2); //~ ERROR mismatched types } diff --git a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr index a117b6145e34a..2ebe6b3f1968d 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -9,8 +9,8 @@ LL | (a, .., b, ..) = (0, 1); error[E0308]: mismatched types --> $DIR/tuple_destructure_fail.rs:6:5 | -LL | (a, a, b) = (1,2); - | ^^^^^^^^^ ----- this expression has type `({integer}, {integer})` +LL | (a, a, b) = (1, 2); + | ^^^^^^^^^ ------ this expression has type `({integer}, {integer})` | | | expected a tuple with 2 elements, found one with 3 elements | @@ -20,8 +20,8 @@ LL | (a, a, b) = (1,2); error[E0308]: mismatched types --> $DIR/tuple_destructure_fail.rs:7:5 | -LL | (_,) = (1,2); - | ^^^^ ----- this expression has type `({integer}, {integer})` +LL | (_,) = (1, 2); + | ^^^^ ------ this expression has type `({integer}, {integer})` | | | expected a tuple with 2 elements, found one with 1 element | diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs index 6f0af6b781d58..1cbdd3cca931f 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs @@ -7,9 +7,11 @@ struct TupleStruct(S, T); fn main() { let (mut a, mut b); TupleStruct(a, b) = TupleStruct(0, 1); - assert_eq!((a,b), (0,1)); - TupleStruct(a, .., b) = TupleStruct(1,2); - assert_eq!((a,b), (1,2)); - TupleStruct(_, a) = TupleStruct(2,2); - assert_eq!((a,b), (2,2)); + assert_eq!((a, b), (0, 1)); + TupleStruct(a, .., b) = TupleStruct(1, 2); + assert_eq!((a, b), (1, 2)); + TupleStruct(_, a) = TupleStruct(2, 2); + assert_eq!((a, b), (2, 2)); + TupleStruct(..) = TupleStruct(3, 4); + assert_eq!((a, b), (2, 2)); } diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs index 2f71a4d479d89..3b8a491d8dea9 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs @@ -6,11 +6,11 @@ fn main() { let (mut a, mut b); TupleStruct(a, .., b, ..) = TupleStruct(0, 1); //~^ ERROR `..` can only be used once per tuple struct pattern - TupleStruct(a, a, b) = TupleStruct(1,2); + TupleStruct(a, a, b) = TupleStruct(1, 2); //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields // Check if `test` is recognized as not a tuple struct but a function call: - test() = TupleStruct(0,0); //~ ERROR invalid left-hand side of assignment - TupleStruct(_) = TupleStruct(1,2); + test() = TupleStruct(0, 0); //~ ERROR invalid left-hand side of assignment + TupleStruct(_) = TupleStruct(1, 2); //~^ ERROR this pattern has 1 field, but the corresponding tuple struct has 2 fields } diff --git a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr index 9bf6a9cc29167..e50191a40d93c 100644 --- a/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr @@ -12,13 +12,13 @@ error[E0023]: this pattern has 3 fields, but the corresponding tuple struct has LL | struct TupleStruct(S, T); | ------------------------------- tuple struct defined here ... -LL | TupleStruct(a, a, b) = TupleStruct(1,2); +LL | TupleStruct(a, a, b) = TupleStruct(1, 2); | ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3 error[E0070]: invalid left-hand side of assignment --> $DIR/tuple_struct_destructure_fail.rs:12:12 | -LL | test() = TupleStruct(0,0); +LL | test() = TupleStruct(0, 0); | ------ ^ | | | cannot assign to this expression @@ -29,7 +29,7 @@ error[E0023]: this pattern has 1 field, but the corresponding tuple struct has 2 LL | struct TupleStruct(S, T); | ------------------------------- tuple struct defined here ... -LL | TupleStruct(_) = TupleStruct(1,2); +LL | TupleStruct(_) = TupleStruct(1, 2); | ^^^^^^^^^^^^^^ expected 2 fields, found 1 error: aborting due to 4 previous errors 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..f99a202e937ba --- /dev/null +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs @@ -0,0 +1,13 @@ +// 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); +} 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..29de4fd300aa3 --- /dev/null +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr @@ -0,0 +1,13 @@ +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? + From 1841f2d003d710c2c04fdabe2f3ba12610e7b0f3 Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 14 Apr 2020 16:00:51 +0100 Subject: [PATCH 16/28] Update feature gate issue --- compiler/rustc_feature/src/active.rs | 4 +--- src/test/ui/bad/bad-expr-lhs.stderr | 4 ++-- .../destructuring-assignment/note-unsupported.stderr | 10 +++++----- .../feature-gate-destructuring_assignment.stderr | 2 +- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index e4071e182dfd5..84114fc773533 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -611,9 +611,7 @@ declare_features! ( (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows the use of destructuring assignments. - /// The current issue reference refers to the rust-lang/rfcs issue. This will be changed to a - /// valid rust-lang/rust issue when the accompanying RFC is accepted. - (active, destructuring_assignment, "1.49.0", Some(372), None), + (active, destructuring_assignment, "1.49.0", Some(71126), None), // ------------------------------------------------------------------------- // feature-group-end: actual feature gates diff --git a/src/test/ui/bad/bad-expr-lhs.stderr b/src/test/ui/bad/bad-expr-lhs.stderr index cb3c12384d16f..584e77d0b8794 100644 --- a/src/test/ui/bad/bad-expr-lhs.stderr +++ b/src/test/ui/bad/bad-expr-lhs.stderr @@ -22,7 +22,7 @@ LL | (1, 2) = (3, 4); | | | cannot assign to this expression | - = note: see issue #372 for more information + = 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 @@ -49,7 +49,7 @@ LL | (a, b) = (3, 4); | | | cannot assign to this expression | - = note: see issue #372 for more information + = 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 diff --git a/src/test/ui/destructuring-assignment/note-unsupported.stderr b/src/test/ui/destructuring-assignment/note-unsupported.stderr index bcf7c5d29189a..e8a38a910c500 100644 --- a/src/test/ui/destructuring-assignment/note-unsupported.stderr +++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr @@ -12,7 +12,7 @@ LL | (a, b) = (3, 4); | | | cannot assign to this expression | - = note: see issue #372 for more information + = 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})` @@ -39,7 +39,7 @@ LL | [a, b] = [3, 4]; | | | cannot assign to this expression | - = note: see issue #372 for more information + = 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}; 2]` @@ -66,7 +66,7 @@ LL | S { x: a, y: b } = s; | | | cannot assign to this expression | - = note: see issue #372 for more information + = 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 `S` @@ -95,7 +95,7 @@ LL | S { x: a, ..s } = S { x: 3, y: 4 }; | | | cannot assign to this expression | - = note: see issue #372 for more information + = note: see issue #71126 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0658]: destructuring assignments are unstable @@ -106,7 +106,7 @@ LL | ((a, b), c) = ((3, 4), 5); | | | cannot assign to this expression | - = note: see issue #372 for more information + = note: see issue #71126 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error: aborting due to 12 previous errors diff --git a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr index a890dc6e3c6c9..62e71decb32a0 100644 --- a/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr +++ b/src/test/ui/feature-gates/feature-gate-destructuring_assignment.stderr @@ -6,7 +6,7 @@ LL | (a, b) = (2, 3); | | | cannot assign to this expression | - = note: see issue #372 for more information + = note: see issue #71126 for more information = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error: aborting due to previous error From 76bcd89154b46f7e29dc3bf21979db7d9e09d14f Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 14 Apr 2020 16:37:53 +0100 Subject: [PATCH 17/28] Support `(..)` --- compiler/rustc_ast_lowering/src/expr.rs | 15 +++++++++++++++ .../destructuring-assignment/tuple_destructure.rs | 5 ++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index af9f337a7df2c..eb45ef622c9d8 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -896,6 +896,14 @@ impl<'hir> LoweringContext<'_, 'hir> { true } } + // `(..)`. + ExprKind::Paren(e) => { + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + false + } else { + true + } + } _ => true, }; if is_ordinary { @@ -1032,6 +1040,13 @@ impl<'hir> LoweringContext<'_, 'hir> { let tuple_pat = hir::PatKind::Tuple(pats, rest.map(|r| r.0)); return self.pat(lhs.span, tuple_pat); } + // `(..)`. We special-case this for consistency with declarations. + ExprKind::Paren(e) => { + if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { + let tuple_pat = hir::PatKind::Tuple(&[], Some(0)); + return self.pat(lhs.span, tuple_pat); + } + } _ => {} } // Treat all other cases as normal lvalue. diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs index 748144e9f2b9e..7972ba6057a73 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -12,7 +12,6 @@ fn main() { assert_eq!((a, b), (1, 2)); (_, a) = (1, 2); assert_eq!((a, b), (2, 2)); - // The following currently does not work, but should. - // (..) = (3, 4); - // assert_eq!((a, b), (2, 2)); + (..) = (3, 4); + assert_eq!((a, b), (2, 2)); } From 6b18424710acbe69172be00ce721caf254f1809b Mon Sep 17 00:00:00 2001 From: varkor Date: Tue, 14 Apr 2020 17:04:06 +0100 Subject: [PATCH 18/28] Add more complex example --- .../warn-unused-duplication.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.rs b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs index f99a202e937ba..c1c5c2cd3cebb 100644 --- a/src/test/ui/destructuring-assignment/warn-unused-duplication.rs +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.rs @@ -10,4 +10,14 @@ fn main() { // 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 } From ced8ce43f60047300f54158a9971c859d77f3a06 Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Wed, 15 Apr 2020 00:58:53 +0200 Subject: [PATCH 19/28] Fix tests --- .../ui/destructuring-assignment/struct_destructure_fail.stderr | 2 +- .../ui/destructuring-assignment/warn-unused-duplication.stderr | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index 30e435bbda6ce..316dd01b7c148 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -10,7 +10,7 @@ error: functional record updates are not allowed in destructuring assignments --> $DIR/struct_destructure_fail.rs:14:19 | LL | Struct { a, ..d } = Struct { a: 1, b: 2 }; - | ^ help: consider removing the trailing expression + | ^ help: consider removing the trailing pattern error: base expression required after `..` --> $DIR/struct_destructure_fail.rs:16:19 diff --git a/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr index 29de4fd300aa3..b87ef6f1571c1 100644 --- a/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr +++ b/src/test/ui/destructuring-assignment/warn-unused-duplication.stderr @@ -11,3 +11,5 @@ LL | #![warn(unused_assignments)] | ^^^^^^^^^^^^^^^^^^ = help: maybe it is overwritten before being read? +warning: 1 warning emitted + From fb02d9971cd2847d3922967e08e710c6c4b1ecec Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 15 Apr 2020 02:09:41 +0100 Subject: [PATCH 20/28] Fix handling of rest without base in LHS --- compiler/rustc_ast/src/ast.rs | 15 +++++-- compiler/rustc_ast/src/mut_visit.rs | 4 +- compiler/rustc_ast/src/visit.rs | 4 +- compiler/rustc_ast_lowering/src/expr.rs | 39 +++++++++---------- compiler/rustc_ast_pretty/src/pprust/state.rs | 21 +++++----- compiler/rustc_expand/src/build.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 13 ++----- .../rustc_save_analysis/src/dump_visitor.rs | 10 +++-- .../struct_destructure_fail.stderr | 2 +- 9 files changed, 58 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 9702def6e4d7c..f08bea128005c 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1219,6 +1219,16 @@ pub enum RangeLimits { Closed, } +#[derive(Clone, Encodable, Decodable, Debug)] +pub enum StructRest { + /// `..x`. + Base(P), + /// `..`. + Rest(Span), + /// No trailing `..` or expression. + None, +} + #[derive(Clone, Encodable, Decodable, Debug)] pub enum ExprKind { /// A `box x` expression. @@ -1343,9 +1353,8 @@ pub enum ExprKind { /// A struct literal expression. /// - /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. base}`, - /// where `base` is the `Option`. - Struct(Path, Vec, Option>), + /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`. + Struct(Path, Vec, StructRest), /// An array literal constructed from one repeated element. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index e951b60daea8d..4e9284e6cf815 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1275,7 +1275,9 @@ pub fn noop_visit_expr( ExprKind::Struct(path, fields, expr) => { vis.visit_path(path); fields.flat_map_in_place(|field| vis.flat_map_field(field)); - visit_opt(expr, |expr| vis.visit_expr(expr)); + if let StructRest::Base(expr) = expr { + vis.visit_expr(expr); + } } ExprKind::Paren(expr) => { vis.visit_expr(expr); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 419093ae4f541..b85dba4ccb104 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -724,7 +724,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Struct(ref path, ref fields, ref optional_base) => { visitor.visit_path(path, expression.id); walk_list!(visitor, visit_field, fields); - walk_list!(visitor, visit_expr, optional_base); + if let StructRest::Base(expr) = optional_base { + visitor.visit_expr(expr); + } } ExprKind::Tup(ref subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index eb45ef622c9d8..603cd6c734f0e 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -989,7 +989,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } // Structs. - ExprKind::Struct(path, fields, base) => { + ExprKind::Struct(path, fields, rest) => { let field_pats = self.arena.alloc_from_iter(fields.iter().map(|f| { let pat = self.destructure_assign(&f.expr, eq_sign_span, assignments); hir::FieldPat { @@ -1007,28 +1007,25 @@ impl<'hir> LoweringContext<'_, 'hir> { ParamMode::Optional, ImplTraitContext::disallowed(), ); - let fields_omitted = match base { - None => false, - Some(e) => { - match e.kind { - ExprKind::Underscore => {} - _ => self - .sess - .struct_span_err( - e.span, - "functional record updates are not allowed in destructuring \ - assignments", - ) - .span_suggestion( - e.span, - "consider removing the trailing pattern", - String::new(), - rustc_errors::Applicability::MachineApplicable, - ) - .emit(), - } + let fields_omitted = match rest { + StructRest::Base(e) => { + self.sess + .struct_span_err( + e.span, + "functional record updates are not allowed in destructuring \ + assignments", + ) + .span_suggestion( + e.span, + "consider removing the trailing pattern", + String::new(), + rustc_errors::Applicability::MachineApplicable, + ) + .emit(); true } + StructRest::Rest(_) => true, + StructRest::None => false, }; let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); return self.pat(lhs.span, struct_pat); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 26f0e05109b19..b768c84789ef1 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1729,7 +1729,7 @@ impl<'a> State<'a> { &mut self, path: &ast::Path, fields: &[ast::Field], - wth: &Option>, + rest: &ast::StructRest, attrs: &[ast::Attribute], ) { self.print_path(path, true, 0); @@ -1750,22 +1750,21 @@ impl<'a> State<'a> { }, |f| f.span, ); - match *wth { - Some(ref expr) => { + match rest { + StructRest::Base(_) | StructRest::Rest(_) => { self.ibox(INDENT_UNIT); if !fields.is_empty() { self.s.word(","); self.s.space(); } self.s.word(".."); - self.print_expr(expr); - self.end(); - } - _ => { - if !fields.is_empty() { - self.s.word(",") + if let StructRest::Base(ref expr) = *rest { + self.print_expr(expr); } + self.end(); } + StructRest::None if !fields.is_empty() => self.s.word(","), + _ => {} } self.s.word("}"); } @@ -1891,8 +1890,8 @@ impl<'a> State<'a> { ast::ExprKind::Repeat(ref element, ref count) => { self.print_expr_repeat(element, count, attrs); } - ast::ExprKind::Struct(ref path, ref fields, ref wth) => { - self.print_expr_struct(path, &fields[..], wth, attrs); + ast::ExprKind::Struct(ref path, ref fields, ref rest) => { + self.print_expr_struct(path, &fields[..], rest, attrs); } ast::ExprKind::Tup(ref exprs) => { self.print_expr_tup(&exprs[..], attrs); diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 1c9bfb902d61a..30f0fc6cddfa2 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -298,7 +298,7 @@ impl<'a> ExtCtxt<'a> { path: ast::Path, fields: Vec, ) -> P { - self.expr(span, ast::ExprKind::Struct(path, fields, None)) + self.expr(span, ast::ExprKind::Struct(path, fields, ast::StructRest::None)) } pub fn expr_struct_ident( &self, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f6ccca0eb8894..6892a95c74893 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2089,7 +2089,7 @@ impl<'a> Parser<'a> { recover: bool, ) -> PResult<'a, P> { let mut fields = Vec::new(); - let mut base = None; + let mut base = ast::StructRest::None; let mut recover_async = false; attrs.extend(self.parse_inner_attributes()?); @@ -2104,18 +2104,13 @@ impl<'a> Parser<'a> { while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; - // If the struct ends in `.. }`, treat it like `.. _ }`. - // AST lowering will then report an error if it's not on the LHS of an assignment. + // We permit `.. }` on the left-hand side of a destructuring assignment. if self.token == token::CloseDelim(token::Brace) { - base = Some(self.mk_expr( - self.prev_token.span.shrink_to_hi(), - ExprKind::Underscore, - AttrVec::new(), - )); + base = StructRest::Rest(self.prev_token.span.shrink_to_hi()); break; } match self.parse_expr() { - Ok(e) => base = Some(e), + Ok(e) => base = StructRest::Base(e), Err(mut e) if recover => { e.emit(); self.recover_stmt(); diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index dbb5e3cc9f066..b7d92757a0c66 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -816,7 +816,7 @@ impl<'tcx> DumpVisitor<'tcx> { path: &'tcx hir::QPath<'tcx>, fields: &'tcx [hir::Field<'tcx>], variant: &'tcx ty::VariantDef, - base: Option<&'tcx hir::Expr<'tcx>>, + rest: &'tcx StructRest, ) { if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { if let hir::QPath::Resolved(_, path) = path { @@ -836,7 +836,9 @@ impl<'tcx> DumpVisitor<'tcx> { } } - walk_list!(self, visit_expr, base); + if let StructRest::Base(base) = rest { + self.visit_expr(base); + } } fn process_method_call( @@ -1399,7 +1401,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { debug!("visit_expr {:?}", ex.kind); self.process_macro_use(ex.span); match ex.kind { - hir::ExprKind::Struct(ref path, ref fields, ref base) => { + hir::ExprKind::Struct(ref path, ref fields, ref rest) => { let hir_expr = self.save_ctxt.tcx.hir().expect_expr(ex.hir_id); let adt = match self.save_ctxt.typeck_results().expr_ty_opt(&hir_expr) { Some(ty) if ty.ty_adt_def().is_some() => ty.ty_adt_def().unwrap(), @@ -1409,7 +1411,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { } }; let res = self.save_ctxt.get_path_res(hir_expr.hir_id); - self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *base) + self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), rest) } hir::ExprKind::MethodCall(ref seg, _, args, _) => { self.process_method_call(ex, seg, args) diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index 316dd01b7c148..7317b27d33498 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -16,7 +16,7 @@ error: base expression required after `..` --> $DIR/struct_destructure_fail.rs:16:19 | LL | Struct { a, .. }; - | ^ add the base expression here + | ^ add a base expression here error[E0026]: struct `Struct` does not have a field named `c` --> $DIR/struct_destructure_fail.rs:11:20 From ac37e3c464c51d516eba2d14c472c76e0e4e3abe Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 15 Apr 2020 02:30:42 +0100 Subject: [PATCH 21/28] Add additional tests for trailing `..` --- src/test/ui/destructuring-assignment/slice_destructure.rs | 4 +++- src/test/ui/destructuring-assignment/tuple_destructure.rs | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/ui/destructuring-assignment/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs index 1dbc720b435fd..76cdc1260fcde 100644 --- a/src/test/ui/destructuring-assignment/slice_destructure.rs +++ b/src/test/ui/destructuring-assignment/slice_destructure.rs @@ -6,10 +6,12 @@ fn main() { let (mut a, mut b); [a, b] = [0, 1]; assert_eq!((a, b), (0, 1)); - let c; + let mut c; [a, .., b, c] = [1, 2, 3, 4, 5]; assert_eq!((a, b, c), (1, 4, 5)); [_, a, _] = [1, 2, 3]; assert_eq!((a, b), (2, 4)); [..] = [1, 2, 3]; + [c, ..] = [5, 6, 6]; + assert_eq!(c, 5); } diff --git a/src/test/ui/destructuring-assignment/tuple_destructure.rs b/src/test/ui/destructuring-assignment/tuple_destructure.rs index 7972ba6057a73..b0f223072ba52 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure.rs +++ b/src/test/ui/destructuring-assignment/tuple_destructure.rs @@ -14,4 +14,6 @@ fn main() { assert_eq!((a, b), (2, 2)); (..) = (3, 4); assert_eq!((a, b), (2, 2)); + (b, ..) = (5, 6, 7); + assert_eq!(b, 5); } From f015a2ecda3aef8c0e714942cb1ee41d7222a0f6 Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 15 Apr 2020 02:31:23 +0100 Subject: [PATCH 22/28] Add pre-expansion gating --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_parse/src/parser/expr.rs | 2 ++ .../underscore-range-expr-gating.rs | 10 +++++++++ .../underscore-range-expr-gating.stderr | 21 +++++++++++++++++++ 4 files changed, 34 insertions(+) create mode 100644 src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs create mode 100644 src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index f20084497671f..2831675cb3671 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,6 +630,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); + gate_all!(destructuring_assignment, "destructuring assignments are unstable"); // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 6892a95c74893..4e4cad4776012 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -1117,6 +1117,7 @@ impl<'a> Parser<'a> { self.parse_lit_expr(attrs) } } else if self.eat_keyword(kw::Underscore) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); Ok(self.mk_expr(self.prev_token.span, ExprKind::Underscore, attrs)) } else { self.parse_lit_expr(attrs) @@ -2106,6 +2107,7 @@ impl<'a> Parser<'a> { let exp_span = self.prev_token.span; // We permit `.. }` on the left-hand side of a destructuring assignment. if self.token == token::CloseDelim(token::Brace) { + self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); base = StructRest::Rest(self.prev_token.span.shrink_to_hi()); break; } diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs new file mode 100644 index 0000000000000..4ed4f56702c32 --- /dev/null +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs @@ -0,0 +1,10 @@ +fn main() {} + +struct S { x : u32 } + +#[cfg(FALSE)] +fn foo() { + _; //~ ERROR destructuring assignments are unstable + + S { x: 5, .. }; //~ ERROR destructuring assignments are unstable +} diff --git a/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr new file mode 100644 index 0000000000000..a5ed761a01c33 --- /dev/null +++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr @@ -0,0 +1,21 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore-range-expr-gating.rs:7:5 + | +LL | _; + | ^ + | + = 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/underscore-range-expr-gating.rs:9:15 + | +LL | S { x: 5, .. }; + | ^^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0658`. From 9a5d7258f14dbeac2fc56cbf38aa25aa4f8da6d4 Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 15 Apr 2020 12:07:22 +0100 Subject: [PATCH 23/28] Make pre-expansion gate slightly less obnoxious --- compiler/rustc_ast_passes/src/feature_gate.rs | 6 +- src/test/ui/cross/cross-file-errors/main.rs | 1 + .../ui/cross/cross-file-errors/main.stderr | 18 ++++- ...fn-or-tuple-struct-with-underscore-args.rs | 6 ++ ...r-tuple-struct-with-underscore-args.stderr | 65 +++++++++++++++++-- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2831675cb3671..7716ad1e86744 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,7 +630,11 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); - gate_all!(destructuring_assignment, "destructuring assignments are unstable"); + if parse_sess.span_diagnostic.err_count() == 0 { + // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is + // involved, so we only emit errors where there are no other parsing errors. + gate_all!(destructuring_assignment, "destructuring assignments are unstable"); + } // All uses of `gate_all!` below this point were added in #65742, // and subsequently disabled (with the non-early gating readded). diff --git a/src/test/ui/cross/cross-file-errors/main.rs b/src/test/ui/cross/cross-file-errors/main.rs index 74e9461803c52..46728d453df9a 100644 --- a/src/test/ui/cross/cross-file-errors/main.rs +++ b/src/test/ui/cross/cross-file-errors/main.rs @@ -4,4 +4,5 @@ mod underscore; fn main() { underscore!(); //~^ ERROR expected expression, found reserved identifier `_` + //~^^ ERROR destructuring assignments are unstable } diff --git a/src/test/ui/cross/cross-file-errors/main.stderr b/src/test/ui/cross/cross-file-errors/main.stderr index f9101d8a583d3..e1bdb6fab71f1 100644 --- a/src/test/ui/cross/cross-file-errors/main.stderr +++ b/src/test/ui/cross/cross-file-errors/main.stderr @@ -1,3 +1,18 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/underscore.rs:8:9 + | +LL | _ + | ^ + | + ::: $DIR/main.rs:5:5 + | +LL | underscore!(); + | -------------- in this macro invocation + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) + error: expected expression, found reserved identifier `_` --> $DIR/underscore.rs:8:9 | @@ -11,5 +26,6 @@ LL | underscore!(); | = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to previous error +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs index a8ea3faefe876..adff79682635b 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs +++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.rs @@ -10,10 +10,16 @@ fn main() { let _: usize = foo(_, _); //~^ ERROR expected expression //~| ERROR expected expression + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable let _: S = S(_, _); //~^ ERROR expected expression //~| ERROR expected expression + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable let _: usize = T::baz(_, _); //~^ ERROR expected expression //~| ERROR expected expression + //~| ERROR destructuring assignments are unstable + //~| ERROR destructuring assignments are unstable } diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr index a6d1c4b859f2f..d45cb709422d0 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-with-underscore-args.stderr @@ -1,3 +1,57 @@ +error[E0658]: destructuring assignments are unstable + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24 + | +LL | let _: usize = foo(_, _); + | ^ + | + = 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/fn-or-tuple-struct-with-underscore-args.rs:10:27 + | +LL | let _: usize = foo(_, _); + | ^ + | + = 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/fn-or-tuple-struct-with-underscore-args.rs:15:18 + | +LL | let _: S = S(_, _); + | ^ + | + = 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/fn-or-tuple-struct-with-underscore-args.rs:15:21 + | +LL | let _: S = S(_, _); + | ^ + | + = 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/fn-or-tuple-struct-with-underscore-args.rs:20:27 + | +LL | let _: usize = T::baz(_, _); + | ^ + | + = 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/fn-or-tuple-struct-with-underscore-args.rs:20:30 + | +LL | let _: usize = T::baz(_, _); + | ^ + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + error: expected expression, found reserved identifier `_` --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:10:24 | @@ -11,28 +65,29 @@ LL | let _: usize = foo(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:18 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:18 | LL | let _: S = S(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:13:21 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:15:21 | LL | let _: S = S(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:27 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:27 | LL | let _: usize = T::baz(_, _); | ^ expected expression error: expected expression, found reserved identifier `_` - --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:16:30 + --> $DIR/fn-or-tuple-struct-with-underscore-args.rs:20:30 | LL | let _: usize = T::baz(_, _); | ^ expected expression -error: aborting due to 6 previous errors +error: aborting due to 12 previous errors +For more information about this error, try `rustc --explain E0658`. From fbced7db10693620c901e4d5855d9e04a764836e Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 15 Apr 2020 13:47:40 +0100 Subject: [PATCH 24/28] Fix ui-fulldeps test --- src/test/ui-fulldeps/pprust-expr-roundtrip.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs index caf55bec53ddd..bff92d8607ece 100644 --- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs @@ -155,7 +155,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P)) { }, 17 => { let path = Path::from_ident(Ident::from_str("S")); - g(ExprKind::Struct(path, vec![], Some(make_x()))); + g(ExprKind::Struct(path, vec![], StructRest::Base(make_x()))); }, 18 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); From 60552c74fe9e31ac03c5f3084a23de0a9039539b Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 14 Sep 2020 20:55:07 +0100 Subject: [PATCH 25/28] Fix rebase issues --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast_passes/src/feature_gate.rs | 2 +- compiler/rustc_ast_pretty/src/pprust/state.rs | 6 +- compiler/rustc_parse/src/parser/expr.rs | 4 +- .../rustc_save_analysis/src/dump_visitor.rs | 8 +- src/test/ui/issues/issue-34334.rs | 6 -- src/test/ui/issues/issue-34334.stderr | 48 +---------- src/test/ui/suggestions/if-let-typo.rs | 5 ++ src/test/ui/suggestions/if-let-typo.stderr | 80 ++++++++++++++----- 9 files changed, 78 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index f08bea128005c..6a2b8ca6d0126 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1061,7 +1061,7 @@ pub struct Expr { // `Expr` is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(target_arch = "x86_64")] -rustc_data_structures::static_assert_size!(Expr, 112); +rustc_data_structures::static_assert_size!(Expr, 120); impl Expr { /// Returns `true` if this expression would be valid somewhere that expects a value; diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 7716ad1e86744..181783441f3ff 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -630,7 +630,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(const_trait_impl, "const trait impls are experimental"); gate_all!(half_open_range_patterns, "half-open range patterns are unstable"); gate_all!(inline_const, "inline-const is experimental"); - if parse_sess.span_diagnostic.err_count() == 0 { + if sess.parse_sess.span_diagnostic.err_count() == 0 { // Errors for `destructuring_assignment` can get quite noisy, especially where `_` is // involved, so we only emit errors where there are no other parsing errors. gate_all!(destructuring_assignment, "destructuring assignments are unstable"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index b768c84789ef1..51317c34e1bba 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1751,19 +1751,19 @@ impl<'a> State<'a> { |f| f.span, ); match rest { - StructRest::Base(_) | StructRest::Rest(_) => { + ast::StructRest::Base(_) | ast::StructRest::Rest(_) => { self.ibox(INDENT_UNIT); if !fields.is_empty() { self.s.word(","); self.s.space(); } self.s.word(".."); - if let StructRest::Base(ref expr) = *rest { + if let ast::StructRest::Base(ref expr) = *rest { self.print_expr(expr); } self.end(); } - StructRest::None if !fields.is_empty() => self.s.word(","), + ast::StructRest::None if !fields.is_empty() => self.s.word(","), _ => {} } self.s.word("}"); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 4e4cad4776012..6e199b78b6789 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2108,11 +2108,11 @@ impl<'a> Parser<'a> { // We permit `.. }` on the left-hand side of a destructuring assignment. if self.token == token::CloseDelim(token::Brace) { self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span); - base = StructRest::Rest(self.prev_token.span.shrink_to_hi()); + base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi()); break; } match self.parse_expr() { - Ok(e) => base = StructRest::Base(e), + Ok(e) => base = ast::StructRest::Base(e), Err(mut e) if recover => { e.emit(); self.recover_stmt(); diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs index b7d92757a0c66..40d60a8394be3 100644 --- a/compiler/rustc_save_analysis/src/dump_visitor.rs +++ b/compiler/rustc_save_analysis/src/dump_visitor.rs @@ -816,7 +816,7 @@ impl<'tcx> DumpVisitor<'tcx> { path: &'tcx hir::QPath<'tcx>, fields: &'tcx [hir::Field<'tcx>], variant: &'tcx ty::VariantDef, - rest: &'tcx StructRest, + rest: Option<&'tcx hir::Expr<'tcx>>, ) { if let Some(struct_lit_data) = self.save_ctxt.get_expr_data(ex) { if let hir::QPath::Resolved(_, path) = path { @@ -836,8 +836,8 @@ impl<'tcx> DumpVisitor<'tcx> { } } - if let StructRest::Base(base) = rest { - self.visit_expr(base); + if let Some(base) = rest { + self.visit_expr(&base); } } @@ -1411,7 +1411,7 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> { } }; let res = self.save_ctxt.get_path_res(hir_expr.hir_id); - self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), rest) + self.process_struct_lit(ex, path, fields, adt.variant_of_res(res), *rest) } hir::ExprKind::MethodCall(ref seg, _, args, _) => { self.process_method_call(ex, seg, args) diff --git a/src/test/ui/issues/issue-34334.rs b/src/test/ui/issues/issue-34334.rs index 4cd93a3e84cdd..bf2d091a01e95 100644 --- a/src/test/ui/issues/issue-34334.rs +++ b/src/test/ui/issues/issue-34334.rs @@ -1,12 +1,6 @@ fn main () { let sr: Vec<(u32, _, _) = vec![]; //~^ ERROR expected one of `,` or `>`, found `=` - //~| ERROR expected value, found struct `Vec` - //~| ERROR expected value, found builtin type `u32` - //~| ERROR mismatched types - //~| ERROR invalid left-hand side of assignment - //~| ERROR expected expression, found reserved identifier `_` - //~| ERROR expected expression, found reserved identifier `_` let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); //~^ ERROR a value of type `Vec<(u32, _, _)>` cannot be built } diff --git a/src/test/ui/issues/issue-34334.stderr b/src/test/ui/issues/issue-34334.stderr index d4333c6211a80..c10a41443050a 100644 --- a/src/test/ui/issues/issue-34334.stderr +++ b/src/test/ui/issues/issue-34334.stderr @@ -6,56 +6,14 @@ LL | let sr: Vec<(u32, _, _) = vec![]; | | | while parsing the type for `sr` -error[E0423]: expected value, found struct `Vec` - --> $DIR/issue-34334.rs:2:13 - | -LL | let sr: Vec<(u32, _, _) = vec![]; - | ^^^ did you mean `Vec { /* fields */ }`? - -error[E0423]: expected value, found builtin type `u32` - --> $DIR/issue-34334.rs:2:18 - | -LL | let sr: Vec<(u32, _, _) = vec![]; - | ^^^ not a value - -error: expected expression, found reserved identifier `_` - --> $DIR/issue-34334.rs:2:23 - | -LL | let sr: Vec<(u32, _, _) = vec![]; - | ^ expected expression - -error: expected expression, found reserved identifier `_` - --> $DIR/issue-34334.rs:2:26 - | -LL | let sr: Vec<(u32, _, _) = vec![]; - | ^ expected expression - -error[E0308]: mismatched types - --> $DIR/issue-34334.rs:2:31 - | -LL | let sr: Vec<(u32, _, _) = vec![]; - | ^^^^^^ expected `bool`, found struct `std::vec::Vec` - | - = note: expected type `bool` - found struct `std::vec::Vec<_>` - = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info) - -error[E0070]: invalid left-hand side of assignment - --> $DIR/issue-34334.rs:2:29 - | -LL | let sr: Vec<(u32, _, _) = vec![]; - | --------------- ^ - | | - | cannot assign to this expression - -error[E0599]: no method named `iter` found for unit type `()` in the current scope - --> $DIR/issue-34334.rs:10:36 +error[E0277]: a value of type `Vec<(u32, _, _)>` cannot be built from an iterator over elements of type `()` + --> $DIR/issue-34334.rs:4:87 | LL | let sr2: Vec<(u32, _, _)> = sr.iter().map(|(faction, th_sender, th_receiver)| {}).collect(); | ^^^^^^^ value of type `Vec<(u32, _, _)>` cannot be built from `std::iter::Iterator` | = help: the trait `FromIterator<()>` is not implemented for `Vec<(u32, _, _)>` -error: aborting due to 8 previous errors +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/if-let-typo.rs b/src/test/ui/suggestions/if-let-typo.rs index 87def13c476c7..688b6e8265826 100644 --- a/src/test/ui/suggestions/if-let-typo.rs +++ b/src/test/ui/suggestions/if-let-typo.rs @@ -2,7 +2,12 @@ fn main() { let foo = Some(0); let bar = None; if Some(x) = foo {} //~ ERROR cannot find value `x` in this scope + //~^ ERROR mismatched types + //~^^ ERROR destructuring assignments are unstable if Some(foo) = bar {} //~ ERROR mismatched types + //~^ ERROR destructuring assignments are unstable if 3 = foo {} //~ ERROR mismatched types if Some(3) = foo {} //~ ERROR mismatched types + //~^ ERROR destructuring assignments are unstable + //~^^ ERROR invalid left-hand side of assignment } diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr index d8e50cae55ad1..b4ac3625b449b 100644 --- a/src/test/ui/suggestions/if-let-typo.stderr +++ b/src/test/ui/suggestions/if-let-typo.stderr @@ -9,23 +9,51 @@ help: you might have meant to use pattern matching LL | if let Some(x) = foo {} | ^^^ +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:4:16 + | +LL | if Some(x) = foo {} + | ------- ^ + | | + | cannot assign to this expression + | + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:5:8 + --> $DIR/if-let-typo.rs:4:8 + | +LL | if Some(x) = foo {} + | ^^^^^^^^^^^^^ expected `bool`, found `()` + +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:7:18 | LL | if Some(foo) = bar {} - | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + | --------- ^ + | | + | cannot assign to this expression | -help: you might have meant to use pattern matching + = note: see issue #71126 for more information + = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:7:8 | -LL | if let Some(foo) = bar {} - | ^^^ -help: you might have meant to compare for equality +LL | if Some(foo) = bar {} + | ^^^^^^^^^^^^^^^ expected `bool`, found `()` + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:9:12 + | +LL | if 3 = foo {} + | ^^^ expected integer, found enum `Option` | -LL | if Some(foo) == bar {} - | ^^ + = note: expected type `{integer}` + found enum `Option<{integer}>` error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:6:8 + --> $DIR/if-let-typo.rs:9:8 | LL | if 3 = foo {} | ^^^^^^^ expected `bool`, found `()` @@ -35,22 +63,32 @@ help: you might have meant to use pattern matching LL | if let 3 = foo {} | ^^^ -error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:7:8 +error[E0658]: destructuring assignments are unstable + --> $DIR/if-let-typo.rs:11:16 | LL | if Some(3) = foo {} - | ^^^^^^^^^^^^^ expected `bool`, found `()` + | ------- ^ + | | + | cannot assign to this expression | -help: you might have meant to use pattern matching + = 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/if-let-typo.rs:11:16 | -LL | if let Some(3) = foo {} - | ^^^ -help: you might have meant to compare for equality +LL | if Some(3) = foo {} + | - ^ + | | + | cannot assign to this expression + +error[E0308]: mismatched types + --> $DIR/if-let-typo.rs:11:8 | -LL | if Some(3) == foo {} - | ^^ +LL | if Some(3) = foo {} + | ^^^^^^^^^^^^^ expected `bool`, found `()` -error: aborting due to 4 previous errors +error: aborting due to 10 previous errors -Some errors have detailed explanations: E0308, E0425. -For more information about an error, try `rustc --explain E0308`. +Some errors have detailed explanations: E0070, E0308, E0425, E0658. +For more information about an error, try `rustc --explain E0070`. From 8614f44cab5f9e5e7b1fd4f0bd07b0ce1d3460f5 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 14 Sep 2020 20:55:26 +0100 Subject: [PATCH 26/28] Add support for `StructRest` in clippy --- src/tools/clippy/clippy_lints/src/utils/ast_utils.rs | 11 ++++++++++- src/tools/clippy/clippy_lints/src/utils/sugg.rs | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs index 0e9feef3746e7..7b65e664867ff 100644 --- a/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs +++ b/src/tools/clippy/clippy_lints/src/utils/ast_utils.rs @@ -107,6 +107,15 @@ pub fn eq_expr_opt(l: &Option>, r: &Option>) -> bool { both(l, r, |l, r| eq_expr(l, r)) } +pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool { + match (l, r) { + (StructRest::Base(lb), StructRest::Base(rb)) => eq_expr(lb, rb), + (StructRest::Rest(_), StructRest::Rest(_)) => true, + (StructRest::None, StructRest::None) => true, + _ => false, + } +} + pub fn eq_expr(l: &Expr, r: &Expr) -> bool { use ExprKind::*; if !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r)) { @@ -150,7 +159,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp), (MacCall(l), MacCall(r)) => eq_mac_call(l, r), (Struct(lp, lfs, lb), Struct(rp, rfs, rb)) => { - eq_path(lp, rp) && eq_expr_opt(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) + eq_path(lp, rp) && eq_struct_rest(lb, rb) && unordered_over(lfs, rfs, |l, r| eq_field(l, r)) }, _ => false, } diff --git a/src/tools/clippy/clippy_lints/src/utils/sugg.rs b/src/tools/clippy/clippy_lints/src/utils/sugg.rs index 625120b880eb5..1fcd41e4dbfed 100644 --- a/src/tools/clippy/clippy_lints/src/utils/sugg.rs +++ b/src/tools/clippy/clippy_lints/src/utils/sugg.rs @@ -170,6 +170,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::MacCall(..) | ast::ExprKind::MethodCall(..) | ast::ExprKind::Paren(..) + | ast::ExprKind::Underscore | ast::ExprKind::Path(..) | ast::ExprKind::Repeat(..) | ast::ExprKind::Ret(..) From a0d4c5509ea6c7eb26a128bf51b63504f5484363 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 14 Sep 2020 23:06:09 +0100 Subject: [PATCH 27/28] Prevent default match bindings in destructuring assignment --- compiler/rustc_ast_lowering/src/expr.rs | 18 +++++++++++------- compiler/rustc_ast_lowering/src/lib.rs | 17 ++++++++++++++++- compiler/rustc_ast_lowering/src/pat.rs | 7 ++++++- compiler/rustc_hir/src/hir.rs | 3 +++ compiler/rustc_typeck/src/check/pat.rs | 5 +++++ compiler/rustc_typeck/src/check/regionck.rs | 2 +- .../default-match-bindings-forbidden.rs | 7 +++++++ .../default-match-bindings-forbidden.stderr | 14 ++++++++++++++ .../tuple_destructure_fail.stderr | 8 ++++---- 9 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 src/test/ui/destructuring-assignment/default-match-bindings-forbidden.rs create mode 100644 src/test/ui/destructuring-assignment/default-match-bindings-forbidden.stderr diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 603cd6c734f0e..f8318b12448a2 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -945,7 +945,7 @@ impl<'hir> LoweringContext<'_, 'hir> { match &lhs.kind { // Underscore pattern. ExprKind::Underscore => { - return self.pat(lhs.span, hir::PatKind::Wild); + return self.pat_without_dbm(lhs.span, hir::PatKind::Wild); } // Slice patterns. ExprKind::Array(elements) => { @@ -953,11 +953,15 @@ impl<'hir> LoweringContext<'_, 'hir> { self.destructure_sequence(elements, "slice", eq_sign_span, assignments); let slice_pat = if let Some((i, span)) = rest { let (before, after) = pats.split_at(i); - hir::PatKind::Slice(before, Some(self.pat(span, hir::PatKind::Wild)), after) + hir::PatKind::Slice( + before, + Some(self.pat_without_dbm(span, hir::PatKind::Wild)), + after, + ) } else { hir::PatKind::Slice(pats, None, &[]) }; - return self.pat(lhs.span, slice_pat); + return self.pat_without_dbm(lhs.span, slice_pat); } // Tuple structs. ExprKind::Call(callee, args) => { @@ -979,7 +983,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // Destructure like a tuple struct since the path is in fact a constructor. let tuple_struct_pat = hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0)); - return self.pat(lhs.span, tuple_struct_pat); + return self.pat_without_dbm(lhs.span, tuple_struct_pat); } _ => { // If the path is not a constructor, lower as an ordinary LHS. @@ -1028,20 +1032,20 @@ impl<'hir> LoweringContext<'_, 'hir> { StructRest::None => false, }; let struct_pat = hir::PatKind::Struct(qpath, field_pats, fields_omitted); - return self.pat(lhs.span, struct_pat); + return self.pat_without_dbm(lhs.span, struct_pat); } // 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(lhs.span, tuple_pat); + return self.pat_without_dbm(lhs.span, tuple_pat); } // `(..)`. We special-case this for consistency with declarations. ExprKind::Paren(e) => { if let ExprKind::Range(None, None, RangeLimits::HalfOpen) = e.kind { let tuple_pat = hir::PatKind::Tuple(&[], Some(0)); - return self.pat(lhs.span, tuple_pat); + return self.pat_without_dbm(lhs.span, tuple_pat); } } _ => {} 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 18790956b0670..e4e7b24d29e52 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -273,7 +273,12 @@ 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. diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index b5f652f30fbb5..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<'_> { 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/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/tuple_destructure_fail.stderr b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr index 2ebe6b3f1968d..9a83635d295d6 100644 --- a/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/tuple_destructure_fail.stderr @@ -14,8 +14,8 @@ LL | (a, a, b) = (1, 2); | | | expected a tuple with 2 elements, found one with 3 elements | - = note: expected tuple `({integer}, {integer})` - found tuple `(_, _, _)` + = note: expected type `({integer}, {integer})` + found tuple `(_, _, _)` error[E0308]: mismatched types --> $DIR/tuple_destructure_fail.rs:7:5 @@ -25,8 +25,8 @@ LL | (_,) = (1, 2); | | | expected a tuple with 2 elements, found one with 1 element | - = note: expected tuple `({integer}, {integer})` - found tuple `(_,)` + = note: expected type `({integer}, {integer})` + found tuple `(_,)` error: aborting due to 3 previous errors From c25779a6624ac821abbcee18a56fa48f9e6c474e Mon Sep 17 00:00:00 2001 From: Fabian Zaiser Date: Sun, 11 Oct 2020 16:13:49 +0100 Subject: [PATCH 28/28] Fix rebase issue and test --- .../rustc_typeck/src/check/fn_ctxt/checks.rs | 18 ++++++++++++++++++ .../struct_destructure_fail.stderr | 9 +++++++++ src/test/ui/suggestions/if-let-typo.stderr | 17 ++++------------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs index a820661d8432a..9f94d61cf0aba 100644 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs @@ -17,6 +17,7 @@ use rustc_hir::{ExprKind, Node, QPath}; use rustc_middle::ty::adjustment::AllowTwoPhase; use rustc_middle::ty::fold::TypeFoldable; use rustc_middle::ty::{self, Ty}; +use rustc_session::parse::feature_err; use rustc_session::Session; use rustc_span::symbol::{sym, Ident}; use rustc_span::{self, MultiSpan, Span}; @@ -515,6 +516,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Type check a `let` statement. pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { + // Check for destructuring assignment. + match local.source { + hir::LocalSource::AssignDesugar(eq_sign_span) + if !self.tcx.features().destructuring_assignment => + { + feature_err( + &self.tcx.sess.parse_sess, + sym::destructuring_assignment, + eq_sign_span, + "destructuring assignments are unstable", + ) + .span_label(local.pat.span, "cannot assign to this expression") + .emit(); + } + _ => {} + } + // Determine and write the type which we'll check the pattern against. let ty = self.local_ty(local.span, local.hir_id).decl_ty; self.write_ty(local.hir_id, ty); diff --git a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr index 7317b27d33498..c8ddc4bafc17c 100644 --- a/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr +++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr @@ -29,6 +29,15 @@ error[E0027]: pattern does not mention field `b` | LL | Struct { a, _ } = Struct { a: 1, b: 2 }; | ^^^^^^^^^^^^^^^ missing field `b` + | +help: include the missing field in the pattern + | +LL | Struct { a, b, _ } = Struct { a: 1, b: 2 }; + | ^^^ +help: if you don't care about this missing field, you can explicitely ignore it + | +LL | Struct { a, .., _ } = Struct { a: 1, b: 2 }; + | ^^^^ error: aborting due to 5 previous errors diff --git a/src/test/ui/suggestions/if-let-typo.stderr b/src/test/ui/suggestions/if-let-typo.stderr index b4ac3625b449b..2c8ec11d39495 100644 --- a/src/test/ui/suggestions/if-let-typo.stderr +++ b/src/test/ui/suggestions/if-let-typo.stderr @@ -43,15 +43,6 @@ error[E0308]: mismatched types LL | if Some(foo) = bar {} | ^^^^^^^^^^^^^^^ expected `bool`, found `()` -error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:9:12 - | -LL | if 3 = foo {} - | ^^^ expected integer, found enum `Option` - | - = note: expected type `{integer}` - found enum `Option<{integer}>` - error[E0308]: mismatched types --> $DIR/if-let-typo.rs:9:8 | @@ -64,7 +55,7 @@ LL | if let 3 = foo {} | ^^^ error[E0658]: destructuring assignments are unstable - --> $DIR/if-let-typo.rs:11:16 + --> $DIR/if-let-typo.rs:10:16 | LL | if Some(3) = foo {} | ------- ^ @@ -75,7 +66,7 @@ LL | if Some(3) = foo {} = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable error[E0070]: invalid left-hand side of assignment - --> $DIR/if-let-typo.rs:11:16 + --> $DIR/if-let-typo.rs:10:16 | LL | if Some(3) = foo {} | - ^ @@ -83,12 +74,12 @@ LL | if Some(3) = foo {} | cannot assign to this expression error[E0308]: mismatched types - --> $DIR/if-let-typo.rs:11:8 + --> $DIR/if-let-typo.rs:10:8 | LL | if Some(3) = foo {} | ^^^^^^^^^^^^^ expected `bool`, found `()` -error: aborting due to 10 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0070, E0308, E0425, E0658. For more information about an error, try `rustc --explain E0070`.