Forbid extern statics from appearing in patterns

Fixes #16149.
This commit is contained in:
Jakub Wieczorek 2014-08-17 22:10:25 +02:00
parent a12a4ddcfa
commit d283574558
5 changed files with 90 additions and 47 deletions

View File

@ -139,7 +139,6 @@ register_diagnostics!(
E0120,
E0121,
E0122,
E0123,
E0124,
E0125,
E0126,
@ -173,5 +172,6 @@ register_diagnostics!(
E0154,
E0155,
E0156,
E0157
E0157,
E0158
)

View File

@ -23,7 +23,7 @@ use std::gc::{Gc, GC};
use std::iter::AdditiveIterator;
use std::iter::range_inclusive;
use syntax::ast::*;
use syntax::ast_util::{is_unguarded, walk_pat};
use syntax::ast_util::walk_pat;
use syntax::codemap::{Span, Spanned, DUMMY_SP};
use syntax::fold::{Folder, noop_fold_pat};
use syntax::print::pprust::pat_to_string;
@ -159,13 +159,31 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
}
}
// Third, check for unreachable arms.
check_arms(cx, arms.as_slice());
let mut static_inliner = StaticInliner::new(cx.tcx);
let inlined_arms = arms
.iter()
.map(|arm| Arm {
pats: arm.pats.iter().map(|pat| {
static_inliner.fold_pat(*pat)
}).collect(),
..arm.clone()
})
.collect::<Vec<Arm>>();
if static_inliner.failed {
return;
}
// Third, check if there are any references to NaN that we should warn about.
check_for_static_nan(cx, inlined_arms.as_slice());
// Fourth, check for unreachable arms.
check_arms(cx, inlined_arms.as_slice());
// Finally, check if the whole match expression is exhaustive.
// Check for empty enum, because is_useful only works on inhabited types.
let pat_ty = node_id_to_type(cx.tcx, scrut.id);
if arms.is_empty() {
if inlined_arms.is_empty() {
if !type_is_empty(cx.tcx, pat_ty) {
// We know the type is inhabited, so this must be wrong
span_err!(cx.tcx.sess, ex.span, E0002,
@ -177,19 +195,16 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
return;
}
let mut static_inliner = StaticInliner { tcx: cx.tcx };
let matrix: Matrix = arms
.iter()
.filter(|&arm| is_unguarded(arm))
.flat_map(|arm| arm.pats.iter())
.map(|pat| vec![static_inliner.fold_pat(*pat)])
let matrix: Matrix = inlined_arms
.move_iter()
.filter(|arm| arm.guard.is_none())
.flat_map(|arm| arm.pats.move_iter())
.map(|pat| vec![pat])
.collect();
check_exhaustive(cx, ex.span, &matrix);
},
ExprForLoop(ref pat, _, _, _) => {
let mut static_inliner = StaticInliner {
tcx: cx.tcx
};
let mut static_inliner = StaticInliner::new(cx.tcx);
match is_refutable(cx, static_inliner.fold_pat(*pat)) {
Some(uncovered_pat) => {
cx.tcx.sess.span_err(
@ -216,19 +231,14 @@ fn is_expr_const_nan(tcx: &ty::ctxt, expr: &Expr) -> bool {
}
}
// Check for unreachable patterns
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
let mut seen = Matrix(vec!());
let mut static_inliner = StaticInliner { tcx: cx.tcx };
// Check that we do not match against a static NaN (#6804)
fn check_for_static_nan(cx: &MatchCheckCtxt, arms: &[Arm]) {
for arm in arms.iter() {
for pat in arm.pats.iter() {
let inlined = static_inliner.fold_pat(*pat);
// Check that we do not match against a static NaN (#6804)
walk_pat(&*inlined, |p| {
for &pat in arm.pats.iter() {
walk_pat(&*pat, |p| {
match p.node {
PatLit(expr) if is_expr_const_nan(cx.tcx, &*expr) => {
span_warn!(cx.tcx.sess, pat.span, E0003,
span_warn!(cx.tcx.sess, p.span, E0003,
"unmatchable NaN in pattern, \
use the is_nan method in a guard instead");
}
@ -236,8 +246,16 @@ fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
}
true
});
}
}
}
let v = vec![inlined];
// Check for unreachable patterns
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
let mut seen = Matrix(vec!());
for arm in arms.iter() {
for &pat in arm.pats.iter() {
let v = vec![pat];
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
Useful => (),
@ -293,7 +311,17 @@ fn const_val_to_expr(value: &const_val) -> Gc<Expr> {
}
pub struct StaticInliner<'a> {
pub tcx: &'a ty::ctxt
pub tcx: &'a ty::ctxt,
pub failed: bool
}
impl<'a> StaticInliner<'a> {
pub fn new<'a>(tcx: &'a ty::ctxt) -> StaticInliner<'a> {
StaticInliner {
tcx: tcx,
failed: false
}
}
}
impl<'a> Folder for StaticInliner<'a> {
@ -302,9 +330,17 @@ impl<'a> Folder for StaticInliner<'a> {
PatIdent(..) | PatEnum(..) => {
let def = self.tcx.def_map.borrow().find_copy(&pat.id);
match def {
Some(DefStatic(did, _)) => {
let const_expr = lookup_const_by_id(self.tcx, did).unwrap();
const_expr_to_pat(self.tcx, const_expr)
Some(DefStatic(did, _)) => match lookup_const_by_id(self.tcx, did) {
Some(const_expr) => box (GC) Pat {
span: pat.span,
..(*const_expr_to_pat(self.tcx, const_expr)).clone()
},
None => {
self.failed = true;
span_err!(self.tcx.sess, pat.span, E0158,
"extern statics cannot be referenced in patterns");
pat
}
},
_ => noop_fold_pat(pat, self)
}
@ -813,7 +849,7 @@ fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) {
LocalFor => "`for` loop"
};
let mut static_inliner = StaticInliner { tcx: cx.tcx };
let mut static_inliner = StaticInliner::new(cx.tcx);
match is_refutable(cx, static_inliner.fold_pat(loc.pat)) {
Some(pat) => {
span_err!(cx.tcx.sess, loc.pat.span, E0005,

View File

@ -1422,7 +1422,7 @@ fn trans_match_inner<'a>(scope_cx: &'a Block<'a>,
bindings_map: create_bindings_map(bcx, *arm.pats.get(0), discr_expr, &*arm.body)
}).collect();
let mut static_inliner = StaticInliner { tcx: scope_cx.tcx() };
let mut static_inliner = StaticInliner::new(scope_cx.tcx());
let mut matches = Vec::new();
for arm_data in arm_datas.iter() {
matches.extend(arm_data.arm.pats.iter().map(|&p| Match {

View File

@ -209,21 +209,6 @@ pub fn name_to_dummy_lifetime(name: Name) -> Lifetime {
name: name }
}
pub fn is_unguarded(a: &Arm) -> bool {
match a.guard {
None => true,
_ => false
}
}
pub fn unguarded_pat(a: &Arm) -> Option<Vec<Gc<Pat>>> {
if is_unguarded(a) {
Some(/* FIXME (#2543) */ a.pats.clone())
} else {
None
}
}
/// Generate a "pretty" name for an `impl` from its type and trait.
/// This is designed so that symbols of `impl`'d methods give some
/// hint of where they came from, (previously they would all just be

View File

@ -0,0 +1,22 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
extern {
static externalValue: int;
}
fn main() {
let boolValue = match 42 {
externalValue => true,
//~^ ERROR extern statics cannot be referenced in patterns
_ => false
};
}