Add crude support for casts in constant expressions

Only casts to integral and float types are supported

Closes #1975
This commit is contained in:
Marijn Haverbeke 2012-03-14 18:04:03 +01:00
parent c988800cf5
commit de79caa97e
8 changed files with 129 additions and 73 deletions

View file

@ -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)}; }

View file

@ -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 }
}

View file

@ -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");

View file

@ -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));
}
_ {}
}

View file

@ -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"); }
}

View file

@ -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"); }
}

View file

@ -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;
}

View file

@ -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_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) }
// Precondition?
_ {
fail "eval_const_expr: non-constant expression";
}
}
}
@ -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