Skip to content

Forbid extern statics from appearing in patterns #16567

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from Aug 18, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/librustc/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@ register_diagnostics!(
E0120,
E0121,
E0122,
E0123,
E0124,
E0125,
E0126,
Expand Down Expand Up @@ -173,5 +172,6 @@ register_diagnostics!(
E0154,
E0155,
E0156,
E0157
E0157,
E0158
)
94 changes: 65 additions & 29 deletions src/librustc/middle/check_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -216,28 +231,31 @@ 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");
}
_ => ()
}
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 => (),
Expand Down Expand Up @@ -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> {
Expand All @@ -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)
}
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/_match.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 0 additions & 15 deletions src/libsyntax/ast_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 22 additions & 0 deletions src/test/compile-fail/issue-16149.rs
Original file line number Diff line number Diff line change
@@ -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
};
}