diff --git a/Cargo.lock b/Cargo.lock
index 6c3a4eefa5f28..2847e3cfb04b1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1564,7 +1564,7 @@ dependencies = [
  "rand_xoshiro",
  "sized-chunks",
  "typenum",
- "version_check 0.9.1",
+ "version_check",
 ]
 
 [[package]]
@@ -2014,9 +2014,9 @@ dependencies = [
 
 [[package]]
 name = "memchr"
-version = "2.2.0"
+version = "2.3.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39"
+checksum = "53445de381a1f436797497c61d851644d0e8e88e6140f22872ad33a704933978"
 
 [[package]]
 name = "memmap"
@@ -2604,23 +2604,23 @@ dependencies = [
 
 [[package]]
 name = "pulldown-cmark"
-version = "0.5.3"
+version = "0.6.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77043da1282374688ee212dc44b3f37ff929431de9c9adc3053bd3cee5630357"
+checksum = "1c205cc82214f3594e2d50686730314f817c67ffa80fe800cf0db78c3c2b9d9e"
 dependencies = [
  "bitflags",
+ "getopts",
  "memchr",
  "unicase",
 ]
 
 [[package]]
 name = "pulldown-cmark"
-version = "0.6.1"
+version = "0.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c205cc82214f3594e2d50686730314f817c67ffa80fe800cf0db78c3c2b9d9e"
+checksum = "2c2d7fd131800e0d63df52aff46201acaab70b431a4a1ec6f0343fe8e64f35a4"
 dependencies = [
  "bitflags",
- "getopts",
  "memchr",
  "unicase",
 ]
@@ -4160,7 +4160,7 @@ version = "0.0.0"
 dependencies = [
  "itertools 0.8.0",
  "minifier",
- "pulldown-cmark 0.5.3",
+ "pulldown-cmark 0.7.0",
  "rustc-rayon",
  "serde",
  "serde_json",
@@ -5160,11 +5160,11 @@ checksum = "535c204ee4d8434478593480b8f86ab45ec9aae0e83c568ca81abf0fd0e88f86"
 
 [[package]]
 name = "unicase"
-version = "2.5.1"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150"
+checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
 dependencies = [
- "version_check 0.1.5",
+ "version_check",
 ]
 
 [[package]]
@@ -5334,12 +5334,6 @@ dependencies = [
  "failure",
 ]
 
-[[package]]
-name = "version_check"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
-
 [[package]]
 name = "version_check"
 version = "0.9.1"
diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs
index 0aac6fb81e4a3..07352a3f9478a 100644
--- a/src/librustc/traits/fulfill.rs
+++ b/src/librustc/traits/fulfill.rs
@@ -18,10 +18,13 @@ use super::{FulfillmentError, FulfillmentErrorCode};
 use super::{ObligationCause, PredicateObligation};
 
 impl<'tcx> ForestObligation for PendingPredicateObligation<'tcx> {
-    type Predicate = ty::Predicate<'tcx>;
+    /// Note that we include both the `ParamEnv` and the `Predicate`,
+    /// as the `ParamEnv` can influence whether fulfillment succeeds
+    /// or fails.
+    type CacheKey = ty::ParamEnvAnd<'tcx, ty::Predicate<'tcx>>;
 
-    fn as_predicate(&self) -> &Self::Predicate {
-        &self.obligation.predicate
+    fn as_cache_key(&self) -> Self::CacheKey {
+        self.obligation.param_env.and(self.obligation.predicate)
     }
 }
 
diff --git a/src/librustc_ast_lowering/pat.rs b/src/librustc_ast_lowering/pat.rs
index 4c3c4ddac78ee..b42b12c4dd851 100644
--- a/src/librustc_ast_lowering/pat.rs
+++ b/src/librustc_ast_lowering/pat.rs
@@ -128,6 +128,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         let mut slice = None;
         let mut prev_rest_span = None;
 
+        // Lowers `$bm $ident @ ..` to `$bm $ident @ _`.
+        let lower_rest_sub = |this: &mut Self, pat, bm, ident, sub| {
+            let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
+            let node = this.lower_pat_ident(pat, bm, ident, lower_sub);
+            this.pat_with_node_id_of(pat, node)
+        };
+
         let mut iter = pats.iter();
         // Lower all the patterns until the first occurrence of a sub-slice pattern.
         for pat in iter.by_ref() {
@@ -142,9 +149,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
                 // Record, lower it to `$binding_mode $ident @ _`, and stop here.
                 PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
                     prev_rest_span = Some(sub.span);
-                    let lower_sub = |this: &mut Self| Some(this.pat_wild_with_node_id_of(sub));
-                    let node = self.lower_pat_ident(pat, bm, ident, lower_sub);
-                    slice = Some(self.pat_with_node_id_of(pat, node));
+                    slice = Some(lower_rest_sub(self, pat, bm, ident, sub));
                     break;
                 }
                 // It was not a subslice pattern so lower it normally.
@@ -157,9 +162,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             // There was a previous subslice pattern; make sure we don't allow more.
             let rest_span = match pat.kind {
                 PatKind::Rest => Some(pat.span),
-                PatKind::Ident(.., Some(ref sub)) if sub.is_rest() => {
-                    // The `HirValidator` is merciless; add a `_` pattern to avoid ICEs.
-                    after.push(self.pat_wild_with_node_id_of(pat));
+                PatKind::Ident(ref bm, ident, Some(ref sub)) if sub.is_rest() => {
+                    // #69103: Lower into `binding @ _` as above to avoid ICEs.
+                    after.push(lower_rest_sub(self, pat, bm, ident, sub));
                     Some(sub.span)
                 }
                 _ => None,
diff --git a/src/librustc_data_structures/obligation_forest/graphviz.rs b/src/librustc_data_structures/obligation_forest/graphviz.rs
index 0fd83dad56b1a..e1784f44fd112 100644
--- a/src/librustc_data_structures/obligation_forest/graphviz.rs
+++ b/src/librustc_data_structures/obligation_forest/graphviz.rs
@@ -52,7 +52,7 @@ impl<'a, O: ForestObligation + 'a> dot::Labeller<'a> for &'a ObligationForest<O>
 
     fn node_label(&self, index: &Self::Node) -> dot::LabelText<'_> {
         let node = &self.nodes[*index];
-        let label = format!("{:?} ({:?})", node.obligation.as_predicate(), node.state.get());
+        let label = format!("{:?} ({:?})", node.obligation.as_cache_key(), node.state.get());
 
         dot::LabelText::LabelStr(label.into())
     }
diff --git a/src/librustc_data_structures/obligation_forest/mod.rs b/src/librustc_data_structures/obligation_forest/mod.rs
index 0384776e9fbc9..334daddba8820 100644
--- a/src/librustc_data_structures/obligation_forest/mod.rs
+++ b/src/librustc_data_structures/obligation_forest/mod.rs
@@ -86,9 +86,13 @@ mod graphviz;
 mod tests;
 
 pub trait ForestObligation: Clone + Debug {
-    type Predicate: Clone + hash::Hash + Eq + Debug;
+    type CacheKey: Clone + hash::Hash + Eq + Debug;
 
-    fn as_predicate(&self) -> &Self::Predicate;
+    /// Converts this `ForestObligation` suitable for use as a cache key.
+    /// If two distinct `ForestObligations`s return the same cache key,
+    /// then it must be sound to use the result of processing one obligation
+    /// (e.g. success for error) for the other obligation
+    fn as_cache_key(&self) -> Self::CacheKey;
 }
 
 pub trait ObligationProcessor {
@@ -138,12 +142,12 @@ pub struct ObligationForest<O: ForestObligation> {
     nodes: Vec<Node<O>>,
 
     /// A cache of predicates that have been successfully completed.
-    done_cache: FxHashSet<O::Predicate>,
+    done_cache: FxHashSet<O::CacheKey>,
 
     /// A cache of the nodes in `nodes`, indexed by predicate. Unfortunately,
     /// its contents are not guaranteed to match those of `nodes`. See the
     /// comments in `process_obligation` for details.
-    active_cache: FxHashMap<O::Predicate, usize>,
+    active_cache: FxHashMap<O::CacheKey, usize>,
 
     /// A vector reused in compress(), to avoid allocating new vectors.
     node_rewrites: Vec<usize>,
@@ -157,7 +161,7 @@ pub struct ObligationForest<O: ForestObligation> {
     /// See [this][details] for details.
     ///
     /// [details]: https://github.com/rust-lang/rust/pull/53255#issuecomment-421184780
-    error_cache: FxHashMap<ObligationTreeId, FxHashSet<O::Predicate>>,
+    error_cache: FxHashMap<ObligationTreeId, FxHashSet<O::CacheKey>>,
 }
 
 #[derive(Debug)]
@@ -305,11 +309,12 @@ impl<O: ForestObligation> ObligationForest<O> {
 
     // Returns Err(()) if we already know this obligation failed.
     fn register_obligation_at(&mut self, obligation: O, parent: Option<usize>) -> Result<(), ()> {
-        if self.done_cache.contains(obligation.as_predicate()) {
+        if self.done_cache.contains(&obligation.as_cache_key()) {
+            debug!("register_obligation_at: ignoring already done obligation: {:?}", obligation);
             return Ok(());
         }
 
-        match self.active_cache.entry(obligation.as_predicate().clone()) {
+        match self.active_cache.entry(obligation.as_cache_key().clone()) {
             Entry::Occupied(o) => {
                 let node = &mut self.nodes[*o.get()];
                 if let Some(parent_index) = parent {
@@ -333,7 +338,7 @@ impl<O: ForestObligation> ObligationForest<O> {
                     && self
                         .error_cache
                         .get(&obligation_tree_id)
-                        .map(|errors| errors.contains(obligation.as_predicate()))
+                        .map(|errors| errors.contains(&obligation.as_cache_key()))
                         .unwrap_or(false);
 
                 if already_failed {
@@ -380,7 +385,7 @@ impl<O: ForestObligation> ObligationForest<O> {
         self.error_cache
             .entry(node.obligation_tree_id)
             .or_default()
-            .insert(node.obligation.as_predicate().clone());
+            .insert(node.obligation.as_cache_key().clone());
     }
 
     /// Performs a pass through the obligation list. This must
@@ -618,11 +623,11 @@ impl<O: ForestObligation> ObligationForest<O> {
                     // `self.nodes`. See the comment in `process_obligation`
                     // for more details.
                     if let Some((predicate, _)) =
-                        self.active_cache.remove_entry(node.obligation.as_predicate())
+                        self.active_cache.remove_entry(&node.obligation.as_cache_key())
                     {
                         self.done_cache.insert(predicate);
                     } else {
-                        self.done_cache.insert(node.obligation.as_predicate().clone());
+                        self.done_cache.insert(node.obligation.as_cache_key().clone());
                     }
                     if do_completed == DoCompleted::Yes {
                         // Extract the success stories.
@@ -635,7 +640,7 @@ impl<O: ForestObligation> ObligationForest<O> {
                     // We *intentionally* remove the node from the cache at this point. Otherwise
                     // tests must come up with a different type on every type error they
                     // check against.
-                    self.active_cache.remove(node.obligation.as_predicate());
+                    self.active_cache.remove(&node.obligation.as_cache_key());
                     self.insert_into_error_cache(index);
                     node_rewrites[index] = orig_nodes_len;
                     dead_nodes += 1;
diff --git a/src/librustc_data_structures/obligation_forest/tests.rs b/src/librustc_data_structures/obligation_forest/tests.rs
index e29335aab2808..01652465eea2c 100644
--- a/src/librustc_data_structures/obligation_forest/tests.rs
+++ b/src/librustc_data_structures/obligation_forest/tests.rs
@@ -4,9 +4,9 @@ use std::fmt;
 use std::marker::PhantomData;
 
 impl<'a> super::ForestObligation for &'a str {
-    type Predicate = &'a str;
+    type CacheKey = &'a str;
 
-    fn as_predicate(&self) -> &Self::Predicate {
+    fn as_cache_key(&self) -> Self::CacheKey {
         self
     }
 }
diff --git a/src/librustc_expand/mbe/macro_rules.rs b/src/librustc_expand/mbe/macro_rules.rs
index 9e6edee265c98..f6abcce4d3fac 100644
--- a/src/librustc_expand/mbe/macro_rules.rs
+++ b/src/librustc_expand/mbe/macro_rules.rs
@@ -191,9 +191,9 @@ fn generic_extension<'cx>(
     let mut best_failure: Option<(Token, &str)> = None;
 
     // We create a base parser that can be used for the "black box" parts.
-    // Every iteration needs a fresh copy of that base parser. However, the
-    // parser is not mutated on many of the iterations, particularly when
-    // dealing with macros like this:
+    // Every iteration needs a fresh copy of that parser. However, the parser
+    // is not mutated on many of the iterations, particularly when dealing with
+    // macros like this:
     //
     // macro_rules! foo {
     //     ("a") => (A);
@@ -209,11 +209,9 @@ fn generic_extension<'cx>(
     // hacky, but speeds up the `html5ever` benchmark significantly. (Issue
     // 68836 suggests a more comprehensive but more complex change to deal with
     // this situation.)
-    let base_parser = base_parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
+    let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
 
     for (i, lhs) in lhses.iter().enumerate() {
-        let mut parser = Cow::Borrowed(&base_parser);
-
         // try each arm's matchers
         let lhs_tt = match *lhs {
             mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
@@ -224,13 +222,14 @@ fn generic_extension<'cx>(
         // This is used so that if a matcher is not `Success(..)`ful,
         // then the spans which became gated when parsing the unsuccessful matcher
         // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
-        let mut gated_spans_snaphot = mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut());
+        let mut gated_spans_snapshot =
+            mem::take(&mut *cx.parse_sess.gated_spans.spans.borrow_mut());
 
-        match parse_tt(&mut parser, lhs_tt) {
+        match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
             Success(named_matches) => {
                 // The matcher was `Success(..)`ful.
                 // Merge the gated spans from parsing the matcher with the pre-existing ones.
-                cx.parse_sess.gated_spans.merge(gated_spans_snaphot);
+                cx.parse_sess.gated_spans.merge(gated_spans_snapshot);
 
                 let rhs = match rhses[i] {
                     // ignore delimiters
@@ -291,9 +290,9 @@ fn generic_extension<'cx>(
 
         // The matcher was not `Success(..)`ful.
         // Restore to the state before snapshotting and maybe try again.
-        mem::swap(&mut gated_spans_snaphot, &mut cx.parse_sess.gated_spans.spans.borrow_mut());
+        mem::swap(&mut gated_spans_snapshot, &mut cx.parse_sess.gated_spans.spans.borrow_mut());
     }
-    drop(base_parser);
+    drop(parser);
 
     let (token, label) = best_failure.expect("ran no matchers");
     let span = token.span.substitute_dummy(sp);
@@ -311,9 +310,8 @@ fn generic_extension<'cx>(
                 mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
                 _ => continue,
             };
-            let base_parser =
-                base_parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
-            match parse_tt(&mut Cow::Borrowed(&base_parser), lhs_tt) {
+            let parser = parser_from_cx(&cx.current_expansion, &cx.parse_sess, arg.clone());
+            match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
                 Success(_) => {
                     if comma_span.is_dummy() {
                         err.note("you might be missing a comma");
@@ -395,8 +393,8 @@ pub fn compile_declarative_macro(
         ),
     ];
 
-    let base_parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS);
-    let argument_map = match parse_tt(&mut Cow::Borrowed(&base_parser), &argument_gram) {
+    let parser = Parser::new(sess, body, None, true, true, rustc_parse::MACRO_ARGUMENTS);
+    let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
         Success(m) => m,
         Failure(token, msg) => {
             let s = parse_failure_msg(&token);
@@ -1212,7 +1210,7 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
     }
 }
 
-fn base_parser_from_cx<'cx>(
+fn parser_from_cx<'cx>(
     current_expansion: &'cx ExpansionData,
     sess: &'cx ParseSess,
     tts: TokenStream,
diff --git a/src/librustc_mir/transform/simplify_try.rs b/src/librustc_mir/transform/simplify_try.rs
index bd661195a4852..3f28f033047a2 100644
--- a/src/librustc_mir/transform/simplify_try.rs
+++ b/src/librustc_mir/transform/simplify_try.rs
@@ -52,6 +52,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
                 Some(x) => x,
             };
             if local_tmp_s0 != local_tmp_s1
+                // Avoid moving into ourselves.
+                || local_0 == local_1
                 // The field-and-variant information match up.
                 || vf_s0 != vf_s1
                 // Source and target locals have the same type.
@@ -64,6 +66,7 @@ impl<'tcx> MirPass<'tcx> for SimplifyArmIdentity {
             }
 
             // Right shape; transform!
+            s0.source_info = s2.source_info;
             match &mut s0.kind {
                 StatementKind::Assign(box (place, rvalue)) => {
                     *place = local_0.into();
diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs
index 028c39b80e426..0c1557a59c2bc 100644
--- a/src/librustc_typeck/check/op.rs
+++ b/src/librustc_typeck/check/op.rs
@@ -25,7 +25,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let ty =
             if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) {
-                self.enforce_builtin_binop_types(lhs, lhs_ty, rhs, rhs_ty, op);
+                self.enforce_builtin_binop_types(&lhs.span, lhs_ty, &rhs.span, rhs_ty, op);
                 self.tcx.mk_unit()
             } else {
                 return_ty
@@ -86,8 +86,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     && !rhs_ty.is_ty_var()
                     && is_builtin_binop(lhs_ty, rhs_ty, op)
                 {
-                    let builtin_return_ty =
-                        self.enforce_builtin_binop_types(lhs_expr, lhs_ty, rhs_expr, rhs_ty, op);
+                    let builtin_return_ty = self.enforce_builtin_binop_types(
+                        &lhs_expr.span,
+                        lhs_ty,
+                        &rhs_expr.span,
+                        rhs_ty,
+                        op,
+                    );
                     self.demand_suptype(expr.span, builtin_return_ty, return_ty);
                 }
 
@@ -98,19 +103,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn enforce_builtin_binop_types(
         &self,
-        lhs_expr: &'tcx hir::Expr<'tcx>,
+        lhs_span: &Span,
         lhs_ty: Ty<'tcx>,
-        rhs_expr: &'tcx hir::Expr<'tcx>,
+        rhs_span: &Span,
         rhs_ty: Ty<'tcx>,
         op: hir::BinOp,
     ) -> Ty<'tcx> {
         debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op));
 
+        // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
+        // (See https://github.com/rust-lang/rust/issues/57447.)
+        let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
+
         let tcx = self.tcx;
         match BinOpCategory::from(op) {
             BinOpCategory::Shortcircuit => {
-                self.demand_suptype(lhs_expr.span, tcx.mk_bool(), lhs_ty);
-                self.demand_suptype(rhs_expr.span, tcx.mk_bool(), rhs_ty);
+                self.demand_suptype(*lhs_span, tcx.mk_bool(), lhs_ty);
+                self.demand_suptype(*rhs_span, tcx.mk_bool(), rhs_ty);
                 tcx.mk_bool()
             }
 
@@ -121,13 +130,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             BinOpCategory::Math | BinOpCategory::Bitwise => {
                 // both LHS and RHS and result will have the same type
-                self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
+                self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
                 lhs_ty
             }
 
             BinOpCategory::Comparison => {
                 // both LHS and RHS and result will have the same type
-                self.demand_suptype(rhs_expr.span, lhs_ty, rhs_ty);
+                self.demand_suptype(*rhs_span, lhs_ty, rhs_ty);
                 tcx.mk_bool()
             }
         }
@@ -862,6 +871,14 @@ enum Op {
     Unary(hir::UnOp, Span),
 }
 
+/// Dereferences a single level of immutable referencing.
+fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> {
+    match ty.kind {
+        ty::Ref(_, ty, hir::Mutability::Not) => ty,
+        _ => ty,
+    }
+}
+
 /// Returns `true` if this is a built-in arithmetic operation (e.g., u32
 /// + u32, i16x4 == i16x4) and false if these types would have to be
 /// overloaded to be legal. There are two reasons that we distinguish
@@ -878,7 +895,11 @@ enum Op {
 /// Reason #2 is the killer. I tried for a while to always use
 /// overloaded logic and just check the types in constants/codegen after
 /// the fact, and it worked fine, except for SIMD types. -nmatsakis
-fn is_builtin_binop(lhs: Ty<'_>, rhs: Ty<'_>, op: hir::BinOp) -> bool {
+fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool {
+    // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work.
+    // (See https://github.com/rust-lang/rust/issues/57447.)
+    let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
+
     match BinOpCategory::from(op) {
         BinOpCategory::Shortcircuit => true,
 
diff --git a/src/librustc_typeck/check/pat.rs b/src/librustc_typeck/check/pat.rs
index 47baae6860896..72a2d56af153f 100644
--- a/src/librustc_typeck/check/pat.rs
+++ b/src/librustc_typeck/check/pat.rs
@@ -89,6 +89,18 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
     }
 }
 
+const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not);
+
+/// Mode for adjusting the expected type and binding mode.
+enum AdjustMode {
+    /// Peel off all immediate reference types.
+    Peel,
+    /// Reset binding mode to the inital mode.
+    Reset,
+    /// Pass on the input binding mode and expected type.
+    Pass,
+}
+
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     /// Type check the given top level pattern against the `expected` type.
     ///
@@ -105,8 +117,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         span: Option<Span>,
         origin_expr: bool,
     ) {
-        let def_bm = BindingMode::BindByValue(hir::Mutability::Not);
-        self.check_pat(pat, expected, def_bm, TopInfo { expected, origin_expr, span });
+        self.check_pat(pat, expected, INITIAL_BM, TopInfo { expected, origin_expr, span });
     }
 
     /// Type check the given `pat` against the `expected` type
@@ -123,12 +134,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     ) {
         debug!("check_pat(pat={:?},expected={:?},def_bm={:?})", pat, expected, def_bm);
 
-        let path_resolution = match &pat.kind {
+        let path_res = match &pat.kind {
             PatKind::Path(qpath) => Some(self.resolve_ty_and_res_ufcs(qpath, pat.hir_id, pat.span)),
             _ => None,
         };
-        let is_nrp = self.is_non_ref_pat(pat, path_resolution.map(|(res, ..)| res));
-        let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, is_nrp);
+        let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res));
+        let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode);
 
         let ty = match pat.kind {
             PatKind::Wild => expected,
@@ -141,7 +152,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti)
             }
             PatKind::Path(ref qpath) => {
-                self.check_pat_path(pat, path_resolution.unwrap(), qpath, expected)
+                self.check_pat_path(pat, path_res.unwrap(), qpath, expected)
             }
             PatKind::Struct(ref qpath, fields, etc) => {
                 self.check_pat_struct(pat, qpath, fields, etc, expected, def_bm, ti)
@@ -223,64 +234,68 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         pat: &'tcx Pat<'tcx>,
         expected: Ty<'tcx>,
         def_bm: BindingMode,
-        is_non_ref_pat: bool,
+        adjust_mode: AdjustMode,
     ) -> (Ty<'tcx>, BindingMode) {
-        if is_non_ref_pat {
-            debug!("pattern is non reference pattern");
-            self.peel_off_references(pat, expected, def_bm)
-        } else {
-            // When you encounter a `&pat` pattern, reset to "by
-            // value". This is so that `x` and `y` here are by value,
-            // as they appear to be:
-            //
-            // ```
-            // match &(&22, &44) {
-            //   (&x, &y) => ...
-            // }
-            // ```
-            //
-            // See issue #46688.
-            let def_bm = match pat.kind {
-                PatKind::Ref(..) => ty::BindByValue(hir::Mutability::Not),
-                _ => def_bm,
-            };
-            (expected, def_bm)
+        match adjust_mode {
+            AdjustMode::Pass => (expected, def_bm),
+            AdjustMode::Reset => (expected, INITIAL_BM),
+            AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm),
         }
     }
 
-    /// Is the pattern a "non reference pattern"?
+    /// How should the binding mode and expected type be adjusted?
+    ///
     /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`.
-    fn is_non_ref_pat(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> bool {
-        match pat.kind {
+    fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode {
+        match &pat.kind {
+            // Type checking these product-like types successfully always require
+            // that the expected type be of those types and not reference types.
             PatKind::Struct(..)
             | PatKind::TupleStruct(..)
             | PatKind::Tuple(..)
             | PatKind::Box(_)
             | PatKind::Range(..)
-            | PatKind::Slice(..) => true,
-            PatKind::Lit(ref lt) => {
-                let ty = self.check_expr(lt);
-                match ty.kind {
-                    ty::Ref(..) => false,
-                    _ => true,
-                }
-            }
+            | PatKind::Slice(..) => AdjustMode::Peel,
+            // String and byte-string literals result in types `&str` and `&[u8]` respectively.
+            // All other literals result in non-reference types.
+            // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`.
+            PatKind::Lit(lt) => match self.check_expr(lt).kind {
+                ty::Ref(..) => AdjustMode::Pass,
+                _ => AdjustMode::Peel,
+            },
             PatKind::Path(_) => match opt_path_res.unwrap() {
-                Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => false,
-                _ => true,
+                // These constants can be of a reference type, e.g. `const X: &u8 = &0;`.
+                // Peeling the reference types too early will cause type checking failures.
+                // Although it would be possible to *also* peel the types of the constants too.
+                Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => AdjustMode::Pass,
+                // In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which
+                // could successfully compile. The former being `Self` requires a unit struct.
+                // In either case, and unlike constants, the pattern itself cannot be
+                // a reference type wherefore peeling doesn't give up any expressivity.
+                _ => AdjustMode::Peel,
             },
-            // FIXME(or_patterns; Centril | dlrobertson): To keep things compiling
-            // for or-patterns at the top level, we need to make `p_0 | ... | p_n`
-            // a "non reference pattern". For example the following currently compiles:
+            // When encountering a `& mut? pat` pattern, reset to "by value".
+            // This is so that `x` and `y` here are by value, as they appear to be:
+            //
             // ```
-            // match &1 {
-            //     e @ &(1...2) | e @ &(3...4) => {}
-            //     _ => {}
+            // match &(&22, &44) {
+            //   (&x, &y) => ...
             // }
             // ```
             //
-            // We should consider whether we should do something special in nested or-patterns.
-            PatKind::Or(_) | PatKind::Wild | PatKind::Binding(..) | PatKind::Ref(..) => false,
+            // See issue #46688.
+            PatKind::Ref(..) => AdjustMode::Reset,
+            // A `_` pattern works with any expected type, so there's no need to do anything.
+            PatKind::Wild
+            // Bindings also work with whatever the expected type is,
+            // and moreover if we peel references off, that will give us the wrong binding type.
+            // Also, we can have a subpattern `binding @ pat`.
+            // Each side of the `@` should be treated independently (like with OR-patterns).
+            | PatKind::Binding(..)
+            // An OR-pattern just propagates to each individual alternative.
+            // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`.
+            // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`.
+            | PatKind::Or(_) => AdjustMode::Pass,
         }
     }
 
@@ -508,7 +523,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty;
         let eq_ty = match bm {
             ty::BindByReference(mutbl) => {
-                // If the binding is like `ref x | ref const x | ref mut x`
+                // If the binding is like `ref x | ref mut x`,
                 // then `x` is assigned a value of type `&M T` where M is the
                 // mutability and T is the expected type.
                 //
diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml
index 74e46b1eae362..4af13e4cd587a 100644
--- a/src/librustdoc/Cargo.toml
+++ b/src/librustdoc/Cargo.toml
@@ -9,7 +9,7 @@ name = "rustdoc"
 path = "lib.rs"
 
 [dependencies]
-pulldown-cmark = { version = "0.5.3", default-features = false }
+pulldown-cmark = { version = "0.7", default-features = false }
 minifier = "0.0.33"
 rayon = { version = "0.3.0", package = "rustc-rayon" }
 serde = { version = "1.0", features = ["derive"] }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index c87964af0200c..07c092a511c04 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -33,7 +33,7 @@ use crate::html::highlight;
 use crate::html::toc::TocBuilder;
 use crate::test;
 
-use pulldown_cmark::{html, CowStr, Event, Options, Parser, Tag};
+use pulldown_cmark::{html, CodeBlockKind, CowStr, Event, Options, Parser, Tag};
 
 #[cfg(test)]
 mod tests;
@@ -189,10 +189,15 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
         let compile_fail;
         let ignore;
         let edition;
-        if let Some(Event::Start(Tag::CodeBlock(lang))) = event {
-            let parse_result = LangString::parse(&lang, self.check_error_codes, false);
+        if let Some(Event::Start(Tag::CodeBlock(kind))) = event {
+            let parse_result = match kind {
+                CodeBlockKind::Fenced(ref lang) => {
+                    LangString::parse(&lang, self.check_error_codes, false)
+                }
+                CodeBlockKind::Indented => LangString::all_false(),
+            };
             if !parse_result.rust {
-                return Some(Event::Start(Tag::CodeBlock(lang)));
+                return Some(Event::Start(Tag::CodeBlock(kind)));
             }
             compile_fail = parse_result.compile_fail;
             ignore = parse_result.ignore;
@@ -370,11 +375,11 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
         }
 
         let event = self.inner.next();
-        if let Some(Event::Start(Tag::Header(level))) = event {
+        if let Some(Event::Start(Tag::Heading(level))) = event {
             let mut id = String::new();
             for event in &mut self.inner {
                 match &event {
-                    Event::End(Tag::Header(..)) => break,
+                    Event::End(Tag::Heading(..)) => break,
                     Event::Text(text) | Event::Code(text) => {
                         id.extend(text.chars().filter_map(slugify));
                     }
@@ -391,10 +396,10 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
                 let mut html_header = String::new();
                 html::push_html(&mut html_header, self.buf.iter().cloned());
                 let sec = builder.push(level as u32, html_header, id.clone());
-                self.buf.push_front(Event::InlineHtml(format!("{} ", sec).into()));
+                self.buf.push_front(Event::Html(format!("{} ", sec).into()));
             }
 
-            self.buf.push_back(Event::InlineHtml(format!("</a></h{}>", level).into()));
+            self.buf.push_back(Event::Html(format!("</a></h{}>", level).into()));
 
             let start_tags = format!(
                 "<h{level} id=\"{id}\" class=\"section-header\">\
@@ -402,7 +407,7 @@ impl<'a, 'b, 'ids, I: Iterator<Item = Event<'a>>> Iterator for HeadingLinks<'a,
                 id = id,
                 level = level
             );
-            return Some(Event::InlineHtml(start_tags.into()));
+            return Some(Event::Html(start_tags.into()));
         }
         event
     }
@@ -556,40 +561,44 @@ pub fn find_testable_code<T: test::Tester>(
     error_codes: ErrorCodes,
     enable_per_target_ignores: bool,
 ) {
-    let mut parser = Parser::new(doc);
+    let mut parser = Parser::new(doc).into_offset_iter();
     let mut prev_offset = 0;
     let mut nb_lines = 0;
     let mut register_header = None;
-    while let Some(event) = parser.next() {
+    while let Some((event, offset)) = parser.next() {
         match event {
-            Event::Start(Tag::CodeBlock(s)) => {
-                let offset = parser.get_offset();
-
-                let block_info = if s.is_empty() {
-                    LangString::all_false()
-                } else {
-                    LangString::parse(&*s, error_codes, enable_per_target_ignores)
+            Event::Start(Tag::CodeBlock(kind)) => {
+                let block_info = match kind {
+                    CodeBlockKind::Fenced(ref lang) => {
+                        if lang.is_empty() {
+                            LangString::all_false()
+                        } else {
+                            LangString::parse(lang, error_codes, enable_per_target_ignores)
+                        }
+                    }
+                    CodeBlockKind::Indented => LangString::all_false(),
                 };
                 if !block_info.rust {
                     continue;
                 }
+
                 let mut test_s = String::new();
 
-                while let Some(Event::Text(s)) = parser.next() {
+                while let Some((Event::Text(s), _)) = parser.next() {
                     test_s.push_str(&s);
                 }
-
                 let text = test_s
                     .lines()
                     .map(|l| map_line(l).for_code())
                     .collect::<Vec<Cow<'_, str>>>()
                     .join("\n");
-                nb_lines += doc[prev_offset..offset].lines().count();
-                let line = tests.get_line() + nb_lines;
+
+                nb_lines += doc[prev_offset..offset.start].lines().count();
+                let line = tests.get_line() + nb_lines + 1;
                 tests.add_test(text, block_info, line);
-                prev_offset = offset;
+                prev_offset = offset.start;
             }
-            Event::Start(Tag::Header(level)) => {
+            Event::Start(Tag::Heading(level)) => {
                 register_header = Some(level as u32);
             }
             Event::Text(ref s) if register_header.is_some() => {
@@ -783,7 +792,7 @@ impl MarkdownHtml<'_> {
 
         // Treat inline HTML as plain text.
         let p = p.map(|event| match event {
-            Event::Html(text) | Event::InlineHtml(text) => Event::Text(text),
+            Event::Html(text) => Event::Text(text),
             _ => event,
         });
 
@@ -842,10 +851,10 @@ pub fn plain_summary_line(md: &str) -> String {
             let next_event = next_event.unwrap();
             let (ret, is_in) = match next_event {
                 Event::Start(Tag::Paragraph) => (None, 1),
-                Event::Start(Tag::Header(_)) => (None, 1),
+                Event::Start(Tag::Heading(_)) => (None, 1),
                 Event::Code(code) => (Some(format!("`{}`", code)), 0),
                 Event::Text(ref s) if self.is_in > 0 => (Some(s.as_ref().to_owned()), 0),
-                Event::End(Tag::Paragraph) | Event::End(Tag::Header(_)) => (None, -1),
+                Event::End(Tag::Paragraph) | Event::End(Tag::Heading(_)) => (None, -1),
                 _ => (None, 0),
             };
             if is_in > 0 || (is_in < 0 && self.is_in > 0) {
@@ -940,68 +949,79 @@ crate fn rust_code_blocks(md: &str) -> Vec<RustCodeBlock> {
         return code_blocks;
     }
 
-    let mut p = Parser::new_ext(md, opts());
-
-    let mut code_block_start = 0;
-    let mut code_start = 0;
-    let mut is_fenced = false;
-    let mut previous_offset = 0;
-    let mut in_rust_code_block = false;
-    while let Some(event) = p.next() {
-        let offset = p.get_offset();
+    let mut p = Parser::new_ext(md, opts()).into_offset_iter();
 
+    while let Some((event, offset)) = p.next() {
         match event {
             Event::Start(Tag::CodeBlock(syntax)) => {
-                let lang_string = if syntax.is_empty() {
-                    LangString::all_false()
-                } else {
-                    LangString::parse(&*syntax, ErrorCodes::Yes, false)
-                };
-
-                if lang_string.rust {
-                    in_rust_code_block = true;
-
-                    code_start = offset;
-                    code_block_start = match md[previous_offset..offset].find("```") {
-                        Some(fence_idx) => {
-                            is_fenced = true;
-                            previous_offset + fence_idx
+                let (syntax, code_start, code_end, range, is_fenced) = match syntax {
+                    CodeBlockKind::Fenced(syntax) => {
+                        let syntax = syntax.as_ref();
+                        let lang_string = if syntax.is_empty() {
+                            LangString::all_false()
+                        } else {
+                            LangString::parse(&*syntax, ErrorCodes::Yes, false)
+                        };
+                        if !lang_string.rust {
+                            continue;
                         }
-                        None => {
-                            is_fenced = false;
-                            offset
+                        let syntax = if syntax.is_empty() { None } else { Some(syntax.to_owned()) };
+                        let (code_start, mut code_end) = match p.next() {
+                            Some((Event::Text(_), offset)) => (offset.start, offset.end),
+                            Some((_, sub_offset)) => {
+                                let code = Range { start: sub_offset.start, end: sub_offset.start };
+                                code_blocks.push(RustCodeBlock {
+                                    is_fenced: true,
+                                    range: offset,
+                                    code,
+                                    syntax,
+                                });
+                                continue;
+                            }
+                            None => {
+                                let code = Range { start: offset.end, end: offset.end };
+                                code_blocks.push(RustCodeBlock {
+                                    is_fenced: true,
+                                    range: offset,
+                                    code,
+                                    syntax,
+                                });
+                                continue;
+                            }
+                        };
+                        while let Some((Event::Text(_), offset)) = p.next() {
+                            code_end = offset.end;
                         }
-                    };
-                }
-            }
-            Event::End(Tag::CodeBlock(syntax)) if in_rust_code_block => {
-                in_rust_code_block = false;
-
-                let code_block_end = if is_fenced {
-                    let fence_str = &md[previous_offset..offset].chars().rev().collect::<String>();
-                    fence_str
-                        .find("```")
-                        .map(|fence_idx| offset - fence_idx)
-                        .unwrap_or_else(|| offset)
-                } else if md.as_bytes().get(offset).map(|b| *b == b'\n').unwrap_or_default() {
-                    offset - 1
-                } else {
-                    offset
+                        (syntax, code_start, code_end, offset, true)
+                    }
+                    CodeBlockKind::Indented => {
+                        // The ending of the offset goes too far sometime so we reduce it by one in
+                        // these cases.
+                        if offset.end > offset.start
+                            && md.get(offset.end..=offset.end) == Some(&"\n")
+                        {
+                            (
+                                None,
+                                offset.start,
+                                offset.end,
+                                Range { start: offset.start, end: offset.end - 1 },
+                                false,
+                            )
+                        } else {
+                            (None, offset.start, offset.end, offset, false)
+                        }
+                    }
                 };
 
-                let code_end = if is_fenced { previous_offset } else { code_block_end };
-
                 code_blocks.push(RustCodeBlock {
                     is_fenced,
-                    range: Range { start: code_block_start, end: code_block_end },
+                    range,
                     code: Range { start: code_start, end: code_end },
-                    syntax: if !syntax.is_empty() { Some(syntax.into_string()) } else { None },
+                    syntax,
                 });
             }
             _ => (),
         }
-
-        previous_offset = offset;
     }
 
     code_blocks
diff --git a/src/test/ui/array-slice-vec/issue-69103-extra-binding-subslice.rs b/src/test/ui/array-slice-vec/issue-69103-extra-binding-subslice.rs
new file mode 100644
index 0000000000000..061b0d675b31f
--- /dev/null
+++ b/src/test/ui/array-slice-vec/issue-69103-extra-binding-subslice.rs
@@ -0,0 +1,18 @@
+// We used to not lower the extra `b @ ..` into `b @ _` which meant that no type
+// was registered for the binding `b` although it passed through resolve.
+// This resulted in an ICE (#69103).
+
+fn main() {
+    let [a @ .., b @ ..] = &mut [1, 2];
+    //~^ ERROR `..` can only be used once per slice pattern
+    b;
+
+    let [.., c @ ..] = [1, 2];
+    //~^ ERROR `..` can only be used once per slice pattern
+    c;
+
+    // This never ICEd, but let's make sure it won't regress either.
+    let (.., d @ ..) = (1, 2);
+    //~^ ERROR `..` patterns are not allowed here
+    d;
+}
diff --git a/src/test/ui/array-slice-vec/issue-69103-extra-binding-subslice.stderr b/src/test/ui/array-slice-vec/issue-69103-extra-binding-subslice.stderr
new file mode 100644
index 0000000000000..9432e2f0c9d34
--- /dev/null
+++ b/src/test/ui/array-slice-vec/issue-69103-extra-binding-subslice.stderr
@@ -0,0 +1,26 @@
+error: `..` can only be used once per slice pattern
+  --> $DIR/issue-69103-extra-binding-subslice.rs:6:22
+   |
+LL |     let [a @ .., b @ ..] = &mut [1, 2];
+   |              --      ^^ can only be used once per slice pattern
+   |              |
+   |              previously used here
+
+error: `..` can only be used once per slice pattern
+  --> $DIR/issue-69103-extra-binding-subslice.rs:10:18
+   |
+LL |     let [.., c @ ..] = [1, 2];
+   |          --      ^^ can only be used once per slice pattern
+   |          |
+   |          previously used here
+
+error: `..` patterns are not allowed here
+  --> $DIR/issue-69103-extra-binding-subslice.rs:15:18
+   |
+LL |     let (.., d @ ..) = (1, 2);
+   |                  ^^
+   |
+   = note: only allowed in tuple, tuple struct, and slice patterns
+
+error: aborting due to 3 previous errors
+
diff --git a/src/test/ui/inference/infer-binary-operand-behind-reference.rs b/src/test/ui/inference/infer-binary-operand-behind-reference.rs
new file mode 100644
index 0000000000000..0c0a3171ee9a1
--- /dev/null
+++ b/src/test/ui/inference/infer-binary-operand-behind-reference.rs
@@ -0,0 +1,30 @@
+// check-pass
+
+fn main() {
+    // Test that we can infer the type of binary operands when
+    // references are involved, on various types and operators.
+    let _: u8 = 0 + 0;
+    let _: u8 = 0 + &0;
+    let _: u8 = &0 + 0;
+    let _: u8 = &0 + &0;
+
+    let _: f32 = 0.0 + 0.0;
+    let _: f32 = 0.0 + &0.0;
+    let _: f32 = &0.0 + 0.0;
+    let _: f32 = &0.0 + &0.0;
+
+    let _: u8 = 0 << 0;
+    let _: u8 = 0 << &0;
+    let _: u8 = &0 << 0;
+    let _: u8 = &0 << &0;
+
+    // Test type inference when variable types are indirectly inferred.
+    let a = 22;
+    let _: usize = a + &44;
+
+    // When we have no expected type, the types of the operands is the default type.
+    let _ = 0 + 0;
+    let _ = 0 + &0;
+    let _ = &0 + 0;
+    let _ = &0 + &0;
+}
diff --git a/src/test/ui/or-patterns/or-pattern-mismatch.rs b/src/test/ui/or-patterns/or-pattern-mismatch.rs
deleted file mode 100644
index 973954bca5a5e..0000000000000
--- a/src/test/ui/or-patterns/or-pattern-mismatch.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-enum Blah { A(isize, isize, usize), B(isize, isize) }
-
-fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, y) => { } } }
-//~^ ERROR mismatched types
diff --git a/src/test/ui/or-patterns/or-pattern-mismatch.stderr b/src/test/ui/or-patterns/or-pattern-mismatch.stderr
deleted file mode 100644
index bc288e0625075..0000000000000
--- a/src/test/ui/or-patterns/or-pattern-mismatch.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/or-pattern-mismatch.rs:3:68
-   |
-LL | fn main() { match Blah::A(1, 1, 2) { Blah::A(_, x, y) | Blah::B(x, y) => { } } }
-   |                   ---------------- this expression has type `Blah` ^ expected `usize`, found `isize`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs
new file mode 100644
index 0000000000000..5ec7dc6962c18
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.rs
@@ -0,0 +1,68 @@
+// Here we test type checking of bindings when combined with or-patterns.
+// Specifically, we ensure that introducing bindings of different types result in type errors.
+
+#![feature(or_patterns)]
+
+fn main() {
+    enum Blah {
+        A(isize, isize, usize),
+        B(isize, isize),
+    }
+
+    match Blah::A(1, 1, 2) {
+        Blah::A(_, x, y) | Blah::B(x, y) => {} //~ ERROR mismatched types
+    }
+
+    match Some(Blah::A(1, 1, 2)) {
+        Some(Blah::A(_, x, y) | Blah::B(x, y)) => {} //~ ERROR mismatched types
+    }
+
+    match (0u8, 1u16) {
+        (x, y) | (y, x) => {} //~ ERROR mismatched types
+                              //~^ ERROR mismatched types
+    }
+
+    match Some((0u8, Some((1u16, 2u32)))) {
+        Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
+        //~^ ERROR mismatched types
+        //~| ERROR mismatched types
+        //~| ERROR mismatched types
+        //~| ERROR mismatched types
+        _ => {}
+    }
+
+    if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) {
+        //~^ ERROR mismatched types
+    }
+
+    if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) {
+        //~^ ERROR mismatched types
+    }
+
+    if let (x, y) | (y, x) = (0u8, 1u16) {
+        //~^ ERROR mismatched types
+        //~| ERROR mismatched types
+    }
+
+    if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
+        //~^ ERROR mismatched types
+        //~| ERROR mismatched types
+        //~| ERROR mismatched types
+        //~| ERROR mismatched types
+    = Some((0u8, Some((1u16, 2u32))))
+    {}
+
+    let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2);
+    //~^ ERROR mismatched types
+
+    let (x, y) | (y, x) = (0u8, 1u16);
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
+
+    fn f1((Blah::A(_, x, y) | Blah::B(x, y)): Blah) {}
+    //~^ ERROR mismatched types
+
+    fn f2(((x, y) | (y, x)): (u8, u16)) {}
+    //~^ ERROR mismatched types
+    //~| ERROR mismatched types
+}
diff --git a/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr
new file mode 100644
index 0000000000000..5094f04b9204f
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-binding-type-mismatch.stderr
@@ -0,0 +1,183 @@
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:13:39
+   |
+LL |     match Blah::A(1, 1, 2) {
+   |           ---------------- this expression has type `main::Blah`
+LL |         Blah::A(_, x, y) | Blah::B(x, y) => {}
+   |                                       ^ expected `usize`, found `isize`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:17:44
+   |
+LL |     match Some(Blah::A(1, 1, 2)) {
+   |           ---------------------- this expression has type `std::option::Option<main::Blah>`
+LL |         Some(Blah::A(_, x, y) | Blah::B(x, y)) => {}
+   |                                            ^ expected `usize`, found `isize`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:21:19
+   |
+LL |     match (0u8, 1u16) {
+   |           ----------- this expression has type `(u8, u16)`
+LL |         (x, y) | (y, x) => {}
+   |                   ^ expected `u16`, found `u8`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:21:22
+   |
+LL |     match (0u8, 1u16) {
+   |           ----------- this expression has type `(u8, u16)`
+LL |         (x, y) | (y, x) => {}
+   |                      ^ expected `u8`, found `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:26:41
+   |
+LL |     match Some((0u8, Some((1u16, 2u32)))) {
+   |           ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+LL |         Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
+   |                                         ^ expected `u16`, found `u8`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:26:50
+   |
+LL |     match Some((0u8, Some((1u16, 2u32)))) {
+   |           ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+LL |         Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
+   |                                                  ^ expected `u8`, found `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:26:59
+   |
+LL |     match Some((0u8, Some((1u16, 2u32)))) {
+   |           ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+LL |         Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
+   |                                                           ^ expected `u32`, found `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:26:62
+   |
+LL |     match Some((0u8, Some((1u16, 2u32)))) {
+   |           ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+LL |         Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x)))) => {}
+   |                                                              ^ expected `u8`, found `u32`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:34:42
+   |
+LL |     if let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2) {
+   |                                          ^    ---------------- this expression has type `main::Blah`
+   |                                          |
+   |                                          expected `usize`, found `isize`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:38:47
+   |
+LL |     if let Some(Blah::A(_, x, y) | Blah::B(x, y)) = Some(Blah::A(1, 1, 2)) {
+   |                                               ^     ---------------------- this expression has type `std::option::Option<main::Blah>`
+   |                                               |
+   |                                               expected `usize`, found `isize`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:42:22
+   |
+LL |     if let (x, y) | (y, x) = (0u8, 1u16) {
+   |                      ^       ----------- this expression has type `(u8, u16)`
+   |                      |
+   |                      expected `u16`, found `u8`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:42:25
+   |
+LL |     if let (x, y) | (y, x) = (0u8, 1u16) {
+   |                         ^    ----------- this expression has type `(u8, u16)`
+   |                         |
+   |                         expected `u8`, found `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:47:44
+   |
+LL |     if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
+   |                                            ^ expected `u16`, found `u8`
+...
+LL |     = Some((0u8, Some((1u16, 2u32))))
+   |       ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:47:53
+   |
+LL |     if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
+   |                                                     ^ expected `u8`, found `u16`
+...
+LL |     = Some((0u8, Some((1u16, 2u32))))
+   |       ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:47:62
+   |
+LL |     if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
+   |                                                              ^ expected `u32`, found `u16`
+...
+LL |     = Some((0u8, Some((1u16, 2u32))))
+   |       ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:47:65
+   |
+LL |     if let Some((x, Some((y, z)))) | Some((y, Some((x, z) | (z, x))))
+   |                                                                 ^ expected `u8`, found `u32`
+...
+LL |     = Some((0u8, Some((1u16, 2u32))))
+   |       ------------------------------- this expression has type `std::option::Option<(u8, std::option::Option<(u16, u32)>)>`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:55:39
+   |
+LL |     let Blah::A(_, x, y) | Blah::B(x, y) = Blah::A(1, 1, 2);
+   |                                       ^    ---------------- this expression has type `main::Blah`
+   |                                       |
+   |                                       expected `usize`, found `isize`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:58:19
+   |
+LL |     let (x, y) | (y, x) = (0u8, 1u16);
+   |                   ^       ----------- this expression has type `(u8, u16)`
+   |                   |
+   |                   expected `u16`, found `u8`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:58:22
+   |
+LL |     let (x, y) | (y, x) = (0u8, 1u16);
+   |                      ^    ----------- this expression has type `(u8, u16)`
+   |                      |
+   |                      expected `u8`, found `u16`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:62:42
+   |
+LL |     fn f1((Blah::A(_, x, y) | Blah::B(x, y)): Blah) {}
+   |                                          ^    ---- expected due to this
+   |                                          |
+   |                                          expected `usize`, found `isize`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:65:22
+   |
+LL |     fn f2(((x, y) | (y, x)): (u8, u16)) {}
+   |                      ^       --------- expected due to this
+   |                      |
+   |                      expected `u16`, found `u8`
+
+error[E0308]: mismatched types
+  --> $DIR/or-patterns-binding-type-mismatch.rs:65:25
+   |
+LL |     fn f2(((x, y) | (y, x)): (u8, u16)) {}
+   |                         ^    --------- expected due to this
+   |                         |
+   |                         expected `u8`, found `u16`
+
+error: aborting due to 22 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs b/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs
new file mode 100644
index 0000000000000..3b6047c7be47d
--- /dev/null
+++ b/src/test/ui/or-patterns/or-patterns-default-binding-modes.rs
@@ -0,0 +1,132 @@
+// Test that or-patterns are pass-through with respect to default binding modes.
+
+// check-pass
+
+#![feature(or_patterns)]
+#![allow(irrefutable_let_patterns)]
+
+fn main() {
+    // A regression test for a mistake we made at one point:
+    match &1 {
+        e @ &(1..=2) | e @ &(3..=4) => {}
+        _ => {}
+    }
+
+    match &0 {
+        0 | &1 => {}
+        _ => {}
+    }
+
+    type R<'a> = &'a Result<u8, u8>;
+
+    let res: R<'_> = &Ok(0);
+
+    match res {
+        // Alternatives propagate expected type / binding mode independently.
+        Ok(mut x) | &Err(mut x) => drop::<u8>(x),
+    }
+    match res {
+        &(Ok(x) | Err(x)) => drop::<u8>(x),
+    }
+    match res {
+        Ok(x) | Err(x) => drop::<&u8>(x),
+    }
+    if let Ok(mut x) | &Err(mut x) = res {
+        drop::<u8>(x);
+    }
+    if let &(Ok(x) | Err(x)) = res {
+        drop::<u8>(x);
+    }
+    let Ok(mut x) | &Err(mut x) = res;
+    drop::<u8>(x);
+    let &(Ok(x) | Err(x)) = res;
+    drop::<u8>(x);
+    let Ok(x) | Err(x) = res;
+    drop::<&u8>(x);
+    for Ok(mut x) | &Err(mut x) in std::iter::once(res) {
+        drop::<u8>(x);
+    }
+    for &(Ok(x) | Err(x)) in std::iter::once(res) {
+        drop::<u8>(x);
+    }
+    for Ok(x) | Err(x) in std::iter::once(res) {
+        drop::<&u8>(x);
+    }
+    fn f1((Ok(mut x) | &Err(mut x)): R<'_>) {
+        drop::<u8>(x);
+    }
+    fn f2(&(Ok(x) | Err(x)): R<'_>) {
+        drop::<u8>(x);
+    }
+    fn f3((Ok(x) | Err(x)): R<'_>) {
+        drop::<&u8>(x);
+    }
+
+    // Wrap inside another type (a product for a simplity with irrefutable contexts).
+    #[derive(Copy, Clone)]
+    struct Wrap<T>(T);
+    let wres = Wrap(res);
+
+    match wres {
+        Wrap(Ok(mut x) | &Err(mut x)) => drop::<u8>(x),
+    }
+    match wres {
+        Wrap(&(Ok(x) | Err(x))) => drop::<u8>(x),
+    }
+    match wres {
+        Wrap(Ok(x) | Err(x)) => drop::<&u8>(x),
+    }
+    if let Wrap(Ok(mut x) | &Err(mut x)) = wres {
+        drop::<u8>(x);
+    }
+    if let Wrap(&(Ok(x) | Err(x))) = wres {
+        drop::<u8>(x);
+    }
+    if let Wrap(Ok(x) | Err(x)) = wres {
+        drop::<&u8>(x);
+    }
+    let Wrap(Ok(mut x) | &Err(mut x)) = wres;
+    drop::<u8>(x);
+    let Wrap(&(Ok(x) | Err(x))) = wres;
+    drop::<u8>(x);
+    let Wrap(Ok(x) | Err(x)) = wres;
+    drop::<&u8>(x);
+    for Wrap(Ok(mut x) | &Err(mut x)) in std::iter::once(wres) {
+        drop::<u8>(x);
+    }
+    for Wrap(&(Ok(x) | Err(x))) in std::iter::once(wres) {
+        drop::<u8>(x);
+    }
+    for Wrap(Ok(x) | Err(x)) in std::iter::once(wres) {
+        drop::<&u8>(x);
+    }
+    fn fw1(Wrap(Ok(mut x) | &Err(mut x)): Wrap<R<'_>>) {
+        drop::<u8>(x);
+    }
+    fn fw2(Wrap(&(Ok(x) | Err(x))): Wrap<R<'_>>) {
+        drop::<u8>(x);
+    }
+    fn fw3(Wrap(Ok(x) | Err(x)): Wrap<R<'_>>) {
+        drop::<&u8>(x);
+    }
+
+    // Nest some more:
+
+    enum Tri<P> {
+        A(P),
+        B(P),
+        C(P),
+    }
+
+    let tri = &Tri::A(&Ok(0));
+    let Tri::A(Ok(mut x) | Err(mut x))
+    | Tri::B(&Ok(mut x) | Err(mut x))
+    | &Tri::C(Ok(mut x) | Err(mut x)) = tri;
+    drop::<u8>(x);
+
+    match tri {
+        Tri::A(Ok(mut x) | Err(mut x))
+        | Tri::B(&Ok(mut x) | Err(mut x))
+        | &Tri::C(Ok(mut x) | Err(mut x)) => drop::<u8>(x),
+    }
+}