Skip to content

Commit fba1194

Browse files
author
Jakub Wieczorek
committed
Add support for patterns referencing non-trivial statics
This is accomplished by rewriting static expressions into equivalent patterns. This way, patterns referencing static variables can both participate in exhaustiveness analysis as well as be compiled down into the appropriate branch of the decision trees that match expressions are codegened to. Fixes #6533. Fixes #13626. Fixes #13731. Fixes #14576. Fixes #15393.
1 parent 7502b4c commit fba1194

File tree

9 files changed

+489
-162
lines changed

9 files changed

+489
-162
lines changed

src/librustc/lib.rs

+26-26
Original file line numberDiff line numberDiff line change
@@ -68,42 +68,42 @@ pub mod back {
6868
}
6969

7070
pub mod middle {
71-
pub mod def;
72-
pub mod trans;
73-
pub mod ty;
74-
pub mod ty_fold;
75-
pub mod subst;
76-
pub mod resolve;
77-
pub mod resolve_lifetime;
78-
pub mod typeck;
71+
pub mod astencode;
72+
pub mod borrowck;
73+
pub mod cfg;
74+
pub mod check_const;
7975
pub mod check_loop;
8076
pub mod check_match;
81-
pub mod check_const;
8277
pub mod check_static;
83-
pub mod borrowck;
78+
pub mod const_eval;
8479
pub mod dataflow;
85-
pub mod mem_categorization;
86-
pub mod liveness;
87-
pub mod kind;
80+
pub mod dead;
81+
pub mod def;
82+
pub mod dependency_format;
83+
pub mod effect;
84+
pub mod entry;
85+
pub mod expr_use_visitor;
8886
pub mod freevars;
89-
pub mod pat_util;
90-
pub mod region;
91-
pub mod const_eval;
92-
pub mod astencode;
87+
pub mod graph;
88+
pub mod intrinsicck;
89+
pub mod kind;
9390
pub mod lang_items;
91+
pub mod liveness;
92+
pub mod mem_categorization;
93+
pub mod pat_util;
9494
pub mod privacy;
95-
pub mod entry;
96-
pub mod effect;
9795
pub mod reachable;
98-
pub mod graph;
99-
pub mod cfg;
100-
pub mod dead;
101-
pub mod expr_use_visitor;
102-
pub mod dependency_format;
103-
pub mod weak_lang_items;
96+
pub mod region;
97+
pub mod resolve;
98+
pub mod resolve_lifetime;
10499
pub mod save;
105-
pub mod intrinsicck;
106100
pub mod stability;
101+
pub mod subst;
102+
pub mod trans;
103+
pub mod ty;
104+
pub mod ty_fold;
105+
pub mod typeck;
106+
pub mod weak_lang_items;
107107
}
108108

109109
pub mod front {

src/librustc/middle/check_match.rs

+78-82
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
// except according to those terms.
1010

1111
use middle::const_eval::{compare_const_vals, const_bool, const_float, const_nil, const_val};
12-
use middle::const_eval::{eval_const_expr, lookup_const_by_id};
12+
use middle::const_eval::{const_expr_to_pat, eval_const_expr, lookup_const_by_id};
1313
use middle::def::*;
1414
use middle::pat_util::*;
1515
use middle::ty::*;
@@ -21,8 +21,9 @@ use std::iter::range_inclusive;
2121
use syntax::ast::*;
2222
use syntax::ast_util::{is_unguarded, walk_pat};
2323
use syntax::codemap::{Span, Spanned, DUMMY_SP};
24-
use syntax::owned_slice::OwnedSlice;
24+
use syntax::fold::{Folder, noop_fold_pat};
2525
use syntax::print::pprust::pat_to_string;
26+
use syntax::parse::token;
2627
use syntax::visit;
2728
use syntax::visit::{Visitor, FnKind};
2829
use util::ppaux::ty_to_string;
@@ -76,6 +77,12 @@ impl fmt::Show for Matrix {
7677
}
7778
}
7879

80+
impl FromIterator<Vec<Gc<Pat>>> for Matrix {
81+
fn from_iter<T: Iterator<Vec<Gc<Pat>>>>(mut iterator: T) -> Matrix {
82+
Matrix(iterator.collect())
83+
}
84+
}
85+
7986
pub struct MatchCheckCtxt<'a> {
8087
pub tcx: &'a ty::ctxt
8188
}
@@ -120,10 +127,8 @@ impl<'a> Visitor<()> for MatchCheckCtxt<'a> {
120127
}
121128

122129
pub fn check_crate(tcx: &ty::ctxt, krate: &Crate) {
123-
let mut cx = MatchCheckCtxt { tcx: tcx, };
124-
130+
let mut cx = MatchCheckCtxt { tcx: tcx };
125131
visit::walk_crate(&mut cx, krate, ());
126-
127132
tcx.sess.abort_if_errors();
128133
}
129134

@@ -155,48 +160,49 @@ fn check_expr(cx: &mut MatchCheckCtxt, ex: &Expr) {
155160
// If the type *is* empty, it's vacuously exhaustive
156161
return;
157162
}
158-
let m: Matrix = Matrix(arms
163+
164+
let mut static_inliner = StaticInliner { tcx: cx.tcx };
165+
let matrix: Matrix = arms
159166
.iter()
160167
.filter(|&arm| is_unguarded(arm))
161168
.flat_map(|arm| arm.pats.iter())
162-
.map(|pat| vec!(pat.clone()))
163-
.collect());
164-
check_exhaustive(cx, ex.span, &m);
169+
.map(|pat| vec![static_inliner.fold_pat(*pat)])
170+
.collect();
171+
check_exhaustive(cx, ex.span, &matrix);
165172
},
166173
_ => ()
167174
}
168175
}
169176

177+
fn is_expr_const_nan(tcx: &ty::ctxt, expr: &Expr) -> bool {
178+
match eval_const_expr(tcx, expr) {
179+
const_float(f) => f.is_nan(),
180+
_ => false
181+
}
182+
}
183+
170184
// Check for unreachable patterns
171185
fn check_arms(cx: &MatchCheckCtxt, arms: &[Arm]) {
172186
let mut seen = Matrix(vec!());
187+
let mut static_inliner = StaticInliner { tcx: cx.tcx };
173188
for arm in arms.iter() {
174189
for pat in arm.pats.iter() {
190+
let inlined = static_inliner.fold_pat(*pat);
191+
175192
// Check that we do not match against a static NaN (#6804)
176-
let pat_matches_nan: |&Pat| -> bool = |p| {
177-
let opt_def = cx.tcx.def_map.borrow().find_copy(&p.id);
178-
match opt_def {
179-
Some(DefStatic(did, false)) => {
180-
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
181-
match eval_const_expr(cx.tcx, &*const_expr) {
182-
const_float(f) if f.is_nan() => true,
183-
_ => false
184-
}
193+
walk_pat(&*inlined, |p| {
194+
match p.node {
195+
PatLit(expr) if is_expr_const_nan(cx.tcx, &*expr) => {
196+
span_warn!(cx.tcx.sess, pat.span, E0003,
197+
"unmatchable NaN in pattern, \
198+
use the is_nan method in a guard instead");
185199
}
186-
_ => false
187-
}
188-
};
189-
190-
walk_pat(&**pat, |p| {
191-
if pat_matches_nan(p) {
192-
span_warn!(cx.tcx.sess, p.span, E0003,
193-
"unmatchable NaN in pattern, use the is_nan method in a guard instead"
194-
);
200+
_ => ()
195201
}
196202
true
197203
});
198204

199-
let v = vec!(*pat);
205+
let v = vec![inlined];
200206
match is_useful(cx, &seen, v.as_slice(), LeaveOutWitness) {
201207
NotUseful => span_err!(cx.tcx.sess, pat.span, E0001, "unreachable pattern"),
202208
Useful => (),
@@ -218,8 +224,8 @@ fn raw_pat(p: Gc<Pat>) -> Gc<Pat> {
218224
}
219225
}
220226

221-
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, m: &Matrix) {
222-
match is_useful(cx, m, [wild()], ConstructWitness) {
227+
fn check_exhaustive(cx: &MatchCheckCtxt, sp: Span, matrix: &Matrix) {
228+
match is_useful(cx, matrix, [wild()], ConstructWitness) {
223229
UsefulWithWitness(pats) => {
224230
let witness = match pats.as_slice() {
225231
[witness] => witness,
@@ -251,16 +257,26 @@ fn const_val_to_expr(value: &const_val) -> Gc<Expr> {
251257
}
252258
}
253259

254-
fn def_to_path(tcx: &ty::ctxt, id: DefId) -> Path {
255-
ty::with_path(tcx, id, |mut path| Path {
256-
global: false,
257-
segments: path.last().map(|elem| PathSegment {
258-
identifier: Ident::new(elem.name()),
259-
lifetimes: vec!(),
260-
types: OwnedSlice::empty()
261-
}).move_iter().collect(),
262-
span: DUMMY_SP,
263-
})
260+
pub struct StaticInliner<'a> {
261+
pub tcx: &'a ty::ctxt
262+
}
263+
264+
impl<'a> Folder for StaticInliner<'a> {
265+
fn fold_pat(&mut self, pat: Gc<Pat>) -> Gc<Pat> {
266+
match pat.node {
267+
PatIdent(..) | PatEnum(..) => {
268+
let def = self.tcx.def_map.borrow().find_copy(&pat.id);
269+
match def {
270+
Some(DefStatic(did, _)) => {
271+
let const_expr = lookup_const_by_id(self.tcx, did).unwrap();
272+
const_expr_to_pat(self.tcx, const_expr)
273+
},
274+
_ => noop_fold_pat(pat, self)
275+
}
276+
}
277+
_ => noop_fold_pat(pat, self)
278+
}
279+
}
264280
}
265281

266282
/// Constructs a partial witness for a pattern given a list of
@@ -283,9 +299,11 @@ fn construct_witness(cx: &MatchCheckCtxt, ctor: &Constructor,
283299

284300
ty::ty_enum(cid, _) | ty::ty_struct(cid, _) => {
285301
let (vid, is_structure) = match ctor {
286-
&Variant(vid) => (vid,
287-
ty::enum_variant_with_id(cx.tcx, cid, vid).arg_names.is_some()),
288-
_ => (cid, true)
302+
&Variant(vid) =>
303+
(vid, ty::enum_variant_with_id(cx.tcx, cid, vid).arg_names.is_some()),
304+
_ =>
305+
(cid, ty::lookup_struct_fields(cx.tcx, cid).iter()
306+
.any(|field| field.name != token::special_idents::unnamed_field.name))
289307
};
290308
if is_structure {
291309
let fields = ty::lookup_struct_fields(cx.tcx, vid);
@@ -459,8 +477,7 @@ fn is_useful(cx: &MatchCheckCtxt, matrix @ &Matrix(ref rows): &Matrix,
459477
},
460478

461479
Some(constructor) => {
462-
let matrix = Matrix(rows.iter().filter_map(|r|
463-
default(cx, r.as_slice())).collect());
480+
let matrix = rows.iter().filter_map(|r| default(cx, r.as_slice())).collect();
464481
match is_useful(cx, &matrix, v.tail(), witness) {
465482
UsefulWithWitness(pats) => {
466483
let arity = constructor_arity(cx, &constructor, left_ty);
@@ -506,25 +523,23 @@ fn pat_constructors(cx: &MatchCheckCtxt, p: Gc<Pat>,
506523
match pat.node {
507524
PatIdent(..) =>
508525
match cx.tcx.def_map.borrow().find(&pat.id) {
509-
Some(&DefStatic(did, false)) => {
510-
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
511-
vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
512-
},
526+
Some(&DefStatic(..)) =>
527+
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
513528
Some(&DefStruct(_)) => vec!(Single),
514529
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
515530
_ => vec!()
516531
},
517532
PatEnum(..) =>
518533
match cx.tcx.def_map.borrow().find(&pat.id) {
519-
Some(&DefStatic(did, false)) => {
520-
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
521-
vec!(ConstantValue(eval_const_expr(cx.tcx, &*const_expr)))
522-
},
534+
Some(&DefStatic(..)) =>
535+
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
523536
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
524537
_ => vec!(Single)
525538
},
526539
PatStruct(..) =>
527540
match cx.tcx.def_map.borrow().find(&pat.id) {
541+
Some(&DefStatic(..)) =>
542+
cx.tcx.sess.span_bug(pat.span, "static pattern should've been rewritten"),
528543
Some(&DefVariant(_, id, _)) => vec!(Variant(id)),
529544
_ => vec!(Single)
530545
},
@@ -583,7 +598,7 @@ pub fn constructor_arity(cx: &MatchCheckCtxt, ctor: &Constructor, ty: ty::t) ->
583598
}
584599

585600
fn range_covered_by_constructor(ctor: &Constructor,
586-
from: &const_val,to: &const_val) -> Option<bool> {
601+
from: &const_val, to: &const_val) -> Option<bool> {
587602
let (c_from, c_to) = match *ctor {
588603
ConstantValue(ref value) => (value, value),
589604
ConstantRange(ref from, ref to) => (from, to),
@@ -621,44 +636,22 @@ pub fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
621636
&PatIdent(_, _, _) => {
622637
let opt_def = cx.tcx.def_map.borrow().find_copy(&pat_id);
623638
match opt_def {
639+
Some(DefStatic(..)) =>
640+
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
624641
Some(DefVariant(_, id, _)) => if *constructor == Variant(id) {
625642
Some(vec!())
626643
} else {
627644
None
628645
},
629-
Some(DefStatic(did, _)) => {
630-
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
631-
let e_v = eval_const_expr(cx.tcx, &*const_expr);
632-
match range_covered_by_constructor(constructor, &e_v, &e_v) {
633-
Some(true) => Some(vec!()),
634-
Some(false) => None,
635-
None => {
636-
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
637-
None
638-
}
639-
}
640-
}
641-
_ => {
642-
Some(Vec::from_elem(arity, wild()))
643-
}
646+
_ => Some(Vec::from_elem(arity, wild()))
644647
}
645648
}
646649

647650
&PatEnum(_, ref args) => {
648651
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
649652
match def {
650-
DefStatic(did, _) => {
651-
let const_expr = lookup_const_by_id(cx.tcx, did).unwrap();
652-
let e_v = eval_const_expr(cx.tcx, &*const_expr);
653-
match range_covered_by_constructor(constructor, &e_v, &e_v) {
654-
Some(true) => Some(vec!()),
655-
Some(false) => None,
656-
None => {
657-
cx.tcx.sess.span_err(pat_span, "mismatched types between arms");
658-
None
659-
}
660-
}
661-
}
653+
DefStatic(..) =>
654+
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
662655
DefVariant(_, id, _) if *constructor != Variant(id) => None,
663656
DefVariant(..) | DefFn(..) | DefStruct(..) => {
664657
Some(match args {
@@ -674,6 +667,8 @@ pub fn specialize(cx: &MatchCheckCtxt, r: &[Gc<Pat>],
674667
// Is this a struct or an enum variant?
675668
let def = cx.tcx.def_map.borrow().get_copy(&pat_id);
676669
let class_id = match def {
670+
DefStatic(..) =>
671+
cx.tcx.sess.span_bug(pat_span, "static pattern should've been rewritten"),
677672
DefVariant(_, variant_id, _) => if *constructor == Variant(variant_id) {
678673
Some(variant_id)
679674
} else {
@@ -782,7 +777,8 @@ fn check_local(cx: &mut MatchCheckCtxt, loc: &Local) {
782777
LocalFor => "`for` loop"
783778
};
784779

785-
match is_refutable(cx, loc.pat) {
780+
let mut static_inliner = StaticInliner { tcx: cx.tcx };
781+
match is_refutable(cx, static_inliner.fold_pat(loc.pat)) {
786782
Some(pat) => {
787783
span_err!(cx.tcx.sess, loc.pat.span, E0005,
788784
"refutable pattern in {} binding: `{}` not covered",

0 commit comments

Comments
 (0)