about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_arena/src/lib.rs124
-rw-r--r--compiler/rustc_ast/src/entry.rs27
-rw-r--r--compiler/rustc_builtin_macros/src/test_harness.rs22
-rw-r--r--compiler/rustc_middle/src/mir/consts.rs11
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs242
-rw-r--r--compiler/rustc_passes/src/entry.rs33
-rw-r--r--compiler/rustc_resolve/src/diagnostics.rs4
-rw-r--r--library/std/src/lib.rs2
-rw-r--r--library/std/src/sys/uefi/mod.rs1
-rw-r--r--library/std/src/sys/uefi/stdio.rs162
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile3
-rw-r--r--src/doc/rustc/src/platform-support/unknown-uefi.md8
-rw-r--r--tests/coverage-map/unreachable.cov-map24
-rw-r--r--tests/coverage-map/unreachable.rs37
-rw-r--r--tests/run-coverage/unreachable.coverage38
-rw-r--r--tests/run-coverage/unreachable.rs37
-rw-r--r--tests/ui/pattern/usefulness/floats.rs40
-rw-r--r--tests/ui/pattern/usefulness/floats.stderr52
-rw-r--r--tests/ui/resolve/issue-116164.rs19
-rw-r--r--tests/ui/resolve/issue-116164.stderr14
20 files changed, 640 insertions, 260 deletions
diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs
index 23fdd272ffd..06eb8a185be 100644
--- a/compiler/rustc_arena/src/lib.rs
+++ b/compiler/rustc_arena/src/lib.rs
@@ -172,8 +172,8 @@ impl<T, const N: usize> IterExt<T> for std::array::IntoIter<T, N> {
             return &mut [];
         }
         // Move the content to the arena by copying and then forgetting it.
+        let start_ptr = arena.alloc_raw_slice(len);
         unsafe {
-            let start_ptr = arena.alloc_raw_slice(len);
             self.as_slice().as_ptr().copy_to_nonoverlapping(start_ptr, len);
             mem::forget(self);
             slice::from_raw_parts_mut(start_ptr, len)
@@ -189,8 +189,8 @@ impl<T> IterExt<T> for Vec<T> {
             return &mut [];
         }
         // Move the content to the arena by copying and then forgetting it.
+        let start_ptr = arena.alloc_raw_slice(len);
         unsafe {
-            let start_ptr = arena.alloc_raw_slice(len);
             self.as_ptr().copy_to_nonoverlapping(start_ptr, len);
             self.set_len(0);
             slice::from_raw_parts_mut(start_ptr, len)
@@ -206,8 +206,8 @@ impl<A: smallvec::Array> IterExt<A::Item> for SmallVec<A> {
             return &mut [];
         }
         // Move the content to the arena by copying and then forgetting it.
+        let start_ptr = arena.alloc_raw_slice(len);
         unsafe {
-            let start_ptr = arena.alloc_raw_slice(len);
             self.as_ptr().copy_to_nonoverlapping(start_ptr, len);
             self.set_len(0);
             slice::from_raw_parts_mut(start_ptr, len)
@@ -250,25 +250,20 @@ impl<T> TypedArena<T> {
         available_bytes >= additional_bytes
     }
 
-    /// Ensures there's enough space in the current chunk to fit `len` objects.
     #[inline]
-    fn ensure_capacity(&self, additional: usize) {
-        if !self.can_allocate(additional) {
-            self.grow(additional);
-            debug_assert!(self.can_allocate(additional));
-        }
-    }
-
-    #[inline]
-    unsafe fn alloc_raw_slice(&self, len: usize) -> *mut T {
+    fn alloc_raw_slice(&self, len: usize) -> *mut T {
         assert!(mem::size_of::<T>() != 0);
         assert!(len != 0);
 
-        self.ensure_capacity(len);
+        // Ensure the current chunk can fit `len` objects.
+        if !self.can_allocate(len) {
+            self.grow(len);
+            debug_assert!(self.can_allocate(len));
+        }
 
         let start_ptr = self.ptr.get();
-        // SAFETY: `self.ensure_capacity` makes sure that there is enough space
-        // for `len` elements.
+        // SAFETY: `can_allocate`/`grow` ensures that there is enough space for
+        // `len` elements.
         unsafe { self.ptr.set(start_ptr.add(len)) };
         start_ptr
     }
@@ -407,6 +402,8 @@ impl Default for DroplessArena {
     #[inline]
     fn default() -> DroplessArena {
         DroplessArena {
+            // We set both `start` and `end` to 0 so that the first call to
+            // alloc() will trigger a grow().
             start: Cell::new(ptr::null_mut()),
             end: Cell::new(ptr::null_mut()),
             chunks: Default::default(),
@@ -415,9 +412,11 @@ impl Default for DroplessArena {
 }
 
 impl DroplessArena {
+    #[inline(never)]
+    #[cold]
     fn grow(&self, layout: Layout) {
         // Add some padding so we can align `self.end` while
-        // stilling fitting in a `layout` allocation.
+        // still fitting in a `layout` allocation.
         let additional = layout.size() + cmp::max(DROPLESS_ALIGNMENT, layout.align()) - 1;
 
         unsafe {
@@ -441,7 +440,7 @@ impl DroplessArena {
             let mut chunk = ArenaChunk::new(align_up(new_cap, PAGE));
             self.start.set(chunk.start());
 
-            // Align the end to DROPLESS_ALIGNMENT
+            // Align the end to DROPLESS_ALIGNMENT.
             let end = align_down(chunk.end().addr(), DROPLESS_ALIGNMENT);
 
             // Make sure we don't go past `start`. This should not happen since the allocation
@@ -454,55 +453,40 @@ impl DroplessArena {
         }
     }
 
-    #[inline(never)]
-    #[cold]
-    fn grow_and_alloc_raw(&self, layout: Layout) -> *mut u8 {
-        self.grow(layout);
-        self.alloc_raw_without_grow(layout).unwrap()
-    }
-
-    #[inline(never)]
-    #[cold]
-    fn grow_and_alloc<T>(&self) -> *mut u8 {
-        self.grow_and_alloc_raw(Layout::new::<T>())
-    }
-
-    /// Allocates a byte slice with specified layout from the current memory
-    /// chunk. Returns `None` if there is no free space left to satisfy the
-    /// request.
-    #[inline]
-    fn alloc_raw_without_grow(&self, layout: Layout) -> Option<*mut u8> {
-        let start = self.start.get().addr();
-        let old_end = self.end.get();
-        let end = old_end.addr();
-
-        // Align allocated bytes so that `self.end` stays aligned to DROPLESS_ALIGNMENT
-        let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
-
-        // Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT
-        unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) };
-
-        let new_end = align_down(end.checked_sub(bytes)?, layout.align());
-        if start <= new_end {
-            let new_end = old_end.with_addr(new_end);
-            // `new_end` is aligned to DROPLESS_ALIGNMENT as `align_down` preserves alignment
-            // as both `end` and `bytes` are already aligned to DROPLESS_ALIGNMENT.
-            self.end.set(new_end);
-            Some(new_end)
-        } else {
-            None
-        }
-    }
-
     #[inline]
     pub fn alloc_raw(&self, layout: Layout) -> *mut u8 {
         assert!(layout.size() != 0);
-        if let Some(a) = self.alloc_raw_without_grow(layout) {
-            return a;
+
+        // This loop executes once or twice: if allocation fails the first
+        // time, the `grow` ensures it will succeed the second time.
+        loop {
+            let start = self.start.get().addr();
+            let old_end = self.end.get();
+            let end = old_end.addr();
+
+            // Align allocated bytes so that `self.end` stays aligned to
+            // DROPLESS_ALIGNMENT.
+            let bytes = align_up(layout.size(), DROPLESS_ALIGNMENT);
+
+            // Tell LLVM that `end` is aligned to DROPLESS_ALIGNMENT.
+            unsafe { intrinsics::assume(end == align_down(end, DROPLESS_ALIGNMENT)) };
+
+            if let Some(sub) = end.checked_sub(bytes) {
+                let new_end = align_down(sub, layout.align());
+                if start <= new_end {
+                    let new_end = old_end.with_addr(new_end);
+                    // `new_end` is aligned to DROPLESS_ALIGNMENT as `align_down`
+                    // preserves alignment as both `end` and `bytes` are already
+                    // aligned to DROPLESS_ALIGNMENT.
+                    self.end.set(new_end);
+                    return new_end;
+                }
+            }
+
+            // No free space left. Allocate a new chunk to satisfy the request.
+            // On failure the grow will panic or abort.
+            self.grow(layout);
         }
-        // No free space left. Allocate a new chunk to satisfy the request.
-        // On failure the grow will panic or abort.
-        self.grow_and_alloc_raw(layout)
     }
 
     #[inline]
@@ -510,13 +494,7 @@ impl DroplessArena {
         assert!(!mem::needs_drop::<T>());
         assert!(mem::size_of::<T>() != 0);
 
-        let mem = if let Some(a) = self.alloc_raw_without_grow(Layout::for_value::<T>(&object)) {
-            a
-        } else {
-            // No free space left. Allocate a new chunk to satisfy the request.
-            // On failure the grow will panic or abort.
-            self.grow_and_alloc::<T>()
-        } as *mut T;
+        let mem = self.alloc_raw(Layout::new::<T>()) as *mut T;
 
         unsafe {
             // Write into uninitialized memory.
@@ -713,10 +691,10 @@ pub macro declare_arena([$($a:tt $name:ident: $ty:ty,)*]) {
         }
 
         #[allow(clippy::mut_from_ref)]
-        pub fn alloc_from_iter<'a, T: ArenaAllocatable<'tcx, C>, C>(
-            &'a self,
+        pub fn alloc_from_iter<T: ArenaAllocatable<'tcx, C>, C>(
+            &self,
             iter: impl ::std::iter::IntoIterator<Item = T>,
-        ) -> &'a mut [T] {
+        ) -> &mut [T] {
             T::allocate_from_iter(self, iter)
         }
     }
diff --git a/compiler/rustc_ast/src/entry.rs b/compiler/rustc_ast/src/entry.rs
index 3370146193a..2dd5e96e513 100644
--- a/compiler/rustc_ast/src/entry.rs
+++ b/compiler/rustc_ast/src/entry.rs
@@ -1,3 +1,7 @@
+use crate::{attr, Attribute};
+use rustc_span::symbol::sym;
+use rustc_span::Symbol;
+
 #[derive(Debug)]
 pub enum EntryPointType {
     None,
@@ -6,3 +10,26 @@ pub enum EntryPointType {
     Start,
     OtherMain, // Not an entry point, but some other function named main
 }
+
+pub fn entry_point_type(
+    attrs: &[Attribute],
+    at_root: bool,
+    name: Option<Symbol>,
+) -> EntryPointType {
+    if attr::contains_name(attrs, sym::start) {
+        EntryPointType::Start
+    } else if attr::contains_name(attrs, sym::rustc_main) {
+        EntryPointType::RustcMainAttr
+    } else {
+        if let Some(name) = name && name == sym::main {
+            if at_root {
+                // This is a top-level function so it can be `main`.
+                EntryPointType::MainNamed
+            } else {
+                EntryPointType::OtherMain
+            }
+        } else {
+            EntryPointType::None
+        }
+    }
+}
diff --git a/compiler/rustc_builtin_macros/src/test_harness.rs b/compiler/rustc_builtin_macros/src/test_harness.rs
index 53ff089d7b4..9c57f68f0b8 100644
--- a/compiler/rustc_builtin_macros/src/test_harness.rs
+++ b/compiler/rustc_builtin_macros/src/test_harness.rs
@@ -169,29 +169,15 @@ impl<'a> Visitor<'a> for InnerItemLinter<'_> {
     }
 }
 
-// Beware, this is duplicated in librustc_passes/entry.rs (with
-// `rustc_hir::Item`), so make sure to keep them in sync.
-fn entry_point_type(item: &ast::Item, depth: usize) -> EntryPointType {
+fn entry_point_type(item: &ast::Item, at_root: bool) -> EntryPointType {
     match item.kind {
         ast::ItemKind::Fn(..) => {
-            if attr::contains_name(&item.attrs, sym::start) {
-                EntryPointType::Start
-            } else if attr::contains_name(&item.attrs, sym::rustc_main) {
-                EntryPointType::RustcMainAttr
-            } else if item.ident.name == sym::main {
-                if depth == 0 {
-                    // This is a top-level function so can be 'main'
-                    EntryPointType::MainNamed
-                } else {
-                    EntryPointType::OtherMain
-                }
-            } else {
-                EntryPointType::None
-            }
+            rustc_ast::entry::entry_point_type(&item.attrs, at_root, Some(item.ident.name))
         }
         _ => EntryPointType::None,
     }
 }
+
 /// A folder used to remove any entry points (like fn main) because the harness
 /// generator will provide its own
 struct EntryPointCleaner<'a> {
@@ -210,7 +196,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
         // Remove any #[rustc_main] or #[start] from the AST so it doesn't
         // clash with the one we're going to add, but mark it as
         // #[allow(dead_code)] to avoid printing warnings.
-        let item = match entry_point_type(&item, self.depth) {
+        let item = match entry_point_type(&item, self.depth == 0) {
             EntryPointType::MainNamed | EntryPointType::RustcMainAttr | EntryPointType::Start => {
                 item.map(|ast::Item { id, ident, attrs, kind, vis, span, tokens }| {
                     let allow_dead_code = attr::mk_attr_nested_word(
diff --git a/compiler/rustc_middle/src/mir/consts.rs b/compiler/rustc_middle/src/mir/consts.rs
index 7c8a57b840b..8f63ed757ba 100644
--- a/compiler/rustc_middle/src/mir/consts.rs
+++ b/compiler/rustc_middle/src/mir/consts.rs
@@ -288,7 +288,16 @@ impl<'tcx> Const<'tcx> {
         tcx: TyCtxt<'tcx>,
         param_env: ty::ParamEnv<'tcx>,
     ) -> Option<ScalarInt> {
-        self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
+        match self {
+            // If the constant is already evaluated, we shortcut here.
+            Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => {
+                valtree.try_to_scalar_int()
+            },
+            // This is a more general form of the previous case.
+            _ => {
+                self.try_eval_scalar(tcx, param_env)?.try_to_int().ok()
+            },
+        }
     }
 
     #[inline]
diff --git a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
index b79beb1c537..3ee4befa121 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/deconstruct_pat.rs
@@ -50,6 +50,7 @@ use std::ops::RangeInclusive;
 
 use smallvec::{smallvec, SmallVec};
 
+use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
 use rustc_data_structures::captures::Captures;
 use rustc_hir::{HirId, RangeEnd};
 use rustc_index::Idx;
@@ -60,12 +61,11 @@ use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
 use rustc_session::lint;
 use rustc_span::{Span, DUMMY_SP};
-use rustc_target::abi::{FieldIdx, Integer, Size, VariantIdx, FIRST_VARIANT};
+use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
 
 use self::Constructor::*;
 use self::SliceKind::*;
 
-use super::compare_const_vals;
 use super::usefulness::{MatchCheckCtxt, PatCtxt};
 use crate::errors::{Overlap, OverlappingRangeEndpoints};
 
@@ -99,10 +99,6 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
 #[derive(Clone, PartialEq, Eq)]
 pub(crate) struct IntRange {
     range: RangeInclusive<u128>,
-    /// Keeps the bias used for encoding the range. It depends on the type of the range and
-    /// possibly the pointer size of the current architecture. The algorithm ensures we never
-    /// compare `IntRange`s with different types/architectures.
-    bias: u128,
 }
 
 impl IntRange {
@@ -120,37 +116,12 @@ impl IntRange {
     }
 
     #[inline]
-    fn integral_size_and_signed_bias(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Size, u128)> {
-        match *ty.kind() {
-            ty::Bool => Some((Size::from_bytes(1), 0)),
-            ty::Char => Some((Size::from_bytes(4), 0)),
-            ty::Int(ity) => {
-                let size = Integer::from_int_ty(&tcx, ity).size();
-                Some((size, 1u128 << (size.bits() as u128 - 1)))
-            }
-            ty::Uint(uty) => Some((Integer::from_uint_ty(&tcx, uty).size(), 0)),
-            _ => None,
-        }
-    }
-
-    #[inline]
-    fn from_constant<'tcx>(
-        tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
-        value: mir::Const<'tcx>,
-    ) -> Option<IntRange> {
-        let ty = value.ty();
-        let (target_size, bias) = Self::integral_size_and_signed_bias(tcx, ty)?;
-        let val = match value {
-            mir::Const::Ty(c) if let ty::ConstKind::Value(valtree) = c.kind() => {
-                valtree.unwrap_leaf().to_bits(target_size).ok()
-            },
-            // This is a more general form of the previous case.
-            _ => value.try_eval_bits(tcx, param_env),
-        }?;
-
-        let val = val ^ bias;
-        Some(IntRange { range: val..=val, bias })
+    fn from_bits<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, bits: u128) -> IntRange {
+        let bias = IntRange::signed_bias(tcx, ty);
+        // Perform a shift if the underlying types are signed,
+        // which makes the interval arithmetic simpler.
+        let val = bits ^ bias;
+        IntRange { range: val..=val }
     }
 
     #[inline]
@@ -159,20 +130,18 @@ impl IntRange {
         lo: u128,
         hi: u128,
         ty: Ty<'tcx>,
-        end: &RangeEnd,
-    ) -> Option<IntRange> {
-        Self::is_integral(ty).then(|| {
-            // Perform a shift if the underlying types are signed,
-            // which makes the interval arithmetic simpler.
-            let bias = IntRange::signed_bias(tcx, ty);
-            let (lo, hi) = (lo ^ bias, hi ^ bias);
-            let offset = (*end == RangeEnd::Excluded) as u128;
-            if lo > hi || (lo == hi && *end == RangeEnd::Excluded) {
-                // This should have been caught earlier by E0030.
-                bug!("malformed range pattern: {}..={}", lo, (hi - offset));
-            }
-            IntRange { range: lo..=(hi - offset), bias }
-        })
+        end: RangeEnd,
+    ) -> IntRange {
+        // Perform a shift if the underlying types are signed,
+        // which makes the interval arithmetic simpler.
+        let bias = IntRange::signed_bias(tcx, ty);
+        let (lo, hi) = (lo ^ bias, hi ^ bias);
+        let offset = (end == RangeEnd::Excluded) as u128;
+        if lo > hi || (lo == hi && end == RangeEnd::Excluded) {
+            // This should have been caught earlier by E0030.
+            bug!("malformed range pattern: {}..={}", lo, (hi - offset));
+        }
+        IntRange { range: lo..=(hi - offset) }
     }
 
     // The return value of `signed_bias` should be XORed with an endpoint to encode/decode it.
@@ -194,7 +163,7 @@ impl IntRange {
         let (lo, hi) = self.boundaries();
         let (other_lo, other_hi) = other.boundaries();
         if lo <= other_hi && other_lo <= hi {
-            Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi), bias: self.bias })
+            Some(IntRange { range: max(lo, other_lo)..=min(hi, other_hi) })
         } else {
             None
         }
@@ -221,7 +190,7 @@ impl IntRange {
     fn to_pat<'tcx>(&self, tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Pat<'tcx> {
         let (lo, hi) = self.boundaries();
 
-        let bias = self.bias;
+        let bias = IntRange::signed_bias(tcx, ty);
         let (lo, hi) = (lo ^ bias, hi ^ bias);
 
         let env = ty::ParamEnv::empty().and(ty);
@@ -304,8 +273,6 @@ impl IntRange {
 impl fmt::Debug for IntRange {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let (lo, hi) = self.boundaries();
-        let bias = self.bias;
-        let (lo, hi) = (lo ^ bias, hi ^ bias);
         write!(f, "{lo}")?;
         write!(f, "{}", RangeEnd::Included)?;
         write!(f, "{hi}")
@@ -402,7 +369,7 @@ impl SplitIntRange {
                     (JustBefore(n), AfterMax) => n..=u128::MAX,
                     _ => unreachable!(), // Ruled out by the sorting and filtering we did
                 };
-                IntRange { range, bias: self.range.bias }
+                IntRange { range }
             })
     }
 }
@@ -619,7 +586,8 @@ pub(super) enum Constructor<'tcx> {
     /// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
     IntRange(IntRange),
     /// Ranges of floating-point literal values (`2.0..=5.2`).
-    FloatRange(mir::Const<'tcx>, mir::Const<'tcx>, RangeEnd),
+    F32Range(IeeeFloat<SingleS>, IeeeFloat<SingleS>, RangeEnd),
+    F64Range(IeeeFloat<DoubleS>, IeeeFloat<DoubleS>, RangeEnd),
     /// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
     Str(mir::Const<'tcx>),
     /// Array and slice patterns.
@@ -634,7 +602,9 @@ pub(super) enum Constructor<'tcx> {
     /// Stands for constructors that are not seen in the matrix, as explained in the documentation
     /// for [`SplitWildcard`]. The carried `bool` is used for the `non_exhaustive_omitted_patterns`
     /// lint.
-    Missing { nonexhaustive_enum_missing_real_variants: bool },
+    Missing {
+        nonexhaustive_enum_missing_real_variants: bool,
+    },
     /// Wildcard pattern.
     Wildcard,
     /// Or-pattern.
@@ -722,7 +692,8 @@ impl<'tcx> Constructor<'tcx> {
             },
             Slice(slice) => slice.arity(),
             Str(..)
-            | FloatRange(..)
+            | F32Range(..)
+            | F64Range(..)
             | IntRange(..)
             | NonExhaustive
             | Opaque
@@ -795,21 +766,21 @@ impl<'tcx> Constructor<'tcx> {
             (Variant(self_id), Variant(other_id)) => self_id == other_id,
 
             (IntRange(self_range), IntRange(other_range)) => self_range.is_covered_by(other_range),
-            (
-                FloatRange(self_from, self_to, self_end),
-                FloatRange(other_from, other_to, other_end),
-            ) => {
-                match (
-                    compare_const_vals(pcx.cx.tcx, *self_to, *other_to, pcx.cx.param_env),
-                    compare_const_vals(pcx.cx.tcx, *self_from, *other_from, pcx.cx.param_env),
-                ) {
-                    (Some(to), Some(from)) => {
-                        (from == Ordering::Greater || from == Ordering::Equal)
-                            && (to == Ordering::Less
-                                || (other_end == self_end && to == Ordering::Equal))
+            (F32Range(self_from, self_to, self_end), F32Range(other_from, other_to, other_end)) => {
+                self_from.ge(other_from)
+                    && match self_to.partial_cmp(other_to) {
+                        Some(Ordering::Less) => true,
+                        Some(Ordering::Equal) => other_end == self_end,
+                        _ => false,
+                    }
+            }
+            (F64Range(self_from, self_to, self_end), F64Range(other_from, other_to, other_end)) => {
+                self_from.ge(other_from)
+                    && match self_to.partial_cmp(other_to) {
+                        Some(Ordering::Less) => true,
+                        Some(Ordering::Equal) => other_end == self_end,
+                        _ => false,
                     }
-                    _ => false,
-                }
             }
             (Str(self_val), Str(other_val)) => {
                 // FIXME Once valtrees are available we can directly use the bytes
@@ -859,7 +830,7 @@ impl<'tcx> Constructor<'tcx> {
                 .any(|other| slice.is_covered_by(other)),
             // This constructor is never covered by anything else
             NonExhaustive => false,
-            Str(..) | FloatRange(..) | Opaque | Missing { .. } | Wildcard | Or => {
+            Str(..) | F32Range(..) | F64Range(..) | Opaque | Missing { .. } | Wildcard | Or => {
                 span_bug!(pcx.span, "found unexpected ctor in all_ctors: {:?}", self)
             }
         }
@@ -896,7 +867,7 @@ impl<'tcx> SplitWildcard<'tcx> {
         let make_range = |start, end| {
             IntRange(
                 // `unwrap()` is ok because we know the type is an integer.
-                IntRange::from_range(cx.tcx, start, end, pcx.ty, &RangeEnd::Included).unwrap(),
+                IntRange::from_range(cx.tcx, start, end, pcx.ty, RangeEnd::Included),
             )
         };
         // This determines the set of all possible constructors for the type `pcx.ty`. For numbers,
@@ -1203,7 +1174,8 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
                 _ => bug!("bad slice pattern {:?} {:?}", constructor, pcx),
             },
             Str(..)
-            | FloatRange(..)
+            | F32Range(..)
+            | F64Range(..)
             | IntRange(..)
             | NonExhaustive
             | Opaque
@@ -1343,50 +1315,78 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
                 }
             }
             PatKind::Constant { value } => {
-                if let Some(int_range) = IntRange::from_constant(cx.tcx, cx.param_env, *value) {
-                    ctor = IntRange(int_range);
-                    fields = Fields::empty();
-                } else {
-                    match pat.ty.kind() {
-                        ty::Float(_) => {
-                            ctor = FloatRange(*value, *value, RangeEnd::Included);
-                            fields = Fields::empty();
-                        }
-                        ty::Ref(_, t, _) if t.is_str() => {
-                            // We want a `&str` constant to behave like a `Deref` pattern, to be compatible
-                            // with other `Deref` patterns. This could have been done in `const_to_pat`,
-                            // but that causes issues with the rest of the matching code.
-                            // So here, the constructor for a `"foo"` pattern is `&` (represented by
-                            // `Single`), and has one field. That field has constructor `Str(value)` and no
-                            // fields.
-                            // Note: `t` is `str`, not `&str`.
-                            let subpattern =
-                                DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span);
-                            ctor = Single;
-                            fields = Fields::singleton(cx, subpattern)
-                        }
-                        // All constants that can be structurally matched have already been expanded
-                        // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
-                        // opaque.
-                        _ => {
-                            ctor = Opaque;
-                            fields = Fields::empty();
-                        }
+                match pat.ty.kind() {
+                    ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) => {
+                        ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
+                            Some(bits) => IntRange(IntRange::from_bits(cx.tcx, pat.ty, bits)),
+                            None => Opaque,
+                        };
+                        fields = Fields::empty();
+                    }
+                    ty::Float(ty::FloatTy::F32) => {
+                        ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
+                            Some(bits) => {
+                                use rustc_apfloat::Float;
+                                let value = rustc_apfloat::ieee::Single::from_bits(bits);
+                                F32Range(value, value, RangeEnd::Included)
+                            }
+                            None => Opaque,
+                        };
+                        fields = Fields::empty();
+                    }
+                    ty::Float(ty::FloatTy::F64) => {
+                        ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
+                            Some(bits) => {
+                                use rustc_apfloat::Float;
+                                let value = rustc_apfloat::ieee::Double::from_bits(bits);
+                                F64Range(value, value, RangeEnd::Included)
+                            }
+                            None => Opaque,
+                        };
+                        fields = Fields::empty();
+                    }
+                    ty::Ref(_, t, _) if t.is_str() => {
+                        // We want a `&str` constant to behave like a `Deref` pattern, to be compatible
+                        // with other `Deref` patterns. This could have been done in `const_to_pat`,
+                        // but that causes issues with the rest of the matching code.
+                        // So here, the constructor for a `"foo"` pattern is `&` (represented by
+                        // `Single`), and has one field. That field has constructor `Str(value)` and no
+                        // fields.
+                        // Note: `t` is `str`, not `&str`.
+                        let subpattern =
+                            DeconstructedPat::new(Str(*value), Fields::empty(), *t, pat.span);
+                        ctor = Single;
+                        fields = Fields::singleton(cx, subpattern)
+                    }
+                    // All constants that can be structurally matched have already been expanded
+                    // into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
+                    // opaque.
+                    _ => {
+                        ctor = Opaque;
+                        fields = Fields::empty();
                     }
                 }
             }
-            &PatKind::Range(box PatRange { lo, hi, end }) => {
+            PatKind::Range(box PatRange { lo, hi, end }) => {
+                use rustc_apfloat::Float;
                 let ty = lo.ty();
-                ctor = if let Some(int_range) = IntRange::from_range(
-                    cx.tcx,
-                    lo.eval_bits(cx.tcx, cx.param_env),
-                    hi.eval_bits(cx.tcx, cx.param_env),
-                    ty,
-                    &end,
-                ) {
-                    IntRange(int_range)
-                } else {
-                    FloatRange(lo, hi, end)
+                let lo = lo.try_eval_bits(cx.tcx, cx.param_env).unwrap();
+                let hi = hi.try_eval_bits(cx.tcx, cx.param_env).unwrap();
+                ctor = match ty.kind() {
+                    ty::Char | ty::Int(_) | ty::Uint(_) => {
+                        IntRange(IntRange::from_range(cx.tcx, lo, hi, ty, *end))
+                    }
+                    ty::Float(ty::FloatTy::F32) => {
+                        let lo = rustc_apfloat::ieee::Single::from_bits(lo);
+                        let hi = rustc_apfloat::ieee::Single::from_bits(hi);
+                        F32Range(lo, hi, *end)
+                    }
+                    ty::Float(ty::FloatTy::F64) => {
+                        let lo = rustc_apfloat::ieee::Double::from_bits(lo);
+                        let hi = rustc_apfloat::ieee::Double::from_bits(hi);
+                        F64Range(lo, hi, *end)
+                    }
+                    _ => bug!("invalid type for range pattern: {}", ty),
                 };
                 fields = Fields::empty();
             }
@@ -1491,14 +1491,13 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
                 }
             }
             &Str(value) => PatKind::Constant { value },
-            &FloatRange(lo, hi, end) => PatKind::Range(Box::new(PatRange { lo, hi, end })),
             IntRange(range) => return range.to_pat(cx.tcx, self.ty),
             Wildcard | NonExhaustive => PatKind::Wild,
             Missing { .. } => bug!(
                 "trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
                 `Missing` should have been processed in `apply_constructors`"
             ),
-            Opaque | Or => {
+            F32Range(..) | F64Range(..) | Opaque | Or => {
                 bug!("can't convert to pattern: {:?}", self)
             }
         };
@@ -1673,11 +1672,8 @@ impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
                 }
                 write!(f, "]")
             }
-            &FloatRange(lo, hi, end) => {
-                write!(f, "{lo}")?;
-                write!(f, "{end}")?;
-                write!(f, "{hi}")
-            }
+            F32Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
+            F64Range(lo, hi, end) => write!(f, "{lo}{end}{hi}"),
             IntRange(range) => write!(f, "{range:?}"), // Best-effort, will render e.g. `false` as `0..=0`
             Wildcard | Missing { .. } | NonExhaustive => write!(f, "_ : {:?}", self.ty),
             Or => {
diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs
index 4f71704b885..c92d0b878ea 100644
--- a/compiler/rustc_passes/src/entry.rs
+++ b/compiler/rustc_passes/src/entry.rs
@@ -52,31 +52,6 @@ fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> {
     configure_main(tcx, &ctxt)
 }
 
-// Beware, this is duplicated in `librustc_builtin_macros/test_harness.rs`
-// (with `ast::Item`), so make sure to keep them in sync.
-// A small optimization was added so that hir::Item is fetched only when needed.
-// An equivalent optimization was not applied to the duplicated code in test_harness.rs.
-fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> EntryPointType {
-    let attrs = ctxt.tcx.hir().attrs(id.hir_id());
-    if attr::contains_name(attrs, sym::start) {
-        EntryPointType::Start
-    } else if attr::contains_name(attrs, sym::rustc_main) {
-        EntryPointType::RustcMainAttr
-    } else {
-        if let Some(name) = ctxt.tcx.opt_item_name(id.owner_id.to_def_id())
-            && name == sym::main {
-            if at_root {
-                // This is a top-level function so can be `main`.
-                EntryPointType::MainNamed
-            } else {
-                EntryPointType::OtherMain
-            }
-        } else {
-            EntryPointType::None
-        }
-    }
-}
-
 fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Option<Span> {
     let attrs = ctxt.tcx.hir().attrs(id.hir_id());
     attr::find_by_name(attrs, sym).map(|attr| attr.span)
@@ -85,7 +60,13 @@ fn attr_span_by_symbol(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol) -> Opti
 fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
     let at_root = ctxt.tcx.opt_local_parent(id.owner_id.def_id) == Some(CRATE_DEF_ID);
 
-    match entry_point_type(ctxt, id, at_root) {
+    let attrs = ctxt.tcx.hir().attrs(id.hir_id());
+    let entry_point_type = rustc_ast::entry::entry_point_type(
+        attrs,
+        at_root,
+        ctxt.tcx.opt_item_name(id.owner_id.to_def_id()),
+    );
+    match entry_point_type {
         EntryPointType::None => {
             if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
                 ctxt.tcx.sess.emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs
index 907a6b1c46c..110286255c5 100644
--- a/compiler/rustc_resolve/src/diagnostics.rs
+++ b/compiler/rustc_resolve/src/diagnostics.rs
@@ -1169,6 +1169,10 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
                     return;
                 }
 
+                if ident.name == kw::Underscore {
+                    return;
+                }
+
                 let child_accessible =
                     accessible && this.is_accessible_from(name_binding.vis, parent_scope.module);
 
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index f1f0f8b1653..6f78778f01a 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -259,7 +259,7 @@
     all(target_vendor = "fortanix", target_env = "sgx"),
     feature(slice_index_methods, coerce_unsized, sgx_platform)
 )]
-#![cfg_attr(windows, feature(round_char_boundary))]
+#![cfg_attr(any(windows, target_os = "uefi"), feature(round_char_boundary))]
 #![cfg_attr(target_os = "xous", feature(slice_ptr_len))]
 //
 // Language features:
diff --git a/library/std/src/sys/uefi/mod.rs b/library/std/src/sys/uefi/mod.rs
index 9a10395af8e..097396ae993 100644
--- a/library/std/src/sys/uefi/mod.rs
+++ b/library/std/src/sys/uefi/mod.rs
@@ -36,7 +36,6 @@ pub mod path;
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
 pub mod process;
-#[path = "../unsupported/stdio.rs"]
 pub mod stdio;
 #[path = "../unsupported/thread.rs"]
 pub mod thread;
diff --git a/library/std/src/sys/uefi/stdio.rs b/library/std/src/sys/uefi/stdio.rs
new file mode 100644
index 00000000000..a533d8a0575
--- /dev/null
+++ b/library/std/src/sys/uefi/stdio.rs
@@ -0,0 +1,162 @@
+use crate::io;
+use crate::iter::Iterator;
+use crate::mem::MaybeUninit;
+use crate::os::uefi;
+use crate::ptr::NonNull;
+
+const MAX_BUFFER_SIZE: usize = 8192;
+
+pub struct Stdin;
+pub struct Stdout;
+pub struct Stderr;
+
+impl Stdin {
+    pub const fn new() -> Stdin {
+        Stdin
+    }
+}
+
+impl io::Read for Stdin {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
+        let stdin = unsafe { (*st.as_ptr()).con_in };
+
+        // Try reading any pending data
+        let inp = match read_key_stroke(stdin) {
+            Ok(x) => x,
+            Err(e) if e == r_efi::efi::Status::NOT_READY => {
+                // Wait for keypress for new data
+                wait_stdin(stdin)?;
+                read_key_stroke(stdin).map_err(|x| io::Error::from_raw_os_error(x.as_usize()))?
+            }
+            Err(e) => {
+                return Err(io::Error::from_raw_os_error(e.as_usize()));
+            }
+        };
+
+        // Check if the key is printiable character
+        if inp.scan_code != 0x00 {
+            return Err(io::const_io_error!(io::ErrorKind::Interrupted, "Special Key Press"));
+        }
+
+        // SAFETY: Iterator will have only 1 character since we are reading only 1 Key
+        // SAFETY: This character will always be UCS-2 and thus no surrogates.
+        let ch: char = char::decode_utf16([inp.unicode_char]).next().unwrap().unwrap();
+        if ch.len_utf8() > buf.len() {
+            return Ok(0);
+        }
+
+        ch.encode_utf8(buf);
+
+        Ok(ch.len_utf8())
+    }
+}
+
+impl Stdout {
+    pub const fn new() -> Stdout {
+        Stdout
+    }
+}
+
+impl io::Write for Stdout {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
+        let stdout = unsafe { (*st.as_ptr()).con_out };
+
+        write(stdout, buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Stderr {
+    pub const fn new() -> Stderr {
+        Stderr
+    }
+}
+
+impl io::Write for Stderr {
+    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+        let st: NonNull<r_efi::efi::SystemTable> = uefi::env::system_table().cast();
+        let stderr = unsafe { (*st.as_ptr()).std_err };
+
+        write(stderr, buf)
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+// UCS-2 character should occupy 3 bytes at most in UTF-8
+pub const STDIN_BUF_SIZE: usize = 3;
+
+pub fn is_ebadf(_err: &io::Error) -> bool {
+    true
+}
+
+pub fn panic_output() -> Option<impl io::Write> {
+    uefi::env::try_system_table().map(|_| Stderr::new())
+}
+
+fn write(
+    protocol: *mut r_efi::protocols::simple_text_output::Protocol,
+    buf: &[u8],
+) -> io::Result<usize> {
+    let mut utf16 = [0; MAX_BUFFER_SIZE / 2];
+
+    // Get valid UTF-8 buffer
+    let utf8 = match crate::str::from_utf8(buf) {
+        Ok(x) => x,
+        Err(e) => unsafe { crate::str::from_utf8_unchecked(&buf[..e.valid_up_to()]) },
+    };
+    // Clip UTF-8 buffer to max UTF-16 buffer we support
+    let utf8 = &utf8[..utf8.floor_char_boundary(utf16.len() - 1)];
+
+    for (i, ch) in utf8.encode_utf16().enumerate() {
+        utf16[i] = ch;
+    }
+
+    unsafe { simple_text_output(protocol, &mut utf16) }?;
+
+    Ok(utf8.len())
+}
+
+unsafe fn simple_text_output(
+    protocol: *mut r_efi::protocols::simple_text_output::Protocol,
+    buf: &mut [u16],
+) -> io::Result<()> {
+    let res = unsafe { ((*protocol).output_string)(protocol, buf.as_mut_ptr()) };
+    if res.is_error() { Err(io::Error::from_raw_os_error(res.as_usize())) } else { Ok(()) }
+}
+
+fn wait_stdin(stdin: *mut r_efi::protocols::simple_text_input::Protocol) -> io::Result<()> {
+    let boot_services: NonNull<r_efi::efi::BootServices> =
+        uefi::env::boot_services().unwrap().cast();
+    let wait_for_event = unsafe { (*boot_services.as_ptr()).wait_for_event };
+    let wait_for_key_event = unsafe { (*stdin).wait_for_key };
+
+    let r = {
+        let mut x: usize = 0;
+        (wait_for_event)(1, [wait_for_key_event].as_mut_ptr(), &mut x)
+    };
+    if r.is_error() { Err(io::Error::from_raw_os_error(r.as_usize())) } else { Ok(()) }
+}
+
+fn read_key_stroke(
+    stdin: *mut r_efi::protocols::simple_text_input::Protocol,
+) -> Result<r_efi::protocols::simple_text_input::InputKey, r_efi::efi::Status> {
+    let mut input_key: MaybeUninit<r_efi::protocols::simple_text_input::InputKey> =
+        MaybeUninit::uninit();
+
+    let r = unsafe { ((*stdin).read_key_stroke)(stdin, input_key.as_mut_ptr()) };
+
+    if r.is_error() {
+        Err(r)
+    } else {
+        let input_key = unsafe { input_key.assume_init() };
+        Ok(input_key)
+    }
+}
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
index 6f1b2a6a638..c04121a8bee 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
@@ -84,7 +84,8 @@ ENV RUST_CONFIGURE_ARGS \
       --set llvm.ninja=false \
       --set rust.jemalloc \
       --set rust.use-lld=true \
-      --set rust.lto=thin
+      --set rust.lto=thin \
+      --set rust.codegen-units=1
 
 ENV SCRIPT python3 ../x.py build --set rust.debug=true opt-dist && \
     ./build/$HOSTS/stage0-tools-bin/opt-dist linux-ci -- python3 ../x.py dist \
diff --git a/src/doc/rustc/src/platform-support/unknown-uefi.md b/src/doc/rustc/src/platform-support/unknown-uefi.md
index 68cd7fae319..370939520dc 100644
--- a/src/doc/rustc/src/platform-support/unknown-uefi.md
+++ b/src/doc/rustc/src/platform-support/unknown-uefi.md
@@ -265,9 +265,12 @@ cargo build --target x86_64-unknown-uefi -Zbuild-std=std,panic_abort
 #### os_str
 - While the strings in UEFI should be valid UCS-2, in practice, many implementations just do not care and use UTF-16 strings.
 - Thus, the current implementation supports full UTF-16 strings.
+#### stdio
+- Uses `Simple Text Input Protocol` and `Simple Text Output Protocol`.
+- Note: UEFI uses CRLF for new line. This means Enter key is registered as CR instead of LF.
 
 ## Example: Hello World With std
-The following code features a valid UEFI application, including stdio and `alloc` (`OsString` and `Vec`):
+The following code features a valid UEFI application, including `stdio` and `alloc` (`OsString` and `Vec`):
 
 This example can be compiled as binary crate via `cargo` using the toolchain
 compiled from the above source (named custom):
@@ -286,6 +289,9 @@ use std::{
 };
 
 pub fn main() {
+  println!("Starting Rust Application...");
+
+  // Use System Table Directly
   let st = env::system_table().as_ptr() as *mut efi::SystemTable;
   let mut s: Vec<u16> = OsString::from("Hello World!\n").encode_wide().collect();
   s.push(0);
diff --git a/tests/coverage-map/unreachable.cov-map b/tests/coverage-map/unreachable.cov-map
new file mode 100644
index 00000000000..495419820c1
--- /dev/null
+++ b/tests/coverage-map/unreachable.cov-map
@@ -0,0 +1,24 @@
+Function name: unreachable::UNREACHABLE_CLOSURE::{closure#0}
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 0f, 27, 00, 49]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 15, 39) to (start + 0, 73)
+
+Function name: unreachable::unreachable_function
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 11, 01, 02, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 17, 1) to (start + 2, 2)
+
+Function name: unreachable::unreachable_intrinsic
+Raw bytes (9): 0x[01, 01, 00, 01, 01, 16, 01, 02, 02]
+Number of files: 1
+- file 0 => global file 1
+Number of expressions: 0
+Number of file 0 mappings: 1
+- Code(Counter(0)) at (prev + 22, 1) to (start + 2, 2)
+
diff --git a/tests/coverage-map/unreachable.rs b/tests/coverage-map/unreachable.rs
new file mode 100644
index 00000000000..6385bfa160d
--- /dev/null
+++ b/tests/coverage-map/unreachable.rs
@@ -0,0 +1,37 @@
+#![feature(core_intrinsics)]
+#![feature(coverage_attribute)]
+// compile-flags: --edition=2021
+
+// <https://github.com/rust-lang/rust/issues/116171>
+// If we instrument a function for coverage, but all of its counter-increment
+// statements are removed by MIR optimizations, LLVM will think it isn't
+// instrumented and it will disappear from coverage maps and coverage reports.
+// Most MIR opts won't cause this because they tend not to remove statements
+// from bb0, but `UnreachablePropagation` can do so if it sees that bb0 ends
+// with `TerminatorKind::Unreachable`.
+
+use std::hint::{black_box, unreachable_unchecked};
+
+static UNREACHABLE_CLOSURE: fn() = || unsafe { unreachable_unchecked() };
+
+fn unreachable_function() {
+    unsafe { unreachable_unchecked() }
+}
+
+// Use an intrinsic to more reliably trigger unreachable-propagation.
+fn unreachable_intrinsic() {
+    unsafe { std::intrinsics::unreachable() }
+}
+
+#[coverage(off)]
+fn main() {
+    if black_box(false) {
+        UNREACHABLE_CLOSURE();
+    }
+    if black_box(false) {
+        unreachable_function();
+    }
+    if black_box(false) {
+        unreachable_intrinsic();
+    }
+}
diff --git a/tests/run-coverage/unreachable.coverage b/tests/run-coverage/unreachable.coverage
new file mode 100644
index 00000000000..fa0ac9ccfa1
--- /dev/null
+++ b/tests/run-coverage/unreachable.coverage
@@ -0,0 +1,38 @@
+   LL|       |#![feature(core_intrinsics)]
+   LL|       |#![feature(coverage_attribute)]
+   LL|       |// compile-flags: --edition=2021
+   LL|       |
+   LL|       |// <https://github.com/rust-lang/rust/issues/116171>
+   LL|       |// If we instrument a function for coverage, but all of its counter-increment
+   LL|       |// statements are removed by MIR optimizations, LLVM will think it isn't
+   LL|       |// instrumented and it will disappear from coverage maps and coverage reports.
+   LL|       |// Most MIR opts won't cause this because they tend not to remove statements
+   LL|       |// from bb0, but `UnreachablePropagation` can do so if it sees that bb0 ends
+   LL|       |// with `TerminatorKind::Unreachable`.
+   LL|       |
+   LL|       |use std::hint::{black_box, unreachable_unchecked};
+   LL|       |
+   LL|      0|static UNREACHABLE_CLOSURE: fn() = || unsafe { unreachable_unchecked() };
+   LL|       |
+   LL|      0|fn unreachable_function() {
+   LL|      0|    unsafe { unreachable_unchecked() }
+   LL|      0|}
+   LL|       |
+   LL|       |// Use an intrinsic to more reliably trigger unreachable-propagation.
+   LL|      0|fn unreachable_intrinsic() {
+   LL|      0|    unsafe { std::intrinsics::unreachable() }
+   LL|      0|}
+   LL|       |
+   LL|       |#[coverage(off)]
+   LL|       |fn main() {
+   LL|       |    if black_box(false) {
+   LL|       |        UNREACHABLE_CLOSURE();
+   LL|       |    }
+   LL|       |    if black_box(false) {
+   LL|       |        unreachable_function();
+   LL|       |    }
+   LL|       |    if black_box(false) {
+   LL|       |        unreachable_intrinsic();
+   LL|       |    }
+   LL|       |}
+
diff --git a/tests/run-coverage/unreachable.rs b/tests/run-coverage/unreachable.rs
new file mode 100644
index 00000000000..6385bfa160d
--- /dev/null
+++ b/tests/run-coverage/unreachable.rs
@@ -0,0 +1,37 @@
+#![feature(core_intrinsics)]
+#![feature(coverage_attribute)]
+// compile-flags: --edition=2021
+
+// <https://github.com/rust-lang/rust/issues/116171>
+// If we instrument a function for coverage, but all of its counter-increment
+// statements are removed by MIR optimizations, LLVM will think it isn't
+// instrumented and it will disappear from coverage maps and coverage reports.
+// Most MIR opts won't cause this because they tend not to remove statements
+// from bb0, but `UnreachablePropagation` can do so if it sees that bb0 ends
+// with `TerminatorKind::Unreachable`.
+
+use std::hint::{black_box, unreachable_unchecked};
+
+static UNREACHABLE_CLOSURE: fn() = || unsafe { unreachable_unchecked() };
+
+fn unreachable_function() {
+    unsafe { unreachable_unchecked() }
+}
+
+// Use an intrinsic to more reliably trigger unreachable-propagation.
+fn unreachable_intrinsic() {
+    unsafe { std::intrinsics::unreachable() }
+}
+
+#[coverage(off)]
+fn main() {
+    if black_box(false) {
+        UNREACHABLE_CLOSURE();
+    }
+    if black_box(false) {
+        unreachable_function();
+    }
+    if black_box(false) {
+        unreachable_intrinsic();
+    }
+}
diff --git a/tests/ui/pattern/usefulness/floats.rs b/tests/ui/pattern/usefulness/floats.rs
index 095f5ac9a89..2616dfadb85 100644
--- a/tests/ui/pattern/usefulness/floats.rs
+++ b/tests/ui/pattern/usefulness/floats.rs
@@ -1,19 +1,45 @@
+#![feature(exclusive_range_pattern)]
 #![allow(illegal_floating_point_literal_pattern)]
 #![deny(unreachable_patterns)]
 
 fn main() {
     match 0.0 {
-      0.0..=1.0 => {}
-      _ => {} // ok
+        0.0..=1.0 => {}
+        _ => {} // ok
     }
 
-    match 0.0 { //~ ERROR non-exhaustive patterns
-      0.0..=1.0 => {}
+    match 0.0 {
+        //~^ ERROR non-exhaustive patterns
+        0.0..=1.0 => {}
     }
 
     match 1.0f64 {
-      0.01f64 ..= 6.5f64 => {}
-      0.02f64 => {} //~ ERROR unreachable pattern
-      _ => {}
+        0.01f64..=6.5f64 => {}
+        0.005f64 => {}
+        0.01f64 => {} //~ ERROR unreachable pattern
+        0.02f64 => {} //~ ERROR unreachable pattern
+        6.5f64 => {}  //~ ERROR unreachable pattern
+        6.6f64 => {}
+        1.0f64..=4.0f64 => {} //~ ERROR unreachable pattern
+        5.0f64..=7.0f64 => {}
+        _ => {}
+    };
+    match 1.0f64 {
+        0.01f64..6.5f64 => {}
+        6.5f64 => {} // this is reachable
+        _ => {}
+    };
+
+    match 1.0f32 {
+        0.01f32..=6.5f32 => {}
+        0.01f32 => {} //~ ERROR unreachable pattern
+        0.02f32 => {} //~ ERROR unreachable pattern
+        6.5f32 => {}  //~ ERROR unreachable pattern
+        _ => {}
+    };
+    match 1.0f32 {
+        0.01f32..6.5f32 => {}
+        6.5f32 => {} // this is reachable
+        _ => {}
     };
 }
diff --git a/tests/ui/pattern/usefulness/floats.stderr b/tests/ui/pattern/usefulness/floats.stderr
index d66d4ba298b..f5041911824 100644
--- a/tests/ui/pattern/usefulness/floats.stderr
+++ b/tests/ui/pattern/usefulness/floats.stderr
@@ -1,5 +1,5 @@
 error[E0004]: non-exhaustive patterns: `_` not covered
-  --> $DIR/floats.rs:10:11
+  --> $DIR/floats.rs:11:11
    |
 LL |     match 0.0 {
    |           ^^^ pattern `_` not covered
@@ -7,22 +7,58 @@ LL |     match 0.0 {
    = note: the matched value is of type `f64`
 help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
    |
-LL ~       0.0..=1.0 => {},
-LL +       _ => todo!()
+LL ~         0.0..=1.0 => {},
+LL +         _ => todo!()
    |
 
 error: unreachable pattern
-  --> $DIR/floats.rs:16:7
+  --> $DIR/floats.rs:19:9
    |
-LL |       0.02f64 => {}
-   |       ^^^^^^^
+LL |         0.01f64 => {}
+   |         ^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/floats.rs:2:9
+  --> $DIR/floats.rs:3:9
    |
 LL | #![deny(unreachable_patterns)]
    |         ^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: unreachable pattern
+  --> $DIR/floats.rs:20:9
+   |
+LL |         0.02f64 => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/floats.rs:21:9
+   |
+LL |         6.5f64 => {}
+   |         ^^^^^^
+
+error: unreachable pattern
+  --> $DIR/floats.rs:23:9
+   |
+LL |         1.0f64..=4.0f64 => {}
+   |         ^^^^^^^^^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/floats.rs:35:9
+   |
+LL |         0.01f32 => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/floats.rs:36:9
+   |
+LL |         0.02f32 => {}
+   |         ^^^^^^^
+
+error: unreachable pattern
+  --> $DIR/floats.rs:37:9
+   |
+LL |         6.5f32 => {}
+   |         ^^^^^^
+
+error: aborting due to 8 previous errors
 
 For more information about this error, try `rustc --explain E0004`.
diff --git a/tests/ui/resolve/issue-116164.rs b/tests/ui/resolve/issue-116164.rs
new file mode 100644
index 00000000000..d30c8f514b3
--- /dev/null
+++ b/tests/ui/resolve/issue-116164.rs
@@ -0,0 +1,19 @@
+#![allow(unused_imports)]
+
+mod inner {
+    pub enum Example {
+        ExOne,
+    }
+}
+
+mod reexports {
+    pub use crate::inner::Example as _;
+}
+
+use crate::reexports::*;
+//~^ SUGGESTION: use inner::Example::ExOne
+
+fn main() {
+    ExOne;
+    //~^ ERROR: cannot find value `ExOne` in this scope
+}
diff --git a/tests/ui/resolve/issue-116164.stderr b/tests/ui/resolve/issue-116164.stderr
new file mode 100644
index 00000000000..5820a189fd5
--- /dev/null
+++ b/tests/ui/resolve/issue-116164.stderr
@@ -0,0 +1,14 @@
+error[E0425]: cannot find value `ExOne` in this scope
+  --> $DIR/issue-116164.rs:17:5
+   |
+LL |     ExOne;
+   |     ^^^^^ not found in this scope
+   |
+help: consider importing this unit variant
+   |
+LL + use inner::Example::ExOne;
+   |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.