]> git.proxmox.com Git - rustc.git/blob - src/librustc_mir/hair/cx/pattern.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / librustc_mir / hair / cx / pattern.rs
1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use hair::*;
12 use hair::cx::Cx;
13 use rustc_data_structures::fnv::FnvHashMap;
14 use rustc_const_eval as const_eval;
15 use rustc::hir::def::Def;
16 use rustc::hir::pat_util::{pat_is_resolved_const, pat_is_binding};
17 use rustc::ty::{self, Ty};
18 use rustc::mir::repr::*;
19 use rustc::hir::{self, PatKind};
20 use syntax::ast;
21 use syntax::codemap::Span;
22 use syntax::ptr::P;
23
24 /// When there are multiple patterns in a single arm, each one has its
25 /// own node-ids for the bindings. References to the variables always
26 /// use the node-ids from the first pattern in the arm, so we just
27 /// remap the ids for all subsequent bindings to the first one.
28 ///
29 /// Example:
30 /// ```
31 /// match foo {
32 /// Test1(flavor /* def 1 */) |
33 /// Test2(flavor /* def 2 */) if flavor /* ref 1 */.is_tasty() => { ... }
34 /// _ => { ... }
35 /// }
36 /// ```
37 struct PatCx<'patcx, 'cx: 'patcx, 'tcx: 'cx> {
38 cx: &'patcx mut Cx<'cx, 'tcx>,
39 binding_map: Option<&'patcx FnvHashMap<ast::Name, ast::NodeId>>,
40 }
41
42 impl<'cx, 'tcx> Cx<'cx, 'tcx> {
43 pub fn irrefutable_pat(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
44 PatCx::new(self, None).to_pattern(pat)
45 }
46
47 pub fn refutable_pat(&mut self,
48 binding_map: Option<&FnvHashMap<ast::Name, ast::NodeId>>,
49 pat: &hir::Pat)
50 -> Pattern<'tcx> {
51 PatCx::new(self, binding_map).to_pattern(pat)
52 }
53 }
54
55 impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> {
56 fn new(cx: &'patcx mut Cx<'cx, 'tcx>,
57 binding_map: Option<&'patcx FnvHashMap<ast::Name, ast::NodeId>>)
58 -> PatCx<'patcx, 'cx, 'tcx> {
59 PatCx {
60 cx: cx,
61 binding_map: binding_map,
62 }
63 }
64
65 fn to_pattern(&mut self, pat: &hir::Pat) -> Pattern<'tcx> {
66 let mut ty = self.cx.tcx.node_id_to_type(pat.id);
67
68 let kind = match pat.node {
69 PatKind::Wild => PatternKind::Wild,
70
71 PatKind::Lit(ref value) => {
72 let value = const_eval::eval_const_expr(self.cx.tcx, value);
73 PatternKind::Constant { value: value }
74 }
75
76 PatKind::Range(ref lo, ref hi) => {
77 let lo = const_eval::eval_const_expr(self.cx.tcx, lo);
78 let lo = Literal::Value { value: lo };
79 let hi = const_eval::eval_const_expr(self.cx.tcx, hi);
80 let hi = Literal::Value { value: hi };
81 PatternKind::Range { lo: lo, hi: hi }
82 },
83
84 PatKind::Path(..) | PatKind::Ident(..) | PatKind::QPath(..)
85 if pat_is_resolved_const(&self.cx.tcx.def_map.borrow(), pat) =>
86 {
87 let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
88 match def {
89 Def::Const(def_id) | Def::AssociatedConst(def_id) => {
90 let substs = Some(self.cx.tcx.node_id_item_substs(pat.id).substs);
91 match const_eval::lookup_const_by_id(self.cx.tcx, def_id, substs) {
92 Some((const_expr, _const_ty)) => {
93 match const_eval::const_expr_to_pat(self.cx.tcx,
94 const_expr,
95 pat.id,
96 pat.span) {
97 Ok(pat) =>
98 return self.to_pattern(&pat),
99 Err(_) =>
100 span_bug!(
101 pat.span, "illegal constant"),
102 }
103 }
104 None => {
105 span_bug!(
106 pat.span,
107 "cannot eval constant: {:?}",
108 def_id)
109 }
110 }
111 }
112 _ =>
113 span_bug!(
114 pat.span,
115 "def not a constant: {:?}",
116 def),
117 }
118 }
119
120 PatKind::Ref(ref subpattern, _) |
121 PatKind::Box(ref subpattern) => {
122 PatternKind::Deref { subpattern: self.to_pattern(subpattern) }
123 }
124
125 PatKind::Vec(ref prefix, ref slice, ref suffix) => {
126 let ty = self.cx.tcx.node_id_to_type(pat.id);
127 match ty.sty {
128 ty::TyRef(_, mt) =>
129 PatternKind::Deref {
130 subpattern: Pattern {
131 ty: mt.ty,
132 span: pat.span,
133 kind: Box::new(self.slice_or_array_pattern(pat.span, mt.ty, prefix,
134 slice, suffix)),
135 },
136 },
137
138 ty::TySlice(..) |
139 ty::TyArray(..) =>
140 self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix),
141
142 ref sty =>
143 span_bug!(
144 pat.span,
145 "unexpanded type for vector pattern: {:?}",
146 sty),
147 }
148 }
149
150 PatKind::Tup(ref subpatterns) => {
151 let subpatterns =
152 subpatterns.iter()
153 .enumerate()
154 .map(|(i, subpattern)| FieldPattern {
155 field: Field::new(i),
156 pattern: self.to_pattern(subpattern),
157 })
158 .collect();
159
160 PatternKind::Leaf { subpatterns: subpatterns }
161 }
162
163 PatKind::Ident(bm, ref ident, ref sub)
164 if pat_is_binding(&self.cx.tcx.def_map.borrow(), pat) =>
165 {
166 let id = match self.binding_map {
167 None => pat.id,
168 Some(ref map) => map[&ident.node.name],
169 };
170 let var_ty = self.cx.tcx.node_id_to_type(pat.id);
171 let region = match var_ty.sty {
172 ty::TyRef(&r, _) => Some(r),
173 _ => None,
174 };
175 let (mutability, mode) = match bm {
176 hir::BindByValue(hir::MutMutable) =>
177 (Mutability::Mut, BindingMode::ByValue),
178 hir::BindByValue(hir::MutImmutable) =>
179 (Mutability::Not, BindingMode::ByValue),
180 hir::BindByRef(hir::MutMutable) =>
181 (Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Mut)),
182 hir::BindByRef(hir::MutImmutable) =>
183 (Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Shared)),
184 };
185
186 // A ref x pattern is the same node used for x, and as such it has
187 // x's type, which is &T, where we want T (the type being matched).
188 if let hir::BindByRef(_) = bm {
189 if let ty::TyRef(_, mt) = ty.sty {
190 ty = mt.ty;
191 } else {
192 bug!("`ref {}` has wrong type {}", ident.node, ty);
193 }
194 }
195
196 PatternKind::Binding {
197 mutability: mutability,
198 mode: mode,
199 name: ident.node.name,
200 var: id,
201 ty: var_ty,
202 subpattern: self.to_opt_pattern(sub),
203 }
204 }
205
206 PatKind::Ident(..) | PatKind::Path(..) => {
207 self.variant_or_leaf(pat, vec![])
208 }
209
210 PatKind::TupleStruct(_, ref opt_subpatterns) => {
211 let subpatterns =
212 opt_subpatterns.iter()
213 .flat_map(|v| v.iter())
214 .enumerate()
215 .map(|(i, field)| FieldPattern {
216 field: Field::new(i),
217 pattern: self.to_pattern(field),
218 })
219 .collect();
220 self.variant_or_leaf(pat, subpatterns)
221 }
222
223 PatKind::Struct(_, ref fields, _) => {
224 let pat_ty = self.cx.tcx.node_id_to_type(pat.id);
225 let adt_def = match pat_ty.sty {
226 ty::TyStruct(adt_def, _) | ty::TyEnum(adt_def, _) => adt_def,
227 _ => {
228 span_bug!(
229 pat.span,
230 "struct pattern not applied to struct or enum");
231 }
232 };
233
234 let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
235 let variant_def = adt_def.variant_of_def(def);
236
237 let subpatterns =
238 fields.iter()
239 .map(|field| {
240 let index = variant_def.index_of_field_named(field.node.name);
241 let index = index.unwrap_or_else(|| {
242 span_bug!(
243 pat.span,
244 "no field with name {:?}",
245 field.node.name);
246 });
247 FieldPattern {
248 field: Field::new(index),
249 pattern: self.to_pattern(&field.node.pat),
250 }
251 })
252 .collect();
253
254 self.variant_or_leaf(pat, subpatterns)
255 }
256
257 PatKind::QPath(..) => {
258 span_bug!(pat.span, "unexpanded macro or bad constant etc");
259 }
260 };
261
262 Pattern {
263 span: pat.span,
264 ty: ty,
265 kind: Box::new(kind),
266 }
267 }
268
269 fn to_patterns(&mut self, pats: &[P<hir::Pat>]) -> Vec<Pattern<'tcx>> {
270 pats.iter().map(|p| self.to_pattern(p)).collect()
271 }
272
273 fn to_opt_pattern(&mut self, pat: &Option<P<hir::Pat>>) -> Option<Pattern<'tcx>> {
274 pat.as_ref().map(|p| self.to_pattern(p))
275 }
276
277 fn slice_or_array_pattern(&mut self,
278 span: Span,
279 ty: Ty<'tcx>,
280 prefix: &[P<hir::Pat>],
281 slice: &Option<P<hir::Pat>>,
282 suffix: &[P<hir::Pat>])
283 -> PatternKind<'tcx> {
284 match ty.sty {
285 ty::TySlice(..) => {
286 // matching a slice or fixed-length array
287 PatternKind::Slice {
288 prefix: self.to_patterns(prefix),
289 slice: self.to_opt_pattern(slice),
290 suffix: self.to_patterns(suffix),
291 }
292 }
293
294 ty::TyArray(_, len) => {
295 // fixed-length array
296 assert!(len >= prefix.len() + suffix.len());
297 PatternKind::Array {
298 prefix: self.to_patterns(prefix),
299 slice: self.to_opt_pattern(slice),
300 suffix: self.to_patterns(suffix),
301 }
302 }
303
304 _ => {
305 span_bug!(span, "unexpanded macro or bad constant etc");
306 }
307 }
308 }
309
310 fn variant_or_leaf(&mut self,
311 pat: &hir::Pat,
312 subpatterns: Vec<FieldPattern<'tcx>>)
313 -> PatternKind<'tcx> {
314 let def = self.cx.tcx.def_map.borrow().get(&pat.id).unwrap().full_def();
315 match def {
316 Def::Variant(enum_id, variant_id) => {
317 let adt_def = self.cx.tcx.lookup_adt_def(enum_id);
318 if adt_def.variants.len() > 1 {
319 PatternKind::Variant {
320 adt_def: adt_def,
321 variant_index: adt_def.variant_index_with_id(variant_id),
322 subpatterns: subpatterns,
323 }
324 } else {
325 PatternKind::Leaf { subpatterns: subpatterns }
326 }
327 }
328
329 Def::Struct(..) | Def::TyAlias(..) => {
330 PatternKind::Leaf { subpatterns: subpatterns }
331 }
332
333 _ => {
334 span_bug!(pat.span, "inappropriate def for pattern: {:?}", def);
335 }
336 }
337 }
338 }