diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index e5100c32cc2da..951633eab2647 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -266,7 +266,10 @@ pub enum const_val { const_binary(Rc>), const_bool(bool), Struct(ast::NodeId), - Tuple(ast::NodeId) + Tuple(ast::NodeId), + Array(ast::NodeId, u64), + Repeat(ast::NodeId, u64), + Ref(Box), } pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P { @@ -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), @@ -377,6 +397,13 @@ pub enum ErrKind { ExpectedConstTuple, ExpectedConstStruct, TupleIndexOutOfBounds, + IndexedNonVec, + IndexNotNatural, + IndexNotInt, + IndexOutOfBounds, + RepeatCountNotNatural, + RepeatCountNotInt, + MutableRef, MiscBinaryOp, MiscCatchAll, @@ -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(), @@ -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(), @@ -530,6 +581,15 @@ fn const_uint_not(a: u64, opt_ety: Option) -> const_val { const_uint(!a & mask) } +pub fn eval_const_index(tcx: &ty::ctxt, idx: &Expr) -> Result { + 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, @@ -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) => { @@ -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) => { @@ -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) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index 5ff6ee3c8b081..53faca8b3f71b 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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 {}", diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index c11bb922f1cb2..3547fd9674426 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -43,6 +43,51 @@ use syntax::ptr::P; pub type FnArgMap<'a> = Option<&'a NodeMap>; +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"); @@ -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)), @@ -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; diff --git a/src/test/compile-fail/array_const_index-0.rs b/src/test/compile-fail/array_const_index-0.rs new file mode 100644 index 0000000000000..63a5cf65e3674 --- /dev/null +++ b/src/test/compile-fail/array_const_index-0.rs @@ -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 or the MIT license +// , 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; +} diff --git a/src/test/compile-fail/const-array-oob.rs b/src/test/compile-fail/const-array-oob.rs index 84d3529260824..94d5abb655c91 100644 --- a/src/test/compile-fail/const-array-oob.rs +++ b/src/test/compile-fail/const-array-oob.rs @@ -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; diff --git a/src/test/run-pass/array_const_index-1.rs b/src/test/run-pass/array_const_index-1.rs new file mode 100644 index 0000000000000..9cd85bbed99f4 --- /dev/null +++ b/src/test/run-pass/array_const_index-1.rs @@ -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 or the MIT license +// , 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]; +} diff --git a/src/test/run-pass/check-static-slice.rs b/src/test/run-pass/check-static-slice.rs index 8408597f35409..1ca2a8d20f29a 100644 --- a/src/test/run-pass/check-static-slice.rs +++ b/src/test/run-pass/check-static-slice.rs @@ -12,30 +12,33 @@ // and unsized) work properly. -const aa: [isize; 3] = [1, 2, 3]; -const ab: &'static [isize; 3] = &aa; -const ac: &'static [isize] = ab; -const ad: &'static [isize] = &aa; -const ae: &'static [isize; 3] = &[1, 2, 3]; -const af: &'static [isize] = &[1, 2, 3]; +const AA: [isize; 3] = [1, 2, 3]; +const AB: &'static [isize; 3] = &AA; +const AC: &'static [isize] = AB; +const AD: &'static [isize] = &AA; +const AE: &'static [isize; 3] = &[1, 2, 3]; +const AF: &'static [isize] = &[1, 2, 3]; -static ca: isize = aa[0]; -static cb: isize = ab[1]; -static cc: isize = ac[2]; -static cd: isize = ad[0]; -static ce: isize = ae[1]; -static cf: isize = af[2]; +static CA: isize = AA[0]; +static CB: isize = AB[1]; +static CC: isize = AC[2]; +static CD: isize = AD[0]; +static CE: isize = AE[1]; +static CF: isize = AF[2]; + +static AG: &'static isize = &AA[2]; fn main () { let b: &[isize] = &[1, 2, 3]; - assert!(ac == b); - assert!(ad == b); - assert!(af == b); + assert!(AC == b); + assert!(AD == b); + assert!(AF == b); + assert!(*AG == 3); - assert!(ca == 1); - assert!(cb == 2); - assert!(cc == 3); - assert!(cd == 1); - assert!(ce == 2); - assert!(cf == 3); + assert!(CA == 1); + assert!(CB == 2); + assert!(CC == 3); + assert!(CD == 1); + assert!(CE == 2); + assert!(CF == 3); } diff --git a/src/test/run-pass/const-enum-vec-index.rs b/src/test/run-pass/const-enum-vec-index.rs index 2f1cd8dbf9b12..61a744aab987c 100644 --- a/src/test/run-pass/const-enum-vec-index.rs +++ b/src/test/run-pass/const-enum-vec-index.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - enum E { V1(isize), V0 } const C: &'static [E] = &[E::V0, E::V1(0xDEADBEE)]; static C0: E = C[0]; static C1: E = C[1]; -const D: &'static [E; 2] = &[E::V0, E::V1(0xDEADBEE)]; -static D0: E = C[0]; -static D1: E = C[1]; +const D: &'static [E; 2] = &[E::V0, E::V1(0xDEAFBEE)]; +static D0: E = D[0]; +static D1: E = D[1]; pub fn main() { match C0 { @@ -32,7 +31,7 @@ pub fn main() { _ => panic!() } match D1 { - E::V1(n) => assert!(n == 0xDEADBEE), + E::V1(n) => assert!(n == 0xDEAFBEE), _ => panic!() } } diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs index 1cdb98a8bc05b..889f81474b6d0 100644 --- a/src/test/run-pass/const-str-ptr.rs +++ b/src/test/run-pass/const-str-ptr.rs @@ -18,6 +18,7 @@ const C: *const u8 = B as *const u8; pub fn main() { unsafe { let foo = &A as *const u8; + assert_eq!(foo, C); assert_eq!(str::from_utf8_unchecked(&A), "hi"); assert!(*C == A[0]); assert!(*(&B[0] as *const u8) == A[0]);