From 792181084244abc7ab361fd1db809c4217148304 Mon Sep 17 00:00:00 2001
From: Jeong YunWon <jeong@youknowone.org>
Date: Fri, 1 Mar 2013 18:40:23 +0900
Subject: [PATCH] Allow constant c-like enum to integral/float cast

---
 src/librustc/middle/trans/consts.rs | 35 +++++++++++++++++++++++++++--
 src/librustc/middle/ty.rs           |  2 +-
 src/test/run-pass/enum-cast.rs      | 31 +++++++++++++++++++++++++
 3 files changed, 65 insertions(+), 3 deletions(-)
 create mode 100644 src/test/run-pass/enum-cast.rs

diff --git a/src/librustc/middle/trans/consts.rs b/src/librustc/middle/trans/consts.rs
index 36cda3dfbe9..064dd2d0b06 100644
--- a/src/librustc/middle/trans/consts.rs
+++ b/src/librustc/middle/trans/consts.rs
@@ -10,7 +10,7 @@
 
 use core::prelude::*;
 
-use lib::llvm::{llvm, ValueRef, True, TypeRef, False};
+use lib::llvm::{llvm, ValueRef, TypeRef, Bool, True, False};
 use middle::const_eval;
 use middle::trans::base;
 use middle::trans::base::get_insn_ctxt;
@@ -304,7 +304,7 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
                    expr::cast_type_kind(ety)) {
 
               (expr::cast_integral, expr::cast_integral) => {
-                let s = if ty::type_is_signed(basety) { True } else { False };
+                let s = ty::type_is_signed(basety) as Bool;
                 llvm::LLVMConstIntCast(v, llty, s)
               }
               (expr::cast_integral, expr::cast_float) => {
@@ -321,6 +321,37 @@ pub fn const_expr(cx: @CrateContext, e: @ast::expr) -> ValueRef {
                 if ty::type_is_signed(ety) { llvm::LLVMConstFPToSI(v, llty) }
                 else { llvm::LLVMConstFPToUI(v, llty) }
               }
+              (expr::cast_enum, expr::cast_integral) |
+              (expr::cast_enum, expr::cast_float)  => {
+                let def = ty::resolve_expr(cx.tcx, base);
+                let (enum_did, variant_did) = match def {
+                    ast::def_variant(enum_did, variant_did) => {
+                        (enum_did, variant_did)
+                    }
+                    _ => cx.sess.bug(~"enum cast source is not enum")
+                };
+                // Note that we know this is a C-like (nullary) enum
+                // variant or we wouldn't have gotten here
+                let variants = ty::enum_variants(cx.tcx, enum_did);
+                let iv = if variants.len() == 1 {
+                    // Univariants don't have a discriminant field,
+                    // because there's only one value it could have:
+                    C_integral(T_i64(),
+                               variants[0].disr_val as u64, True)
+                } else {
+                    base::get_discrim_val(cx, e.span, enum_did, variant_did)
+                };
+                let ety_cast = expr::cast_type_kind(ety);
+                match ety_cast {
+                    expr::cast_integral => {
+                        let s = ty::type_is_signed(ety) as Bool;
+                        llvm::LLVMConstIntCast(iv, llty, s)
+                    }
+                    expr::cast_float => llvm::LLVMConstUIToFP(iv, llty),
+                    _ => cx.sess.bug(~"enum cast destination is not \
+                                       integral or float")
+                }
+              }
               _ => {
                 cx.sess.impossible_case(e.span,
                                         ~"bad combination of types for cast")
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index 8bca7f42b48..868f885dfcd 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -3017,7 +3017,7 @@ pub fn method_call_bounds(tcx: ctxt, method_map: typeck::method_map,
     }
 }
 
-fn resolve_expr(tcx: ctxt, expr: @ast::expr) -> ast::def {
+pub fn resolve_expr(tcx: ctxt, expr: @ast::expr) -> ast::def {
     match tcx.def_map.find(&expr.id) {
         Some(def) => def,
         None => {
diff --git a/src/test/run-pass/enum-cast.rs b/src/test/run-pass/enum-cast.rs
new file mode 100644
index 00000000000..eed5645776b
--- /dev/null
+++ b/src/test/run-pass/enum-cast.rs
@@ -0,0 +1,31 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+enum A { A1, A2 }
+enum B { B1=0, B2=2 }
+
+fn main () {
+    const c1: int = A2 as int;
+    const c2: int = B2 as int;
+    const c3: float = A2 as float;
+    const c4: float = B2 as float;
+    let a1 = A2 as int;
+    let a2 = B2 as int;
+    let a3 = A2 as float;
+    let a4 = B2 as float;
+    assert(c1 == 1);
+    assert(c2 == 2);
+    assert(c3 == 1.0);
+    assert(c4 == 2.0);
+    assert(a1 == 1);
+    assert(a2 == 2);
+    assert(a3 == 1.0);
+    assert(a4 == 2.0);
+}