Frontend bits for #2317, general const-expr classification.

This commit is contained in:
Graydon Hoare 2012-07-30 19:05:56 -07:00
parent bf8c773936
commit 290f079474
5 changed files with 208 additions and 1 deletions

View File

@ -469,6 +469,9 @@ fn id_visitor(vfn: fn@(node_id)) -> visit::vt<()> {
vfn(e.id);
},
visit_expr_post: fn@(_e: @expr) {
},
visit_ty: fn@(t: @ty) {
alt t.node {
ty_path(_, id) {

View File

@ -55,6 +55,7 @@ fn tps_of_fn(fk: fn_kind) -> ~[ty_param] {
visit_pat: fn@(@pat, E, vt<E>),
visit_decl: fn@(@decl, E, vt<E>),
visit_expr: fn@(@expr, E, vt<E>),
visit_expr_post: fn@(@expr, E, vt<E>),
visit_ty: fn@(@ty, E, vt<E>),
visit_ty_params: fn@(~[ty_param], E, vt<E>),
visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id, E, vt<E>),
@ -74,6 +75,7 @@ fn default_visitor<E>() -> visitor<E> {
visit_pat: |a,b,c|visit_pat::<E>(a, b, c),
visit_decl: |a,b,c|visit_decl::<E>(a, b, c),
visit_expr: |a,b,c|visit_expr::<E>(a, b, c),
visit_expr_post: |_a,_b,_c| (),
visit_ty: |a,b,c|skip_ty::<E>(a, b, c),
visit_ty_params: |a,b,c|visit_ty_params::<E>(a, b, c),
visit_fn: |a,b,c,d,e,f,g|visit_fn::<E>(a, b, c, d, e, f, g),
@ -428,6 +430,7 @@ fn visit_expr<E>(ex: @expr, e: E, v: vt<E>) {
}
expr_mac(mac) { visit_mac(mac, e, v); }
}
v.visit_expr_post(ex, e, v);
}
fn visit_arm<E>(a: arm, e: E, v: vt<E>) {
@ -451,6 +454,7 @@ fn visit_arm<E>(a: arm, e: E, v: vt<E>) {
visit_pat: fn@(@pat),
visit_decl: fn@(@decl),
visit_expr: fn@(@expr),
visit_expr_post: fn@(@expr),
visit_ty: fn@(@ty),
visit_ty_params: fn@(~[ty_param]),
visit_fn: fn@(fn_kind, fn_decl, blk, span, node_id),
@ -472,6 +476,7 @@ fn default_simple_visitor() -> simple_visitor {
visit_pat: fn@(_p: @pat) { },
visit_decl: fn@(_d: @decl) { },
visit_expr: fn@(_e: @expr) { },
visit_expr_post: fn@(_e: @expr) { },
visit_ty: simple_ignore_ty,
visit_ty_params: fn@(_ps: ~[ty_param]) {},
visit_fn: fn@(_fk: fn_kind, _d: fn_decl, _b: blk, _sp: span,
@ -529,6 +534,9 @@ fn v_expr(f: fn@(@expr), ex: @expr, &&e: (), v: vt<()>) {
f(ex);
visit_expr(ex, e, v);
}
fn v_expr_post(f: fn@(@expr), ex: @expr, &&_e: (), _v: vt<()>) {
f(ex);
}
fn v_ty(f: fn@(@ty), ty: @ty, &&e: (), v: vt<()>) {
f(ty);
visit_ty(ty, e, v);
@ -578,6 +586,8 @@ fn v_class_item(f: fn@(@class_member),
visit_pat: |a,b,c|v_pat(v.visit_pat, a, b, c),
visit_decl: |a,b,c|v_decl(v.visit_decl, a, b, c),
visit_expr: |a,b,c|v_expr(v.visit_expr, a, b, c),
visit_expr_post: |a,b,c| v_expr_post(v.visit_expr_post,
a, b, c),
visit_ty: visit_ty,
visit_ty_params: |a,b,c|
v_ty_params(v.visit_ty_params, a, b, c),

View File

@ -197,6 +197,9 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
impl_map,
trait_map,
crate));
// These next two const passes can probably be merged
time(time_passes, ~"const marking", ||
middle::const_eval::process_crate(crate, def_map, ty_cx));
time(time_passes, ~"const checking", ||
middle::check_const::check_crate(sess, crate, ast_map, def_map,

View File

@ -1,4 +1,182 @@
import syntax::ast::*;
import syntax::{ast,ast_util,visit};
import ast::*;
//
// This pass classifies expressions by their constant-ness.
//
// Constant-ness comes in 3 flavours:
//
// - Integer-constants: can be evaluated by the frontend all the way down
// to their actual value. They are used in a few places (enum
// discriminants, switch arms) and are a subset of
// general-constants. They cover all the integer and integer-ish
// literals (nil, bool, int, uint, char, iNN, uNN) and all integer
// operators and copies applied to them.
//
// - General-constants: can be evaluated by LLVM but not necessarily by
// the frontend; usually due to reliance on target-specific stuff such
// as "where in memory the value goes" or "what floating point mode the
// target uses". This _includes_ integer-constants, plus the following
// constructors:
//
// fixed-size vectors and strings: []/_ and ""/_
// vector and string slices: &[] and &""
// tuples: (,)
// records: {...}
// enums: foo(...)
// floating point literals and operators
// & and * pointers
// copies of general constants
//
// (in theory, probably not at first: if/alt on integer-const
// conditions / descriminants)
//
// - Non-constants: everything else.
//
enum constness {
integral_const,
general_const,
non_const
}
fn join(a: constness, b: constness) -> constness {
alt (a,b) {
(integral_const, integral_const) { integral_const }
(integral_const, general_const)
| (general_const, integral_const)
| (general_const, general_const) { general_const }
_ { non_const }
}
}
fn join_all(cs: &[constness]) -> constness {
vec::foldl(integral_const, cs, join)
}
fn classify(e: @expr,
def_map: resolve3::DefMap,
tcx: ty::ctxt) -> constness {
let did = ast_util::local_def(e.id);
alt tcx.ccache.find(did) {
some(x) { x }
none {
let cn =
alt e.node {
ast::expr_lit(lit) {
alt lit.node {
ast::lit_str(*) |
ast::lit_float(*) { general_const }
_ { integral_const }
}
}
ast::expr_copy(inner) |
ast::expr_unary(_, inner) {
classify(inner, def_map, tcx)
}
ast::expr_binary(_, a, b) {
join(classify(a, def_map, tcx),
classify(b, def_map, tcx))
}
ast::expr_tup(es) |
ast::expr_vec(es, ast::m_imm) {
join_all(vec::map(es, |e| classify(e, def_map, tcx)))
}
ast::expr_vstore(e, vstore) {
alt vstore {
ast::vstore_fixed(_) |
ast::vstore_slice(_) { classify(e, def_map, tcx) }
ast::vstore_uniq |
ast::vstore_box { non_const }
}
}
ast::expr_rec(fs, none) {
let cs = do vec::map(fs) |f| {
if f.node.mutbl == ast::m_imm {
classify(f.node.expr, def_map, tcx)
} else {
non_const
}
};
join_all(cs)
}
ast::expr_cast(base, _) {
let ty = ty::expr_ty(tcx, e);
let base = classify(base, def_map, tcx);
if ty::type_is_integral(ty) {
join(integral_const, base)
} else if ty::type_is_fp(ty) {
join(general_const, base)
} else {
non_const
}
}
ast::expr_field(base, _, _) {
classify(base, def_map, tcx)
}
ast::expr_index(base, idx) {
join(classify(base, def_map, tcx),
classify(idx, def_map, tcx))
}
ast::expr_addr_of(ast::m_imm, base) {
classify(base, def_map, tcx)
}
// FIXME: #1272, we can probably do something CCI-ish
// surrounding nonlocal constants. But we don't yet.
ast::expr_path(_) {
alt def_map.find(e.id) {
some(ast::def_const(def_id)) {
if ast_util::is_local(def_id) {
let ty = ty::expr_ty(tcx, e);
if ty::type_is_integral(ty) {
integral_const
} else {
general_const
}
} else {
non_const
}
}
some(_) {
non_const
}
none {
tcx.sess.span_bug(e.span,
~"unknown path when \
classifying constants");
}
}
}
_ { non_const }
};
tcx.ccache.insert(did, cn);
cn
}
}
}
fn process_crate(crate: @ast::crate,
def_map: resolve3::DefMap,
tcx: ty::ctxt) {
let v = visit::mk_simple_visitor(@{
visit_expr_post: |e| { classify(e, def_map, tcx); }
with *visit::default_simple_visitor()
});
visit::visit_crate(*crate, (), v);
tcx.sess.abort_if_errors();
}
// FIXME (#33): this doesn't handle big integer/float literals correctly
// (nor does the rest of our literal handling).
@ -175,3 +353,12 @@ fn lit_expr_eq(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> bool {
fn lit_eq(a: @lit, b: @lit) -> bool {
compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0
}
// Local Variables:
// mode: rust
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:

View File

@ -249,6 +249,7 @@ enum ast_ty_to_ty_cache_entry {
freevars: freevars::freevar_map,
tcache: type_cache,
rcache: creader_cache,
ccache: constness_cache,
short_names_cache: hashmap<t, @~str>,
needs_drop_cache: hashmap<t, bool>,
needs_unwind_cleanup_cache: hashmap<t, bool>,
@ -534,6 +535,8 @@ fn param_bounds_to_kind(bounds: param_bounds) -> kind {
type type_cache = hashmap<ast::def_id, ty_param_bounds_and_ty>;
type constness_cache = hashmap<ast::def_id, const_eval::constness>;
type node_type_table = @smallintmap::smallintmap<t>;
fn mk_rcache() -> creader_cache {
@ -581,6 +584,7 @@ fn mk_ctxt(s: session::session,
freevars: freevars,
tcache: ast_util::new_def_hash(),
rcache: mk_rcache(),
ccache: ast_util::new_def_hash(),
short_names_cache: new_ty_hash(),
needs_drop_cache: new_ty_hash(),
needs_unwind_cleanup_cache: new_ty_hash(),