1 // Copyright 2012 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.
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.
13 use metadata::csearch;
14 use middle::astencode;
21 use syntax::{ast, ast_map, ast_util, visit};
24 use std::oldmap::HashMap;
27 // This pass classifies expressions by their constant-ness.
29 // Constant-ness comes in 3 flavours:
31 // - Integer-constants: can be evaluated by the frontend all the way down
32 // to their actual value. They are used in a few places (enum
33 // discriminants, switch arms) and are a subset of
34 // general-constants. They cover all the integer and integer-ish
35 // literals (nil, bool, int, uint, char, iNN, uNN) and all integer
36 // operators and copies applied to them.
38 // - General-constants: can be evaluated by LLVM but not necessarily by
39 // the frontend; usually due to reliance on target-specific stuff such
40 // as "where in memory the value goes" or "what floating point mode the
41 // target uses". This _includes_ integer-constants, plus the following
44 // fixed-size vectors and strings: [] and ""/_
45 // vector and string slices: &[] and &""
49 // floating point literals and operators
51 // copies of general constants
53 // (in theory, probably not at first: if/match on integer-const
54 // conditions / descriminants)
56 // - Non-constants: everything else.
65 pub fn join(a: constness, b: constness) -> constness {
67 (integral_const, integral_const) => integral_const,
68 (integral_const, general_const)
69 | (general_const, integral_const)
70 | (general_const, general_const) => general_const,
75 pub fn join_all(cs: &[constness]) -> constness {
76 vec::foldl(integral_const, cs, |a, b| join(a, *b))
79 pub fn classify(e: @expr,
80 def_map: resolve::DefMap,
83 let did = ast_util::local_def(e.id);
84 match tcx.ccache.find(&did) {
89 ast::expr_lit(lit) => {
92 ast::lit_float(*) => general_const,
97 ast::expr_copy(inner) |
98 ast::expr_unary(_, inner) |
99 ast::expr_paren(inner) => {
100 classify(inner, def_map, tcx)
103 ast::expr_binary(_, a, b) => {
104 join(classify(a, def_map, tcx),
105 classify(b, def_map, tcx))
108 ast::expr_tup(ref es) |
109 ast::expr_vec(ref es, ast::m_imm) => {
110 join_all(vec::map(*es, |e| classify(*e, def_map, tcx)))
113 ast::expr_vstore(e, vstore) => {
115 ast::expr_vstore_fixed(_) |
116 ast::expr_vstore_slice => classify(e, def_map, tcx),
117 ast::expr_vstore_uniq |
118 ast::expr_vstore_box |
119 ast::expr_vstore_mut_box |
120 ast::expr_vstore_mut_slice => non_const
124 ast::expr_struct(_, ref fs, None) => {
125 let cs = do vec::map((*fs)) |f| {
126 if f.node.mutbl == ast::m_imm {
127 classify(f.node.expr, def_map, tcx)
135 ast::expr_cast(base, _) => {
136 let ty = ty::expr_ty(tcx, e);
137 let base = classify(base, def_map, tcx);
138 if ty::type_is_integral(ty) {
139 join(integral_const, base)
140 } else if ty::type_is_fp(ty) {
141 join(general_const, base)
147 ast::expr_field(base, _, _) => {
148 classify(base, def_map, tcx)
151 ast::expr_index(base, idx) => {
152 join(classify(base, def_map, tcx),
153 classify(idx, def_map, tcx))
156 ast::expr_addr_of(ast::m_imm, base) => {
157 classify(base, def_map, tcx)
160 // FIXME: (#3728) we can probably do something CCI-ish
161 // surrounding nonlocal constants. But we don't yet.
162 ast::expr_path(_) => {
163 lookup_constness(tcx, e)
168 tcx.ccache.insert(did, cn);
174 pub fn lookup_const(tcx: ty::ctxt, e: @expr) -> Option<@expr> {
175 match tcx.def_map.find(&e.id) {
176 Some(ast::def_const(def_id)) => lookup_const_by_id(tcx, def_id),
181 pub fn lookup_const_by_id(tcx: ty::ctxt,
184 if ast_util::is_local(def_id) {
185 match tcx.items.find(&def_id.node) {
187 Some(ast_map::node_item(it, _)) => match it.node {
188 item_const(_, const_expr) => Some(const_expr),
194 let maps = astencode::Maps {
195 mutbl_map: HashMap(),
197 last_use_map: HashMap(),
198 method_map: HashMap(),
199 vtable_map: HashMap(),
200 write_guard_map: HashMap(),
201 moves_map: HashMap(),
202 capture_map: HashMap()
204 match csearch::maybe_get_item_ast(tcx, def_id,
205 |a, b, c, d| astencode::decode_inlined_item(a, b, maps, /*bar*/ copy c, d)) {
206 csearch::found(ast::ii_item(item)) => match item.node {
207 item_const(_, const_expr) => Some(const_expr),
215 pub fn lookup_constness(tcx: ty::ctxt, e: @expr) -> constness {
216 match lookup_const(tcx, e) {
218 let ty = ty::expr_ty(tcx, rhs);
219 if ty::type_is_integral(ty) {
229 pub fn process_crate(crate: @ast::crate,
230 def_map: resolve::DefMap,
232 let v = visit::mk_simple_visitor(@visit::SimpleVisitor {
233 visit_expr_post: |e| { classify(e, def_map, tcx); },
234 .. *visit::default_simple_visitor()
236 visit::visit_crate(*crate, (), v);
237 tcx.sess.abort_if_errors();
241 // FIXME (#33): this doesn't handle big integer/float literals correctly
242 // (nor does the rest of our literal handling).
252 pub fn eval_const_expr(tcx: middle::ty::ctxt, e: @expr) -> const_val {
253 match eval_const_expr_partial(tcx, e) {
254 Ok(ref r) => (/*bad*/copy *r),
255 Err(ref s) => fail!(/*bad*/copy *s)
259 pub fn eval_const_expr_partial(tcx: middle::ty::ctxt, e: @expr)
260 -> Result<const_val, ~str> {
262 fn fromb(b: bool) -> Result<const_val, ~str> { Ok(const_int(b as i64)) }
264 expr_unary(neg, inner) => {
265 match eval_const_expr_partial(tcx, inner) {
266 Ok(const_float(f)) => Ok(const_float(-f)),
267 Ok(const_int(i)) => Ok(const_int(-i)),
268 Ok(const_uint(i)) => Ok(const_uint(-i)),
269 Ok(const_str(_)) => Err(~"Negate on string"),
270 Ok(const_bool(_)) => Err(~"Negate on boolean"),
271 ref err => (/*bad*/copy *err)
274 expr_unary(not, inner) => {
275 match eval_const_expr_partial(tcx, inner) {
276 Ok(const_int(i)) => Ok(const_int(!i)),
277 Ok(const_uint(i)) => Ok(const_uint(!i)),
278 Ok(const_bool(b)) => Ok(const_bool(!b)),
279 _ => Err(~"Not on float or string")
282 expr_binary(op, a, b) => {
283 match (eval_const_expr_partial(tcx, a),
284 eval_const_expr_partial(tcx, b)) {
285 (Ok(const_float(a)), Ok(const_float(b))) => {
287 add => Ok(const_float(a + b)),
288 subtract => Ok(const_float(a - b)),
289 mul => Ok(const_float(a * b)),
290 div => Ok(const_float(a / b)),
291 rem => Ok(const_float(a % b)),
298 _ => Err(~"Can't do this op on floats")
301 (Ok(const_int(a)), Ok(const_int(b))) => {
303 add => Ok(const_int(a + b)),
304 subtract => Ok(const_int(a - b)),
305 mul => Ok(const_int(a * b)),
306 div if b == 0 => Err(~"divide by zero"),
307 div => Ok(const_int(a / b)),
308 rem if b == 0 => Err(~"modulo zero"),
309 rem => Ok(const_int(a % b)),
310 and | bitand => Ok(const_int(a & b)),
311 or | bitor => Ok(const_int(a | b)),
312 bitxor => Ok(const_int(a ^ b)),
313 shl => Ok(const_int(a << b)),
314 shr => Ok(const_int(a >> b)),
323 (Ok(const_uint(a)), Ok(const_uint(b))) => {
325 add => Ok(const_uint(a + b)),
326 subtract => Ok(const_uint(a - b)),
327 mul => Ok(const_uint(a * b)),
328 div if b == 0 => Err(~"divide by zero"),
329 div => Ok(const_uint(a / b)),
330 rem if b == 0 => Err(~"modulo zero"),
331 rem => Ok(const_uint(a % b)),
332 and | bitand => Ok(const_uint(a & b)),
333 or | bitor => Ok(const_uint(a | b)),
334 bitxor => Ok(const_uint(a ^ b)),
335 shl => Ok(const_uint(a << b)),
336 shr => Ok(const_uint(a >> b)),
345 // shifts can have any integral type as their rhs
346 (Ok(const_int(a)), Ok(const_uint(b))) => {
348 shl => Ok(const_int(a << b)),
349 shr => Ok(const_int(a >> b)),
350 _ => Err(~"Can't do this op on an int and uint")
353 (Ok(const_uint(a)), Ok(const_int(b))) => {
355 shl => Ok(const_uint(a << b)),
356 shr => Ok(const_uint(a >> b)),
357 _ => Err(~"Can't do this op on a uint and int")
360 (Ok(const_bool(a)), Ok(const_bool(b))) => {
361 Ok(const_bool(match op {
369 _ => return Err(~"Can't do this op on bools")
372 _ => Err(~"Bad operands for binary")
375 expr_cast(base, _) => {
376 let ety = ty::expr_ty(tcx, e);
377 let base = eval_const_expr_partial(tcx, base);
378 match ty::get(ety).sty {
381 Ok(const_uint(u)) => Ok(const_float(u as f64)),
382 Ok(const_int(i)) => Ok(const_float(i as f64)),
383 Ok(const_float(_)) => base,
384 _ => Err(~"Can't cast float to str")
389 Ok(const_uint(_)) => base,
390 Ok(const_int(i)) => Ok(const_uint(i as u64)),
391 Ok(const_float(f)) => Ok(const_uint(f as u64)),
392 _ => Err(~"Can't cast str to uint")
395 ty::ty_int(_) | ty::ty_bool => {
397 Ok(const_uint(u)) => Ok(const_int(u as i64)),
398 Ok(const_int(_)) => base,
399 Ok(const_float(f)) => Ok(const_int(f as i64)),
400 _ => Err(~"Can't cast str to int")
403 _ => Err(~"Can't cast this type")
407 match lookup_const(tcx, e) {
408 Some(actual_e) => eval_const_expr_partial(tcx, actual_e),
409 None => Err(~"Non-constant path in constant expr")
412 expr_lit(lit) => Ok(lit_to_const(lit)),
413 // If we have a vstore, just keep going; it has to be a string
414 expr_vstore(e, _) => eval_const_expr_partial(tcx, e),
415 expr_paren(e) => eval_const_expr_partial(tcx, e),
416 _ => Err(~"Unsupported constant expr")
420 pub fn lit_to_const(lit: @lit) -> const_val {
422 lit_str(s) => const_str(/*bad*/copy *s),
423 lit_int(n, _) => const_int(n),
424 lit_uint(n, _) => const_uint(n),
425 lit_int_unsuffixed(n) => const_int(n),
426 lit_float(n, _) => const_float(float::from_str(*n).get() as f64),
427 lit_float_unsuffixed(n) =>
428 const_float(float::from_str(*n).get() as f64),
429 lit_nil => const_int(0i64),
430 lit_bool(b) => const_bool(b)
434 pub fn compare_const_vals(a: const_val, b: const_val) -> int {
436 (&const_int(a), &const_int(b)) => {
445 (&const_uint(a), &const_uint(b)) => {
454 (&const_float(a), &const_float(b)) => {
463 (&const_str(ref a), &const_str(ref b)) => {
466 } else if (*a) < (*b) {
472 (&const_bool(a), &const_bool(b)) => {
481 _ => fail!(~"compare_const_vals: ill-typed comparison")
485 pub fn compare_lit_exprs(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> int {
486 compare_const_vals(eval_const_expr(tcx, a), eval_const_expr(tcx, b))
489 pub fn lit_expr_eq(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> bool {
490 compare_lit_exprs(tcx, a, b) == 0
493 pub fn lit_eq(a: @lit, b: @lit) -> bool {
494 compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0
501 // indent-tabs-mode: nil
503 // buffer-file-coding-system: utf-8-unix