diff options
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`. |
