Commit 418197b6c5
Changed files (3)
test
behavior
src/Sema.zig
@@ -16739,7 +16739,7 @@ fn elemPtrArray(
const index_u64 = index_val.toUnsignedInt();
// @intCast here because it would have been impossible to construct a value that
// required a larger index.
- const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, @intCast(usize, index_u64));
+ const elem_ptr = try array_ptr_val.elemPtrDirect(array_ptr_ty, sema.arena, @intCast(usize, index_u64));
return sema.addConstant(result_ty, elem_ptr);
}
}
src/value.zig
@@ -505,6 +505,7 @@ pub const Value = extern union {
.array_ptr = try payload.data.array_ptr.copy(arena),
.elem_ty = try payload.data.elem_ty.copy(arena),
.index = payload.data.index,
+ .direct = payload.data.direct,
},
};
return Value{ .ptr_otherwise = &new_payload.base };
@@ -2402,7 +2403,11 @@ pub const Value = extern union {
.decl_ref_mut => return val.castTag(.decl_ref_mut).?.data.decl.val.elemValueAdvanced(index, arena, buffer),
.elem_ptr => {
const data = val.castTag(.elem_ptr).?.data;
- return data.array_ptr.elemValueAdvanced(index + data.index, arena, buffer);
+ if (!data.direct)
+ return data.array_ptr.elemValueAdvanced(index + data.index, arena, buffer);
+
+ const underlying = try data.array_ptr.elemValueAdvanced(data.index, arena, buffer);
+ return underlying.elemValueAdvanced(index, arena, buffer);
},
// The child type of arrays which have only one possible value need
@@ -2465,12 +2470,25 @@ pub const Value = extern union {
/// Returns a pointer to the element value at the index.
pub fn elemPtr(val: Value, ty: Type, arena: Allocator, index: usize) Allocator.Error!Value {
+ return val.elemPtrAdvanced(ty, arena, index, false);
+ }
+
+ /// Returns a pointer to the element value at the index. The behavior
+ /// of this is slightly different for comptime; the "direct" means that
+ /// indexing indexes the referenced child value, not the parent array.
+ pub fn elemPtrDirect(val: Value, ty: Type, arena: Allocator, index: usize) Allocator.Error!Value {
+ return val.elemPtrAdvanced(ty, arena, index, true);
+ }
+
+ pub fn elemPtrAdvanced(val: Value, ty: Type, arena: Allocator, index: usize, direct: bool) Allocator.Error!Value {
const elem_ty = ty.elemType2();
const ptr_val = switch (val.tag()) {
.slice => val.castTag(.slice).?.data.ptr,
else => val,
};
+ // If the val is already an elem ptr, then we do ptr arithmetic logic
+ // and just move the index.
if (ptr_val.tag() == .elem_ptr) {
const elem_ptr = ptr_val.castTag(.elem_ptr).?.data;
if (elem_ptr.elem_ty.eql(elem_ty)) {
@@ -2478,6 +2496,12 @@ pub const Value = extern union {
.array_ptr = elem_ptr.array_ptr,
.elem_ty = elem_ptr.elem_ty,
.index = elem_ptr.index + index,
+
+ // Retain the direct preference. This enables a direct
+ // elem ptr (i.e. &arr[0]) to be bitcasted to a many-pointer
+ // with pointer arithmetic then casted back to a single
+ // pointer.
+ .direct = elem_ptr.direct,
});
}
}
@@ -2485,6 +2509,7 @@ pub const Value = extern union {
.array_ptr = ptr_val,
.elem_ty = elem_ty,
.index = index,
+ .direct = direct,
});
}
@@ -4194,6 +4219,7 @@ pub const Value = extern union {
array_ptr: Value,
elem_ty: Type,
index: usize,
+ direct: bool,
},
};
test/behavior/pointers.zig
@@ -437,3 +437,71 @@ test "indexing array with sentinel returns correct type" {
var s: [:0]const u8 = "abc";
try testing.expectEqualSlices(u8, "*const u8", @typeName(@TypeOf(&s[0])));
}
+
+test "element pointer to slice" {
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+ const S = struct {
+ fn doTheTest() !void {
+ var cases: [2][2]i32 = [_][2]i32{
+ [_]i32{ 0, 1 },
+ [_]i32{ 2, 3 },
+ };
+
+ const items: []i32 = &cases[0]; // *[2]i32
+ try testing.expect(items.len == 2);
+ try testing.expect(items[1] == 1);
+ try testing.expect(items[0] == 0);
+ }
+ };
+
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
+
+test "element pointer arithmetic to slice" {
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+ const S = struct {
+ fn doTheTest() !void {
+ var cases: [2][2]i32 = [_][2]i32{
+ [_]i32{ 0, 1 },
+ [_]i32{ 2, 3 },
+ };
+
+ const elem_ptr = &cases[0]; // *[2]i32
+ const many = @ptrCast([*][2]i32, elem_ptr);
+ const many_elem = @ptrCast(*[2]i32, &many[1]);
+ const items: []i32 = many_elem;
+ try testing.expect(items.len == 2);
+ try testing.expect(items[1] == 3);
+ try testing.expect(items[0] == 2);
+ }
+ };
+
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}
+
+test "array slicing to slice" {
+ if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
+ if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+
+ const S = struct {
+ fn doTheTest() !void {
+ var str: [5]i32 = [_]i32{ 1, 2, 3, 4, 5 };
+ var sub: *[2]i32 = str[1..3];
+ var slice: []i32 = sub; // used to cause failures
+ try testing.expect(slice.len == 2);
+ try testing.expect(slice[0] == 2);
+ }
+ };
+
+ try S.doTheTest();
+ comptime try S.doTheTest();
+}