diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index ebd3f0100771b..913613653a67d 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -144,11 +144,23 @@ impl Clone for VecDeque { #[stable(feature = "rust1", since = "1.0.0")] unsafe impl<#[may_dangle] T> Drop for VecDeque { fn drop(&mut self) { + /// Runs the destructor for all items in the slice when it gets dropped (normally or + /// during unwinding). + struct Dropper<'a, T>(&'a mut [T]); + + impl<'a, T> Drop for Dropper<'a, T> { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); + } + } + } + let (front, back) = self.as_mut_slices(); unsafe { + let _back_dropper = Dropper(back); // use drop for [T] ptr::drop_in_place(front); - ptr::drop_in_place(back); } // RawVec handles deallocation } diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index ebcc832017172..1ab3694a3ca61 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -2,6 +2,7 @@ use std::collections::TryReserveError::*; use std::collections::{vec_deque::Drain, VecDeque}; use std::fmt::Debug; use std::mem::size_of; +use std::panic::catch_unwind; use std::{isize, usize}; use crate::hash; @@ -709,6 +710,39 @@ fn test_drop_clear() { assert_eq!(unsafe { DROPS }, 4); } +#[test] +fn test_drop_panic() { + static mut DROPS: i32 = 0; + + struct D(bool); + + impl Drop for D { + fn drop(&mut self) { + unsafe { + DROPS += 1; + } + + if self.0 { + panic!("panic in `drop`"); + } + } + } + + let mut q = VecDeque::new(); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(false)); + q.push_front(D(false)); + q.push_front(D(true)); + + catch_unwind(move || drop(q)).ok(); + + assert_eq!(unsafe { DROPS }, 8); +} + #[test] fn test_reserve_grow() { // test growth path A diff --git a/src/libcore/convert/mod.rs b/src/libcore/convert/mod.rs index 5414d9ac2344a..b7db3e4197db4 100644 --- a/src/libcore/convert/mod.rs +++ b/src/libcore/convert/mod.rs @@ -291,7 +291,7 @@ pub trait Into: Sized { /// [`Into`]. /// /// One should always prefer implementing `From` over [`Into`] -/// because implementing `From` automatically provides one with a implementation of [`Into`] +/// because implementing `From` automatically provides one with an implementation of [`Into`] /// thanks to the blanket implementation in the standard library. /// /// Only implement [`Into`] if a conversion to a type outside the current crate is required. diff --git a/src/libcore/mem/mod.rs b/src/libcore/mem/mod.rs index bba441464ff35..ec926aa6c2308 100644 --- a/src/libcore/mem/mod.rs +++ b/src/libcore/mem/mod.rs @@ -510,7 +510,9 @@ pub unsafe fn zeroed() -> T { /// **This function is deprecated.** Use [`MaybeUninit`] instead. /// /// The reason for deprecation is that the function basically cannot be used -/// correctly: [the Rust compiler assumes][inv] that values are properly initialized. +/// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit]. +/// As the [`assume_init` documentation][assume_init] explains, +/// [the Rust compiler assumes][inv] that values are properly initialized. /// As a consequence, calling e.g. `mem::uninitialized::()` causes immediate /// undefined behavior for returning a `bool` that is not definitely either `true` /// or `false`. Worse, truly uninitialized memory like what gets returned here @@ -521,6 +523,8 @@ pub unsafe fn zeroed() -> T { /// until they are, it is advisable to avoid them.) /// /// [`MaybeUninit`]: union.MaybeUninit.html +/// [uninit]: union.MaybeUninit.html#method.uninit +/// [assume_init]: union.MaybeUninit.html#method.assume_init /// [inv]: union.MaybeUninit.html#initialization-invariant #[inline] #[rustc_deprecated(since = "1.39.0", reason = "use `mem::MaybeUninit` instead")] diff --git a/src/librustc_mir/hair/pattern/_match.rs b/src/librustc_mir/hair/pattern/_match.rs index 37a9381271a8c..c372032850695 100644 --- a/src/librustc_mir/hair/pattern/_match.rs +++ b/src/librustc_mir/hair/pattern/_match.rs @@ -238,7 +238,7 @@ use super::{FieldPat, Pat, PatKind, PatRange}; use rustc::hir::def_id::DefId; use rustc::hir::{HirId, RangeEnd}; use rustc::ty::layout::{Integer, IntegerExt, Size, VariantIdx}; -use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, Const, Ty, TyCtxt, TypeFoldable, VariantDef}; use rustc::lint; use rustc::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; @@ -354,7 +354,7 @@ impl PatternFolder<'tcx> for LiteralExpander<'tcx> { } impl<'tcx> Pat<'tcx> { - fn is_wildcard(&self) -> bool { + pub(super) fn is_wildcard(&self) -> bool { match *self.kind { PatKind::Binding { subpattern: None, .. } | PatKind::Wild => true, _ => false, @@ -596,9 +596,21 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } } - fn is_local(&self, ty: Ty<'tcx>) -> bool { + // Returns whether the given type is an enum from another crate declared `#[non_exhaustive]`. + pub fn is_foreign_non_exhaustive_enum(&self, ty: Ty<'tcx>) -> bool { match ty.kind { - ty::Adt(adt_def, ..) => adt_def.did.is_local(), + ty::Adt(def, ..) => { + def.is_enum() && def.is_variant_list_non_exhaustive() && !def.did.is_local() + } + _ => false, + } + } + + // Returns whether the given variant is from another crate and has its fields declared + // `#[non_exhaustive]`. + fn is_foreign_non_exhaustive_variant(&self, ty: Ty<'tcx>, variant: &VariantDef) -> bool { + match ty.kind { + ty::Adt(def, ..) => variant.is_field_list_non_exhaustive() && !def.did.is_local(), _ => false, } } @@ -758,6 +770,10 @@ impl<'tcx> Constructor<'tcx> { // Returns the set of constructors covered by `self` but not by // anything in `other_ctors`. fn subtract_ctors(&self, other_ctors: &Vec>) -> Vec> { + if other_ctors.is_empty() { + return vec![self.clone()]; + } + match self { // Those constructors can only match themselves. Single | Variant(_) | ConstantValue(..) | FloatRange(..) => { @@ -858,8 +874,7 @@ impl<'tcx> Constructor<'tcx> { vec![Pat::wildcard_from_ty(substs.type_at(0))] } else { let variant = &adt.variants[self.variant_index_for_adt(cx, adt)]; - let is_non_exhaustive = - variant.is_field_list_non_exhaustive() && !cx.is_local(ty); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(ty, variant); variant .fields .iter() @@ -1205,6 +1220,8 @@ impl<'tcx> Witness<'tcx> { /// /// We make sure to omit constructors that are statically impossible. E.g., for /// `Option`, we do not include `Some(_)` in the returned list of constructors. +/// Invariant: this returns an empty `Vec` if and only if the type is uninhabited (as determined by +/// `cx.is_uninhabited()`). fn all_constructors<'a, 'tcx>( cx: &mut MatchCheckCtxt<'a, 'tcx>, pcx: PatCtxt<'tcx>, @@ -1235,47 +1252,45 @@ fn all_constructors<'a, 'tcx>( vec![Slice(Slice { array_len: None, kind })] } ty::Adt(def, substs) if def.is_enum() => { - let ctors: Vec<_> = def - .variants - .iter() - .filter(|v| { - !cx.tcx.features().exhaustive_patterns - || !v - .uninhabited_from(cx.tcx, substs, def.adt_kind()) + let ctors: Vec<_> = if cx.tcx.features().exhaustive_patterns { + // If `exhaustive_patterns` is enabled, we exclude variants known to be + // uninhabited. + def.variants + .iter() + .filter(|v| { + !v.uninhabited_from(cx.tcx, substs, def.adt_kind()) .contains(cx.tcx, cx.module) - }) - .map(|v| Variant(v.def_id)) - .collect(); - - // If our scrutinee is *privately* an empty enum, we must treat it as though it had an - // "unknown" constructor (in that case, all other patterns obviously can't be variants) - // to avoid exposing its emptyness. See the `match_privately_empty` test for details. - // FIXME: currently the only way I know of something can be a privately-empty enum is - // when the exhaustive_patterns feature flag is not present, so this is only needed for - // that case. - let is_privately_empty = ctors.is_empty() && !cx.is_uninhabited(pcx.ty); - // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an - // additionnal "unknown" constructor. - let is_declared_nonexhaustive = - def.is_variant_list_non_exhaustive() && !cx.is_local(pcx.ty); - - if is_privately_empty || is_declared_nonexhaustive { - // There is no point in enumerating all possible variants, because the user can't - // actually match against them themselves. So we return only the fictitious - // constructor. - // E.g., in an example like: - // ``` - // let err: io::ErrorKind = ...; - // match err { - // io::ErrorKind::NotFound => {}, - // } - // ``` - // we don't want to show every possible IO error, but instead have only `_` as the - // witness. - vec![NonExhaustive] + }) + .map(|v| Variant(v.def_id)) + .collect() } else { - ctors - } + def.variants.iter().map(|v| Variant(v.def_id)).collect() + }; + + // If the enum is declared as `#[non_exhaustive]`, we treat it as if it had an + // additional "unknown" constructor. + // There is no point in enumerating all possible variants, because the user can't + // actually match against them all themselves. So we always return only the fictitious + // constructor. + // E.g., in an example like: + // ``` + // let err: io::ErrorKind = ...; + // match err { + // io::ErrorKind::NotFound => {}, + // } + // ``` + // we don't want to show every possible IO error, but instead have only `_` as the + // witness. + let is_declared_nonexhaustive = cx.is_foreign_non_exhaustive_enum(pcx.ty); + + // If `exhaustive_patterns` is disabled and our scrutinee is an empty enum, we treat it + // as though it had an "unknown" constructor to avoid exposing its emptyness. Note that + // an empty match will still be considered exhaustive because that case is handled + // separately in `check_match`. + let is_secretly_empty = + def.variants.is_empty() && !cx.tcx.features().exhaustive_patterns; + + if is_secretly_empty || is_declared_nonexhaustive { vec![NonExhaustive] } else { ctors } } ty::Char => { vec![ @@ -1605,6 +1620,7 @@ pub fn is_useful<'p, 'tcx>( v: &PatStack<'p, 'tcx>, witness_preference: WitnessPreference, hir_id: HirId, + is_top_level: bool, ) -> Usefulness<'tcx, 'p> { let &Matrix(ref rows) = matrix; debug!("is_useful({:#?}, {:#?})", matrix, v); @@ -1632,7 +1648,7 @@ pub fn is_useful<'p, 'tcx>( let mut unreachable_pats = Vec::new(); let mut any_is_useful = false; for v in vs { - let res = is_useful(cx, &matrix, &v, witness_preference, hir_id); + let res = is_useful(cx, &matrix, &v, witness_preference, hir_id, false); match res { Useful(pats) => { any_is_useful = true; @@ -1732,7 +1748,7 @@ pub fn is_useful<'p, 'tcx>( } else { let matrix = matrix.specialize_wildcard(); let v = v.to_tail(); - let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id); + let usefulness = is_useful(cx, &matrix, &v, witness_preference, hir_id, false); // In this case, there's at least one "free" // constructor that is only matched against by @@ -1761,7 +1777,10 @@ pub fn is_useful<'p, 'tcx>( // `(, , true)` - we are // satisfied with `(_, _, true)`. In this case, // `used_ctors` is empty. - if missing_ctors.all_ctors_are_missing() { + // The exception is: if we are at the top-level, for example in an empty match, we + // sometimes prefer reporting the list of constructors instead of just `_`. + let report_ctors_rather_than_wildcard = is_top_level && !IntRange::is_integral(pcx.ty); + if missing_ctors.all_ctors_are_missing() && !report_ctors_rather_than_wildcard { // All constructors are unused. Add a wild pattern // rather than each individual constructor. usefulness.apply_wildcard(pcx.ty) @@ -1793,7 +1812,7 @@ fn is_useful_specialized<'p, 'tcx>( cx.pattern_arena.alloc_from_iter(ctor.wildcard_subpatterns(cx, lty)); let matrix = matrix.specialize_constructor(cx, &ctor, ctor_wild_subpatterns); v.specialize_constructor(cx, &ctor, ctor_wild_subpatterns) - .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id)) + .map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, false)) .map(|u| u.apply_constructor(cx, &ctor, lty)) .unwrap_or(NotUseful) } @@ -2308,7 +2327,7 @@ fn specialize_one_pattern<'p, 'tcx>( PatKind::Variant { adt_def, variant_index, ref subpatterns, .. } => { let ref variant = adt_def.variants[variant_index]; - let is_non_exhaustive = variant.is_field_list_non_exhaustive() && !cx.is_local(pat.ty); + let is_non_exhaustive = cx.is_foreign_non_exhaustive_variant(pat.ty, variant); Some(Variant(variant.def_id)) .filter(|variant_constructor| variant_constructor == constructor) .map(|_| { diff --git a/src/librustc_mir/hair/pattern/check_match.rs b/src/librustc_mir/hair/pattern/check_match.rs index 737af3e1358f4..8156cfe7ab51d 100644 --- a/src/librustc_mir/hair/pattern/check_match.rs +++ b/src/librustc_mir/hair/pattern/check_match.rs @@ -148,8 +148,8 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { self.tables, ); patcx.include_lint_checks(); - let pattern: &_ = - cx.pattern_arena.alloc(expand_pattern(cx, patcx.lower_pattern(&arm.pat))); + let pattern = patcx.lower_pattern(&arm.pat); + let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern)); if !patcx.errors.is_empty() { patcx.report_inlining_errors(arm.pat.span); have_errors = true; @@ -166,73 +166,12 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { // Fourth, check for unreachable arms. let matrix = check_arms(cx, &inlined_arms, source); - // Then, if the match has no arms, check whether the scrutinee - // is uninhabited. - let pat_ty = self.tables.node_type(scrut.hir_id); - let module = self.tcx.hir().get_module_parent(scrut.hir_id); - let mut def_span = None; - let mut missing_variants = vec![]; - if inlined_arms.is_empty() { - let scrutinee_is_uninhabited = if self.tcx.features().exhaustive_patterns { - self.tcx.is_ty_uninhabited_from(module, pat_ty) - } else { - match pat_ty.kind { - ty::Never => true, - ty::Adt(def, _) => { - def_span = self.tcx.hir().span_if_local(def.did); - if def.variants.len() < 4 && !def.variants.is_empty() { - // keep around to point at the definition of non-covered variants - missing_variants = - def.variants.iter().map(|variant| variant.ident).collect(); - } - - let is_non_exhaustive_and_non_local = - def.is_variant_list_non_exhaustive() && !def.did.is_local(); - - !(is_non_exhaustive_and_non_local) && def.variants.is_empty() - } - _ => false, - } - }; - if !scrutinee_is_uninhabited { - // We know the type is inhabited, so this must be wrong - let mut err = create_e0004( - self.tcx.sess, - scrut.span, - format!( - "non-exhaustive patterns: {}", - match missing_variants.len() { - 0 => format!("type `{}` is non-empty", pat_ty), - 1 => format!( - "pattern `{}` of type `{}` is not handled", - missing_variants[0].name, pat_ty, - ), - _ => format!( - "multiple patterns of type `{}` are not handled", - pat_ty - ), - } - ), - ); - err.help( - "ensure that all possible cases are being handled, \ - possibly by adding wildcards or more match arms", - ); - if let Some(sp) = def_span { - err.span_label(sp, format!("`{}` defined here", pat_ty)); - } - // point at the definition of non-covered enum variants - for variant in &missing_variants { - err.span_label(variant.span, "variant not covered"); - } - err.emit(); - } - // If the type *is* uninhabited, it's vacuously exhaustive - return; - } - + // Fifth, check if the match is exhaustive. let scrut_ty = self.tables.node_type(scrut.hir_id); - check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id); + // Note: An empty match isn't the same as an empty matrix for diagnostics purposes, + // since an empty matrix can occur when there are arms, if those arms all have guards. + let is_empty_match = inlined_arms.is_empty(); + check_exhaustive(cx, scrut_ty, scrut.span, &matrix, scrut.hir_id, is_empty_match); }) } @@ -390,7 +329,7 @@ fn check_arms<'p, 'tcx>( for (arm_index, (pat, hir_pat, has_guard)) in arms.iter().enumerate() { let v = PatStack::from_pattern(pat); - match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id) { + match is_useful(cx, &seen, &v, LeaveOutWitness, hir_pat.hir_id, true) { NotUseful => { match source { hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar => bug!(), @@ -478,7 +417,8 @@ fn check_not_useful<'p, 'tcx>( hir_id: HirId, ) -> Result<(), Vec>> { let wild_pattern = cx.pattern_arena.alloc(super::Pat::wildcard_from_ty(ty)); - match is_useful(cx, matrix, &PatStack::from_pattern(wild_pattern), ConstructWitness, hir_id) { + let v = PatStack::from_pattern(wild_pattern); + match is_useful(cx, matrix, &v, ConstructWitness, hir_id, true) { NotUseful => Ok(()), // This is good, wildcard pattern isn't reachable. UsefulWithWitness(pats) => Err(if pats.is_empty() { bug!("Exhaustiveness check returned no witnesses") @@ -495,25 +435,60 @@ fn check_exhaustive<'p, 'tcx>( sp: Span, matrix: &Matrix<'p, 'tcx>, hir_id: HirId, + is_empty_match: bool, ) { + // In the absence of the `exhaustive_patterns` feature, empty matches are not detected by + // `is_useful` to exhaustively match uninhabited types, so we manually check here. + if is_empty_match && !cx.tcx.features().exhaustive_patterns { + let scrutinee_is_visibly_uninhabited = match scrut_ty.kind { + ty::Never => true, + ty::Adt(def, _) => { + def.is_enum() + && def.variants.is_empty() + && !cx.is_foreign_non_exhaustive_enum(scrut_ty) + } + _ => false, + }; + if scrutinee_is_visibly_uninhabited { + // If the type *is* uninhabited, an empty match is vacuously exhaustive. + return; + } + } + let witnesses = match check_not_useful(cx, scrut_ty, matrix, hir_id) { Ok(_) => return, Err(err) => err, }; - let joined_patterns = joined_uncovered_patterns(&witnesses); - let mut err = create_e0004( - cx.tcx.sess, - sp, - format!("non-exhaustive patterns: {} not covered", joined_patterns), - ); - err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); + let non_empty_enum = match scrut_ty.kind { + ty::Adt(def, _) => def.is_enum() && !def.variants.is_empty(), + _ => false, + }; + // In the case of an empty match, replace the '`_` not covered' diagnostic with something more + // informative. + let mut err; + if is_empty_match && !non_empty_enum { + err = create_e0004( + cx.tcx.sess, + sp, + format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty), + ); + } else { + let joined_patterns = joined_uncovered_patterns(&witnesses); + err = create_e0004( + cx.tcx.sess, + sp, + format!("non-exhaustive patterns: {} not covered", joined_patterns), + ); + err.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns)); + }; + adt_defined_here(cx, &mut err, scrut_ty, &witnesses); err.help( "ensure that all possible cases are being handled, \ possibly by adding wildcards or more match arms", - ) - .emit(); + ); + err.emit(); } fn joined_uncovered_patterns(witnesses: &[super::Pat<'_>]) -> String { diff --git a/src/librustc_mir/interpret/traits.rs b/src/librustc_mir/interpret/traits.rs index efa0d266cbc21..916ea3dc393e9 100644 --- a/src/librustc_mir/interpret/traits.rs +++ b/src/librustc_mir/interpret/traits.rs @@ -140,7 +140,18 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let fn_sig = drop_instance.ty(*self.tcx).fn_sig(*self.tcx); let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, &fn_sig); // The drop function takes `*mut T` where `T` is the type being dropped, so get that. - let ty = fn_sig.inputs()[0].builtin_deref(true).unwrap().ty; + let args = fn_sig.inputs(); + if args.len() != 1 { + throw_ub_format!( + "drop fn should have 1 argument, but signature is {:?}", fn_sig + ); + } + let ty = args[0].builtin_deref(true) + .ok_or_else(|| err_ub_format!( + "drop fn argument type {} is not a pointer type", + args[0] + ))? + .ty; Ok((drop_instance, ty)) } diff --git a/src/librustc_mir/interpret/validity.rs b/src/librustc_mir/interpret/validity.rs index e358df2f213ba..1dbcfe5588e48 100644 --- a/src/librustc_mir/interpret/validity.rs +++ b/src/librustc_mir/interpret/validity.rs @@ -22,28 +22,23 @@ use super::{ macro_rules! throw_validation_failure { ($what:expr, $where:expr, $details:expr) => {{ - let where_ = path_format(&$where); - let where_ = if where_.is_empty() { - String::new() - } else { - format!(" at {}", where_) - }; - throw_unsup!(ValidationFailure(format!( - "encountered {}{}, but expected {}", - $what, where_, $details, - ))) + let mut msg = format!("encountered {}", $what); + let where_ = &$where; + if !where_.is_empty() { + msg.push_str(" at "); + write_path(&mut msg, where_); + } + write!(&mut msg, ", but expected {}", $details).unwrap(); + throw_unsup!(ValidationFailure(msg)) }}; ($what:expr, $where:expr) => {{ - let where_ = path_format(&$where); - let where_ = if where_.is_empty() { - String::new() - } else { - format!(" at {}", where_) - }; - throw_unsup!(ValidationFailure(format!( - "encountered {}{}", - $what, where_, - ))) + let mut msg = format!("encountered {}", $what); + let where_ = &$where; + if !where_.is_empty() { + msg.push_str(" at "); + write_path(&mut msg, where_); + } + throw_unsup!(ValidationFailure(msg)) }}; } @@ -60,7 +55,7 @@ macro_rules! try_validation { Ok(x) => x, Err(_) => throw_validation_failure!($what, $where), } - }} + }}; } /// We want to show a nice path to the invalid field for diagnostics, @@ -113,10 +108,9 @@ impl RefTracking } /// Format a path -fn path_format(path: &Vec) -> String { +fn write_path(out: &mut String, path: &Vec) { use self::PathElem::*; - let mut out = String::new(); for elem in path.iter() { match elem { Field(name) => write!(out, ".{}", name), @@ -135,7 +129,6 @@ fn path_format(path: &Vec) -> String { DynDowncast => write!(out, "."), }.unwrap() } - out } // Test if a range that wraps at overflow contains `test` @@ -428,7 +421,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> err_unsup!(InvalidNullPointerUsage) => throw_validation_failure!("NULL reference", self.path), err_unsup!(AlignmentCheckFailed { required, has }) => - throw_validation_failure!(format!("unaligned reference \ + throw_validation_failure!(format_args!("unaligned reference \ (required {} byte alignment but found {})", required.bytes(), has.bytes()), self.path), err_unsup!(ReadBytesAsPointer) => @@ -519,7 +512,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> let value = try_validation!(value.not_undef(), value, self.path, - format!( + format_args!( "something {}", wrapping_range_format(&layout.valid_range, max_hi), ) @@ -532,7 +525,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> throw_validation_failure!( "a potentially NULL pointer", self.path, - format!( + format_args!( "something that cannot possibly fail to be {}", wrapping_range_format(&layout.valid_range, max_hi) ) @@ -545,7 +538,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> throw_validation_failure!( "a pointer", self.path, - format!( + format_args!( "something that cannot possibly fail to be {}", wrapping_range_format(&layout.valid_range, max_hi) ) @@ -562,7 +555,7 @@ impl<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M> throw_validation_failure!( bits, self.path, - format!("something {}", wrapping_range_format(&layout.valid_range, max_hi)) + format_args!("something {}", wrapping_range_format(&layout.valid_range, max_hi)) ) } } diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 253fc5575c5bc..726b3ba985778 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1289,8 +1289,20 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { } // Error possibly reported in `check_assign` so avoid emitting error again. - err.emit_unless(expression.filter(|e| fcx.is_assign_to_bool(e, expected)) - .is_some()); + let assign_to_bool = expression + // #67273: Use initial expected type as opposed to `expected`. + // Otherwise we end up using prior coercions in e.g. a `match` expression: + // ``` + // match i { + // 0 => true, // Because of this... + // 1 => i = 1, // ...`expected == bool` now, but not when checking `i = 1`. + // _ => (), + // }; + // ``` + .filter(|e| fcx.is_assign_to_bool(e, self.expected_ty())) + .is_some(); + + err.emit_unless(assign_to_bool); self.final_ty = Some(fcx.tcx.types.err); } diff --git a/src/test/ui/error-codes/E0004-2.stderr b/src/test/ui/error-codes/E0004-2.stderr index db0a2b5a08593..f5b41cd1cc0bb 100644 --- a/src/test/ui/error-codes/E0004-2.stderr +++ b/src/test/ui/error-codes/E0004-2.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: multiple patterns of type `std::option::Option` are not handled +error[E0004]: non-exhaustive patterns: `None` and `Some(_)` not covered --> $DIR/E0004-2.rs:4:11 | LL | match x { } - | ^ + | ^ patterns `None` and `Some(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/issues/issue-3601.stderr b/src/test/ui/issues/issue-3601.stderr index fa0fa3345f5ee..445eb4107d1df 100644 --- a/src/test/ui/issues/issue-3601.stderr +++ b/src/test/ui/issues/issue-3601.stderr @@ -1,8 +1,8 @@ -error[E0004]: non-exhaustive patterns: `_` not covered +error[E0004]: non-exhaustive patterns: `Box(_)` not covered --> $DIR/issue-3601.rs:30:44 | LL | box NodeKind::Element(ed) => match ed.kind { - | ^^^^^^^ pattern `_` not covered + | ^^^^^^^ pattern `Box(_)` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr index 792ab6f59a439..1b1096c977ad4 100644 --- a/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr +++ b/src/test/ui/pattern/usefulness/always-inhabited-union-ref.stderr @@ -9,8 +9,13 @@ LL | match uninhab_ref() { error[E0004]: non-exhaustive patterns: type `Foo` is non-empty --> $DIR/always-inhabited-union-ref.rs:27:11 | -LL | match uninhab_union() { - | ^^^^^^^^^^^^^^^ +LL | / pub union Foo { +LL | | foo: !, +LL | | } + | |_- `Foo` defined here +... +LL | match uninhab_union() { + | ^^^^^^^^^^^^^^^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs new file mode 100644 index 0000000000000..57b6b910ca1da --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.rs @@ -0,0 +1,93 @@ +#![feature(never_type)] +#![feature(exhaustive_patterns)] +#![deny(unreachable_patterns)] +enum Foo {} + +struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here + foo: (), +} +union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here + foo: (), + bar: (), +} +enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here + Foo(bool), + //~^ not covered + //~| not covered +} +enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here + Foo(bool), + //~^ not covered + //~| not covered + Bar, + //~^ not covered + //~| not covered +} +enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here + V1, V2, V3, V4, V5, +} + +macro_rules! match_empty { + ($e:expr) => { + match $e {} + }; +} +macro_rules! match_false { + ($e:expr) => { + match $e { + _ if false => {} + } + }; +} + +fn foo(x: Foo) { + match_empty!(x); // ok + match x { + _ => {}, //~ ERROR unreachable pattern + } + match x { + _ if false => {}, //~ ERROR unreachable pattern + } +} + +fn main() { + match None:: { + None => {} + Some(_) => {} //~ ERROR unreachable pattern + } + match None:: { + None => {} + Some(_) => {} //~ ERROR unreachable pattern + } + + match_empty!(0u8); + //~^ ERROR type `u8` is non-empty + match_empty!(NonEmptyStruct(true)); + //~^ ERROR type `NonEmptyStruct` is non-empty + match_empty!((NonEmptyUnion1 { foo: () })); + //~^ ERROR type `NonEmptyUnion1` is non-empty + match_empty!((NonEmptyUnion2 { foo: () })); + //~^ ERROR type `NonEmptyUnion2` is non-empty + match_empty!(NonEmptyEnum1::Foo(true)); + //~^ ERROR `Foo(_)` not covered + match_empty!(NonEmptyEnum2::Foo(true)); + //~^ ERROR `Foo(_)` and `Bar` not covered + match_empty!(NonEmptyEnum5::V1); + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered + + match_false!(0u8); + //~^ ERROR `_` not covered + match_false!(NonEmptyStruct(true)); + //~^ ERROR `NonEmptyStruct(_)` not covered + match_false!((NonEmptyUnion1 { foo: () })); + //~^ ERROR `NonEmptyUnion1 { .. }` not covered + match_false!((NonEmptyUnion2 { foo: () })); + //~^ ERROR `NonEmptyUnion2 { .. }` not covered + match_false!(NonEmptyEnum1::Foo(true)); + //~^ ERROR `Foo(_)` not covered + match_false!(NonEmptyEnum2::Foo(true)); + //~^ ERROR `Foo(_)` and `Bar` not covered + match_false!(NonEmptyEnum5::V1); + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered +} diff --git a/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr new file mode 100644 index 0000000000000..f242ecf2dae4e --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty-exhaustive_patterns.stderr @@ -0,0 +1,223 @@ +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:47:9 + | +LL | _ => {}, + | ^ + | +note: lint level defined here + --> $DIR/match-empty-exhaustive_patterns.rs:3:9 + | +LL | #![deny(unreachable_patterns)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:50:9 + | +LL | _ if false => {}, + | ^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:57:9 + | +LL | Some(_) => {} + | ^^^^^^^ + +error: unreachable pattern + --> $DIR/match-empty-exhaustive_patterns.rs:61:9 + | +LL | Some(_) => {} + | ^^^^^^^ + +error[E0004]: non-exhaustive patterns: type `u8` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:64:18 + | +LL | match_empty!(0u8); + | ^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:66:18 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... +LL | match_empty!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:68:18 + | +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_empty!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty + --> $DIR/match-empty-exhaustive_patterns.rs:70:18 + | +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_empty!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:72:18 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match_empty!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:74:18 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | Bar, + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match_empty!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered + --> $DIR/match-empty-exhaustive_patterns.rs:76:18 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match_empty!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:79:18 + | +LL | match_false!(0u8); + | ^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:81:18 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... +LL | match_false!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:83:18 + | +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_false!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:85:18 + | +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_false!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:87:18 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match_false!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered + --> $DIR/match-empty-exhaustive_patterns.rs:89:18 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | Bar, + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match_false!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered + --> $DIR/match-empty-exhaustive_patterns.rs:91:18 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match_false!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 18 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/pattern/usefulness/match-empty.rs b/src/test/ui/pattern/usefulness/match-empty.rs new file mode 100644 index 0000000000000..f7577125d8a3f --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty.rs @@ -0,0 +1,92 @@ +#![feature(never_type)] +#![deny(unreachable_patterns)] +enum Foo {} + +struct NonEmptyStruct(bool); //~ `NonEmptyStruct` defined here +union NonEmptyUnion1 { //~ `NonEmptyUnion1` defined here + foo: (), +} +union NonEmptyUnion2 { //~ `NonEmptyUnion2` defined here + foo: (), + bar: (), +} +enum NonEmptyEnum1 { //~ `NonEmptyEnum1` defined here + Foo(bool), + //~^ not covered + //~| not covered +} +enum NonEmptyEnum2 { //~ `NonEmptyEnum2` defined here + Foo(bool), + //~^ not covered + //~| not covered + Bar, + //~^ not covered + //~| not covered +} +enum NonEmptyEnum5 { //~ `NonEmptyEnum5` defined here + V1, V2, V3, V4, V5, +} + +macro_rules! match_empty { + ($e:expr) => { + match $e {} + }; +} +macro_rules! match_false { + ($e:expr) => { + match $e { + _ if false => {} + } + }; +} + +fn foo(x: Foo) { + match_empty!(x); // ok + match_false!(x); // Not detected as unreachable nor exhaustive. + //~^ ERROR non-exhaustive patterns: `_` not covered + match x { + _ => {}, // Not detected as unreachable, see #55123. + } +} + +fn main() { + // `exhaustive_patterns` is not on, so uninhabited branches are not detected as unreachable. + match None:: { + None => {} + Some(_) => {} + } + match None:: { + None => {} + Some(_) => {} + } + + match_empty!(0u8); + //~^ ERROR type `u8` is non-empty + match_empty!(NonEmptyStruct(true)); + //~^ ERROR type `NonEmptyStruct` is non-empty + match_empty!((NonEmptyUnion1 { foo: () })); + //~^ ERROR type `NonEmptyUnion1` is non-empty + match_empty!((NonEmptyUnion2 { foo: () })); + //~^ ERROR type `NonEmptyUnion2` is non-empty + match_empty!(NonEmptyEnum1::Foo(true)); + //~^ ERROR `Foo(_)` not covered + match_empty!(NonEmptyEnum2::Foo(true)); + //~^ ERROR `Foo(_)` and `Bar` not covered + match_empty!(NonEmptyEnum5::V1); + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered + + match_false!(0u8); + //~^ ERROR `_` not covered + match_false!(NonEmptyStruct(true)); + //~^ ERROR `NonEmptyStruct(_)` not covered + match_false!((NonEmptyUnion1 { foo: () })); + //~^ ERROR `NonEmptyUnion1 { .. }` not covered + match_false!((NonEmptyUnion2 { foo: () })); + //~^ ERROR `NonEmptyUnion2 { .. }` not covered + match_false!(NonEmptyEnum1::Foo(true)); + //~^ ERROR `Foo(_)` not covered + match_false!(NonEmptyEnum2::Foo(true)); + //~^ ERROR `Foo(_)` and `Bar` not covered + match_false!(NonEmptyEnum5::V1); + //~^ ERROR `V1`, `V2`, `V3` and 2 more not covered +} diff --git a/src/test/ui/pattern/usefulness/match-empty.stderr b/src/test/ui/pattern/usefulness/match-empty.stderr new file mode 100644 index 0000000000000..72e3fc0a16744 --- /dev/null +++ b/src/test/ui/pattern/usefulness/match-empty.stderr @@ -0,0 +1,204 @@ +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:45:18 + | +LL | enum Foo {} + | ----------- `Foo` defined here +... +LL | match_false!(x); // Not detected as unreachable nor exhaustive. + | ^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `u8` is non-empty + --> $DIR/match-empty.rs:63:18 + | +LL | match_empty!(0u8); + | ^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyStruct` is non-empty + --> $DIR/match-empty.rs:65:18 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... +LL | match_empty!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion1` is non-empty + --> $DIR/match-empty.rs:67:18 + | +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_empty!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: type `NonEmptyUnion2` is non-empty + --> $DIR/match-empty.rs:69:18 + | +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_empty!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered + --> $DIR/match-empty.rs:71:18 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match_empty!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered + --> $DIR/match-empty.rs:73:18 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | Bar, + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match_empty!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered + --> $DIR/match-empty.rs:75:18 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match_empty!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/match-empty.rs:78:18 + | +LL | match_false!(0u8); + | ^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `NonEmptyStruct(_)` not covered + --> $DIR/match-empty.rs:80:18 + | +LL | struct NonEmptyStruct(bool); + | ---------------------------- `NonEmptyStruct` defined here +... +LL | match_false!(NonEmptyStruct(true)); + | ^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyStruct(_)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `NonEmptyUnion1 { .. }` not covered + --> $DIR/match-empty.rs:82:18 + | +LL | / union NonEmptyUnion1 { +LL | | foo: (), +LL | | } + | |_- `NonEmptyUnion1` defined here +... +LL | match_false!((NonEmptyUnion1 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion1 { .. }` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `NonEmptyUnion2 { .. }` not covered + --> $DIR/match-empty.rs:84:18 + | +LL | / union NonEmptyUnion2 { +LL | | foo: (), +LL | | bar: (), +LL | | } + | |_- `NonEmptyUnion2` defined here +... +LL | match_false!((NonEmptyUnion2 { foo: () })); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `NonEmptyUnion2 { .. }` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` not covered + --> $DIR/match-empty.rs:86:18 + | +LL | / enum NonEmptyEnum1 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum1` defined here +... +LL | match_false!(NonEmptyEnum1::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo(_)` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Foo(_)` and `Bar` not covered + --> $DIR/match-empty.rs:88:18 + | +LL | / enum NonEmptyEnum2 { +LL | | Foo(bool), + | | --- not covered +LL | | +LL | | +LL | | Bar, + | | --- not covered +LL | | +LL | | +LL | | } + | |_- `NonEmptyEnum2` defined here +... +LL | match_false!(NonEmptyEnum2::Foo(true)); + | ^^^^^^^^^^^^^^^^^^^^^^^^ patterns `Foo(_)` and `Bar` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `V1`, `V2`, `V3` and 2 more not covered + --> $DIR/match-empty.rs:90:18 + | +LL | / enum NonEmptyEnum5 { +LL | | V1, V2, V3, V4, V5, +LL | | } + | |_- `NonEmptyEnum5` defined here +... +LL | match_false!(NonEmptyEnum5::V1); + | ^^^^^^^^^^^^^^^^^ patterns `V1`, `V2`, `V3` and 2 more not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 15 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs index bbc25d40256ff..8516bafef9bd9 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/auxiliary/enums.rs @@ -6,3 +6,6 @@ pub enum NonExhaustiveEnum { Tuple(u32), Struct { field: u32 } } + +#[non_exhaustive] +pub enum EmptyNonExhaustiveEnum {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.rs b/src/test/ui/rfc-2008-non-exhaustive/enum.rs index 7423a970e2e3b..802f20b4bed60 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.rs +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.rs @@ -1,7 +1,14 @@ // aux-build:enums.rs extern crate enums; -use enums::NonExhaustiveEnum; +use enums::{EmptyNonExhaustiveEnum, NonExhaustiveEnum}; + +fn empty(x: EmptyNonExhaustiveEnum) { + match x {} //~ ERROR type `enums::EmptyNonExhaustiveEnum` is non-empty + match x { + _ => {}, // ok + } +} fn main() { let enum_unit = NonExhaustiveEnum::Unit; @@ -13,6 +20,9 @@ fn main() { NonExhaustiveEnum::Struct { .. } => "third" }; + match enum_unit {}; + //~^ ERROR non-exhaustive patterns: `_` not covered [E0004] + // Everything below this is expected to compile successfully. let enum_unit = NonExhaustiveEnum::Unit; diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr index b5c1a4ebba4ac..a2bdcbaa4478d 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/enum.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/enum.stderr @@ -1,11 +1,27 @@ +error[E0004]: non-exhaustive patterns: type `enums::EmptyNonExhaustiveEnum` is non-empty + --> $DIR/enum.rs:7:11 + | +LL | match x {} + | ^ + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + error[E0004]: non-exhaustive patterns: `_` not covered - --> $DIR/enum.rs:9:11 + --> $DIR/enum.rs:16:11 | LL | match enum_unit { | ^^^^^^^^^ pattern `_` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error: aborting due to previous error +error[E0004]: non-exhaustive patterns: `_` not covered + --> $DIR/enum.rs:23:11 + | +LL | match enum_unit {}; + | ^^^^^^^^^ pattern `_` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs new file mode 100644 index 0000000000000..afd6d996c15a8 --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.rs @@ -0,0 +1,37 @@ +#![deny(unreachable_patterns)] + +#[non_exhaustive] +pub enum NonExhaustiveEnum { + Unit, + //~^ not covered + Tuple(u32), + //~^ not covered + Struct { field: u32 } + //~^ not covered +} + +pub enum NormalEnum { + Unit, + //~^ not covered + Tuple(u32), + //~^ not covered + Struct { field: u32 } + //~^ not covered +} + +#[non_exhaustive] +pub enum EmptyNonExhaustiveEnum {} + +fn empty_non_exhaustive(x: EmptyNonExhaustiveEnum) { + match x {} + match x { + _ => {} // not detected as unreachable + } +} + +fn main() { + match NonExhaustiveEnum::Unit {} + //~^ ERROR `Unit`, `Tuple(_)` and `Struct { .. }` not covered [E0004] + match NormalEnum::Unit {} + //~^ ERROR `Unit`, `Tuple(_)` and `Struct { .. }` not covered [E0004] +} diff --git a/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr new file mode 100644 index 0000000000000..a99a690bc9e5a --- /dev/null +++ b/src/test/ui/rfc-2008-non-exhaustive/enum_same_crate_empty_match.stderr @@ -0,0 +1,45 @@ +error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered + --> $DIR/enum_same_crate_empty_match.rs:33:11 + | +LL | / pub enum NonExhaustiveEnum { +LL | | Unit, + | | ---- not covered +LL | | +LL | | Tuple(u32), + | | ----- not covered +LL | | +LL | | Struct { field: u32 } + | | ------ not covered +LL | | +LL | | } + | |_- `NonExhaustiveEnum` defined here +... +LL | match NonExhaustiveEnum::Unit {} + | ^^^^^^^^^^^^^^^^^^^^^^^ patterns `Unit`, `Tuple(_)` and `Struct { .. }` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error[E0004]: non-exhaustive patterns: `Unit`, `Tuple(_)` and `Struct { .. }` not covered + --> $DIR/enum_same_crate_empty_match.rs:35:11 + | +LL | / pub enum NormalEnum { +LL | | Unit, + | | ---- not covered +LL | | +LL | | Tuple(u32), + | | ----- not covered +LL | | +LL | | Struct { field: u32 } + | | ------ not covered +LL | | +LL | | } + | |_- `NormalEnum` defined here +... +LL | match NormalEnum::Unit {} + | ^^^^^^^^^^^^^^^^ patterns `Unit`, `Tuple(_)` and `Struct { .. }` not covered + | + = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0004`. diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr index b903e9b288ef7..0d669a9a4e9d9 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match.stderr @@ -1,4 +1,4 @@ -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `uninhabited::IndirectUninhabitedEnum` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match.rs:18:11 | LL | match x {} @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `uninhabited::IndirectUninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match.rs:22:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `uninhabited::IndirectUninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match.rs:26:11 | LL | match x {} @@ -22,7 +22,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `uninhabited::IndirectUninhabitedVariants` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match.rs:32:11 | LL | match x {} diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr index f94616dc64b1b..41a37cf6a036f 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/indirect_match_same_crate.stderr @@ -1,53 +1,41 @@ -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedEnum` of type `IndirectUninhabitedEnum` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedEnum` is non-empty --> $DIR/indirect_match_same_crate.rs:32:11 | LL | pub struct IndirectUninhabitedEnum(UninhabitedEnum); - | ---------------------------------------------------- - | | | - | | variant not covered - | `IndirectUninhabitedEnum` defined here + | ---------------------------------------------------- `IndirectUninhabitedEnum` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedStruct` of type `IndirectUninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedStruct` is non-empty --> $DIR/indirect_match_same_crate.rs:36:11 | LL | pub struct IndirectUninhabitedStruct(UninhabitedStruct); - | -------------------------------------------------------- - | | | - | | variant not covered - | `IndirectUninhabitedStruct` defined here + | -------------------------------------------------------- `IndirectUninhabitedStruct` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedTupleStruct` of type `IndirectUninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedTupleStruct` is non-empty --> $DIR/indirect_match_same_crate.rs:40:11 | LL | pub struct IndirectUninhabitedTupleStruct(UninhabitedTupleStruct); - | ------------------------------------------------------------------ - | | | - | | variant not covered - | `IndirectUninhabitedTupleStruct` defined here + | ------------------------------------------------------------------ `IndirectUninhabitedTupleStruct` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `IndirectUninhabitedVariants` of type `IndirectUninhabitedVariants` is not handled +error[E0004]: non-exhaustive patterns: type `IndirectUninhabitedVariants` is non-empty --> $DIR/indirect_match_same_crate.rs:46:11 | LL | pub struct IndirectUninhabitedVariants(UninhabitedVariants); - | ------------------------------------------------------------ - | | | - | | variant not covered - | `IndirectUninhabitedVariants` defined here + | ------------------------------------------------------------ `IndirectUninhabitedVariants` defined here ... LL | match x {} | ^ diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr index de3fa900cd622..10a456a5a879a 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match.stderr @@ -6,7 +6,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `uninhabited::UninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedStruct` is non-empty --> $DIR/match.rs:22:11 | LL | match x {} @@ -14,7 +14,7 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `uninhabited::UninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedTupleStruct` is non-empty --> $DIR/match.rs:26:11 | LL | match x {} @@ -22,11 +22,11 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `uninhabited::UninhabitedVariants` are not handled +error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match.rs:30:11 | LL | match x {} - | ^ + | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr index 3dd1a914d5542..148af8c07b1c6 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_same_crate.stderr @@ -1,10 +1,7 @@ -error[E0004]: non-exhaustive patterns: pattern `UninhabitedStruct` of type `UninhabitedStruct` is not handled +error[E0004]: non-exhaustive patterns: type `UninhabitedStruct` is non-empty --> $DIR/match_same_crate.rs:28:11 | -LL | pub struct UninhabitedStruct { - | - ----------------- variant not covered - | _| - | | +LL | / pub struct UninhabitedStruct { LL | | _priv: !, LL | | } | |_- `UninhabitedStruct` defined here @@ -14,33 +11,30 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: pattern `UninhabitedTupleStruct` of type `UninhabitedTupleStruct` is not handled +error[E0004]: non-exhaustive patterns: type `UninhabitedTupleStruct` is non-empty --> $DIR/match_same_crate.rs:32:11 | LL | pub struct UninhabitedTupleStruct(!); - | ------------------------------------- - | | | - | | variant not covered - | `UninhabitedTupleStruct` defined here + | ------------------------------------- `UninhabitedTupleStruct` defined here ... LL | match x {} | ^ | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: multiple patterns of type `UninhabitedVariants` are not handled +error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_same_crate.rs:36:11 | LL | / pub enum UninhabitedVariants { LL | | #[non_exhaustive] Tuple(!), - | | ----- variant not covered + | | ----- not covered LL | | #[non_exhaustive] Struct { x: ! } - | | ------ variant not covered + | | ------ not covered LL | | } | |_- `UninhabitedVariants` defined here ... LL | match x {} - | ^ + | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr index 3b56c6890710d..2fc09c81ab031 100644 --- a/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr +++ b/src/test/ui/rfc-2008-non-exhaustive/uninhabited/match_with_exhaustive_patterns.stderr @@ -22,11 +22,11 @@ LL | match x {} | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms -error[E0004]: non-exhaustive patterns: type `uninhabited::UninhabitedVariants` is non-empty +error[E0004]: non-exhaustive patterns: `Tuple(_)` and `Struct { .. }` not covered --> $DIR/match_with_exhaustive_patterns.rs:33:11 | LL | match x {} - | ^ + | ^ patterns `Tuple(_)` and `Struct { .. }` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms diff --git a/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs new file mode 100644 index 0000000000000..e23c0d0a40a7e --- /dev/null +++ b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs @@ -0,0 +1,27 @@ +fn main() { + let mut i: i64; + // Expected type is an inference variable `?T` + // because the `match` is used as a statement. + // This is the "initial" type of the `coercion`. + match i { + // Add `bool` to the overall `coercion`. + 0 => true, + + // Necessary to cause the ICE: + 1 => true, + + // Suppose that we had `let _: bool = match i { ... }`. + // In that case, as the expected type would be `bool`, + // we would suggest `i == 1` as a fix. + // + // However, no type error happens when checking `i = 1` because `expected == ?T`, + // which will unify with `typeof(i = 1) == ()`. + // + // However, in #67273, we would delay the unification of this arm with the above + // because we used the hitherto accumulated coercion as opposed to the "initial" type. + 2 => i = 1, + //~^ ERROR match arms have incompatible types + + _ => (), + } +} diff --git a/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr new file mode 100644 index 0000000000000..3547285542a9f --- /dev/null +++ b/src/test/ui/type/type-check/issue-67273-assignment-match-prior-arm-bool-expected-unit.stderr @@ -0,0 +1,22 @@ +error[E0308]: match arms have incompatible types + --> $DIR/issue-67273-assignment-match-prior-arm-bool-expected-unit.rs:22:14 + | +LL | / match i { +LL | | // Add `bool` to the overall `coercion`. +LL | | 0 => true, + | | ---- this is found to be of type `bool` +LL | | +LL | | // Necessary to cause the ICE: +LL | | 1 => true, + | | ---- this is found to be of type `bool` +... | +LL | | 2 => i = 1, + | | ^^^^^ expected `bool`, found `()` +... | +LL | | _ => (), +LL | | } + | |_____- `match` arms have incompatible types + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index 7af6075262c6d..18ffdccb9b2db 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -9,6 +9,9 @@ LL | let _ = match x { error[E0004]: non-exhaustive patterns: type `&Void` is non-empty --> $DIR/uninhabited-matches-feature-gated.rs:12:19 | +LL | enum Void {} + | ------------ `Void` defined here +... LL | let _ = match x {}; | ^ |