diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs index c2dbe303be99f..f94da2f3805ab 100644 --- a/src/librustc_mir_build/hair/pattern/_match.rs +++ b/src/librustc_mir_build/hair/pattern/_match.rs @@ -242,7 +242,7 @@ use rustc_hir::{HirId, RangeEnd}; use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar}; use rustc_middle::mir::Field; use rustc_middle::ty::layout::IntegerExt; -use rustc_middle::ty::{self, Const, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Const, Ty, TyCtxt}; use rustc_session::lint; use rustc_span::{Span, DUMMY_SP}; use rustc_target::abi::{Integer, Size, VariantIdx}; @@ -1739,11 +1739,7 @@ impl<'tcx> fmt::Debug for MissingConstructors<'tcx> { /// to a set of such vectors `m` - this is defined as there being a set of /// inputs that will match `v` but not any of the sets in `m`. /// -/// All the patterns at each column of the `matrix ++ v` matrix must -/// have the same type, except that wildcard (PatKind::Wild) patterns -/// with type `TyErr` are also allowed, even if the "type of the column" -/// is not `TyErr`. That is used to represent private fields, as using their -/// real type would assert that they are inhabited. +/// All the patterns at each column of the `matrix ++ v` matrix must have the same type. /// /// This is used both for reachability checking (if a pattern isn't useful in /// relation to preceding patterns, it is not reachable) and exhaustiveness @@ -1807,34 +1803,7 @@ crate fn is_useful<'p, 'tcx>( return if any_is_useful { Useful(unreachable_pats) } else { NotUseful }; } - let (ty, span) = matrix - .heads() - .map(|r| (r.ty, r.span)) - .find(|(ty, _)| !ty.references_error()) - .unwrap_or((v.head().ty, v.head().span)); - let pcx = PatCtxt { - // TyErr is used to represent the type of wildcard patterns matching - // against inaccessible (private) fields of structs, so that we won't - // be able to observe whether the types of the struct's fields are - // inhabited. - // - // If the field is truly inaccessible, then all the patterns - // matching against it must be wildcard patterns, so its type - // does not matter. - // - // However, if we are matching against non-wildcard patterns, we - // need to know the real type of the field so we can specialize - // against it. This primarily occurs through constants - they - // can include contents for fields that are inaccessible at the - // location of the match. In that case, the field's type is - // inhabited - by the constant - so we can just use it. - // - // FIXME: this might lead to "unstable" behavior with macro hygiene - // introducing uninhabited patterns for inaccessible fields. We - // need to figure out how to model that. - ty, - span, - }; + let pcx = PatCtxt { ty: v.head().ty, span: v.head().span }; debug!("is_useful_expand_first_col: pcx={:#?}, expanding {:#?}", pcx, v.head()); diff --git a/src/librustc_mir_build/hair/pattern/check_match.rs b/src/librustc_mir_build/hair/pattern/check_match.rs index 65ff311d18215..b805e858aacd9 100644 --- a/src/librustc_mir_build/hair/pattern/check_match.rs +++ b/src/librustc_mir_build/hair/pattern/check_match.rs @@ -186,8 +186,28 @@ impl<'tcx> MatchVisitor<'_, 'tcx> { // Fourth, check for unreachable arms. let matrix = check_arms(&mut cx, &inlined_arms, source); - // Fifth, check if the match is exhaustive. + // FIXME: getting the type using `node_type` means that if `f` has output type `!`, we + // get `scrut_ty = !` instead of `bool` in the following: + // ``` + // fn from(never: !) -> usize { + // match never { + // true => 1, + // false => 0, + // } + // } + // ``` + // If we use `expr_ty_adjusted` instead, then the following breaks, because we get + // `scrut_ty = ()` instead of `!`. + // ``` + // fn from(never: !) -> usize { + // match never {} + // } + // ``` + // As a workaround, we retrieve the type from the match arms when possible. let scrut_ty = self.tables.node_type(scrut.hir_id); + let scrut_ty = inlined_arms.iter().map(|(p, _, _)| p.ty).next().unwrap_or(scrut_ty); + + // Fifth, check if the match is exhaustive. // 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(); diff --git a/src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs b/src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs new file mode 100644 index 0000000000000..e2ff9ac87ef51 --- /dev/null +++ b/src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs @@ -0,0 +1,22 @@ +// check-pass + +// In PR 71930, it was discovered that the code to retrieve the inferred type of a match scrutinee +// was incorrect. + +fn f() -> ! { + panic!() +} + +fn g() -> usize { + match f() { // Should infer type `bool` + false => 0, + true => 1, + } +} + +fn h() -> usize { + match f() { // Should infer type `!` + } +} + +fn main() {}