about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2012-03-14 18:04:03 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2012-03-14 18:05:28 +0100
commitde79caa97ebfb86adaaae1c54237a94a610f94c0 (patch)
treee434939aacae6c1777d971d125be161cca1ea00a
parentc988800cf5611130e468761caa4d2f5adbdc6781 (diff)
downloadrust-de79caa97ebfb86adaaae1c54237a94a610f94c0.tar.gz
rust-de79caa97ebfb86adaaae1c54237a94a610f94c0.zip
Add crude support for casts in constant expressions
Only casts to integral and float types are supported

Closes #1975
-rw-r--r--src/rustc/driver/driver.rs3
-rw-r--r--src/rustc/middle/check_alt.rs10
-rw-r--r--src/rustc/middle/check_const.rs17
-rw-r--r--src/rustc/middle/trans/alt.rs26
-rw-r--r--src/rustc/middle/trans/base.rs68
-rw-r--r--src/rustc/middle/ty.rs2
-rw-r--r--src/rustc/middle/typeck.rs13
-rw-r--r--src/rustc/syntax/ast_util.rs63
8 files changed, 129 insertions, 73 deletions
diff --git a/src/rustc/driver/driver.rs b/src/rustc/driver/driver.rs
index 71f39a77e36..eb4f74105f2 100644
--- a/src/rustc/driver/driver.rs
+++ b/src/rustc/driver/driver.rs
@@ -148,7 +148,8 @@ fn compile_upto(sess: session, cfg: ast::crate_cfg,
         time(time_passes, "typechecking",
              bind typeck::check_crate(ty_cx, impl_map, crate));
     time(time_passes, "const checking",
-         bind middle::check_const::check_crate(sess, crate, method_map));
+         bind middle::check_const::check_crate(sess, crate, method_map,
+                                               ty_cx));
 
     if upto == cu_typeck { ret {crate: crate, tcx: some(ty_cx)}; }
 
diff --git a/src/rustc/middle/check_alt.rs b/src/rustc/middle/check_alt.rs
index 74c8250b236..334535efb42 100644
--- a/src/rustc/middle/check_alt.rs
+++ b/src/rustc/middle/check_alt.rs
@@ -258,19 +258,19 @@ fn pattern_supersedes(tcx: ty::ctxt, a: @pat, b: @pat) -> bool {
       }
       pat_lit(la) {
         alt b.node {
-          pat_lit(lb) { lit_expr_eq(la, lb) }
+          pat_lit(lb) { lit_expr_eq(tcx, la, lb) }
           _ { false }
         }
       }
       pat_range(begina, enda) {
         alt b.node {
           pat_lit(lb) {
-            compare_lit_exprs(begina, lb) <= 0 &&
-            compare_lit_exprs(enda, lb) >= 0
+            compare_lit_exprs(tcx, begina, lb) <= 0 &&
+            compare_lit_exprs(tcx, enda, lb) >= 0
           }
           pat_range(beginb, endb) {
-            compare_lit_exprs(begina, beginb) <= 0 &&
-            compare_lit_exprs(enda, endb) >= 0
+            compare_lit_exprs(tcx, begina, beginb) <= 0 &&
+            compare_lit_exprs(tcx, enda, endb) >= 0
           }
           _ { false }
         }
diff --git a/src/rustc/middle/check_const.rs b/src/rustc/middle/check_const.rs
index c36aeaede76..9098bf1fc11 100644
--- a/src/rustc/middle/check_const.rs
+++ b/src/rustc/middle/check_const.rs
@@ -3,11 +3,12 @@ import syntax::{visit, ast_util};
 import driver::session::session;
 import std::map::hashmap;
 
-fn check_crate(sess: session, crate: @crate, method_map: typeck::method_map) {
+fn check_crate(sess: session, crate: @crate, method_map: typeck::method_map,
+               tcx: ty::ctxt) {
     visit::visit_crate(*crate, false, visit::mk_vt(@{
         visit_item: check_item,
         visit_pat: check_pat,
-        visit_expr: bind check_expr(sess, method_map, _, _, _)
+        visit_expr: bind check_expr(sess, method_map, tcx, _, _, _)
         with *visit::default_visitor()
     }));
     sess.abort_if_errors();
@@ -42,8 +43,8 @@ fn check_pat(p: @pat, &&_is_const: bool, v: visit::vt<bool>) {
     }
 }
 
-fn check_expr(sess: session, method_map: typeck::method_map, e: @expr,
-              &&is_const: bool, v: visit::vt<bool>) {
+fn check_expr(sess: session, method_map: typeck::method_map, tcx: ty::ctxt,
+              e: @expr, &&is_const: bool, v: visit::vt<bool>) {
     if is_const {
         alt e.node {
           expr_unary(box(_), _) | expr_unary(uniq(_), _) |
@@ -63,6 +64,14 @@ fn check_expr(sess: session, method_map: typeck::method_map, e: @expr,
             }
           }
           expr_lit(_) {}
+          expr_cast(_, _) {
+            let ety = ty::expr_ty(tcx, e);
+            if !ty::type_is_numeric(ety) {
+                sess.span_err(e.span, "can not cast to `" +
+                              util::ppaux::ty_to_str(tcx, ety) +
+                              "` in a constant expression");
+            }
+          }
           _ {
             sess.span_err(e.span,
                           "constant contains unimplemented expression type");
diff --git a/src/rustc/middle/trans/alt.rs b/src/rustc/middle/trans/alt.rs
index fcdaf71acfa..eeb2c4136cf 100644
--- a/src/rustc/middle/trans/alt.rs
+++ b/src/rustc/middle/trans/alt.rs
@@ -23,12 +23,12 @@ enum opt {
     var(/* disr val */int, /* variant dids */{enm: def_id, var: def_id}),
     range(@ast::expr, @ast::expr)
 }
-fn opt_eq(a: opt, b: opt) -> bool {
+fn opt_eq(tcx: ty::ctxt, a: opt, b: opt) -> bool {
     alt (a, b) {
-      (lit(a), lit(b)) { ast_util::compare_lit_exprs(a, b) == 0 }
+      (lit(a), lit(b)) { ast_util::compare_lit_exprs(tcx, a, b) == 0 }
       (range(a1, a2), range(b1, b2)) {
-        ast_util::compare_lit_exprs(a1, b1) == 0 &&
-        ast_util::compare_lit_exprs(a2, b2) == 0
+        ast_util::compare_lit_exprs(tcx, a1, b1) == 0 &&
+        ast_util::compare_lit_exprs(tcx, a2, b2) == 0
       }
       (var(a, _), var(b, _)) { a == b }
       _ { false }
@@ -161,18 +161,18 @@ fn enter_opt(tcx: ty::ctxt, m: match, opt: opt, col: uint,
     enter_match(tcx.def_map, m, col, val) {|p|
         alt p.node {
           ast::pat_enum(_, subpats) {
-            if opt_eq(variant_opt(tcx, p.id), opt) { some(subpats) }
+            if opt_eq(tcx, variant_opt(tcx, p.id), opt) { some(subpats) }
             else { none }
           }
           ast::pat_ident(_, none) if pat_is_variant(tcx.def_map, p) {
-            if opt_eq(variant_opt(tcx, p.id), opt) { some([]) }
+            if opt_eq(tcx, variant_opt(tcx, p.id), opt) { some([]) }
             else { none }
           }
           ast::pat_lit(l) {
-            if opt_eq(lit(l), opt) { some([]) } else { none }
+            if opt_eq(tcx, lit(l), opt) { some([]) } else { none }
           }
           ast::pat_range(l1, l2) {
-            if opt_eq(range(l1, l2), opt) { some([]) } else { none }
+            if opt_eq(tcx, range(l1, l2), opt) { some([]) } else { none }
           }
           _ { some(vec::from_elem(variant_size, dummy)) }
         }
@@ -232,8 +232,8 @@ fn enter_uniq(dm: def_map, m: match, col: uint, val: ValueRef) -> match {
 }
 
 fn get_options(ccx: crate_ctxt, m: match, col: uint) -> [opt] {
-    fn add_to_set(&set: [opt], val: opt) {
-        for l in set { if opt_eq(l, val) { ret; } }
+    fn add_to_set(tcx: ty::ctxt, &set: [opt], val: opt) {
+        for l in set { if opt_eq(tcx, l, val) { ret; } }
         set += [val];
     }
 
@@ -241,12 +241,12 @@ fn get_options(ccx: crate_ctxt, m: match, col: uint) -> [opt] {
     for br in m {
         let cur = br.pats[col];
         if pat_is_variant(ccx.tcx.def_map, cur) {
-            add_to_set(found, variant_opt(ccx.tcx, br.pats[col].id));
+            add_to_set(ccx.tcx, found, variant_opt(ccx.tcx, br.pats[col].id));
         } else {
             alt cur.node {
-              ast::pat_lit(l) { add_to_set(found, lit(l)); }
+              ast::pat_lit(l) { add_to_set(ccx.tcx, found, lit(l)); }
               ast::pat_range(l1, l2) {
-                add_to_set(found, range(l1, l2));
+                add_to_set(ccx.tcx, found, range(l1, l2));
               }
               _ {}
             }
diff --git a/src/rustc/middle/trans/base.rs b/src/rustc/middle/trans/base.rs
index 67723337cfb..a4a2c9cfcd4 100644
--- a/src/rustc/middle/trans/base.rs
+++ b/src/rustc/middle/trans/base.rs
@@ -2592,6 +2592,17 @@ fn float_cast(bcx: block, lldsttype: TypeRef, llsrctype: TypeRef,
         } else { llsrc };
 }
 
+enum cast_kind { cast_pointer, cast_integral, cast_float,
+                 cast_enum, cast_other, }
+fn cast_type_kind(t: ty::t) -> cast_kind {
+    if ty::type_is_fp(t) { cast_float }
+    else if ty::type_is_unsafe_ptr(t) { cast_pointer }
+    else if ty::type_is_integral(t) { cast_integral }
+    else if ty::type_is_enum(t) { cast_enum }
+    else { cast_other }
+}
+
+
 fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id,
               dest: dest) -> block {
     let ccx = cx.ccx();
@@ -2605,55 +2616,48 @@ fn trans_cast(cx: block, e: @ast::expr, id: ast::node_id,
     let t_in = expr_ty(cx, e);
     let ll_t_out = type_of(ccx, t_out);
 
-    enum kind { pointer, integral, float, enum_, other, }
-    fn t_kind(t: ty::t) -> kind {
-        ret if ty::type_is_fp(t) { float }
-        else if ty::type_is_unsafe_ptr(t) { pointer }
-        else if ty::type_is_integral(t) { integral }
-        else if ty::type_is_enum(t) { enum_ }
-        else { other };
-    }
-    let k_in = t_kind(t_in);
-    let k_out = t_kind(t_out);
-    let s_in = k_in == integral && ty::type_is_signed(t_in);
+    let k_in = cast_type_kind(t_in);
+    let k_out = cast_type_kind(t_out);
+    let s_in = k_in == cast_integral && ty::type_is_signed(t_in);
 
     let newval =
         alt {in: k_in, out: k_out} {
-          {in: integral, out: integral} {
+          {in: cast_integral, out: cast_integral} {
             int_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val, s_in)
           }
-          {in: float, out: float} {
+          {in: cast_float, out: cast_float} {
             float_cast(e_res.bcx, ll_t_out, ll_t_in, e_res.val)
           }
-          {in: integral, out: float} {
+          {in: cast_integral, out: cast_float} {
             if s_in {
                 SIToFP(e_res.bcx, e_res.val, ll_t_out)
             } else { UIToFP(e_res.bcx, e_res.val, ll_t_out) }
           }
-          {in: float, out: integral} {
+          {in: cast_float, out: cast_integral} {
             if ty::type_is_signed(t_out) {
                 FPToSI(e_res.bcx, e_res.val, ll_t_out)
             } else { FPToUI(e_res.bcx, e_res.val, ll_t_out) }
           }
-          {in: integral, out: pointer} {
+          {in: cast_integral, out: cast_pointer} {
             IntToPtr(e_res.bcx, e_res.val, ll_t_out)
           }
-          {in: pointer, out: integral} {
+          {in: cast_pointer, out: cast_integral} {
             PtrToInt(e_res.bcx, e_res.val, ll_t_out)
           }
-          {in: pointer, out: pointer} {
+          {in: cast_pointer, out: cast_pointer} {
             PointerCast(e_res.bcx, e_res.val, ll_t_out)
           }
-          {in: enum_, out: integral} | {in: enum_, out: float} {
+          {in: cast_enum, out: cast_integral} |
+          {in: cast_enum, out: cast_float} {
             let cx = e_res.bcx;
             let llenumty = T_opaque_enum_ptr(ccx);
             let av_enum = PointerCast(cx, e_res.val, llenumty);
             let lldiscrim_a_ptr = GEPi(cx, av_enum, [0, 0]);
             let lldiscrim_a = Load(cx, lldiscrim_a_ptr);
             alt k_out {
-              integral {int_cast(e_res.bcx, ll_t_out,
-                                  val_ty(lldiscrim_a), lldiscrim_a, true)}
-              float {SIToFP(e_res.bcx, lldiscrim_a, ll_t_out)}
+              cast_integral {int_cast(e_res.bcx, ll_t_out,
+                                      val_ty(lldiscrim_a), lldiscrim_a, true)}
+              cast_float {SIToFP(e_res.bcx, lldiscrim_a, ll_t_out)}
               _ { ccx.sess.bug("translating unsupported cast.") }
             }
           }
@@ -4335,6 +4339,26 @@ fn trans_const_expr(cx: crate_ctxt, e: @ast::expr) -> ValueRef {
           }
         }
       }
+      ast::expr_cast(base, tp) {
+        let ety = ty::expr_ty(cx.tcx, e), llty = type_of(cx, ety);
+        let basety = ty::expr_ty(cx.tcx, base);
+        let v = trans_const_expr(cx, base);
+        alt check (cast_type_kind(basety), cast_type_kind(ety)) {
+          (cast_integral, cast_integral) {
+            let s = if ty::type_is_signed(basety) { True } else { False };
+            llvm::LLVMConstIntCast(v, llty, s)
+          }
+          (cast_integral, cast_float) {
+            if ty::type_is_signed(basety) { llvm::LLVMConstSIToFP(v, llty) }
+            else { llvm::LLVMConstUIToFP(v, llty) }
+          }
+          (cast_float, cast_float) { llvm::LLVMConstFPCast(v, llty) }
+          (cast_float, cast_integral) {
+            if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) }
+            else { llvm::LLVMConstFPToUI(v, llty) }
+          }
+        }
+      }
       _ { cx.sess.span_bug(e.span,
             "bad constant expression type in trans_const_expr"); }
     }
diff --git a/src/rustc/middle/ty.rs b/src/rustc/middle/ty.rs
index dd9a86c4b6a..e2272c429ce 100644
--- a/src/rustc/middle/ty.rs
+++ b/src/rustc/middle/ty.rs
@@ -2373,7 +2373,7 @@ fn enum_variants(cx: ctxt, id: ast::def_id) -> @[variant_info] {
                 alt variant.node.disr_expr {
                   some (ex) {
                     // FIXME: issue #1417
-                    disr_val = alt syntax::ast_util::eval_const_expr(ex) {
+                    disr_val = alt syntax::ast_util::eval_const_expr(cx, ex) {
                       ast_util::const_int(val) {val as int}
                       _ { cx.sess.bug("tag_variants: bad disr expr"); }
                     }
diff --git a/src/rustc/middle/typeck.rs b/src/rustc/middle/typeck.rs
index b5e6bd77695..74f8fcca4ad 100644
--- a/src/rustc/middle/typeck.rs
+++ b/src/rustc/middle/typeck.rs
@@ -1437,8 +1437,9 @@ fn check_lit(ccx: @crate_ctxt, lit: @ast::lit) -> ty::t {
     }
 }
 
-fn valid_range_bounds(from: @ast::expr, to: @ast::expr) -> bool {
-    ast_util::compare_lit_exprs(from, to) <= 0
+fn valid_range_bounds(tcx: ty::ctxt, from: @ast::expr, to: @ast::expr)
+    -> bool {
+    ast_util::compare_lit_exprs(tcx, from, to) <= 0
 }
 
 fn check_pat_variant(fcx: @fn_ctxt, map: pat_util::pat_id_map,
@@ -1516,7 +1517,7 @@ fn check_pat(fcx: @fn_ctxt, map: pat_util::pat_id_map, pat: @ast::pat,
             tcx.sess.span_err(pat.span, "mismatched types in range");
         } else if !ty::type_is_numeric(b_ty) {
             tcx.sess.span_err(pat.span, "non-numeric type used in range");
-        } else if !valid_range_bounds(begin, end) {
+        } else if !valid_range_bounds(tcx, begin, end) {
             tcx.sess.span_err(begin.span, "lower range bound must be less \
                                            than upper");
         }
@@ -2913,14 +2914,14 @@ fn check_enum_variants(ccx: @crate_ctxt, sp: span, vs: [ast::variant],
         alt v.node.disr_expr {
           some(e) {
             check_expr(fcx, e);
-            let cty = expr_ty(fcx.ccx.tcx, e);
-            let declty = ty::mk_int(fcx.ccx.tcx);
+            let cty = expr_ty(ccx.tcx, e);
+            let declty = ty::mk_int(ccx.tcx);
             demand::simple(fcx, e.span, declty, cty);
             // FIXME: issue #1417
             // Also, check_expr (from check_const pass) doesn't guarantee that
             // the expression in an form that eval_const_expr can handle, so
             // we may still get an internal compiler error
-            alt syntax::ast_util::eval_const_expr(e) {
+            alt syntax::ast_util::eval_const_expr(ccx.tcx, e) {
               syntax::ast_util::const_int(val) {
                 disr_val = val as int;
               }
diff --git a/src/rustc/syntax/ast_util.rs b/src/rustc/syntax/ast_util.rs
index a6e34f3b24d..e5a15edf1a3 100644
--- a/src/rustc/syntax/ast_util.rs
+++ b/src/rustc/syntax/ast_util.rs
@@ -250,38 +250,36 @@ enum const_val {
 }
 
 // FIXME: issue #1417
-fn eval_const_expr(e: @expr) -> const_val {
+fn eval_const_expr(tcx: middle::ty::ctxt, e: @expr) -> const_val {
+    import middle::ty;
     fn fromb(b: bool) -> const_val { const_int(b as i64) }
-    alt e.node {
+    alt check e.node {
       expr_unary(neg, inner) {
-        alt eval_const_expr(inner) {
+        alt check eval_const_expr(tcx, inner) {
           const_float(f) { const_float(-f) }
           const_int(i) { const_int(-i) }
           const_uint(i) { const_uint(-i) }
-          _ { fail "eval_const_expr: bad neg argument"; }
         }
       }
       expr_unary(not, inner) {
-        alt eval_const_expr(inner) {
+        alt check eval_const_expr(tcx, inner) {
           const_int(i) { const_int(!i) }
           const_uint(i) { const_uint(!i) }
-          _ { fail "eval_const_expr: bad not argument"; }
         }
       }
       expr_binary(op, a, b) {
-        alt (eval_const_expr(a), eval_const_expr(b)) {
+        alt check (eval_const_expr(tcx, a), eval_const_expr(tcx, b)) {
           (const_float(a), const_float(b)) {
-            alt op {
+            alt check op {
               add { const_float(a + b) } subtract { const_float(a - b) }
               mul { const_float(a * b) } div { const_float(a / b) }
               rem { const_float(a % b) } eq { fromb(a == b) }
               lt { fromb(a < b) } le { fromb(a <= b) } ne { fromb(a != b) }
               ge { fromb(a >= b) } gt { fromb(a > b) }
-              _ { fail "eval_const_expr: can't apply this binop to floats"; }
             }
           }
           (const_int(a), const_int(b)) {
-            alt op {
+            alt check op {
               add { const_int(a + b) } subtract { const_int(a - b) }
               mul { const_int(a * b) } div { const_int(a / b) }
               rem { const_int(a % b) } and | bitand { const_int(a & b) }
@@ -291,11 +289,11 @@ fn eval_const_expr(e: @expr) -> const_val {
               eq { fromb(a == b) } lt { fromb(a < b) }
               le { fromb(a <= b) } ne { fromb(a != b) }
               ge { fromb(a >= b) } gt { fromb(a > b) }
-              _ { fail "eval_const_expr: can't apply this binop to ints"; }
             }
+
           }
           (const_uint(a), const_uint(b)) {
-            alt op {
+            alt check op {
               add { const_uint(a + b) } subtract { const_uint(a - b) }
               mul { const_uint(a * b) } div { const_uint(a / b) }
               rem { const_uint(a % b) } and | bitand { const_uint(a & b) }
@@ -306,17 +304,38 @@ fn eval_const_expr(e: @expr) -> const_val {
               eq { fromb(a == b) } lt { fromb(a < b) }
               le { fromb(a <= b) } ne { fromb(a != b) }
               ge { fromb(a >= b) } gt { fromb(a > b) }
-              _ { fail "eval_const_expr: can't apply this binop to uints"; }
             }
           }
-          _ { fail "eval_constr_expr: bad binary arguments"; }
         }
       }
-      expr_lit(lit) { lit_to_const(lit) }
-      // Precondition?
-      _ {
-          fail "eval_const_expr: non-constant expression";
+      expr_cast(base, _) {
+        let ety = ty::expr_ty(tcx, e);
+        let base = eval_const_expr(tcx, base);
+        alt check ty::get(ety).struct {
+          ty::ty_float(_) {
+            alt check base {
+              const_uint(u) { const_float(u as f64) }
+              const_int(i) { const_float(i as f64) }
+              const_float(_) { base }
+            }
+          }
+          ty::ty_uint(_) {
+            alt check base {
+              const_uint(_) { base }
+              const_int(i) { const_uint(i as u64) }
+              const_float(f) { const_uint(f as u64) }
+            }
+          }
+          ty::ty_int(_) | ty::ty_bool {
+            alt check base {
+              const_uint(u) { const_int(u as i64) }
+              const_int(_) { base }
+              const_float(f) { const_int(f as i64) }
+            }
+          }
+        }
       }
+      expr_lit(lit) { lit_to_const(lit) }
     }
 }
 
@@ -375,11 +394,13 @@ fn compare_const_vals(a: const_val, b: const_val) -> int {
   }
 }
 
-fn compare_lit_exprs(a: @expr, b: @expr) -> int {
-  compare_const_vals(eval_const_expr(a), eval_const_expr(b))
+fn compare_lit_exprs(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> int {
+  compare_const_vals(eval_const_expr(tcx, a), eval_const_expr(tcx, b))
 }
 
-fn lit_expr_eq(a: @expr, b: @expr) -> bool { compare_lit_exprs(a, b) == 0 }
+fn lit_expr_eq(tcx: middle::ty::ctxt, a: @expr, b: @expr) -> bool {
+    compare_lit_exprs(tcx, a, b) == 0
+}
 
 fn lit_eq(a: @lit, b: @lit) -> bool {
     compare_const_vals(lit_to_const(a), lit_to_const(b)) == 0