Skip to content

Commit 06df02b

Browse files
committed
Support array length.
1 parent e46a6aa commit 06df02b

27 files changed

+147
-66
lines changed

compiler/rustc_mir_dataflow/src/value_analysis.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,14 @@ impl<V: Clone + HasTop + HasBottom> State<V> {
568568
}
569569
}
570570

571+
/// Retrieve the value stored for a place, or ⊤ if it is not tracked.
572+
pub fn get_len(&self, place: PlaceRef<'_>, map: &Map) -> V {
573+
match map.find_len(place) {
574+
Some(place) => self.get_idx(place, map),
575+
None => V::TOP,
576+
}
577+
}
578+
571579
/// Retrieve the value stored for a place index, or ⊤ if it is not tracked.
572580
pub fn get_idx(&self, place: PlaceIndex, map: &Map) -> V {
573581
match &self.0 {
@@ -750,6 +758,25 @@ impl Map {
750758
}
751759
}
752760

761+
if let Some(ref_ty) = ty.builtin_deref(true) && let ty::Slice(..) = ref_ty.ty.kind() {
762+
let len = *self
763+
.projections
764+
.entry((place, TrackElem::DerefLen))
765+
.or_insert_with(|| {
766+
// Prepend new child to the linked list.
767+
let next = self.places.push(PlaceInfo::new(Some(TrackElem::DerefLen)));
768+
self.places[next].next_sibling = self.places[place].first_child;
769+
self.places[place].first_child = Some(next);
770+
next
771+
});
772+
773+
// Allocate a value slot if it doesn't have one.
774+
if self.places[len].value_index.is_none() {
775+
self.places[len].value_index = Some(self.value_count.into());
776+
self.value_count += 1;
777+
}
778+
}
779+
753780
// Recurse with all fields of this place.
754781
iter_fields(ty, tcx, ty::ParamEnv::reveal_all(), |variant, field, ty| {
755782
worklist.push_back((
@@ -818,6 +845,11 @@ impl Map {
818845
self.find_extra(place, [TrackElem::Discriminant])
819846
}
820847

848+
/// Locates the given place and applies `DerefLen`, if it exists in the tree.
849+
pub fn find_len(&self, place: PlaceRef<'_>) -> Option<PlaceIndex> {
850+
self.find_extra(place, [TrackElem::DerefLen])
851+
}
852+
821853
/// Iterate over all direct children.
822854
pub fn children(&self, parent: PlaceIndex) -> impl Iterator<Item = PlaceIndex> + '_ {
823855
Children::new(self, parent)
@@ -969,6 +1001,8 @@ pub enum TrackElem {
9691001
Field(FieldIdx),
9701002
Variant(VariantIdx),
9711003
Discriminant,
1004+
// Length of a slice.
1005+
DerefLen,
9721006
}
9731007

9741008
impl<V, T> TryFrom<ProjectionElem<V, T>> for TrackElem {
@@ -1108,6 +1142,9 @@ fn debug_with_context_rec<V: Debug + Eq>(
11081142
format!("{}.{}", place_str, field.index())
11091143
}
11101144
}
1145+
TrackElem::DerefLen => {
1146+
format!("Len(*{})", place_str)
1147+
}
11111148
};
11121149
debug_with_context_rec(child, &child_place_str, new, old, map, f)?;
11131150
}

compiler/rustc_mir_transform/src/dataflow_const_prop.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,24 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, '_, 'tcx> {
185185
}
186186
}
187187
}
188+
Rvalue::Cast(
189+
CastKind::PointerCoercion(ty::adjustment::PointerCoercion::Unsize),
190+
operand,
191+
_,
192+
) => {
193+
let pointer = self.handle_operand(operand, state);
194+
state.assign(target.as_ref(), pointer, self.map());
195+
196+
if let Some(target_len) = self.map().find_len(target.as_ref())
197+
&& let operand_ty = operand.ty(self.local_decls, self.tcx)
198+
&& let Some(operand_ty) = operand_ty.builtin_deref(true)
199+
&& let ty::Array(_, len) = operand_ty.ty.kind()
200+
&& let Some(len) = ConstantKind::Ty(*len).eval(self.tcx, self.param_env).try_to_scalar()
201+
{
202+
let scalar_len = self.wrap_scalar(len, self.tcx.types.usize);
203+
state.insert_value_idx(target_len, scalar_len, self.map());
204+
}
205+
}
188206
_ => self.super_assign(target, rvalue, state),
189207
}
190208
}
@@ -195,6 +213,19 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, '_, 'tcx> {
195213
state: &mut State<Self::Value>,
196214
) -> ValueOrPlace<Self::Value> {
197215
let val = match rvalue {
216+
Rvalue::Len(place) => {
217+
let place_ty = place.ty(self.local_decls, self.tcx);
218+
if let ty::Array(_, len) = place_ty.ty.kind() {
219+
ConstantKind::Ty(*len)
220+
.eval(self.tcx, self.param_env)
221+
.try_to_scalar()
222+
.map_or(FlatSet::Top, |len| self.wrap_scalar(len, self.tcx.types.usize))
223+
} else if let [ProjectionElem::Deref] = place.projection[..] {
224+
state.get_len(place.local.into(), self.map())
225+
} else {
226+
FlatSet::Top
227+
}
228+
}
198229
Rvalue::Cast(CastKind::IntToInt | CastKind::IntToFloat, operand, ty) => {
199230
match self.eval_operand(operand, state) {
200231
FlatSet::Elem(op) => self

tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind unreachable];
24+
+ _4 = const 4_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/array_index.main.ConstProp.32bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind continue];
24+
+ _4 = const 4_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind unreachable];
24+
+ _4 = const 4_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind unreachable];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/array_index.main.ConstProp.64bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u32, const 1_u32, const 2_u32, const 3_u32];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind continue];
24+
+ _4 = const 4_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 4_usize, const 2_usize) -> [success: bb1, unwind continue];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-abort.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
StorageLive(_5);
3535
StorageLive(_6);
3636
_6 = const 3_usize;
37-
_7 = const 3_usize;
37+
- _7 = Len((*_1));
3838
- _8 = Lt(_6, _7);
3939
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
40+
+ _7 = const 3_usize;
4041
+ _8 = const false;
4142
+ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
4243
}

tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.32bit.panic-unwind.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
StorageLive(_5);
3535
StorageLive(_6);
3636
_6 = const 3_usize;
37-
_7 = const 3_usize;
37+
- _7 = Len((*_1));
3838
- _8 = Lt(_6, _7);
3939
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
40+
+ _7 = const 3_usize;
4041
+ _8 = const false;
4142
+ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
4243
}

tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-abort.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
StorageLive(_5);
3535
StorageLive(_6);
3636
_6 = const 3_usize;
37-
_7 = const 3_usize;
37+
- _7 = Len((*_1));
3838
- _8 = Lt(_6, _7);
3939
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
40+
+ _7 = const 3_usize;
4041
+ _8 = const false;
4142
+ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind unreachable];
4243
}

tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.main.ConstProp.64bit.panic-unwind.diff

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@
3434
StorageLive(_5);
3535
StorageLive(_6);
3636
_6 = const 3_usize;
37-
_7 = const 3_usize;
37+
- _7 = Len((*_1));
3838
- _8 = Lt(_6, _7);
3939
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind continue];
40+
+ _7 = const 3_usize;
4041
+ _8 = const false;
4142
+ assert(const false, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 3_usize) -> [success: bb1, unwind continue];
4243
}

tests/mir-opt/const_prop/bad_op_unsafe_oob_for_slices.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// unit-test: ConstProp
22
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
3-
// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
4-
53
// EMIT_MIR_FOR_EACH_BIT_WIDTH
4+
65
// EMIT_MIR bad_op_unsafe_oob_for_slices.main.ConstProp.diff
76
#[allow(unconditional_panic)]
87
fn main() {

tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u8; 5000];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind unreachable];
24+
+ _4 = const 5000_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/large_array_index.main.ConstProp.32bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u8; 5000];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind continue];
24+
+ _4 = const 5000_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u8; 5000];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind unreachable];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind unreachable];
24+
+ _4 = const 5000_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind unreachable];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/large_array_index.main.ConstProp.64bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@
1818
_2 = [const 0_u8; 5000];
1919
StorageLive(_3);
2020
_3 = const 2_usize;
21-
_4 = Len(_2);
21+
- _4 = Len(_2);
2222
- _5 = Lt(_3, _4);
2323
- assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind continue];
24-
+ _5 = Lt(const 2_usize, _4);
25-
+ assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, const 2_usize) -> [success: bb1, unwind continue];
24+
+ _4 = const 5000_usize;
25+
+ _5 = const true;
26+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 5000_usize, const 2_usize) -> [success: bb1, unwind continue];
2627
}
2728

2829
bb1: {

tests/mir-opt/const_prop/large_array_index.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// unit-test: ConstProp
22
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
3-
// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
43
// EMIT_MIR_FOR_EACH_BIT_WIDTH
54

65
// EMIT_MIR large_array_index.main.ConstProp.diff

tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
_3 = [const 42_u32; 8];
2121
StorageLive(_4);
2222
_4 = const 2_usize;
23-
_5 = Len(_3);
23+
- _5 = Len(_3);
2424
- _6 = Lt(_4, _5);
2525
- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
26-
+ _6 = Lt(const 2_usize, _5);
27-
+ assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, const 2_usize) -> [success: bb1, unwind unreachable];
26+
+ _5 = const 8_usize;
27+
+ _6 = const true;
28+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
2829
}
2930

3031
bb1: {

tests/mir-opt/const_prop/repeat.main.ConstProp.32bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
_3 = [const 42_u32; 8];
2121
StorageLive(_4);
2222
_4 = const 2_usize;
23-
_5 = Len(_3);
23+
- _5 = Len(_3);
2424
- _6 = Lt(_4, _5);
2525
- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
26-
+ _6 = Lt(const 2_usize, _5);
27-
+ assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, const 2_usize) -> [success: bb1, unwind continue];
26+
+ _5 = const 8_usize;
27+
+ _6 = const true;
28+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
2829
}
2930

3031
bb1: {

tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
_3 = [const 42_u32; 8];
2121
StorageLive(_4);
2222
_4 = const 2_usize;
23-
_5 = Len(_3);
23+
- _5 = Len(_3);
2424
- _6 = Lt(_4, _5);
2525
- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind unreachable];
26-
+ _6 = Lt(const 2_usize, _5);
27-
+ assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, const 2_usize) -> [success: bb1, unwind unreachable];
26+
+ _5 = const 8_usize;
27+
+ _6 = const true;
28+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind unreachable];
2829
}
2930

3031
bb1: {

tests/mir-opt/const_prop/repeat.main.ConstProp.64bit.panic-unwind.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@
2020
_3 = [const 42_u32; 8];
2121
StorageLive(_4);
2222
_4 = const 2_usize;
23-
_5 = Len(_3);
23+
- _5 = Len(_3);
2424
- _6 = Lt(_4, _5);
2525
- assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, _4) -> [success: bb1, unwind continue];
26-
+ _6 = Lt(const 2_usize, _5);
27-
+ assert(move _6, "index out of bounds: the length is {} but the index is {}", move _5, const 2_usize) -> [success: bb1, unwind continue];
26+
+ _5 = const 8_usize;
27+
+ _6 = const true;
28+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 8_usize, const 2_usize) -> [success: bb1, unwind continue];
2829
}
2930

3031
bb1: {

tests/mir-opt/const_prop/repeat.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// unit-test: ConstProp
22
// EMIT_MIR_FOR_EACH_PANIC_STRATEGY
3-
// compile-flags: -Zmir-enable-passes=+NormalizeArrayLen
43
// EMIT_MIR_FOR_EACH_BIT_WIDTH
54

65
// EMIT_MIR repeat.main.ConstProp.diff

tests/mir-opt/const_prop/slice_len.main.ConstProp.32bit.panic-abort.diff

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,12 @@
2525
StorageDead(_3);
2626
StorageLive(_6);
2727
_6 = const 1_usize;
28-
_7 = Len((*_2));
28+
- _7 = Len((*_2));
2929
- _8 = Lt(_6, _7);
3030
- assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, _6) -> [success: bb1, unwind unreachable];
31-
+ _8 = Lt(const 1_usize, _7);
32-
+ assert(move _8, "index out of bounds: the length is {} but the index is {}", move _7, const 1_usize) -> [success: bb1, unwind unreachable];
31+
+ _7 = const 3_usize;
32+
+ _8 = const true;
33+
+ assert(const true, "index out of bounds: the length is {} but the index is {}", const 3_usize, const 1_usize) -> [success: bb1, unwind unreachable];
3334
}
3435

3536
bb1: {

0 commit comments

Comments
 (0)