]> git.proxmox.com Git - rustc.git/blob - src/librustc/middle/const_eval.rs
Imported Upstream version 0.6
[rustc.git] / src / librustc / middle / const_eval.rs
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.
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 core::prelude::*;
12
13 use metadata::csearch;
14 use middle::astencode;
15 use middle::ty;
16 use middle;
17
18 use core::float;
19 use core::vec;
20 use syntax::{ast, ast_map, ast_util, visit};
21 use syntax::ast::*;
22
23 use core::hashmap::linear::{LinearMap, LinearSet};
24
25 //
26 // This pass classifies expressions by their constant-ness.
27 //
28 // Constant-ness comes in 3 flavours:
29 //
30 // - Integer-constants: can be evaluated by the frontend all the way down
31 // to their actual value. They are used in a few places (enum
32 // discriminants, switch arms) and are a subset of
33 // general-constants. They cover all the integer and integer-ish
34 // literals (nil, bool, int, uint, char, iNN, uNN) and all integer
35 // operators and copies applied to them.
36 //
37 // - General-constants: can be evaluated by LLVM but not necessarily by
38 // the frontend; usually due to reliance on target-specific stuff such
39 // as "where in memory the value goes" or "what floating point mode the
40 // target uses". This _includes_ integer-constants, plus the following
41 // constructors:
42 //
43 // fixed-size vectors and strings: [] and ""/_
44 // vector and string slices: &[] and &""
45 // tuples: (,)
46 // records: {...}
47 // enums: foo(...)
48 // floating point literals and operators
49 // & and * pointers
50 // copies of general constants
51 //
52 // (in theory, probably not at first: if/match on integer-const
53 // conditions / descriminants)
54 //
55 // - Non-constants: everything else.
56 //
57
58 pub enum constness {
59 integral_const,
60 general_const,
61 non_const
62 }
63
64 pub fn join(a: constness, b: constness) -> constness {
65 match (a, b) {
66 (integral_const, integral_const) => integral_const,
67 (integral_const, general_const)
68 | (general_const, integral_const)
69 | (general_const, general_const) => general_const,
70 _ => non_const
71 }
72 }
73
74 pub fn join_all(cs: &[constness]) -> constness {
75 vec::foldl(integral_const, cs, |a, b| join(a, *b))
76 }
77
78 pub fn classify(e: @expr,
79 tcx: ty::ctxt)
80 -> constness {
81 let did = ast_util::local_def(e.id);
82 match tcx.ccache.find(&did) {
83 Some(&x) => x,
84 None => {
85 let cn =
86 match e.node {
87 ast::expr_lit(lit) => {
88 match lit.node {
89 ast::lit_str(*) |
90 ast::lit_float(*) => general_const,
91 _ => integral_const
92 }
93 }
94
95 ast::expr_copy(inner) |
96 ast::expr_unary(_, inner) |
97 ast::expr_paren(inner) => {
98 classify(inner, tcx)
99 }
100
101 ast::expr_binary(_, a, b) => {
102 join(classify(a, tcx),
103 classify(b, tcx))
104 }
105
106 ast::expr_tup(ref es) |
107 ast::expr_vec(ref es, ast::m_imm) => {
108 join_all(vec::map(*es, |e| classify(*e, tcx)))
109 }
110
111 ast::expr_vstore(e, vstore) => {
112 match vstore {
113 ast::expr_vstore_slice => classify(e, tcx),
114 ast::expr_vstore_uniq |
115 ast::expr_vstore_box |
116 ast::expr_vstore_mut_box |
117 ast::expr_vstore_mut_slice => non_const
118 }
119 }
120
121 ast::expr_struct(_, ref fs, None) => {
122 let cs = do vec::map((*fs)) |f| {
123 if f.node.mutbl == ast::m_imm {
124 classify(f.node.expr, tcx)
125 } else {
126 non_const
127 }
128 };
129 join_all(cs)
130 }
131
132 ast::expr_cast(base, _) => {
133 let ty = ty::expr_ty(tcx, e);
134 let base = classify(base, tcx);
135 if ty::type_is_integral(ty) {
136 join(integral_const, base)
137 } else if ty::type_is_fp(ty) {
138 join(general_const, base)
139 } else {
140 non_const
141 }
142 }
143
144 ast::expr_field(base, _, _) => {
145 classify(base, tcx)
146 }
147
148 ast::expr_index(base, idx) => {
149 join(classify(base, tcx),
150 classify(idx, tcx))
151 }
152
153 ast::expr_addr_of(ast::m_imm, base) => {
154 classify(base, tcx)
155 }
156
157 // FIXME: (#3728) we can probably do something CCI-ish
158 // surrounding nonlocal constants. But we don't yet.
159 ast::expr_path(_) => {
160 lookup_constness(tcx, e)
161 }
162
163 _ => non_const
164 };
165 tcx.ccache.insert(did, cn);
166 cn
167 }
168 }
169 }
170
171 pub fn lookup_const(tcx: ty::ctxt, e: @expr) -> Option<@expr> {
172 match tcx.def_map.find(&e.id) {
173 Some(&ast::def_const(def_id)) => lookup_const_by_id(tcx, def_id),
174 _ => None
175 }
176 }
177
178 pub fn lookup_const_by_id(tcx: ty::ctxt,
179 def_id: ast::def_id)
180 -> Option<@expr> {
181 if ast_util::is_local(def_id) {
182 match tcx.items.find(&def_id.node) {
183 None => None,
184 Some(&ast_map::node_item(it, _)) => match it.node {
185 item_const(_, const_expr) => Some(const_expr),
186 _ => None
187 },
188 Some(_) => None
189 }
190 } else {
191 let maps = astencode::Maps {
192 mutbl_map: @mut LinearSet::new(),
193 root_map: @mut LinearMap::new(),
194 last_use_map: @mut LinearMap::new(),
195 method_map: @mut LinearMap::new(),
196 vtable_map: @mut LinearMap::new(),
197 write_guard_map: @mut LinearSet::new(),
198 moves_map: @mut LinearSet::new(),
199 capture_map: @mut LinearMap::new()
200 };
201 match csearch::maybe_get_item_ast(tcx, def_id,
202 |a, b, c, d| astencode::decode_inlined_item(a, b, maps, /*bar*/ copy c, d)) {
203 csearch::found(ast::ii_item(item)) => match item.node {
204 item_const(_, const_expr) => Some(const_expr),
205 _ => None
206 },
207 _ => None
208 }
209 }
210 }
211
212 pub fn lookup_constness(tcx: ty::ctxt, e: @expr) -> constness {
213 match lookup_const(tcx, e) {
214 Some(rhs) => {
215 let ty = ty::expr_ty(tcx, rhs);
216 if ty::type_is_integral(ty) {
217 integral_const
218 } else {
219 general_const
220 }
221 }
222 None => non_const
223 }
224 }
225
226 pub fn process_crate(crate: @ast::crate,
227 tcx: ty::ctxt) {
228 let v = visit::mk_simple_visitor(@visit::SimpleVisitor {
229 visit_expr_post: |e| { classify(e, tcx); },
230 .. *visit::default_simple_visitor()
231 });
232 visit::visit_crate(*crate, (), v);
233 tcx.sess.abort_if_errors();
234 }
235
236
237 // FIXME (#33): this doesn't handle big integer/float literals correctly
238 // (nor does the rest of our literal handling).
239 #[deriving(Eq)]
240 pub enum const_val {
241 const_float(f64),
242 const_int(i64),
243 const_uint(u64),
244 const_str(~str),
245 const_bool(bool)
246 }
247
248 pub fn eval_const_expr(tcx: middle::ty::ctxt, e: @expr) -> const_val {
249 match eval_const_expr_partial(tcx, e) {
250 Ok(ref r) => (/*bad*/copy *r),
251 Err(ref s) => fail!(/*bad*/copy *s)
252 }
253 }
254
255 pub fn eval_const_expr_partial(tcx: middle::ty::ctxt, e: @expr)
256 -> Result<const_val, ~str> {
257 use middle::ty;
258 fn fromb(b: bool) -> Result<const_val, ~str> { Ok(const_int(b as i64)) }
259 match e.node {
260 expr_unary(neg, inner) => {
261 match eval_const_expr_partial(tcx, inner) {
262 Ok(const_float(f)) => Ok(const_float(-f)),
263 Ok(const_int(i)) => Ok(const_int(-i)),
264 Ok(const_uint(i)) => Ok(const_uint(-i)),
265 Ok(const_str(_)) => Err(~"Negate on string"),
266 Ok(const_bool(_)) => Err(~"Negate on boolean"),
267 ref err => (/*bad*/copy *err)
268 }
269 }
270 expr_unary(not, inner) => {
271 match eval_const_expr_partial(tcx, inner) {
272 Ok(const_int(i)) => Ok(const_int(!i)),
273 Ok(const_uint(i)) => Ok(const_uint(!i)),
274 Ok(const_bool(b)) => Ok(const_bool(!b)),
275 _ => Err(~"Not on float or string")
276 }
277 }
278 expr_binary(op, a, b) => {
279 match (eval_const_expr_partial(tcx, a),
280 eval_const_expr_partial(tcx, b)) {
281 (Ok(const_float(a)), Ok(const_float(b))) => {
282 match op {
283 add => Ok(const_float(a + b)),
284 subtract => Ok(const_float(a - b)),
285 mul => Ok(const_float(a * b)),
286 div => Ok(const_float(a / b)),
287 rem => Ok(const_float(a % b)),
288 eq => fromb(a == b),
289 lt => fromb(a < b),
290 le => fromb(a <= b),
291 ne => fromb(a != b),
292 ge => fromb(a >= b),
293 gt => fromb(a > b),
294 _ => Err(~"Can't do this op on floats")
295 }
296 }
297 (Ok(const_int(a)), Ok(const_int(b))) => {
298 match op {
299 add => Ok(const_int(a + b)),
300 subtract => Ok(const_int(a - b)),
301 mul => Ok(const_int(a * b)),
302 div if b == 0 => Err(~"divide by zero"),
303 div => Ok(const_int(a / b)),
304 rem if b == 0 => Err(~"modulo zero"),
305 rem => Ok(const_int(a % b)),
306 and | bitand => Ok(const_int(a & b)),
307 or | bitor => Ok(const_int(a | b)),
308 bitxor => Ok(const_int(a ^ b)),
309 shl => Ok(const_int(a << b)),
310 shr => Ok(const_int(a >> b)),
311 eq => fromb(a == b),
312 lt => fromb(a < b),
313 le => fromb(a <= b),
314 ne => fromb(a != b),
315 ge => fromb(a >= b),
316 gt => fromb(a > b)
317 }
318 }
319 (Ok(const_uint(a)), Ok(const_uint(b))) => {
320 match op {
321 add => Ok(const_uint(a + b)),
322 subtract => Ok(const_uint(a - b)),
323 mul => Ok(const_uint(a * b)),
324 div if b == 0 => Err(~"divide by zero"),
325 div => Ok(const_uint(a / b)),
326 rem if b == 0 => Err(~"modulo zero"),
327 rem => Ok(const_uint(a % b)),
328 and | bitand => Ok(const_uint(a & b)),
329 or | bitor => Ok(const_uint(a | b)),
330 bitxor => Ok(const_uint(a ^ b)),
331 shl => Ok(const_uint(a << b)),
332 shr => Ok(const_uint(a >> b)),
333 eq => fromb(a == b),
334 lt => fromb(a < b),
335 le => fromb(a <= b),
336 ne => fromb(a != b),
337 ge => fromb(a >= b),
338 gt => fromb(a > b),
339 }
340 }
341 // shifts can have any integral type as their rhs
342 (Ok(const_int(a)), Ok(const_uint(b))) => {
343 match op {
344 shl => Ok(const_int(a << b)),
345 shr => Ok(const_int(a >> b)),
346 _ => Err(~"Can't do this op on an int and uint")
347 }
348 }
349 (Ok(const_uint(a)), Ok(const_int(b))) => {
350 match op {
351 shl => Ok(const_uint(a << b)),
352 shr => Ok(const_uint(a >> b)),
353 _ => Err(~"Can't do this op on a uint and int")
354 }
355 }
356 (Ok(const_bool(a)), Ok(const_bool(b))) => {
357 Ok(const_bool(match op {
358 and => a && b,
359 or => a || b,
360 bitxor => a ^ b,
361 bitand => a & b,
362 bitor => a | b,
363 eq => a == b,
364 ne => a != b,
365 _ => return Err(~"Can't do this op on bools")
366 }))
367 }
368 _ => Err(~"Bad operands for binary")
369 }
370 }
371 expr_cast(base, _) => {
372 let ety = ty::expr_ty(tcx, e);
373 let base = eval_const_expr_partial(tcx, base);
374 match ty::get(ety).sty {
375 ty::ty_float(_) => {
376 match base {
377 Ok(const_uint(u)) => Ok(const_float(u as f64)),
378 Ok(const_int(i)) => Ok(const_float(i as f64)),
379 Ok(const_float(_)) => base,
380 _ => Err(~"Can't cast float to str")
381 }
382 }
383 ty::ty_uint(_) => {
384 match base {
385 Ok(const_uint(_)) => base,
386 Ok(const_int(i)) => Ok(const_uint(i as u64)),
387 Ok(const_float(f)) => Ok(const_uint(f as u64)),
388 _ => Err(~"Can't cast str to uint")
389 }
390 }
391 ty::ty_int(_) | ty::ty_bool => {
392 match base {
393 Ok(const_uint(u)) => Ok(const_int(u as i64)),
394 Ok(const_int(_)) => base,
395 Ok(const_float(f)) => Ok(const_int(f as i64)),
396 _ => Err(~"Can't cast str to int")
397 }
398 }
399 _ => Err(~"Can't cast this type")
400 }
401 }
402 expr_path(_) => {
403 match lookup_const(tcx, e) {
404 Some(actual_e) => eval_const_expr_partial(tcx, actual_e),
405 None => Err(~"Non-constant path in constant expr")
406 }
407 }
408 expr_lit(lit) => Ok(lit_to_const(lit)),
409 // If we have a vstore, just keep going; it has to be a string
410 expr_vstore(e, _) => eval_const_expr_partial(tcx, e),
411 expr_paren(e) => eval_const_expr_partial(tcx, e),
412 _ => Err(~"Unsupported constant expr")
413 }
414 }
415
416 pub fn lit_to_const(lit: @lit) -> const_val {
417 match lit.node {
418 lit_str(s) => const_str(/*bad*/copy *s),
419 lit_int(n, _) => const_int(n),
420 lit_uint(n, _) => const_uint(n),
421 lit_int_unsuffixed(n) => const_int(n),
422 lit_float(n, _) => const_float(float::from_str(*n).get() as f64),
423 lit_float_unsuffixed(n) =>
424 const_float(float::from_str(*n).get() as f64),
425 lit_nil => const_int(0i64),
426 lit_bool(b) => const_bool(b)
427 }
428 }
429
430 pub fn compare_const_vals(a: const_val, b: const_val) -> int {
431 match (&a, &b) {
432 (&const_int(a), &const_int(b)) => {
433 if a == b {
434 0
435 } else if a < b {
436 -1
437 } else {
438 1
439 }
440 }
441 (&const_uint(a), &const_uint(b)) => {
442 if a == b {
443 0
444 } else if a < b {
445 -1
446 } else {
447 1
448 }
449 }
450 (&const_float(a), &const_float(b)) => {
451 if a == b {
452 0
453 } else if a < b {
454 -1
455 } else {
456 1
457 }
458 }
459 (&const_str(ref a), &const_str(ref b)) => {
460 if (*a) == (*b) {
461 0
462 } else if (*a) < (*b) {
463 -1
464 } else {
465 1
466 }
467 }
468 (&const_bool(a), &const_bool(b)) => {
469 if a == b {
470 0
471 } else if a < b {
472 -1
473 } else {
474 1
475 }
476 }
477 _ => fail!(~"compare_const_vals: ill-typed comparison")
478 }
479 }
480
481 pub fn compare_lit_exprs(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> int {
482 compare_const_vals(eval_const_expr(tcx, a), eval_const_expr(tcx, b))
483 }
484
485 pub fn lit_expr_eq(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> bool {
486 compare_lit_exprs(tcx, a, b) == 0
487 }
488
489 pub fn lit_eq(a: @lit, b: @lit) -> bool {
490 compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0
491 }
492
493
494 // Local Variables:
495 // mode: rust
496 // fill-column: 78;
497 // indent-tabs-mode: nil
498 // c-basic-offset: 4
499 // buffer-file-coding-system: utf-8-unix
500 // End: