From de84ad95b4b097e504171c7c606bf3715803c13b Mon Sep 17 00:00:00 2001
From: Fabian Zaiser <fabian.zaiser@gmail.com>
Date: Sat, 7 Nov 2020 14:28:55 +0000
Subject: [PATCH] Implement destructuring assignment for structs and slices

Co-authored-by: varkor <github@varkor.com>
---
 compiler/rustc_ast/src/ast.rs                 |  19 ++-
 compiler/rustc_ast/src/mut_visit.rs           |   6 +-
 compiler/rustc_ast/src/visit.rs               |   6 +-
 compiler/rustc_ast_lowering/src/expr.rs       | 126 +++++++++++++++++-
 compiler/rustc_ast_passes/src/feature_gate.rs |   1 +
 compiler/rustc_ast_pretty/src/pprust/state.rs |  21 ++-
 compiler/rustc_expand/src/build.rs            |   2 +-
 compiler/rustc_hir/src/def.rs                 |   5 +
 compiler/rustc_parse/src/parser/expr.rs       |  10 +-
 compiler/rustc_resolve/src/late.rs            |   4 +-
 .../rustc_save_analysis/src/dump_visitor.rs   |  10 +-
 src/test/ui-fulldeps/pprust-expr-roundtrip.rs |   2 +-
 .../nested_destructure.rs                     |  17 +++
 .../note-unsupported.rs                       |   7 +-
 .../note-unsupported.stderr                   |  69 ++++++----
 .../slice_destructure.rs                      |  15 +++
 .../slice_destructure_fail.rs                 |   7 +
 .../slice_destructure_fail.stderr             |  17 +++
 .../struct_destructure.rs                     |  19 +++
 .../struct_destructure_fail.rs                |  15 +++
 .../struct_destructure_fail.stderr            |  21 +++
 .../tuple_struct_destructure.rs               |  34 +++++
 .../tuple_struct_destructure_fail.rs          |  42 ++++++
 .../tuple_struct_destructure_fail.stderr      |  62 +++++++++
 .../underscore-range-expr-gating.rs           |   8 ++
 .../underscore-range-expr-gating.stderr       |  12 ++
 src/test/ui/issues/issue-77218.rs             |  10 +-
 src/test/ui/issues/issue-77218.stderr         |  38 +++++-
 src/test/ui/suggestions/if-let-typo.rs        |   5 +
 src/test/ui/suggestions/if-let-typo.stderr    |  75 +++++++----
 .../clippy_lints/src/utils/ast_utils.rs       |  11 +-
 .../clippy/tests/ui/crashes/ice-6250.stderr   |  29 +++-
 32 files changed, 618 insertions(+), 107 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_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
 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/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index f13d67b9c1584..1886581df9a94 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;
@@ -1218,6 +1218,16 @@ pub enum RangeLimits {
     Closed,
 }
 
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum StructRest {
+    /// `..x`.
+    Base(P<Expr>),
+    /// `..`.
+    Rest(Span),
+    /// No trailing `..` or expression.
+    None,
+}
+
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub enum ExprKind {
     /// A `box x` expression.
@@ -1312,7 +1322,7 @@ pub enum ExprKind {
     Field(P<Expr>, Ident),
     /// An indexing operation (e.g., `foo[2]`).
     Index(P<Expr>, P<Expr>),
-    /// 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<P<Expr>>, Option<P<Expr>>, RangeLimits),
 
     /// Variable reference, possibly containing `::` and/or type
@@ -1340,9 +1350,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<Expr>`.
-    Struct(Path, Vec<Field>, Option<P<Expr>>),
+    /// E.g., `Foo {x: 1, y: 2}`, or `Foo {x: 1, .. rest}`.
+    Struct(Path, Vec<Field>, 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 7b85d28568bd3..a80a6adb4fae8 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -1288,7 +1288,11 @@ pub fn noop_visit_expr<T: MutVisitor>(
         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));
+            match expr {
+                StructRest::Base(expr) => vis.visit_expr(expr),
+                StructRest::Rest(_span) => {}
+                StructRest::None => {}
+            }
         }
         ExprKind::Paren(expr) => {
             vis.visit_expr(expr);
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 8751f09cfcbbe..0666eb199e673 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -719,7 +719,11 @@ 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);
+            match optional_base {
+                StructRest::Base(expr) => visitor.visit_expr(expr),
+                StructRest::Rest(_span) => {}
+                StructRest::None => {}
+            }
         }
         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 1f2aba2b27e68..330776fc8c598 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -187,8 +187,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,
@@ -198,7 +208,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()),
@@ -851,20 +861,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
         whole_span: Span,
     ) -> hir::ExprKind<'hir> {
         // Return early in case of an ordinary assignment.
-        fn is_ordinary(lhs: &Expr) -> bool {
+        fn is_ordinary(lower_ctx: &mut LoweringContext<'_, '_>, lhs: &Expr) -> bool {
             match &lhs.kind {
-                ExprKind::Tup(..) => false,
+                ExprKind::Array(..) | ExprKind::Struct(..) | ExprKind::Tup(..) => false,
+                // Check for tuple struct constructor.
+                ExprKind::Call(callee, ..) => lower_ctx.extract_tuple_struct_path(callee).is_none(),
                 ExprKind::Paren(e) => {
                     match e.kind {
                         // We special-case `(..)` for consistency with patterns.
                         ExprKind::Range(None, None, RangeLimits::HalfOpen) => false,
-                        _ => is_ordinary(e),
+                        _ => is_ordinary(lower_ctx, e),
                     }
                 }
                 _ => true,
             }
         }
-        if is_ordinary(lhs) {
+        if is_ordinary(self, lhs) {
             return hir::ExprKind::Assign(self.lower_expr(lhs), self.lower_expr(rhs), eq_sign_span);
         }
         if !self.sess.features_untracked().destructuring_assignment {
@@ -902,6 +914,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
         hir::ExprKind::Block(&self.block_all(whole_span, stmts, None), None)
     }
 
+    /// If the given expression is a path to a tuple struct, returns that path.
+    /// It is not a complete check, but just tries to reject most paths early
+    /// if they are not tuple structs.
+    /// Type checking will take care of the full validation later.
+    fn extract_tuple_struct_path<'a>(&mut self, expr: &'a Expr) -> Option<&'a Path> {
+        // For tuple struct destructuring, it must be a non-qualified path (like in patterns).
+        if let ExprKind::Path(None, path) = &expr.kind {
+            // Does the path resolves to something disallowed in a tuple struct/variant pattern?
+            if let Some(partial_res) = self.resolver.get_partial_res(expr.id) {
+                if partial_res.unresolved_segments() == 0
+                    && !partial_res.base_res().expected_in_tuple_struct_pat()
+                {
+                    return None;
+                }
+            }
+            return Some(path);
+        }
+        None
+    }
+
     /// Convert the LHS of a destructuring assignment to a pattern.
     /// Each sub-assignment is recorded in `assignments`.
     fn destructure_assign(
@@ -911,6 +943,86 @@ impl<'hir> LoweringContext<'_, 'hir> {
         assignments: &mut Vec<hir::Stmt<'hir>>,
     ) -> &'hir hir::Pat<'hir> {
         match &lhs.kind {
+            // Slice patterns.
+            ExprKind::Array(elements) => {
+                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_without_dbm(span, hir::PatKind::Wild)),
+                        after,
+                    )
+                } else {
+                    hir::PatKind::Slice(pats, None, &[])
+                };
+                return self.pat_without_dbm(lhs.span, slice_pat);
+            }
+            // Tuple structs.
+            ExprKind::Call(callee, args) => {
+                if let Some(path) = self.extract_tuple_struct_path(callee) {
+                    let (pats, rest) = self.destructure_sequence(
+                        args,
+                        "tuple struct or variant",
+                        eq_sign_span,
+                        assignments,
+                    );
+                    let qpath = self.lower_qpath(
+                        callee.id,
+                        &None,
+                        path,
+                        ParamMode::Optional,
+                        ImplTraitContext::disallowed(),
+                    );
+                    // Destructure like a tuple struct.
+                    let tuple_struct_pat =
+                        hir::PatKind::TupleStruct(qpath, pats, rest.map(|r| r.0));
+                    return self.pat_without_dbm(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 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_without_dbm(lhs.span, struct_pat);
+            }
             // Tuples.
             ExprKind::Tup(elements) => {
                 let (pats, rest) =
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_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 9fcba90244330..17b3711b485ea 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<P<ast::Expr>>,
+        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 {
+            ast::StructRest::Base(_) | ast::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 ast::StructRest::Base(ref expr) = *rest {
+                    self.print_expr(expr);
                 }
+                self.end();
             }
+            ast::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<ast::Field>,
     ) -> P<ast::Expr> {
-        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_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 193247af584bb..298cfcc254c86 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -484,4 +484,9 @@ impl<Id> Res<Id> {
     pub fn matches_ns(&self, ns: Namespace) -> bool {
         self.ns().map_or(true, |actual_ns| actual_ns == ns)
     }
+
+    /// Returns whether such a resolved path can occur in a tuple struct/variant pattern
+    pub fn expected_in_tuple_struct_pat(&self) -> bool {
+        matches!(self, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
+    }
 }
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index c2a13d4b0dec1..188bf227c4249 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -2087,7 +2087,7 @@ impl<'a> Parser<'a> {
         recover: bool,
     ) -> PResult<'a, P<Expr>> {
         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()?);
@@ -2102,8 +2102,14 @@ impl<'a> Parser<'a> {
         while self.token != token::CloseDelim(token::Brace) {
             if self.eat(&token::DotDot) {
                 let exp_span = self.prev_token.span;
+                // We permit `.. }` on the left-hand side of a destructuring assignment.
+                if self.check(&token::CloseDelim(token::Brace)) {
+                    self.sess.gated_spans.gate(sym::destructuring_assignment, self.prev_token.span);
+                    base = ast::StructRest::Rest(self.prev_token.span.shrink_to_hi());
+                    break;
+                }
                 match self.parse_expr() {
-                    Ok(e) => base = Some(e),
+                    Ok(e) => base = ast::StructRest::Base(e),
                     Err(mut e) if recover => {
                         e.emit();
                         self.recover_stmt();
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index 2337f0d09abb7..2149fd7e01184 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -298,9 +298,7 @@ impl<'a> PathSource<'a> {
                     _,
                 )
                 | Res::SelfCtor(..)),
-            PathSource::TupleStruct(..) => {
-                matches!(res, Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) | Res::SelfCtor(..))
-            }
+            PathSource::TupleStruct(..) => res.expected_in_tuple_struct_pat(),
             PathSource::Struct => matches!(res, Res::Def(
                     DefKind::Struct
                     | DefKind::Union
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index dbb5e3cc9f066..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,
-        base: Option<&'tcx hir::Expr<'tcx>>,
+        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,7 +836,9 @@ impl<'tcx> DumpVisitor<'tcx> {
             }
         }
 
-        walk_list!(self, visit_expr, base);
+        if let Some(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-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<Expr>)) {
             },
             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)));
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<S, T> {
+    a: S,
+    b: T,
+}
+
+struct TupleStruct<S, T>(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 e0cb9dc9158e2..249fba7f920bc 100644
--- a/src/test/ui/destructuring-assignment/note-unsupported.rs
+++ b/src/test/ui/destructuring-assignment/note-unsupported.rs
@@ -7,18 +7,19 @@ fn main() {
     (a, b) += (3, 4); //~ ERROR invalid left-hand side of assignment
     //~| ERROR binary assignment operation `+=` cannot be applied
 
-    [a, b] = [3, 4]; //~ ERROR invalid left-hand side of assignment
+    [a, b] = [3, 4]; //~ ERROR destructuring assignments are unstable
     [a, b] += [3, 4]; //~ ERROR invalid left-hand side of assignment
     //~| ERROR binary assignment operation `+=` cannot be applied
 
     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 destructuring assignments are unstable
     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
+    //~^ 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 c5543fab825eb..a81324b99e586 100644
--- a/src/test/ui/destructuring-assignment/note-unsupported.stderr
+++ b/src/test/ui/destructuring-assignment/note-unsupported.stderr
@@ -10,7 +10,46 @@ LL |     (a, b) = (3, 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:10:12
+   |
+LL |     [a, b] = [3, 4];
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/note-unsupported.rs:16:22
+   |
+LL |     S { x: a, y: b } = s;
+   |     ---------------- ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/note-unsupported.rs:20:21
+   |
+LL |     S { x: a, ..s } = S { x: 3, y: 4 };
+   |     --------------- ^
+   |     |
+   |     cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error: 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 the trailing pattern
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/note-unsupported.rs:26:17
    |
 LL |     ((a, b), c) = ((3, 4), 5);
    |     ----------- ^
@@ -36,14 +75,6 @@ LL |     (a, b) += (3, 4);
    |     |
    |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/note-unsupported.rs:10:12
-   |
-LL |     [a, b] = [3, 4];
-   |     ------ ^
-   |     |
-   |     cannot assign to this expression
-
 error[E0368]: binary assignment operation `+=` cannot be applied to type `[{integer}; 2]`
   --> $DIR/note-unsupported.rs:11:5
    |
@@ -60,14 +91,6 @@ LL |     [a, b] += [3, 4];
    |     |
    |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/note-unsupported.rs:16:22
-   |
-LL |     S { x: a, y: b } = s;
-   |     ---------------- ^
-   |     |
-   |     cannot assign to this expression
-
 error[E0368]: binary assignment operation `+=` cannot be applied to type `S`
   --> $DIR/note-unsupported.rs:17:5
    |
@@ -86,15 +109,7 @@ LL |     S { x: a, y: b } += s;
    |     |
    |     cannot assign to this expression
 
-error[E0070]: invalid left-hand side of assignment
-  --> $DIR/note-unsupported.rs:20:21
-   |
-LL |     S { x: a, ..s } = S { x: 3, y: 4 };
-   |     --------------- ^
-   |     |
-   |     cannot assign to this expression
-
-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/slice_destructure.rs b/src/test/ui/destructuring-assignment/slice_destructure.rs
new file mode 100644
index 0000000000000..3dd10aff19c72
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/slice_destructure.rs
@@ -0,0 +1,15 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+
+fn main() {
+  let (mut a, mut b);
+  [a, b] = [0, 1];
+  assert_eq!((a, b), (0, 1));
+  let mut c;
+  [a, .., b, c] = [1, 2, 3, 4, 5];
+  assert_eq!((a, b, c), (1, 4, 5));
+  [..] = [1, 2, 3];
+  [c, ..] = [5, 6, 6];
+  assert_eq!(c, 5);
+}
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..f636ea3511c26
--- /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..728687deb8bbb
--- /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..b3a96ee157346
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/struct_destructure.rs
@@ -0,0 +1,19 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+struct Struct<S, T> {
+    a: S,
+    b: T,
+}
+
+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, .. } = Struct { a: 1, b: 3 };
+    assert_eq!((a, b), (1, 1));
+    Struct { .. } = Struct { a: 1, b: 4 };
+    assert_eq!((a, b), (1, 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..c22695ed38849
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.rs
@@ -0,0 +1,15 @@
+#![feature(destructuring_assignment)]
+struct Struct<S, T> {
+    a: S,
+    b: T,
+}
+
+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, ..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
new file mode 100644
index 0000000000000..4da4698804f1a
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/struct_destructure_fail.stderr
@@ -0,0 +1,21 @@
+error: functional record updates are not allowed in destructuring assignments
+  --> $DIR/struct_destructure_fail.rs:12:19
+   |
+LL |     Struct { a, ..d } = Struct { a: 1, b: 2 };
+   |                   ^ help: consider removing the trailing pattern
+
+error: base expression required after `..`
+  --> $DIR/struct_destructure_fail.rs:14:19
+   |
+LL |     Struct { a, .. };
+   |                   ^ add a base expression here
+
+error[E0026]: struct `Struct` does not have a field named `c`
+  --> $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: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0026`.
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..106a9b16db459
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure.rs
@@ -0,0 +1,34 @@
+// run-pass
+
+#![feature(destructuring_assignment)]
+
+struct TupleStruct<S, T>(S, T);
+
+impl<S, T> TupleStruct<S, T> {
+    fn assign(self, first: &mut S, second: &mut T) {
+        // Test usage of `Self` instead of the struct name:
+        Self(*first, *second) = self
+    }
+}
+
+enum Enum<S, T> {
+    SingleVariant(S, T)
+}
+
+type Alias<S> = Enum<S, isize>;
+
+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(..) = TupleStruct(3, 4);
+    assert_eq!((a, b), (1, 2));
+    TupleStruct(5,6).assign(&mut a, &mut b);
+    assert_eq!((a, b), (5, 6));
+    Enum::SingleVariant(a, b) = Enum::SingleVariant(7, 8);
+    assert_eq!((a, b), (7, 8));
+    Alias::SingleVariant(a, b) = Alias::SingleVariant(9, 10);
+    assert_eq!((a, b), (9, 10));
+}
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..61ae42a51751f
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.rs
@@ -0,0 +1,42 @@
+#![feature(destructuring_assignment)]
+
+struct TupleStruct<S, T>(S, T);
+
+enum Enum<S, T> {
+    SingleVariant(S, T)
+}
+
+type Alias<S> = Enum<S, isize>;
+
+trait Test {
+    fn test() -> TupleStruct<isize, isize> {
+        TupleStruct(0, 0)
+    }
+}
+
+impl Test for Alias<isize> {}
+
+fn test() -> TupleStruct<isize, isize> {
+    TupleStruct(0, 0)
+}
+
+fn main() {
+    let (mut a, mut b);
+    TupleStruct(a, .., b, ..) = TupleStruct(0, 1);
+    //~^ ERROR `..` can only be used once per tuple struct or variant pattern
+    Enum::SingleVariant(a, .., b, ..) = Enum::SingleVariant(0, 1);
+    //~^ ERROR `..` can only be used once per tuple struct or variant pattern
+
+    TupleStruct(a, a, b) = TupleStruct(1, 2);
+    //~^ ERROR this pattern has 3 fields, but the corresponding tuple struct has 2 fields
+    Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
+    //~^ ERROR this pattern has 3 fields, but the corresponding tuple variant 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
+    <Alias::<isize> as Test>::test() = TupleStruct(0, 0);
+    //~^ ERROR invalid left-hand side of assignment
+}
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..863eedecf7697
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/tuple_struct_destructure_fail.stderr
@@ -0,0 +1,62 @@
+error: `..` can only be used once per tuple struct or variant pattern
+  --> $DIR/tuple_struct_destructure_fail.rs:25:27
+   |
+LL |     TupleStruct(a, .., b, ..) = TupleStruct(0, 1);
+   |                    --     ^^ can only be used once per tuple struct or variant pattern
+   |                    |
+   |                    previously used here
+
+error: `..` can only be used once per tuple struct or variant pattern
+  --> $DIR/tuple_struct_destructure_fail.rs:27:35
+   |
+LL |     Enum::SingleVariant(a, .., b, ..) = Enum::SingleVariant(0, 1);
+   |                            --     ^^ can only be used once per tuple struct or variant 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:30:5
+   |
+LL | struct TupleStruct<S, T>(S, T);
+   | ------------------------------- tuple struct defined here
+...
+LL |     TupleStruct(a, a, b) = TupleStruct(1, 2);
+   |     ^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
+
+error[E0023]: this pattern has 3 fields, but the corresponding tuple variant has 2 fields
+  --> $DIR/tuple_struct_destructure_fail.rs:32:5
+   |
+LL |     SingleVariant(S, T)
+   |     ------------------- tuple variant defined here
+...
+LL |     Enum::SingleVariant(a, a, b) = Enum::SingleVariant(1, 2);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected 2 fields, found 3
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/tuple_struct_destructure_fail.rs:36:12
+   |
+LL |     test() = TupleStruct(0, 0);
+   |     ------ ^
+   |     |
+   |     cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/tuple_struct_destructure_fail.rs:38:14
+   |
+LL |     (test)() = TupleStruct(0, 0);
+   |     -------- ^
+   |     |
+   |     cannot assign to this expression
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/tuple_struct_destructure_fail.rs:40:38
+   |
+LL |     <Alias::<isize> as Test>::test() = TupleStruct(0, 0);
+   |     -------------------------------- ^
+   |     |
+   |     cannot assign to this expression
+
+error: aborting due to 7 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/destructuring-assignment/underscore-range-expr-gating.rs b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
new file mode 100644
index 0000000000000..b41f2f52a3d6f
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.rs
@@ -0,0 +1,8 @@
+fn main() {}
+
+struct S { x : u32 }
+
+#[cfg(FALSE)]
+fn foo() {
+    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..442e36cd3065e
--- /dev/null
+++ b/src/test/ui/destructuring-assignment/underscore-range-expr-gating.stderr
@@ -0,0 +1,12 @@
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/underscore-range-expr-gating.rs:7:15
+   |
+LL |     S { x: 5, .. };
+   |               ^^
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/issues/issue-77218.rs b/src/test/ui/issues/issue-77218.rs
index bc992c21dca5c..a6a2401795ff4 100644
--- a/src/test/ui/issues/issue-77218.rs
+++ b/src/test/ui/issues/issue-77218.rs
@@ -1,7 +1,11 @@
 fn main() {
     let value = [7u8];
-    while Some(0) = value.get(0) { //~ ERROR mismatched types
-        //~^ NOTE expected `bool`, found `()`
-        //~| HELP you might have meant to use pattern matching
+    while Some(0) = value.get(0) { //~ ERROR destructuring assignments are unstable
+        //~| ERROR invalid left-hand side of assignment
+        //~| ERROR mismatched types
+        //~| ERROR mismatched types
+
+        // FIXME The following diagnostic should also be emitted
+        // HELP you might have meant to use pattern matching
     }
 }
diff --git a/src/test/ui/issues/issue-77218.stderr b/src/test/ui/issues/issue-77218.stderr
index eca44725eb258..4f6fbaa2265d5 100644
--- a/src/test/ui/issues/issue-77218.stderr
+++ b/src/test/ui/issues/issue-77218.stderr
@@ -1,14 +1,38 @@
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/issue-77218.rs:3:19
+   |
+LL |     while Some(0) = value.get(0) {
+   |           ------- ^
+   |           |
+   |           cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/issue-77218.rs:3:19
+   |
+LL |     while Some(0) = value.get(0) {
+   |                -  ^
+   |                |
+   |                cannot assign to this expression
+
+error[E0308]: mismatched types
+  --> $DIR/issue-77218.rs:3:16
+   |
+LL |     while Some(0) = value.get(0) {
+   |                ^
+   |                |
+   |                expected integer, found `&u8`
+   |                help: consider dereferencing the borrow: `*0`
+
 error[E0308]: mismatched types
   --> $DIR/issue-77218.rs:3:11
    |
 LL |     while Some(0) = value.get(0) {
    |           ^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
-   |
-help: you might have meant to use pattern matching
-   |
-LL |     while let Some(0) = value.get(0) {
-   |           ^^^
 
-error: aborting due to previous error
+error: aborting due to 4 previous errors
 
-For more information about this error, try `rustc --explain E0308`.
+Some errors have detailed explanations: E0070, E0308, E0658.
+For more information about an error, try `rustc --explain E0070`.
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..ce1ee0cd06d48 100644
--- a/src/test/ui/suggestions/if-let-typo.stderr
+++ b/src/test/ui/suggestions/if-let-typo.stderr
@@ -9,23 +9,53 @@ help: you might have meant to use pattern matching
 LL |     if let Some(x) = foo {}
    |        ^^^
 
-error[E0308]: mismatched types
-  --> $DIR/if-let-typo.rs:5:8
+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 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/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 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/if-let-typo.rs:10:16
    |
-LL |     if let Some(foo) = bar {}
-   |        ^^^
-help: you might have meant to compare for equality
+LL |     if Some(3) = foo {}
+   |        ------- ^
+   |        |
+   |        cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
+error[E0308]: mismatched types
+  --> $DIR/if-let-typo.rs:4:8
+   |
+LL |     if Some(x) = foo {}
+   |        ^^^^^^^^^^^^^ expected `bool`, found `()`
+
+error[E0308]: mismatched types
+  --> $DIR/if-let-typo.rs:7:8
    |
-LL |     if Some(foo) == bar {}
-   |                  ^^
+LL |     if Some(foo) = bar {}
+   |        ^^^^^^^^^^^^^^^ expected `bool`, found `()`
 
 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 +65,21 @@ help: you might have meant to use pattern matching
 LL |     if let 3 = foo {}
    |        ^^^
 
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/if-let-typo.rs:10:16
+   |
+LL |     if Some(3) = foo {}
+   |             -  ^
+   |             |
+   |             cannot assign to this expression
+
 error[E0308]: mismatched types
-  --> $DIR/if-let-typo.rs:7:8
+  --> $DIR/if-let-typo.rs:10:8
    |
 LL |     if Some(3) = foo {}
    |        ^^^^^^^^^^^^^ expected `bool`, found `()`
-   |
-help: you might have meant to use pattern matching
-   |
-LL |     if let Some(3) = foo {}
-   |        ^^^
-help: you might have meant to compare for equality
-   |
-LL |     if Some(3) == foo {}
-   |                ^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 9 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`.
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<P<Expr>>, r: &Option<P<Expr>>) -> 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/tests/ui/crashes/ice-6250.stderr b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
index 8241dcd8feb7b..c38727316cd4e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6250.stderr
@@ -1,3 +1,14 @@
+error[E0658]: destructuring assignments are unstable
+  --> $DIR/ice-6250.rs:12:25
+   |
+LL |         Some(reference) = cache.data.get(key) {
+   |         --------------- ^
+   |         |
+   |         cannot assign to this expression
+   |
+   = note: see issue #71126 <https://github.com/rust-lang/rust/issues/71126> for more information
+   = help: add `#![feature(destructuring_assignment)]` to the crate attributes to enable
+
 error[E0601]: `main` function not found in crate `ice_6250`
   --> $DIR/ice-6250.rs:4:1
    |
@@ -10,18 +21,22 @@ LL | |     }
 LL | | }
    | |_^ consider adding a `main` function to `$DIR/ice-6250.rs`
 
+error[E0308]: mismatched types
+  --> $DIR/ice-6250.rs:12:14
+   |
+LL |         Some(reference) = cache.data.get(key) {
+   |              ^^^^^^^^^
+   |              |
+   |              expected integer, found `&i32`
+   |              help: consider dereferencing the borrow: `*reference`
+
 error[E0308]: mismatched types
   --> $DIR/ice-6250.rs:12:9
    |
 LL |         Some(reference) = cache.data.get(key) {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `bool`, found `()`
-   |
-help: you might have meant to use pattern matching
-   |
-LL |         let Some(reference) = cache.data.get(key) {
-   |         ^^^
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
-Some errors have detailed explanations: E0308, E0601.
+Some errors have detailed explanations: E0308, E0601, E0658.
 For more information about an error, try `rustc --explain E0308`.