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
;
20 use syntax
::{ast, ast_map, ast_util, visit}
;
23 use core
::hashmap
::linear
::{LinearMap, LinearSet}
;
26 // This pass classifies expressions by their constant-ness.
28 // Constant-ness comes in 3 flavours:
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.
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
43 // fixed-size vectors and strings: [] and ""/_
44 // vector and string slices: &[] and &""
48 // floating point literals and operators
50 // copies of general constants
52 // (in theory, probably not at first: if/match on integer-const
53 // conditions / descriminants)
55 // - Non-constants: everything else.
64 pub fn join(a
: constness
, b
: constness
) -> constness
{
66 (integral_const
, integral_const
) => integral_const
,
67 (integral_const
, general_const
)
68 | (general_const
, integral_const
)
69 | (general_const
, general_const
) => general_const
,
74 pub fn join_all(cs
: &[constness
]) -> constness
{
75 vec
::foldl(integral_const
, cs
, |a
, b
| join(a
, *b
))
78 pub fn classify(e
: @expr
,
81 let did
= ast_util
::local_def(e
.id
);
82 match tcx
.ccache
.find(&did
) {
87 ast
::expr_lit(lit
) => {
90 ast
::lit_float(*) => general_const
,
95 ast
::expr_copy(inner
) |
96 ast
::expr_unary(_
, inner
) |
97 ast
::expr_paren(inner
) => {
101 ast
::expr_binary(_
, a
, b
) => {
102 join(classify(a
, tcx
),
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
)))
111 ast
::expr_vstore(e
, 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
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
)
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
)
144 ast
::expr_field(base
, _
, _
) => {
148 ast
::expr_index(base
, idx
) => {
149 join(classify(base
, tcx
),
153 ast
::expr_addr_of(ast
::m_imm
, base
) => {
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
)
165 tcx
.ccache
.insert(did
, cn
);
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
),
178 pub fn lookup_const_by_id(tcx
: ty
::ctxt
,
181 if ast_util
::is_local(def_id
) {
182 match tcx
.items
.find(&def_id
.node
) {
184 Some(&ast_map
::node_item(it
, _
)) => match it
.node
{
185 item_const(_
, const_expr
) => Some(const_expr
),
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()
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
),
212 pub fn lookup_constness(tcx
: ty
::ctxt
, e
: @expr
) -> constness
{
213 match lookup_const(tcx
, e
) {
215 let ty
= ty
::expr_ty(tcx
, rhs
);
216 if ty
::type_is_integral(ty
) {
226 pub fn process_crate(crate: @ast
::crate,
228 let v
= visit
::mk_simple_visitor(@visit
::SimpleVisitor
{
229 visit_expr_post
: |e
| { classify(e, tcx); }
,
230 .. *visit
::default_simple_visitor()
232 visit
::visit_crate(*crate, (), v
);
233 tcx
.sess
.abort_if_errors();
237 // FIXME (#33): this doesn't handle big integer/float literals correctly
238 // (nor does the rest of our literal handling).
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
)
255 pub fn eval_const_expr_partial(tcx
: middle
::ty
::ctxt
, e
: @expr
)
256 -> Result
<const_val
, ~str> {
258 fn fromb(b
: bool
) -> Result
<const_val
, ~str> { Ok(const_int(b as i64)) }
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
)
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")
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
))) => {
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
)),
294 _
=> Err(~"Can't do this op on floats")
297 (Ok(const_int(a
)), Ok(const_int(b
))) => {
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
)),
319 (Ok(const_uint(a
)), Ok(const_uint(b
))) => {
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
)),
341 // shifts can have any integral type as their rhs
342 (Ok(const_int(a
)), Ok(const_uint(b
))) => {
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")
349 (Ok(const_uint(a
)), Ok(const_int(b
))) => {
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")
356 (Ok(const_bool(a
)), Ok(const_bool(b
))) => {
357 Ok(const_bool(match op
{
365 _
=> return Err(~"Can't do this op on bools")
368 _
=> Err(~"Bad operands for binary")
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
{
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")
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")
391 ty
::ty_int(_
) | ty
::ty_bool
=> {
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")
399 _
=> Err(~"Can't cast this type")
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")
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")
416 pub fn lit_to_const(lit
: @lit
) -> const_val
{
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
)
430 pub fn compare_const_vals(a
: const_val
, b
: const_val
) -> int
{
432 (&const_int(a
), &const_int(b
)) => {
441 (&const_uint(a
), &const_uint(b
)) => {
450 (&const_float(a
), &const_float(b
)) => {
459 (&const_str(ref a
), &const_str(ref b
)) => {
462 } else if (*a
) < (*b
) {
468 (&const_bool(a
), &const_bool(b
)) => {
477 _
=> fail
!(~"compare_const_vals: ill-typed comparison")
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
))
485 pub fn lit_expr_eq(tcx
: middle
::ty
::ctxt
, a
: @expr
, b
: @expr
) -> bool
{
486 compare_lit_exprs(tcx
, a
, b
) == 0
489 pub fn lit_eq(a
: @lit
, b
: @lit
) -> bool
{
490 compare_const_vals(lit_to_const(a
), lit_to_const(b
)) == 0
497 // indent-tabs-mode: nil
499 // buffer-file-coding-system: utf-8-unix