From 426af68b7d234bceba029d65f3388ad2376da649 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 3 Sep 2025 16:08:06 -0700 Subject: compiler: require comptime vector indexes --- lib/std/testing.zig | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'lib/std/testing.zig') diff --git a/lib/std/testing.zig b/lib/std/testing.zig index b2dd271dc0..1cc1c4b4b5 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -135,9 +135,8 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void { .array => |array| try expectEqualSlices(array.child, &expected, &actual), .vector => |info| { - var i: usize = 0; - while (i < info.len) : (i += 1) { - if (!std.meta.eql(expected[i], actual[i])) { + inline for (0..info.len) |i| { + if (expected[i] != actual[i]) { print("index {d} incorrect. expected {any}, found {any}\n", .{ i, expected[i], actual[i], }); @@ -828,8 +827,7 @@ fn expectEqualDeepInner(comptime T: type, expected: T, actual: T) error{TestExpe print("Vector len not the same, expected {d}, found {d}\n", .{ info.len, @typeInfo(@TypeOf(actual)).vector.len }); return error.TestExpectedEqual; } - var i: usize = 0; - while (i < info.len) : (i += 1) { + inline for (0..info.len) |i| { expectEqualDeep(expected[i], actual[i]) catch |e| { print("index {d} incorrect. expected {any}, found {any}\n", .{ i, expected[i], actual[i], -- cgit v1.2.3 From 5ec0a7d8a5201fb35334ce62f82c5958b6ba296e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 5 Sep 2025 15:00:30 -0700 Subject: coerce vectors to arrays rather than inline for --- lib/std/Io/Writer.zig | 36 +++++++++++++++++++-------------- lib/std/crypto/chacha20.zig | 20 +++++++++--------- lib/std/json/static.zig | 29 +++++---------------------- lib/std/meta.zig | 7 +------ lib/std/testing.zig | 11 +++------- lib/std/zon/Serializer.zig | 31 ++++++++++++++-------------- lib/std/zon/parse.zig | 49 +++++++++++++++------------------------------ test/behavior/math.zig | 5 +---- 8 files changed, 72 insertions(+), 116 deletions(-) (limited to 'lib/std/testing.zig') diff --git a/lib/std/Io/Writer.zig b/lib/std/Io/Writer.zig index 7f1b1fd884..26d9417b65 100644 --- a/lib/std/Io/Writer.zig +++ b/lib/std/Io/Writer.zig @@ -1370,19 +1370,12 @@ pub fn printValue( }, .array => { if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})"); - if (max_depth == 0) return w.writeAll("{ ... }"); - try w.writeAll("{ "); - for (value, 0..) |elem, i| { - try w.printValue(fmt, options, elem, max_depth - 1); - if (i < value.len - 1) { - try w.writeAll(", "); - } - } - try w.writeAll(" }"); + return printArray(w, fmt, options, &value, max_depth); }, - .vector => { + .vector => |vector| { if (!is_any and fmt.len != 0) invalidFmtError(fmt, value); - return printVector(w, fmt, options, value, max_depth); + const array: [vector.len]vector.child = value; + return printArray(w, fmt, options, &array, max_depth); }, .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"), .type => { @@ -1436,12 +1429,25 @@ pub fn printVector( value: anytype, max_depth: usize, ) Error!void { - const len = @typeInfo(@TypeOf(value)).vector.len; + const vector = @typeInfo(@TypeOf(value)).vector; + const array: [vector.len]vector.child = value; + return printArray(w, fmt, options, &array, max_depth); +} + +pub fn printArray( + w: *Writer, + comptime fmt: []const u8, + options: std.fmt.Options, + ptr_to_array: anytype, + max_depth: usize, +) Error!void { if (max_depth == 0) return w.writeAll("{ ... }"); try w.writeAll("{ "); - inline for (0..len) |i| { - try w.printValue(fmt, options, value[i], max_depth - 1); - if (i < len - 1) try w.writeAll(", "); + for (ptr_to_array, 0..) |elem, i| { + try w.printValue(fmt, options, elem, max_depth - 1); + if (i < ptr_to_array.len - 1) { + try w.writeAll(", "); + } } try w.writeAll(" }"); } diff --git a/lib/std/crypto/chacha20.zig b/lib/std/crypto/chacha20.zig index 17fb7e7ab3..24c1777a60 100644 --- a/lib/std/crypto/chacha20.zig +++ b/lib/std/crypto/chacha20.zig @@ -215,7 +215,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type } } - fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: BlockVec) void { + fn hashToBytes(comptime dm: usize, out: *[64 * dm]u8, x: *const BlockVec) void { inline for (0..dm) |d| { for (0..4) |i| { mem.writeInt(u32, out[64 * d + 16 * i + 0 ..][0..4], x[i][0 + 4 * d], .little); @@ -242,7 +242,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type while (degree >= d and i + 64 * d <= in.len) : (i += 64 * d) { chacha20Core(x[0..], ctx); contextFeedback(&x, ctx); - hashToBytes(d, buf[0 .. 64 * d], x); + hashToBytes(d, buf[0 .. 64 * d], &x); var xout = out[i..]; const xin = in[i..]; @@ -266,7 +266,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type if (i < in.len) { chacha20Core(x[0..], ctx); contextFeedback(&x, ctx); - hashToBytes(1, buf[0..64], x); + hashToBytes(1, buf[0..64], &x); var xout = out[i..]; const xin = in[i..]; @@ -284,7 +284,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type while (degree >= d and i + 64 * d <= out.len) : (i += 64 * d) { chacha20Core(x[0..], ctx); contextFeedback(&x, ctx); - hashToBytes(d, out[i..][0 .. 64 * d], x); + hashToBytes(d, out[i..][0 .. 64 * d], &x); inline for (0..d) |d_| { if (count64) { const next = @addWithOverflow(ctx[3][4 * d_], d); @@ -301,7 +301,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type contextFeedback(&x, ctx); var buf: [64]u8 = undefined; - hashToBytes(1, buf[0..], x); + hashToBytes(1, buf[0..], &x); @memcpy(out[i..], buf[0 .. out.len - i]); } } @@ -394,7 +394,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { } } - fn hashToBytes(out: *[64]u8, x: BlockVec) void { + fn hashToBytes(out: *[64]u8, x: *const BlockVec) void { for (0..4) |i| { mem.writeInt(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0], .little); mem.writeInt(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1], .little); @@ -417,7 +417,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { while (i + 64 <= in.len) : (i += 64) { chacha20Core(x[0..], ctx); contextFeedback(&x, ctx); - hashToBytes(buf[0..], x); + hashToBytes(buf[0..], &x); var xout = out[i..]; const xin = in[i..]; @@ -438,7 +438,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { if (i < in.len) { chacha20Core(x[0..], ctx); contextFeedback(&x, ctx); - hashToBytes(buf[0..], x); + hashToBytes(buf[0..], &x); var xout = out[i..]; const xin = in[i..]; @@ -455,7 +455,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { while (i + 64 <= out.len) : (i += 64) { chacha20Core(x[0..], ctx); contextFeedback(&x, ctx); - hashToBytes(out[i..][0..64], x); + hashToBytes(out[i..][0..64], &x); if (count64) { const next = @addWithOverflow(ctx[12], 1); ctx[12] = next[0]; @@ -469,7 +469,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type { contextFeedback(&x, ctx); var buf: [64]u8 = undefined; - hashToBytes(buf[0..], x); + hashToBytes(buf[0..], &x); @memcpy(out[i..], buf[0 .. out.len - i]); } } diff --git a/lib/std/json/static.zig b/lib/std/json/static.zig index 4de5676bd6..ac2b1954b2 100644 --- a/lib/std/json/static.zig +++ b/lib/std/json/static.zig @@ -440,10 +440,11 @@ pub fn innerParse( } }, - .vector => |vecInfo| { + .vector => |vector_info| { switch (try source.peekNextTokenType()) { .array_begin => { - return internalParseVector(T, vecInfo.child, vecInfo.len, allocator, source, options); + const A = [vector_info.len]vector_info.child; + return try internalParseArray(A, vector_info.child, allocator, source, options); }, else => return error.UnexpectedToken, } @@ -535,26 +536,6 @@ fn internalParseArray( return r; } -fn internalParseVector( - comptime T: type, - comptime Child: type, - comptime len: comptime_int, - allocator: Allocator, - source: anytype, - options: ParseOptions, -) !T { - assert(.array_begin == try source.next()); - - var r: T = undefined; - inline for (0..len) |i| { - r[i] = try innerParse(Child, allocator, source, options); - } - - if (.array_end != try source.next()) return error.UnexpectedToken; - - return r; -} - /// This is an internal function called recursively /// during the implementation of `parseFromValueLeaky`. /// It is exposed primarily to enable custom `jsonParseFromValue()` methods to call back into the `parseFromValue*` system, @@ -587,12 +568,12 @@ pub fn innerParseFromValue( if (@round(f) != f) return error.InvalidNumber; if (f > @as(@TypeOf(f), @floatFromInt(std.math.maxInt(T)))) return error.Overflow; if (f < @as(@TypeOf(f), @floatFromInt(std.math.minInt(T)))) return error.Overflow; - return @as(T, @intFromFloat(f)); + return @intFromFloat(f); }, .integer => |i| { if (i > std.math.maxInt(T)) return error.Overflow; if (i < std.math.minInt(T)) return error.Overflow; - return @as(T, @intCast(i)); + return @intCast(i); }, .number_string, .string => |s| { return sliceToInt(T, s); diff --git a/lib/std/meta.zig b/lib/std/meta.zig index 222d3276d5..9f4d0aaeeb 100644 --- a/lib/std/meta.zig +++ b/lib/std/meta.zig @@ -742,12 +742,7 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool { if (!eql(e, b[i])) return false; return true; }, - .vector => |info| { - inline for (0..info.len) |i| { - if (a[i] != b[i]) return false; - } - return true; - }, + .vector => return @reduce(.And, a == b), .pointer => |info| { return switch (info.size) { .one, .many, .c => a == b, diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 1cc1c4b4b5..1c6c639796 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -135,14 +135,9 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void { .array => |array| try expectEqualSlices(array.child, &expected, &actual), .vector => |info| { - inline for (0..info.len) |i| { - if (expected[i] != actual[i]) { - print("index {d} incorrect. expected {any}, found {any}\n", .{ - i, expected[i], actual[i], - }); - return error.TestExpectedEqual; - } - } + const expect_array: [info.len]info.child = expected; + const actual_array: [info.len]info.child = actual; + try expectEqualSlices(info.child, &expect_array, &actual_array); }, .@"struct" => |structType| { diff --git a/lib/std/zon/Serializer.zig b/lib/std/zon/Serializer.zig index 5515cc2f06..6f92a64dbd 100644 --- a/lib/std/zon/Serializer.zig +++ b/lib/std/zon/Serializer.zig @@ -157,13 +157,11 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption } }, .array => { - var container = try self.beginTuple( - .{ .whitespace_style = .{ .fields = val.len } }, - ); - for (val) |item_val| { - try container.fieldArbitraryDepth(item_val, options); - } - try container.end(); + try valueArbitraryDepthArray(self, @TypeOf(val), &val, options); + }, + .vector => |vector| { + const array: [vector.len]vector.child = val; + try valueArbitraryDepthArray(self, @TypeOf(array), &array, options); }, .@"struct" => |@"struct"| if (@"struct".is_tuple) { var container = try self.beginTuple( @@ -231,20 +229,21 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption } else { try self.writer.writeAll("null"); }, - .vector => |vector| { - var container = try self.beginTuple( - .{ .whitespace_style = .{ .fields = vector.len } }, - ); - inline for (0..vector.len) |i| { - try container.fieldArbitraryDepth(val[i], options); - } - try container.end(); - }, else => comptime unreachable, } } +fn valueArbitraryDepthArray(s: *Serializer, comptime A: type, array: *const A, options: ValueOptions) Error!void { + var container = try s.beginTuple( + .{ .whitespace_style = .{ .fields = array.len } }, + ); + for (array) |elem| { + try container.fieldArbitraryDepth(elem, options); + } + try container.end(); +} + /// Serialize an integer. pub fn int(self: *Serializer, val: anytype) Error!void { try self.writer.printInt(val, 10, .lower, .{}); diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 89d3f94637..a3d0b69251 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -430,8 +430,12 @@ pub fn free(gpa: Allocator, value: anytype) void { .many, .c => comptime unreachable, } }, - .array => for (value) |item| { - free(gpa, item); + .array => { + freeArray(gpa, @TypeOf(value), &value); + }, + .vector => |vector| { + const array: [vector.len]vector.child = value; + freeArray(gpa, @TypeOf(array), &array); }, .@"struct" => |@"struct"| inline for (@"struct".fields) |field| { free(gpa, @field(value, field.name)); @@ -446,12 +450,15 @@ pub fn free(gpa: Allocator, value: anytype) void { .optional => if (value) |some| { free(gpa, some); }, - .vector => |vector| inline for (0..vector.len) |i| free(gpa, value[i]), .void => {}, else => comptime unreachable, } } +fn freeArray(gpa: Allocator, comptime A: type, array: *const A) void { + for (array) |elem| free(gpa, elem); +} + fn requiresAllocator(T: type) bool { _ = valid_types; return switch (@typeInfo(T)) { @@ -521,12 +528,15 @@ const Parser = struct { else => comptime unreachable, }, .array => return self.parseArray(T, node), + .vector => |vector| { + const A = [vector.len]vector.child; + return try self.parseArray(A, node); + }, .@"struct" => |@"struct"| if (@"struct".is_tuple) return self.parseTuple(T, node) else return self.parseStruct(T, node), .@"union" => return self.parseUnion(T, node), - .vector => return self.parseVector(T, node), else => comptime unreachable, } @@ -999,33 +1009,6 @@ const Parser = struct { } } - fn parseVector(self: *@This(), T: type, node: Zoir.Node.Index) !T { - const vector_info = @typeInfo(T).vector; - - const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) { - .array_literal => |nodes| nodes, - .empty_literal => .{ .start = node, .len = 0 }, - else => return error.WrongType, - }; - - var result: T = undefined; - - if (nodes.len != vector_info.len) { - return self.failNodeFmt( - node, - "expected {} vector elements; found {}", - .{ vector_info.len, nodes.len }, - ); - } - - inline for (0..vector_info.len) |i| { - errdefer inline for (0..i) |j| free(self.gpa, result[j]); - result[i] = try self.parseExpr(vector_info.child, nodes.at(@intCast(i))); - } - - return result; - } - fn failTokenFmt( self: @This(), token: Ast.TokenIndex, @@ -3206,7 +3189,7 @@ test "std.zon vector" { fromSlice(@Vector(2, f32), gpa, ".{0.5}", &diag, .{}), ); try std.testing.expectFmt( - "1:2: error: expected 2 vector elements; found 1\n", + "1:2: error: expected 2 array elements; found 1\n", "{f}", .{diag}, ); @@ -3221,7 +3204,7 @@ test "std.zon vector" { fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &diag, .{}), ); try std.testing.expectFmt( - "1:2: error: expected 2 vector elements; found 3\n", + "1:13: error: index 2 outside of array of length 2\n", "{f}", .{diag}, ); diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 5e844af619..a4b85fb62d 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -139,10 +139,7 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void { const len_a = @typeInfo(@TypeOf(a)).vector.len; const len_b = @typeInfo(@TypeOf(b)).vector.len; try expect(len_a == len_b); - - inline for (0..len_a) |i| { - try expect(a[i] == b[i]); - } + try expect(@reduce(.And, a == b)); } test "@ctz" { -- cgit v1.2.3