From bcdbe942e108e47ffa712fa44ad9b251c53c105b Mon Sep 17 00:00:00 2001 From: Andrew Cann Date: Tue, 29 Nov 2016 15:10:26 +0800 Subject: Make is_useful handle empty types properly --- src/librustc_const_eval/_match.rs | 63 ++++++++++++++++++++++++++++------ src/librustc_const_eval/check_match.rs | 20 +++++++---- src/librustc_const_eval/diagnostics.rs | 2 +- 3 files changed, 66 insertions(+), 19 deletions(-) (limited to 'src/librustc_const_eval') diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index e1894544657..b517a77255e 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -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>> 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( 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 { +fn missing_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, + matrix: &Matrix, + pcx: PatternContext<'tcx>) -> Vec { let used_constructors: Vec = 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 { +/// +/// 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 +{ 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] + } + } } } diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index 53e83815b46..d6187f40b7f 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -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 => { diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index b24cd261dd5..139443a1719 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -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 } ``` -- cgit 1.4.1-3-g733a5