Skip to content

Commit

Permalink
don't go through llvm for const indexing
Browse files Browse the repository at this point in the history
references as first class const values
  • Loading branch information
Oliver 'ker' Schneider committed May 25, 2015
1 parent ba0e1cd commit 71f35e6
Show file tree
Hide file tree
Showing 9 changed files with 261 additions and 35 deletions.
130 changes: 128 additions & 2 deletions src/librustc/middle/const_eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,10 @@ pub enum const_val {
const_binary(Rc<Vec<u8>>),
const_bool(bool),
Struct(ast::NodeId),
Tuple(ast::NodeId)
Tuple(ast::NodeId),
Array(ast::NodeId, u64),
Repeat(ast::NodeId, u64),
Ref(Box<const_val>),
}

pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat> {
Expand Down Expand Up @@ -353,11 +356,28 @@ pub enum ErrKind {
NegateOnBinary,
NegateOnStruct,
NegateOnTuple,
NegateOnArray,
NegateOnRepeat,
NegateOnRef,
NotOnFloat,
NotOnString,
NotOnBinary,
NotOnStruct,
NotOnTuple,
NotOnArray,
NotOnRepeat,
NotOnRef,

DerefInt,
DerefUInt,
DerefBool,
DerefString,
DerefFloat,
DerefBinary,
DerefTuple,
DerefStruct,
DerefArray,
DerefRepeat,

NegateWithOverflow(i64),
AddiWithOverflow(i64, i64),
Expand All @@ -377,6 +397,13 @@ pub enum ErrKind {
ExpectedConstTuple,
ExpectedConstStruct,
TupleIndexOutOfBounds,
IndexedNonVec,
IndexNotNatural,
IndexNotInt,
IndexOutOfBounds,
RepeatCountNotNatural,
RepeatCountNotInt,
MutableRef,

MiscBinaryOp,
MiscCatchAll,
Expand All @@ -398,11 +425,28 @@ impl ConstEvalErr {
NegateOnBinary => "negate on binary literal".into_cow(),
NegateOnStruct => "negate on struct".into_cow(),
NegateOnTuple => "negate on tuple".into_cow(),
NegateOnArray => "negate on array".into_cow(),
NegateOnRepeat => "negate on repeat".into_cow(),
NegateOnRef => "negate on ref".into_cow(),
NotOnFloat => "not on float or string".into_cow(),
NotOnString => "not on float or string".into_cow(),
NotOnBinary => "not on binary literal".into_cow(),
NotOnStruct => "not on struct".into_cow(),
NotOnTuple => "not on tuple".into_cow(),
NotOnArray => "not on array".into_cow(),
NotOnRepeat => "not on repeat".into_cow(),
NotOnRef => "not on ref".into_cow(),

DerefInt => "deref on int".into_cow(),
DerefUInt => "deref on unsigned int".into_cow(),
DerefBool => "deref on float".into_cow(),
DerefFloat => "deref on float".into_cow(),
DerefString => "deref on string".into_cow(),
DerefBinary => "deref on binary literal".into_cow(),
DerefStruct => "deref on struct".into_cow(),
DerefTuple => "deref on tuple".into_cow(),
DerefArray => "deref on array".into_cow(),
DerefRepeat => "deref on repeat".into_cow(),

NegateWithOverflow(..) => "attempted to negate with overflow".into_cow(),
AddiWithOverflow(..) => "attempted to add with overflow".into_cow(),
Expand All @@ -422,6 +466,13 @@ impl ConstEvalErr {
ExpectedConstTuple => "expected constant tuple".into_cow(),
ExpectedConstStruct => "expected constant struct".into_cow(),
TupleIndexOutOfBounds => "tuple index out of bounds".into_cow(),
IndexedNonVec => "indexing is only supported for arrays".into_cow(),
IndexNotNatural => "indices must be a natural number".into_cow(),
IndexNotInt => "indices must be integers".into_cow(),
IndexOutOfBounds => "array index out of bounds".into_cow(),
RepeatCountNotNatural => "repeat count must be a natural number".into_cow(),
RepeatCountNotInt => "repeat count must be integers".into_cow(),
MutableRef => "cannot get a mutable reference to a constant".into_cow(),

MiscBinaryOp => "bad operands for binary".into_cow(),
MiscCatchAll => "unsupported constant expr".into_cow(),
Expand Down Expand Up @@ -530,6 +581,15 @@ fn const_uint_not(a: u64, opt_ety: Option<UintTy>) -> const_val {
const_uint(!a & mask)
}

pub fn eval_const_index(tcx: &ty::ctxt, idx: &Expr) -> Result<u64, ConstEvalErr> {
match try!(eval_const_expr_partial(tcx, idx, None)) {
const_int(i) if i >= 0 => Ok(i as u64),
const_int(_) => signal!(idx, IndexNotNatural),
const_uint(i) => Ok(i),
_ => signal!(idx, IndexNotInt),
}
}

macro_rules! overflow_checking_body {
($a:ident, $b:ident, $ety:ident, $overflowing_op:ident,
lhs: $to_8_lhs:ident $to_16_lhs:ident $to_32_lhs:ident,
Expand Down Expand Up @@ -741,6 +801,9 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
const_binary(_) => signal!(e, NegateOnBinary),
const_val::Tuple(_) => signal!(e, NegateOnTuple),
const_val::Struct(..) => signal!(e, NegateOnStruct),
const_val::Array(..) => signal!(e, NegateOnArray),
const_val::Repeat(..) => signal!(e, NegateOnRepeat),
const_val::Ref(_) => signal!(e, NegateOnRef),
}
}
ast::ExprUnary(ast::UnNot, ref inner) => {
Expand All @@ -753,6 +816,24 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
const_binary(_) => signal!(e, NotOnBinary),
const_val::Tuple(_) => signal!(e, NotOnTuple),
const_val::Struct(..) => signal!(e, NotOnStruct),
const_val::Array(..) => signal!(e, NotOnArray),
const_val::Repeat(..) => signal!(e, NotOnRepeat),
const_val::Ref(_) => signal!(e, NotOnRef),
}
}
ast::ExprUnary(ast::UnDeref, ref inner) => {
match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
const_int(_) => signal!(e, DerefInt),
const_uint(_) => signal!(e, DerefUInt),
const_bool(_) => signal!(e, DerefBool),
const_str(_) => signal!(e, DerefString),
const_float(_) => signal!(e, DerefFloat),
const_binary(_) => signal!(e, DerefBinary),
const_val::Tuple(_) => signal!(e, DerefTuple),
const_val::Struct(..) => signal!(e, DerefStruct),
const_val::Array(..) => signal!(e, DerefArray),
const_val::Repeat(..) => signal!(e, DerefRepeat),
const_val::Ref(inner) => *inner,
}
}
ast::ExprBinary(op, ref a, ref b) => {
Expand Down Expand Up @@ -936,12 +1017,57 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
ast::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ety)),
None => const_int(0)
None => unreachable!(),
}
}
ast::ExprTup(_) => {
const_val::Tuple(e.id)
}
ast::ExprIndex(ref arr, ref idx) => {
let mut arr = try!(eval_const_expr_partial(tcx, arr, None));
while let const_val::Ref(inner) = arr {
arr = *inner;
}
let idx = try!(eval_const_index(tcx, idx));
match arr {
const_val::Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
const_val::Array(v, _) => if let ast::ExprVec(ref v) = tcx.map.expect_expr(v).node {
try!(eval_const_expr_partial(tcx, &*v[idx as usize], None))
} else {
unreachable!()
},

const_val::Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds),
const_val::Repeat(elem, _) => try!(eval_const_expr_partial(
tcx,
&*tcx.map.expect_expr(elem),
None,
)),

const_val::const_binary(ref data) if idx as usize >= data.len()
=> signal!(e, IndexOutOfBounds),
const_val::const_binary(data) => const_val::const_uint(data[idx as usize] as u64),

const_val::const_str(ref s) if idx as usize >= s.len()
=> signal!(e, IndexOutOfBounds),
const_val::const_str(_) => unimplemented!(), // there's no const_char type
_ => signal!(e, IndexedNonVec),
}
}
ast::ExprAddrOf(ast::Mutability::MutMutable, _) => signal!(e, MutableRef),
ast::ExprAddrOf(ast::Mutability::MutImmutable, ref expr) => {
const_val::Ref(Box::new(try!(eval_const_expr_partial(tcx, &**expr, None))))
},
ast::ExprVec(ref v) => const_val::Array(e.id, v.len() as u64),
ast::ExprRepeat(_, ref n) => const_val::Repeat(
e.id,
match try!(eval_const_expr_partial(tcx, &**n, None)) {
const_int(i) if i >= 0 => i as u64,
const_int(_) => signal!(e, RepeatCountNotNatural),
const_uint(i) => i,
_ => signal!(e, RepeatCountNotInt),
},
),
ast::ExprStruct(..) => {
const_val::Struct(e.id)
}
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/middle/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6242,7 +6242,10 @@ pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> usize {
const_eval::const_bool(_) => "boolean",
const_eval::const_binary(_) => "binary array",
const_eval::Struct(..) => "struct",
const_eval::Tuple(_) => "tuple"
const_eval::Tuple(_) => "tuple",
const_eval::Array(..) => "array",
const_eval::Repeat(..) => "repeat",
const_eval::Ref(_) => "ref",
};
span_err!(tcx.sess, count_expr.span, E0306,
"expected positive integer for repeat count, found {}",
Expand Down
71 changes: 66 additions & 5 deletions src/librustc_trans/trans/consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,51 @@ use syntax::ptr::P;

pub type FnArgMap<'a> = Option<&'a NodeMap<ValueRef>>;

fn const_val_for_idx<'a, 'tcx>(
cx: &CrateContext<'a, 'tcx>,
val: const_eval::const_val,
param_substs: &'tcx Substs<'tcx>,
ety: Ty<'tcx>,
fn_args: FnArgMap,
) -> ValueRef
{
let id2val = |id| {
let expr = cx.tcx().map.expect_expr(id);
const_expr(cx, expr, param_substs, fn_args).0
};
match val {
const_eval::const_int(i) => if let ty::ty_int(t) = ety.sty {
C_integral(Type::int_from_ty(cx, t), i as u64, true)
} else {
cx.sess().bug(
&format!(
"integer literal has type {}",
ty_to_string(cx.tcx(), ety),
)
)
},
const_eval::const_uint(u) => if let ty::ty_uint(t) = ety.sty {
C_integral(Type::uint_from_ty(cx, t), u as u64, false)
} else {
cx.sess().bug(
&format!(
"unsigned integer literal has type {}",
ty_to_string(cx.tcx(), ety),
)
)
},
const_eval::const_float(f) => C_floating(&f.to_string(), Type::f64(cx)),
const_eval::const_str(s) => C_str_slice(cx, s),
const_eval::const_binary(data) => addr_of(cx, C_bytes(cx, &data[..]), "binary"),
const_eval::const_bool(b) => C_bool(cx, b),
const_eval::Struct(id) => id2val(id),
const_eval::Tuple(id) => id2val(id),
const_eval::Array(id, _) => id2val(id),
const_eval::Repeat(id, _) => id2val(id),
const_eval::Ref(inner) => const_val_for_idx(cx, *inner, param_substs, ety, fn_args),
}
}

pub fn const_lit(cx: &CrateContext, e: &ast::Expr, lit: &ast::Lit)
-> ValueRef {
let _icx = push_ctxt("trans_lit");
Expand Down Expand Up @@ -596,12 +641,20 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
}

ast::ExprIndex(ref base, ref index) => {
match const_eval::eval_const_expr_partial(cx.tcx(), &e, None) {
Ok(val) => const_val_for_idx(cx, val, param_substs, ety, fn_args),
Err(const_eval::ConstEvalErr {
kind: const_eval::ErrKind::NonConstPath, ..
}) | Err(const_eval::ConstEvalErr {
kind: const_eval::ErrKind::MiscCatchAll, ..
}) => {
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
let iv = match const_eval::eval_const_expr_partial(cx.tcx(), &**index, None) {
Ok(const_eval::const_int(i)) => i as u64,
Ok(const_eval::const_uint(u)) => u,
_ => cx.sess().span_bug(index.span,
"index is not an integer-constant expression")
let iv = match const_eval::eval_const_index(cx.tcx(), &**index) {
Ok(idx) => idx,
Err(err) => {
cx.sess().span_err(err.span, &*err.description());
cx.sess().span_fatal(index.span, "constant evaluation of index failed");
},
};
let (arr, len) = match bt.sty {
ty::ty_vec(_, Some(u)) => (bv, C_uint(cx, u)),
Expand Down Expand Up @@ -638,12 +691,20 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
if iv >= len {
// FIXME #3170: report this earlier on in the const-eval
// pass. Reporting here is a bit late.
// Since some static-eval expressions aren't const-evaluable
// this has to stay
cx.sess().span_err(e.span,
"const index-expr is out of bounds");
C_undef(type_of::type_of(cx, bt).element_type())
} else {
const_get_elt(cx, arr, &[iv as c_uint])
}
},
Err(err) => {
cx.sess().span_err(err.span, &*err.description());
cx.sess().span_fatal(e.span,"constant indexing failed");
},
}
}
ast::ExprCast(ref base, _) => {
let t_cast = ety;
Expand Down
16 changes: 16 additions & 0 deletions src/test/compile-fail/array_const_index-0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2015 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.

static A: &'static [i32] = &[];
static B: i32 = (&A)[1]; //~ ERROR: const index-expr is out of bounds

fn main() {
let _ = B;
}
3 changes: 2 additions & 1 deletion src/test/compile-fail/const-array-oob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
// except according to those terms.

const FOO: [u32; 3] = [1, 2, 3];
const BAR: u32 = FOO[5]; //~ ERROR const index-expr is out of bounds
const BAR: u32 = FOO[5]; //~ ERROR array index out of bounds
//~^ ERROR constant indexing failed

fn main() {
let _ = BAR;
Expand Down
16 changes: 16 additions & 0 deletions src/test/run-pass/array_const_index-1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2015 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.


fn main() {
const arr: [i32; 6] = [42, 43, 44, 45, 46, 47];
const idx: usize = 3;
const val: i32 = arr[idx];
}
Loading

0 comments on commit 71f35e6

Please sign in to comment.