Make is_useful handle empty types properly

This commit is contained in:
Andrew Cann 2016-11-29 15:10:26 +08:00
parent 9ad20442e8
commit bcdbe942e1
24 changed files with 246 additions and 85 deletions

View file

@ -17,7 +17,7 @@ use eval::{compare_const_vals};
use rustc_const_math::ConstInt;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::indexed_vec::Idx;
use pattern::{FieldPattern, Pattern, PatternKind};
@ -29,6 +29,7 @@ use rustc::ty::{self, Ty, TyCtxt, TypeFoldable};
use rustc::mir::Field;
use rustc::util::common::ErrorReported;
use syntax::ast::NodeId;
use syntax_pos::{Span, DUMMY_SP};
use arena::TypedArena;
@ -144,6 +145,14 @@ impl<'a, 'tcx> FromIterator<Vec<&'a Pattern<'tcx>>> for Matrix<'a, 'tcx> {
//NOTE: appears to be the only place other then InferCtxt to contain a ParamEnv
pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
/// (roughly) where in the code the match occurs. This is necessary for
/// checking inhabited-ness of types because whether a type is (visibly)
/// inhabited can depend on whether it was defined in the current module or
/// not. eg.
/// struct Foo { _private: ! }
/// can not be seen to be empty outside it's module and should not
/// be matchable with an empty match statement.
pub node: NodeId,
/// A wild pattern with an error type - it exists to avoid having to normalize
/// associated types to get field types.
pub wild_pattern: &'a Pattern<'tcx>,
@ -154,6 +163,7 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
pub fn create_and_enter<F, R>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
node: NodeId,
f: F) -> R
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
{
@ -167,6 +177,7 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
f(MatchCheckCtxt {
tcx: tcx,
node: node,
wild_pattern: &wild_pattern,
pattern_arena: &pattern_arena,
byte_array_map: FxHashMap(),
@ -362,9 +373,9 @@ impl<'tcx> Witness<'tcx> {
/// Therefore, if there is some pattern that is unmatched by `matrix`, it will
/// still be unmatched if the first constructor is replaced by any of the constructors
/// in the return value.
fn missing_constructors(cx: &mut MatchCheckCtxt,
matrix: &Matrix,
pcx: PatternContext) -> Vec<Constructor> {
fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
matrix: &Matrix,
pcx: PatternContext<'tcx>) -> Vec<Constructor> {
let used_constructors: Vec<Constructor> =
matrix.0.iter()
.flat_map(|row| pat_constructors(cx, row[0], pcx).unwrap_or(vec![]))
@ -384,16 +395,46 @@ fn missing_constructors(cx: &mut MatchCheckCtxt,
///
/// but is instead bounded by the maximum fixed length of slice patterns in
/// the column of patterns being analyzed.
fn all_constructors(_cx: &mut MatchCheckCtxt, pcx: PatternContext) -> Vec<Constructor> {
///
/// We make sure to omit constructors that are statically impossible. eg for
/// Option<!> we do not include Some(_) in the returned list of constructors.
fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
pcx: PatternContext<'tcx>) -> Vec<Constructor>
{
match pcx.ty.sty {
ty::TyBool =>
[true, false].iter().map(|b| ConstantValue(ConstVal::Bool(*b))).collect(),
ty::TySlice(_) =>
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect(),
ty::TyArray(_, length) => vec![Slice(length)],
ty::TyAdt(def, _) if def.is_enum() && def.variants.len() > 1 =>
def.variants.iter().map(|v| Variant(v.did)).collect(),
_ => vec![Single]
ty::TySlice(ref sub_ty) => {
if sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![Slice(0)]
} else {
(0..pcx.max_slice_length+1).map(|length| Slice(length)).collect()
}
}
ty::TyArray(ref sub_ty, length) => {
if length == 0 || !sub_ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![Slice(length)]
} else {
vec![]
}
}
ty::TyAdt(def, substs) if def.is_enum() && def.variants.len() != 1 => {
def.variants.iter().filter_map(|v| {
let mut visited = FxHashSet::default();
if v.is_uninhabited_recurse(&mut visited, Some(cx.node), cx.tcx, substs, false) {
None
} else {
Some(Variant(v.did))
}
}).collect()
}
_ => {
if pcx.ty.is_uninhabited(Some(cx.node), cx.tcx) {
vec![]
} else {
vec![Single]
}
}
}
}

View file

@ -25,6 +25,7 @@ use rustc::middle::mem_categorization::{cmt};
use rustc::session::Session;
use rustc::traits::Reveal;
use rustc::ty::{self, TyCtxt};
use rustc::lint;
use rustc_errors::DiagnosticBuilder;
use rustc::hir::def::*;
@ -150,7 +151,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
}
}
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
MatchCheckCtxt::create_and_enter(self.tcx, scrut.id, |ref mut cx| {
let mut have_errors = false;
let inlined_arms : Vec<(Vec<_>, _)> = arms.iter().map(|arm| (
@ -210,7 +211,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
"local binding"
};
MatchCheckCtxt::create_and_enter(self.tcx, |ref mut cx| {
MatchCheckCtxt::create_and_enter(self.tcx, pat.id, |ref mut cx| {
let mut patcx = PatternContext::new(self.tcx);
let pats : Matrix = vec![vec![
expand_pattern(cx, patcx.lower_pattern(pat))
@ -324,14 +325,19 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
},
hir::MatchSource::Normal => {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
// if we had a catchall pattern, hint at that
// if we had a catchall pattern, raise an error.
// Otherwise an unreachable pattern raises a warning.
if let Some(catchall) = catchall {
let mut err = struct_span_err!(cx.tcx.sess, pat.span, E0001,
"unreachable pattern");
err.span_label(pat.span, &"this is an unreachable pattern");
err.span_note(catchall, "this pattern matches any value");
err.emit();
} else {
cx.tcx.sess.add_lint(lint::builtin::UNREACHABLE_PATTERNS,
hir_pat.id, pat.span,
String::from("unreachable pattern"));
}
err.emit();
},
hir::MatchSource::TryDesugar => {

View file

@ -28,7 +28,7 @@ For example, the following `match` block has too many arms:
```compile_fail,E0001
match Some(0) {
Some(bar) => {/* ... */}
None => {/* ... */}
x => {/* ... */} // This handles the `None` case
_ => {/* ... */} // All possible cases have already been handled
}
```