]>
Commit | Line | Data |
---|---|---|
62682a34 | 1 | // Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT |
223e47cc LB |
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 | ||
e9174d1e | 11 | //#![allow(non_camel_case_types)] |
1a4d82fc | 12 | |
62682a34 | 13 | use self::ConstVal::*; |
c34b1796 | 14 | use self::ErrKind::*; |
c1a9b12d | 15 | use self::EvalHint::*; |
c34b1796 | 16 | |
e9174d1e SL |
17 | use front::map as ast_map; |
18 | use front::map::blocks::FnLikeNode; | |
92a42be0 SL |
19 | use middle::cstore::{self, CrateStore, InlinedItem}; |
20 | use middle::{def, infer, subst, traits}; | |
b039eaaf | 21 | use middle::def_id::DefId; |
1a4d82fc | 22 | use middle::pat_util::def_to_path; |
85aaf69f | 23 | use middle::ty::{self, Ty}; |
c34b1796 | 24 | use middle::astconv_util::ast_ty_to_prim_ty; |
9346a6ac | 25 | use util::num::ToPrimitive; |
b039eaaf | 26 | use util::nodemap::NodeMap; |
223e47cc | 27 | |
b039eaaf | 28 | use syntax::{ast, abi}; |
e9174d1e SL |
29 | use rustc_front::hir::Expr; |
30 | use rustc_front::hir; | |
92a42be0 | 31 | use rustc_front::intravisit::FnKind; |
85aaf69f | 32 | use syntax::codemap::Span; |
1a4d82fc JJ |
33 | use syntax::parse::token::InternedString; |
34 | use syntax::ptr::P; | |
e9174d1e | 35 | use syntax::codemap; |
223e47cc | 36 | |
c34b1796 AL |
37 | use std::borrow::{Cow, IntoCow}; |
38 | use std::num::wrapping::OverflowingOps; | |
85aaf69f | 39 | use std::cmp::Ordering; |
1a4d82fc | 40 | use std::collections::hash_map::Entry::Vacant; |
92a42be0 | 41 | use std::hash; |
b039eaaf | 42 | use std::mem::transmute; |
62682a34 | 43 | use std::{i8, i16, i32, i64, u8, u16, u32, u64}; |
1a4d82fc | 44 | use std::rc::Rc; |
223e47cc | 45 | |
1a4d82fc | 46 | fn lookup_const<'a>(tcx: &'a ty::ctxt, e: &Expr) -> Option<&'a Expr> { |
c34b1796 | 47 | let opt_def = tcx.def_map.borrow().get(&e.id).map(|d| d.full_def()); |
1a4d82fc | 48 | match opt_def { |
d9579d0f | 49 | Some(def::DefConst(def_id)) | |
c1a9b12d | 50 | Some(def::DefAssociatedConst(def_id)) => { |
d9579d0f | 51 | lookup_const_by_id(tcx, def_id, Some(e.id)) |
1a4d82fc JJ |
52 | } |
53 | Some(def::DefVariant(enum_def, variant_def, _)) => { | |
54 | lookup_variant_by_id(tcx, enum_def, variant_def) | |
55 | } | |
56 | _ => None | |
57 | } | |
58 | } | |
59 | ||
60 | fn lookup_variant_by_id<'a>(tcx: &'a ty::ctxt, | |
e9174d1e SL |
61 | enum_def: DefId, |
62 | variant_def: DefId) | |
1a4d82fc | 63 | -> Option<&'a Expr> { |
92a42be0 | 64 | fn variant_expr<'a>(variants: &'a [hir::Variant], id: ast::NodeId) |
1a4d82fc | 65 | -> Option<&'a Expr> { |
85aaf69f | 66 | for variant in variants { |
b039eaaf | 67 | if variant.node.data.id() == id { |
1a4d82fc JJ |
68 | return variant.node.disr_expr.as_ref().map(|e| &**e); |
69 | } | |
70 | } | |
71 | None | |
72 | } | |
73 | ||
b039eaaf SL |
74 | if let Some(enum_node_id) = tcx.map.as_local_node_id(enum_def) { |
75 | let variant_node_id = tcx.map.as_local_node_id(variant_def).unwrap(); | |
76 | match tcx.map.find(enum_node_id) { | |
1a4d82fc JJ |
77 | None => None, |
78 | Some(ast_map::NodeItem(it)) => match it.node { | |
e9174d1e | 79 | hir::ItemEnum(hir::EnumDef { ref variants }, _) => { |
92a42be0 | 80 | variant_expr(variants, variant_node_id) |
223e47cc | 81 | } |
1a4d82fc JJ |
82 | _ => None |
83 | }, | |
84 | Some(_) => None | |
85 | } | |
86 | } else { | |
b039eaaf | 87 | None |
1a4d82fc JJ |
88 | } |
89 | } | |
223e47cc | 90 | |
d9579d0f | 91 | pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, |
e9174d1e | 92 | def_id: DefId, |
d9579d0f AL |
93 | maybe_ref_id: Option<ast::NodeId>) |
94 | -> Option<&'tcx Expr> { | |
b039eaaf SL |
95 | if let Some(node_id) = tcx.map.as_local_node_id(def_id) { |
96 | match tcx.map.find(node_id) { | |
1a4d82fc JJ |
97 | None => None, |
98 | Some(ast_map::NodeItem(it)) => match it.node { | |
e9174d1e | 99 | hir::ItemConst(_, ref const_expr) => { |
d9579d0f AL |
100 | Some(&*const_expr) |
101 | } | |
102 | _ => None | |
103 | }, | |
104 | Some(ast_map::NodeTraitItem(ti)) => match ti.node { | |
e9174d1e | 105 | hir::ConstTraitItem(_, _) => { |
d9579d0f AL |
106 | match maybe_ref_id { |
107 | // If we have a trait item, and we know the expression | |
108 | // that's the source of the obligation to resolve it, | |
109 | // `resolve_trait_associated_const` will select an impl | |
110 | // or the default. | |
111 | Some(ref_id) => { | |
c1a9b12d | 112 | let trait_id = tcx.trait_of_item(def_id) |
d9579d0f | 113 | .unwrap(); |
c1a9b12d | 114 | let substs = tcx.node_id_item_substs(ref_id) |
62682a34 | 115 | .substs; |
d9579d0f | 116 | resolve_trait_associated_const(tcx, ti, trait_id, |
62682a34 | 117 | substs) |
d9579d0f AL |
118 | } |
119 | // Technically, without knowing anything about the | |
120 | // expression that generates the obligation, we could | |
121 | // still return the default if there is one. However, | |
122 | // it's safer to return `None` than to return some value | |
123 | // that may differ from what you would get from | |
124 | // correctly selecting an impl. | |
125 | None => None | |
126 | } | |
127 | } | |
128 | _ => None | |
129 | }, | |
130 | Some(ast_map::NodeImplItem(ii)) => match ii.node { | |
92a42be0 | 131 | hir::ImplItemKind::Const(_, ref expr) => { |
d9579d0f | 132 | Some(&*expr) |
1a4d82fc JJ |
133 | } |
134 | _ => None | |
135 | }, | |
136 | Some(_) => None | |
137 | } | |
138 | } else { | |
139 | match tcx.extern_const_statics.borrow().get(&def_id) { | |
140 | Some(&ast::DUMMY_NODE_ID) => return None, | |
141 | Some(&expr_id) => { | |
142 | return Some(tcx.map.expect_expr(expr_id)); | |
143 | } | |
144 | None => {} | |
145 | } | |
d9579d0f | 146 | let mut used_ref_id = false; |
92a42be0 SL |
147 | let expr_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { |
148 | cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => match item.node { | |
e9174d1e | 149 | hir::ItemConst(_, ref const_expr) => Some(const_expr.id), |
1a4d82fc JJ |
150 | _ => None |
151 | }, | |
92a42be0 | 152 | cstore::FoundAst::Found(&InlinedItem::TraitItem(trait_id, ref ti)) => match ti.node { |
e9174d1e | 153 | hir::ConstTraitItem(_, _) => { |
d9579d0f AL |
154 | used_ref_id = true; |
155 | match maybe_ref_id { | |
156 | // As mentioned in the comments above for in-crate | |
157 | // constants, we only try to find the expression for | |
158 | // a trait-associated const if the caller gives us | |
159 | // the expression that refers to it. | |
160 | Some(ref_id) => { | |
c1a9b12d | 161 | let substs = tcx.node_id_item_substs(ref_id) |
62682a34 | 162 | .substs; |
d9579d0f | 163 | resolve_trait_associated_const(tcx, ti, trait_id, |
62682a34 | 164 | substs).map(|e| e.id) |
d9579d0f AL |
165 | } |
166 | None => None | |
167 | } | |
168 | } | |
169 | _ => None | |
170 | }, | |
92a42be0 SL |
171 | cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref ii)) => match ii.node { |
172 | hir::ImplItemKind::Const(_, ref expr) => Some(expr.id), | |
d9579d0f AL |
173 | _ => None |
174 | }, | |
1a4d82fc JJ |
175 | _ => None |
176 | }; | |
d9579d0f AL |
177 | // If we used the reference expression, particularly to choose an impl |
178 | // of a trait-associated const, don't cache that, because the next | |
179 | // lookup with the same def_id may yield a different result. | |
180 | if !used_ref_id { | |
181 | tcx.extern_const_statics | |
182 | .borrow_mut().insert(def_id, | |
183 | expr_id.unwrap_or(ast::DUMMY_NODE_ID)); | |
184 | } | |
1a4d82fc JJ |
185 | expr_id.map(|id| tcx.map.expect_expr(id)) |
186 | } | |
187 | } | |
223e47cc | 188 | |
e9174d1e | 189 | fn inline_const_fn_from_external_crate(tcx: &ty::ctxt, def_id: DefId) |
62682a34 SL |
190 | -> Option<ast::NodeId> { |
191 | match tcx.extern_const_fns.borrow().get(&def_id) { | |
192 | Some(&ast::DUMMY_NODE_ID) => return None, | |
193 | Some(&fn_id) => return Some(fn_id), | |
194 | None => {} | |
195 | } | |
196 | ||
92a42be0 | 197 | if !tcx.sess.cstore.is_const_fn(def_id) { |
62682a34 SL |
198 | tcx.extern_const_fns.borrow_mut().insert(def_id, ast::DUMMY_NODE_ID); |
199 | return None; | |
200 | } | |
201 | ||
92a42be0 SL |
202 | let fn_id = match tcx.sess.cstore.maybe_get_item_ast(tcx, def_id) { |
203 | cstore::FoundAst::Found(&InlinedItem::Item(ref item)) => Some(item.id), | |
204 | cstore::FoundAst::Found(&InlinedItem::ImplItem(_, ref item)) => Some(item.id), | |
62682a34 SL |
205 | _ => None |
206 | }; | |
207 | tcx.extern_const_fns.borrow_mut().insert(def_id, | |
208 | fn_id.unwrap_or(ast::DUMMY_NODE_ID)); | |
209 | fn_id | |
210 | } | |
211 | ||
e9174d1e | 212 | pub fn lookup_const_fn_by_id<'tcx>(tcx: &ty::ctxt<'tcx>, def_id: DefId) |
62682a34 SL |
213 | -> Option<FnLikeNode<'tcx>> |
214 | { | |
b039eaaf SL |
215 | let fn_id = if let Some(node_id) = tcx.map.as_local_node_id(def_id) { |
216 | node_id | |
217 | } else { | |
62682a34 SL |
218 | if let Some(fn_id) = inline_const_fn_from_external_crate(tcx, def_id) { |
219 | fn_id | |
220 | } else { | |
221 | return None; | |
222 | } | |
62682a34 SL |
223 | }; |
224 | ||
225 | let fn_like = match FnLikeNode::from_node(tcx.map.get(fn_id)) { | |
226 | Some(fn_like) => fn_like, | |
227 | None => return None | |
228 | }; | |
229 | ||
230 | match fn_like.kind() { | |
e9174d1e | 231 | FnKind::ItemFn(_, _, _, hir::Constness::Const, _, _) => { |
62682a34 SL |
232 | Some(fn_like) |
233 | } | |
e9174d1e SL |
234 | FnKind::Method(_, m, _) => { |
235 | if m.constness == hir::Constness::Const { | |
62682a34 SL |
236 | Some(fn_like) |
237 | } else { | |
238 | None | |
239 | } | |
240 | } | |
241 | _ => None | |
242 | } | |
243 | } | |
244 | ||
b039eaaf | 245 | #[derive(Clone, Debug)] |
62682a34 SL |
246 | pub enum ConstVal { |
247 | Float(f64), | |
248 | Int(i64), | |
249 | Uint(u64), | |
250 | Str(InternedString), | |
e9174d1e | 251 | ByteStr(Rc<Vec<u8>>), |
62682a34 | 252 | Bool(bool), |
c34b1796 | 253 | Struct(ast::NodeId), |
62682a34 | 254 | Tuple(ast::NodeId), |
b039eaaf | 255 | Function(DefId), |
92a42be0 SL |
256 | Array(ast::NodeId, u64), |
257 | Repeat(ast::NodeId, u64), | |
258 | } | |
259 | ||
260 | impl hash::Hash for ConstVal { | |
261 | fn hash<H: hash::Hasher>(&self, state: &mut H) { | |
262 | match *self { | |
263 | Float(a) => unsafe { transmute::<_,u64>(a) }.hash(state), | |
264 | Int(a) => a.hash(state), | |
265 | Uint(a) => a.hash(state), | |
266 | Str(ref a) => a.hash(state), | |
267 | ByteStr(ref a) => a.hash(state), | |
268 | Bool(a) => a.hash(state), | |
269 | Struct(a) => a.hash(state), | |
270 | Tuple(a) => a.hash(state), | |
271 | Function(a) => a.hash(state), | |
272 | Array(a, n) => { a.hash(state); n.hash(state) }, | |
273 | Repeat(a, n) => { a.hash(state); n.hash(state) }, | |
274 | } | |
275 | } | |
b039eaaf SL |
276 | } |
277 | ||
278 | /// Note that equality for `ConstVal` means that the it is the same | |
279 | /// constant, not that the rust values are equal. In particular, `NaN | |
280 | /// == NaN` (at least if it's the same NaN; distinct encodings for NaN | |
281 | /// are considering unequal). | |
282 | impl PartialEq for ConstVal { | |
283 | fn eq(&self, other: &ConstVal) -> bool { | |
284 | match (self, other) { | |
285 | (&Float(a), &Float(b)) => unsafe{transmute::<_,u64>(a) == transmute::<_,u64>(b)}, | |
286 | (&Int(a), &Int(b)) => a == b, | |
287 | (&Uint(a), &Uint(b)) => a == b, | |
288 | (&Str(ref a), &Str(ref b)) => a == b, | |
289 | (&ByteStr(ref a), &ByteStr(ref b)) => a == b, | |
290 | (&Bool(a), &Bool(b)) => a == b, | |
291 | (&Struct(a), &Struct(b)) => a == b, | |
292 | (&Tuple(a), &Tuple(b)) => a == b, | |
293 | (&Function(a), &Function(b)) => a == b, | |
92a42be0 SL |
294 | (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn), |
295 | (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn), | |
b039eaaf SL |
296 | _ => false, |
297 | } | |
298 | } | |
223e47cc LB |
299 | } |
300 | ||
92a42be0 SL |
301 | impl Eq for ConstVal { } |
302 | ||
c1a9b12d SL |
303 | impl ConstVal { |
304 | pub fn description(&self) -> &'static str { | |
305 | match *self { | |
306 | Float(_) => "float", | |
307 | Int(i) if i < 0 => "negative integer", | |
308 | Int(_) => "positive integer", | |
309 | Uint(_) => "unsigned integer", | |
310 | Str(_) => "string literal", | |
e9174d1e | 311 | ByteStr(_) => "byte string literal", |
c1a9b12d SL |
312 | Bool(_) => "boolean", |
313 | Struct(_) => "struct", | |
314 | Tuple(_) => "tuple", | |
b039eaaf | 315 | Function(_) => "function definition", |
92a42be0 SL |
316 | Array(..) => "array", |
317 | Repeat(..) => "repeat", | |
c1a9b12d SL |
318 | } |
319 | } | |
320 | } | |
321 | ||
e9174d1e | 322 | pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<hir::Pat> { |
1a4d82fc | 323 | let pat = match expr.node { |
e9174d1e SL |
324 | hir::ExprTup(ref exprs) => |
325 | hir::PatTup(exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect()), | |
1a4d82fc | 326 | |
e9174d1e | 327 | hir::ExprCall(ref callee, ref args) => { |
c34b1796 | 328 | let def = *tcx.def_map.borrow().get(&callee.id).unwrap(); |
1a4d82fc JJ |
329 | if let Vacant(entry) = tcx.def_map.borrow_mut().entry(expr.id) { |
330 | entry.insert(def); | |
331 | } | |
c34b1796 | 332 | let path = match def.full_def() { |
1a4d82fc JJ |
333 | def::DefStruct(def_id) => def_to_path(tcx, def_id), |
334 | def::DefVariant(_, variant_did, _) => def_to_path(tcx, variant_did), | |
335 | _ => unreachable!() | |
336 | }; | |
85aaf69f | 337 | let pats = args.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect(); |
e9174d1e | 338 | hir::PatEnum(path, Some(pats)) |
1a4d82fc JJ |
339 | } |
340 | ||
e9174d1e | 341 | hir::ExprStruct(ref path, ref fields, None) => { |
1a4d82fc JJ |
342 | let field_pats = fields.iter().map(|field| codemap::Spanned { |
343 | span: codemap::DUMMY_SP, | |
e9174d1e | 344 | node: hir::FieldPat { |
b039eaaf | 345 | name: field.name.node, |
85aaf69f | 346 | pat: const_expr_to_pat(tcx, &*field.expr, span), |
1a4d82fc JJ |
347 | is_shorthand: false, |
348 | }, | |
349 | }).collect(); | |
e9174d1e | 350 | hir::PatStruct(path.clone(), field_pats, false) |
1a4d82fc JJ |
351 | } |
352 | ||
e9174d1e | 353 | hir::ExprVec(ref exprs) => { |
85aaf69f | 354 | let pats = exprs.iter().map(|expr| const_expr_to_pat(tcx, &**expr, span)).collect(); |
e9174d1e | 355 | hir::PatVec(pats, None, vec![]) |
1a4d82fc JJ |
356 | } |
357 | ||
e9174d1e | 358 | hir::ExprPath(_, ref path) => { |
c34b1796 | 359 | let opt_def = tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()); |
1a4d82fc JJ |
360 | match opt_def { |
361 | Some(def::DefStruct(..)) => | |
e9174d1e | 362 | hir::PatStruct(path.clone(), vec![], false), |
1a4d82fc | 363 | Some(def::DefVariant(..)) => |
e9174d1e | 364 | hir::PatEnum(path.clone(), None), |
1a4d82fc JJ |
365 | _ => { |
366 | match lookup_const(tcx, expr) { | |
85aaf69f | 367 | Some(actual) => return const_expr_to_pat(tcx, actual, span), |
1a4d82fc JJ |
368 | _ => unreachable!() |
369 | } | |
370 | } | |
371 | } | |
372 | } | |
373 | ||
e9174d1e | 374 | _ => hir::PatLit(P(expr.clone())) |
1a4d82fc | 375 | }; |
e9174d1e | 376 | P(hir::Pat { id: expr.id, node: pat, span: span }) |
1a4d82fc JJ |
377 | } |
378 | ||
62682a34 | 379 | pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> ConstVal { |
b039eaaf | 380 | match eval_const_expr_partial(tcx, e, ExprTypeChecked, None) { |
970d7e83 | 381 | Ok(r) => r, |
c34b1796 AL |
382 | Err(s) => tcx.sess.span_fatal(s.span, &s.description()) |
383 | } | |
384 | } | |
385 | ||
b039eaaf | 386 | pub type FnArgMap<'a> = Option<&'a NodeMap<ConstVal>>; |
c34b1796 AL |
387 | |
388 | #[derive(Clone)] | |
389 | pub struct ConstEvalErr { | |
390 | pub span: Span, | |
391 | pub kind: ErrKind, | |
392 | } | |
393 | ||
394 | #[derive(Clone)] | |
395 | pub enum ErrKind { | |
396 | CannotCast, | |
397 | CannotCastTo(&'static str), | |
92a42be0 SL |
398 | InvalidOpForInts(hir::BinOp_), |
399 | InvalidOpForUInts(hir::BinOp_), | |
e9174d1e SL |
400 | InvalidOpForBools(hir::BinOp_), |
401 | InvalidOpForFloats(hir::BinOp_), | |
402 | InvalidOpForIntUint(hir::BinOp_), | |
403 | InvalidOpForUintInt(hir::BinOp_), | |
c1a9b12d SL |
404 | NegateOn(ConstVal), |
405 | NotOn(ConstVal), | |
92a42be0 | 406 | CallOn(ConstVal), |
c34b1796 AL |
407 | |
408 | NegateWithOverflow(i64), | |
409 | AddiWithOverflow(i64, i64), | |
410 | SubiWithOverflow(i64, i64), | |
411 | MuliWithOverflow(i64, i64), | |
412 | AdduWithOverflow(u64, u64), | |
413 | SubuWithOverflow(u64, u64), | |
414 | MuluWithOverflow(u64, u64), | |
415 | DivideByZero, | |
416 | DivideWithOverflow, | |
417 | ModuloByZero, | |
418 | ModuloWithOverflow, | |
419 | ShiftLeftWithOverflow, | |
420 | ShiftRightWithOverflow, | |
421 | MissingStructField, | |
422 | NonConstPath, | |
92a42be0 | 423 | UnimplementedConstVal(&'static str), |
b039eaaf | 424 | UnresolvedPath, |
c34b1796 AL |
425 | ExpectedConstTuple, |
426 | ExpectedConstStruct, | |
427 | TupleIndexOutOfBounds, | |
92a42be0 SL |
428 | IndexedNonVec, |
429 | IndexNegative, | |
430 | IndexNotInt, | |
431 | IndexOutOfBounds, | |
432 | RepeatCountNotNatural, | |
433 | RepeatCountNotInt, | |
c34b1796 AL |
434 | |
435 | MiscBinaryOp, | |
436 | MiscCatchAll, | |
92a42be0 SL |
437 | |
438 | IndexOpFeatureGated, | |
c34b1796 AL |
439 | } |
440 | ||
441 | impl ConstEvalErr { | |
442 | pub fn description(&self) -> Cow<str> { | |
443 | use self::ErrKind::*; | |
444 | ||
445 | match self.kind { | |
446 | CannotCast => "can't cast this type".into_cow(), | |
447 | CannotCastTo(s) => format!("can't cast this type to {}", s).into_cow(), | |
92a42be0 SL |
448 | InvalidOpForInts(_) => "can't do this op on signed integrals".into_cow(), |
449 | InvalidOpForUInts(_) => "can't do this op on unsigned integrals".into_cow(), | |
c34b1796 AL |
450 | InvalidOpForBools(_) => "can't do this op on bools".into_cow(), |
451 | InvalidOpForFloats(_) => "can't do this op on floats".into_cow(), | |
452 | InvalidOpForIntUint(..) => "can't do this op on an isize and usize".into_cow(), | |
453 | InvalidOpForUintInt(..) => "can't do this op on a usize and isize".into_cow(), | |
c1a9b12d SL |
454 | NegateOn(ref const_val) => format!("negate on {}", const_val.description()).into_cow(), |
455 | NotOn(ref const_val) => format!("not on {}", const_val.description()).into_cow(), | |
92a42be0 | 456 | CallOn(ref const_val) => format!("call on {}", const_val.description()).into_cow(), |
c34b1796 AL |
457 | |
458 | NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(), | |
459 | AddiWithOverflow(..) => "attempted to add with overflow".into_cow(), | |
460 | SubiWithOverflow(..) => "attempted to sub with overflow".into_cow(), | |
461 | MuliWithOverflow(..) => "attempted to mul with overflow".into_cow(), | |
462 | AdduWithOverflow(..) => "attempted to add with overflow".into_cow(), | |
463 | SubuWithOverflow(..) => "attempted to sub with overflow".into_cow(), | |
464 | MuluWithOverflow(..) => "attempted to mul with overflow".into_cow(), | |
465 | DivideByZero => "attempted to divide by zero".into_cow(), | |
466 | DivideWithOverflow => "attempted to divide with overflow".into_cow(), | |
467 | ModuloByZero => "attempted remainder with a divisor of zero".into_cow(), | |
468 | ModuloWithOverflow => "attempted remainder with overflow".into_cow(), | |
469 | ShiftLeftWithOverflow => "attempted left shift with overflow".into_cow(), | |
470 | ShiftRightWithOverflow => "attempted right shift with overflow".into_cow(), | |
471 | MissingStructField => "nonexistent struct field".into_cow(), | |
b039eaaf | 472 | NonConstPath => "non-constant path in constant expression".into_cow(), |
92a42be0 SL |
473 | UnimplementedConstVal(what) => |
474 | format!("unimplemented constant expression: {}", what).into_cow(), | |
b039eaaf | 475 | UnresolvedPath => "unresolved path in constant expression".into_cow(), |
c34b1796 AL |
476 | ExpectedConstTuple => "expected constant tuple".into_cow(), |
477 | ExpectedConstStruct => "expected constant struct".into_cow(), | |
478 | TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(), | |
92a42be0 SL |
479 | IndexedNonVec => "indexing is only supported for arrays".into_cow(), |
480 | IndexNegative => "indices must be non-negative integers".into_cow(), | |
481 | IndexNotInt => "indices must be integers".into_cow(), | |
482 | IndexOutOfBounds => "array index out of bounds".into_cow(), | |
483 | RepeatCountNotNatural => "repeat count must be a natural number".into_cow(), | |
484 | RepeatCountNotInt => "repeat count must be integers".into_cow(), | |
c34b1796 AL |
485 | |
486 | MiscBinaryOp => "bad operands for binary".into_cow(), | |
487 | MiscCatchAll => "unsupported constant expr".into_cow(), | |
92a42be0 | 488 | IndexOpFeatureGated => "the index operation on const values is unstable".into_cow(), |
c34b1796 AL |
489 | } |
490 | } | |
491 | } | |
492 | ||
62682a34 SL |
493 | pub type EvalResult = Result<ConstVal, ConstEvalErr>; |
494 | pub type CastResult = Result<ConstVal, ErrKind>; | |
c34b1796 | 495 | |
c1a9b12d SL |
496 | // FIXME: Long-term, this enum should go away: trying to evaluate |
497 | // an expression which hasn't been type-checked is a recipe for | |
498 | // disaster. That said, it's not clear how to fix ast_ty_to_ty | |
499 | // to avoid the ordering issue. | |
500 | ||
501 | /// Hint to determine how to evaluate constant expressions which | |
502 | /// might not be type-checked. | |
503 | #[derive(Copy, Clone, Debug)] | |
504 | pub enum EvalHint<'tcx> { | |
505 | /// We have a type-checked expression. | |
506 | ExprTypeChecked, | |
507 | /// We have an expression which hasn't been type-checked, but we have | |
508 | /// an idea of what the type will be because of the context. For example, | |
509 | /// the length of an array is always `usize`. (This is referred to as | |
510 | /// a hint because it isn't guaranteed to be consistent with what | |
511 | /// type-checking would compute.) | |
512 | UncheckedExprHint(Ty<'tcx>), | |
513 | /// We have an expression which has not yet been type-checked, and | |
514 | /// and we have no clue what the type will be. | |
515 | UncheckedExprNoHint, | |
516 | } | |
517 | ||
92a42be0 SL |
518 | impl<'tcx> EvalHint<'tcx> { |
519 | fn erase_hint(&self) -> EvalHint<'tcx> { | |
520 | match *self { | |
521 | ExprTypeChecked => ExprTypeChecked, | |
522 | UncheckedExprHint(_) | UncheckedExprNoHint => UncheckedExprNoHint, | |
523 | } | |
524 | } | |
525 | fn checked_or(&self, ty: Ty<'tcx>) -> EvalHint<'tcx> { | |
526 | match *self { | |
527 | ExprTypeChecked => ExprTypeChecked, | |
528 | _ => UncheckedExprHint(ty), | |
529 | } | |
530 | } | |
531 | } | |
532 | ||
c34b1796 AL |
533 | #[derive(Copy, Clone, PartialEq, Debug)] |
534 | pub enum IntTy { I8, I16, I32, I64 } | |
535 | #[derive(Copy, Clone, PartialEq, Debug)] | |
536 | pub enum UintTy { U8, U16, U32, U64 } | |
537 | ||
538 | impl IntTy { | |
b039eaaf SL |
539 | pub fn from(tcx: &ty::ctxt, t: ast::IntTy) -> IntTy { |
540 | let t = if let ast::TyIs = t { | |
c34b1796 AL |
541 | tcx.sess.target.int_type |
542 | } else { | |
543 | t | |
544 | }; | |
545 | match t { | |
b039eaaf SL |
546 | ast::TyIs => unreachable!(), |
547 | ast::TyI8 => IntTy::I8, | |
548 | ast::TyI16 => IntTy::I16, | |
549 | ast::TyI32 => IntTy::I32, | |
550 | ast::TyI64 => IntTy::I64, | |
c34b1796 AL |
551 | } |
552 | } | |
553 | } | |
554 | ||
555 | impl UintTy { | |
b039eaaf SL |
556 | pub fn from(tcx: &ty::ctxt, t: ast::UintTy) -> UintTy { |
557 | let t = if let ast::TyUs = t { | |
c34b1796 AL |
558 | tcx.sess.target.uint_type |
559 | } else { | |
560 | t | |
561 | }; | |
562 | match t { | |
b039eaaf SL |
563 | ast::TyUs => unreachable!(), |
564 | ast::TyU8 => UintTy::U8, | |
565 | ast::TyU16 => UintTy::U16, | |
566 | ast::TyU32 => UintTy::U32, | |
567 | ast::TyU64 => UintTy::U64, | |
c34b1796 AL |
568 | } |
569 | } | |
570 | } | |
571 | ||
572 | macro_rules! signal { | |
573 | ($e:expr, $exn:expr) => { | |
574 | return Err(ConstEvalErr { span: $e.span, kind: $exn }) | |
575 | } | |
576 | } | |
577 | ||
578 | // The const_{int,uint}_checked_{neg,add,sub,mul,div,shl,shr} family | |
579 | // of functions catch and signal overflow errors during constant | |
580 | // evaluation. | |
581 | // | |
582 | // They all take the operator's arguments (`a` and `b` if binary), the | |
583 | // overall expression (`e`) and, if available, whole expression's | |
584 | // concrete type (`opt_ety`). | |
585 | // | |
586 | // If the whole expression's concrete type is None, then this is a | |
587 | // constant evaluation happening before type check (e.g. in the check | |
588 | // to confirm that a pattern range's left-side is not greater than its | |
589 | // right-side). We do not do arithmetic modulo the type's bitwidth in | |
590 | // such a case; we just do 64-bit arithmetic and assume that later | |
591 | // passes will do it again with the type information, and thus do the | |
592 | // overflow checks then. | |
593 | ||
594 | pub fn const_int_checked_neg<'a>( | |
595 | a: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult { | |
596 | ||
597 | let (min,max) = match opt_ety { | |
598 | // (-i8::MIN is itself not an i8, etc, but this is an easy way | |
599 | // to allow literals to pass the check. Of course that does | |
600 | // not work for i64::MIN.) | |
601 | Some(IntTy::I8) => (-(i8::MAX as i64), -(i8::MIN as i64)), | |
602 | Some(IntTy::I16) => (-(i16::MAX as i64), -(i16::MIN as i64)), | |
603 | Some(IntTy::I32) => (-(i32::MAX as i64), -(i32::MIN as i64)), | |
604 | None | Some(IntTy::I64) => (-i64::MAX, -(i64::MIN+1)), | |
605 | }; | |
606 | ||
607 | let oflo = a < min || a > max; | |
608 | if oflo { | |
609 | signal!(e, NegateWithOverflow(a)); | |
610 | } else { | |
62682a34 | 611 | Ok(Int(-a)) |
c34b1796 AL |
612 | } |
613 | } | |
614 | ||
615 | pub fn const_uint_checked_neg<'a>( | |
616 | a: u64, _e: &'a Expr, _opt_ety: Option<UintTy>) -> EvalResult { | |
617 | // This always succeeds, and by definition, returns `(!a)+1`. | |
62682a34 SL |
618 | Ok(Uint((!a).wrapping_add(1))) |
619 | } | |
620 | ||
621 | fn const_uint_not(a: u64, opt_ety: Option<UintTy>) -> ConstVal { | |
622 | let mask = match opt_ety { | |
623 | Some(UintTy::U8) => u8::MAX as u64, | |
624 | Some(UintTy::U16) => u16::MAX as u64, | |
625 | Some(UintTy::U32) => u32::MAX as u64, | |
626 | None | Some(UintTy::U64) => u64::MAX, | |
627 | }; | |
628 | Uint(!a & mask) | |
c34b1796 AL |
629 | } |
630 | ||
631 | macro_rules! overflow_checking_body { | |
632 | ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident, | |
633 | lhs: $to_8_lhs:ident $to_16_lhs:ident $to_32_lhs:ident, | |
634 | rhs: $to_8_rhs:ident $to_16_rhs:ident $to_32_rhs:ident $to_64_rhs:ident, | |
635 | $EnumTy:ident $T8: ident $T16: ident $T32: ident $T64: ident, | |
636 | $result_type: ident) => { { | |
637 | let (a,b,opt_ety) = ($a,$b,$ety); | |
638 | match opt_ety { | |
639 | Some($EnumTy::$T8) => match (a.$to_8_lhs(), b.$to_8_rhs()) { | |
640 | (Some(a), Some(b)) => { | |
641 | let (a, oflo) = a.$overflowing_op(b); | |
642 | (a as $result_type, oflo) | |
643 | } | |
644 | (None, _) | (_, None) => (0, true) | |
645 | }, | |
646 | Some($EnumTy::$T16) => match (a.$to_16_lhs(), b.$to_16_rhs()) { | |
647 | (Some(a), Some(b)) => { | |
648 | let (a, oflo) = a.$overflowing_op(b); | |
649 | (a as $result_type, oflo) | |
650 | } | |
651 | (None, _) | (_, None) => (0, true) | |
652 | }, | |
653 | Some($EnumTy::$T32) => match (a.$to_32_lhs(), b.$to_32_rhs()) { | |
654 | (Some(a), Some(b)) => { | |
655 | let (a, oflo) = a.$overflowing_op(b); | |
656 | (a as $result_type, oflo) | |
657 | } | |
658 | (None, _) | (_, None) => (0, true) | |
659 | }, | |
660 | None | Some($EnumTy::$T64) => match b.$to_64_rhs() { | |
661 | Some(b) => a.$overflowing_op(b), | |
662 | None => (0, true), | |
663 | } | |
664 | } | |
665 | } } | |
666 | } | |
667 | ||
668 | macro_rules! int_arith_body { | |
669 | ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => { | |
670 | overflow_checking_body!( | |
671 | $a, $b, $ety, $overflowing_op, | |
672 | lhs: to_i8 to_i16 to_i32, | |
673 | rhs: to_i8 to_i16 to_i32 to_i64, IntTy I8 I16 I32 I64, i64) | |
674 | } | |
675 | } | |
676 | ||
677 | macro_rules! uint_arith_body { | |
678 | ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => { | |
679 | overflow_checking_body!( | |
680 | $a, $b, $ety, $overflowing_op, | |
681 | lhs: to_u8 to_u16 to_u32, | |
682 | rhs: to_u8 to_u16 to_u32 to_u64, UintTy U8 U16 U32 U64, u64) | |
683 | } | |
684 | } | |
685 | ||
686 | macro_rules! int_shift_body { | |
687 | ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => { | |
688 | overflow_checking_body!( | |
689 | $a, $b, $ety, $overflowing_op, | |
690 | lhs: to_i8 to_i16 to_i32, | |
691 | rhs: to_u32 to_u32 to_u32 to_u32, IntTy I8 I16 I32 I64, i64) | |
692 | } | |
693 | } | |
694 | ||
695 | macro_rules! uint_shift_body { | |
696 | ($a:ident, $b:ident, $ety:ident, $overflowing_op:ident) => { | |
697 | overflow_checking_body!( | |
698 | $a, $b, $ety, $overflowing_op, | |
699 | lhs: to_u8 to_u16 to_u32, | |
700 | rhs: to_u32 to_u32 to_u32 to_u32, UintTy U8 U16 U32 U64, u64) | |
701 | } | |
702 | } | |
703 | ||
704 | macro_rules! pub_fn_checked_op { | |
705 | {$fn_name:ident ($a:ident : $a_ty:ty, $b:ident : $b_ty:ty,.. $WhichTy:ident) { | |
706 | $ret_oflo_body:ident $overflowing_op:ident | |
707 | $const_ty:ident $signal_exn:expr | |
708 | }} => { | |
709 | pub fn $fn_name<'a>($a: $a_ty, | |
710 | $b: $b_ty, | |
711 | e: &'a Expr, | |
712 | opt_ety: Option<$WhichTy>) -> EvalResult { | |
713 | let (ret, oflo) = $ret_oflo_body!($a, $b, opt_ety, $overflowing_op); | |
714 | if !oflo { Ok($const_ty(ret)) } else { signal!(e, $signal_exn) } | |
715 | } | |
223e47cc LB |
716 | } |
717 | } | |
718 | ||
c34b1796 | 719 | pub_fn_checked_op!{ const_int_checked_add(a: i64, b: i64,.. IntTy) { |
62682a34 | 720 | int_arith_body overflowing_add Int AddiWithOverflow(a, b) |
c34b1796 AL |
721 | }} |
722 | ||
723 | pub_fn_checked_op!{ const_int_checked_sub(a: i64, b: i64,.. IntTy) { | |
62682a34 | 724 | int_arith_body overflowing_sub Int SubiWithOverflow(a, b) |
c34b1796 AL |
725 | }} |
726 | ||
727 | pub_fn_checked_op!{ const_int_checked_mul(a: i64, b: i64,.. IntTy) { | |
62682a34 | 728 | int_arith_body overflowing_mul Int MuliWithOverflow(a, b) |
c34b1796 AL |
729 | }} |
730 | ||
731 | pub fn const_int_checked_div<'a>( | |
732 | a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult { | |
733 | if b == 0 { signal!(e, DivideByZero); } | |
734 | let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_div); | |
62682a34 | 735 | if !oflo { Ok(Int(ret)) } else { signal!(e, DivideWithOverflow) } |
c34b1796 AL |
736 | } |
737 | ||
738 | pub fn const_int_checked_rem<'a>( | |
739 | a: i64, b: i64, e: &'a Expr, opt_ety: Option<IntTy>) -> EvalResult { | |
740 | if b == 0 { signal!(e, ModuloByZero); } | |
741 | let (ret, oflo) = int_arith_body!(a, b, opt_ety, overflowing_rem); | |
62682a34 | 742 | if !oflo { Ok(Int(ret)) } else { signal!(e, ModuloWithOverflow) } |
c34b1796 AL |
743 | } |
744 | ||
745 | pub_fn_checked_op!{ const_int_checked_shl(a: i64, b: i64,.. IntTy) { | |
62682a34 | 746 | int_shift_body overflowing_shl Int ShiftLeftWithOverflow |
c34b1796 AL |
747 | }} |
748 | ||
749 | pub_fn_checked_op!{ const_int_checked_shl_via_uint(a: i64, b: u64,.. IntTy) { | |
62682a34 | 750 | int_shift_body overflowing_shl Int ShiftLeftWithOverflow |
c34b1796 AL |
751 | }} |
752 | ||
753 | pub_fn_checked_op!{ const_int_checked_shr(a: i64, b: i64,.. IntTy) { | |
62682a34 | 754 | int_shift_body overflowing_shr Int ShiftRightWithOverflow |
c34b1796 AL |
755 | }} |
756 | ||
757 | pub_fn_checked_op!{ const_int_checked_shr_via_uint(a: i64, b: u64,.. IntTy) { | |
62682a34 | 758 | int_shift_body overflowing_shr Int ShiftRightWithOverflow |
c34b1796 AL |
759 | }} |
760 | ||
761 | pub_fn_checked_op!{ const_uint_checked_add(a: u64, b: u64,.. UintTy) { | |
62682a34 | 762 | uint_arith_body overflowing_add Uint AdduWithOverflow(a, b) |
c34b1796 AL |
763 | }} |
764 | ||
765 | pub_fn_checked_op!{ const_uint_checked_sub(a: u64, b: u64,.. UintTy) { | |
62682a34 | 766 | uint_arith_body overflowing_sub Uint SubuWithOverflow(a, b) |
c34b1796 AL |
767 | }} |
768 | ||
769 | pub_fn_checked_op!{ const_uint_checked_mul(a: u64, b: u64,.. UintTy) { | |
62682a34 | 770 | uint_arith_body overflowing_mul Uint MuluWithOverflow(a, b) |
c34b1796 AL |
771 | }} |
772 | ||
773 | pub fn const_uint_checked_div<'a>( | |
774 | a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult { | |
775 | if b == 0 { signal!(e, DivideByZero); } | |
776 | let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_div); | |
62682a34 | 777 | if !oflo { Ok(Uint(ret)) } else { signal!(e, DivideWithOverflow) } |
c34b1796 AL |
778 | } |
779 | ||
780 | pub fn const_uint_checked_rem<'a>( | |
781 | a: u64, b: u64, e: &'a Expr, opt_ety: Option<UintTy>) -> EvalResult { | |
782 | if b == 0 { signal!(e, ModuloByZero); } | |
783 | let (ret, oflo) = uint_arith_body!(a, b, opt_ety, overflowing_rem); | |
62682a34 | 784 | if !oflo { Ok(Uint(ret)) } else { signal!(e, ModuloWithOverflow) } |
c34b1796 AL |
785 | } |
786 | ||
787 | pub_fn_checked_op!{ const_uint_checked_shl(a: u64, b: u64,.. UintTy) { | |
62682a34 | 788 | uint_shift_body overflowing_shl Uint ShiftLeftWithOverflow |
c34b1796 AL |
789 | }} |
790 | ||
791 | pub_fn_checked_op!{ const_uint_checked_shl_via_int(a: u64, b: i64,.. UintTy) { | |
62682a34 | 792 | uint_shift_body overflowing_shl Uint ShiftLeftWithOverflow |
c34b1796 AL |
793 | }} |
794 | ||
795 | pub_fn_checked_op!{ const_uint_checked_shr(a: u64, b: u64,.. UintTy) { | |
62682a34 | 796 | uint_shift_body overflowing_shr Uint ShiftRightWithOverflow |
c34b1796 AL |
797 | }} |
798 | ||
799 | pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) { | |
62682a34 | 800 | uint_shift_body overflowing_shr Uint ShiftRightWithOverflow |
c34b1796 AL |
801 | }} |
802 | ||
c1a9b12d SL |
803 | /// Evaluate a constant expression in a context where the expression isn't |
804 | /// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked, | |
805 | /// but a few places need to evaluate constants during type-checking, like | |
806 | /// computing the length of an array. (See also the FIXME above EvalHint.) | |
85aaf69f SL |
807 | pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, |
808 | e: &Expr, | |
b039eaaf SL |
809 | ty_hint: EvalHint<'tcx>, |
810 | fn_args: FnArgMap) -> EvalResult { | |
c1a9b12d SL |
811 | // Try to compute the type of the expression based on the EvalHint. |
812 | // (See also the definition of EvalHint, and the FIXME above EvalHint.) | |
813 | let ety = match ty_hint { | |
814 | ExprTypeChecked => { | |
815 | // After type-checking, expr_ty is guaranteed to succeed. | |
816 | Some(tcx.expr_ty(e)) | |
817 | } | |
818 | UncheckedExprHint(ty) => { | |
819 | // Use the type hint; it's not guaranteed to be right, but it's | |
820 | // usually good enough. | |
821 | Some(ty) | |
822 | } | |
823 | UncheckedExprNoHint => { | |
824 | // This expression might not be type-checked, and we have no hint. | |
825 | // Try to query the context for a type anyway; we might get lucky | |
826 | // (for example, if the expression was imported from another crate). | |
827 | tcx.expr_ty_opt(e) | |
828 | } | |
829 | }; | |
85aaf69f | 830 | |
c34b1796 AL |
831 | // If type of expression itself is int or uint, normalize in these |
832 | // bindings so that isize/usize is mapped to a type with an | |
833 | // inherently known bitwidth. | |
834 | let expr_int_type = ety.and_then(|ty| { | |
62682a34 | 835 | if let ty::TyInt(t) = ty.sty { |
c34b1796 AL |
836 | Some(IntTy::from(tcx, t)) } else { None } |
837 | }); | |
838 | let expr_uint_type = ety.and_then(|ty| { | |
62682a34 | 839 | if let ty::TyUint(t) = ty.sty { |
c34b1796 AL |
840 | Some(UintTy::from(tcx, t)) } else { None } |
841 | }); | |
842 | ||
843 | let result = match e.node { | |
e9174d1e | 844 | hir::ExprUnary(hir::UnNeg, ref inner) => { |
b039eaaf | 845 | match try!(eval_const_expr_partial(tcx, &**inner, ty_hint, fn_args)) { |
62682a34 SL |
846 | Float(f) => Float(-f), |
847 | Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)), | |
848 | Uint(i) => { | |
c34b1796 AL |
849 | try!(const_uint_checked_neg(i, e, expr_uint_type)) |
850 | } | |
c1a9b12d | 851 | const_val => signal!(e, NegateOn(const_val)), |
223e47cc LB |
852 | } |
853 | } | |
e9174d1e | 854 | hir::ExprUnary(hir::UnNot, ref inner) => { |
b039eaaf | 855 | match try!(eval_const_expr_partial(tcx, &**inner, ty_hint, fn_args)) { |
62682a34 SL |
856 | Int(i) => Int(!i), |
857 | Uint(i) => const_uint_not(i, expr_uint_type), | |
858 | Bool(b) => Bool(!b), | |
c1a9b12d | 859 | const_val => signal!(e, NotOn(const_val)), |
223e47cc LB |
860 | } |
861 | } | |
e9174d1e | 862 | hir::ExprBinary(op, ref a, ref b) => { |
85aaf69f | 863 | let b_ty = match op.node { |
92a42be0 | 864 | hir::BiShl | hir::BiShr => ty_hint.checked_or(tcx.types.usize), |
c1a9b12d | 865 | _ => ty_hint |
85aaf69f | 866 | }; |
b039eaaf SL |
867 | match (try!(eval_const_expr_partial(tcx, &**a, ty_hint, fn_args)), |
868 | try!(eval_const_expr_partial(tcx, &**b, b_ty, fn_args))) { | |
62682a34 | 869 | (Float(a), Float(b)) => { |
85aaf69f | 870 | match op.node { |
e9174d1e SL |
871 | hir::BiAdd => Float(a + b), |
872 | hir::BiSub => Float(a - b), | |
873 | hir::BiMul => Float(a * b), | |
874 | hir::BiDiv => Float(a / b), | |
875 | hir::BiRem => Float(a % b), | |
92a42be0 SL |
876 | hir::BiEq => Bool(a == b), |
877 | hir::BiLt => Bool(a < b), | |
878 | hir::BiLe => Bool(a <= b), | |
879 | hir::BiNe => Bool(a != b), | |
880 | hir::BiGe => Bool(a >= b), | |
881 | hir::BiGt => Bool(a > b), | |
882 | _ => signal!(e, InvalidOpForFloats(op.node)), | |
223e47cc LB |
883 | } |
884 | } | |
62682a34 | 885 | (Int(a), Int(b)) => { |
85aaf69f | 886 | match op.node { |
e9174d1e SL |
887 | hir::BiAdd => try!(const_int_checked_add(a,b,e,expr_int_type)), |
888 | hir::BiSub => try!(const_int_checked_sub(a,b,e,expr_int_type)), | |
889 | hir::BiMul => try!(const_int_checked_mul(a,b,e,expr_int_type)), | |
890 | hir::BiDiv => try!(const_int_checked_div(a,b,e,expr_int_type)), | |
891 | hir::BiRem => try!(const_int_checked_rem(a,b,e,expr_int_type)), | |
92a42be0 SL |
892 | hir::BiBitAnd => Int(a & b), |
893 | hir::BiBitOr => Int(a | b), | |
e9174d1e SL |
894 | hir::BiBitXor => Int(a ^ b), |
895 | hir::BiShl => try!(const_int_checked_shl(a,b,e,expr_int_type)), | |
896 | hir::BiShr => try!(const_int_checked_shr(a,b,e,expr_int_type)), | |
92a42be0 SL |
897 | hir::BiEq => Bool(a == b), |
898 | hir::BiLt => Bool(a < b), | |
899 | hir::BiLe => Bool(a <= b), | |
900 | hir::BiNe => Bool(a != b), | |
901 | hir::BiGe => Bool(a >= b), | |
902 | hir::BiGt => Bool(a > b), | |
903 | _ => signal!(e, InvalidOpForInts(op.node)), | |
223e47cc LB |
904 | } |
905 | } | |
62682a34 | 906 | (Uint(a), Uint(b)) => { |
85aaf69f | 907 | match op.node { |
e9174d1e SL |
908 | hir::BiAdd => try!(const_uint_checked_add(a,b,e,expr_uint_type)), |
909 | hir::BiSub => try!(const_uint_checked_sub(a,b,e,expr_uint_type)), | |
910 | hir::BiMul => try!(const_uint_checked_mul(a,b,e,expr_uint_type)), | |
911 | hir::BiDiv => try!(const_uint_checked_div(a,b,e,expr_uint_type)), | |
912 | hir::BiRem => try!(const_uint_checked_rem(a,b,e,expr_uint_type)), | |
92a42be0 SL |
913 | hir::BiBitAnd => Uint(a & b), |
914 | hir::BiBitOr => Uint(a | b), | |
e9174d1e SL |
915 | hir::BiBitXor => Uint(a ^ b), |
916 | hir::BiShl => try!(const_uint_checked_shl(a,b,e,expr_uint_type)), | |
917 | hir::BiShr => try!(const_uint_checked_shr(a,b,e,expr_uint_type)), | |
92a42be0 SL |
918 | hir::BiEq => Bool(a == b), |
919 | hir::BiLt => Bool(a < b), | |
920 | hir::BiLe => Bool(a <= b), | |
921 | hir::BiNe => Bool(a != b), | |
922 | hir::BiGe => Bool(a >= b), | |
923 | hir::BiGt => Bool(a > b), | |
924 | _ => signal!(e, InvalidOpForUInts(op.node)), | |
223e47cc LB |
925 | } |
926 | } | |
927 | // shifts can have any integral type as their rhs | |
62682a34 | 928 | (Int(a), Uint(b)) => { |
85aaf69f | 929 | match op.node { |
e9174d1e SL |
930 | hir::BiShl => try!(const_int_checked_shl_via_uint(a,b,e,expr_int_type)), |
931 | hir::BiShr => try!(const_int_checked_shr_via_uint(a,b,e,expr_int_type)), | |
c34b1796 | 932 | _ => signal!(e, InvalidOpForIntUint(op.node)), |
223e47cc LB |
933 | } |
934 | } | |
62682a34 | 935 | (Uint(a), Int(b)) => { |
85aaf69f | 936 | match op.node { |
e9174d1e SL |
937 | hir::BiShl => try!(const_uint_checked_shl_via_int(a,b,e,expr_uint_type)), |
938 | hir::BiShr => try!(const_uint_checked_shr_via_int(a,b,e,expr_uint_type)), | |
c34b1796 | 939 | _ => signal!(e, InvalidOpForUintInt(op.node)), |
223e47cc LB |
940 | } |
941 | } | |
62682a34 SL |
942 | (Bool(a), Bool(b)) => { |
943 | Bool(match op.node { | |
e9174d1e SL |
944 | hir::BiAnd => a && b, |
945 | hir::BiOr => a || b, | |
946 | hir::BiBitXor => a ^ b, | |
947 | hir::BiBitAnd => a & b, | |
948 | hir::BiBitOr => a | b, | |
949 | hir::BiEq => a == b, | |
950 | hir::BiNe => a != b, | |
c34b1796 AL |
951 | _ => signal!(e, InvalidOpForBools(op.node)), |
952 | }) | |
223e47cc | 953 | } |
c34b1796 AL |
954 | |
955 | _ => signal!(e, MiscBinaryOp), | |
223e47cc LB |
956 | } |
957 | } | |
e9174d1e | 958 | hir::ExprCast(ref base, ref target_ty) => { |
85aaf69f | 959 | let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty)) |
1a4d82fc JJ |
960 | .unwrap_or_else(|| { |
961 | tcx.sess.span_fatal(target_ty.span, | |
962 | "target type not found for const cast") | |
963 | }); | |
c34b1796 | 964 | |
c1a9b12d SL |
965 | let base_hint = if let ExprTypeChecked = ty_hint { |
966 | ExprTypeChecked | |
967 | } else { | |
968 | // FIXME (#23833): the type-hint can cause problems, | |
969 | // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result | |
970 | // type to the sum, and thus no overflow is signaled. | |
971 | match tcx.expr_ty_opt(&base) { | |
972 | Some(t) => UncheckedExprHint(t), | |
973 | None => ty_hint | |
974 | } | |
975 | }; | |
976 | ||
b039eaaf | 977 | let val = try!(eval_const_expr_partial(tcx, &**base, base_hint, fn_args)); |
c34b1796 AL |
978 | match cast_const(tcx, val, ety) { |
979 | Ok(val) => val, | |
980 | Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), | |
981 | } | |
223e47cc | 982 | } |
e9174d1e | 983 | hir::ExprPath(..) => { |
b039eaaf SL |
984 | let opt_def = if let Some(def) = tcx.def_map.borrow().get(&e.id) { |
985 | // After type-checking, def_map contains definition of the | |
986 | // item referred to by the path. During type-checking, it | |
987 | // can contain the raw output of path resolution, which | |
988 | // might be a partially resolved path. | |
989 | // FIXME: There's probably a better way to make sure we don't | |
990 | // panic here. | |
991 | if def.depth != 0 { | |
992 | signal!(e, UnresolvedPath); | |
993 | } | |
994 | Some(def.full_def()) | |
995 | } else { | |
996 | None | |
997 | }; | |
85aaf69f SL |
998 | let (const_expr, const_ty) = match opt_def { |
999 | Some(def::DefConst(def_id)) => { | |
b039eaaf SL |
1000 | if let Some(node_id) = tcx.map.as_local_node_id(def_id) { |
1001 | match tcx.map.find(node_id) { | |
85aaf69f | 1002 | Some(ast_map::NodeItem(it)) => match it.node { |
e9174d1e | 1003 | hir::ItemConst(ref ty, ref expr) => { |
85aaf69f SL |
1004 | (Some(&**expr), Some(&**ty)) |
1005 | } | |
1006 | _ => (None, None) | |
1007 | }, | |
1008 | _ => (None, None) | |
1009 | } | |
1010 | } else { | |
d9579d0f AL |
1011 | (lookup_const_by_id(tcx, def_id, Some(e.id)), None) |
1012 | } | |
1013 | } | |
c1a9b12d | 1014 | Some(def::DefAssociatedConst(def_id)) => { |
b039eaaf | 1015 | if let Some(node_id) = tcx.map.as_local_node_id(def_id) { |
c1a9b12d | 1016 | match tcx.impl_or_trait_item(def_id).container() { |
b039eaaf | 1017 | ty::TraitContainer(trait_id) => match tcx.map.find(node_id) { |
d9579d0f | 1018 | Some(ast_map::NodeTraitItem(ti)) => match ti.node { |
e9174d1e | 1019 | hir::ConstTraitItem(ref ty, _) => { |
c1a9b12d SL |
1020 | if let ExprTypeChecked = ty_hint { |
1021 | let substs = tcx.node_id_item_substs(e.id).substs; | |
1022 | (resolve_trait_associated_const(tcx, | |
1023 | ti, | |
1024 | trait_id, | |
1025 | substs), | |
1026 | Some(&**ty)) | |
1027 | } else { | |
1028 | (None, None) | |
1029 | } | |
d9579d0f AL |
1030 | } |
1031 | _ => (None, None) | |
1032 | }, | |
1033 | _ => (None, None) | |
1034 | }, | |
b039eaaf | 1035 | ty::ImplContainer(_) => match tcx.map.find(node_id) { |
d9579d0f | 1036 | Some(ast_map::NodeImplItem(ii)) => match ii.node { |
92a42be0 | 1037 | hir::ImplItemKind::Const(ref ty, ref expr) => { |
d9579d0f AL |
1038 | (Some(&**expr), Some(&**ty)) |
1039 | } | |
1040 | _ => (None, None) | |
1041 | }, | |
1042 | _ => (None, None) | |
1043 | }, | |
1044 | } | |
1045 | } else { | |
1046 | (lookup_const_by_id(tcx, def_id, Some(e.id)), None) | |
85aaf69f SL |
1047 | } |
1048 | } | |
1049 | Some(def::DefVariant(enum_def, variant_def, _)) => { | |
1050 | (lookup_variant_by_id(tcx, enum_def, variant_def), None) | |
1051 | } | |
c1a9b12d SL |
1052 | Some(def::DefStruct(_)) => { |
1053 | return Ok(ConstVal::Struct(e.id)) | |
1054 | } | |
b039eaaf SL |
1055 | Some(def::DefLocal(_, id)) => { |
1056 | debug!("DefLocal({:?}): {:?}", id, fn_args); | |
1057 | if let Some(val) = fn_args.and_then(|args| args.get(&id)) { | |
1058 | return Ok(val.clone()); | |
1059 | } else { | |
1060 | (None, None) | |
1061 | } | |
1062 | }, | |
92a42be0 | 1063 | Some(def::DefMethod(id)) | Some(def::DefFn(id, _)) => return Ok(Function(id)), |
85aaf69f SL |
1064 | _ => (None, None) |
1065 | }; | |
1066 | let const_expr = match const_expr { | |
1067 | Some(actual_e) => actual_e, | |
c34b1796 | 1068 | None => signal!(e, NonConstPath) |
85aaf69f | 1069 | }; |
c1a9b12d SL |
1070 | let item_hint = if let UncheckedExprNoHint = ty_hint { |
1071 | match const_ty { | |
1072 | Some(ty) => match ast_ty_to_prim_ty(tcx, ty) { | |
1073 | Some(ty) => UncheckedExprHint(ty), | |
1074 | None => UncheckedExprNoHint | |
1075 | }, | |
1076 | None => UncheckedExprNoHint | |
1077 | } | |
1078 | } else { | |
1079 | ty_hint | |
1080 | }; | |
b039eaaf | 1081 | try!(eval_const_expr_partial(tcx, const_expr, item_hint, fn_args)) |
85aaf69f | 1082 | } |
b039eaaf | 1083 | hir::ExprCall(ref callee, ref args) => { |
92a42be0 SL |
1084 | let sub_ty_hint = ty_hint.erase_hint(); |
1085 | let callee_val = try!(eval_const_expr_partial(tcx, callee, sub_ty_hint, fn_args)); | |
1086 | let (decl, block, constness) = try!(get_fn_def(tcx, e, callee_val)); | |
1087 | match (ty_hint, constness) { | |
1088 | (ExprTypeChecked, _) => { | |
1089 | // no need to check for constness... either check_const | |
1090 | // already forbids this or we const eval over whatever | |
1091 | // we want | |
1092 | }, | |
1093 | (_, hir::Constness::Const) => { | |
1094 | // we don't know much about the function, so we force it to be a const fn | |
1095 | // so compilation will fail later in case the const fn's body is not const | |
b039eaaf SL |
1096 | }, |
1097 | _ => signal!(e, NonConstPath), | |
b039eaaf SL |
1098 | } |
1099 | assert_eq!(decl.inputs.len(), args.len()); | |
b039eaaf SL |
1100 | |
1101 | let mut call_args = NodeMap(); | |
1102 | for (arg, arg_expr) in decl.inputs.iter().zip(args.iter()) { | |
1103 | let arg_val = try!(eval_const_expr_partial( | |
1104 | tcx, | |
1105 | arg_expr, | |
1106 | sub_ty_hint, | |
1107 | fn_args | |
1108 | )); | |
1109 | debug!("const call arg: {:?}", arg); | |
1110 | let old = call_args.insert(arg.pat.id, arg_val); | |
1111 | assert!(old.is_none()); | |
1112 | } | |
1113 | let result = block.expr.as_ref().unwrap(); | |
1114 | debug!("const call({:?})", call_args); | |
1115 | try!(eval_const_expr_partial(tcx, &**result, ty_hint, Some(&call_args))) | |
1116 | }, | |
92a42be0 | 1117 | hir::ExprLit(ref lit) => lit_to_const(&**lit, ety), |
e9174d1e | 1118 | hir::ExprBlock(ref block) => { |
1a4d82fc | 1119 | match block.expr { |
b039eaaf | 1120 | Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint, fn_args)), |
92a42be0 | 1121 | None => unreachable!(), |
1a4d82fc JJ |
1122 | } |
1123 | } | |
e9174d1e SL |
1124 | hir::ExprTup(_) => Tuple(e.id), |
1125 | hir::ExprStruct(..) => Struct(e.id), | |
92a42be0 SL |
1126 | hir::ExprIndex(ref arr, ref idx) => { |
1127 | if !tcx.sess.features.borrow().const_indexing { | |
1128 | signal!(e, IndexOpFeatureGated); | |
1129 | } | |
1130 | let arr_hint = ty_hint.erase_hint(); | |
1131 | let arr = try!(eval_const_expr_partial(tcx, arr, arr_hint, fn_args)); | |
1132 | let idx_hint = ty_hint.checked_or(tcx.types.usize); | |
1133 | let idx = match try!(eval_const_expr_partial(tcx, idx, idx_hint, fn_args)) { | |
1134 | Int(i) if i >= 0 => i as u64, | |
1135 | Int(_) => signal!(idx, IndexNegative), | |
1136 | Uint(i) => i, | |
1137 | _ => signal!(idx, IndexNotInt), | |
c1a9b12d | 1138 | }; |
92a42be0 SL |
1139 | match arr { |
1140 | Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds), | |
1141 | Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { | |
1142 | try!(eval_const_expr_partial(tcx, &*v[idx as usize], ty_hint, fn_args)) | |
1143 | } else { | |
1144 | unreachable!() | |
1145 | }, | |
1146 | ||
1147 | Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds), | |
1148 | Repeat(elem, _) => try!(eval_const_expr_partial( | |
1149 | tcx, | |
1150 | &*tcx.map.expect_expr(elem), | |
1151 | ty_hint, | |
1152 | fn_args, | |
1153 | )), | |
1154 | ||
1155 | ByteStr(ref data) if idx as usize >= data.len() | |
1156 | => signal!(e, IndexOutOfBounds), | |
1157 | ByteStr(data) => Uint(data[idx as usize] as u64), | |
1158 | ||
1159 | Str(ref s) if idx as usize >= s.len() | |
1160 | => signal!(e, IndexOutOfBounds), | |
1161 | Str(_) => unimplemented!(), // there's no const_char type | |
1162 | _ => signal!(e, IndexedNonVec), | |
1163 | } | |
1164 | } | |
1165 | hir::ExprVec(ref v) => Array(e.id, v.len() as u64), | |
1166 | hir::ExprRepeat(_, ref n) => { | |
1167 | let len_hint = ty_hint.checked_or(tcx.types.usize); | |
1168 | Repeat( | |
1169 | e.id, | |
1170 | match try!(eval_const_expr_partial(tcx, &**n, len_hint, fn_args)) { | |
1171 | Int(i) if i >= 0 => i as u64, | |
1172 | Int(_) => signal!(e, RepeatCountNotNatural), | |
1173 | Uint(i) => i, | |
1174 | _ => signal!(e, RepeatCountNotInt), | |
1175 | }, | |
1176 | ) | |
1177 | }, | |
1178 | hir::ExprTupField(ref base, index) => { | |
1179 | let base_hint = ty_hint.erase_hint(); | |
b039eaaf | 1180 | if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) { |
62682a34 | 1181 | if let Tuple(tup_id) = c { |
e9174d1e | 1182 | if let hir::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { |
c34b1796 | 1183 | if index.node < fields.len() { |
b039eaaf | 1184 | return eval_const_expr_partial(tcx, &fields[index.node], base_hint, fn_args) |
c34b1796 AL |
1185 | } else { |
1186 | signal!(e, TupleIndexOutOfBounds); | |
1187 | } | |
1188 | } else { | |
1189 | unreachable!() | |
1190 | } | |
1a4d82fc | 1191 | } else { |
c34b1796 | 1192 | signal!(base, ExpectedConstTuple); |
1a4d82fc | 1193 | } |
c34b1796 AL |
1194 | } else { |
1195 | signal!(base, NonConstPath) | |
1a4d82fc | 1196 | } |
1a4d82fc | 1197 | } |
e9174d1e | 1198 | hir::ExprField(ref base, field_name) => { |
92a42be0 | 1199 | let base_hint = ty_hint.erase_hint(); |
1a4d82fc | 1200 | // Get the base expression if it is a struct and it is constant |
b039eaaf | 1201 | if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint, fn_args) { |
62682a34 | 1202 | if let Struct(struct_id) = c { |
e9174d1e | 1203 | if let hir::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { |
c34b1796 | 1204 | // Check that the given field exists and evaluate it |
c1a9b12d | 1205 | // if the idents are compared run-pass/issue-19244 fails |
b039eaaf SL |
1206 | if let Some(f) = fields.iter().find(|f| f.name.node |
1207 | == field_name.node) { | |
1208 | return eval_const_expr_partial(tcx, &*f.expr, base_hint, fn_args) | |
c34b1796 AL |
1209 | } else { |
1210 | signal!(e, MissingStructField); | |
1211 | } | |
1212 | } else { | |
1213 | unreachable!() | |
1214 | } | |
1a4d82fc | 1215 | } else { |
c34b1796 | 1216 | signal!(base, ExpectedConstStruct); |
1a4d82fc | 1217 | } |
c34b1796 AL |
1218 | } else { |
1219 | signal!(base, NonConstPath); | |
1a4d82fc | 1220 | } |
1a4d82fc | 1221 | } |
c34b1796 AL |
1222 | _ => signal!(e, MiscCatchAll) |
1223 | }; | |
1224 | ||
1225 | Ok(result) | |
223e47cc LB |
1226 | } |
1227 | ||
d9579d0f | 1228 | fn resolve_trait_associated_const<'a, 'tcx: 'a>(tcx: &'a ty::ctxt<'tcx>, |
e9174d1e SL |
1229 | ti: &'tcx hir::TraitItem, |
1230 | trait_id: DefId, | |
62682a34 | 1231 | rcvr_substs: subst::Substs<'tcx>) |
d9579d0f AL |
1232 | -> Option<&'tcx Expr> |
1233 | { | |
d9579d0f AL |
1234 | let subst::SeparateVecsPerParamSpace { |
1235 | types: rcvr_type, | |
1236 | selfs: rcvr_self, | |
1237 | fns: _, | |
1238 | } = rcvr_substs.types.split(); | |
1239 | let trait_substs = | |
1240 | subst::Substs::erased(subst::VecPerParamSpace::new(rcvr_type, | |
1241 | rcvr_self, | |
1242 | Vec::new())); | |
1243 | let trait_substs = tcx.mk_substs(trait_substs); | |
62682a34 SL |
1244 | debug!("resolve_trait_associated_const: trait_substs={:?}", |
1245 | trait_substs); | |
d9579d0f AL |
1246 | let trait_ref = ty::Binder(ty::TraitRef { def_id: trait_id, |
1247 | substs: trait_substs }); | |
1248 | ||
c1a9b12d SL |
1249 | tcx.populate_implementations_for_trait_if_necessary(trait_ref.def_id()); |
1250 | let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, None, false); | |
d9579d0f | 1251 | |
c1a9b12d | 1252 | let mut selcx = traits::SelectionContext::new(&infcx); |
d9579d0f AL |
1253 | let obligation = traits::Obligation::new(traits::ObligationCause::dummy(), |
1254 | trait_ref.to_poly_trait_predicate()); | |
1255 | let selection = match selcx.select(&obligation) { | |
1256 | Ok(Some(vtable)) => vtable, | |
1257 | // Still ambiguous, so give up and let the caller decide whether this | |
1258 | // expression is really needed yet. Some associated constant values | |
1259 | // can't be evaluated until monomorphization is done in trans. | |
1260 | Ok(None) => { | |
1261 | return None | |
1262 | } | |
1263 | Err(e) => { | |
1264 | tcx.sess.span_bug(ti.span, | |
62682a34 | 1265 | &format!("Encountered error `{:?}` when trying \ |
d9579d0f AL |
1266 | to select an implementation for \ |
1267 | constant trait item reference.", | |
62682a34 | 1268 | e)) |
d9579d0f AL |
1269 | } |
1270 | }; | |
1271 | ||
1272 | match selection { | |
1273 | traits::VtableImpl(ref impl_data) => { | |
c1a9b12d | 1274 | match tcx.associated_consts(impl_data.impl_def_id) |
b039eaaf | 1275 | .iter().find(|ic| ic.name == ti.name) { |
d9579d0f AL |
1276 | Some(ic) => lookup_const_by_id(tcx, ic.def_id, None), |
1277 | None => match ti.node { | |
e9174d1e | 1278 | hir::ConstTraitItem(_, Some(ref expr)) => Some(&*expr), |
d9579d0f AL |
1279 | _ => None, |
1280 | }, | |
1281 | } | |
1282 | } | |
1283 | _ => { | |
1284 | tcx.sess.span_bug( | |
1285 | ti.span, | |
92a42be0 | 1286 | "resolve_trait_associated_const: unexpected vtable type") |
d9579d0f AL |
1287 | } |
1288 | } | |
1289 | } | |
1290 | ||
62682a34 | 1291 | fn cast_const<'tcx>(tcx: &ty::ctxt<'tcx>, val: ConstVal, ty: Ty) -> CastResult { |
c34b1796 AL |
1292 | macro_rules! convert_val { |
1293 | ($intermediate_ty:ty, $const_type:ident, $target_ty:ty) => { | |
1294 | match val { | |
62682a34 SL |
1295 | Bool(b) => Ok($const_type(b as u64 as $intermediate_ty as $target_ty)), |
1296 | Uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)), | |
1297 | Int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)), | |
1298 | Float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)), | |
c34b1796 AL |
1299 | _ => Err(ErrKind::CannotCastTo(stringify!($const_type))), |
1300 | } | |
1301 | } | |
1302 | } | |
1303 | ||
1304 | // Issue #23890: If isize/usize, then dispatch to appropriate target representation type | |
1305 | match (&ty.sty, tcx.sess.target.int_type, tcx.sess.target.uint_type) { | |
b039eaaf SL |
1306 | (&ty::TyInt(ast::TyIs), ast::TyI32, _) => return convert_val!(i32, Int, i64), |
1307 | (&ty::TyInt(ast::TyIs), ast::TyI64, _) => return convert_val!(i64, Int, i64), | |
1308 | (&ty::TyInt(ast::TyIs), _, _) => panic!("unexpected target.int_type"), | |
c34b1796 | 1309 | |
b039eaaf SL |
1310 | (&ty::TyUint(ast::TyUs), _, ast::TyU32) => return convert_val!(u32, Uint, u64), |
1311 | (&ty::TyUint(ast::TyUs), _, ast::TyU64) => return convert_val!(u64, Uint, u64), | |
1312 | (&ty::TyUint(ast::TyUs), _, _) => panic!("unexpected target.uint_type"), | |
c34b1796 AL |
1313 | |
1314 | _ => {} | |
85aaf69f SL |
1315 | } |
1316 | ||
c34b1796 | 1317 | match ty.sty { |
b039eaaf SL |
1318 | ty::TyInt(ast::TyIs) => unreachable!(), |
1319 | ty::TyUint(ast::TyUs) => unreachable!(), | |
c34b1796 | 1320 | |
b039eaaf SL |
1321 | ty::TyInt(ast::TyI8) => convert_val!(i8, Int, i64), |
1322 | ty::TyInt(ast::TyI16) => convert_val!(i16, Int, i64), | |
1323 | ty::TyInt(ast::TyI32) => convert_val!(i32, Int, i64), | |
1324 | ty::TyInt(ast::TyI64) => convert_val!(i64, Int, i64), | |
c34b1796 | 1325 | |
b039eaaf SL |
1326 | ty::TyUint(ast::TyU8) => convert_val!(u8, Uint, u64), |
1327 | ty::TyUint(ast::TyU16) => convert_val!(u16, Uint, u64), | |
1328 | ty::TyUint(ast::TyU32) => convert_val!(u32, Uint, u64), | |
1329 | ty::TyUint(ast::TyU64) => convert_val!(u64, Uint, u64), | |
c34b1796 | 1330 | |
b039eaaf SL |
1331 | ty::TyFloat(ast::TyF32) => convert_val!(f32, Float, f64), |
1332 | ty::TyFloat(ast::TyF64) => convert_val!(f64, Float, f64), | |
c34b1796 | 1333 | _ => Err(ErrKind::CannotCast), |
85aaf69f SL |
1334 | } |
1335 | } | |
1336 | ||
b039eaaf | 1337 | fn lit_to_const(lit: &ast::Lit, ty_hint: Option<Ty>) -> ConstVal { |
223e47cc | 1338 | match lit.node { |
b039eaaf SL |
1339 | ast::LitStr(ref s, _) => Str((*s).clone()), |
1340 | ast::LitByteStr(ref data) => { | |
e9174d1e | 1341 | ByteStr(data.clone()) |
1a4d82fc | 1342 | } |
b039eaaf SL |
1343 | ast::LitByte(n) => Uint(n as u64), |
1344 | ast::LitChar(n) => Uint(n as u64), | |
1345 | ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => Int(n as i64), | |
1346 | ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => { | |
85aaf69f | 1347 | match ty_hint.map(|ty| &ty.sty) { |
62682a34 SL |
1348 | Some(&ty::TyUint(_)) => Uint(n), |
1349 | _ => Int(n as i64) | |
85aaf69f SL |
1350 | } |
1351 | } | |
b039eaaf SL |
1352 | ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) | |
1353 | ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => Int(-(n as i64)), | |
1354 | ast::LitInt(n, ast::UnsignedIntLit(_)) => Uint(n), | |
1355 | ast::LitFloat(ref n, _) | | |
1356 | ast::LitFloatUnsuffixed(ref n) => { | |
62682a34 | 1357 | Float(n.parse::<f64>().unwrap() as f64) |
1a4d82fc | 1358 | } |
b039eaaf | 1359 | ast::LitBool(b) => Bool(b) |
223e47cc LB |
1360 | } |
1361 | } | |
1362 | ||
62682a34 | 1363 | pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> { |
85aaf69f | 1364 | Some(match (a, b) { |
62682a34 SL |
1365 | (&Int(a), &Int(b)) => a.cmp(&b), |
1366 | (&Uint(a), &Uint(b)) => a.cmp(&b), | |
1367 | (&Float(a), &Float(b)) => { | |
85aaf69f SL |
1368 | // This is pretty bad but it is the existing behavior. |
1369 | if a == b { | |
1370 | Ordering::Equal | |
1371 | } else if a < b { | |
1372 | Ordering::Less | |
1373 | } else { | |
1374 | Ordering::Greater | |
1375 | } | |
1376 | } | |
62682a34 SL |
1377 | (&Str(ref a), &Str(ref b)) => a.cmp(b), |
1378 | (&Bool(a), &Bool(b)) => a.cmp(&b), | |
e9174d1e | 1379 | (&ByteStr(ref a), &ByteStr(ref b)) => a.cmp(b), |
85aaf69f SL |
1380 | _ => return None |
1381 | }) | |
223e47cc LB |
1382 | } |
1383 | ||
c1a9b12d SL |
1384 | pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, |
1385 | a: &Expr, | |
1386 | b: &Expr) -> Option<Ordering> { | |
b039eaaf | 1387 | let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked, None) { |
85aaf69f | 1388 | Ok(a) => a, |
c34b1796 AL |
1389 | Err(e) => { |
1390 | tcx.sess.span_err(a.span, &e.description()); | |
85aaf69f SL |
1391 | return None; |
1392 | } | |
1393 | }; | |
b039eaaf | 1394 | let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked, None) { |
85aaf69f | 1395 | Ok(b) => b, |
c34b1796 AL |
1396 | Err(e) => { |
1397 | tcx.sess.span_err(b.span, &e.description()); | |
85aaf69f SL |
1398 | return None; |
1399 | } | |
1400 | }; | |
1401 | compare_const_vals(&a, &b) | |
223e47cc | 1402 | } |
92a42be0 SL |
1403 | |
1404 | ||
1405 | // returns Err if callee is not `Function` | |
1406 | // `e` is only used for error reporting/spans | |
1407 | fn get_fn_def<'a>(tcx: &'a ty::ctxt, | |
1408 | e: &hir::Expr, | |
1409 | callee: ConstVal) | |
1410 | -> Result<(&'a hir::FnDecl, &'a hir::Block, hir::Constness), ConstEvalErr> { | |
1411 | let did = match callee { | |
1412 | Function(did) => did, | |
1413 | callee => signal!(e, CallOn(callee)), | |
1414 | }; | |
1415 | debug!("fn call: {:?}", tcx.map.get_if_local(did)); | |
1416 | match tcx.map.get_if_local(did) { | |
1417 | None => signal!(e, UnimplementedConstVal("calling non-local const fn")), // non-local | |
1418 | Some(ast_map::NodeItem(it)) => match it.node { | |
1419 | hir::ItemFn( | |
1420 | ref decl, | |
1421 | hir::Unsafety::Normal, | |
1422 | constness, | |
1423 | abi::Abi::Rust, | |
1424 | _, // ducktype generics? types are funky in const_eval | |
1425 | ref block, | |
1426 | ) => Ok((&**decl, &**block, constness)), | |
1427 | _ => signal!(e, NonConstPath), | |
1428 | }, | |
1429 | Some(ast_map::NodeImplItem(it)) => match it.node { | |
1430 | hir::ImplItemKind::Method( | |
1431 | hir::MethodSig { | |
1432 | ref decl, | |
1433 | unsafety: hir::Unsafety::Normal, | |
1434 | constness, | |
1435 | abi: abi::Abi::Rust, | |
1436 | .. // ducktype generics? types are funky in const_eval | |
1437 | }, | |
1438 | ref block, | |
1439 | ) => Ok((decl, block, constness)), | |
1440 | _ => signal!(e, NonConstPath), | |
1441 | }, | |
1442 | Some(ast_map::NodeTraitItem(..)) => signal!(e, NonConstPath), | |
1443 | Some(_) => unimplemented!(), | |
1444 | } | |
1445 | } |