Skip to content

Commit a615638

Browse files
committed
Sema: allow indexing tuple and vector pointers
Resolves: #13852 Resolves: #14705
1 parent 38fc826 commit a615638

File tree

5 files changed

+100
-17
lines changed

5 files changed

+100
-17
lines changed

src/Sema.zig

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9991,7 +9991,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
99919991
indexable_ty.fmt(mod),
99929992
});
99939993
errdefer msg.destroy(sema.gpa);
9994-
if (indexable_ty.zigTypeTag(mod) == .Array) {
9994+
if (indexable_ty.isIndexable(mod)) {
99959995
try sema.errNote(block, src, msg, "consider using '&' here", .{});
99969996
}
99979997
break :msg msg;
@@ -26088,8 +26088,19 @@ fn elemPtrOneLayerOnly(
2608826088
return block.addPtrElemPtr(indexable, elem_index, result_ty);
2608926089
},
2609026090
.One => {
26091-
assert(indexable_ty.childType(mod).zigTypeTag(mod) == .Array); // Guaranteed by checkIndexable
26092-
return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);
26091+
const child_ty = indexable_ty.childType(mod);
26092+
switch (child_ty.zigTypeTag(mod)) {
26093+
.Array, .Vector => {
26094+
return sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety);
26095+
},
26096+
.Struct => {
26097+
assert(child_ty.isTuple(mod));
26098+
const index_val = try sema.resolveConstValue(block, elem_index_src, elem_index, "tuple field access index must be comptime-known");
26099+
const index = @intCast(u32, index_val.toUnsignedInt(mod));
26100+
return sema.tupleFieldPtr(block, indexable_src, indexable, elem_index_src, index, false);
26101+
},
26102+
else => unreachable, // Guaranteed by checkIndexable
26103+
}
2609326104
},
2609426105
}
2609526106
}
@@ -26139,19 +26150,15 @@ fn elemVal(
2613926150
return block.addBinOp(.ptr_elem_val, indexable, elem_index);
2614026151
},
2614126152
.One => {
26142-
const array_ty = indexable_ty.childType(mod); // Guaranteed by checkIndexable
26143-
assert(array_ty.zigTypeTag(mod) == .Array);
26144-
26145-
if (array_ty.sentinel(mod)) |sentinel| {
26146-
// index must be defined since it can access out of bounds
26147-
if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| {
26148-
const index = @intCast(usize, index_val.toUnsignedInt(mod));
26149-
if (index == array_ty.arrayLen(mod)) {
26150-
return sema.addConstant(array_ty.childType(mod), sentinel);
26151-
}
26152-
}
26153+
arr_sent: {
26154+
const inner_ty = indexable_ty.childType(mod);
26155+
if (inner_ty.zigTypeTag(mod) != .Array) break :arr_sent;
26156+
const sentinel = inner_ty.sentinel(mod) orelse break :arr_sent;
26157+
const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent;
26158+
const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(mod));
26159+
if (index != inner_ty.arrayLen(mod)) break :arr_sent;
26160+
return sema.addConstant(inner_ty.childType(mod), sentinel);
2615326161
}
26154-
2615526162
const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety);
2615626163
return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src);
2615726164
},

src/type.zig

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2832,7 +2832,11 @@ pub const Type = struct {
28322832
.Array, .Vector => true,
28332833
.Pointer => switch (ty.ptrSize(mod)) {
28342834
.Slice, .Many, .C => true,
2835-
.One => ty.childType(mod).zigTypeTag(mod) == .Array,
2835+
.One => switch (ty.childType(mod).zigTypeTag(mod)) {
2836+
.Array, .Vector => true,
2837+
.Struct => ty.childType(mod).isTuple(mod),
2838+
else => false,
2839+
},
28362840
},
28372841
.Struct => ty.isTuple(mod),
28382842
else => false,
@@ -2845,7 +2849,11 @@ pub const Type = struct {
28452849
.Pointer => switch (ty.ptrSize(mod)) {
28462850
.Many, .C => false,
28472851
.Slice => true,
2848-
.One => ty.childType(mod).zigTypeTag(mod) == .Array,
2852+
.One => switch (ty.childType(mod).zigTypeTag(mod)) {
2853+
.Array, .Vector => true,
2854+
.Struct => ty.childType(mod).isTuple(mod),
2855+
else => false,
2856+
},
28492857
},
28502858
.Struct => ty.isTuple(mod),
28512859
else => false,

test/behavior/for.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,3 +463,19 @@ test "inline for with counter as the comptime-known" {
463463

464464
try expect(S.ok == 2);
465465
}
466+
467+
test "inline for on tuple pointer" {
468+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
469+
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
470+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
471+
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
472+
473+
const S = struct { u32, u32, u32 };
474+
var s: S = .{ 100, 200, 300 };
475+
476+
inline for (&s, 0..) |*x, i| {
477+
x.* = i;
478+
}
479+
480+
try expectEqual(S{ 0, 1, 2 }, s);
481+
}

test/behavior/tuple.zig

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const builtin = @import("builtin");
22
const std = @import("std");
33
const testing = std.testing;
4+
const assert = std.debug.assert;
45
const expect = testing.expect;
56
const expectEqualStrings = std.testing.expectEqualStrings;
67
const expectEqual = std.testing.expectEqual;
@@ -428,3 +429,27 @@ test "sentinel slice in tuple" {
428429

429430
_ = S;
430431
}
432+
433+
test "tuple pointer is indexable" {
434+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
435+
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
436+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
437+
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
438+
439+
const S = struct { u32, bool };
440+
441+
const x: S = .{ 123, true };
442+
comptime assert(@TypeOf(&(&x)[0]) == *const u32); // validate constness
443+
try expectEqual(@as(u32, 123), (&x)[0]);
444+
try expectEqual(true, (&x)[1]);
445+
446+
var y: S = .{ 123, true };
447+
comptime assert(@TypeOf(&(&y)[0]) == *u32); // validate constness
448+
try expectEqual(@as(u32, 123), (&y)[0]);
449+
try expectEqual(true, (&y)[1]);
450+
451+
(&y)[0] = 100;
452+
(&y)[1] = false;
453+
try expectEqual(@as(u32, 100), (&y)[0]);
454+
try expectEqual(false, (&y)[1]);
455+
}

test/behavior/vector.zig

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const std = @import("std");
22
const builtin = @import("builtin");
33
const mem = std.mem;
44
const math = std.math;
5+
const assert = std.debug.assert;
56
const expect = std.testing.expect;
67
const expectEqual = std.testing.expectEqual;
78

@@ -1343,3 +1344,29 @@ test "compare vectors with different element types" {
13431344
var b: @Vector(2, u9) = .{ 3, 0 };
13441345
try expectEqual(@Vector(2, bool){ true, false }, a < b);
13451346
}
1347+
1348+
test "vector pointer is indexable" {
1349+
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1350+
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1351+
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
1352+
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1353+
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1354+
if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO
1355+
1356+
const V = @Vector(2, u32);
1357+
1358+
const x: V = .{ 123, 456 };
1359+
comptime assert(@TypeOf(&(&x)[0]) == *const u32); // validate constness
1360+
try expectEqual(@as(u32, 123), (&x)[0]);
1361+
try expectEqual(@as(u32, 456), (&x)[1]);
1362+
1363+
var y: V = .{ 123, 456 };
1364+
comptime assert(@TypeOf(&(&y)[0]) == *u32); // validate constness
1365+
try expectEqual(@as(u32, 123), (&y)[0]);
1366+
try expectEqual(@as(u32, 456), (&y)[1]);
1367+
1368+
(&y)[0] = 100;
1369+
(&y)[1] = 200;
1370+
try expectEqual(@as(u32, 100), (&y)[0]);
1371+
try expectEqual(@as(u32, 200), (&y)[1]);
1372+
}

0 commit comments

Comments
 (0)