about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-02-02 19:07:07 +0000
committerbors <bors@rust-lang.org>2022-02-02 19:07:07 +0000
commit27f5d830eb11cd7bdc834d6f0d78120976f75443 (patch)
tree2f08e48c477c4a63fdffcdd0fd7b3ae26dcd693d
parent7cd14d2f561a61e9838546f133afcf06038d761b (diff)
parent93155c59563a6a9017bc868c41243ffe7dc7fe89 (diff)
downloadrust-27f5d830eb11cd7bdc834d6f0d78120976f75443.tar.gz
rust-27f5d830eb11cd7bdc834d6f0d78120976f75443.zip
Auto merge of #93594 - matthiaskrgr:rollup-lcvhpdv, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #92528 (Make `Fingerprint::combine_commutative` associative)
 - #93221 ([borrowck] Fix help on mutating &self in async fns)
 - #93542 (Prevent lifetime elision in type alias)
 - #93546 (Validate that values in switch int terminator are unique)
 - #93571 (better suggestion for duplicated `where` clause)
 - #93574 (don't suggest adding `let` due to bad assignment expressions inside of `while` loop)
 - #93590 (More let_else adoptions)
 - #93592 (Remove unused dep from rustc_arena)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock1
-rw-r--r--compiler/rustc_arena/Cargo.toml1
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs4
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs1
-rw-r--r--compiler/rustc_attr/src/builtin.rs138
-rw-r--r--compiler/rustc_attr/src/lib.rs2
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs32
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/region_name.rs137
-rw-r--r--compiler/rustc_borrowck/src/lib.rs5
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs18
-rw-r--r--compiler/rustc_data_structures/src/fingerprint.rs5
-rw-r--r--compiler/rustc_data_structures/src/fingerprint/tests.rs14
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs47
-rw-r--r--compiler/rustc_mir_build/src/build/matches/test.rs94
-rw-r--r--compiler/rustc_parse/src/parser/generics.rs18
-rw-r--r--compiler/rustc_parse/src/parser/item.rs29
-rw-r--r--compiler/rustc_save_analysis/src/dump_visitor.rs5
-rw-r--r--compiler/rustc_save_analysis/src/lib.rs1
-rw-r--r--compiler/rustc_typeck/src/check/expr.rs24
-rw-r--r--src/librustdoc/clean/mod.rs16
-rw-r--r--src/test/rustdoc/lifetime-name.rs5
-rw-r--r--src/test/ui/borrowck/issue-93093.rs14
-rw-r--r--src/test/ui/borrowck/issue-93093.stderr12
-rw-r--r--src/test/ui/parser/bad-struct-following-where.rs2
-rw-r--r--src/test/ui/parser/bad-struct-following-where.stderr8
-rw-r--r--src/test/ui/parser/duplicate-where-clauses.rs19
-rw-r--r--src/test/ui/parser/duplicate-where-clauses.stderr80
-rw-r--r--src/test/ui/typeck/issue-93486.rs6
-rw-r--r--src/test/ui/typeck/issue-93486.stderr11
29 files changed, 494 insertions, 255 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 53ba61da2b0..208ce841759 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3403,7 +3403,6 @@ dependencies = [
 name = "rustc_arena"
 version = "0.0.0"
 dependencies = [
- "rustc_data_structures",
  "smallvec",
 ]
 
diff --git a/compiler/rustc_arena/Cargo.toml b/compiler/rustc_arena/Cargo.toml
index 33ccd044503..ee3a7b51b69 100644
--- a/compiler/rustc_arena/Cargo.toml
+++ b/compiler/rustc_arena/Cargo.toml
@@ -4,5 +4,4 @@ version = "0.0.0"
 edition = "2021"
 
 [dependencies]
-rustc_data_structures = { path = "../rustc_data_structures" }
 smallvec = { version = "1.6.1", features = ["union", "may_dangle"] }
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 8b4a0840df5..cacc36b616a 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -471,9 +471,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
                 // two imports.
                 for new_node_id in [id1, id2] {
                     let new_id = self.resolver.local_def_id(new_node_id);
-                    let res = if let Some(res) = resolutions.next() {
-                        res
-                    } else {
+                    let Some(res) = resolutions.next() else {
                         // Associate an HirId to both ids even if there is no resolution.
                         let _old = self
                             .node_id_to_hir_id
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index a594339296f..b1e601516ab 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -32,6 +32,7 @@
 
 #![feature(crate_visibility_modifier)]
 #![feature(box_patterns)]
+#![feature(let_else)]
 #![feature(never_type)]
 #![recursion_limit = "256"]
 #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
diff --git a/compiler/rustc_attr/src/builtin.rs b/compiler/rustc_attr/src/builtin.rs
index bab50df3dd5..8c5beb10258 100644
--- a/compiler/rustc_attr/src/builtin.rs
+++ b/compiler/rustc_attr/src/builtin.rs
@@ -217,85 +217,81 @@ where
                     let mut issue_num = None;
                     let mut is_soft = false;
                     for meta in metas {
-                        if let Some(mi) = meta.meta_item() {
-                            match mi.name_or_empty() {
-                                sym::feature => {
-                                    if !get(mi, &mut feature) {
-                                        continue 'outer;
-                                    }
+                        let Some(mi) = meta.meta_item() else {
+                            handle_errors(
+                                &sess.parse_sess,
+                                meta.span(),
+                                AttrError::UnsupportedLiteral("unsupported literal", false),
+                            );
+                            continue 'outer;
+                        };
+                        match mi.name_or_empty() {
+                            sym::feature => {
+                                if !get(mi, &mut feature) {
+                                    continue 'outer;
                                 }
-                                sym::reason => {
-                                    if !get(mi, &mut reason) {
-                                        continue 'outer;
-                                    }
+                            }
+                            sym::reason => {
+                                if !get(mi, &mut reason) {
+                                    continue 'outer;
+                                }
+                            }
+                            sym::issue => {
+                                if !get(mi, &mut issue) {
+                                    continue 'outer;
                                 }
-                                sym::issue => {
-                                    if !get(mi, &mut issue) {
-                                        continue 'outer;
-                                    }
 
-                                    // These unwraps are safe because `get` ensures the meta item
-                                    // is a name/value pair string literal.
-                                    issue_num = match issue.unwrap().as_str() {
-                                        "none" => None,
-                                        issue => {
-                                            let emit_diag = |msg: &str| {
-                                                struct_span_err!(
-                                                    diagnostic,
-                                                    mi.span,
-                                                    E0545,
-                                                    "`issue` must be a non-zero numeric string \
-                                                    or \"none\"",
-                                                )
-                                                .span_label(
-                                                    mi.name_value_literal_span().unwrap(),
-                                                    msg,
-                                                )
-                                                .emit();
-                                            };
-                                            match issue.parse() {
-                                                Ok(0) => {
-                                                    emit_diag(
-                                                        "`issue` must not be \"0\", \
-                                                        use \"none\" instead",
-                                                    );
-                                                    continue 'outer;
-                                                }
-                                                Ok(num) => NonZeroU32::new(num),
-                                                Err(err) => {
-                                                    emit_diag(&err.to_string());
-                                                    continue 'outer;
-                                                }
+                                // These unwraps are safe because `get` ensures the meta item
+                                // is a name/value pair string literal.
+                                issue_num = match issue.unwrap().as_str() {
+                                    "none" => None,
+                                    issue => {
+                                        let emit_diag = |msg: &str| {
+                                            struct_span_err!(
+                                                diagnostic,
+                                                mi.span,
+                                                E0545,
+                                                "`issue` must be a non-zero numeric string \
+                                                or \"none\"",
+                                            )
+                                            .span_label(mi.name_value_literal_span().unwrap(), msg)
+                                            .emit();
+                                        };
+                                        match issue.parse() {
+                                            Ok(0) => {
+                                                emit_diag(
+                                                    "`issue` must not be \"0\", \
+                                                    use \"none\" instead",
+                                                );
+                                                continue 'outer;
+                                            }
+                                            Ok(num) => NonZeroU32::new(num),
+                                            Err(err) => {
+                                                emit_diag(&err.to_string());
+                                                continue 'outer;
                                             }
                                         }
-                                    };
-                                }
-                                sym::soft => {
-                                    if !mi.is_word() {
-                                        let msg = "`soft` should not have any arguments";
-                                        sess.parse_sess.span_diagnostic.span_err(mi.span, msg);
                                     }
-                                    is_soft = true;
-                                }
-                                _ => {
-                                    handle_errors(
-                                        &sess.parse_sess,
-                                        meta.span(),
-                                        AttrError::UnknownMetaItem(
-                                            pprust::path_to_string(&mi.path),
-                                            &["feature", "reason", "issue", "soft"],
-                                        ),
-                                    );
-                                    continue 'outer;
+                                };
+                            }
+                            sym::soft => {
+                                if !mi.is_word() {
+                                    let msg = "`soft` should not have any arguments";
+                                    sess.parse_sess.span_diagnostic.span_err(mi.span, msg);
                                 }
+                                is_soft = true;
+                            }
+                            _ => {
+                                handle_errors(
+                                    &sess.parse_sess,
+                                    meta.span(),
+                                    AttrError::UnknownMetaItem(
+                                        pprust::path_to_string(&mi.path),
+                                        &["feature", "reason", "issue", "soft"],
+                                    ),
+                                );
+                                continue 'outer;
                             }
-                        } else {
-                            handle_errors(
-                                &sess.parse_sess,
-                                meta.span(),
-                                AttrError::UnsupportedLiteral("unsupported literal", false),
-                            );
-                            continue 'outer;
                         }
                     }
 
diff --git a/compiler/rustc_attr/src/lib.rs b/compiler/rustc_attr/src/lib.rs
index 3fb11f77872..c95c1c40a34 100644
--- a/compiler/rustc_attr/src/lib.rs
+++ b/compiler/rustc_attr/src/lib.rs
@@ -4,6 +4,8 @@
 //! The goal is to move the definition of `MetaItem` and things that don't need to be in `syntax`
 //! to this crate.
 
+#![feature(let_else)]
+
 #[macro_use]
 extern crate rustc_macros;
 
diff --git a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
index 8ed50075ecb..049c3aab979 100644
--- a/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
@@ -488,12 +488,32 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                                     // don't create labels for compiler-generated spans
                                     Some(_) => None,
                                     None => {
-                                        let (span, suggestion) = suggest_ampmut(
-                                            self.infcx.tcx,
-                                            local_decl,
-                                            opt_assignment_rhs_span,
-                                            *opt_ty_info,
-                                        );
+                                        let (span, suggestion) = if name != kw::SelfLower {
+                                            suggest_ampmut(
+                                                self.infcx.tcx,
+                                                local_decl,
+                                                opt_assignment_rhs_span,
+                                                *opt_ty_info,
+                                            )
+                                        } else {
+                                            match local_decl.local_info.as_deref() {
+                                                Some(LocalInfo::User(ClearCrossCrate::Set(
+                                                    mir::BindingForm::Var(mir::VarBindingForm {
+                                                        opt_ty_info: None,
+                                                        ..
+                                                    }),
+                                                ))) => {
+                                                    suggest_ampmut_self(self.infcx.tcx, local_decl)
+                                                }
+                                                // explicit self (eg `self: &'a Self`)
+                                                _ => suggest_ampmut(
+                                                    self.infcx.tcx,
+                                                    local_decl,
+                                                    opt_assignment_rhs_span,
+                                                    *opt_ty_info,
+                                                ),
+                                            }
+                                        };
                                         Some((true, span, suggestion))
                                     }
                                 }
diff --git a/compiler/rustc_borrowck/src/diagnostics/region_name.rs b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
index 01cc72121c7..3409f14c98b 100644
--- a/compiler/rustc_borrowck/src/diagnostics/region_name.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/region_name.rs
@@ -311,43 +311,39 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
                 ty::BoundRegionKind::BrEnv => {
                     let def_ty = self.regioncx.universal_regions().defining_ty;
 
-                    if let DefiningTy::Closure(_, substs) = def_ty {
-                        let args_span = if let hir::ExprKind::Closure(_, _, _, span, _) =
-                            tcx.hir().expect_expr(self.mir_hir_id()).kind
-                        {
-                            span
-                        } else {
-                            bug!("Closure is not defined by a closure expr");
-                        };
-                        let region_name = self.synthesize_region_name();
-
-                        let closure_kind_ty = substs.as_closure().kind_ty();
-                        let note = match closure_kind_ty.to_opt_closure_kind() {
-                            Some(ty::ClosureKind::Fn) => {
-                                "closure implements `Fn`, so references to captured variables \
-                                 can't escape the closure"
-                            }
-                            Some(ty::ClosureKind::FnMut) => {
-                                "closure implements `FnMut`, so references to captured variables \
-                                 can't escape the closure"
-                            }
-                            Some(ty::ClosureKind::FnOnce) => {
-                                bug!("BrEnv in a `FnOnce` closure");
-                            }
-                            None => bug!("Closure kind not inferred in borrow check"),
-                        };
-
-                        Some(RegionName {
-                            name: region_name,
-                            source: RegionNameSource::SynthesizedFreeEnvRegion(
-                                args_span,
-                                note.to_string(),
-                            ),
-                        })
-                    } else {
+                    let DefiningTy::Closure(_, substs) = def_ty else {
                         // Can't have BrEnv in functions, constants or generators.
                         bug!("BrEnv outside of closure.");
-                    }
+                    };
+                    let hir::ExprKind::Closure(_, _, _, args_span, _) =
+                        tcx.hir().expect_expr(self.mir_hir_id()).kind else {
+                        bug!("Closure is not defined by a closure expr");
+                    };
+                    let region_name = self.synthesize_region_name();
+
+                    let closure_kind_ty = substs.as_closure().kind_ty();
+                    let note = match closure_kind_ty.to_opt_closure_kind() {
+                        Some(ty::ClosureKind::Fn) => {
+                            "closure implements `Fn`, so references to captured variables \
+                                can't escape the closure"
+                        }
+                        Some(ty::ClosureKind::FnMut) => {
+                            "closure implements `FnMut`, so references to captured variables \
+                                can't escape the closure"
+                        }
+                        Some(ty::ClosureKind::FnOnce) => {
+                            bug!("BrEnv in a `FnOnce` closure");
+                        }
+                        None => bug!("Closure kind not inferred in borrow check"),
+                    };
+
+                    Some(RegionName {
+                        name: region_name,
+                        source: RegionNameSource::SynthesizedFreeEnvRegion(
+                            args_span,
+                            note.to_string(),
+                        ),
+                    })
                 }
 
                 ty::BoundRegionKind::BrAnon(_) => None,
@@ -765,48 +761,45 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
     fn get_future_inner_return_ty(&self, hir_ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
         let hir = self.infcx.tcx.hir();
 
-        if let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind {
-            let opaque_ty = hir.item(id);
-            if let hir::ItemKind::OpaqueTy(hir::OpaqueTy {
-                bounds:
-                    [
-                        hir::GenericBound::LangItemTrait(
-                            hir::LangItem::Future,
-                            _,
-                            _,
-                            hir::GenericArgs {
-                                bindings:
-                                    [
-                                        hir::TypeBinding {
-                                            ident: Ident { name: sym::Output, .. },
-                                            kind:
-                                                hir::TypeBindingKind::Equality {
-                                                    term: hir::Term::Ty(ty),
-                                                },
-                                            ..
-                                        },
-                                    ],
-                                ..
-                            },
-                        ),
-                    ],
-                ..
-            }) = opaque_ty.kind
-            {
-                ty
-            } else {
-                span_bug!(
-                    hir_ty.span,
-                    "bounds from lowered return type of async fn did not match expected format: {:?}",
-                    opaque_ty
-                );
-            }
-        } else {
+        let hir::TyKind::OpaqueDef(id, _) = hir_ty.kind else {
             span_bug!(
                 hir_ty.span,
                 "lowered return type of async fn is not OpaqueDef: {:?}",
                 hir_ty
             );
+        };
+        let opaque_ty = hir.item(id);
+        if let hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+            bounds:
+                [
+                    hir::GenericBound::LangItemTrait(
+                        hir::LangItem::Future,
+                        _,
+                        _,
+                        hir::GenericArgs {
+                            bindings:
+                                [
+                                    hir::TypeBinding {
+                                        ident: Ident { name: sym::Output, .. },
+                                        kind:
+                                            hir::TypeBindingKind::Equality { term: hir::Term::Ty(ty) },
+                                        ..
+                                    },
+                                ],
+                            ..
+                        },
+                    ),
+                ],
+            ..
+        }) = opaque_ty.kind
+        {
+            ty
+        } else {
+            span_bug!(
+                hir_ty.span,
+                "bounds from lowered return type of async fn did not match expected format: {:?}",
+                opaque_ty
+            );
         }
     }
 
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index c288cc96990..5597a8b0915 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -1427,9 +1427,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                             bug!("temporary should be initialized exactly once")
                         };
 
-                        let loc = match init.location {
-                            InitLocation::Statement(stmt) => stmt,
-                            _ => bug!("temporary initialized in arguments"),
+                        let InitLocation::Statement(loc) = init.location else {
+                            bug!("temporary initialized in arguments")
                         };
 
                         let body = self.body;
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index 22ef0b2dda5..cf15fc4ddc3 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -55,6 +55,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
             reachable_blocks: traversal::reachable_as_bitset(body),
             storage_liveness,
             place_cache: Vec::new(),
+            value_cache: Vec::new(),
         }
         .visit_body(body);
     }
@@ -109,6 +110,7 @@ struct TypeChecker<'a, 'tcx> {
     reachable_blocks: BitSet<BasicBlock>,
     storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
     place_cache: Vec<PlaceRef<'tcx>>,
+    value_cache: Vec<u128>,
 }
 
 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
@@ -398,6 +400,22 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     self.check_edge(location, target, EdgeKind::Normal);
                 }
                 self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
+
+                self.value_cache.clear();
+                self.value_cache.extend(targets.iter().map(|(value, _)| value));
+                let all_len = self.value_cache.len();
+                self.value_cache.sort_unstable();
+                self.value_cache.dedup();
+                let has_duplicates = all_len != self.value_cache.len();
+                if has_duplicates {
+                    self.fail(
+                        location,
+                        format!(
+                            "duplicated values in `SwitchInt` terminator: {:?}",
+                            terminator.kind,
+                        ),
+                    );
+                }
             }
             TerminatorKind::Drop { target, unwind, .. } => {
                 self.check_edge(location, *target, EdgeKind::Normal);
diff --git a/compiler/rustc_data_structures/src/fingerprint.rs b/compiler/rustc_data_structures/src/fingerprint.rs
index 525cd650dd2..e931379dd3a 100644
--- a/compiler/rustc_data_structures/src/fingerprint.rs
+++ b/compiler/rustc_data_structures/src/fingerprint.rs
@@ -3,6 +3,9 @@ use rustc_serialize::{Decodable, Encodable};
 use std::convert::TryInto;
 use std::hash::{Hash, Hasher};
 
+#[cfg(test)]
+mod tests;
+
 #[derive(Eq, PartialEq, Ord, PartialOrd, Debug, Clone, Copy)]
 #[repr(C)]
 pub struct Fingerprint(u64, u64);
@@ -54,7 +57,7 @@ impl Fingerprint {
 
         let c = a.wrapping_add(b);
 
-        Fingerprint((c >> 64) as u64, c as u64)
+        Fingerprint(c as u64, (c >> 64) as u64)
     }
 
     pub fn to_hex(&self) -> String {
diff --git a/compiler/rustc_data_structures/src/fingerprint/tests.rs b/compiler/rustc_data_structures/src/fingerprint/tests.rs
new file mode 100644
index 00000000000..9b0783e33ab
--- /dev/null
+++ b/compiler/rustc_data_structures/src/fingerprint/tests.rs
@@ -0,0 +1,14 @@
+use super::*;
+
+// Check that `combine_commutative` is order independent.
+#[test]
+fn combine_commutative_is_order_independent() {
+    let a = Fingerprint::new(0xf6622fb349898b06, 0x70be9377b2f9c610);
+    let b = Fingerprint::new(0xa9562bf5a2a5303c, 0x67d9b6c82034f13d);
+    let c = Fingerprint::new(0x0d013a27811dbbc3, 0x9a3f7b3d9142ec43);
+    let permutations = [(a, b, c), (a, c, b), (b, a, c), (b, c, a), (c, a, b), (c, b, a)];
+    let f = a.combine_commutative(b).combine_commutative(c);
+    for p in &permutations {
+        assert_eq!(f, p.0.combine_commutative(p.1).combine_commutative(p.2));
+    }
+}
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 3294f2cf641..6329bcee4fa 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -602,33 +602,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 for binding in &candidate_ref.bindings {
                     let local = self.var_local_id(binding.var_id, OutsideGuard);
 
-                    if let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
+                    let Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
                         VarBindingForm { opt_match_place: Some((ref mut match_place, _)), .. },
-                    )))) = self.local_decls[local].local_info
-                    {
-                        // `try_upvars_resolved` may fail if it is unable to resolve the given
-                        // `PlaceBuilder` inside a closure. In this case, we don't want to include
-                        // a scrutinee place. `scrutinee_place_builder` will fail for destructured
-                        // assignments. This is because a closure only captures the precise places
-                        // that it will read and as a result a closure may not capture the entire
-                        // tuple/struct and rather have individual places that will be read in the
-                        // final MIR.
-                        // Example:
-                        // ```
-                        // let foo = (0, 1);
-                        // let c = || {
-                        //    let (v1, v2) = foo;
-                        // };
-                        // ```
-                        if let Ok(match_pair_resolved) =
-                            initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
-                        {
-                            let place =
-                                match_pair_resolved.into_place(self.tcx, self.typeck_results);
-                            *match_place = Some(place);
-                        }
-                    } else {
+                    )))) = self.local_decls[local].local_info else {
                         bug!("Let binding to non-user variable.")
+                    };
+                    // `try_upvars_resolved` may fail if it is unable to resolve the given
+                    // `PlaceBuilder` inside a closure. In this case, we don't want to include
+                    // a scrutinee place. `scrutinee_place_builder` will fail for destructured
+                    // assignments. This is because a closure only captures the precise places
+                    // that it will read and as a result a closure may not capture the entire
+                    // tuple/struct and rather have individual places that will be read in the
+                    // final MIR.
+                    // Example:
+                    // ```
+                    // let foo = (0, 1);
+                    // let c = || {
+                    //    let (v1, v2) = foo;
+                    // };
+                    // ```
+                    if let Ok(match_pair_resolved) =
+                        initializer.clone().try_upvars_resolved(self.tcx, self.typeck_results)
+                    {
+                        let place = match_pair_resolved.into_place(self.tcx, self.typeck_results);
+                        *match_place = Some(place);
                     }
                 }
                 // All of the subcandidates should bind the same locals, so we
diff --git a/compiler/rustc_mir_build/src/build/matches/test.rs b/compiler/rustc_mir_build/src/build/matches/test.rs
index f4bf28bfa5c..49cd21c2137 100644
--- a/compiler/rustc_mir_build/src/build/matches/test.rs
+++ b/compiler/rustc_mir_build/src/build/matches/test.rs
@@ -227,16 +227,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let target_blocks = make_target_blocks(self);
                 let terminator = if *switch_ty.kind() == ty::Bool {
                     assert!(!options.is_empty() && options.len() <= 2);
-                    if let [first_bb, second_bb] = *target_blocks {
-                        let (true_bb, false_bb) = match options[0] {
-                            1 => (first_bb, second_bb),
-                            0 => (second_bb, first_bb),
-                            v => span_bug!(test.span, "expected boolean value but got {:?}", v),
-                        };
-                        TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb)
-                    } else {
+                    let [first_bb, second_bb] = *target_blocks else {
                         bug!("`TestKind::SwitchInt` on `bool` should have two targets")
-                    }
+                    };
+                    let (true_bb, false_bb) = match options[0] {
+                        1 => (first_bb, second_bb),
+                        0 => (second_bb, first_bb),
+                        v => span_bug!(test.span, "expected boolean value but got {:?}", v),
+                    };
+                    TerminatorKind::if_(self.tcx, Operand::Copy(place), true_bb, false_bb)
                 } else {
                     // The switch may be inexhaustive so we have a catch all block
                     debug_assert_eq!(options.len() + 1, target_blocks.len());
@@ -285,24 +284,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 let hi = self.literal_operand(test.span, hi);
                 let val = Operand::Copy(place);
 
-                if let [success, fail] = *target_blocks {
-                    self.compare(
-                        block,
-                        lower_bound_success,
-                        fail,
-                        source_info,
-                        BinOp::Le,
-                        lo,
-                        val.clone(),
-                    );
-                    let op = match *end {
-                        RangeEnd::Included => BinOp::Le,
-                        RangeEnd::Excluded => BinOp::Lt,
-                    };
-                    self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
-                } else {
+                let [success, fail] = *target_blocks else {
                     bug!("`TestKind::Range` should have two target blocks");
-                }
+                };
+                self.compare(
+                    block,
+                    lower_bound_success,
+                    fail,
+                    source_info,
+                    BinOp::Le,
+                    lo,
+                    val.clone(),
+                );
+                let op = match *end {
+                    RangeEnd::Included => BinOp::Le,
+                    RangeEnd::Excluded => BinOp::Lt,
+                };
+                self.compare(lower_bound_success, success, fail, source_info, op, val, hi);
             }
 
             TestKind::Len { len, op } => {
@@ -317,21 +315,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // expected = <N>
                 let expected = self.push_usize(block, source_info, len);
 
-                if let [true_bb, false_bb] = *target_blocks {
-                    // result = actual == expected OR result = actual < expected
-                    // branch based on result
-                    self.compare(
-                        block,
-                        true_bb,
-                        false_bb,
-                        source_info,
-                        op,
-                        Operand::Move(actual),
-                        Operand::Move(expected),
-                    );
-                } else {
+                let [true_bb, false_bb] = *target_blocks else {
                     bug!("`TestKind::Len` should have two target blocks");
-                }
+                };
+                // result = actual == expected OR result = actual < expected
+                // branch based on result
+                self.compare(
+                    block,
+                    true_bb,
+                    false_bb,
+                    source_info,
+                    op,
+                    Operand::Move(actual),
+                    Operand::Move(expected),
+                );
             }
         }
     }
@@ -459,16 +456,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         );
         self.diverge_from(block);
 
-        if let [success_block, fail_block] = *make_target_blocks(self) {
-            // check the result
-            self.cfg.terminate(
-                eq_block,
-                source_info,
-                TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block),
-            );
-        } else {
+        let [success_block, fail_block] = *make_target_blocks(self) else {
             bug!("`TestKind::Eq` should have two target blocks")
-        }
+        };
+        // check the result
+        self.cfg.terminate(
+            eq_block,
+            source_info,
+            TerminatorKind::if_(self.tcx, Operand::Move(eq_result), success_block, fail_block),
+        );
     }
 
     /// Given that we are performing `test` against `test_place`, this job
diff --git a/compiler/rustc_parse/src/parser/generics.rs b/compiler/rustc_parse/src/parser/generics.rs
index 419ea9cced0..62ed104aef3 100644
--- a/compiler/rustc_parse/src/parser/generics.rs
+++ b/compiler/rustc_parse/src/parser/generics.rs
@@ -4,7 +4,7 @@ use rustc_ast::token;
 use rustc_ast::{
     self as ast, Attribute, GenericBounds, GenericParam, GenericParamKind, WhereClause,
 };
-use rustc_errors::PResult;
+use rustc_errors::{Applicability, PResult};
 use rustc_span::symbol::kw;
 
 impl<'a> Parser<'a> {
@@ -256,7 +256,21 @@ impl<'a> Parser<'a> {
                 break;
             }
 
-            if !self.eat(&token::Comma) {
+            let prev_token = self.prev_token.span;
+            let ate_comma = self.eat(&token::Comma);
+
+            if self.eat_keyword_noexpect(kw::Where) {
+                let msg = &format!("cannot define duplicate `where` clauses on an item");
+                let mut err = self.struct_span_err(self.token.span, msg);
+                err.span_label(lo, "previous `where` clause starts here");
+                err.span_suggestion_verbose(
+                    prev_token.shrink_to_hi().to(self.prev_token.span),
+                    "consider joining the two `where` clauses into one",
+                    ",".to_owned(),
+                    Applicability::MaybeIncorrect,
+                );
+                err.emit();
+            } else if !ate_comma {
                 break;
             }
         }
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 06849b31256..93f5d79c0db 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1221,7 +1221,7 @@ impl<'a> Parser<'a> {
 
                 let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
                     // Parse a struct variant.
-                    let (fields, recovered) = this.parse_record_struct_body("struct")?;
+                    let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
                     VariantData::Struct(fields, recovered)
                 } else if this.check(&token::OpenDelim(token::Paren)) {
                     VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
@@ -1275,7 +1275,8 @@ impl<'a> Parser<'a> {
                 VariantData::Unit(DUMMY_NODE_ID)
             } else {
                 // If we see: `struct Foo<T> where T: Copy { ... }`
-                let (fields, recovered) = self.parse_record_struct_body("struct")?;
+                let (fields, recovered) =
+                    self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
                 VariantData::Struct(fields, recovered)
             }
         // No `where` so: `struct Foo<T>;`
@@ -1283,7 +1284,8 @@ impl<'a> Parser<'a> {
             VariantData::Unit(DUMMY_NODE_ID)
         // Record-style struct definition
         } else if self.token == token::OpenDelim(token::Brace) {
-            let (fields, recovered) = self.parse_record_struct_body("struct")?;
+            let (fields, recovered) =
+                self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
             VariantData::Struct(fields, recovered)
         // Tuple-style struct definition with optional where-clause.
         } else if self.token == token::OpenDelim(token::Paren) {
@@ -1313,10 +1315,12 @@ impl<'a> Parser<'a> {
 
         let vdata = if self.token.is_keyword(kw::Where) {
             generics.where_clause = self.parse_where_clause()?;
-            let (fields, recovered) = self.parse_record_struct_body("union")?;
+            let (fields, recovered) =
+                self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
             VariantData::Struct(fields, recovered)
         } else if self.token == token::OpenDelim(token::Brace) {
-            let (fields, recovered) = self.parse_record_struct_body("union")?;
+            let (fields, recovered) =
+                self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
             VariantData::Struct(fields, recovered)
         } else {
             let token_str = super::token_descr(&self.token);
@@ -1332,6 +1336,7 @@ impl<'a> Parser<'a> {
     fn parse_record_struct_body(
         &mut self,
         adt_ty: &str,
+        parsed_where: bool,
     ) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
         let mut fields = Vec::new();
         let mut recovered = false;
@@ -1353,9 +1358,19 @@ impl<'a> Parser<'a> {
             self.eat(&token::CloseDelim(token::Brace));
         } else {
             let token_str = super::token_descr(&self.token);
-            let msg = &format!("expected `where`, or `{{` after struct name, found {}", token_str);
+            let msg = &format!(
+                "expected {}`{{` after struct name, found {}",
+                if parsed_where { "" } else { "`where`, or " },
+                token_str
+            );
             let mut err = self.struct_span_err(self.token.span, msg);
-            err.span_label(self.token.span, "expected `where`, or `{` after struct name");
+            err.span_label(
+                self.token.span,
+                format!(
+                    "expected {}`{{` after struct name",
+                    if parsed_where { "" } else { "`where`, or " }
+                ),
+            );
             return Err(err);
         }
 
diff --git a/compiler/rustc_save_analysis/src/dump_visitor.rs b/compiler/rustc_save_analysis/src/dump_visitor.rs
index 8b4ab77dffb..a3f7e84b1d5 100644
--- a/compiler/rustc_save_analysis/src/dump_visitor.rs
+++ b/compiler/rustc_save_analysis/src/dump_visitor.rs
@@ -47,11 +47,10 @@ use rls_data::{
 
 use tracing::{debug, error};
 
+#[rustfmt::skip] // https://github.com/rust-lang/rustfmt/issues/5213
 macro_rules! down_cast_data {
     ($id:ident, $kind:ident, $sp:expr) => {
-        let $id = if let super::Data::$kind(data) = $id {
-            data
-        } else {
+        let super::Data::$kind($id) = $id else {
             span_bug!($sp, "unexpected data kind: {:?}", $id);
         };
     };
diff --git a/compiler/rustc_save_analysis/src/lib.rs b/compiler/rustc_save_analysis/src/lib.rs
index c14b459570f..2eebddb47df 100644
--- a/compiler/rustc_save_analysis/src/lib.rs
+++ b/compiler/rustc_save_analysis/src/lib.rs
@@ -1,6 +1,7 @@
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 #![feature(if_let_guard)]
 #![feature(nll)]
+#![feature(let_else)]
 #![recursion_limit = "256"]
 #![cfg_attr(not(bootstrap), allow(rustc::potential_query_instability))]
 
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index 0e1dbc53806..82cda5a2f2e 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -865,14 +865,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         ),
                     ..
                 }) => {
-                    // We have a situation like `while Some(0) = value.get(0) {`, where `while let`
-                    // was more likely intended.
-                    err.span_suggestion_verbose(
-                        expr.span.shrink_to_lo(),
-                        "you might have meant to use pattern destructuring",
-                        "let ".to_string(),
-                        Applicability::MachineApplicable,
-                    );
+                    // Check if our lhs is a child of the condition of a while loop
+                    let expr_is_ancestor = std::iter::successors(Some(lhs.hir_id), |id| {
+                        self.tcx.hir().find_parent_node(*id)
+                    })
+                    .take_while(|id| *id != parent)
+                    .any(|id| id == expr.hir_id);
+                    // if it is, then we have a situation like `while Some(0) = value.get(0) {`,
+                    // where `while let` was more likely intended.
+                    if expr_is_ancestor {
+                        err.span_suggestion_verbose(
+                            expr.span.shrink_to_lo(),
+                            "you might have meant to use pattern destructuring",
+                            "let ".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    }
                     break;
                 }
                 hir::Node::Item(_)
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 7d209accec9..95404b33822 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1427,15 +1427,25 @@ fn normalize<'tcx>(cx: &mut DocContext<'tcx>, ty: Ty<'_>) -> Option<Ty<'tcx>> {
         return None;
     }
 
+    use crate::rustc_trait_selection::infer::TyCtxtInferExt;
+    use crate::rustc_trait_selection::traits::query::normalize::AtExt;
+    use rustc_middle::traits::ObligationCause;
+
     // Try to normalize `<X as Y>::T` to a type
     let lifted = ty.lift_to_tcx(cx.tcx).unwrap();
-    match cx.tcx.try_normalize_erasing_regions(cx.param_env, lifted) {
+    let normalized = cx.tcx.infer_ctxt().enter(|infcx| {
+        infcx
+            .at(&ObligationCause::dummy(), cx.param_env)
+            .normalize(lifted)
+            .map(|resolved| infcx.resolve_vars_if_possible(resolved.value))
+    });
+    match normalized {
         Ok(normalized_value) => {
-            trace!("normalized {:?} to {:?}", ty, normalized_value);
+            debug!("normalized {:?} to {:?}", ty, normalized_value);
             Some(normalized_value)
         }
         Err(err) => {
-            info!("failed to normalize {:?}: {:?}", ty, err);
+            debug!("failed to normalize {:?}: {:?}", ty, err);
             None
         }
     }
diff --git a/src/test/rustdoc/lifetime-name.rs b/src/test/rustdoc/lifetime-name.rs
new file mode 100644
index 00000000000..5d30a745a61
--- /dev/null
+++ b/src/test/rustdoc/lifetime-name.rs
@@ -0,0 +1,5 @@
+#![crate_name = "foo"]
+
+// @has 'foo/type.Resolutions.html'
+// @has - '//*[@class="rust typedef"]' "pub type Resolutions<'tcx> = &'tcx u8;"
+pub type Resolutions<'tcx> = &'tcx u8;
diff --git a/src/test/ui/borrowck/issue-93093.rs b/src/test/ui/borrowck/issue-93093.rs
new file mode 100644
index 00000000000..f4db5ecafac
--- /dev/null
+++ b/src/test/ui/borrowck/issue-93093.rs
@@ -0,0 +1,14 @@
+// edition:2018
+struct S {
+    foo: usize,
+}
+impl S {
+    async fn bar(&self) { //~ HELP consider changing this to be a mutable reference
+        //~| SUGGESTION &mut self
+        self.foo += 1; //~ ERROR cannot assign to `self.foo`, which is behind a `&` reference [E0594]
+    }
+}
+
+fn main() {
+    S { foo: 1 }.bar();
+}
diff --git a/src/test/ui/borrowck/issue-93093.stderr b/src/test/ui/borrowck/issue-93093.stderr
new file mode 100644
index 00000000000..031128af476
--- /dev/null
+++ b/src/test/ui/borrowck/issue-93093.stderr
@@ -0,0 +1,12 @@
+error[E0594]: cannot assign to `self.foo`, which is behind a `&` reference
+  --> $DIR/issue-93093.rs:8:9
+   |
+LL |     async fn bar(&self) {
+   |                  ----- help: consider changing this to be a mutable reference: `&mut self`
+LL |
+LL |         self.foo += 1;
+   |         ^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be written
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0594`.
diff --git a/src/test/ui/parser/bad-struct-following-where.rs b/src/test/ui/parser/bad-struct-following-where.rs
new file mode 100644
index 00000000000..823880b1b42
--- /dev/null
+++ b/src/test/ui/parser/bad-struct-following-where.rs
@@ -0,0 +1,2 @@
+struct A where T: Sized !
+//~^ ERROR expected `{` after struct name, found
diff --git a/src/test/ui/parser/bad-struct-following-where.stderr b/src/test/ui/parser/bad-struct-following-where.stderr
new file mode 100644
index 00000000000..bb79776dc84
--- /dev/null
+++ b/src/test/ui/parser/bad-struct-following-where.stderr
@@ -0,0 +1,8 @@
+error: expected `{` after struct name, found `!`
+  --> $DIR/bad-struct-following-where.rs:1:25
+   |
+LL | struct A where T: Sized !
+   |                         ^ expected `{` after struct name
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/parser/duplicate-where-clauses.rs b/src/test/ui/parser/duplicate-where-clauses.rs
new file mode 100644
index 00000000000..9eb2ffb06f0
--- /dev/null
+++ b/src/test/ui/parser/duplicate-where-clauses.rs
@@ -0,0 +1,19 @@
+struct A where (): Sized where (): Sized {}
+//~^ ERROR cannot define duplicate `where` clauses on an item
+
+fn b() where (): Sized where (): Sized {}
+//~^ ERROR cannot define duplicate `where` clauses on an item
+
+enum C where (): Sized where (): Sized {}
+//~^ ERROR cannot define duplicate `where` clauses on an item
+
+struct D where (): Sized, where (): Sized {}
+//~^ ERROR cannot define duplicate `where` clauses on an item
+
+fn e() where (): Sized, where (): Sized {}
+//~^ ERROR cannot define duplicate `where` clauses on an item
+
+enum F where (): Sized, where (): Sized {}
+//~^ ERROR cannot define duplicate `where` clauses on an item
+
+fn main() {}
diff --git a/src/test/ui/parser/duplicate-where-clauses.stderr b/src/test/ui/parser/duplicate-where-clauses.stderr
new file mode 100644
index 00000000000..8250d4f1e05
--- /dev/null
+++ b/src/test/ui/parser/duplicate-where-clauses.stderr
@@ -0,0 +1,80 @@
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clauses.rs:1:32
+   |
+LL | struct A where (): Sized where (): Sized {}
+   |                -               ^
+   |                |
+   |                previous `where` clause starts here
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL | struct A where (): Sized, (): Sized {}
+   |                         ~
+
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clauses.rs:4:30
+   |
+LL | fn b() where (): Sized where (): Sized {}
+   |              -               ^
+   |              |
+   |              previous `where` clause starts here
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL | fn b() where (): Sized, (): Sized {}
+   |                       ~
+
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clauses.rs:7:30
+   |
+LL | enum C where (): Sized where (): Sized {}
+   |              -               ^
+   |              |
+   |              previous `where` clause starts here
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL | enum C where (): Sized, (): Sized {}
+   |                       ~
+
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clauses.rs:10:33
+   |
+LL | struct D where (): Sized, where (): Sized {}
+   |                -                ^
+   |                |
+   |                previous `where` clause starts here
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL | struct D where (): Sized, (): Sized {}
+   |                         ~
+
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clauses.rs:13:31
+   |
+LL | fn e() where (): Sized, where (): Sized {}
+   |              -                ^
+   |              |
+   |              previous `where` clause starts here
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL | fn e() where (): Sized, (): Sized {}
+   |                       ~
+
+error: cannot define duplicate `where` clauses on an item
+  --> $DIR/duplicate-where-clauses.rs:16:31
+   |
+LL | enum F where (): Sized, where (): Sized {}
+   |              -                ^
+   |              |
+   |              previous `where` clause starts here
+   |
+help: consider joining the two `where` clauses into one
+   |
+LL | enum F where (): Sized, (): Sized {}
+   |                       ~
+
+error: aborting due to 6 previous errors
+
diff --git a/src/test/ui/typeck/issue-93486.rs b/src/test/ui/typeck/issue-93486.rs
new file mode 100644
index 00000000000..f8f98d5c1c7
--- /dev/null
+++ b/src/test/ui/typeck/issue-93486.rs
@@ -0,0 +1,6 @@
+fn main() {
+    while let 1 = 1 {
+        vec![].last_mut().unwrap() = 3_u8;
+        //~^ ERROR invalid left-hand side of assignment
+    }
+}
diff --git a/src/test/ui/typeck/issue-93486.stderr b/src/test/ui/typeck/issue-93486.stderr
new file mode 100644
index 00000000000..70b5b63f1cb
--- /dev/null
+++ b/src/test/ui/typeck/issue-93486.stderr
@@ -0,0 +1,11 @@
+error[E0070]: invalid left-hand side of assignment
+  --> $DIR/issue-93486.rs:3:36
+   |
+LL |         vec![].last_mut().unwrap() = 3_u8;
+   |         -------------------------- ^
+   |         |
+   |         cannot assign to this expression
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0070`.