about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAndrew Cann <shum@canndrew.org>2017-01-17 21:32:27 +0800
committerAndrew Cann <shum@canndrew.org>2017-01-17 21:32:27 +0800
commit1c51f60ee2fad1db6ed0615bae8ed99ce08b0fdd (patch)
treefbdc6696124690c881c1833edbe8d816cd6e1d66
parent31dee37a15961f51e25dfea66b1364e051be1a65 (diff)
downloadrust-1c51f60ee2fad1db6ed0615bae8ed99ce08b0fdd.tar.gz
rust-1c51f60ee2fad1db6ed0615bae8ed99ce08b0fdd.zip
Change unreachable patterns ICEs to warnings
Allow code with unreachable `?` and `for` patterns to compile.
Add some tests.
-rw-r--r--src/librustc/hir/lowering.rs64
-rw-r--r--src/librustc_const_eval/check_match.rs13
-rw-r--r--src/test/compile-fail/recursive-types-are-not-uninhabited.rs26
-rw-r--r--src/test/compile-fail/unreachable-loop-patterns.rs20
-rw-r--r--src/test/run-pass/unreachable-try-pattern.rs22
5 files changed, 125 insertions, 20 deletions
diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs
index 3b20a77ad03..698db3f7169 100644
--- a/src/librustc/hir/lowering.rs
+++ b/src/librustc/hir/lowering.rs
@@ -51,6 +51,7 @@ use util::nodemap::{NodeMap, FxHashMap};
 use std::collections::BTreeMap;
 use std::iter;
 
+use syntax::attr;
 use syntax::ast::*;
 use syntax::errors;
 use syntax::ptr::P;
@@ -1814,7 +1815,8 @@ impl<'a> LoweringContext<'a> {
                     let match_expr = P(self.expr_match(e.span,
                                                        into_iter_expr,
                                                        hir_vec![iter_arm],
-                                                       hir::MatchSource::ForLoopDesugar));
+                                                       hir::MatchSource::ForLoopDesugar,
+                                                       ThinVec::new()));
 
                     // `{ let _result = ...; _result }`
                     // underscore prevents an unused_variables lint if the head diverges
@@ -1833,8 +1835,12 @@ impl<'a> LoweringContext<'a> {
                 ExprKind::Try(ref sub_expr) => {
                     // to:
                     //
+                    // #[allow(unreachable_patterns)]
                     // match Carrier::translate(<expr>) {
-                    //     Ok(val) => val,
+                    //     Ok(val) => {
+                    //         #[allow(unreachable_code)]
+                    //         val
+                    //     }
                     //     Err(err) => return Carrier::from_error(From::from(err))
                     // }
                     let unstable_span = self.allow_internal_unstable("?", e.span);
@@ -1849,14 +1855,31 @@ impl<'a> LoweringContext<'a> {
                         P(self.expr_call(e.span, path, hir_vec![sub_expr]))
                     };
 
-                    // Ok(val) => val
+                    // Ok(val) => { #[allow(unreachable_code)] val }
                     let ok_arm = {
                         let val_ident = self.str_to_ident("val");
                         let val_pat = self.pat_ident(e.span, val_ident);
-                        let val_expr = P(self.expr_ident(e.span, val_ident, val_pat.id));
+                        // #[allow(unreachable_code)]
+                        let val_attr = {
+                            // allow(unreachable_code)
+                            let allow = {
+                                let allow_ident = self.str_to_ident("allow");
+                                let uc_ident = self.str_to_ident("unreachable_code");
+                                let uc_meta_item = attr::mk_spanned_word_item(e.span, uc_ident);
+                                let uc_nested_meta_item = NestedMetaItemKind::MetaItem(uc_meta_item);
+                                let uc_spanned = respan(e.span, uc_nested_meta_item);
+                                attr::mk_spanned_list_item(e.span, allow_ident, vec![uc_spanned])
+                            };
+                            attr::mk_spanned_attr_outer(e.span, attr::mk_attr_id(), allow)
+                        };
+                        let attrs = From::from(vec![val_attr]);
+                        let val_expr = P(self.expr_ident_with_attrs(e.span, val_ident, val_pat.id, attrs));
+                        let val_block = P(self.block_expr(val_expr));
+                        let ok_expr = P(self.expr_block(val_block, ThinVec::new()));
+
                         let ok_pat = self.pat_ok(e.span, val_pat);
 
-                        self.arm(hir_vec![ok_pat], val_expr)
+                        self.arm(hir_vec![ok_pat], ok_expr)
                     };
 
                     // Err(err) => return Carrier::from_error(From::from(err))
@@ -1885,8 +1908,23 @@ impl<'a> LoweringContext<'a> {
                         self.arm(hir_vec![err_pat], ret_expr)
                     };
 
+                    // #[allow(unreachable_patterns)]
+                    let match_attr = {
+                        // allow(unreachable_patterns)
+                        let allow = {
+                            let allow_ident = self.str_to_ident("allow");
+                            let up_ident = self.str_to_ident("unreachable_patterns");
+                            let up_meta_item = attr::mk_spanned_word_item(e.span, up_ident);
+                            let up_nested_meta_item = NestedMetaItemKind::MetaItem(up_meta_item);
+                            let up_spanned = respan(e.span, up_nested_meta_item);
+                            attr::mk_spanned_list_item(e.span, allow_ident, vec![up_spanned])
+                        };
+                        attr::mk_spanned_attr_outer(e.span, attr::mk_attr_id(), allow)
+                    };
+
+                    let attrs = From::from(vec![match_attr]);
                     return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm],
-                                           hir::MatchSource::TryDesugar);
+                                           hir::MatchSource::TryDesugar, attrs);
                 }
 
                 ExprKind::Mac(_) => panic!("Shouldn't exist here"),
@@ -2031,6 +2069,13 @@ impl<'a> LoweringContext<'a> {
     }
 
     fn expr_ident(&mut self, span: Span, id: Name, binding: NodeId) -> hir::Expr {
+        self.expr_ident_with_attrs(span, id, binding, ThinVec::new())
+    }
+
+    fn expr_ident_with_attrs(&mut self, span: Span,
+                                        id: Name,
+                                        binding: NodeId,
+                                        attrs: ThinVec<Attribute>) -> hir::Expr {
         let def = {
             let defs = self.resolver.definitions();
             Def::Local(defs.local_def_id(binding))
@@ -2042,7 +2087,7 @@ impl<'a> LoweringContext<'a> {
             segments: hir_vec![hir::PathSegment::from_name(id)],
         })));
 
-        self.expr(span, expr_path, ThinVec::new())
+        self.expr(span, expr_path, attrs)
     }
 
     fn expr_mut_addr_of(&mut self, span: Span, e: P<hir::Expr>) -> hir::Expr {
@@ -2062,9 +2107,10 @@ impl<'a> LoweringContext<'a> {
                   span: Span,
                   arg: P<hir::Expr>,
                   arms: hir::HirVec<hir::Arm>,
-                  source: hir::MatchSource)
+                  source: hir::MatchSource,
+                  attrs: ThinVec<Attribute>)
                   -> hir::Expr {
-        self.expr(span, hir::ExprMatch(arg, arms, source), ThinVec::new())
+        self.expr(span, hir::ExprMatch(arg, arms, source), attrs)
     }
 
     fn expr_block(&mut self, b: P<hir::Block>, attrs: ThinVec<Attribute>) -> hir::Expr {
diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs
index 400af3c7023..5046920f5e3 100644
--- a/src/librustc_const_eval/check_match.rs
+++ b/src/librustc_const_eval/check_match.rs
@@ -308,14 +308,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
                                 .emit();
                         },
 
-                        hir::MatchSource::ForLoopDesugar => {
-                            // this is a bug, because on `match iter.next()` we cover
-                            // `Some(<head>)` and `None`. It's impossible to have an unreachable
-                            // pattern
-                            // (see libsyntax/ext/expand.rs for the full expansion of a for loop)
-                            span_bug!(pat.span, "unreachable for-loop pattern")
-                        },
-
+                        hir::MatchSource::ForLoopDesugar |
                         hir::MatchSource::Normal => {
                             let mut diagnostic = Diagnostic::new(Level::Warning,
                                                                  "unreachable pattern");
@@ -329,9 +322,7 @@ fn check_arms<'a, 'tcx>(cx: &mut MatchCheckCtxt<'a, 'tcx>,
                                                             hir_pat.id, diagnostic);
                         },
 
-                        hir::MatchSource::TryDesugar => {
-                            span_bug!(pat.span, "unreachable try pattern")
-                        },
+                        hir::MatchSource::TryDesugar => {}
                     }
                 }
                 Useful => (),
diff --git a/src/test/compile-fail/recursive-types-are-not-uninhabited.rs b/src/test/compile-fail/recursive-types-are-not-uninhabited.rs
new file mode 100644
index 00000000000..f8d6c3de2ab
--- /dev/null
+++ b/src/test/compile-fail/recursive-types-are-not-uninhabited.rs
@@ -0,0 +1,26 @@
+// 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.
+
+//#![feature(never_type)]
+
+struct R<'a> {
+    r: &'a R<'a>,
+}
+
+fn foo(res: Result<u32, &R>) -> u32 {
+    let Ok(x) = res;
+    //~^ ERROR refutable pattern
+    x
+}
+
+fn main() {
+    foo(Ok(23));
+}
+
diff --git a/src/test/compile-fail/unreachable-loop-patterns.rs b/src/test/compile-fail/unreachable-loop-patterns.rs
new file mode 100644
index 00000000000..6147692658f
--- /dev/null
+++ b/src/test/compile-fail/unreachable-loop-patterns.rs
@@ -0,0 +1,20 @@
+// Copyright 2013 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.
+
+#![feature(never_type)]
+#![deny(unreachable_patterns)]
+
+fn main() {
+    let x: &[!] = &[];
+
+    for _ in x {}
+    //~^ ERROR unreachable pattern
+}
+
diff --git a/src/test/run-pass/unreachable-try-pattern.rs b/src/test/run-pass/unreachable-try-pattern.rs
new file mode 100644
index 00000000000..f58d5c8de0d
--- /dev/null
+++ b/src/test/run-pass/unreachable-try-pattern.rs
@@ -0,0 +1,22 @@
+// Copyright 2013 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.
+
+#![feature(never_type)]
+#![deny(unreachable_code)]
+#![deny(unreachable_patterns)]
+
+fn bar(x: Result<!, i32>) -> Result<u32, i32> {
+    x?
+}
+
+fn main() {
+    let _ = bar(Err(123));
+}
+