]> git.lizzy.rs Git - rust.git/commitdiff
Forbid extern statics from appearing in patterns
authorJakub Wieczorek <jakub@jakub.cc>
Sun, 17 Aug 2014 20:10:25 +0000 (22:10 +0200)
committerJakub Wieczorek <jakub@jakub.cc>
Sun, 17 Aug 2014 22:08:57 +0000 (00:08 +0200)
Fixes #16149.

src/librustc/diagnostics.rs
src/librustc/middle/check_match.rs
src/librustc/middle/trans/_match.rs
src/libsyntax/ast_util.rs
src/test/compile-fail/issue-16149.rs [new file with mode: 0644]

index 1b806e1c257dc6805ffe2fd3beeb63e421f8e904..a789049d4de8c08cd5a276845aeef66c674b0d6f 100644 (file)
     E0120,
     E0121,
     E0122,
-    E0123,
     E0124,
     E0125,
     E0126,
     E0154,
     E0155,
     E0156,
-    E0157
+    E0157,
+    E0158
 )
index d8a88b66310d9b2d9ac705d1dddf792695bd4806..230668e70665382dc1c5ac958e44f56361268ca4 100644 (file)
@@ -23,7 +23,7 @@
 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 @@ fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
             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,
index 5f54fbef74e186abfcfa85254e0705fde405932b..7219c234779ba17fd3e5d5733b16f90e052f3271 100644 (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 {
index 5674c6675f96362bec27e655b90b3e403d79d42b..1a4b41404be01d39f9a194cc8cb4838f9d4bd87c 100644 (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
diff --git a/src/test/compile-fail/issue-16149.rs b/src/test/compile-fail/issue-16149.rs
new file mode 100644 (file)
index 0000000..c52c53e
--- /dev/null
@@ -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
+    };
+}
+