stop using MatchCheckCtxt to hold the param-env for check_match
This commit is contained in:
parent
e313d8b290
commit
37418b850f
|
@ -155,7 +155,6 @@ 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>,
|
||||
pub param_env: ty::ParameterEnvironment<'tcx>,
|
||||
/// 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>,
|
||||
|
@ -165,7 +164,6 @@ pub struct MatchCheckCtxt<'a, 'tcx: 'a> {
|
|||
impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
|
||||
pub fn create_and_enter<F, R>(
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: ty::ParameterEnvironment<'tcx>,
|
||||
f: F) -> R
|
||||
where F: for<'b> FnOnce(MatchCheckCtxt<'b, 'tcx>) -> R
|
||||
{
|
||||
|
@ -179,7 +177,6 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> {
|
|||
|
||||
f(MatchCheckCtxt {
|
||||
tcx: tcx,
|
||||
param_env: param_env,
|
||||
wild_pattern: &wild_pattern,
|
||||
pattern_arena: &pattern_arena,
|
||||
})
|
||||
|
@ -675,18 +672,3 @@ fn specialize<'a, 'tcx>(
|
|||
head
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_refutable<'a, 'tcx, A, F>(
|
||||
cx: &MatchCheckCtxt<'a, 'tcx>,
|
||||
pat: &'a Pattern<'tcx>,
|
||||
refutable: F)
|
||||
-> Option<A> where
|
||||
F: FnOnce(&Witness) -> A,
|
||||
{
|
||||
let pats = Matrix(vec![vec![pat]]);
|
||||
match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) {
|
||||
UsefulWithWitness(pats) => Some(refutable(&pats[0])),
|
||||
NotUseful => None,
|
||||
Useful => bug!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use _match::{MatchCheckCtxt, Matrix, lower_pat, is_refutable, is_useful};
|
||||
use _match::{MatchCheckCtxt, Matrix, lower_pat, is_useful};
|
||||
use _match::{DUMMY_WILD_PAT};
|
||||
use _match::Usefulness::*;
|
||||
use _match::WitnessPreference::*;
|
||||
|
@ -44,23 +44,47 @@ use syntax::ptr::P;
|
|||
use syntax::util::move_map::MoveMap;
|
||||
use syntax_pos::Span;
|
||||
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for MatchCheckCtxt<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, ex: &hir::Expr) {
|
||||
check_expr(self, ex);
|
||||
struct OuterVisitor<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx> }
|
||||
|
||||
impl<'a, 'v, 'tcx> Visitor<'v> for OuterVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, _expr: &hir::Expr) {
|
||||
return // const, static and N in [T; N] - shouldn't contain anything
|
||||
}
|
||||
fn visit_local(&mut self, l: &hir::Local) {
|
||||
check_local(self, l);
|
||||
|
||||
fn visit_trait_item(&mut self, item: &hir::TraitItem) {
|
||||
if let hir::ConstTraitItem(..) = item.node {
|
||||
return // nothing worth match checking in a constant
|
||||
} else {
|
||||
intravisit::walk_trait_item(self, item);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_impl_item(&mut self, item: &hir::ImplItem) {
|
||||
if let hir::ImplItemKind::Const(..) = item.node {
|
||||
return // nothing worth match checking in a constant
|
||||
} else {
|
||||
intravisit::walk_impl_item(self, item);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, n: ast::NodeId) {
|
||||
check_fn(self, fk, fd, b, s, n);
|
||||
b: &'v hir::Block, s: Span, id: ast::NodeId) {
|
||||
if let FnKind::Closure(..) = fk {
|
||||
span_bug!(s, "check_match: closure outside of function")
|
||||
}
|
||||
|
||||
MatchVisitor {
|
||||
tcx: self.tcx,
|
||||
param_env: &ty::ParameterEnvironment::for_item(self.tcx, id)
|
||||
}.visit_fn(fk, fd, b, s, id);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> OuterVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
|
||||
MatchCheckCtxt::create_and_enter(tcx, tcx.empty_parameter_environment(), |mut cx| {
|
||||
tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut cx);
|
||||
});
|
||||
tcx.visit_all_items_in_krate(DepNode::MatchCheck, &mut OuterVisitor { tcx: tcx });
|
||||
tcx.sess.abort_if_errors();
|
||||
}
|
||||
|
||||
|
@ -68,65 +92,108 @@ fn create_e0004<'a>(sess: &'a Session, sp: Span, error_message: String) -> Diagn
|
|||
struct_span_err!(sess, sp, E0004, "{}", &error_message)
|
||||
}
|
||||
|
||||
fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
|
||||
intravisit::walk_expr(cx, ex);
|
||||
match ex.node {
|
||||
hir::ExprMatch(ref scrut, ref arms, source) => {
|
||||
for arm in arms {
|
||||
// First, check legality of move bindings.
|
||||
check_legality_of_move_bindings(cx,
|
||||
arm.guard.is_some(),
|
||||
&arm.pats);
|
||||
struct MatchVisitor<'a, 'tcx: 'a> {
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
param_env: &'a ty::ParameterEnvironment<'tcx>
|
||||
}
|
||||
|
||||
// Second, if there is a guard on each arm, make sure it isn't
|
||||
// assigning or borrowing anything mutably.
|
||||
if let Some(ref guard) = arm.guard {
|
||||
check_for_mutation_in_guard(cx, &guard);
|
||||
}
|
||||
impl<'a, 'tcx, 'v> Visitor<'v> for MatchVisitor<'a, 'tcx> {
|
||||
fn visit_expr(&mut self, ex: &hir::Expr) {
|
||||
intravisit::walk_expr(self, ex);
|
||||
|
||||
match ex.node {
|
||||
hir::ExprMatch(ref scrut, ref arms, source) => {
|
||||
self.check_match(scrut, arms, source, ex.span);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut static_inliner = StaticInliner::new(cx.tcx);
|
||||
let inlined_arms = arms.iter().map(|arm| {
|
||||
(arm.pats.iter().map(|pat| {
|
||||
static_inliner.fold_pat((*pat).clone())
|
||||
}).collect(), arm.guard.as_ref().map(|e| &**e))
|
||||
}).collect::<Vec<(Vec<P<Pat>>, Option<&hir::Expr>)>>();
|
||||
fn visit_local(&mut self, loc: &hir::Local) {
|
||||
intravisit::walk_local(self, loc);
|
||||
|
||||
// Bail out early if inlining failed.
|
||||
if static_inliner.failed {
|
||||
return;
|
||||
let pat = StaticInliner::new(self.tcx).fold_pat(loc.pat.clone());
|
||||
self.check_irrefutable(&pat, false);
|
||||
|
||||
// Check legality of move bindings and `@` patterns.
|
||||
self.check_patterns(false, slice::ref_slice(&loc.pat));
|
||||
}
|
||||
|
||||
fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v hir::FnDecl,
|
||||
b: &'v hir::Block, s: Span, n: ast::NodeId) {
|
||||
intravisit::walk_fn(self, fk, fd, b, s, n);
|
||||
|
||||
for input in &fd.inputs {
|
||||
self.check_irrefutable(&input.pat, true);
|
||||
self.check_patterns(false, slice::ref_slice(&input.pat));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> MatchVisitor<'a, 'tcx> {
|
||||
fn check_patterns(&self, has_guard: bool, pats: &[P<Pat>]) {
|
||||
check_legality_of_move_bindings(self, has_guard, pats);
|
||||
for pat in pats {
|
||||
check_legality_of_bindings_in_at_patterns(self, pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_match(
|
||||
&self,
|
||||
scrut: &hir::Expr,
|
||||
arms: &[hir::Arm],
|
||||
source: hir::MatchSource,
|
||||
span: Span)
|
||||
{
|
||||
for arm in arms {
|
||||
// First, check legality of move bindings.
|
||||
self.check_patterns(arm.guard.is_some(), &arm.pats);
|
||||
|
||||
// Second, if there is a guard on each arm, make sure it isn't
|
||||
// assigning or borrowing anything mutably.
|
||||
if let Some(ref guard) = arm.guard {
|
||||
check_for_mutation_in_guard(self, &guard);
|
||||
}
|
||||
}
|
||||
|
||||
for pat in inlined_arms
|
||||
.iter()
|
||||
.flat_map(|&(ref pats, _)| pats) {
|
||||
// Third, check legality of move bindings.
|
||||
check_legality_of_bindings_in_at_patterns(cx, &pat);
|
||||
let mut static_inliner = StaticInliner::new(self.tcx);
|
||||
let inlined_arms = arms.iter().map(|arm| {
|
||||
(arm.pats.iter().map(|pat| {
|
||||
static_inliner.fold_pat((*pat).clone())
|
||||
}).collect(), arm.guard.as_ref().map(|e| &**e))
|
||||
}).collect::<Vec<(Vec<P<Pat>>, Option<&hir::Expr>)>>();
|
||||
|
||||
// Fourth, check if there are any references to NaN that we should warn about.
|
||||
check_for_static_nan(cx, &pat);
|
||||
// Bail out early if inlining failed.
|
||||
if static_inliner.failed {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fifth, check if for any of the patterns that match an enumerated type
|
||||
// are bindings with the same name as one of the variants of said type.
|
||||
check_for_bindings_named_the_same_as_variants(cx, &pat);
|
||||
}
|
||||
for pat in inlined_arms.iter().flat_map(|&(ref pats, _)| pats) {
|
||||
// Fourth, check if there are any references to NaN that we should warn about.
|
||||
check_for_static_nan(self, &pat);
|
||||
|
||||
// Fifth, check if for any of the patterns that match an enumerated type
|
||||
// are bindings with the same name as one of the variants of said type.
|
||||
check_for_bindings_named_the_same_as_variants(self, &pat);
|
||||
}
|
||||
|
||||
MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| {
|
||||
// Fourth, check for unreachable arms.
|
||||
check_arms(cx, &inlined_arms[..], source);
|
||||
|
||||
// Finally, check if the whole match expression is exhaustive.
|
||||
// Check for empty enum, because is_useful only works on inhabited types.
|
||||
let pat_ty = cx.tcx.node_id_to_type(scrut.id);
|
||||
let pat_ty = self.tcx.node_id_to_type(scrut.id);
|
||||
if inlined_arms.is_empty() {
|
||||
if !pat_ty.is_uninhabited(cx.tcx) {
|
||||
if !pat_ty.is_uninhabited(self.tcx) {
|
||||
// We know the type is inhabited, so this must be wrong
|
||||
let mut err = create_e0004(cx.tcx.sess, ex.span,
|
||||
let mut err = create_e0004(self.tcx.sess, span,
|
||||
format!("non-exhaustive patterns: type {} \
|
||||
is non-empty",
|
||||
pat_ty));
|
||||
span_help!(&mut err, ex.span,
|
||||
"Please ensure that all possible cases are being handled; \
|
||||
possibly adding wildcards or more match arms.");
|
||||
span_help!(&mut err, span,
|
||||
"Please ensure that all possible cases are being handled; \
|
||||
possibly adding wildcards or more match arms.");
|
||||
err.emit();
|
||||
}
|
||||
// If the type *is* uninhabited, it's vacuously exhaustive
|
||||
|
@ -140,12 +207,40 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &hir::Expr) {
|
|||
.map(|pat| vec![lower_pat(cx, &pat)])
|
||||
.collect();
|
||||
check_exhaustive(cx, scrut.span, &matrix, source);
|
||||
},
|
||||
_ => ()
|
||||
})
|
||||
}
|
||||
|
||||
fn check_irrefutable(&self, pat: &Pat, is_fn_arg: bool) {
|
||||
let origin = if is_fn_arg {
|
||||
"function argument"
|
||||
} else {
|
||||
"local binding"
|
||||
};
|
||||
|
||||
MatchCheckCtxt::create_and_enter(self.tcx, |ref cx| {
|
||||
let pats : Matrix = vec![vec![
|
||||
lower_pat(cx, pat)
|
||||
]].into_iter().collect();
|
||||
|
||||
let witness = match is_useful(cx, &pats, &[cx.wild_pattern], ConstructWitness) {
|
||||
UsefulWithWitness(witness) => witness,
|
||||
NotUseful => return,
|
||||
Useful => bug!()
|
||||
};
|
||||
|
||||
let pattern_string = pat_to_string(witness[0].single_pattern());
|
||||
let mut diag = struct_span_err!(
|
||||
self.tcx.sess, pat.span, E0005,
|
||||
"refutable pattern in {}: `{}` not covered",
|
||||
origin, pattern_string
|
||||
);
|
||||
diag.span_label(pat.span, &format!("pattern `{}` not covered", pattern_string));
|
||||
diag.emit();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) {
|
||||
fn check_for_bindings_named_the_same_as_variants(cx: &MatchVisitor, pat: &Pat) {
|
||||
pat.walk(|p| {
|
||||
if let PatKind::Binding(hir::BindByValue(hir::MutImmutable), name, None) = p.node {
|
||||
let pat_ty = cx.tcx.pat_ty(p);
|
||||
|
@ -175,7 +270,7 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat)
|
|||
}
|
||||
|
||||
// Check that we do not match against a static NaN (#6804)
|
||||
fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
|
||||
fn check_for_static_nan(cx: &MatchVisitor, pat: &Pat) {
|
||||
pat.walk(|p| {
|
||||
if let PatKind::Lit(ref expr) = p.node {
|
||||
match eval_const_expr_partial(cx.tcx, &expr, ExprTypeChecked, None) {
|
||||
|
@ -444,56 +539,8 @@ impl<'a, 'tcx> StaticInliner<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_local(cx: &mut MatchCheckCtxt, loc: &hir::Local) {
|
||||
intravisit::walk_local(cx, loc);
|
||||
|
||||
let pat = StaticInliner::new(cx.tcx).fold_pat(loc.pat.clone());
|
||||
check_irrefutable(cx, &pat, false);
|
||||
|
||||
// Check legality of move bindings and `@` patterns.
|
||||
check_legality_of_move_bindings(cx, false, slice::ref_slice(&loc.pat));
|
||||
check_legality_of_bindings_in_at_patterns(cx, &loc.pat);
|
||||
}
|
||||
|
||||
fn check_fn(cx: &mut MatchCheckCtxt,
|
||||
kind: FnKind,
|
||||
decl: &hir::FnDecl,
|
||||
body: &hir::Block,
|
||||
sp: Span,
|
||||
fn_id: ast::NodeId) {
|
||||
match kind {
|
||||
FnKind::Closure(_) => {}
|
||||
_ => cx.param_env = ty::ParameterEnvironment::for_item(cx.tcx, fn_id),
|
||||
}
|
||||
|
||||
intravisit::walk_fn(cx, kind, decl, body, sp, fn_id);
|
||||
|
||||
for input in &decl.inputs {
|
||||
check_irrefutable(cx, &input.pat, true);
|
||||
check_legality_of_move_bindings(cx, false, slice::ref_slice(&input.pat));
|
||||
check_legality_of_bindings_in_at_patterns(cx, &input.pat);
|
||||
}
|
||||
}
|
||||
|
||||
fn check_irrefutable(cx: &MatchCheckCtxt, pat: &Pat, is_fn_arg: bool) {
|
||||
let origin = if is_fn_arg {
|
||||
"function argument"
|
||||
} else {
|
||||
"local binding"
|
||||
};
|
||||
|
||||
is_refutable(cx, &lower_pat(cx, pat), |uncovered_pat| {
|
||||
let pattern_string = pat_to_string(uncovered_pat.single_pattern());
|
||||
struct_span_err!(cx.tcx.sess, pat.span, E0005,
|
||||
"refutable pattern in {}: `{}` not covered",
|
||||
origin,
|
||||
pattern_string,
|
||||
).span_label(pat.span, &format!("pattern `{}` not covered", pattern_string)).emit();
|
||||
});
|
||||
}
|
||||
|
||||
// Legality of move bindings checking
|
||||
fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
|
||||
fn check_legality_of_move_bindings(cx: &MatchVisitor,
|
||||
has_guard: bool,
|
||||
pats: &[P<Pat>]) {
|
||||
let mut by_ref_span = None;
|
||||
|
@ -532,13 +579,9 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
|
|||
pat.walk(|p| {
|
||||
if let PatKind::Binding(hir::BindByValue(..), _, ref sub) = p.node {
|
||||
let pat_ty = cx.tcx.node_id_to_type(p.id);
|
||||
//FIXME: (@jroesch) this code should be floated up as well
|
||||
cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()),
|
||||
Reveal::NotSpecializable).enter(|infcx| {
|
||||
if infcx.type_moves_by_default(pat_ty, pat.span) {
|
||||
check_move(p, sub.as_ref().map(|p| &**p));
|
||||
}
|
||||
});
|
||||
if pat_ty.moves_by_default(cx.tcx, cx.param_env, pat.span) {
|
||||
check_move(p, sub.as_ref().map(|p| &**p));
|
||||
}
|
||||
}
|
||||
true
|
||||
});
|
||||
|
@ -547,8 +590,9 @@ fn check_legality_of_move_bindings(cx: &MatchCheckCtxt,
|
|||
|
||||
/// Ensures that a pattern guard doesn't borrow by mutable reference or
|
||||
/// assign.
|
||||
fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
|
||||
guard: &hir::Expr) {
|
||||
///
|
||||
/// FIXME: this should be done by borrowck.
|
||||
fn check_for_mutation_in_guard(cx: &MatchVisitor, guard: &hir::Expr) {
|
||||
cx.tcx.infer_ctxt(None, Some(cx.param_env.clone()),
|
||||
Reveal::NotSpecializable).enter(|infcx| {
|
||||
let mut checker = MutationChecker {
|
||||
|
@ -560,7 +604,7 @@ fn check_for_mutation_in_guard<'a, 'tcx>(cx: &'a MatchCheckCtxt<'a, 'tcx>,
|
|||
}
|
||||
|
||||
struct MutationChecker<'a, 'gcx: 'a> {
|
||||
cx: &'a MatchCheckCtxt<'a, 'gcx>,
|
||||
cx: &'a MatchVisitor<'a, 'gcx>,
|
||||
}
|
||||
|
||||
impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> {
|
||||
|
@ -600,12 +644,12 @@ impl<'a, 'gcx, 'tcx> Delegate<'tcx> for MutationChecker<'a, 'gcx> {
|
|||
/// Forbids bindings in `@` patterns. This is necessary for memory safety,
|
||||
/// because of the way rvalues are handled in the borrow check. (See issue
|
||||
/// #14587.)
|
||||
fn check_legality_of_bindings_in_at_patterns(cx: &MatchCheckCtxt, pat: &Pat) {
|
||||
fn check_legality_of_bindings_in_at_patterns(cx: &MatchVisitor, pat: &Pat) {
|
||||
AtBindingPatternVisitor { cx: cx, bindings_allowed: true }.visit_pat(pat);
|
||||
}
|
||||
|
||||
struct AtBindingPatternVisitor<'a, 'b:'a, 'tcx:'b> {
|
||||
cx: &'a MatchCheckCtxt<'b, 'tcx>,
|
||||
cx: &'a MatchVisitor<'b, 'tcx>,
|
||||
bindings_allowed: bool
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue