authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-03 16:08:06-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-20 18:33:00-07:00
log426af68b7d234bceba029d65f3388ad2376da649
treef52425484ae66d4cbd636a60a86b1bf91478f0c2
parent14bda4130a9a7f8b529b12bc74a1d6caa71b9545

compiler: require comptime vector indexes


13 files changed, 55 insertions(+), 104 deletions(-)

lib/std/Target.zig+1-1
...@@ -1187,7 +1187,7 @@ pub const Cpu = struct {...@@ -1187,7 +1187,7 @@ pub const Cpu = struct {
1187 pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize)));1187 pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize)));
1188 pub const ShiftInt = std.math.Log2Int(usize);1188 pub const ShiftInt = std.math.Log2Int(usize);
11891189
1190 pub const empty = Set{ .ints = [1]usize{0} ** usize_count };1190 pub const empty: Set = .{ .ints = @splat(0) };
11911191
1192 pub fn isEmpty(set: Set) bool {1192 pub fn isEmpty(set: Set) bool {
1193 return for (set.ints) |x| {1193 return for (set.ints) |x| {
lib/std/crypto/chacha20.zig+1-1
...@@ -216,7 +216,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -216,7 +216,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
216 }216 }
217217
218 fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void {218 fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void {
219 for (0..dm) |d| {219 inline for (0..dm) |d| {
220 for (0..4) |i| {220 for (0..4) |i| {
221 mem.writeInt(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d], .little);221 mem.writeInt(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d], .little);
222 mem.writeInt(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d], .little);222 mem.writeInt(u32, out[64 * d + 16 * i + 4 ..][0..4], x[i][1 + 4 * d], .little);
lib/std/json/static.zig+22-4
...@@ -389,7 +389,7 @@ pub fn innerParse(...@@ -389,7 +389,7 @@ pub fn innerParse(
389 switch (try source.peekNextTokenType()) {389 switch (try source.peekNextTokenType()) {
390 .array_begin => {390 .array_begin => {
391 // Typical array.391 // Typical array.
392 return internalParseArray(T, arrayInfo.child, arrayInfo.len, allocator, source, options);392 return internalParseArray(T, arrayInfo.child, allocator, source, options);
393 },393 },
394 .string => {394 .string => {
395 if (arrayInfo.child != u8) return error.UnexpectedToken;395 if (arrayInfo.child != u8) return error.UnexpectedToken;
...@@ -443,7 +443,7 @@ pub fn innerParse(...@@ -443,7 +443,7 @@ pub fn innerParse(
443 .vector => |vecInfo| {443 .vector => |vecInfo| {
444 switch (try source.peekNextTokenType()) {444 switch (try source.peekNextTokenType()) {
445 .array_begin => {445 .array_begin => {
446 return internalParseArray(T, vecInfo.child, vecInfo.len, allocator, source, options);446 return internalParseVector(T, vecInfo.child, vecInfo.len, allocator, source, options);
447 },447 },
448 else => return error.UnexpectedToken,448 else => return error.UnexpectedToken,
449 }449 }
...@@ -517,6 +517,25 @@ pub fn innerParse(...@@ -517,6 +517,25 @@ pub fn innerParse(
517}517}
518518
519fn internalParseArray(519fn internalParseArray(
520 comptime T: type,
521 comptime Child: type,
522 allocator: Allocator,
523 source: anytype,
524 options: ParseOptions,
525) !T {
526 assert(.array_begin == try source.next());
527
528 var r: T = undefined;
529 for (&r) |*elem| {
530 elem.* = try innerParse(Child, allocator, source, options);
531 }
532
533 if (.array_end != try source.next()) return error.UnexpectedToken;
534
535 return r;
536}
537
538fn internalParseVector(
520 comptime T: type,539 comptime T: type,
521 comptime Child: type,540 comptime Child: type,
522 comptime len: comptime_int,541 comptime len: comptime_int,
...@@ -527,8 +546,7 @@ fn internalParseArray(...@@ -527,8 +546,7 @@ fn internalParseArray(
527 assert(.array_begin == try source.next());546 assert(.array_begin == try source.next());
528547
529 var r: T = undefined;548 var r: T = undefined;
530 var i: usize = 0;549 inline for (0..len) |i| {
531 while (i < len) : (i += 1) {
532 r[i] = try innerParse(Child, allocator, source, options);550 r[i] = try innerParse(Child, allocator, source, options);
533 }551 }
534552
lib/std/meta.zig+2-3
...@@ -743,9 +743,8 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {...@@ -743,9 +743,8 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {
743 return true;743 return true;
744 },744 },
745 .vector => |info| {745 .vector => |info| {
746 var i: usize = 0;746 inline for (0..info.len) |i| {
747 while (i < info.len) : (i += 1) {747 if (a[i] != b[i]) return false;
748 if (!eql(a[i], b[i])) return false;
749 }748 }
750 return true;749 return true;
751 },750 },
lib/std/testing.zig+3-5
...@@ -135,9 +135,8 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -135,9 +135,8 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {
135 .array => |array| try expectEqualSlices(array.child, &expected, &actual),135 .array => |array| try expectEqualSlices(array.child, &expected, &actual),
136136
137 .vector => |info| {137 .vector => |info| {
138 var i: usize = 0;138 inline for (0..info.len) |i| {
139 while (i < info.len) : (i += 1) {139 if (expected[i] != actual[i]) {
140 if (!std.meta.eql(expected[i], actual[i])) {
141 print("index {d} incorrect. expected {any}, found {any}\n", .{140 print("index {d} incorrect. expected {any}, found {any}\n", .{
142 i, expected[i], actual[i],141 i, expected[i], actual[i],
143 });142 });
...@@ -828,8 +827,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe...@@ -828,8 +827,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe
828 print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).vector.len });827 print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).vector.len });
829 return error.TestExpectedEqual;828 return error.TestExpectedEqual;
830 }829 }
831 var i: usize = 0;830 inline for (0..info.len) |i| {
832 while (i < info.len) : (i += 1) {
833 expectEqualDeep(expected[i], actual[i]) catch |e| {831 expectEqualDeep(expected[i], actual[i]) catch |e| {
834 print("index {d} incorrect. expected {any}, found {any}\n", .{832 print("index {d} incorrect. expected {any}, found {any}\n", .{
835 i, expected[i], actual[i],833 i, expected[i], actual[i],
lib/std/zon/Serializer.zig+1-1
...@@ -235,7 +235,7 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption...@@ -235,7 +235,7 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption
235 var container = try self.beginTuple(235 var container = try self.beginTuple(
236 .{ .whitespace_style = .{ .fields = vector.len } },236 .{ .whitespace_style = .{ .fields = vector.len } },
237 );237 );
238 for (0..vector.len) |i| {238 inline for (0..vector.len) |i| {
239 try container.fieldArbitraryDepth(val[i], options);239 try container.fieldArbitraryDepth(val[i], options);
240 }240 }
241 try container.end();241 try container.end();
lib/std/zon/parse.zig+4-8
...@@ -446,7 +446,7 @@ pub fn free(gpa: Allocator, value: anytype) void {...@@ -446,7 +446,7 @@ pub fn free(gpa: Allocator, value: anytype) void {
446 .optional => if (value) |some| {446 .optional => if (value) |some| {
447 free(gpa, some);447 free(gpa, some);
448 },448 },
449 .vector => |vector| for (0..vector.len) |i| free(gpa, value[i]),449 .vector => |vector| inline for (0..vector.len) |i| free(gpa, value[i]),
450 .void => {},450 .void => {},
451 else => comptime unreachable,451 else => comptime unreachable,
452 }452 }
...@@ -998,11 +998,7 @@ const Parser = struct {...@@ -998,11 +998,7 @@ const Parser = struct {
998 }998 }
999 }999 }
10001000
1001 fn parseVector(1001 fn parseVector(self: *@This(), T: type, node: Zoir.Node.Index) !T {
1002 self: *@This(),
1003 T: type,
1004 node: Zoir.Node.Index,
1005 ) !T {
1006 const vector_info = @typeInfo(T).vector;1002 const vector_info = @typeInfo(T).vector;
10071003
1008 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {1004 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
...@@ -1021,8 +1017,8 @@ const Parser = struct {...@@ -1021,8 +1017,8 @@ const Parser = struct {
1021 );1017 );
1022 }1018 }
10231019
1024 for (0..vector_info.len) |i| {1020 inline for (0..vector_info.len) |i| {
1025 errdefer for (0..i) |j| free(self.gpa, result[j]);1021 errdefer inline for (0..i) |j| free(self.gpa, result[j]);
1026 result[i] = try self.parseExpr(vector_info.child, nodes.at(@intCast(i)));1022 result[i] = try self.parseExpr(vector_info.child, nodes.at(@intCast(i)));
1027 }1023 }
10281024
src/Sema.zig+5-13
...@@ -27972,6 +27972,7 @@ fn elemVal(...@@ -27972,6 +27972,7 @@ fn elemVal(
27972 }27972 }
27973}27973}
2797427974
27975/// Called when the index or indexable is runtime known.
27975fn validateRuntimeElemAccess(27976fn validateRuntimeElemAccess(
27976 sema: *Sema,27977 sema: *Sema,
27977 block: *Block,27978 block: *Block,
...@@ -28236,6 +28237,10 @@ fn elemPtrArray(...@@ -28236,6 +28237,10 @@ fn elemPtrArray(
28236 try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);28237 try sema.validateRuntimeValue(block, array_ptr_src, array_ptr);
28237 }28238 }
2823828239
28240 if (offset == null and array_ty.zigTypeTag(zcu) == .vector) {
28241 return sema.fail(block, elem_index_src, "vector index not comptime known", .{});
28242 }
28243
28239 // Runtime check is only needed if unable to comptime check.28244 // Runtime check is only needed if unable to comptime check.
28240 if (oob_safety and block.wantSafety() and offset == null) {28245 if (oob_safety and block.wantSafety() and offset == null) {
28241 const len_inst = try pt.intRef(.usize, array_len);28246 const len_inst = try pt.intRef(.usize, array_len);
...@@ -31425,19 +31430,6 @@ fn analyzeLoad(...@@ -31425,19 +31430,6 @@ fn analyzeLoad(
31425 }31430 }
31426 }31431 }
3142731432
31428 if (ptr_ty.ptrInfo(zcu).flags.vector_index == .runtime) {
31429 const ptr_inst = ptr.toIndex().?;
31430 const air_tags = sema.air_instructions.items(.tag);
31431 if (air_tags[@intFromEnum(ptr_inst)] == .ptr_elem_ptr) {
31432 const ty_pl = sema.air_instructions.items(.data)[@intFromEnum(ptr_inst)].ty_pl;
31433 const bin_op = sema.getTmpAir().extraData(Air.Bin, ty_pl.payload).data;
31434 return block.addBinOp(.ptr_elem_val, bin_op.lhs, bin_op.rhs);
31435 }
31436 return sema.fail(block, ptr_src, "unable to determine vector element index of type '{f}'", .{
31437 ptr_ty.fmt(pt),
31438 });
31439 }
31440
31441 return block.addTyOp(.load, elem_ty, ptr);31433 return block.addTyOp(.load, elem_ty, ptr);
31442}31434}
3144331435
test/behavior/math.zig+1-2
...@@ -140,8 +140,7 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void {...@@ -140,8 +140,7 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void {
140 const len_b = @typeInfo(@TypeOf(b)).vector.len;140 const len_b = @typeInfo(@TypeOf(b)).vector.len;
141 try expect(len_a == len_b);141 try expect(len_a == len_b);
142142
143 var i: usize = 0;143 inline for (0..len_a) |i| {
144 while (i < len_a) : (i += 1) {
145 try expect(a[i] == b[i]);144 try expect(a[i] == b[i]);
146 }145 }
147}146}
test/behavior/vector.zig+10-54
...@@ -441,49 +441,6 @@ test "store vector elements via comptime index" {...@@ -441,49 +441,6 @@ test "store vector elements via comptime index" {
441 try comptime S.doTheTest();441 try comptime S.doTheTest();
442}442}
443443
444test "load vector elements via runtime index" {
445 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
446 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
447
448 const S = struct {
449 fn doTheTest() !void {
450 var v: @Vector(4, i32) = [_]i32{ 1, 2, 3, undefined };
451 _ = &v;
452 var i: u32 = 0;
453 try expect(v[i] == 1);
454 i += 1;
455 try expect(v[i] == 2);
456 i += 1;
457 try expect(v[i] == 3);
458 }
459 };
460
461 try S.doTheTest();
462 try comptime S.doTheTest();
463}
464
465test "store vector elements via runtime index" {
466 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
467 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
468 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
469
470 const S = struct {
471 fn doTheTest() !void {
472 var v: @Vector(4, i32) = [_]i32{ 1, 5, 3, undefined };
473 var i: u32 = 2;
474 v[i] = 1;
475 try expect(v[1] == 5);
476 try expect(v[2] == 1);
477 i += 1;
478 v[i] = -364;
479 try expect(-364 == v[3]);
480 }
481 };
482
483 try S.doTheTest();
484 try comptime S.doTheTest();
485}
486
487test "initialize vector which is a struct field" {444test "initialize vector which is a struct field" {
488 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO445 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
489 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO446 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
...@@ -567,20 +524,20 @@ test "vector division operators" {...@@ -567,20 +524,20 @@ test "vector division operators" {
567 };524 };
568 if (!is_signed_int) {525 if (!is_signed_int) {
569 const d0 = x / y;526 const d0 = x / y;
570 for (@as([4]T, d0), 0..) |v, i| {527 inline for (@as([4]T, d0), 0..) |v, i| {
571 try expect(x[i] / y[i] == v);528 try expect(x[i] / y[i] == v);
572 }529 }
573 }530 }
574 const d1 = @divExact(x, y);531 const d1 = @divExact(x, y);
575 for (@as([4]T, d1), 0..) |v, i| {532 inline for (@as([4]T, d1), 0..) |v, i| {
576 try expect(@divExact(x[i], y[i]) == v);533 try expect(@divExact(x[i], y[i]) == v);
577 }534 }
578 const d2 = @divFloor(x, y);535 const d2 = @divFloor(x, y);
579 for (@as([4]T, d2), 0..) |v, i| {536 inline for (@as([4]T, d2), 0..) |v, i| {
580 try expect(@divFloor(x[i], y[i]) == v);537 try expect(@divFloor(x[i], y[i]) == v);
581 }538 }
582 const d3 = @divTrunc(x, y);539 const d3 = @divTrunc(x, y);
583 for (@as([4]T, d3), 0..) |v, i| {540 inline for (@as([4]T, d3), 0..) |v, i| {
584 try expect(@divTrunc(x[i], y[i]) == v);541 try expect(@divTrunc(x[i], y[i]) == v);
585 }542 }
586 }543 }
...@@ -592,16 +549,16 @@ test "vector division operators" {...@@ -592,16 +549,16 @@ test "vector division operators" {
592 };549 };
593 if (!is_signed_int and @typeInfo(T) != .float) {550 if (!is_signed_int and @typeInfo(T) != .float) {
594 const r0 = x % y;551 const r0 = x % y;
595 for (@as([4]T, r0), 0..) |v, i| {552 inline for (@as([4]T, r0), 0..) |v, i| {
596 try expect(x[i] % y[i] == v);553 try expect(x[i] % y[i] == v);
597 }554 }
598 }555 }
599 const r1 = @mod(x, y);556 const r1 = @mod(x, y);
600 for (@as([4]T, r1), 0..) |v, i| {557 inline for (@as([4]T, r1), 0..) |v, i| {
601 try expect(@mod(x[i], y[i]) == v);558 try expect(@mod(x[i], y[i]) == v);
602 }559 }
603 const r2 = @rem(x, y);560 const r2 = @rem(x, y);
604 for (@as([4]T, r2), 0..) |v, i| {561 inline for (@as([4]T, r2), 0..) |v, i| {
605 try expect(@rem(x[i], y[i]) == v);562 try expect(@rem(x[i], y[i]) == v);
606 }563 }
607 }564 }
...@@ -654,7 +611,7 @@ test "vector bitwise not operator" {...@@ -654,7 +611,7 @@ test "vector bitwise not operator" {
654 const S = struct {611 const S = struct {
655 fn doTheTestNot(comptime T: type, x: @Vector(4, T)) !void {612 fn doTheTestNot(comptime T: type, x: @Vector(4, T)) !void {
656 const y = ~x;613 const y = ~x;
657 for (@as([4]T, y), 0..) |v, i| {614 inline for (@as([4]T, y), 0..) |v, i| {
658 try expect(~x[i] == v);615 try expect(~x[i] == v);
659 }616 }
660 }617 }
...@@ -688,7 +645,7 @@ test "vector boolean not operator" {...@@ -688,7 +645,7 @@ test "vector boolean not operator" {
688 const S = struct {645 const S = struct {
689 fn doTheTestNot(comptime T: type, x: @Vector(4, T)) !void {646 fn doTheTestNot(comptime T: type, x: @Vector(4, T)) !void {
690 const y = !x;647 const y = !x;
691 for (@as([4]T, y), 0..) |v, i| {648 inline for (@as([4]T, y), 0..) |v, i| {
692 try expect(!x[i] == v);649 try expect(!x[i] == v);
693 }650 }
694 }651 }
...@@ -1530,8 +1487,7 @@ test "store packed vector element" {...@@ -1530,8 +1487,7 @@ test "store packed vector element" {
15301487
1531 var v = @Vector(4, u1){ 1, 1, 1, 1 };1488 var v = @Vector(4, u1){ 1, 1, 1, 1 };
1532 try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v);1489 try expectEqual(@Vector(4, u1){ 1, 1, 1, 1 }, v);
1533 var index: usize = 0;1490 const index: usize = 0;
1534 _ = &index;
1535 v[index] = 0;1491 v[index] = 0;
1536 try expectEqual(@Vector(4, u1){ 0, 1, 1, 1 }, v);1492 try expectEqual(@Vector(4, u1){ 0, 1, 1, 1 }, v);
1537}1493}
test/behavior/x86_64/access.zig+3-10
...@@ -52,22 +52,15 @@ fn accessVector(comptime init: anytype) !void {...@@ -52,22 +52,15 @@ fn accessVector(comptime init: anytype) !void {
52 var vector: Vector = undefined;52 var vector: Vector = undefined;
53 vector = init;53 vector = init;
54 inline for (0..@typeInfo(Vector).vector.len) |ct_index| {54 inline for (0..@typeInfo(Vector).vector.len) |ct_index| {
55 var rt_index: usize = undefined;55 if (&vector[ct_index] != &vector[ct_index]) return error.Unexpected;
56 rt_index = ct_index;
57 if (&vector[rt_index] != &vector[ct_index]) return error.Unexpected;
58 if (vector[rt_index] != init[ct_index]) return error.Unexpected;
59 if (vector[ct_index] != init[ct_index]) return error.Unexpected;56 if (vector[ct_index] != init[ct_index]) return error.Unexpected;
60 vector[rt_index] = rt_vals[0];57 vector[ct_index] = rt_vals[0];
61 if (vector[rt_index] != ct_vals[0]) return error.Unexpected;
62 if (vector[ct_index] != ct_vals[0]) return error.Unexpected;58 if (vector[ct_index] != ct_vals[0]) return error.Unexpected;
63 vector[rt_index] = ct_vals[1];59 vector[ct_index] = ct_vals[1];
64 if (vector[rt_index] != ct_vals[1]) return error.Unexpected;
65 if (vector[ct_index] != ct_vals[1]) return error.Unexpected;60 if (vector[ct_index] != ct_vals[1]) return error.Unexpected;
66 vector[ct_index] = ct_vals[0];61 vector[ct_index] = ct_vals[0];
67 if (vector[rt_index] != ct_vals[0]) return error.Unexpected;
68 if (vector[ct_index] != ct_vals[0]) return error.Unexpected;62 if (vector[ct_index] != ct_vals[0]) return error.Unexpected;
69 vector[ct_index] = rt_vals[1];63 vector[ct_index] = rt_vals[1];
70 if (vector[rt_index] != ct_vals[1]) return error.Unexpected;
71 if (vector[ct_index] != ct_vals[1]) return error.Unexpected;64 if (vector[ct_index] != ct_vals[1]) return error.Unexpected;
72 }65 }
73}66}
test/cases/compile_errors/load_vector_pointer_with_unknown_runtime_index.zig+1-1
...@@ -12,4 +12,4 @@ fn loadv(ptr: anytype) i31 {...@@ -12,4 +12,4 @@ fn loadv(ptr: anytype) i31 {
1212
13// error13// error
14//14//
15// :10:15: error: unable to determine vector element index of type '*align(16:0:4:?) i31'15// :5:22: error: vector index not comptime known
test/cases/compile_errors/store_vector_pointer_with_unknown_runtime_index.zig+1-1
...@@ -12,4 +12,4 @@ fn storev(ptr: anytype, val: i31) void {...@@ -12,4 +12,4 @@ fn storev(ptr: anytype, val: i31) void {
1212
13// error13// error
14//14//
15// :10:8: error: unable to determine vector element index of type '*align(16:0:4:?) i31'15// :6:15: error: vector index not comptime known