diff --git a/src/Sema.zig b/src/Sema.zig index 8a762d91d4..4a69c5b895 100644 --- a/src/Sema.zig +++ b/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); } } diff --git a/src/value.zig b/src/value.zig index d63452ee56..2ff196d491 100644 --- a/src/value.zig +++ b/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, }, }; diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 74089611db..96200e73fc 100644 --- a/test/behavior/pointers.zig +++ b/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(); +}