about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNadrieril <nadrieril+git@gmail.com>2020-05-09 13:46:05 +0100
committerNadrieril <nadrieril+git@gmail.com>2020-05-17 17:38:23 +0100
commite5a2cd526a6ad92b90dda81104abc7adf4c83495 (patch)
treea2db8e413c45f97ac473d18d3998584274810cf3
parent8f08b16c030d89049e0633ced8665c317db83f03 (diff)
downloadrust-e5a2cd526a6ad92b90dda81104abc7adf4c83495.tar.gz
rust-e5a2cd526a6ad92b90dda81104abc7adf4c83495.zip
We don't use tyerr anymore
This however unearthed a bug, hence the FIXME and the workaround.
-rw-r--r--src/librustc_mir_build/hair/pattern/_match.rs37
-rw-r--r--src/librustc_mir_build/hair/pattern/check_match.rs22
-rw-r--r--src/test/ui/pattern/usefulness/issue-71930-type-of-match-scrutinee.rs22
3 files changed, 46 insertions, 35 deletions
diff --git a/src/librustc_mir_build/hair/pattern/_match.rs b/src/librustc_mir_build/hair/pattern/_match.rs
index c2dbe303be9..f94da2f3805 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 65ff311d182..b805e858aac 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 00000000000..e2ff9ac87ef
--- /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() {}