]> git.proxmox.com Git - rustc.git/blob - src/librustc/middle/const_eval.rs
Imported Upstream version 1.1.0+dfsg1
[rustc.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 self::ErrKind::*;
17
18 use metadata::csearch;
19 use middle::{astencode, def, infer, subst, traits};
20 use middle::pat_util::def_to_path;
21 use middle::ty::{self, Ty};
22 use middle::astconv_util::ast_ty_to_prim_ty;
23 use util::num::ToPrimitive;
24 use util::ppaux::Repr;
25
26 use syntax::ast::{self, Expr};
27 use syntax::codemap::Span;
28 use syntax::feature_gate;
29 use syntax::parse::token::InternedString;
30 use syntax::ptr::P;
31 use syntax::{ast_map, ast_util, codemap};
32
33 use std::borrow::{Cow, IntoCow};
34 use std::num::wrapping::OverflowingOps;
35 use std::cmp::Ordering;
36 use std::collections::hash_map::Entry::Vacant;
37 use std::{i8, i16, i32, i64};
38 use std::rc::Rc;
39
40 fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> {
41 let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
42 match opt_def {
43 Some(def::DefConst(def_id)) |
44 Some(def::DefAssociatedConst(def_id, _)) => {
45 lookup_const_by_id(tcx, def_id, Some(e.id))
46 }
47 Some(def::DefVariant(enum_def, variant_def, _)) => {
48 lookup_variant_by_id(tcx, enum_def, variant_def)
49 }
50 _ => None
51 }
52 }
53
54 fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt,
55 enum_def: ast::DefId,
56 variant_def: ast::DefId)
57 -> Option<&'a Expr> {
58 fn variant_expr<'a>(variants: &'a [P<ast::Variant>], id: ast::NodeId)
59 -> Option<&'a Expr> {
60 for variant in variants {
61 if variant.node.id == id {
62 return variant.node.disr_expr.as_ref().map(|e| &**e);
63 }
64 }
65 None
66 }
67
68 if ast_util::is_local(enum_def) {
69 match tcx.map.find(enum_def.node) {
70 None => None,
71 Some(ast_map::NodeItem(it)) => match it.node {
72 ast::ItemEnum(ast::EnumDef { ref variants }, _) => {
73 variant_expr(&variants[..], variant_def.node)
74 }
75 _ => None
76 },
77 Some(_) => None
78 }
79 } else {
80 match tcx.extern_const_variants.borrow().get(&variant_def) {
81 Some(&ast::DUMMY_NODE_ID) => return None,
82 Some(&expr_id) => {
83 return Some(tcx.map.expect_expr(expr_id));
84 }
85 None => {}
86 }
87 let expr_id = match csearch::maybe_get_item_ast(tcx, enum_def,
88 Box::new(|a, b, c, d| astencode::decode_inlined_item(a, b, c, d))) {
89 csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node {
90 ast::ItemEnum(ast::EnumDef { ref variants }, _) => {
91 // NOTE this doesn't do the right thing, it compares inlined
92 // NodeId's to the original variant_def's NodeId, but they
93 // come from different crates, so they will likely never match.
94 variant_expr(&variants[..], variant_def.node).map(|e| e.id)
95 }
96 _ => None
97 },
98 _ => None
99 };
100 tcx.extern_const_variants.borrow_mut().insert(variant_def,
101 expr_id.unwrap_or(ast::DUMMY_NODE_ID));
102 expr_id.map(|id| tcx.map.expect_expr(id))
103 }
104 }
105
106 pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
107 def_id: ast::DefId,
108 maybe_ref_id: Option<ast::NodeId>)
109 -> Option<&'tcx Expr> {
110 if ast_util::is_local(def_id) {
111 match tcx.map.find(def_id.node) {
112 None => None,
113 Some(ast_map::NodeItem(it)) => match it.node {
114 ast::ItemConst(_, ref const_expr) => {
115 Some(&*const_expr)
116 }
117 _ => None
118 },
119 Some(ast_map::NodeTraitItem(ti)) => match ti.node {
120 ast::ConstTraitItem(_, _) => {
121 match maybe_ref_id {
122 // If we have a trait item, and we know the expression
123 // that's the source of the obligation to resolve it,
124 // `resolve_trait_associated_const` will select an impl
125 // or the default.
126 Some(ref_id) => {
127 let trait_id = ty::trait_of_item(tcx, def_id)
128 .unwrap();
129 resolve_trait_associated_const(tcx, ti, trait_id,
130 ref_id)
131 }
132 // Technically, without knowing anything about the
133 // expression that generates the obligation, we could
134 // still return the default if there is one. However,
135 // it's safer to return `None` than to return some value
136 // that may differ from what you would get from
137 // correctly selecting an impl.
138 None => None
139 }
140 }
141 _ => None
142 },
143 Some(ast_map::NodeImplItem(ii)) => match ii.node {
144 ast::ConstImplItem(_, ref expr) => {
145 Some(&*expr)
146 }
147 _ => None
148 },
149 Some(_) => None
150 }
151 } else {
152 match tcx.extern_const_statics.borrow().get(&def_id) {
153 Some(&ast::DUMMY_NODE_ID) => return None,
154 Some(&expr_id) => {
155 return Some(tcx.map.expect_expr(expr_id));
156 }
157 None => {}
158 }
159 let mut used_ref_id = false;
160 let expr_id = match csearch::maybe_get_item_ast(tcx, def_id,
161 Box::new(|a, b, c, d| astencode::decode_inlined_item(a, b, c, d))) {
162 csearch::FoundAst::Found(&ast::IIItem(ref item)) => match item.node {
163 ast::ItemConst(_, ref const_expr) => Some(const_expr.id),
164 _ => None
165 },
166 csearch::FoundAst::Found(&ast::IITraitItem(trait_id, ref ti)) => match ti.node {
167 ast::ConstTraitItem(_, _) => {
168 used_ref_id = true;
169 match maybe_ref_id {
170 // As mentioned in the comments above for in-crate
171 // constants, we only try to find the expression for
172 // a trait-associated const if the caller gives us
173 // the expression that refers to it.
174 Some(ref_id) => {
175 resolve_trait_associated_const(tcx, ti, trait_id,
176 ref_id).map(|e| e.id)
177 }
178 None => None
179 }
180 }
181 _ => None
182 },
183 csearch::FoundAst::Found(&ast::IIImplItem(_, ref ii)) => match ii.node {
184 ast::ConstImplItem(_, ref expr) => Some(expr.id),
185 _ => None
186 },
187 _ => None
188 };
189 // If we used the reference expression, particularly to choose an impl
190 // of a trait-associated const, don't cache that, because the next
191 // lookup with the same def_id may yield a different result.
192 if !used_ref_id {
193 tcx.extern_const_statics
194 .borrow_mut().insert(def_id,
195 expr_id.unwrap_or(ast::DUMMY_NODE_ID));
196 }
197 expr_id.map(|id| tcx.map.expect_expr(id))
198 }
199 }
200
201 #[derive(Clone, PartialEq)]
202 pub enum const_val {
203 const_float(f64),
204 const_int(i64),
205 const_uint(u64),
206 const_str(InternedString),
207 const_binary(Rc<Vec<u8>>),
208 const_bool(bool),
209 Struct(ast::NodeId),
210 Tuple(ast::NodeId)
211 }
212
213 pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat> {
214 let pat = match expr.node {
215 ast::ExprTup(ref exprs) =>
216 ast::PatTup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect()),
217
218 ast::ExprCall(ref callee, ref args) => {
219 let def = *tcx.def_map.borrow().get(&callee.id).unwrap();
220 if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) {
221 entry.insert(def);
222 }
223 let path = match def.full_def() {
224 def::DefStruct(def_id) => def_to_path(tcx, def_id),
225 def::DefVariant(_, variant_did, _) => def_to_path(tcx, variant_did),
226 _ => unreachable!()
227 };
228 let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect();
229 ast::PatEnum(path, Some(pats))
230 }
231
232 ast::ExprStruct(ref path, ref fields, None) => {
233 let field_pats = fields.iter().map(|field| codemap::Spanned {
234 span: codemap::DUMMY_SP,
235 node: ast::FieldPat {
236 ident: field.ident.node,
237 pat: const_expr_to_pat(tcx, &*field.expr, span),
238 is_shorthand: false,
239 },
240 }).collect();
241 ast::PatStruct(path.clone(), field_pats, false)
242 }
243
244 ast::ExprVec(ref exprs) => {
245 let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect();
246 ast::PatVec(pats, None, vec![])
247 }
248
249 ast::ExprPath(_, ref path) => {
250 let opt_def = tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def());
251 match opt_def {
252 Some(def::DefStruct(..)) =>
253 ast::PatStruct(path.clone(), vec![], false),
254 Some(def::DefVariant(..)) =>
255 ast::PatEnum(path.clone(), None),
256 _ => {
257 match lookup_const(tcx, expr) {
258 Some(actual) => return const_expr_to_pat(tcx, actual, span),
259 _ => unreachable!()
260 }
261 }
262 }
263 }
264
265 _ => ast::PatLit(P(expr.clone()))
266 };
267 P(ast::Pat { id: expr.id, node: pat, span: span })
268 }
269
270 pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> const_val {
271 match eval_const_expr_partial(tcx, e, None) {
272 Ok(r) => r,
273 Err(s) => tcx.sess.span_fatal(s.span, &s.description())
274 }
275 }
276
277
278 #[derive(Clone)]
279 pub struct ConstEvalErr {
280 pub span: Span,
281 pub kind: ErrKind,
282 }
283
284 #[derive(Clone)]
285 pub enum ErrKind {
286 CannotCast,
287 CannotCastTo(&'static str),
288 InvalidOpForBools(ast::BinOp_),
289 InvalidOpForFloats(ast::BinOp_),
290 InvalidOpForIntUint(ast::BinOp_),
291 InvalidOpForUintInt(ast::BinOp_),
292 NegateOnString,
293 NegateOnBoolean,
294 NegateOnBinary,
295 NegateOnStruct,
296 NegateOnTuple,
297 NotOnFloat,
298 NotOnString,
299 NotOnBinary,
300 NotOnStruct,
301 NotOnTuple,
302
303 NegateWithOverflow(i64),
304 AddiWithOverflow(i64, i64),
305 SubiWithOverflow(i64, i64),
306 MuliWithOverflow(i64, i64),
307 AdduWithOverflow(u64, u64),
308 SubuWithOverflow(u64, u64),
309 MuluWithOverflow(u64, u64),
310 DivideByZero,
311 DivideWithOverflow,
312 ModuloByZero,
313 ModuloWithOverflow,
314 ShiftLeftWithOverflow,
315 ShiftRightWithOverflow,
316 MissingStructField,
317 NonConstPath,
318 ExpectedConstTuple,
319 ExpectedConstStruct,
320 TupleIndexOutOfBounds,
321
322 MiscBinaryOp,
323 MiscCatchAll,
324 }
325
326 impl ConstEvalErr {
327 pub fn description(&self) -> Cow<str> {
328 use self::ErrKind::*;
329
330 match self.kind {
331 CannotCast => "can't cast this type".into_cow(),
332 CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(),
333 InvalidOpForBools(_) => "can't do this op on bools".into_cow(),
334 InvalidOpForFloats(_) => "can't do this op on floats".into_cow(),
335 InvalidOpForIntUint(..) => "can't do this op on an isize and usize".into_cow(),
336 InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(),
337 NegateOnString => "negate on string".into_cow(),
338 NegateOnBoolean => "negate on boolean".into_cow(),
339 NegateOnBinary => "negate on binary literal".into_cow(),
340 NegateOnStruct => "negate on struct".into_cow(),
341 NegateOnTuple => "negate on tuple".into_cow(),
342 NotOnFloat => "not on float or string".into_cow(),
343 NotOnString => "not on float or string".into_cow(),
344 NotOnBinary => "not on binary literal".into_cow(),
345 NotOnStruct => "not on struct".into_cow(),
346 NotOnTuple => "not on tuple".into_cow(),
347
348 NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(),
349 AddiWithOverflow(..) => "attempted to add with overflow".into_cow(),
350 SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(),
351 MuliWithOverflow(..) => "attempted to mul with overflow".into_cow(),
352 AdduWithOverflow(..) => "attempted to add with overflow".into_cow(),
353 SubuWithOverflow(..) => "attempted to sub with overflow".into_cow(),
354 MuluWithOverflow(..) => "attempted to mul with overflow".into_cow(),
355 DivideByZero => "attempted to divide by zero".into_cow(),
356 DivideWithOverflow => "attempted to divide with overflow".into_cow(),
357 ModuloByZero => "attempted remainder with a divisor of zero".into_cow(),
358 ModuloWithOverflow => "attempted remainder with overflow".into_cow(),
359 ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(),
360 ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(),
361 MissingStructField => "nonexistent struct field".into_cow(),
362 NonConstPath => "non-constant path in constant expr".into_cow(),
363 ExpectedConstTuple => "expected constant tuple".into_cow(),
364 ExpectedConstStruct => "expected constant struct".into_cow(),
365 TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(),
366
367 MiscBinaryOp => "bad operands for binary".into_cow(),
368 MiscCatchAll => "unsupported constant expr".into_cow(),
369 }
370 }
371 }
372
373 pub type EvalResult = Result<const_val, ConstEvalErr>;
374 pub type CastResult = Result<const_val, ErrKind>;
375
376 #[derive(Copy, Clone, PartialEq, Debug)]
377 pub enum IntTy { I8, I16, I32, I64 }
378 #[derive(Copy, Clone, PartialEq, Debug)]
379 pub enum UintTy { U8, U16, U32, U64 }
380
381 impl IntTy {
382 pub fn from(tcx: &ty::ctxt, t: ast::IntTy) -> IntTy {
383 let t = if let ast::TyIs = t {
384 tcx.sess.target.int_type
385 } else {
386 t
387 };
388 match t {
389 ast::TyIs => unreachable!(),
390 ast::TyI8 => IntTy::I8,
391 ast::TyI16 => IntTy::I16,
392 ast::TyI32 => IntTy::I32,
393 ast::TyI64 => IntTy::I64,
394 }
395 }
396 }
397
398 impl UintTy {
399 pub fn from(tcx: &ty::ctxt, t: ast::UintTy) -> UintTy {
400 let t = if let ast::TyUs = t {
401 tcx.sess.target.uint_type
402 } else {
403 t
404 };
405 match t {
406 ast::TyUs => unreachable!(),
407 ast::TyU8 => UintTy::U8,
408 ast::TyU16 => UintTy::U16,
409 ast::TyU32 => UintTy::U32,
410 ast::TyU64 => UintTy::U64,
411 }
412 }
413 }
414
415 macro_rules! signal {
416 ($e:expr, $exn:expr) => {
417 return Err(ConstEvalErr { span: $e.span, kind: $exn })
418 }
419 }
420
421 // The const_{int,uint}_checked_{neg,add,sub,mul,div,shl,shr} family
422 // of functions catch and signal overflow errors during constant
423 // evaluation.
424 //
425 // They all take the operator's arguments (`a` and `b` if binary), the
426 // overall expression (`e`) and, if available, whole expression's
427 // concrete type (`opt_ety`).
428 //
429 // If the whole expression's concrete type is None, then this is a
430 // constant evaluation happening before type check (e.g. in the check
431 // to confirm that a pattern range's left-side is not greater than its
432 // right-side). We do not do arithmetic modulo the type's bitwidth in
433 // such a case; we just do 64-bit arithmetic and assume that later
434 // passes will do it again with the type information, and thus do the
435 // overflow checks then.
436
437 pub fn const_int_checked_neg<'a>(
438 a: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
439
440 let (min,max) = match opt_ety {
441 // (-i8::MIN is itself not an i8, etc, but this is an easy way
442 // to allow literals to pass the check. Of course that does
443 // not work for i64::MIN.)
444 Some(IntTy::I8) => (-(i8::MAX as i64), -(i8::MIN as i64)),
445 Some(IntTy::I16) => (-(i16::MAX as i64), -(i16::MIN as i64)),
446 Some(IntTy::I32) => (-(i32::MAX as i64), -(i32::MIN as i64)),
447 None | Some(IntTy::I64) => (-i64::MAX, -(i64::MIN+1)),
448 };
449
450 let oflo = a < min || a > max;
451 if oflo {
452 signal!(e, NegateWithOverflow(a));
453 } else {
454 Ok(const_int(-a))
455 }
456 }
457
458 pub fn const_uint_checked_neg<'a>(
459 a: u64, _e: &'a Expr, _opt_ety: Option<UintTy>) -> EvalResult {
460 // This always succeeds, and by definition, returns `(!a)+1`.
461 Ok(const_uint((!a).wrapping_add(1)))
462 }
463
464 macro_rules! overflow_checking_body {
465 ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident,
466 lhs: $to_8_lhs:ident $to_16_lhs:ident $to_32_lhs:ident,
467 rhs: $to_8_rhs:ident $to_16_rhs:ident $to_32_rhs:ident $to_64_rhs:ident,
468 $EnumTy:ident $T8: ident $T16: ident $T32: ident $T64: ident,
469 $result_type: ident) => { {
470 let (a,b,opt_ety) = ($a,$b,$ety);
471 match opt_ety {
472 Some($EnumTy::$T8) => match (a.$to_8_lhs(), b.$to_8_rhs()) {
473 (Some(a), Some(b)) => {
474 let (a, oflo) = a.$overflowing_op(b);
475 (a as $result_type, oflo)
476 }
477 (None, _) | (_, None) => (0, true)
478 },
479 Some($EnumTy::$T16) => match (a.$to_16_lhs(), b.$to_16_rhs()) {
480 (Some(a), Some(b)) => {
481 let (a, oflo) = a.$overflowing_op(b);
482 (a as $result_type, oflo)
483 }
484 (None, _) | (_, None) => (0, true)
485 },
486 Some($EnumTy::$T32) => match (a.$to_32_lhs(), b.$to_32_rhs()) {
487 (Some(a), Some(b)) => {
488 let (a, oflo) = a.$overflowing_op(b);
489 (a as $result_type, oflo)
490 }
491 (None, _) | (_, None) => (0, true)
492 },
493 None | Some($EnumTy::$T64) => match b.$to_64_rhs() {
494 Some(b) => a.$overflowing_op(b),
495 None => (0, true),
496 }
497 }
498 } }
499 }
500
501 macro_rules! int_arith_body {
502 ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
503 overflow_checking_body!(
504 $a, $b, $ety, $overflowing_op,
505 lhs: to_i8 to_i16 to_i32,
506 rhs: to_i8 to_i16 to_i32 to_i64, IntTy I8 I16 I32 I64, i64)
507 }
508 }
509
510 macro_rules! uint_arith_body {
511 ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
512 overflow_checking_body!(
513 $a, $b, $ety, $overflowing_op,
514 lhs: to_u8 to_u16 to_u32,
515 rhs: to_u8 to_u16 to_u32 to_u64, UintTy U8 U16 U32 U64, u64)
516 }
517 }
518
519 macro_rules! int_shift_body {
520 ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
521 overflow_checking_body!(
522 $a, $b, $ety, $overflowing_op,
523 lhs: to_i8 to_i16 to_i32,
524 rhs: to_u32 to_u32 to_u32 to_u32, IntTy I8 I16 I32 I64, i64)
525 }
526 }
527
528 macro_rules! uint_shift_body {
529 ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => {
530 overflow_checking_body!(
531 $a, $b, $ety, $overflowing_op,
532 lhs: to_u8 to_u16 to_u32,
533 rhs: to_u32 to_u32 to_u32 to_u32, UintTy U8 U16 U32 U64, u64)
534 }
535 }
536
537 macro_rules! pub_fn_checked_op {
538 {$fn_name:ident ($a:ident : $a_ty:ty, $b:ident : $b_ty:ty,.. $WhichTy:ident) {
539 $ret_oflo_body:ident $overflowing_op:ident
540 $const_ty:ident $signal_exn:expr
541 }} => {
542 pub fn $fn_name<'a>($a: $a_ty,
543 $b: $b_ty,
544 e: &'a Expr,
545 opt_ety: Option<$WhichTy>) -> EvalResult {
546 let (ret, oflo) = $ret_oflo_body!($a, $b, opt_ety, $overflowing_op);
547 if !oflo { Ok($const_ty(ret)) } else { signal!(e, $signal_exn) }
548 }
549 }
550 }
551
552 pub_fn_checked_op!{ const_int_checked_add(a: i64, b: i64,.. IntTy) {
553 int_arith_body overflowing_add const_int AddiWithOverflow(a, b)
554 }}
555
556 pub_fn_checked_op!{ const_int_checked_sub(a: i64, b: i64,.. IntTy) {
557 int_arith_body overflowing_sub const_int SubiWithOverflow(a, b)
558 }}
559
560 pub_fn_checked_op!{ const_int_checked_mul(a: i64, b: i64,.. IntTy) {
561 int_arith_body overflowing_mul const_int MuliWithOverflow(a, b)
562 }}
563
564 pub fn const_int_checked_div<'a>(
565 a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
566 if b == 0 { signal!(e, DivideByZero); }
567 let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_div);
568 if !oflo { Ok(const_int(ret)) } else { signal!(e, DivideWithOverflow) }
569 }
570
571 pub fn const_int_checked_rem<'a>(
572 a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult {
573 if b == 0 { signal!(e, ModuloByZero); }
574 let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_rem);
575 if !oflo { Ok(const_int(ret)) } else { signal!(e, ModuloWithOverflow) }
576 }
577
578 pub_fn_checked_op!{ const_int_checked_shl(a: i64, b: i64,.. IntTy) {
579 int_shift_body overflowing_shl const_int ShiftLeftWithOverflow
580 }}
581
582 pub_fn_checked_op!{ const_int_checked_shl_via_uint(a: i64, b: u64,.. IntTy) {
583 int_shift_body overflowing_shl const_int ShiftLeftWithOverflow
584 }}
585
586 pub_fn_checked_op!{ const_int_checked_shr(a: i64, b: i64,.. IntTy) {
587 int_shift_body overflowing_shr const_int ShiftRightWithOverflow
588 }}
589
590 pub_fn_checked_op!{ const_int_checked_shr_via_uint(a: i64, b: u64,.. IntTy) {
591 int_shift_body overflowing_shr const_int ShiftRightWithOverflow
592 }}
593
594 pub_fn_checked_op!{ const_uint_checked_add(a: u64, b: u64,.. UintTy) {
595 uint_arith_body overflowing_add const_uint AdduWithOverflow(a, b)
596 }}
597
598 pub_fn_checked_op!{ const_uint_checked_sub(a: u64, b: u64,.. UintTy) {
599 uint_arith_body overflowing_sub const_uint SubuWithOverflow(a, b)
600 }}
601
602 pub_fn_checked_op!{ const_uint_checked_mul(a: u64, b: u64,.. UintTy) {
603 uint_arith_body overflowing_mul const_uint MuluWithOverflow(a, b)
604 }}
605
606 pub fn const_uint_checked_div<'a>(
607 a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult {
608 if b == 0 { signal!(e, DivideByZero); }
609 let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_div);
610 if !oflo { Ok(const_uint(ret)) } else { signal!(e, DivideWithOverflow) }
611 }
612
613 pub fn const_uint_checked_rem<'a>(
614 a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult {
615 if b == 0 { signal!(e, ModuloByZero); }
616 let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_rem);
617 if !oflo { Ok(const_uint(ret)) } else { signal!(e, ModuloWithOverflow) }
618 }
619
620 pub_fn_checked_op!{ const_uint_checked_shl(a: u64, b: u64,.. UintTy) {
621 uint_shift_body overflowing_shl const_uint ShiftLeftWithOverflow
622 }}
623
624 pub_fn_checked_op!{ const_uint_checked_shl_via_int(a: u64, b: i64,.. UintTy) {
625 uint_shift_body overflowing_shl const_uint ShiftLeftWithOverflow
626 }}
627
628 pub_fn_checked_op!{ const_uint_checked_shr(a: u64, b: u64,.. UintTy) {
629 uint_shift_body overflowing_shr const_uint ShiftRightWithOverflow
630 }}
631
632 pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
633 uint_shift_body overflowing_shr const_uint ShiftRightWithOverflow
634 }}
635
636 pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
637 e: &Expr,
638 ty_hint: Option<Ty<'tcx>>) -> EvalResult {
639 fn fromb(b: bool) -> const_val { const_int(b as i64) }
640
641 let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e));
642
643 // If type of expression itself is int or uint, normalize in these
644 // bindings so that isize/usize is mapped to a type with an
645 // inherently known bitwidth.
646 let expr_int_type = ety.and_then(|ty| {
647 if let ty::ty_int(t) = ty.sty {
648 Some(IntTy::from(tcx, t)) } else { None }
649 });
650 let expr_uint_type = ety.and_then(|ty| {
651 if let ty::ty_uint(t) = ty.sty {
652 Some(UintTy::from(tcx, t)) } else { None }
653 });
654
655 let result = match e.node {
656 ast::ExprUnary(ast::UnNeg, ref inner) => {
657 match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
658 const_float(f) => const_float(-f),
659 const_int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
660 const_uint(i) => {
661 if !tcx.sess.features.borrow().negate_unsigned {
662 feature_gate::emit_feature_err(
663 &tcx.sess.parse_sess.span_diagnostic,
664 "negate_unsigned",
665 e.span,
666 "unary negation of unsigned integers may be removed in the future");
667 }
668 try!(const_uint_checked_neg(i, e, expr_uint_type))
669 }
670 const_str(_) => signal!(e, NegateOnString),
671 const_bool(_) => signal!(e, NegateOnBoolean),
672 const_binary(_) => signal!(e, NegateOnBinary),
673 const_val::Tuple(_) => signal!(e, NegateOnTuple),
674 const_val::Struct(..) => signal!(e, NegateOnStruct),
675 }
676 }
677 ast::ExprUnary(ast::UnNot, ref inner) => {
678 match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
679 const_int(i) => const_int(!i),
680 const_uint(i) => const_uint(!i),
681 const_bool(b) => const_bool(!b),
682 const_str(_) => signal!(e, NotOnString),
683 const_float(_) => signal!(e, NotOnFloat),
684 const_binary(_) => signal!(e, NotOnBinary),
685 const_val::Tuple(_) => signal!(e, NotOnTuple),
686 const_val::Struct(..) => signal!(e, NotOnStruct),
687 }
688 }
689 ast::ExprBinary(op, ref a, ref b) => {
690 let b_ty = match op.node {
691 ast::BiShl | ast::BiShr => Some(tcx.types.usize),
692 _ => ety
693 };
694 match (try!(eval_const_expr_partial(tcx, &**a, ety)),
695 try!(eval_const_expr_partial(tcx, &**b, b_ty))) {
696 (const_float(a), const_float(b)) => {
697 match op.node {
698 ast::BiAdd => const_float(a + b),
699 ast::BiSub => const_float(a - b),
700 ast::BiMul => const_float(a * b),
701 ast::BiDiv => const_float(a / b),
702 ast::BiRem => const_float(a % b),
703 ast::BiEq => fromb(a == b),
704 ast::BiLt => fromb(a < b),
705 ast::BiLe => fromb(a <= b),
706 ast::BiNe => fromb(a != b),
707 ast::BiGe => fromb(a >= b),
708 ast::BiGt => fromb(a > b),
709 _ => signal!(e, InvalidOpForFloats(op.node))
710 }
711 }
712 (const_int(a), const_int(b)) => {
713 match op.node {
714 ast::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)),
715 ast::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)),
716 ast::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)),
717 ast::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)),
718 ast::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)),
719 ast::BiAnd | ast::BiBitAnd => const_int(a & b),
720 ast::BiOr | ast::BiBitOr => const_int(a | b),
721 ast::BiBitXor => const_int(a ^ b),
722 ast::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)),
723 ast::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)),
724 ast::BiEq => fromb(a == b),
725 ast::BiLt => fromb(a < b),
726 ast::BiLe => fromb(a <= b),
727 ast::BiNe => fromb(a != b),
728 ast::BiGe => fromb(a >= b),
729 ast::BiGt => fromb(a > b)
730 }
731 }
732 (const_uint(a), const_uint(b)) => {
733 match op.node {
734 ast::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)),
735 ast::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)),
736 ast::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)),
737 ast::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)),
738 ast::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)),
739 ast::BiAnd | ast::BiBitAnd => const_uint(a & b),
740 ast::BiOr | ast::BiBitOr => const_uint(a | b),
741 ast::BiBitXor => const_uint(a ^ b),
742 ast::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)),
743 ast::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)),
744 ast::BiEq => fromb(a == b),
745 ast::BiLt => fromb(a < b),
746 ast::BiLe => fromb(a <= b),
747 ast::BiNe => fromb(a != b),
748 ast::BiGe => fromb(a >= b),
749 ast::BiGt => fromb(a > b),
750 }
751 }
752 // shifts can have any integral type as their rhs
753 (const_int(a), const_uint(b)) => {
754 match op.node {
755 ast::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)),
756 ast::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)),
757 _ => signal!(e, InvalidOpForIntUint(op.node)),
758 }
759 }
760 (const_uint(a), const_int(b)) => {
761 match op.node {
762 ast::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)),
763 ast::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)),
764 _ => signal!(e, InvalidOpForUintInt(op.node)),
765 }
766 }
767 (const_bool(a), const_bool(b)) => {
768 const_bool(match op.node {
769 ast::BiAnd => a && b,
770 ast::BiOr => a || b,
771 ast::BiBitXor => a ^ b,
772 ast::BiBitAnd => a & b,
773 ast::BiBitOr => a | b,
774 ast::BiEq => a == b,
775 ast::BiNe => a != b,
776 _ => signal!(e, InvalidOpForBools(op.node)),
777 })
778 }
779
780 _ => signal!(e, MiscBinaryOp),
781 }
782 }
783 ast::ExprCast(ref base, ref target_ty) => {
784 // This tends to get called w/o the type actually having been
785 // populated in the ctxt, which was causing things to blow up
786 // (#5900). Fall back to doing a limited lookup to get past it.
787 let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
788 .unwrap_or_else(|| {
789 tcx.sess.span_fatal(target_ty.span,
790 "target type not found for const cast")
791 });
792
793 // Prefer known type to noop, but always have a type hint.
794 //
795 // FIXME (#23833): the type-hint can cause problems,
796 // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
797 // type to the sum, and thus no overflow is signaled.
798 let base_hint = ty::expr_ty_opt(tcx, &**base).unwrap_or(ety);
799 let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
800 match cast_const(tcx, val, ety) {
801 Ok(val) => val,
802 Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
803 }
804 }
805 ast::ExprPath(..) => {
806 let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def());
807 let (const_expr, const_ty) = match opt_def {
808 Some(def::DefConst(def_id)) => {
809 if ast_util::is_local(def_id) {
810 match tcx.map.find(def_id.node) {
811 Some(ast_map::NodeItem(it)) => match it.node {
812 ast::ItemConst(ref ty, ref expr) => {
813 (Some(&**expr), Some(&**ty))
814 }
815 _ => (None, None)
816 },
817 _ => (None, None)
818 }
819 } else {
820 (lookup_const_by_id(tcx, def_id, Some(e.id)), None)
821 }
822 }
823 Some(def::DefAssociatedConst(def_id, provenance)) => {
824 if ast_util::is_local(def_id) {
825 match provenance {
826 def::FromTrait(trait_id) => match tcx.map.find(def_id.node) {
827 Some(ast_map::NodeTraitItem(ti)) => match ti.node {
828 ast::ConstTraitItem(ref ty, _) => {
829 (resolve_trait_associated_const(tcx, ti,
830 trait_id, e.id),
831 Some(&**ty))
832 }
833 _ => (None, None)
834 },
835 _ => (None, None)
836 },
837 def::FromImpl(_) => match tcx.map.find(def_id.node) {
838 Some(ast_map::NodeImplItem(ii)) => match ii.node {
839 ast::ConstImplItem(ref ty, ref expr) => {
840 (Some(&**expr), Some(&**ty))
841 }
842 _ => (None, None)
843 },
844 _ => (None, None)
845 },
846 }
847 } else {
848 (lookup_const_by_id(tcx, def_id, Some(e.id)), None)
849 }
850 }
851 Some(def::DefVariant(enum_def, variant_def, _)) => {
852 (lookup_variant_by_id(tcx, enum_def, variant_def), None)
853 }
854 _ => (None, None)
855 };
856 let const_expr = match const_expr {
857 Some(actual_e) => actual_e,
858 None => signal!(e, NonConstPath)
859 };
860 let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
861 try!(eval_const_expr_partial(tcx, const_expr, ety))
862 }
863 ast::ExprLit(ref lit) => {
864 lit_to_const(&**lit, ety)
865 }
866 ast::ExprParen(ref e) => try!(eval_const_expr_partial(tcx, &**e, ety)),
867 ast::ExprBlock(ref block) => {
868 match block.expr {
869 Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ety)),
870 None => const_int(0)
871 }
872 }
873 ast::ExprTup(_) => {
874 const_val::Tuple(e.id)
875 }
876 ast::ExprStruct(..) => {
877 const_val::Struct(e.id)
878 }
879 ast::ExprTupField(ref base, index) => {
880 if let Ok(c) = eval_const_expr_partial(tcx, base, None) {
881 if let const_val::Tuple(tup_id) = c {
882 if let ast::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
883 if index.node < fields.len() {
884 return eval_const_expr_partial(tcx, &fields[index.node], None)
885 } else {
886 signal!(e, TupleIndexOutOfBounds);
887 }
888 } else {
889 unreachable!()
890 }
891 } else {
892 signal!(base, ExpectedConstTuple);
893 }
894 } else {
895 signal!(base, NonConstPath)
896 }
897 }
898 ast::ExprField(ref base, field_name) => {
899 // Get the base expression if it is a struct and it is constant
900 if let Ok(c) = eval_const_expr_partial(tcx, base, None) {
901 if let const_val::Struct(struct_id) = c {
902 if let ast::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
903 // Check that the given field exists and evaluate it
904 if let Some(f) = fields.iter().find(|f| f.ident.node.as_str()
905 == field_name.node.as_str()) {
906 return eval_const_expr_partial(tcx, &*f.expr, None)
907 } else {
908 signal!(e, MissingStructField);
909 }
910 } else {
911 unreachable!()
912 }
913 } else {
914 signal!(base, ExpectedConstStruct);
915 }
916 } else {
917 signal!(base, NonConstPath);
918 }
919 }
920 _ => signal!(e, MiscCatchAll)
921 };
922
923 Ok(result)
924 }
925
926 fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>,
927 ti: &'tcx ast::TraitItem,
928 trait_id: ast::DefId,
929 ref_id: ast::NodeId)
930 -> Option<&'tcx Expr>
931 {
932 let rcvr_substs = ty::node_id_item_substs(tcx, ref_id).substs;
933 let subst::SeparateVecsPerParamSpace {
934 types: rcvr_type,
935 selfs: rcvr_self,
936 fns: _,
937 } = rcvr_substs.types.split();
938 let trait_substs =
939 subst::Substs::erased(subst::VecPerParamSpace::new(rcvr_type,
940 rcvr_self,
941 Vec::new()));
942 let trait_substs = tcx.mk_substs(trait_substs);
943 debug!("resolve_trait_associated_const: trait_substs={}",
944 trait_substs.repr(tcx));
945 let trait_ref = ty::Binder(ty::TraitRef { def_id: trait_id,
946 substs: trait_substs });
947
948 ty::populate_implementations_for_trait_if_necessary(tcx, trait_ref.def_id());
949 let infcx = infer::new_infer_ctxt(tcx);
950
951 let param_env = ty::empty_parameter_environment(tcx);
952 let mut selcx = traits::SelectionContext::new(&infcx, &param_env);
953 let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
954 trait_ref.to_poly_trait_predicate());
955 let selection = match selcx.select(&obligation) {
956 Ok(Some(vtable)) => vtable,
957 // Still ambiguous, so give up and let the caller decide whether this
958 // expression is really needed yet. Some associated constant values
959 // can't be evaluated until monomorphization is done in trans.
960 Ok(None) => {
961 return None
962 }
963 Err(e) => {
964 tcx.sess.span_bug(ti.span,
965 &format!("Encountered error `{}` when trying \
966 to select an implementation for \
967 constant trait item reference.",
968 e.repr(tcx)))
969 }
970 };
971
972 match selection {
973 traits::VtableImpl(ref impl_data) => {
974 match ty::associated_consts(tcx, impl_data.impl_def_id)
975 .iter().find(|ic| ic.name == ti.ident.name) {
976 Some(ic) => lookup_const_by_id(tcx, ic.def_id, None),
977 None => match ti.node {
978 ast::ConstTraitItem(_, Some(ref expr)) => Some(&*expr),
979 _ => None,
980 },
981 }
982 }
983 _ => {
984 tcx.sess.span_bug(
985 ti.span,
986 &format!("resolve_trait_associated_const: unexpected vtable type"))
987 }
988 }
989 }
990
991 fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: const_val, ty: Ty) -> CastResult {
992 macro_rules! convert_val {
993 ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => {
994 match val {
995 const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
996 const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
997 const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
998 const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
999 _ => Err(ErrKind::CannotCastTo(stringify!($const_type))),
1000 }
1001 }
1002 }
1003
1004 // Issue #23890: If isize/usize, then dispatch to appropriate target representation type
1005 match (&ty.sty, tcx.sess.target.int_type, tcx.sess.target.uint_type) {
1006 (&ty::ty_int(ast::TyIs), ast::TyI32, _) => return convert_val!(i32, const_int, i64),
1007 (&ty::ty_int(ast::TyIs), ast::TyI64, _) => return convert_val!(i64, const_int, i64),
1008 (&ty::ty_int(ast::TyIs), _, _) => panic!("unexpected target.int_type"),
1009
1010 (&ty::ty_uint(ast::TyUs), _, ast::TyU32) => return convert_val!(u32, const_uint, u64),
1011 (&ty::ty_uint(ast::TyUs), _, ast::TyU64) => return convert_val!(u64, const_uint, u64),
1012 (&ty::ty_uint(ast::TyUs), _, _) => panic!("unexpected target.uint_type"),
1013
1014 _ => {}
1015 }
1016
1017 match ty.sty {
1018 ty::ty_int(ast::TyIs) => unreachable!(),
1019 ty::ty_uint(ast::TyUs) => unreachable!(),
1020
1021 ty::ty_int(ast::TyI8) => convert_val!(i8, const_int, i64),
1022 ty::ty_int(ast::TyI16) => convert_val!(i16, const_int, i64),
1023 ty::ty_int(ast::TyI32) => convert_val!(i32, const_int, i64),
1024 ty::ty_int(ast::TyI64) => convert_val!(i64, const_int, i64),
1025
1026 ty::ty_uint(ast::TyU8) => convert_val!(u8, const_uint, u64),
1027 ty::ty_uint(ast::TyU16) => convert_val!(u16, const_uint, u64),
1028 ty::ty_uint(ast::TyU32) => convert_val!(u32, const_uint, u64),
1029 ty::ty_uint(ast::TyU64) => convert_val!(u64, const_uint, u64),
1030
1031 ty::ty_float(ast::TyF32) => convert_val!(f32, const_float, f64),
1032 ty::ty_float(ast::TyF64) => convert_val!(f64, const_float, f64),
1033 _ => Err(ErrKind::CannotCast),
1034 }
1035 }
1036
1037 fn lit_to_const(lit: &ast::Lit, ty_hint: Option<Ty>) -> const_val {
1038 match lit.node {
1039 ast::LitStr(ref s, _) => const_str((*s).clone()),
1040 ast::LitBinary(ref data) => {
1041 const_binary(data.clone())
1042 }
1043 ast::LitByte(n) => const_uint(n as u64),
1044 ast::LitChar(n) => const_uint(n as u64),
1045 ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => const_int(n as i64),
1046 ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => {
1047 match ty_hint.map(|ty| &ty.sty) {
1048 Some(&ty::ty_uint(_)) => const_uint(n),
1049 _ => const_int(n as i64)
1050 }
1051 }
1052 ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) |
1053 ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => const_int(-(n as i64)),
1054 ast::LitInt(n, ast::UnsignedIntLit(_)) => const_uint(n),
1055 ast::LitFloat(ref n, _) |
1056 ast::LitFloatUnsuffixed(ref n) => {
1057 const_float(n.parse::<f64>().unwrap() as f64)
1058 }
1059 ast::LitBool(b) => const_bool(b)
1060 }
1061 }
1062
1063 pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<Ordering> {
1064 Some(match (a, b) {
1065 (&const_int(a), &const_int(b)) => a.cmp(&b),
1066 (&const_uint(a), &const_uint(b)) => a.cmp(&b),
1067 (&const_float(a), &const_float(b)) => {
1068 // This is pretty bad but it is the existing behavior.
1069 if a == b {
1070 Ordering::Equal
1071 } else if a < b {
1072 Ordering::Less
1073 } else {
1074 Ordering::Greater
1075 }
1076 }
1077 (&const_str(ref a), &const_str(ref b)) => a.cmp(b),
1078 (&const_bool(a), &const_bool(b)) => a.cmp(&b),
1079 (&const_binary(ref a), &const_binary(ref b)) => a.cmp(b),
1080 _ => return None
1081 })
1082 }
1083
1084 pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
1085 a: &Expr,
1086 b: &Expr,
1087 ty_hint: Option<Ty<'tcx>>)
1088 -> Option<Ordering> {
1089 let a = match eval_const_expr_partial(tcx, a, ty_hint) {
1090 Ok(a) => a,
1091 Err(e) => {
1092 tcx.sess.span_err(a.span, &e.description());
1093 return None;
1094 }
1095 };
1096 let b = match eval_const_expr_partial(tcx, b, ty_hint) {
1097 Ok(b) => b,
1098 Err(e) => {
1099 tcx.sess.span_err(b.span, &e.description());
1100 return None;
1101 }
1102 };
1103 compare_const_vals(&a, &b)
1104 }