about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-06-19 08:56:43 +0000
committerbors <bors@rust-lang.org>2024-06-19 08:56:43 +0000
commit5978f353309af5131e16e67897292f659ad54d19 (patch)
tree3297b0694599c33be9861ed8b5c4c266ee285afe
parent3c0f019b3c56c1a0e6e0cbed6f8a8b0b245e5665 (diff)
parentb980f6d7b147ba924ff3e746ac1367a41c4778e0 (diff)
downloadrust-5978f353309af5131e16e67897292f659ad54d19.tar.gz
rust-5978f353309af5131e16e67897292f659ad54d19.zip
Auto merge of #126671 - fmease:rollup-dmet4fi, r=fmease
Rollup of 7 pull requests

Successful merges:

 - #123782 (Test that opaque types can't have themselves as a hidden type with incompatible lifetimes)
 - #124580 (Suggest removing unused tuple fields if they are the last fields)
 - #125787 (Migrate `bin-emit-no-symbols` `run-make` test to `rmake`)
 - #126553 (match lowering: expand or-candidates mixed with candidates above)
 - #126594 (Make async drop code more consistent with regular drop code)
 - #126654 (Make pretty printing for `f16` and `f128` consistent)
 - #126656 (rustc_type_ir: Omit some struct fields from Debug output)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--compiler/rustc_codegen_ssa/src/back/symbol_export.rs23
-rw-r--r--compiler/rustc_middle/src/query/mod.rs33
-rw-r--r--compiler/rustc_middle/src/ty/instance.rs23
-rw-r--r--compiler/rustc_middle/src/ty/print/pretty.rs10
-rw-r--r--compiler/rustc_mir_build/src/build/matches/mod.rs217
-rw-r--r--compiler/rustc_passes/messages.ftl9
-rw-r--r--compiler/rustc_passes/src/dead.rs58
-rw-r--r--compiler/rustc_passes/src/errors.rs19
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs1
-rw-r--r--compiler/rustc_type_ir/src/binder.rs1
-rw-r--r--compiler/rustc_type_ir/src/predicate.rs4
-rw-r--r--compiler/rustc_type_ir/src/ty_kind.rs4
-rw-r--r--src/tools/run-make-support/src/llvm.rs28
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt1
-rw-r--r--tests/mir-opt/or_pattern.rs24
-rw-r--r--tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir100
-rw-r--r--tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir75
-rw-r--r--tests/run-make/bin-emit-no-symbols/app.rs (renamed from tests/run-make/issue-51671/app.rs)0
-rw-r--r--tests/run-make/bin-emit-no-symbols/rmake.rs16
-rw-r--r--tests/run-make/issue-51671/Makefile9
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.next.stderr8
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.old.stderr16
-rw-r--r--tests/ui/higher-ranked/structually-relate-aliases.stderr4
-rw-r--r--tests/ui/lint/dead-code/tuple-struct-field.rs31
-rw-r--r--tests/ui/lint/dead-code/tuple-struct-field.stderr37
-rw-r--r--tests/ui/or-patterns/bindings-runpass-2.rs1
-rw-r--r--tests/ui/or-patterns/inner-or-pat.or3.stderr2
-rw-r--r--tests/ui/or-patterns/inner-or-pat.or4.stderr2
-rw-r--r--tests/ui/or-patterns/inner-or-pat.rs4
-rw-r--r--tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs21
-rw-r--r--tests/ui/or-patterns/search-via-bindings.rs22
-rw-r--r--tests/ui/or-patterns/simplification_subtleties.rs11
-rw-r--r--tests/ui/traits/next-solver/issue-118950-root-region.stderr8
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal.rs14
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr15
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs14
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr20
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs22
-rw-r--r--tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr10
39 files changed, 698 insertions, 219 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
index 1d61c156409..6abe4fa1c38 100644
--- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
+++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs
@@ -371,7 +371,7 @@ fn exported_symbols_provider_local(
                 }) => {
                     // A little sanity-check
                     debug_assert_eq!(
-                        args.non_erasable_generics(tcx, def_id).skip(1).next(),
+                        args.non_erasable_generics(tcx, def_id).next(),
                         Some(GenericArgKind::Type(ty))
                     );
                     symbols.push((
@@ -422,10 +422,7 @@ fn upstream_monomorphizations_provider(
                 }
                 ExportedSymbol::AsyncDropGlueCtorShim(ty) => {
                     if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id {
-                        (
-                            async_drop_in_place_fn_def_id,
-                            tcx.mk_args(&[tcx.lifetimes.re_erased.into(), ty.into()]),
-                        )
+                        (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()]))
                     } else {
                         // `drop_in_place` in place does not exist, don't try
                         // to use it.
@@ -473,11 +470,16 @@ fn upstream_drop_glue_for_provider<'tcx>(
     tcx: TyCtxt<'tcx>,
     args: GenericArgsRef<'tcx>,
 ) -> Option<CrateNum> {
-    if let Some(def_id) = tcx.lang_items().drop_in_place_fn() {
-        tcx.upstream_monomorphizations_for(def_id).and_then(|monos| monos.get(&args).cloned())
-    } else {
-        None
-    }
+    let def_id = tcx.lang_items().drop_in_place_fn()?;
+    tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned()
+}
+
+fn upstream_async_drop_glue_for_provider<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    args: GenericArgsRef<'tcx>,
+) -> Option<CrateNum> {
+    let def_id = tcx.lang_items().async_drop_in_place_fn()?;
+    tcx.upstream_monomorphizations_for(def_id)?.get(&args).cloned()
 }
 
 fn is_unreachable_local_definition_provider(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
@@ -491,6 +493,7 @@ pub fn provide(providers: &mut Providers) {
     providers.upstream_monomorphizations = upstream_monomorphizations_provider;
     providers.is_unreachable_local_definition = is_unreachable_local_definition_provider;
     providers.upstream_drop_glue_for = upstream_drop_glue_for_provider;
+    providers.upstream_async_drop_glue_for = upstream_async_drop_glue_for_provider;
     providers.wasm_import_module_map = wasm_import_module_map;
     providers.extern_queries.is_reachable_non_generic = is_reachable_non_generic_provider_extern;
     providers.extern_queries.upstream_monomorphizations_for =
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index 0f08694c4ea..c5afecffb07 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -1554,12 +1554,13 @@ rustc_queries! {
         }
     }
 
-    /// The entire set of monomorphizations the local crate can safely link
-    /// to because they are exported from upstream crates. Do not depend on
-    /// this directly, as its value changes anytime a monomorphization gets
-    /// added or removed in any upstream crate. Instead use the narrower
-    /// `upstream_monomorphizations_for`, `upstream_drop_glue_for`, or, even
-    /// better, `Instance::upstream_monomorphization()`.
+    /// The entire set of monomorphizations the local crate can safely
+    /// link to because they are exported from upstream crates. Do
+    /// not depend on this directly, as its value changes anytime
+    /// a monomorphization gets added or removed in any upstream
+    /// crate. Instead use the narrower `upstream_monomorphizations_for`,
+    /// `upstream_drop_glue_for`, `upstream_async_drop_glue_for`, or,
+    /// even better, `Instance::upstream_monomorphization()`.
     query upstream_monomorphizations(_: ()) -> &'tcx DefIdMap<UnordMap<GenericArgsRef<'tcx>, CrateNum>> {
         arena_cache
         desc { "collecting available upstream monomorphizations" }
@@ -1601,6 +1602,26 @@ rustc_queries! {
         desc { "available upstream drop-glue for `{:?}`", args }
     }
 
+    /// Returns the upstream crate that exports async-drop-glue for
+    /// the given type (`args` is expected to be a single-item list
+    /// containing the type one wants async-drop-glue for).
+    ///
+    /// This is a subset of `upstream_monomorphizations_for` in order
+    /// to increase dep-tracking granularity. Otherwise adding or
+    /// removing any type with async-drop-glue in any upstream crate
+    /// would invalidate all functions calling async-drop-glue of an
+    /// upstream type.
+    ///
+    /// You likely want to call `Instance::upstream_monomorphization()`
+    /// instead of invoking this query directly.
+    ///
+    /// NOTE: This query could easily be extended to also support other
+    ///       common functions that have are large set of monomorphizations
+    ///       (like `Clone::clone` for example).
+    query upstream_async_drop_glue_for(args: GenericArgsRef<'tcx>) -> Option<CrateNum> {
+        desc { "available upstream async-drop-glue for `{:?}`", args }
+    }
+
     /// Returns a list of all `extern` blocks of a crate.
     query foreign_modules(_: CrateNum) -> &'tcx FxIndexMap<DefId, ForeignModule> {
         arena_cache
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 5718264c944..efaf9c7231b 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -219,8 +219,9 @@ impl<'tcx> Instance<'tcx> {
             InstanceKind::Item(def) => tcx
                 .upstream_monomorphizations_for(def)
                 .and_then(|monos| monos.get(&self.args).cloned()),
-            InstanceKind::DropGlue(_, Some(_)) | InstanceKind::AsyncDropGlueCtorShim(_, _) => {
-                tcx.upstream_drop_glue_for(self.args)
+            InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args),
+            InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => {
+                tcx.upstream_async_drop_glue_for(self.args)
             }
             _ => None,
         }
@@ -256,7 +257,7 @@ impl<'tcx> InstanceKind<'tcx> {
         match self {
             ty::InstanceKind::Item(def) => Some(def),
             ty::InstanceKind::DropGlue(def_id, Some(_))
-            | InstanceKind::AsyncDropGlueCtorShim(def_id, _)
+            | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_))
             | InstanceKind::ThreadLocalShim(def_id) => Some(def_id),
             InstanceKind::VTableShim(..)
             | InstanceKind::ReifyShim(..)
@@ -267,6 +268,7 @@ impl<'tcx> InstanceKind<'tcx> {
             | ty::InstanceKind::ConstructCoroutineInClosureShim { .. }
             | ty::InstanceKind::CoroutineKindShim { .. }
             | InstanceKind::DropGlue(..)
+            | InstanceKind::AsyncDropGlueCtorShim(..)
             | InstanceKind::CloneShim(..)
             | InstanceKind::FnPtrAddrShim(..) => None,
         }
@@ -312,7 +314,9 @@ impl<'tcx> InstanceKind<'tcx> {
         if self.requires_inline(tcx) {
             return true;
         }
-        if let ty::InstanceKind::DropGlue(.., Some(ty)) = *self {
+        if let ty::InstanceKind::DropGlue(.., Some(ty))
+        | ty::InstanceKind::AsyncDropGlueCtorShim(.., Some(ty)) = *self
+        {
             // Drop glue generally wants to be instantiated at every codegen
             // unit, but without an #[inline] hint. We should make this
             // available to normal end-users.
@@ -327,9 +331,14 @@ impl<'tcx> InstanceKind<'tcx> {
             // drops of `Option::None` before LTO. We also respect the intent of
             // `#[inline]` on `Drop::drop` implementations.
             return ty.ty_adt_def().map_or(true, |adt_def| {
-                adt_def
-                    .destructor(tcx)
-                    .map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.did))
+                match *self {
+                    ty::InstanceKind::DropGlue(..) => adt_def.destructor(tcx).map(|dtor| dtor.did),
+                    ty::InstanceKind::AsyncDropGlueCtorShim(..) => {
+                        adt_def.async_destructor(tcx).map(|dtor| dtor.ctor)
+                    }
+                    _ => unreachable!(),
+                }
+                .map_or_else(|| adt_def.is_enum(), |did| tcx.cross_crate_inlinable(did))
             });
         }
         if let ty::InstanceKind::ThreadLocalShim(..) = *self {
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index de1796d4800..72cb3e13402 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -7,7 +7,7 @@ use crate::ty::{
     ConstInt, Expr, ParamConst, ScalarInt, Term, TermKind, TypeFoldable, TypeSuperFoldable,
     TypeSuperVisitable, TypeVisitable, TypeVisitableExt,
 };
-use rustc_apfloat::ieee::{Double, Single};
+use rustc_apfloat::ieee::{Double, Half, Quad, Single};
 use rustc_apfloat::Float;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_data_structures::unord::UnordMap;
@@ -1710,6 +1710,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
             ty::Bool if int == ScalarInt::FALSE => p!("false"),
             ty::Bool if int == ScalarInt::TRUE => p!("true"),
             // Float
+            ty::Float(ty::FloatTy::F16) => {
+                let val = Half::try_from(int).unwrap();
+                p!(write("{}{}f16", val, if val.is_finite() { "" } else { "_" }))
+            }
             ty::Float(ty::FloatTy::F32) => {
                 let val = Single::try_from(int).unwrap();
                 p!(write("{}{}f32", val, if val.is_finite() { "" } else { "_" }))
@@ -1718,6 +1722,10 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
                 let val = Double::try_from(int).unwrap();
                 p!(write("{}{}f64", val, if val.is_finite() { "" } else { "_" }))
             }
+            ty::Float(ty::FloatTy::F128) => {
+                let val = Quad::try_from(int).unwrap();
+                p!(write("{}{}f128", val, if val.is_finite() { "" } else { "_" }))
+            }
             // Int
             ty::Uint(_) | ty::Int(_) => {
                 let int =
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 544f27b84e9..68244136d1a 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -1074,12 +1074,9 @@ struct Candidate<'pat, 'tcx> {
     // because that would break binding consistency.
     subcandidates: Vec<Candidate<'pat, 'tcx>>,
 
-    /// ...and the guard must be evaluated if there is one.
+    /// ...and if there is a guard it must be evaluated; if it's `false` then branch to `otherwise_block`.
     has_guard: bool,
 
-    /// If the guard is `false` then branch to `otherwise_block`.
-    otherwise_block: Option<BasicBlock>,
-
     /// If the candidate matches, bindings and ascriptions must be established.
     extra_data: PatternExtraData<'tcx>,
 
@@ -1090,6 +1087,9 @@ struct Candidate<'pat, 'tcx> {
     /// The block before the `bindings` have been established.
     pre_binding_block: Option<BasicBlock>,
 
+    /// The block to branch to if the guard or a nested candidate fails to match.
+    otherwise_block: Option<BasicBlock>,
+
     /// The earliest block that has only candidates >= this one as descendents. Used for false
     /// edges, see the doc for [`Builder::match_expr`].
     false_edge_start_block: Option<BasicBlock>,
@@ -1364,56 +1364,105 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
         otherwise_block: BasicBlock,
         candidates: &mut [&mut Candidate<'pat, 'tcx>],
     ) {
-        let mut split_or_candidate = false;
-        for candidate in &mut *candidates {
-            if let [MatchPair { test_case: TestCase::Or { .. }, .. }] = &*candidate.match_pairs {
-                // Split a candidate in which the only match-pair is an or-pattern into multiple
-                // candidates. This is so that
-                //
-                // match x {
-                //     0 | 1 => { ... },
-                //     2 | 3 => { ... },
-                // }
-                //
-                // only generates a single switch.
-                let match_pair = candidate.match_pairs.pop().unwrap();
-                self.create_or_subcandidates(candidate, match_pair);
-                split_or_candidate = true;
+        // We process or-patterns here. If any candidate starts with an or-pattern, we have to
+        // expand the or-pattern before we can proceed further.
+        //
+        // We can't expand them freely however. The rule is: if the candidate has an or-pattern as
+        // its only remaining match pair, we can expand it freely. If it has other match pairs, we
+        // can expand it but we can't process more candidates after it.
+        //
+        // If we didn't stop, the `otherwise` cases could get mixed up. E.g. in the following,
+        // or-pattern simplification (in `merge_trivial_subcandidates`) makes it so the `1` and `2`
+        // cases branch to a same block (which then tests `false`). If we took `(2, _)` in the same
+        // set of candidates, when we reach the block that tests `false` we don't know whether we
+        // came from `1` or `2`, hence we can't know where to branch on failure.
+        // ```ignore(illustrative)
+        // match (1, true) {
+        //     (1 | 2, false) => {},
+        //     (2, _) => {},
+        //     _ => {}
+        // }
+        // ```
+        //
+        // We therefore split the `candidates` slice in two, expand or-patterns in the first half,
+        // and process both halves separately.
+        let mut expand_until = 0;
+        for (i, candidate) in candidates.iter().enumerate() {
+            if matches!(
+                &*candidate.match_pairs,
+                [MatchPair { test_case: TestCase::Or { .. }, .. }, ..]
+            ) {
+                expand_until = i + 1;
+                if candidate.match_pairs.len() > 1 {
+                    break;
+                }
             }
         }
+        let (candidates_to_expand, remaining_candidates) = candidates.split_at_mut(expand_until);
 
         ensure_sufficient_stack(|| {
-            if split_or_candidate {
-                // At least one of the candidates has been split into subcandidates.
-                // We need to change the candidate list to include those.
-                let mut new_candidates = Vec::new();
-                for candidate in candidates.iter_mut() {
-                    candidate.visit_leaves(|leaf_candidate| new_candidates.push(leaf_candidate));
+            if candidates_to_expand.is_empty() {
+                // No candidates start with an or-pattern, we can continue.
+                self.match_expanded_candidates(
+                    span,
+                    scrutinee_span,
+                    start_block,
+                    otherwise_block,
+                    remaining_candidates,
+                );
+            } else {
+                // Expand one level of or-patterns for each candidate in `candidates_to_expand`.
+                let mut expanded_candidates = Vec::new();
+                for candidate in candidates_to_expand.iter_mut() {
+                    if let [MatchPair { test_case: TestCase::Or { .. }, .. }, ..] =
+                        &*candidate.match_pairs
+                    {
+                        let or_match_pair = candidate.match_pairs.remove(0);
+                        // Expand the or-pattern into subcandidates.
+                        self.create_or_subcandidates(candidate, or_match_pair);
+                        // Collect the newly created subcandidates.
+                        for subcandidate in candidate.subcandidates.iter_mut() {
+                            expanded_candidates.push(subcandidate);
+                        }
+                    } else {
+                        expanded_candidates.push(candidate);
+                    }
                 }
+
+                // Process the expanded candidates.
+                let remainder_start = self.cfg.start_new_block();
+                // There might be new or-patterns obtained from expanding the old ones, so we call
+                // `match_candidates` again.
                 self.match_candidates(
                     span,
                     scrutinee_span,
                     start_block,
-                    otherwise_block,
-                    &mut *new_candidates,
+                    remainder_start,
+                    expanded_candidates.as_mut_slice(),
                 );
 
-                for candidate in candidates {
-                    self.merge_trivial_subcandidates(candidate);
+                // Simplify subcandidates and process any leftover match pairs.
+                for candidate in candidates_to_expand {
+                    if !candidate.subcandidates.is_empty() {
+                        self.finalize_or_candidate(span, scrutinee_span, candidate);
+                    }
                 }
-            } else {
-                self.match_simplified_candidates(
+
+                // Process the remaining candidates.
+                self.match_candidates(
                     span,
                     scrutinee_span,
-                    start_block,
+                    remainder_start,
                     otherwise_block,
-                    candidates,
+                    remaining_candidates,
                 );
             }
         });
     }
 
-    fn match_simplified_candidates(
+    /// Construct the decision tree for `candidates`. Caller must ensure that no candidate in
+    /// `candidates` starts with an or-pattern.
+    fn match_expanded_candidates(
         &mut self,
         span: Span,
         scrutinee_span: Span,
@@ -1438,7 +1487,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
                 // The first candidate has satisfied all its match pairs; we link it up and continue
                 // with the remaining candidates.
                 start_block = self.select_matched_candidate(first, start_block);
-                self.match_simplified_candidates(
+                self.match_expanded_candidates(
                     span,
                     scrutinee_span,
                     start_block,
@@ -1448,7 +1497,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             }
             candidates => {
                 // The first candidate has some unsatisfied match pairs; we proceed to do more tests.
-                self.test_candidates_with_or(
+                self.test_candidates(
                     span,
                     scrutinee_span,
                     candidates,
@@ -1495,16 +1544,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
 
         candidate.pre_binding_block = Some(start_block);
         let otherwise_block = self.cfg.start_new_block();
-        if candidate.has_guard {
-            // Create the otherwise block for this candidate, which is the
-            // pre-binding block for the next candidate.
-            candidate.otherwise_block = Some(otherwise_block);
-        }
+        // Create the otherwise block for this candidate, which is the
+        // pre-binding block for the next candidate.
+        candidate.otherwise_block = Some(otherwise_block);
         otherwise_block
     }
 
-    /// Tests a candidate where there are only or-patterns left to test, or
-    /// forwards to [Builder::test_candidates].
+    /// Simplify subcandidates and process any leftover match pairs. The candidate should have been
+    /// expanded with `create_or_subcandidates`.
     ///
     /// Given a pattern `(P | Q, R | S)` we (in principle) generate a CFG like
     /// so:
@@ -1556,84 +1603,56 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     ///      |
     ///     ...
     /// ```
-    fn test_candidates_with_or(
+    fn finalize_or_candidate(
         &mut self,
         span: Span,
         scrutinee_span: Span,
-        candidates: &mut [&mut Candidate<'_, 'tcx>],
-        start_block: BasicBlock,
-        otherwise_block: BasicBlock,
+        candidate: &mut Candidate<'_, 'tcx>,
     ) {
-        let (first_candidate, remaining_candidates) = candidates.split_first_mut().unwrap();
-        assert!(first_candidate.subcandidates.is_empty());
-        if !matches!(first_candidate.match_pairs[0].test_case, TestCase::Or { .. }) {
-            self.test_candidates(span, scrutinee_span, candidates, start_block, otherwise_block);
+        if candidate.subcandidates.is_empty() {
             return;
         }
 
-        let first_match_pair = first_candidate.match_pairs.remove(0);
-        let remaining_match_pairs = mem::take(&mut first_candidate.match_pairs);
-        let remainder_start = self.cfg.start_new_block();
-        // Test the alternatives of this or-pattern.
-        self.test_or_pattern(first_candidate, start_block, remainder_start, first_match_pair);
+        self.merge_trivial_subcandidates(candidate);
 
-        if !remaining_match_pairs.is_empty() {
+        if !candidate.match_pairs.is_empty() {
             // If more match pairs remain, test them after each subcandidate.
             // We could add them to the or-candidates before the call to `test_or_pattern` but this
             // would make it impossible to detect simplifiable or-patterns. That would guarantee
             // exponentially large CFGs for cases like `(1 | 2, 3 | 4, ...)`.
-            first_candidate.visit_leaves(|leaf_candidate| {
+            let mut last_otherwise = None;
+            candidate.visit_leaves(|leaf_candidate| {
+                last_otherwise = leaf_candidate.otherwise_block;
+            });
+            let remaining_match_pairs = mem::take(&mut candidate.match_pairs);
+            candidate.visit_leaves(|leaf_candidate| {
                 assert!(leaf_candidate.match_pairs.is_empty());
                 leaf_candidate.match_pairs.extend(remaining_match_pairs.iter().cloned());
                 let or_start = leaf_candidate.pre_binding_block.unwrap();
-                // In a case like `(a | b, c | d)`, if `a` succeeds and `c | d` fails, we know `(b,
-                // c | d)` will fail too. If there is no guard, we skip testing of `b` by branching
-                // directly to `remainder_start`. If there is a guard, we have to try `(b, c | d)`.
-                let or_otherwise = leaf_candidate.otherwise_block.unwrap_or(remainder_start);
-                self.test_candidates_with_or(
+                // In a case like `(P | Q, R | S)`, if `P` succeeds and `R | S` fails, we know `(Q,
+                // R | S)` will fail too. If there is no guard, we skip testing of `Q` by branching
+                // directly to `last_otherwise`. If there is a guard,
+                // `leaf_candidate.otherwise_block` can be reached by guard failure as well, so we
+                // can't skip `Q`.
+                let or_otherwise = if leaf_candidate.has_guard {
+                    leaf_candidate.otherwise_block.unwrap()
+                } else {
+                    last_otherwise.unwrap()
+                };
+                self.match_candidates(
                     span,
                     scrutinee_span,
-                    &mut [leaf_candidate],
                     or_start,
                     or_otherwise,
+                    &mut [leaf_candidate],
                 );
             });
         }
-
-        // Test the remaining candidates.
-        self.match_candidates(
-            span,
-            scrutinee_span,
-            remainder_start,
-            otherwise_block,
-            remaining_candidates,
-        );
-    }
-
-    #[instrument(skip(self, start_block, otherwise_block, candidate, match_pair), level = "debug")]
-    fn test_or_pattern<'pat>(
-        &mut self,
-        candidate: &mut Candidate<'pat, 'tcx>,
-        start_block: BasicBlock,
-        otherwise_block: BasicBlock,
-        match_pair: MatchPair<'pat, 'tcx>,
-    ) {
-        let or_span = match_pair.pattern.span;
-        self.create_or_subcandidates(candidate, match_pair);
-        let mut or_candidate_refs: Vec<_> = candidate.subcandidates.iter_mut().collect();
-        self.match_candidates(
-            or_span,
-            or_span,
-            start_block,
-            otherwise_block,
-            &mut or_candidate_refs,
-        );
-        self.merge_trivial_subcandidates(candidate);
     }
 
     /// Given a match-pair that corresponds to an or-pattern, expand each subpattern into a new
     /// subcandidate. Any candidate that has been expanded that way should be passed to
-    /// `merge_trivial_subcandidates` after its subcandidates have been processed.
+    /// `finalize_or_candidate` after its subcandidates have been processed.
     fn create_or_subcandidates<'pat>(
         &mut self,
         candidate: &mut Candidate<'pat, 'tcx>,
@@ -1651,8 +1670,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
     }
 
     /// Try to merge all of the subcandidates of the given candidate into one. This avoids
-    /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The or-pattern should have
-    /// been expanded with `create_or_subcandidates`.
+    /// exponentially large CFGs in cases like `(1 | 2, 3 | 4, ...)`. The candidate should have been
+    /// expanded with `create_or_subcandidates`.
     fn merge_trivial_subcandidates(&mut self, candidate: &mut Candidate<'_, 'tcx>) {
         if candidate.subcandidates.is_empty() || candidate.has_guard {
             // FIXME(or_patterns; matthewjasper) Don't give up if we have a guard.
@@ -1664,6 +1683,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             subcandidate.subcandidates.is_empty() && subcandidate.extra_data.is_empty()
         });
         if can_merge {
+            let mut last_otherwise = None;
             let any_matches = self.cfg.start_new_block();
             let or_span = candidate.or_span.take().unwrap();
             let source_info = self.source_info(or_span);
@@ -1674,8 +1694,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
             for subcandidate in mem::take(&mut candidate.subcandidates) {
                 let or_block = subcandidate.pre_binding_block.unwrap();
                 self.cfg.goto(or_block, source_info, any_matches);
+                last_otherwise = subcandidate.otherwise_block;
             }
             candidate.pre_binding_block = Some(any_matches);
+            assert!(last_otherwise.is_some());
+            candidate.otherwise_block = last_otherwise;
         } else {
             // Never subcandidates may have a set of bindings inconsistent with their siblings,
             // which would break later code. So we filter them out. Note that we can't filter out
diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl
index 07c82065a80..84dbd192723 100644
--- a/compiler/rustc_passes/messages.ftl
+++ b/compiler/rustc_passes/messages.ftl
@@ -595,6 +595,15 @@ passes_pass_by_value =
 
 passes_proc_macro_bad_sig = {$kind} has incorrect signature
 
+passes_remove_fields =
+    consider removing { $num ->
+      [one] this
+     *[other] these
+    } { $num ->
+      [one] field
+     *[other] fields
+    }
+
 passes_repr_conflicting =
     conflicting representation hints
 
diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs
index 6a74ddc5508..69386c0fbdb 100644
--- a/compiler/rustc_passes/src/dead.rs
+++ b/compiler/rustc_passes/src/dead.rs
@@ -24,8 +24,7 @@ use rustc_target::abi::FieldIdx;
 use std::mem;
 
 use crate::errors::{
-    ChangeFieldsToBeOfUnitType, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo,
-    UselessAssignment,
+    ChangeFields, IgnoredDerivedImpls, MultipleDeadCodes, ParentInfo, UselessAssignment,
 };
 
 // Any local node that may call something in its body block should be
@@ -1071,17 +1070,50 @@ impl<'tcx> DeadVisitor<'tcx> {
             };
 
         let diag = match report_on {
-            ReportOn::TupleField => MultipleDeadCodes::UnusedTupleStructFields {
-                multiple,
-                num,
-                descr,
-                participle,
-                name_list,
-                change_fields_suggestion: ChangeFieldsToBeOfUnitType { num, spans: spans.clone() },
-                parent_info,
-                ignored_derived_impls,
-            },
-
+            ReportOn::TupleField => {
+                let tuple_fields = if let Some(parent_id) = parent_item
+                    && let node = tcx.hir_node_by_def_id(parent_id)
+                    && let hir::Node::Item(hir::Item {
+                        kind: hir::ItemKind::Struct(hir::VariantData::Tuple(fields, _, _), _),
+                        ..
+                    }) = node
+                {
+                    *fields
+                } else {
+                    &[]
+                };
+
+                let trailing_tuple_fields = if tuple_fields.len() >= dead_codes.len() {
+                    LocalDefIdSet::from_iter(
+                        tuple_fields
+                            .iter()
+                            .skip(tuple_fields.len() - dead_codes.len())
+                            .map(|f| f.def_id),
+                    )
+                } else {
+                    LocalDefIdSet::default()
+                };
+
+                let fields_suggestion =
+                    // Suggest removal if all tuple fields are at the end.
+                    // Otherwise suggest removal or changing to unit type
+                    if dead_codes.iter().all(|dc| trailing_tuple_fields.contains(&dc.def_id)) {
+                        ChangeFields::Remove { num }
+                    } else {
+                        ChangeFields::ChangeToUnitTypeOrRemove { num, spans: spans.clone() }
+                    };
+
+                MultipleDeadCodes::UnusedTupleStructFields {
+                    multiple,
+                    num,
+                    descr,
+                    participle,
+                    name_list,
+                    change_fields_suggestion: fields_suggestion,
+                    parent_info,
+                    ignored_derived_impls,
+                }
+            }
             ReportOn::NamedField => MultipleDeadCodes::DeadCodes {
                 multiple,
                 num,
diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs
index 25df80d5a92..d27b94ebd22 100644
--- a/compiler/rustc_passes/src/errors.rs
+++ b/compiler/rustc_passes/src/errors.rs
@@ -1574,7 +1574,7 @@ pub enum MultipleDeadCodes<'tcx> {
         participle: &'tcx str,
         name_list: DiagSymbolList,
         #[subdiagnostic]
-        change_fields_suggestion: ChangeFieldsToBeOfUnitType,
+        change_fields_suggestion: ChangeFields,
         #[subdiagnostic]
         parent_info: Option<ParentInfo<'tcx>>,
         #[subdiagnostic]
@@ -1601,11 +1601,18 @@ pub struct IgnoredDerivedImpls {
 }
 
 #[derive(Subdiagnostic)]
-#[multipart_suggestion(passes_change_fields_to_be_of_unit_type, applicability = "has-placeholders")]
-pub struct ChangeFieldsToBeOfUnitType {
-    pub num: usize,
-    #[suggestion_part(code = "()")]
-    pub spans: Vec<Span>,
+pub enum ChangeFields {
+    #[multipart_suggestion(
+        passes_change_fields_to_be_of_unit_type,
+        applicability = "has-placeholders"
+    )]
+    ChangeToUnitTypeOrRemove {
+        num: usize,
+        #[suggestion_part(code = "()")]
+        spans: Vec<Span>,
+    },
+    #[help(passes_remove_fields)]
+    Remove { num: usize },
 }
 
 #[derive(Diagnostic)]
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 832efb11999..742ec4c377c 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -288,6 +288,7 @@ pub fn transform_instance<'tcx>(
     mut instance: Instance<'tcx>,
     options: TransformTyOptions,
 ) -> Instance<'tcx> {
+    // FIXME: account for async-drop-glue
     if (matches!(instance.def, ty::InstanceKind::Virtual(..))
         && tcx.is_lang_item(instance.def_id(), LangItem::DropInPlace))
         || matches!(instance.def, ty::InstanceKind::DropGlue(..))
diff --git a/compiler/rustc_type_ir/src/binder.rs b/compiler/rustc_type_ir/src/binder.rs
index e50d59ba5f0..5042a38fdb8 100644
--- a/compiler/rustc_type_ir/src/binder.rs
+++ b/compiler/rustc_type_ir/src/binder.rs
@@ -361,6 +361,7 @@ impl<I: Interner> TypeVisitor<I> for ValidateBoundVars<I> {
 #[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
 pub struct EarlyBinder<I: Interner, T> {
     value: T,
+    #[derivative(Debug = "ignore")]
     _tcx: PhantomData<I>,
 }
 
diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs
index bf39f920276..7a3376c7218 100644
--- a/compiler/rustc_type_ir/src/predicate.rs
+++ b/compiler/rustc_type_ir/src/predicate.rs
@@ -485,8 +485,8 @@ pub struct AliasTerm<I: Interner> {
     /// aka. `interner.parent(def_id)`.
     pub def_id: I::DefId,
 
-    /// This field exists to prevent the creation of `AliasTerm` without using
-    /// [AliasTerm::new].
+    /// This field exists to prevent the creation of `AliasTerm` without using [`AliasTerm::new`].
+    #[derivative(Debug = "ignore")]
     _use_alias_term_new_instead: (),
 }
 
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 8b4ad2f5ed0..915888ab59b 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -452,8 +452,8 @@ pub struct AliasTy<I: Interner> {
     /// aka. `interner.parent(def_id)`.
     pub def_id: I::DefId,
 
-    /// This field exists to prevent the creation of `AliasTy` without using
-    /// [AliasTy::new].
+    /// This field exists to prevent the creation of `AliasTy` without using [`AliasTy::new`].
+    #[derivative(Debug = "ignore")]
     pub(crate) _use_alias_ty_new_instead: (),
 }
 
diff --git a/src/tools/run-make-support/src/llvm.rs b/src/tools/run-make-support/src/llvm.rs
index fe4131819ba..99bce08fc23 100644
--- a/src/tools/run-make-support/src/llvm.rs
+++ b/src/tools/run-make-support/src/llvm.rs
@@ -2,8 +2,8 @@ use std::path::{Path, PathBuf};
 
 use crate::{env_var, Command};
 
-/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
-/// at `$LLVM_BIN_DIR/llvm-readobj`.
+/// Construct a new `llvm-readobj` invocation with the `GNU` output style.
+/// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`.
 #[track_caller]
 pub fn llvm_readobj() -> LlvmReadobj {
     LlvmReadobj::new()
@@ -70,13 +70,24 @@ pub fn llvm_bin_dir() -> PathBuf {
 }
 
 impl LlvmReadobj {
-    /// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
-    /// at `$LLVM_BIN_DIR/llvm-readobj`.
+    /// Construct a new `llvm-readobj` invocation with the `GNU` output style.
+    /// This assumes that `llvm-readobj` is available at `$LLVM_BIN_DIR/llvm-readobj`.
     #[track_caller]
     pub fn new() -> Self {
         let llvm_readobj = llvm_bin_dir().join("llvm-readobj");
         let cmd = Command::new(llvm_readobj);
-        Self { cmd }
+        let mut readobj = Self { cmd };
+        readobj.elf_output_style("GNU");
+        readobj
+    }
+
+    /// Specify the format of the ELF information.
+    ///
+    /// Valid options are `LLVM` (default), `GNU`, and `JSON`.
+    pub fn elf_output_style(&mut self, style: &str) -> &mut Self {
+        self.cmd.arg("--elf-output-style");
+        self.cmd.arg(style);
+        self
     }
 
     /// Provide an input file.
@@ -90,6 +101,13 @@ impl LlvmReadobj {
         self.cmd.arg("--file-header");
         self
     }
+
+    /// Specify the section to display.
+    pub fn section(&mut self, section: &str) -> &mut Self {
+        self.cmd.arg("--string-dump");
+        self.cmd.arg(section);
+        self
+    }
 }
 
 impl LlvmProfdata {
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 12d5f3576ca..eb2e8c2542e 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -83,7 +83,6 @@ run-make/issue-37839/Makefile
 run-make/issue-40535/Makefile
 run-make/issue-47384/Makefile
 run-make/issue-47551/Makefile
-run-make/issue-51671/Makefile
 run-make/issue-68794-textrel-on-minimal-lib/Makefile
 run-make/issue-69368/Makefile
 run-make/issue-83045/Makefile
diff --git a/tests/mir-opt/or_pattern.rs b/tests/mir-opt/or_pattern.rs
new file mode 100644
index 00000000000..0ad0ce8ead1
--- /dev/null
+++ b/tests/mir-opt/or_pattern.rs
@@ -0,0 +1,24 @@
+// skip-filecheck
+
+// EMIT_MIR or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir
+fn shortcut_second_or() {
+    // Check that after matching `0`, failing to match `2 | 3` skips trying to match `(1, 2 | 3)`.
+    match ((0, 0), 0) {
+        (x @ (0, _) | x @ (_, 1), y @ 2 | y @ 3) => {}
+        _ => {}
+    }
+}
+
+// EMIT_MIR or_pattern.single_switchint.SimplifyCfg-initial.after.mir
+fn single_switchint() {
+    // Check how many `SwitchInt`s we do. In theory a single one is necessary.
+    match (1, true) {
+        (1, true) => 1,
+        (2, false) => 2,
+        (1 | 2, true | false) => 3,
+        (3 | 4, true | false) => 4,
+        _ => 5,
+    };
+}
+
+fn main() {}
diff --git a/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir
new file mode 100644
index 00000000000..56edd38a6da
--- /dev/null
+++ b/tests/mir-opt/or_pattern.shortcut_second_or.SimplifyCfg-initial.after.mir
@@ -0,0 +1,100 @@
+// MIR for `shortcut_second_or` after SimplifyCfg-initial
+
+fn shortcut_second_or() -> () {
+    let mut _0: ();
+    let mut _1: ((i32, i32), i32);
+    let mut _2: (i32, i32);
+    let _3: (i32, i32);
+    let _4: i32;
+    scope 1 {
+        debug x => _3;
+        debug y => _4;
+    }
+
+    bb0: {
+        StorageLive(_1);
+        StorageLive(_2);
+        _2 = (const 0_i32, const 0_i32);
+        _1 = (move _2, const 0_i32);
+        StorageDead(_2);
+        PlaceMention(_1);
+        switchInt(((_1.0: (i32, i32)).0: i32)) -> [0: bb4, otherwise: bb2];
+    }
+
+    bb1: {
+        _0 = const ();
+        goto -> bb14;
+    }
+
+    bb2: {
+        switchInt(((_1.0: (i32, i32)).1: i32)) -> [1: bb3, otherwise: bb1];
+    }
+
+    bb3: {
+        switchInt((_1.1: i32)) -> [2: bb7, 3: bb8, otherwise: bb1];
+    }
+
+    bb4: {
+        switchInt((_1.1: i32)) -> [2: bb5, 3: bb6, otherwise: bb1];
+    }
+
+    bb5: {
+        falseEdge -> [real: bb10, imaginary: bb6];
+    }
+
+    bb6: {
+        falseEdge -> [real: bb11, imaginary: bb2];
+    }
+
+    bb7: {
+        falseEdge -> [real: bb12, imaginary: bb8];
+    }
+
+    bb8: {
+        falseEdge -> [real: bb13, imaginary: bb1];
+    }
+
+    bb9: {
+        _0 = const ();
+        StorageDead(_4);
+        StorageDead(_3);
+        goto -> bb14;
+    }
+
+    bb10: {
+        StorageLive(_3);
+        _3 = (_1.0: (i32, i32));
+        StorageLive(_4);
+        _4 = (_1.1: i32);
+        goto -> bb9;
+    }
+
+    bb11: {
+        StorageLive(_3);
+        _3 = (_1.0: (i32, i32));
+        StorageLive(_4);
+        _4 = (_1.1: i32);
+        goto -> bb9;
+    }
+
+    bb12: {
+        StorageLive(_3);
+        _3 = (_1.0: (i32, i32));
+        StorageLive(_4);
+        _4 = (_1.1: i32);
+        goto -> bb9;
+    }
+
+    bb13: {
+        StorageLive(_3);
+        _3 = (_1.0: (i32, i32));
+        StorageLive(_4);
+        _4 = (_1.1: i32);
+        goto -> bb9;
+    }
+
+    bb14: {
+        StorageDead(_1);
+        return;
+    }
+}
diff --git a/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir
new file mode 100644
index 00000000000..eafe95b4a11
--- /dev/null
+++ b/tests/mir-opt/or_pattern.single_switchint.SimplifyCfg-initial.after.mir
@@ -0,0 +1,75 @@
+// MIR for `single_switchint` after SimplifyCfg-initial
+
+fn single_switchint() -> () {
+    let mut _0: ();
+    let _1: i32;
+    let mut _2: (i32, bool);
+
+    bb0: {
+        StorageLive(_1);
+        StorageLive(_2);
+        _2 = (const 1_i32, const true);
+        PlaceMention(_2);
+        switchInt((_2.0: i32)) -> [1: bb2, 2: bb4, otherwise: bb1];
+    }
+
+    bb1: {
+        switchInt((_2.0: i32)) -> [3: bb8, 4: bb8, otherwise: bb7];
+    }
+
+    bb2: {
+        switchInt((_2.1: bool)) -> [0: bb6, otherwise: bb3];
+    }
+
+    bb3: {
+        falseEdge -> [real: bb9, imaginary: bb4];
+    }
+
+    bb4: {
+        switchInt((_2.1: bool)) -> [0: bb5, otherwise: bb6];
+    }
+
+    bb5: {
+        falseEdge -> [real: bb10, imaginary: bb6];
+    }
+
+    bb6: {
+        falseEdge -> [real: bb11, imaginary: bb1];
+    }
+
+    bb7: {
+        _1 = const 5_i32;
+        goto -> bb13;
+    }
+
+    bb8: {
+        falseEdge -> [real: bb12, imaginary: bb7];
+    }
+
+    bb9: {
+        _1 = const 1_i32;
+        goto -> bb13;
+    }
+
+    bb10: {
+        _1 = const 2_i32;
+        goto -> bb13;
+    }
+
+    bb11: {
+        _1 = const 3_i32;
+        goto -> bb13;
+    }
+
+    bb12: {
+        _1 = const 4_i32;
+        goto -> bb13;
+    }
+
+    bb13: {
+        StorageDead(_2);
+        StorageDead(_1);
+        _0 = const ();
+        return;
+    }
+}
diff --git a/tests/run-make/issue-51671/app.rs b/tests/run-make/bin-emit-no-symbols/app.rs
index e9dc1e9744f..e9dc1e9744f 100644
--- a/tests/run-make/issue-51671/app.rs
+++ b/tests/run-make/bin-emit-no-symbols/app.rs
diff --git a/tests/run-make/bin-emit-no-symbols/rmake.rs b/tests/run-make/bin-emit-no-symbols/rmake.rs
new file mode 100644
index 00000000000..5586e53c050
--- /dev/null
+++ b/tests/run-make/bin-emit-no-symbols/rmake.rs
@@ -0,0 +1,16 @@
+// When setting the crate type as a "bin" (in app.rs),
+// this could cause a bug where some symbols would not be
+// emitted in the object files. This has been fixed, and
+// this test checks that the correct symbols have been successfully
+// emitted inside the object files.
+// See https://github.com/rust-lang/rust/issues/51671
+
+use run_make_support::{llvm_readobj, rustc};
+
+fn main() {
+    rustc().emit("obj").input("app.rs").run();
+    let out = llvm_readobj().input("app.o").arg("--symbols").run();
+    out.assert_stdout_contains("rust_begin_unwind");
+    out.assert_stdout_contains("rust_eh_personality");
+    out.assert_stdout_contains("__rg_oom");
+}
diff --git a/tests/run-make/issue-51671/Makefile b/tests/run-make/issue-51671/Makefile
deleted file mode 100644
index c9364536992..00000000000
--- a/tests/run-make/issue-51671/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-include ../tools.mk
-
-# ignore-windows-msvc
-
-all:
-	$(RUSTC) --emit=obj app.rs
-	nm $(TMPDIR)/app.o | $(CGREP) rust_begin_unwind
-	nm $(TMPDIR)/app.o | $(CGREP) rust_eh_personality
-	nm $(TMPDIR)/app.o | $(CGREP) __rg_oom
diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr
index bd65cee58d9..4f15be4c7c8 100644
--- a/tests/ui/coherence/occurs-check/associated-type.next.stderr
+++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr
@@ -1,7 +1,7 @@
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
 error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())`
   --> $DIR/associated-type.rs:31:1
    |
diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr
index d26f7665ee7..329086ab7df 100644
--- a/tests/ui/coherence/occurs-check/associated-type.old.stderr
+++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr
@@ -1,11 +1,11 @@
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit), _use_alias_ty_new_instead: () }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, '^0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [*const ?1t, !2_0.Named(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), "'a")], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) }
 error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)`
   --> $DIR/associated-type.rs:31:1
    |
diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr
index e1cffa0fc37..2f1dfd19c48 100644
--- a/tests/ui/higher-ranked/structually-relate-aliases.stderr
+++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr
@@ -1,5 +1,5 @@
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit), _use_alias_ty_new_instead: () }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) }
 error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied
   --> $DIR/structually-relate-aliases.rs:13:36
    |
diff --git a/tests/ui/lint/dead-code/tuple-struct-field.rs b/tests/ui/lint/dead-code/tuple-struct-field.rs
index d13fe029289..ff3da410500 100644
--- a/tests/ui/lint/dead-code/tuple-struct-field.rs
+++ b/tests/ui/lint/dead-code/tuple-struct-field.rs
@@ -5,15 +5,20 @@ use std::marker::PhantomData;
 
 const LEN: usize = 4;
 
-struct SingleUnused(i32, [u8; LEN], String);
-//~^ ERROR: field `1` is never read
+struct UnusedAtTheEnd(i32, f32, [u8; LEN], String, u8);
+//~^ ERROR:fields `1`, `2`, `3`, and `4` are never read
+//~| NOTE: fields in this struct
+//~| HELP: consider removing these fields
+
+struct UnusedJustOneField(i32);
+//~^ ERROR: field `0` is never read
 //~| NOTE: field in this struct
-//~| HELP: consider changing the field to be of unit type
+//~| HELP: consider removing this field
 
-struct MultipleUnused(i32, f32, String, u8);
-//~^ ERROR: fields `0`, `1`, `2`, and `3` are never read
+struct UnusedInTheMiddle(i32, f32, String, u8, u32);
+//~^ ERROR: fields `1`, `2`, and `4` are never read
 //~| NOTE: fields in this struct
-//~| HELP: consider changing the fields to be of unit type
+//~| HELP: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields
 
 struct GoodUnit(());
 
@@ -23,15 +28,19 @@ struct Void;
 struct GoodVoid(Void);
 
 fn main() {
-    let w = SingleUnused(42, [0, 1, 2, 3], "abc".to_string());
-    let _ = w.0;
-    let _ = w.2;
+    let u1 = UnusedAtTheEnd(42, 3.14, [0, 1, 2, 3], "def".to_string(), 4u8);
+    let _ = u1.0;
+
+    let _ = UnusedJustOneField(42);
+
+    let u2 = UnusedInTheMiddle(42, 3.14, "def".to_string(), 4u8, 5);
+    let _ = u2.0;
+    let _ = u2.3;
 
-    let m = MultipleUnused(42, 3.14, "def".to_string(), 4u8);
 
     let gu = GoodUnit(());
     let gp = GoodPhantom(PhantomData);
     let gv = GoodVoid(Void);
 
-    let _ = (gu, gp, gv, m);
+    let _ = (gu, gp, gv);
 }
diff --git a/tests/ui/lint/dead-code/tuple-struct-field.stderr b/tests/ui/lint/dead-code/tuple-struct-field.stderr
index 0154d5489f9..434554d7ae5 100644
--- a/tests/ui/lint/dead-code/tuple-struct-field.stderr
+++ b/tests/ui/lint/dead-code/tuple-struct-field.stderr
@@ -1,33 +1,40 @@
-error: field `1` is never read
-  --> $DIR/tuple-struct-field.rs:8:26
+error: fields `1`, `2`, `3`, and `4` are never read
+  --> $DIR/tuple-struct-field.rs:8:28
    |
-LL | struct SingleUnused(i32, [u8; LEN], String);
-   |        ------------      ^^^^^^^^^
+LL | struct UnusedAtTheEnd(i32, f32, [u8; LEN], String, u8);
+   |        --------------      ^^^  ^^^^^^^^^  ^^^^^^  ^^
    |        |
-   |        field in this struct
+   |        fields in this struct
    |
+   = help: consider removing these fields
 note: the lint level is defined here
   --> $DIR/tuple-struct-field.rs:1:9
    |
 LL | #![deny(dead_code)]
    |         ^^^^^^^^^
-help: consider changing the field to be of unit type to suppress this warning while preserving the field numbering, or remove the field
+
+error: field `0` is never read
+  --> $DIR/tuple-struct-field.rs:13:27
+   |
+LL | struct UnusedJustOneField(i32);
+   |        ------------------ ^^^
+   |        |
+   |        field in this struct
    |
-LL | struct SingleUnused(i32, (), String);
-   |                          ~~
+   = help: consider removing this field
 
-error: fields `0`, `1`, `2`, and `3` are never read
-  --> $DIR/tuple-struct-field.rs:13:23
+error: fields `1`, `2`, and `4` are never read
+  --> $DIR/tuple-struct-field.rs:18:31
    |
-LL | struct MultipleUnused(i32, f32, String, u8);
-   |        -------------- ^^^  ^^^  ^^^^^^  ^^
+LL | struct UnusedInTheMiddle(i32, f32, String, u8, u32);
+   |        -----------------      ^^^  ^^^^^^      ^^^
    |        |
    |        fields in this struct
    |
 help: consider changing the fields to be of unit type to suppress this warning while preserving the field numbering, or remove the fields
    |
-LL | struct MultipleUnused((), (), (), ());
-   |                       ~~  ~~  ~~  ~~
+LL | struct UnusedInTheMiddle(i32, (), (), u8, ());
+   |                               ~~  ~~      ~~
 
-error: aborting due to 2 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/tests/ui/or-patterns/bindings-runpass-2.rs b/tests/ui/or-patterns/bindings-runpass-2.rs
index 657d7f1ed18..a9ae9981084 100644
--- a/tests/ui/or-patterns/bindings-runpass-2.rs
+++ b/tests/ui/or-patterns/bindings-runpass-2.rs
@@ -26,5 +26,6 @@ fn main() {
     assert_eq!(or_at(Err(7)), 207);
     assert_eq!(or_at(Err(8)), 8);
     assert_eq!(or_at(Err(20)), 220);
+    assert_eq!(or_at(Err(34)), 134);
     assert_eq!(or_at(Err(50)), 500);
 }
diff --git a/tests/ui/or-patterns/inner-or-pat.or3.stderr b/tests/ui/or-patterns/inner-or-pat.or3.stderr
index 10ec7c202e4..5c522a97cce 100644
--- a/tests/ui/or-patterns/inner-or-pat.or3.stderr
+++ b/tests/ui/or-patterns/inner-or-pat.or3.stderr
@@ -1,5 +1,5 @@
 error[E0308]: mismatched types
-  --> $DIR/inner-or-pat.rs:38:54
+  --> $DIR/inner-or-pat.rs:36:54
    |
 LL |     match x {
    |           - this expression has type `&str`
diff --git a/tests/ui/or-patterns/inner-or-pat.or4.stderr b/tests/ui/or-patterns/inner-or-pat.or4.stderr
index 97800161d82..508520c8237 100644
--- a/tests/ui/or-patterns/inner-or-pat.or4.stderr
+++ b/tests/ui/or-patterns/inner-or-pat.or4.stderr
@@ -1,5 +1,5 @@
 error[E0408]: variable `x` is not bound in all patterns
-  --> $DIR/inner-or-pat.rs:53:37
+  --> $DIR/inner-or-pat.rs:51:37
    |
 LL |         (x @ "red" | (x @ "blue" |  "red")) => {
    |                       -             ^^^^^ pattern doesn't bind `x`
diff --git a/tests/ui/or-patterns/inner-or-pat.rs b/tests/ui/or-patterns/inner-or-pat.rs
index ceb0a8b3f79..4d136de0053 100644
--- a/tests/ui/or-patterns/inner-or-pat.rs
+++ b/tests/ui/or-patterns/inner-or-pat.rs
@@ -1,7 +1,5 @@
-//@ revisions: or1 or2 or3 or4 or5
+//@ revisions: or1 or3 or4
 //@ [or1] run-pass
-//@ [or2] run-pass
-//@ [or5] run-pass
 
 #![allow(unreachable_patterns)]
 #![allow(unused_variables)]
diff --git a/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs b/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs
index 7d62364a6ae..76dc298a5c8 100644
--- a/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs
+++ b/tests/ui/or-patterns/issue-70413-no-unreachable-pat-and-guard.rs
@@ -1,21 +1,20 @@
-//@ check-pass
+//@ run-pass
 
 #![deny(unreachable_patterns)]
 
 fn main() {
-    match (3,42) {
-        (a,_) | (_,a) if a > 10 => {println!("{}", a)}
-        _ => ()
+    match (3, 42) {
+        (a, _) | (_, a) if a > 10 => {}
+        _ => unreachable!(),
     }
 
-    match Some((3,42)) {
-        Some((a, _)) | Some((_, a)) if a > 10 => {println!("{}", a)}
-        _ => ()
-
+    match Some((3, 42)) {
+        Some((a, _)) | Some((_, a)) if a > 10 => {}
+        _ => unreachable!(),
     }
 
-    match Some((3,42)) {
-        Some((a, _) | (_, a)) if a > 10 => {println!("{}", a)}
-        _ => ()
+    match Some((3, 42)) {
+        Some((a, _) | (_, a)) if a > 10 => {}
+        _ => unreachable!(),
     }
 }
diff --git a/tests/ui/or-patterns/search-via-bindings.rs b/tests/ui/or-patterns/search-via-bindings.rs
index a760112f1d4..42174bd7cef 100644
--- a/tests/ui/or-patterns/search-via-bindings.rs
+++ b/tests/ui/or-patterns/search-via-bindings.rs
@@ -42,6 +42,23 @@ fn search_old_style(target: (bool, bool, bool)) -> u32 {
     }
 }
 
+// Check that a dummy or-pattern also leads to running the guard multiple times.
+fn search_with_dummy(target: (bool, bool)) -> u32 {
+    let x = ((false, true), (false, true), ());
+    let mut guard_count = 0;
+    match x {
+        ((a, _) | (_, a), (b, _) | (_, b), _ | _)
+            if {
+                guard_count += 1;
+                (a, b) == target
+            } =>
+        {
+            guard_count
+        }
+        _ => unreachable!(),
+    }
+}
+
 fn main() {
     assert_eq!(search((false, false, false)), 1);
     assert_eq!(search((false, false, true)), 2);
@@ -60,4 +77,9 @@ fn main() {
     assert_eq!(search_old_style((true, false, true)), 6);
     assert_eq!(search_old_style((true, true, false)), 7);
     assert_eq!(search_old_style((true, true, true)), 8);
+
+    assert_eq!(search_with_dummy((false, false)), 1);
+    assert_eq!(search_with_dummy((false, true)), 3);
+    assert_eq!(search_with_dummy((true, false)), 5);
+    assert_eq!(search_with_dummy((true, true)), 7);
 }
diff --git a/tests/ui/or-patterns/simplification_subtleties.rs b/tests/ui/or-patterns/simplification_subtleties.rs
new file mode 100644
index 00000000000..a932bd531e6
--- /dev/null
+++ b/tests/ui/or-patterns/simplification_subtleties.rs
@@ -0,0 +1,11 @@
+//@ run-pass
+
+#[allow(unreachable_patterns)]
+fn main() {
+    // Test that we don't naively sort the two `2`s together and confuse the failure paths.
+    match (1, true) {
+        (1 | 2, false | false) => unreachable!(),
+        (2, _) => unreachable!(),
+        _ => {}
+    }
+}
diff --git a/tests/ui/traits/next-solver/issue-118950-root-region.stderr b/tests/ui/traits/next-solver/issue-118950-root-region.stderr
index 04c44276195..17da1f52479 100644
--- a/tests/ui/traits/next-solver/issue-118950-root-region.stderr
+++ b/tests/ui/traits/next-solver/issue-118950-root-region.stderr
@@ -25,10 +25,10 @@ help: this trait has no implementations, consider adding one
 LL | trait ToUnit<'a> {
    | ^^^^^^^^^^^^^^^^
 
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () }
- WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc), _use_alias_ty_new_instead: () }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }
+ WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: ['^0.Named(DefId(0:15 ~ issue_118950_root_region[d54f]::{impl#1}::'a), "'a"), ?1t], def_id: DefId(0:8 ~ issue_118950_root_region[d54f]::Assoc) }
 error[E0119]: conflicting implementations of trait `Overlap<fn(_)>` for type `fn(_)`
   --> $DIR/issue-118950-root-region.rs:19:1
    |
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs
new file mode 100644
index 00000000000..8ce471e3956
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.rs
@@ -0,0 +1,14 @@
+#![feature(type_alias_impl_trait)]
+
+pub type Opaque<'a> = impl Sized;
+
+fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> {
+    a
+}
+
+fn get_iter<'a>() -> impl IntoIterator<Item = Opaque<'a>> {
+    //~^ ERROR:  item does not constrain
+    None::<Opaque<'static>>
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr
new file mode 100644
index 00000000000..f27f2234525
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal.stderr
@@ -0,0 +1,15 @@
+error: item does not constrain `Opaque::{opaque#0}`, but has it in its signature
+  --> $DIR/different_args_considered_equal.rs:9:4
+   |
+LL | fn get_iter<'a>() -> impl IntoIterator<Item = Opaque<'a>> {
+   |    ^^^^^^^^
+   |
+   = note: consider moving the opaque type's declaration and defining uses into a separate module
+note: this opaque type is in the signature
+  --> $DIR/different_args_considered_equal.rs:3:23
+   |
+LL | pub type Opaque<'a> = impl Sized;
+   |                       ^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs
new file mode 100644
index 00000000000..43dfea97e6d
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.rs
@@ -0,0 +1,14 @@
+#![feature(type_alias_impl_trait)]
+
+pub type Opaque<'a> = impl Sized;
+
+fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> {
+    if a.is_null() {
+        Some(a)
+    } else {
+        None::<Opaque<'static>>
+        //~^ ERROR hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds
+    }
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr
new file mode 100644
index 00000000000..1104c2c498a
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal2.stderr
@@ -0,0 +1,20 @@
+error[E0700]: hidden type for `Opaque<'static>` captures lifetime that does not appear in bounds
+  --> $DIR/different_args_considered_equal2.rs:9:9
+   |
+LL | pub type Opaque<'a> = impl Sized;
+   |                       ---------- opaque type defined here
+LL |
+LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> {
+   |            -- hidden type `*mut &'a str` captures the lifetime `'a` as defined here
+...
+LL |         None::<Opaque<'static>>
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: to declare that `impl IntoIterator<Item = Opaque<'a>>` captures `'a`, you can add an explicit `'a` lifetime bound
+   |
+LL | fn get_one<'a>(a: *mut &'a str) -> impl IntoIterator<Item = Opaque<'a>> + 'a {
+   |                                                                         ++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0700`.
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs
new file mode 100644
index 00000000000..ea69175ba31
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.rs
@@ -0,0 +1,22 @@
+//! Test that we don't allow coercing an opaque type with a non-static
+//! lifetime to one with a static lifetime. While `get_iter` looks like
+//! it would be doing the opposite, the way we're handling projections
+//! makes `Opaque<'a>` the hidden type of `Opaque<'static>`.
+
+#![feature(type_alias_impl_trait)]
+
+mod defining_scope {
+    pub type Opaque<'a> = impl Sized;
+
+    fn get_one<'a>(a: *mut &'a str) -> Opaque<'a> {
+        a
+    }
+}
+use defining_scope::Opaque;
+
+fn get_iter<'a>() -> impl IntoIterator<Item = Opaque<'a>> {
+    None::<Opaque<'static>>
+    //~^ ERROR lifetime may not live long enough
+}
+
+fn main() {}
diff --git a/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr
new file mode 100644
index 00000000000..d8f70e3d778
--- /dev/null
+++ b/tests/ui/type-alias-impl-trait/different_args_considered_equal3.stderr
@@ -0,0 +1,10 @@
+error: lifetime may not live long enough
+  --> $DIR/different_args_considered_equal3.rs:18:5
+   |
+LL | fn get_iter<'a>() -> impl IntoIterator<Item = Opaque<'a>> {
+   |             -- lifetime `'a` defined here
+LL |     None::<Opaque<'static>>
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+
+error: aborting due to 1 previous error
+