diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 21ece8f381ebf..1f2636ca1ad4e 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -253,6 +253,8 @@ pub enum ConstVal { Struct(ast::NodeId), Tuple(ast::NodeId), Function(DefId), + Array(ast::NodeId, u64), + Repeat(ast::NodeId, u64), } impl hash::Hash for ConstVal { @@ -267,6 +269,8 @@ impl hash::Hash for ConstVal { Struct(a) => a.hash(state), Tuple(a) => a.hash(state), Function(a) => a.hash(state), + Array(a, n) => { a.hash(state); n.hash(state) }, + Repeat(a, n) => { a.hash(state); n.hash(state) }, } } } @@ -287,6 +291,8 @@ impl PartialEq for ConstVal { (&Struct(a), &Struct(b)) => a == b, (&Tuple(a), &Tuple(b)) => a == b, (&Function(a), &Function(b)) => a == b, + (&Array(a, an), &Array(b, bn)) => (a == b) && (an == bn), + (&Repeat(a, an), &Repeat(b, bn)) => (a == b) && (an == bn), _ => false, } } @@ -307,6 +313,8 @@ impl ConstVal { Struct(_) => "struct", Tuple(_) => "tuple", Function(_) => "function definition", + Array(..) => "array", + Repeat(..) => "repeat", } } } @@ -415,6 +423,12 @@ pub enum ErrKind { ExpectedConstTuple, ExpectedConstStruct, TupleIndexOutOfBounds, + IndexedNonVec, + IndexNegative, + IndexNotInt, + IndexOutOfBounds, + RepeatCountNotNatural, + RepeatCountNotInt, MiscBinaryOp, MiscCatchAll, @@ -455,6 +469,12 @@ 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(), + IndexNegative => "indices must be non-negative integers".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(), MiscBinaryOp => "bad operands for binary".into_cow(), MiscCatchAll => "unsupported constant expr".into_cow(), @@ -1111,11 +1131,83 @@ pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, hir::ExprBlock(ref block) => { match block.expr { Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint, fn_args)), - None => Int(0) + None => unreachable!(), } } hir::ExprTup(_) => Tuple(e.id), hir::ExprStruct(..) => Struct(e.id), + hir::ExprIndex(ref arr, ref idx) => { + if !tcx.sess.features.borrow().const_indexing { + tcx.sess.span_err( + e.span, + "const indexing is an unstable feature"); + fileline_help!( + tcx.sess, + e.span, + "in Nightly builds, add `#![feature(const_indexing)]` to the crate \ + attributes to enable"); + signal!(e, NonConstPath) + } + let arr_hint = if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + UncheckedExprNoHint + }; + let arr = try!(eval_const_expr_partial(tcx, arr, arr_hint, fn_args)); + let idx_hint = if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + UncheckedExprHint(tcx.types.usize) + }; + let idx = match try!(eval_const_expr_partial(tcx, idx, idx_hint, fn_args)) { + Int(i) if i >= 0 => i as u64, + Int(_) => signal!(idx, IndexNegative), + Uint(i) => i, + _ => signal!(idx, IndexNotInt), + }; + match arr { + Array(_, n) if idx >= n => signal!(e, IndexOutOfBounds), + Array(v, _) => if let hir::ExprVec(ref v) = tcx.map.expect_expr(v).node { + try!(eval_const_expr_partial(tcx, &*v[idx as usize], ty_hint, fn_args)) + } else { + unreachable!() + }, + + Repeat(_, n) if idx >= n => signal!(e, IndexOutOfBounds), + Repeat(elem, _) => try!(eval_const_expr_partial( + tcx, + &*tcx.map.expect_expr(elem), + ty_hint, + fn_args, + )), + + ByteStr(ref data) if idx as usize >= data.len() + => signal!(e, IndexOutOfBounds), + ByteStr(data) => Uint(data[idx as usize] as u64), + + Str(ref s) if idx as usize >= s.len() + => signal!(e, IndexOutOfBounds), + Str(_) => unimplemented!(), // there's no const_char type + _ => signal!(e, IndexedNonVec), + } + } + hir::ExprVec(ref v) => Array(e.id, v.len() as u64), + hir::ExprRepeat(_, ref n) => { + let len_hint = if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + UncheckedExprHint(tcx.types.usize) + }; + Repeat( + e.id, + match try!(eval_const_expr_partial(tcx, &**n, len_hint, fn_args)) { + Int(i) if i >= 0 => i as u64, + Int(_) => signal!(e, RepeatCountNotNatural), + Uint(i) => i, + _ => signal!(e, RepeatCountNotInt), + }, + ) + }, hir::ExprTupField(ref base, index) => { let base_hint = if let ExprTypeChecked = ty_hint { ExprTypeChecked diff --git a/src/librustc_trans/trans/mir/constant.rs b/src/librustc_trans/trans/mir/constant.rs index 176cc298f9653..4de586543e9df 100644 --- a/src/librustc_trans/trans/mir/constant.rs +++ b/src/librustc_trans/trans/mir/constant.rs @@ -62,6 +62,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { ConstVal::Function(_) => { unimplemented!() } + ConstVal::Array(..) => { + unimplemented!() + } + ConstVal::Repeat(..) => { + unimplemented!() + } }; assert!(!ty.has_erasable_regions()); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index b450331d44036..8fc739946b46c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -179,6 +179,9 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Option, Status // Allows the definition of `const fn` functions. ("const_fn", "1.2.0", Some(24111), Active), + // Allows indexing into constant arrays. + ("const_indexing", "1.4.0", Some(29947), Active), + // Allows using #[prelude_import] on glob `use` items. // // rustc internal @@ -492,6 +495,7 @@ pub struct Features { /// #![feature] attrs for non-language (library) features pub declared_lib_features: Vec<(InternedString, Span)>, pub const_fn: bool, + pub const_indexing: bool, pub static_recursion: bool, pub default_type_parameter_fallback: bool, pub type_macros: bool, @@ -524,6 +528,7 @@ impl Features { declared_stable_lang_features: Vec::new(), declared_lib_features: Vec::new(), const_fn: false, + const_indexing: false, static_recursion: false, default_type_parameter_fallback: false, type_macros: false, @@ -1097,6 +1102,7 @@ fn check_crate_inner(cm: &CodeMap, span_handler: &SpanHandler, declared_stable_lang_features: accepted_features, declared_lib_features: unknown_features, const_fn: cx.has_feature("const_fn"), + const_indexing: cx.has_feature("const_indexing"), static_recursion: cx.has_feature("static_recursion"), default_type_parameter_fallback: cx.has_feature("default_type_parameter_fallback"), type_macros: cx.has_feature("type_macros"), 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/array_const_index-1.rs b/src/test/compile-fail/array_const_index-1.rs new file mode 100644 index 0000000000000..1f56cd8e87512 --- /dev/null +++ b/src/test/compile-fail/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. + +const A: [i32; 0] = []; +const 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-arith.rs b/src/test/compile-fail/const-array-oob-arith.rs new file mode 100644 index 0000000000000..9c07abdc36df7 --- /dev/null +++ b/src/test/compile-fail/const-array-oob-arith.rs @@ -0,0 +1,22 @@ +// 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. + +#![feature(const_indexing)] + +const ARR: [i32; 6] = [42, 43, 44, 45, 46, 47]; +const IDX: usize = 3; +const VAL: i32 = ARR[IDX]; +const BONG: [i32; (ARR[0] - 41) as usize] = [5]; +const BLUB: [i32; (ARR[0] - 40) as usize] = [5]; //~ ERROR: mismatched types +const BOO: [i32; (ARR[0] - 41) as usize] = [5, 99]; //~ ERROR: mismatched types + +fn main() { + let _ = VAL; +} diff --git a/src/test/compile-fail/const-array-oob.rs b/src/test/compile-fail/const-array-oob.rs index 84d3529260824..15426febbcdd7 100644 --- a/src/test/compile-fail/const-array-oob.rs +++ b/src/test/compile-fail/const-array-oob.rs @@ -8,8 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![feature(const_indexing)] + 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]; // no error, because the error below occurs before regular const eval + +const BLUB: [u32; FOO[4]] = [5, 6]; +//~^ ERROR array length constant evaluation error: array index out of bounds [E0250] 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..3e4504eb19575 --- /dev/null +++ b/src/test/run-pass/array_const_index-1.rs @@ -0,0 +1,18 @@ +// 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. + +#![feature(const_indexing)] + +fn main() { + const ARR: [i32; 6] = [42, 43, 44, 45, 46, 47]; + const IDX: usize = 3; + const VAL: i32 = ARR[IDX]; + const BLUB: [i32; (ARR[0] - 41) as usize] = [5]; +} diff --git a/src/test/run-pass/check-static-slice.rs b/src/test/run-pass/check-static-slice.rs index 9286991a4a38a..b5f2bea5a3881 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_eq!(ac, b); - assert_eq!(ad, b); - assert_eq!(af, b); + assert_eq!(AC, b); + assert_eq!(AD, b); + assert_eq!(AF, b); + assert_eq!(*AG, 3); - assert_eq!(ca, 1); - assert_eq!(cb, 2); - assert_eq!(cc, 3); - assert_eq!(cd, 1); - assert_eq!(ce, 2); - assert_eq!(cf, 3); + assert_eq!(CA, 1); + assert_eq!(CB, 2); + assert_eq!(CC, 3); + assert_eq!(CD, 1); + assert_eq!(CE, 2); + assert_eq!(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 56aec867f9d6b..4af6a6d10d5bf 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_eq!(n, 0xDEADBEE), + E::V1(n) => assert_eq!(n, 0xDEAFBEE), _ => panic!() } } diff --git a/src/test/run-pass/const-str-ptr.rs b/src/test/run-pass/const-str-ptr.rs index 93cae04a93301..4c5152ff90ffd 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_eq!(*C, A[0]); assert_eq!(*(&B[0] as *const u8), A[0]);