about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGraydon Hoare <graydon@mozilla.com>2012-07-30 19:05:56 -0700
committerGraydon Hoare <graydon@mozilla.com>2012-07-30 19:06:06 -0700
commit290f079474670a14168104af5e3a32ff49abfae9 (patch)
tree3d9794c4141163461c0ec470820ff47f35f257e8
parentbf8c7739369da57555e65b2c187759e59edd96ae (diff)
downloadrust-290f079474670a14168104af5e3a32ff49abfae9.tar.gz
rust-290f079474670a14168104af5e3a32ff49abfae9.zip
Frontend bits for #2317, general const-expr classification.
-rw-r--r--src/libsyntax/ast_util.rs3
-rw-r--r--src/libsyntax/visit.rs10
-rw-r--r--src/rustc/driver/driver.rs3
-rw-r--r--src/rustc/middle/const_eval.rs189
-rw-r--r--src/rustc/middle/ty.rs4
5 files changed, 208 insertions, 1 deletions
diff --git a/src/libsyntax/ast_util.rs b/src/libsyntax/ast_util.rs
index 98b42ce79ae..265cb7d3749 100644
--- a/src/libsyntax/ast_util.rs
+++ b/src/libsyntax/ast_util.rs
@@ -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) {
diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs
index e279bf3a7f8..a542ca574ac 100644
--- a/src/libsyntax/visit.rs
+++ b/src/libsyntax/visit.rs
@@ -55,6 +55,7 @@ type visitor<E> =
       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 @@ type simple_visitor =
       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 mk_simple_visitor(v: simple_visitor) -> 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 mk_simple_visitor(v: simple_visitor) -> vt<()> {
                 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),
diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs
index 7be6103e34f..bd634a2b46e 100644
--- a/src/rustc/driver/driver.rs
+++ b/src/rustc/driver/driver.rs
@@ -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,
diff --git a/src/rustc/middle/const_eval.rs b/src/rustc/middle/const_eval.rs
index cdb1a3b10d0..50ec4e62175 100644
--- a/src/rustc/middle/const_eval.rs
+++ b/src/rustc/middle/const_eval.rs
@@ -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:
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index cb055055091..bae5b34f5a1 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -249,6 +249,7 @@ type ctxt =
       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 @@ type ty_param_bounds_and_ty = {bounds: @~[param_bounds],
 
 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(),