]> git.lizzy.rs Git - rust.git/blob - src/librustc/middle/const_eval.rs
Auto merge of #22541 - Manishearth:rollup, r=Gankro
[rust.git] / src / librustc / middle / const_eval.rs
1 // Copyright 2012-2014 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 #![allow(non_camel_case_types)]
12 #![allow(unsigned_negation)]
13
14 pub use self::const_val::*;
15
16 use metadata::csearch;
17 use middle::{astencode, def};
18 use middle::pat_util::def_to_path;
19 use middle::ty::{self, Ty};
20 use middle::astconv_util::{ast_ty_to_prim_ty};
21
22 use syntax::ast::{self, Expr};
23 use syntax::codemap::Span;
24 use syntax::parse::token::InternedString;
25 use syntax::ptr::P;
26 use syntax::{ast_map, ast_util, codemap};
27
28 use std::cmp::Ordering;
29 use std::collections::hash_map::Entry::Vacant;
30 use std::{i8, i16, i32, i64};
31 use std::rc::Rc;
32
33 fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> {
34     let opt_def = tcx.def_map.borrow().get(&e.id).cloned();
35     match opt_def {
36         Some(def::DefConst(def_id)) => {
37             lookup_const_by_id(tcx, def_id)
38         }
39         Some(def::DefVariant(enum_def, variant_def, _)) => {
40             lookup_variant_by_id(tcx, enum_def, variant_def)
41         }
42         _ => None
43     }
44 }
45
46 fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt,
47                             enum_def: ast::DefId,
48                             variant_def: ast::DefId)
49                             -> Option<&'a Expr> {
50     fn variant_expr<'a>(variants: &'a [P<ast::Variant>], id: ast::NodeId)
51                         -> Option<&'a Expr> {
52         for variant in variants {
53             if variant.node.id == id {
54                 return variant.node.disr_expr.as_ref().map(|e| &**e);
55             }
56         }
57         None
58     }
59
60     if ast_util::is_local(enum_def) {
61         match tcx.map.find(enum_def.node) {
62             None => None,
63             Some(ast_map::NodeItem(it)) => match it.node {
64                 ast::ItemEnum(ast::EnumDef { ref variants }, _) => {
65                     variant_expr(&variants[..], variant_def.node)
66                 }
67                 _ => None
68             },
69             Some(_) => None
70         }
71     } else {
72         match tcx.extern_const_variants.borrow().get(&variant_def) {
73             Some(&ast::DUMMY_NODE_ID) => return None,
74             Some(&expr_id) => {
75                 return Some(tcx.map.expect_expr(expr_id));
76             }
77             None => {}
78         }
79         let expr_id = match csearch::maybe_get_item_ast(tcx, enum_def,
80             box |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
81             csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node {
82                 ast::ItemEnum(ast::EnumDef { ref variants }, _) => {
83                     // NOTE this doesn't do the right thing, it compares inlined
84                     // NodeId's to the original variant_def's NodeId, but they
85                     // come from different crates, so they will likely never match.
86                     variant_expr(&variants[..], variant_def.node).map(|e| e.id)
87                 }
88                 _ => None
89             },
90             _ => None
91         };
92         tcx.extern_const_variants.borrow_mut().insert(variant_def,
93                                                       expr_id.unwrap_or(ast::DUMMY_NODE_ID));
94         expr_id.map(|id| tcx.map.expect_expr(id))
95     }
96 }
97
98 pub fn lookup_const_by_id<'a>(tcx: &'a ty::ctxt, def_id: ast::DefId)
99                           -> Option<&'a Expr> {
100     if ast_util::is_local(def_id) {
101         match tcx.map.find(def_id.node) {
102             None => None,
103             Some(ast_map::NodeItem(it)) => match it.node {
104                 ast::ItemConst(_, ref const_expr) => {
105                     Some(&**const_expr)
106                 }
107                 _ => None
108             },
109             Some(_) => None
110         }
111     } else {
112         match tcx.extern_const_statics.borrow().get(&def_id) {
113             Some(&ast::DUMMY_NODE_ID) => return None,
114             Some(&expr_id) => {
115                 return Some(tcx.map.expect_expr(expr_id));
116             }
117             None => {}
118         }
119         let expr_id = match csearch::maybe_get_item_ast(tcx, def_id,
120             box |a, b, c, d| astencode::decode_inlined_item(a, b, c, d)) {
121             csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node {
122                 ast::ItemConst(_, ref const_expr) => Some(const_expr.id),
123                 _ => None
124             },
125             _ => None
126         };
127         tcx.extern_const_statics.borrow_mut().insert(def_id,
128                                                      expr_id.unwrap_or(ast::DUMMY_NODE_ID));
129         expr_id.map(|id| tcx.map.expect_expr(id))
130     }
131 }
132
133 // FIXME (#33): this doesn't handle big integer/float literals correctly
134 // (nor does the rest of our literal handling).
135 #[derive(Clone, PartialEq)]
136 pub enum const_val {
137     const_float(f64),
138     const_int(i64),
139     const_uint(u64),
140     const_str(InternedString),
141     const_binary(Rc<Vec<u8> >),
142     const_bool(bool)
143 }
144
145 pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat> {
146     let pat = match expr.node {
147         ast::ExprTup(ref exprs) =>
148             ast::PatTup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect()),
149
150         ast::ExprCall(ref callee, ref args) => {
151             let def = tcx.def_map.borrow()[callee.id].clone();
152             if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) {
153                entry.insert(def);
154             }
155             let path = match def {
156                 def::DefStruct(def_id) => def_to_path(tcx, def_id),
157                 def::DefVariant(_, variant_did, _) => def_to_path(tcx, variant_did),
158                 _ => unreachable!()
159             };
160             let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect();
161             ast::PatEnum(path, Some(pats))
162         }
163
164         ast::ExprStruct(ref path, ref fields, None) => {
165             let field_pats = fields.iter().map(|field| codemap::Spanned {
166                 span: codemap::DUMMY_SP,
167                 node: ast::FieldPat {
168                     ident: field.ident.node,
169                     pat: const_expr_to_pat(tcx, &*field.expr, span),
170                     is_shorthand: false,
171                 },
172             }).collect();
173             ast::PatStruct(path.clone(), field_pats, false)
174         }
175
176         ast::ExprVec(ref exprs) => {
177             let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect();
178             ast::PatVec(pats, None, vec![])
179         }
180
181         ast::ExprPath(ref path) => {
182             let opt_def = tcx.def_map.borrow().get(&expr.id).cloned();
183             match opt_def {
184                 Some(def::DefStruct(..)) =>
185                     ast::PatStruct(path.clone(), vec![], false),
186                 Some(def::DefVariant(..)) =>
187                     ast::PatEnum(path.clone(), None),
188                 _ => {
189                     match lookup_const(tcx, expr) {
190                         Some(actual) => return const_expr_to_pat(tcx, actual, span),
191                         _ => unreachable!()
192                     }
193                 }
194             }
195         }
196
197         ast::ExprQPath(_) => {
198             match lookup_const(tcx, expr) {
199                 Some(actual) => return const_expr_to_pat(tcx, actual, span),
200                 _ => unreachable!()
201             }
202         }
203
204         _ => ast::PatLit(P(expr.clone()))
205     };
206     P(ast::Pat { id: expr.id, node: pat, span: span })
207 }
208
209 pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> const_val {
210     match eval_const_expr_partial(tcx, e, None) {
211         Ok(r) => r,
212         Err(s) => tcx.sess.span_fatal(e.span, &s[..])
213     }
214 }
215
216 pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
217                                      e: &Expr,
218                                      ty_hint: Option<Ty<'tcx>>)
219                                      -> Result<const_val, String> {
220     fn fromb(b: bool) -> Result<const_val, String> { Ok(const_int(b as i64)) }
221
222     let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e));
223
224     match e.node {
225       ast::ExprUnary(ast::UnNeg, ref inner) => {
226         match eval_const_expr_partial(tcx, &**inner, ety) {
227           Ok(const_float(f)) => Ok(const_float(-f)),
228           Ok(const_int(i)) => Ok(const_int(-i)),
229           Ok(const_uint(i)) => Ok(const_uint(-i)),
230           Ok(const_str(_)) => Err("negate on string".to_string()),
231           Ok(const_bool(_)) => Err("negate on boolean".to_string()),
232           ref err => ((*err).clone())
233         }
234       }
235       ast::ExprUnary(ast::UnNot, ref inner) => {
236         match eval_const_expr_partial(tcx, &**inner, ety) {
237           Ok(const_int(i)) => Ok(const_int(!i)),
238           Ok(const_uint(i)) => Ok(const_uint(!i)),
239           Ok(const_bool(b)) => Ok(const_bool(!b)),
240           _ => Err("not on float or string".to_string())
241         }
242       }
243       ast::ExprBinary(op, ref a, ref b) => {
244         let b_ty = match op.node {
245             ast::BiShl | ast::BiShr => Some(tcx.types.uint),
246             _ => ety
247         };
248         match (eval_const_expr_partial(tcx, &**a, ety),
249                eval_const_expr_partial(tcx, &**b, b_ty)) {
250           (Ok(const_float(a)), Ok(const_float(b))) => {
251             match op.node {
252               ast::BiAdd => Ok(const_float(a + b)),
253               ast::BiSub => Ok(const_float(a - b)),
254               ast::BiMul => Ok(const_float(a * b)),
255               ast::BiDiv => Ok(const_float(a / b)),
256               ast::BiRem => Ok(const_float(a % b)),
257               ast::BiEq => fromb(a == b),
258               ast::BiLt => fromb(a < b),
259               ast::BiLe => fromb(a <= b),
260               ast::BiNe => fromb(a != b),
261               ast::BiGe => fromb(a >= b),
262               ast::BiGt => fromb(a > b),
263               _ => Err("can't do this op on floats".to_string())
264             }
265           }
266           (Ok(const_int(a)), Ok(const_int(b))) => {
267             let is_a_min_value = |&:| {
268                 let int_ty = match ty::expr_ty_opt(tcx, e).map(|ty| &ty.sty) {
269                     Some(&ty::ty_int(int_ty)) => int_ty,
270                     _ => return false
271                 };
272                 let int_ty = if let ast::TyIs(_) = int_ty {
273                     tcx.sess.target.int_type
274                 } else {
275                     int_ty
276                 };
277                 match int_ty {
278                     ast::TyI8 => (a as i8) == i8::MIN,
279                     ast::TyI16 =>  (a as i16) == i16::MIN,
280                     ast::TyI32 =>  (a as i32) == i32::MIN,
281                     ast::TyI64 =>  (a as i64) == i64::MIN,
282                     ast::TyIs(_) => unreachable!()
283                 }
284             };
285             match op.node {
286               ast::BiAdd => Ok(const_int(a + b)),
287               ast::BiSub => Ok(const_int(a - b)),
288               ast::BiMul => Ok(const_int(a * b)),
289               ast::BiDiv => {
290                   if b == 0 {
291                       Err("attempted to divide by zero".to_string())
292                   } else if b == -1 && is_a_min_value() {
293                       Err("attempted to divide with overflow".to_string())
294                   } else {
295                       Ok(const_int(a / b))
296                   }
297               }
298               ast::BiRem => {
299                   if b == 0 {
300                       Err("attempted remainder with a divisor of zero".to_string())
301                   } else if b == -1 && is_a_min_value() {
302                       Err("attempted remainder with overflow".to_string())
303                   } else {
304                       Ok(const_int(a % b))
305                   }
306               }
307               ast::BiAnd | ast::BiBitAnd => Ok(const_int(a & b)),
308               ast::BiOr | ast::BiBitOr => Ok(const_int(a | b)),
309               ast::BiBitXor => Ok(const_int(a ^ b)),
310               ast::BiShl => Ok(const_int(a << b as uint)),
311               ast::BiShr => Ok(const_int(a >> b as uint)),
312               ast::BiEq => fromb(a == b),
313               ast::BiLt => fromb(a < b),
314               ast::BiLe => fromb(a <= b),
315               ast::BiNe => fromb(a != b),
316               ast::BiGe => fromb(a >= b),
317               ast::BiGt => fromb(a > b)
318             }
319           }
320           (Ok(const_uint(a)), Ok(const_uint(b))) => {
321             match op.node {
322               ast::BiAdd => Ok(const_uint(a + b)),
323               ast::BiSub => Ok(const_uint(a - b)),
324               ast::BiMul => Ok(const_uint(a * b)),
325               ast::BiDiv if b == 0 => {
326                   Err("attempted to divide by zero".to_string())
327               }
328               ast::BiDiv => Ok(const_uint(a / b)),
329               ast::BiRem if b == 0 => {
330                   Err("attempted remainder with a divisor of \
331                        zero".to_string())
332               }
333               ast::BiRem => Ok(const_uint(a % b)),
334               ast::BiAnd | ast::BiBitAnd => Ok(const_uint(a & b)),
335               ast::BiOr | ast::BiBitOr => Ok(const_uint(a | b)),
336               ast::BiBitXor => Ok(const_uint(a ^ b)),
337               ast::BiShl => Ok(const_uint(a << b as uint)),
338               ast::BiShr => Ok(const_uint(a >> b as uint)),
339               ast::BiEq => fromb(a == b),
340               ast::BiLt => fromb(a < b),
341               ast::BiLe => fromb(a <= b),
342               ast::BiNe => fromb(a != b),
343               ast::BiGe => fromb(a >= b),
344               ast::BiGt => fromb(a > b),
345             }
346           }
347           // shifts can have any integral type as their rhs
348           (Ok(const_int(a)), Ok(const_uint(b))) => {
349             match op.node {
350               ast::BiShl => Ok(const_int(a << b as uint)),
351               ast::BiShr => Ok(const_int(a >> b as uint)),
352               _ => Err("can't do this op on an int and uint".to_string())
353             }
354           }
355           (Ok(const_uint(a)), Ok(const_int(b))) => {
356             match op.node {
357               ast::BiShl => Ok(const_uint(a << b as uint)),
358               ast::BiShr => Ok(const_uint(a >> b as uint)),
359               _ => Err("can't do this op on a uint and int".to_string())
360             }
361           }
362           (Ok(const_bool(a)), Ok(const_bool(b))) => {
363             Ok(const_bool(match op.node {
364               ast::BiAnd => a && b,
365               ast::BiOr => a || b,
366               ast::BiBitXor => a ^ b,
367               ast::BiBitAnd => a & b,
368               ast::BiBitOr => a | b,
369               ast::BiEq => a == b,
370               ast::BiNe => a != b,
371               _ => return Err("can't do this op on bools".to_string())
372              }))
373           }
374           _ => Err("bad operands for binary".to_string())
375         }
376       }
377       ast::ExprCast(ref base, ref target_ty) => {
378         // This tends to get called w/o the type actually having been
379         // populated in the ctxt, which was causing things to blow up
380         // (#5900). Fall back to doing a limited lookup to get past it.
381         let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
382                 .unwrap_or_else(|| {
383                     tcx.sess.span_fatal(target_ty.span,
384                                         "target type not found for const cast")
385                 });
386         // Prefer known type to noop, but always have a type hint.
387         let base_hint = ty::expr_ty_opt(tcx, &**base).unwrap_or(ety);
388         let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
389         cast_const(val, ety)
390       }
391       ast::ExprPath(_) | ast::ExprQPath(_) => {
392           let opt_def = tcx.def_map.borrow().get(&e.id).cloned();
393           let (const_expr, const_ty) = match opt_def {
394               Some(def::DefConst(def_id)) => {
395                   if ast_util::is_local(def_id) {
396                       match tcx.map.find(def_id.node) {
397                           Some(ast_map::NodeItem(it)) => match it.node {
398                               ast::ItemConst(ref ty, ref expr) => {
399                                   (Some(&**expr), Some(&**ty))
400                               }
401                               _ => (None, None)
402                           },
403                           _ => (None, None)
404                       }
405                   } else {
406                       (lookup_const_by_id(tcx, def_id), None)
407                   }
408               }
409               Some(def::DefVariant(enum_def, variant_def, _)) => {
410                   (lookup_variant_by_id(tcx, enum_def, variant_def), None)
411               }
412               _ => (None, None)
413           };
414           let const_expr = match const_expr {
415               Some(actual_e) => actual_e,
416               None => return Err("non-constant path in constant expr".to_string())
417           };
418           let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
419           eval_const_expr_partial(tcx, const_expr, ety)
420       }
421       ast::ExprLit(ref lit) => {
422           Ok(lit_to_const(&**lit, ety))
423       }
424       ast::ExprParen(ref e)     => eval_const_expr_partial(tcx, &**e, ety),
425       ast::ExprBlock(ref block) => {
426         match block.expr {
427             Some(ref expr) => eval_const_expr_partial(tcx, &**expr, ety),
428             None => Ok(const_int(0i64))
429         }
430       }
431       ast::ExprTupField(ref base, index) => {
432         // Get the base tuple if it is constant
433         if let Some(&ast::ExprTup(ref fields)) = lookup_const(tcx, &**base).map(|s| &s.node) {
434             // Check that the given index is within bounds and evaluate its value
435             if fields.len() > index.node {
436                 return eval_const_expr_partial(tcx, &*fields[index.node], None)
437             } else {
438                 return Err("tuple index out of bounds".to_string())
439             }
440         }
441
442         Err("non-constant struct in constant expr".to_string())
443       }
444       ast::ExprField(ref base, field_name) => {
445         // Get the base expression if it is a struct and it is constant
446         if let Some(&ast::ExprStruct(_, ref fields, _)) = lookup_const(tcx, &**base)
447                                                             .map(|s| &s.node) {
448             // Check that the given field exists and evaluate it
449             if let Some(f) = fields.iter().find(|f|
450                                            f.ident.node.as_str() == field_name.node.as_str()) {
451                 return eval_const_expr_partial(tcx, &*f.expr, None)
452             } else {
453                 return Err("nonexistent struct field".to_string())
454             }
455         }
456
457         Err("non-constant struct in constant expr".to_string())
458       }
459       _ => Err("unsupported constant expr".to_string())
460     }
461 }
462
463 fn cast_const(val: const_val, ty: Ty) -> Result<const_val, String> {
464     macro_rules! define_casts {
465         ($($ty_pat:pat => (
466             $intermediate_ty:ty,
467             $const_type:ident,
468             $target_ty:ty
469         )),*) => (match ty.sty {
470             $($ty_pat => {
471                 match val {
472                     const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
473                     const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
474                     const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
475                     const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
476                     _ => Err(concat!("can't cast this type to ",
477                                      stringify!($const_type)).to_string())
478                 }
479             },)*
480             _ => Err("can't cast this type".to_string())
481         })
482     }
483
484     define_casts!{
485         ty::ty_int(ast::TyIs(_)) => (int, const_int, i64),
486         ty::ty_int(ast::TyI8) => (i8, const_int, i64),
487         ty::ty_int(ast::TyI16) => (i16, const_int, i64),
488         ty::ty_int(ast::TyI32) => (i32, const_int, i64),
489         ty::ty_int(ast::TyI64) => (i64, const_int, i64),
490         ty::ty_uint(ast::TyUs(_)) => (uint, const_uint, u64),
491         ty::ty_uint(ast::TyU8) => (u8, const_uint, u64),
492         ty::ty_uint(ast::TyU16) => (u16, const_uint, u64),
493         ty::ty_uint(ast::TyU32) => (u32, const_uint, u64),
494         ty::ty_uint(ast::TyU64) => (u64, const_uint, u64),
495         ty::ty_float(ast::TyF32) => (f32, const_float, f64),
496         ty::ty_float(ast::TyF64) => (f64, const_float, f64)
497     }
498 }
499
500 fn lit_to_const(lit: &ast::Lit, ty_hint: Option<Ty>) -> const_val {
501     match lit.node {
502         ast::LitStr(ref s, _) => const_str((*s).clone()),
503         ast::LitBinary(ref data) => {
504             const_binary(data.clone())
505         }
506         ast::LitByte(n) => const_uint(n as u64),
507         ast::LitChar(n) => const_uint(n as u64),
508         ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => const_int(n as i64),
509         ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => {
510             match ty_hint.map(|ty| &ty.sty) {
511                 Some(&ty::ty_uint(_)) => const_uint(n),
512                 _ => const_int(n as i64)
513             }
514         }
515         ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) |
516         ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => const_int(-(n as i64)),
517         ast::LitInt(n, ast::UnsignedIntLit(_)) => const_uint(n),
518         ast::LitFloat(ref n, _) |
519         ast::LitFloatUnsuffixed(ref n) => {
520             const_float(n.parse::<f64>().unwrap() as f64)
521         }
522         ast::LitBool(b) => const_bool(b)
523     }
524 }
525
526 pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<Ordering> {
527     Some(match (a, b) {
528         (&const_int(a), &const_int(b)) => a.cmp(&b),
529         (&const_uint(a), &const_uint(b)) => a.cmp(&b),
530         (&const_float(a), &const_float(b)) => {
531             // This is pretty bad but it is the existing behavior.
532             if a == b {
533                 Ordering::Equal
534             } else if a < b {
535                 Ordering::Less
536             } else {
537                 Ordering::Greater
538             }
539         }
540         (&const_str(ref a), &const_str(ref b)) => a.cmp(b),
541         (&const_bool(a), &const_bool(b)) => a.cmp(&b),
542         (&const_binary(ref a), &const_binary(ref b)) => a.cmp(b),
543         _ => return None
544     })
545 }
546
547 pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
548                                a: &Expr,
549                                b: &Expr,
550                                ty_hint: Option<Ty<'tcx>>)
551                                -> Option<Ordering> {
552     let a = match eval_const_expr_partial(tcx, a, ty_hint) {
553         Ok(a) => a,
554         Err(s) => {
555             tcx.sess.span_err(a.span, &s[..]);
556             return None;
557         }
558     };
559     let b = match eval_const_expr_partial(tcx, b, ty_hint) {
560         Ok(b) => b,
561         Err(s) => {
562             tcx.sess.span_err(b.span, &s[..]);
563             return None;
564         }
565     };
566     compare_const_vals(&a, &b)
567 }