about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/comp/middle/trans.rs16
-rw-r--r--src/comp/middle/ty.rs22
-rw-r--r--src/comp/middle/typeck.rs15
-rw-r--r--src/test/compile-fail/tag-variant-cast-non-nullary.rs12
-rw-r--r--src/test/run-pass/tag-variant-disr-val.rs4
5 files changed, 63 insertions, 6 deletions
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 998c944b6e0..2c5c45ed570 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -3017,7 +3017,7 @@ fn trans_cast(cx: @block_ctxt, e: @ast::expr, id: ast::node_id,
     check (type_has_static_size(ccx, t_out));
     let ll_t_out = type_of(ccx, e.span, t_out);
 
-    tag kind { pointer; integral; float; other; }
+    tag kind { pointer; integral; float; tag_; other; }
     fn t_kind(tcx: ty::ctxt, t: ty::t) -> kind {
         ret if ty::type_is_fp(tcx, t) {
                 float
@@ -3026,6 +3026,8 @@ fn trans_cast(cx: @block_ctxt, e: @ast::expr, id: ast::node_id,
                 pointer
             } else if ty::type_is_integral(tcx, t) {
                 integral
+            } else if ty::type_is_tag(tcx, t) {
+                tag_
             } else { other };
     }
     let k_in = t_kind(ccx.tcx, t_in);
@@ -3059,6 +3061,18 @@ fn trans_cast(cx: @block_ctxt, e: @ast::expr, id: ast::node_id,
           {in: pointer., out: pointer.} {
             PointerCast(e_res.bcx, e_res.val, ll_t_out)
           }
+          {in: tag_., out: integral.} | {in: tag_., out: float.} {
+            let cx = e_res.bcx;
+            let lltagty = T_opaque_tag_ptr(ccx);
+            let av_tag = PointerCast(cx, e_res.val, lltagty);
+            let lldiscrim_a_ptr = GEPi(cx, av_tag, [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)}
+            }
+          }
           _ { ccx.sess.bug("Translating unsupported cast.") }
         };
     ret store_in_dest(e_res.bcx, newval, dest);
diff --git a/src/comp/middle/ty.rs b/src/comp/middle/ty.rs
index dbd688de402..a79e4401569 100644
--- a/src/comp/middle/ty.rs
+++ b/src/comp/middle/ty.rs
@@ -173,6 +173,8 @@ export type_is_copyable;
 export type_is_tup_like;
 export type_is_str;
 export type_is_unique;
+export type_is_tag;
+export type_is_enum_like;
 export type_structurally_contains_uniques;
 export type_autoderef;
 export type_param;
@@ -1263,6 +1265,26 @@ fn type_is_pod(cx: ctxt, ty: t) -> bool {
     ret result;
 }
 
+fn type_is_tag(cx: ctxt, ty: t) -> bool {
+    alt struct(cx, ty) {
+      ty_tag(_, _) { ret true; }
+      _ { ret false;}
+    }
+}
+
+// Whether a type is enum like, that is a tag type with only nullary
+// constructors
+fn type_is_enum_like(cx: ctxt, ty: t) -> bool {
+    alt struct(cx, ty) {
+      ty_tag(did, tps) {
+        let variants = tag_variants(cx, did);
+        let some_n_ary = vec::any(*variants, {|v| vec::len(v.args) > 0u});
+        ret !some_n_ary;
+      }
+      _ { ret false;}
+    }
+}
+
 fn type_param(cx: ctxt, ty: t) -> option::t<uint> {
     alt struct(cx, ty) {
       ty_param(id, _) { ret some(id); }
diff --git a/src/comp/middle/typeck.rs b/src/comp/middle/typeck.rs
index be926322f64..1d701f9b9ab 100644
--- a/src/comp/middle/typeck.rs
+++ b/src/comp/middle/typeck.rs
@@ -211,6 +211,10 @@ fn type_is_scalar(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
     ret ty::type_is_scalar(fcx.ccx.tcx, typ_s);
 }
 
+fn type_is_enum_like(fcx: @fn_ctxt, sp: span, typ: ty::t) -> bool {
+    let typ_s = structurally_resolved_type(fcx, sp, typ);
+    ret ty::type_is_enum_like(fcx.ccx.tcx, typ_s);
+}
 
 // Parses the programmer's textual representation of a type into our internal
 // notion of a type. `getter` is a function that returns the type
@@ -2211,10 +2215,13 @@ fn check_expr_with_unifier(fcx: @fn_ctxt, expr: @ast::expr, unify: unifier,
           // This will be looked up later on
           ty::ty_iface(_, _) {}
           _ {
-            // FIXME there are more forms of cast to support, eventually.
-            if !(   type_is_scalar(fcx, expr.span, t_e)
-                 && type_is_scalar(fcx, expr.span, t_1)) {
-                tcx.sess.span_err(expr.span, "non-scalar cast: " +
+            let t_1_is_scalar = type_is_scalar(fcx, expr.span, t_1);
+            if type_is_enum_like(fcx,expr.span,t_e) && t_1_is_scalar {
+                /* this case is allowed */
+            } else if !(type_is_scalar(fcx,expr.span,t_e) && t_1_is_scalar) {
+                // FIXME there are more forms of cast to support, eventually.
+                tcx.sess.span_err(expr.span,
+                                  "non-scalar cast: " +
                                   ty_to_str(tcx, t_e) + " as " +
                                   ty_to_str(tcx, t_1));
             }
diff --git a/src/test/compile-fail/tag-variant-cast-non-nullary.rs b/src/test/compile-fail/tag-variant-cast-non-nullary.rs
new file mode 100644
index 00000000000..ecc518e75a1
--- /dev/null
+++ b/src/test/compile-fail/tag-variant-cast-non-nullary.rs
@@ -0,0 +1,12 @@
+//error-pattern: non-scalar cast
+// black and white have the same discriminator value ...
+
+tag non_nullary {
+    nullary;
+    other(int);
+}
+
+fn main() {
+    let v = nullary;
+    let val = v as int;
+}
diff --git a/src/test/run-pass/tag-variant-disr-val.rs b/src/test/run-pass/tag-variant-disr-val.rs
index a09de4794c4..1efe40d547d 100644
--- a/src/test/run-pass/tag-variant-disr-val.rs
+++ b/src/test/run-pass/tag-variant-disr-val.rs
@@ -17,7 +17,9 @@ fn main() {
 }
 
 fn test_color(color: color, val: int, name: str) unsafe {
-    assert unsafe::reinterpret_cast(color) == val;
+    //assert unsafe::reinterpret_cast(color) == val;
+    assert color as int == val;
+    assert color as float == val as float;
     assert get_color_alt(color) == name;
     assert get_color_if(color) == name;
 }