]> git.lizzy.rs Git - rust.git/blob - src/librustc_const_eval/eval.rs
rustdoc: Hide `self: Box<Self>` in list of deref methods
[rust.git] / src / librustc_const_eval / eval.rs
1 // Copyright 2012-2016 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 rustc::middle::const_val::ConstVal::*;
12 use rustc::middle::const_val::ErrKind::*;
13 use rustc::middle::const_val::{ConstVal, ConstEvalErr, EvalResult, ErrKind};
14
15 use rustc::hir::map as hir_map;
16 use rustc::hir::map::blocks::FnLikeNode;
17 use rustc::traits;
18 use rustc::hir::def::{Def, CtorKind};
19 use rustc::hir::def_id::DefId;
20 use rustc::ty::{self, Ty, TyCtxt};
21 use rustc::ty::maps::Providers;
22 use rustc::ty::util::IntTypeExt;
23 use rustc::ty::subst::{Substs, Subst};
24 use rustc::traits::Reveal;
25 use rustc::util::common::ErrorReported;
26 use rustc::util::nodemap::DefIdMap;
27
28 use syntax::ast;
29 use rustc::hir::{self, Expr};
30 use syntax_pos::Span;
31
32 use std::cmp::Ordering;
33
34 use rustc_const_math::*;
35
36 macro_rules! signal {
37     ($e:expr, $exn:expr) => {
38         return Err(ConstEvalErr { span: $e.span, kind: $exn })
39     }
40 }
41
42 macro_rules! math {
43     ($e:expr, $op:expr) => {
44         match $op {
45             Ok(val) => val,
46             Err(e) => signal!($e, ErrKind::from(e)),
47         }
48     }
49 }
50
51 /// * `def_id` is the id of the constant.
52 /// * `substs` is the monomorphized substitutions for the expression.
53 ///
54 /// `substs` is optional and is used for associated constants.
55 /// This generally happens in late/trans const evaluation.
56 pub fn lookup_const_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
57                                     def_id: DefId,
58                                     substs: &'tcx Substs<'tcx>)
59                                     -> Option<(DefId, &'tcx Substs<'tcx>)> {
60     if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
61         match tcx.hir.find(node_id) {
62             Some(hir_map::NodeTraitItem(_)) => {
63                 // If we have a trait item and the substitutions for it,
64                 // `resolve_trait_associated_const` will select an impl
65                 // or the default.
66                 resolve_trait_associated_const(tcx, def_id, substs)
67             }
68             _ => Some((def_id, substs))
69         }
70     } else {
71         match tcx.describe_def(def_id) {
72             Some(Def::AssociatedConst(_)) => {
73                 // As mentioned in the comments above for in-crate
74                 // constants, we only try to find the expression for a
75                 // trait-associated const if the caller gives us the
76                 // substitutions for the reference to it.
77                 if tcx.trait_of_item(def_id).is_some() {
78                     resolve_trait_associated_const(tcx, def_id, substs)
79                 } else {
80                     Some((def_id, substs))
81                 }
82             }
83             _ => Some((def_id, substs))
84         }
85     }
86 }
87
88 pub struct ConstContext<'a, 'tcx: 'a> {
89     tcx: TyCtxt<'a, 'tcx, 'tcx>,
90     tables: &'a ty::TypeckTables<'tcx>,
91     substs: &'tcx Substs<'tcx>,
92     fn_args: Option<DefIdMap<ConstVal<'tcx>>>
93 }
94
95 impl<'a, 'tcx> ConstContext<'a, 'tcx> {
96     pub fn with_tables(tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &'a ty::TypeckTables<'tcx>) -> Self {
97         ConstContext {
98             tcx: tcx,
99             tables: tables,
100             substs: tcx.intern_substs(&[]),
101             fn_args: None
102         }
103     }
104
105     /// Evaluate a constant expression in a context where the expression isn't
106     /// guaranteed to be evaluatable.
107     pub fn eval(&self, e: &Expr) -> EvalResult<'tcx> {
108         if self.tables.tainted_by_errors {
109             signal!(e, TypeckError);
110         }
111         eval_const_expr_partial(self, e)
112     }
113 }
114
115 type CastResult<'tcx> = Result<ConstVal<'tcx>, ErrKind<'tcx>>;
116
117 fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>,
118                                      e: &Expr) -> EvalResult<'tcx> {
119     let tcx = cx.tcx;
120     let ety = cx.tables.expr_ty(e);
121
122     // Avoid applying substitutions if they're empty, that'd ICE.
123     let ety = if cx.substs.is_empty() {
124         ety
125     } else {
126         ety.subst(tcx, cx.substs)
127     };
128
129     let result = match e.node {
130       hir::ExprUnary(hir::UnNeg, ref inner) => {
131         // unary neg literals already got their sign during creation
132         if let hir::ExprLit(ref lit) = inner.node {
133             use syntax::ast::*;
134             use syntax::ast::LitIntType::*;
135             const I8_OVERFLOW: u128 = i8::min_value() as u8 as u128;
136             const I16_OVERFLOW: u128 = i16::min_value() as u16 as u128;
137             const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128;
138             const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128;
139             const I128_OVERFLOW: u128 = i128::min_value() as u128;
140             match (&lit.node, &ety.sty) {
141                 (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) |
142                 (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => {
143                     return Ok(Integral(I8(i8::min_value())))
144                 },
145                 (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) |
146                 (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => {
147                     return Ok(Integral(I16(i16::min_value())))
148                 },
149                 (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) |
150                 (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => {
151                     return Ok(Integral(I32(i32::min_value())))
152                 },
153                 (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) |
154                 (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => {
155                     return Ok(Integral(I64(i64::min_value())))
156                 },
157                 (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) |
158                 (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => {
159                     return Ok(Integral(I128(i128::min_value())))
160                 },
161                 (&LitKind::Int(n, _), &ty::TyInt(IntTy::Is)) |
162                 (&LitKind::Int(n, Signed(IntTy::Is)), _) => {
163                     match tcx.sess.target.int_type {
164                         IntTy::I16 => if n == I16_OVERFLOW {
165                             return Ok(Integral(Isize(Is16(i16::min_value()))));
166                         },
167                         IntTy::I32 => if n == I32_OVERFLOW {
168                             return Ok(Integral(Isize(Is32(i32::min_value()))));
169                         },
170                         IntTy::I64 => if n == I64_OVERFLOW {
171                             return Ok(Integral(Isize(Is64(i64::min_value()))));
172                         },
173                         _ => span_bug!(e.span, "typeck error")
174                     }
175                 },
176                 _ => {},
177             }
178         }
179         match cx.eval(inner)? {
180           Float(f) => Float(-f),
181           Integral(i) => Integral(math!(e, -i)),
182           const_val => signal!(e, NegateOn(const_val)),
183         }
184       }
185       hir::ExprUnary(hir::UnNot, ref inner) => {
186         match cx.eval(inner)? {
187           Integral(i) => Integral(math!(e, !i)),
188           Bool(b) => Bool(!b),
189           const_val => signal!(e, NotOn(const_val)),
190         }
191       }
192       hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")),
193       hir::ExprBinary(op, ref a, ref b) => {
194         // technically, if we don't have type hints, but integral eval
195         // gives us a type through a type-suffix, cast or const def type
196         // we need to re-eval the other value of the BinOp if it was
197         // not inferred
198         match (cx.eval(a)?, cx.eval(b)?) {
199           (Float(a), Float(b)) => {
200             use std::cmp::Ordering::*;
201             match op.node {
202               hir::BiAdd => Float(math!(e, a + b)),
203               hir::BiSub => Float(math!(e, a - b)),
204               hir::BiMul => Float(math!(e, a * b)),
205               hir::BiDiv => Float(math!(e, a / b)),
206               hir::BiRem => Float(math!(e, a % b)),
207               hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
208               hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
209               hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
210               hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
211               hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
212               hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
213               _ => span_bug!(e.span, "typeck error"),
214             }
215           }
216           (Integral(a), Integral(b)) => {
217             use std::cmp::Ordering::*;
218             match op.node {
219               hir::BiAdd => Integral(math!(e, a + b)),
220               hir::BiSub => Integral(math!(e, a - b)),
221               hir::BiMul => Integral(math!(e, a * b)),
222               hir::BiDiv => Integral(math!(e, a / b)),
223               hir::BiRem => Integral(math!(e, a % b)),
224               hir::BiBitAnd => Integral(math!(e, a & b)),
225               hir::BiBitOr => Integral(math!(e, a | b)),
226               hir::BiBitXor => Integral(math!(e, a ^ b)),
227               hir::BiShl => Integral(math!(e, a << b)),
228               hir::BiShr => Integral(math!(e, a >> b)),
229               hir::BiEq => Bool(math!(e, a.try_cmp(b)) == Equal),
230               hir::BiLt => Bool(math!(e, a.try_cmp(b)) == Less),
231               hir::BiLe => Bool(math!(e, a.try_cmp(b)) != Greater),
232               hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal),
233               hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less),
234               hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater),
235               _ => span_bug!(e.span, "typeck error"),
236             }
237           }
238           (Bool(a), Bool(b)) => {
239             Bool(match op.node {
240               hir::BiAnd => a && b,
241               hir::BiOr => a || b,
242               hir::BiBitXor => a ^ b,
243               hir::BiBitAnd => a & b,
244               hir::BiBitOr => a | b,
245               hir::BiEq => a == b,
246               hir::BiNe => a != b,
247               hir::BiLt => a < b,
248               hir::BiLe => a <= b,
249               hir::BiGe => a >= b,
250               hir::BiGt => a > b,
251               _ => span_bug!(e.span, "typeck error"),
252              })
253           }
254           (Char(a), Char(b)) => {
255             Bool(match op.node {
256               hir::BiEq => a == b,
257               hir::BiNe => a != b,
258               hir::BiLt => a < b,
259               hir::BiLe => a <= b,
260               hir::BiGe => a >= b,
261               hir::BiGt => a > b,
262               _ => span_bug!(e.span, "typeck error"),
263              })
264           }
265
266           _ => signal!(e, MiscBinaryOp),
267         }
268       }
269       hir::ExprCast(ref base, _) => {
270         let base_val = cx.eval(base)?;
271         let base_ty = cx.tables.expr_ty(base);
272
273         // Avoid applying substitutions if they're empty, that'd ICE.
274         let base_ty = if cx.substs.is_empty() {
275             base_ty
276         } else {
277             base_ty.subst(tcx, cx.substs)
278         };
279         if ety == base_ty {
280             base_val
281         } else {
282             match cast_const(tcx, base_val, ety) {
283                 Ok(val) => val,
284                 Err(kind) => signal!(e, kind),
285             }
286         }
287       }
288       hir::ExprPath(ref qpath) => {
289         let substs = cx.tables.node_id_item_substs(e.id)
290             .unwrap_or_else(|| tcx.intern_substs(&[]));
291
292         // Avoid applying substitutions if they're empty, that'd ICE.
293         let substs = if cx.substs.is_empty() {
294             substs
295         } else {
296             substs.subst(tcx, cx.substs)
297         };
298
299           match cx.tables.qpath_def(qpath, e.id) {
300               Def::Const(def_id) |
301               Def::AssociatedConst(def_id) => {
302                     match tcx.at(e.span).const_eval((def_id, substs)) {
303                         Ok(val) => val,
304                         Err(ConstEvalErr { kind: TypeckError, .. }) => {
305                             signal!(e, TypeckError);
306                         }
307                         Err(err) => {
308                             debug!("bad reference: {:?}, {:?}", err.description(), err.span);
309                             signal!(e, ErroneousReferencedConstant(box err))
310                         },
311                     }
312               },
313               Def::VariantCtor(variant_def, CtorKind::Const) => {
314                 Variant(variant_def)
315               }
316               Def::VariantCtor(_, CtorKind::Fn) => {
317                   signal!(e, UnimplementedConstVal("enum variants"));
318               }
319               Def::StructCtor(_, CtorKind::Const) => {
320                   ConstVal::Struct(Default::default())
321               }
322               Def::StructCtor(_, CtorKind::Fn) => {
323                   signal!(e, UnimplementedConstVal("tuple struct constructors"))
324               }
325               Def::Local(def_id) => {
326                   debug!("Def::Local({:?}): {:?}", def_id, cx.fn_args);
327                   if let Some(val) = cx.fn_args.as_ref().and_then(|args| args.get(&def_id)) {
328                       val.clone()
329                   } else {
330                       signal!(e, NonConstPath);
331                   }
332               },
333               Def::Method(id) | Def::Fn(id) => Function(id, substs),
334               Def::Err => span_bug!(e.span, "typeck error"),
335               _ => signal!(e, NonConstPath),
336           }
337       }
338       hir::ExprCall(ref callee, ref args) => {
339           let (def_id, substs) = match cx.eval(callee)? {
340               Function(def_id, substs) => (def_id, substs),
341               _ => signal!(e, TypeckError),
342           };
343
344           let body = if let Some(node_id) = tcx.hir.as_local_node_id(def_id) {
345             if let Some(fn_like) = FnLikeNode::from_node(tcx.hir.get(node_id)) {
346                 if fn_like.constness() == hir::Constness::Const {
347                     tcx.hir.body(fn_like.body())
348                 } else {
349                     signal!(e, TypeckError)
350                 }
351             } else {
352                 signal!(e, TypeckError)
353             }
354           } else {
355             if tcx.sess.cstore.is_const_fn(def_id) {
356                 tcx.sess.cstore.item_body(tcx, def_id)
357             } else {
358                 signal!(e, TypeckError)
359             }
360           };
361
362           let arg_defs = body.arguments.iter().map(|arg| match arg.pat.node {
363                hir::PatKind::Binding(_, def_id, _, _) => Some(def_id),
364                _ => None
365            }).collect::<Vec<_>>();
366           assert_eq!(arg_defs.len(), args.len());
367
368           let mut call_args = DefIdMap();
369           for (arg, arg_expr) in arg_defs.into_iter().zip(args.iter()) {
370               let arg_val = cx.eval(arg_expr)?;
371               debug!("const call arg: {:?}", arg);
372               if let Some(def_id) = arg {
373                 assert!(call_args.insert(def_id, arg_val).is_none());
374               }
375           }
376           debug!("const call({:?})", call_args);
377           let callee_cx = ConstContext {
378             tcx: tcx,
379             tables: tcx.typeck_tables_of(def_id),
380             substs: substs,
381             fn_args: Some(call_args)
382           };
383           callee_cx.eval(&body.value)?
384       },
385       hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety) {
386           Ok(val) => val,
387           Err(err) => signal!(e, err),
388       },
389       hir::ExprBlock(ref block) => {
390         match block.expr {
391             Some(ref expr) => cx.eval(expr)?,
392             None => Tuple(vec![]),
393         }
394       }
395       hir::ExprType(ref e, _) => cx.eval(e)?,
396       hir::ExprTup(ref fields) => {
397         Tuple(fields.iter().map(|e| cx.eval(e)).collect::<Result<_, _>>()?)
398       }
399       hir::ExprStruct(_, ref fields, _) => {
400         Struct(fields.iter().map(|f| {
401             cx.eval(&f.expr).map(|v| (f.name.node, v))
402         }).collect::<Result<_, _>>()?)
403       }
404       hir::ExprIndex(ref arr, ref idx) => {
405         if !tcx.sess.features.borrow().const_indexing {
406             signal!(e, IndexOpFeatureGated);
407         }
408         let arr = cx.eval(arr)?;
409         let idx = match cx.eval(idx)? {
410             Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type),
411             _ => signal!(idx, IndexNotUsize),
412         };
413         assert_eq!(idx as usize as u64, idx);
414         match arr {
415             Array(ref v) => {
416                 if let Some(elem) = v.get(idx as usize) {
417                     elem.clone()
418                 } else {
419                     let n = v.len() as u64;
420                     assert_eq!(n as usize as u64, n);
421                     signal!(e, IndexOutOfBounds { len: n, index: idx })
422                 }
423             }
424
425             Repeat(.., n) if idx >= n => {
426                 signal!(e, IndexOutOfBounds { len: n, index: idx })
427             }
428             Repeat(ref elem, _) => (**elem).clone(),
429
430             ByteStr(ref data) if idx >= data.len() as u64 => {
431                 signal!(e, IndexOutOfBounds { len: data.len() as u64, index: idx })
432             }
433             ByteStr(data) => {
434                 Integral(U8(data[idx as usize]))
435             },
436
437             _ => signal!(e, IndexedNonVec),
438         }
439       }
440       hir::ExprArray(ref v) => {
441         Array(v.iter().map(|e| cx.eval(e)).collect::<Result<_, _>>()?)
442       }
443       hir::ExprRepeat(ref elem, _) => {
444           let n = match ety.sty {
445             ty::TyArray(_, n) => n as u64,
446             _ => span_bug!(e.span, "typeck error")
447           };
448           Repeat(Box::new(cx.eval(elem)?), n)
449       },
450       hir::ExprTupField(ref base, index) => {
451         let c = cx.eval(base)?;
452         if let Tuple(ref fields) = c {
453             fields[index.node].clone()
454         } else {
455             signal!(base, ExpectedConstTuple);
456         }
457       }
458       hir::ExprField(ref base, field_name) => {
459         let c = cx.eval(base)?;
460         if let Struct(ref fields) = c {
461             if let Some(f) = fields.get(&field_name.node) {
462                 f.clone()
463             } else {
464                 signal!(e, MissingStructField);
465             }
466         } else {
467             signal!(base, ExpectedConstStruct);
468         }
469       }
470       hir::ExprAddrOf(..) => signal!(e, UnimplementedConstVal("address operator")),
471       _ => signal!(e, MiscCatchAll)
472     };
473
474     Ok(result)
475 }
476
477 fn resolve_trait_associated_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
478                                             def_id: DefId,
479                                             substs: &'tcx Substs<'tcx>)
480                                             -> Option<(DefId, &'tcx Substs<'tcx>)> {
481     let trait_item = tcx.associated_item(def_id);
482     let trait_id = trait_item.container.id();
483     let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, substs));
484     debug!("resolve_trait_associated_const: trait_ref={:?}",
485            trait_ref);
486
487     tcx.infer_ctxt((), Reveal::UserFacing).enter(|infcx| {
488         let mut selcx = traits::SelectionContext::new(&infcx);
489         let obligation = traits::Obligation::new(traits::ObligationCause::dummy(),
490                                                  trait_ref.to_poly_trait_predicate());
491         let selection = match selcx.select(&obligation) {
492             Ok(Some(vtable)) => vtable,
493             // Still ambiguous, so give up and let the caller decide whether this
494             // expression is really needed yet. Some associated constant values
495             // can't be evaluated until monomorphization is done in trans.
496             Ok(None) => {
497                 return None
498             }
499             Err(_) => {
500                 return None
501             }
502         };
503
504         // NOTE: this code does not currently account for specialization, but when
505         // it does so, it should hook into the Reveal to determine when the
506         // constant should resolve; this will also require plumbing through to this
507         // function whether we are in "trans mode" to pick the right Reveal
508         // when constructing the inference context above.
509         match selection {
510             traits::VtableImpl(ref impl_data) => {
511                 let name = trait_item.name;
512                 let ac = tcx.associated_items(impl_data.impl_def_id)
513                     .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name);
514                 match ac {
515                     // FIXME(eddyb) Use proper Instance resolution to
516                     // get the correct Substs returned from here.
517                     Some(ic) => Some((ic.def_id, Substs::empty())),
518                     None => {
519                         if trait_item.defaultness.has_value() {
520                             Some((def_id, substs))
521                         } else {
522                             None
523                         }
524                     }
525                 }
526             }
527             _ => {
528                 bug!("resolve_trait_associated_const: unexpected vtable type")
529             }
530         }
531     })
532 }
533
534 fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
535                             val: ConstInt,
536                             ty: Ty<'tcx>)
537                             -> CastResult<'tcx> {
538     let v = val.to_u128_unchecked();
539     match ty.sty {
540         ty::TyBool if v == 0 => Ok(Bool(false)),
541         ty::TyBool if v == 1 => Ok(Bool(true)),
542         ty::TyInt(ast::IntTy::I8) => Ok(Integral(I8(v as i128 as i8))),
543         ty::TyInt(ast::IntTy::I16) => Ok(Integral(I16(v as i128 as i16))),
544         ty::TyInt(ast::IntTy::I32) => Ok(Integral(I32(v as i128 as i32))),
545         ty::TyInt(ast::IntTy::I64) => Ok(Integral(I64(v as i128 as i64))),
546         ty::TyInt(ast::IntTy::I128) => Ok(Integral(I128(v as i128))),
547         ty::TyInt(ast::IntTy::Is) => {
548             Ok(Integral(Isize(ConstIsize::new_truncating(v as i128, tcx.sess.target.int_type))))
549         },
550         ty::TyUint(ast::UintTy::U8) => Ok(Integral(U8(v as u8))),
551         ty::TyUint(ast::UintTy::U16) => Ok(Integral(U16(v as u16))),
552         ty::TyUint(ast::UintTy::U32) => Ok(Integral(U32(v as u32))),
553         ty::TyUint(ast::UintTy::U64) => Ok(Integral(U64(v as u64))),
554         ty::TyUint(ast::UintTy::U128) => Ok(Integral(U128(v as u128))),
555         ty::TyUint(ast::UintTy::Us) => {
556             Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type))))
557         },
558         ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))),
559         ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))),
560         ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")),
561         ty::TyChar => match val {
562             U8(u) => Ok(Char(u as char)),
563             _ => bug!(),
564         },
565         _ => Err(CannotCast),
566     }
567 }
568
569 fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
570                               val: ConstFloat,
571                               ty: Ty<'tcx>) -> CastResult<'tcx> {
572     match ty.sty {
573         ty::TyInt(_) | ty::TyUint(_) => {
574             let i = match val {
575                 F32(f) if f >= 0.0 => U128(f as u128),
576                 F64(f) if f >= 0.0 => U128(f as u128),
577
578                 F32(f) => I128(f as i128),
579                 F64(f) => I128(f as i128)
580             };
581
582             if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) {
583                 return Err(CannotCast);
584             }
585
586             cast_const_int(tcx, i, ty)
587         }
588         ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val {
589             F32(f) => f as f64,
590             F64(f) => f
591         }))),
592         ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val {
593             F64(f) => f as f32,
594             F32(f) => f
595         }))),
596         _ => Err(CannotCast),
597     }
598 }
599
600 fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
601                         val: ConstVal<'tcx>,
602                         ty: Ty<'tcx>)
603                         -> CastResult<'tcx> {
604     match val {
605         Integral(i) => cast_const_int(tcx, i, ty),
606         Bool(b) => cast_const_int(tcx, U8(b as u8), ty),
607         Float(f) => cast_const_float(tcx, f, ty),
608         Char(c) => cast_const_int(tcx, U32(c as u32), ty),
609         Variant(v) => {
610             let adt = tcx.adt_def(tcx.parent_def_id(v).unwrap());
611             let idx = adt.variant_index_with_id(v);
612             cast_const_int(tcx, adt.discriminant_for_variant(tcx, idx), ty)
613         }
614         Function(..) => Err(UnimplementedConstVal("casting fn pointers")),
615         ByteStr(b) => match ty.sty {
616             ty::TyRawPtr(_) => {
617                 Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr"))
618             },
619             ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
620                 ty::TyArray(ty, n) if ty == tcx.types.u8 && n == b.len() => Ok(ByteStr(b)),
621                 ty::TySlice(_) => {
622                     Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice"))
623                 },
624                 _ => Err(CannotCast),
625             },
626             _ => Err(CannotCast),
627         },
628         Str(s) => match ty.sty {
629             ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")),
630             ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
631                 ty::TyStr => Ok(Str(s)),
632                 _ => Err(CannotCast),
633             },
634             _ => Err(CannotCast),
635         },
636         _ => Err(CannotCast),
637     }
638 }
639
640 fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind,
641                           tcx: TyCtxt<'a, 'tcx, 'tcx>,
642                           mut ty: Ty<'tcx>)
643                           -> Result<ConstVal<'tcx>, ErrKind<'tcx>> {
644     use syntax::ast::*;
645     use syntax::ast::LitIntType::*;
646
647     if let ty::TyAdt(adt, _) = ty.sty {
648         if adt.is_enum() {
649             ty = adt.repr.discr_type().to_ty(tcx)
650         }
651     }
652
653     match *lit {
654         LitKind::Str(ref s, _) => Ok(Str(s.as_str())),
655         LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())),
656         LitKind::Byte(n) => Ok(Integral(U8(n))),
657         LitKind::Int(n, hint) => {
658             match (&ty.sty, hint) {
659                 (&ty::TyInt(ity), _) |
660                 (_, Signed(ity)) => {
661                     Ok(Integral(ConstInt::new_signed_truncating(n as i128,
662                         ity, tcx.sess.target.int_type)))
663                 }
664                 (&ty::TyUint(uty), _) |
665                 (_, Unsigned(uty)) => {
666                     Ok(Integral(ConstInt::new_unsigned_truncating(n as u128,
667                         uty, tcx.sess.target.uint_type)))
668                 }
669                 _ => bug!()
670             }
671         }
672         LitKind::Float(n, fty) => {
673             parse_float(&n.as_str(), fty).map(Float)
674         }
675         LitKind::FloatUnsuffixed(n) => {
676             let fty = match ty.sty {
677                 ty::TyFloat(fty) => fty,
678                 _ => bug!()
679             };
680             parse_float(&n.as_str(), fty).map(Float)
681         }
682         LitKind::Bool(b) => Ok(Bool(b)),
683         LitKind::Char(c) => Ok(Char(c)),
684     }
685 }
686
687 fn parse_float<'tcx>(num: &str, fty: ast::FloatTy)
688                      -> Result<ConstFloat, ErrKind<'tcx>> {
689     let val = match fty {
690         ast::FloatTy::F32 => num.parse::<f32>().map(F32),
691         ast::FloatTy::F64 => num.parse::<f64>().map(F64)
692     };
693     val.map_err(|_| {
694         // FIXME(#31407) this is only necessary because float parsing is buggy
695         UnimplementedConstVal("could not evaluate float literal (see issue #31407)")
696     })
697 }
698
699 pub fn compare_const_vals(tcx: TyCtxt, span: Span, a: &ConstVal, b: &ConstVal)
700                           -> Result<Ordering, ErrorReported>
701 {
702     let result = match (a, b) {
703         (&Integral(a), &Integral(b)) => a.try_cmp(b).ok(),
704         (&Float(a), &Float(b)) => a.try_cmp(b).ok(),
705         (&Str(ref a), &Str(ref b)) => Some(a.cmp(b)),
706         (&Bool(a), &Bool(b)) => Some(a.cmp(&b)),
707         (&ByteStr(ref a), &ByteStr(ref b)) => Some(a.cmp(b)),
708         (&Char(a), &Char(ref b)) => Some(a.cmp(b)),
709         _ => None,
710     };
711
712     match result {
713         Some(result) => Ok(result),
714         None => {
715             // FIXME: can this ever be reached?
716             span_err!(tcx.sess, span, E0298,
717                       "type mismatch comparing {} and {}",
718                       a.description(),
719                       b.description());
720             Err(ErrorReported)
721         }
722     }
723 }
724
725 impl<'a, 'tcx> ConstContext<'a, 'tcx> {
726     pub fn compare_lit_exprs(&self,
727                              span: Span,
728                              a: &Expr,
729                              b: &Expr) -> Result<Ordering, ErrorReported> {
730         let tcx = self.tcx;
731         let a = match self.eval(a) {
732             Ok(a) => a,
733             Err(e) => {
734                 e.report(tcx, a.span, "expression");
735                 return Err(ErrorReported);
736             }
737         };
738         let b = match self.eval(b) {
739             Ok(b) => b,
740             Err(e) => {
741                 e.report(tcx, b.span, "expression");
742                 return Err(ErrorReported);
743             }
744         };
745         compare_const_vals(tcx, span, &a, &b)
746     }
747 }
748
749 pub fn provide(providers: &mut Providers) {
750     *providers = Providers {
751         const_eval,
752         ..*providers
753     };
754 }
755
756 fn const_eval<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
757                         (def_id, substs): (DefId, &'tcx Substs<'tcx>))
758                         -> EvalResult<'tcx> {
759     let (def_id, substs) = if let Some(resolved) = lookup_const_by_id(tcx, def_id, substs) {
760         resolved
761     } else {
762         return Err(ConstEvalErr {
763             span: tcx.def_span(def_id),
764             kind: TypeckError
765         });
766     };
767
768     let cx = ConstContext {
769         tcx,
770         tables: tcx.typeck_tables_of(def_id),
771         substs: substs,
772         fn_args: None
773     };
774
775     let body = if let Some(id) = tcx.hir.as_local_node_id(def_id) {
776         tcx.mir_const_qualif(def_id);
777         tcx.hir.body(tcx.hir.body_owned_by(id))
778     } else {
779         tcx.sess.cstore.item_body(tcx, def_id)
780     };
781     cx.eval(&body.value)
782 }