authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-05 15:00:30-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-20 18:33:00-07:00
log5ec0a7d8a5201fb35334ce62f82c5958b6ba296e
tree1874ea546d4b71e1c02f3cd37e7b2747f3f791e6
parent9b74651cd2b799a73518de26c8293636f910c833

coerce vectors to arrays rather than inline for


8 files changed, 72 insertions(+), 116 deletions(-)

lib/std/Io/Writer.zig+21-15
...@@ -1370,19 +1370,12 @@ pub fn printValue(...@@ -1370,19 +1370,12 @@ pub fn printValue(
1370 },1370 },
1371 .array => {1371 .array => {
1372 if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})");1372 if (!is_any) @compileError("cannot format array without a specifier (i.e. {s} or {any})");
1373 if (max_depth == 0) return w.writeAll("{ ... }");1373 return printArray(w, fmt, options, &value, max_depth);
1374 try w.writeAll("{ ");
1375 for (value, 0..) |elem, i| {
1376 try w.printValue(fmt, options, elem, max_depth - 1);
1377 if (i < value.len - 1) {
1378 try w.writeAll(", ");
1379 }
1380 }
1381 try w.writeAll(" }");
1382 },1374 },
1383 .vector => {1375 .vector => |vector| {
1384 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);1376 if (!is_any and fmt.len != 0) invalidFmtError(fmt, value);
1385 return printVector(w, fmt, options, value, max_depth);1377 const array: [vector.len]vector.child = value;
1378 return printArray(w, fmt, options, &array, max_depth);
1386 },1379 },
1387 .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),1380 .@"fn" => @compileError("unable to format function body type, use '*const " ++ @typeName(T) ++ "' for a function pointer type"),
1388 .type => {1381 .type => {
...@@ -1436,12 +1429,25 @@ pub fn printVector(...@@ -1436,12 +1429,25 @@ pub fn printVector(
1436 value: anytype,1429 value: anytype,
1437 max_depth: usize,1430 max_depth: usize,
1438) Error!void {1431) Error!void {
1439 const len = @typeInfo(@TypeOf(value)).vector.len;1432 const vector = @typeInfo(@TypeOf(value)).vector;
1433 const array: [vector.len]vector.child = value;
1434 return printArray(w, fmt, options, &array, max_depth);
1435}
1436
1437pub fn printArray(
1438 w: *Writer,
1439 comptime fmt: []const u8,
1440 options: std.fmt.Options,
1441 ptr_to_array: anytype,
1442 max_depth: usize,
1443) Error!void {
1440 if (max_depth == 0) return w.writeAll("{ ... }");1444 if (max_depth == 0) return w.writeAll("{ ... }");
1441 try w.writeAll("{ ");1445 try w.writeAll("{ ");
1442 inline for (0..len) |i| {1446 for (ptr_to_array, 0..) |elem, i| {
1443 try w.printValue(fmt, options, value[i], max_depth - 1);1447 try w.printValue(fmt, options, elem, max_depth - 1);
1444 if (i < len - 1) try w.writeAll(", ");1448 if (i < ptr_to_array.len - 1) {
1449 try w.writeAll(", ");
1450 }
1445 }1451 }
1446 try w.writeAll(" }");1452 try w.writeAll(" }");
1447}1453}
lib/std/crypto/chacha20.zig+10-10
...@@ -215,7 +215,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -215,7 +215,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
215 }215 }
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: *const BlockVec) void {
219 inline 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);
...@@ -242,7 +242,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -242,7 +242,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
242 while (degree >= d and i + 64 * d <= in.len) : (i += 64 * d) {242 while (degree >= d and i + 64 * d <= in.len) : (i += 64 * d) {
243 chacha20Core(x[0..], ctx);243 chacha20Core(x[0..], ctx);
244 contextFeedback(&x, ctx);244 contextFeedback(&x, ctx);
245 hashToBytes(d, buf[0 .. 64 * d], x);245 hashToBytes(d, buf[0 .. 64 * d], &x);
246246
247 var xout = out[i..];247 var xout = out[i..];
248 const xin = in[i..];248 const xin = in[i..];
...@@ -266,7 +266,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -266,7 +266,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
266 if (i < in.len) {266 if (i < in.len) {
267 chacha20Core(x[0..], ctx);267 chacha20Core(x[0..], ctx);
268 contextFeedback(&x, ctx);268 contextFeedback(&x, ctx);
269 hashToBytes(1, buf[0..64], x);269 hashToBytes(1, buf[0..64], &x);
270270
271 var xout = out[i..];271 var xout = out[i..];
272 const xin = in[i..];272 const xin = in[i..];
...@@ -284,7 +284,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -284,7 +284,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
284 while (degree >= d and i + 64 * d <= out.len) : (i += 64 * d) {284 while (degree >= d and i + 64 * d <= out.len) : (i += 64 * d) {
285 chacha20Core(x[0..], ctx);285 chacha20Core(x[0..], ctx);
286 contextFeedback(&x, ctx);286 contextFeedback(&x, ctx);
287 hashToBytes(d, out[i..][0 .. 64 * d], x);287 hashToBytes(d, out[i..][0 .. 64 * d], &x);
288 inline for (0..d) |d_| {288 inline for (0..d) |d_| {
289 if (count64) {289 if (count64) {
290 const next = @addWithOverflow(ctx[3][4 * d_], d);290 const next = @addWithOverflow(ctx[3][4 * d_], d);
...@@ -301,7 +301,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type...@@ -301,7 +301,7 @@ fn ChaChaVecImpl(comptime rounds_nb: usize, comptime degree: comptime_int) type
301 contextFeedback(&x, ctx);301 contextFeedback(&x, ctx);
302302
303 var buf: [64]u8 = undefined;303 var buf: [64]u8 = undefined;
304 hashToBytes(1, buf[0..], x);304 hashToBytes(1, buf[0..], &x);
305 @memcpy(out[i..], buf[0 .. out.len - i]);305 @memcpy(out[i..], buf[0 .. out.len - i]);
306 }306 }
307 }307 }
...@@ -394,7 +394,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -394,7 +394,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
394 }394 }
395 }395 }
396396
397 fn hashToBytes(out: *[64]u8, x: BlockVec) void {397 fn hashToBytes(out: *[64]u8, x: *const BlockVec) void {
398 for (0..4) |i| {398 for (0..4) |i| {
399 mem.writeInt(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0], .little);399 mem.writeInt(u32, out[16 * i + 0 ..][0..4], x[i * 4 + 0], .little);
400 mem.writeInt(u32, out[16 * i + 4 ..][0..4], x[i * 4 + 1], .little);400 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 {...@@ -417,7 +417,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
417 while (i + 64 <= in.len) : (i += 64) {417 while (i + 64 <= in.len) : (i += 64) {
418 chacha20Core(x[0..], ctx);418 chacha20Core(x[0..], ctx);
419 contextFeedback(&x, ctx);419 contextFeedback(&x, ctx);
420 hashToBytes(buf[0..], x);420 hashToBytes(buf[0..], &x);
421421
422 var xout = out[i..];422 var xout = out[i..];
423 const xin = in[i..];423 const xin = in[i..];
...@@ -438,7 +438,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -438,7 +438,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
438 if (i < in.len) {438 if (i < in.len) {
439 chacha20Core(x[0..], ctx);439 chacha20Core(x[0..], ctx);
440 contextFeedback(&x, ctx);440 contextFeedback(&x, ctx);
441 hashToBytes(buf[0..], x);441 hashToBytes(buf[0..], &x);
442442
443 var xout = out[i..];443 var xout = out[i..];
444 const xin = in[i..];444 const xin = in[i..];
...@@ -455,7 +455,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -455,7 +455,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
455 while (i + 64 <= out.len) : (i += 64) {455 while (i + 64 <= out.len) : (i += 64) {
456 chacha20Core(x[0..], ctx);456 chacha20Core(x[0..], ctx);
457 contextFeedback(&x, ctx);457 contextFeedback(&x, ctx);
458 hashToBytes(out[i..][0..64], x);458 hashToBytes(out[i..][0..64], &x);
459 if (count64) {459 if (count64) {
460 const next = @addWithOverflow(ctx[12], 1);460 const next = @addWithOverflow(ctx[12], 1);
461 ctx[12] = next[0];461 ctx[12] = next[0];
...@@ -469,7 +469,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {...@@ -469,7 +469,7 @@ fn ChaChaNonVecImpl(comptime rounds_nb: usize) type {
469 contextFeedback(&x, ctx);469 contextFeedback(&x, ctx);
470470
471 var buf: [64]u8 = undefined;471 var buf: [64]u8 = undefined;
472 hashToBytes(buf[0..], x);472 hashToBytes(buf[0..], &x);
473 @memcpy(out[i..], buf[0 .. out.len - i]);473 @memcpy(out[i..], buf[0 .. out.len - i]);
474 }474 }
475 }475 }
lib/std/json/static.zig+5-24
...@@ -440,10 +440,11 @@ pub fn innerParse(...@@ -440,10 +440,11 @@ pub fn innerParse(
440 }440 }
441 },441 },
442442
443 .vector => |vecInfo| {443 .vector => |vector_info| {
444 switch (try source.peekNextTokenType()) {444 switch (try source.peekNextTokenType()) {
445 .array_begin => {445 .array_begin => {
446 return internalParseVector(T, vecInfo.child, vecInfo.len, allocator, source, options);446 const A = [vector_info.len]vector_info.child;
447 return try internalParseArray(A, vector_info.child, allocator, source, options);
447 },448 },
448 else => return error.UnexpectedToken,449 else => return error.UnexpectedToken,
449 }450 }
...@@ -535,26 +536,6 @@ fn internalParseArray(...@@ -535,26 +536,6 @@ fn internalParseArray(
535 return r;536 return r;
536}537}
537538
538fn internalParseVector(
539 comptime T: type,
540 comptime Child: type,
541 comptime len: comptime_int,
542 allocator: Allocator,
543 source: anytype,
544 options: ParseOptions,
545) !T {
546 assert(.array_begin == try source.next());
547
548 var r: T = undefined;
549 inline for (0..len) |i| {
550 r[i] = try innerParse(Child, allocator, source, options);
551 }
552
553 if (.array_end != try source.next()) return error.UnexpectedToken;
554
555 return r;
556}
557
558/// This is an internal function called recursively539/// This is an internal function called recursively
559/// during the implementation of `parseFromValueLeaky`.540/// during the implementation of `parseFromValueLeaky`.
560/// It is exposed primarily to enable custom `jsonParseFromValue()` methods to call back into the `parseFromValue*` system,541/// It is exposed primarily to enable custom `jsonParseFromValue()` methods to call back into the `parseFromValue*` system,
...@@ -587,12 +568,12 @@ pub fn innerParseFromValue(...@@ -587,12 +568,12 @@ pub fn innerParseFromValue(
587 if (@round(f) != f) return error.InvalidNumber;568 if (@round(f) != f) return error.InvalidNumber;
588 if (f > @as(@TypeOf(f), @floatFromInt(std.math.maxInt(T)))) return error.Overflow;569 if (f > @as(@TypeOf(f), @floatFromInt(std.math.maxInt(T)))) return error.Overflow;
589 if (f < @as(@TypeOf(f), @floatFromInt(std.math.minInt(T)))) return error.Overflow;570 if (f < @as(@TypeOf(f), @floatFromInt(std.math.minInt(T)))) return error.Overflow;
590 return @as(T, @intFromFloat(f));571 return @intFromFloat(f);
591 },572 },
592 .integer => |i| {573 .integer => |i| {
593 if (i > std.math.maxInt(T)) return error.Overflow;574 if (i > std.math.maxInt(T)) return error.Overflow;
594 if (i < std.math.minInt(T)) return error.Overflow;575 if (i < std.math.minInt(T)) return error.Overflow;
595 return @as(T, @intCast(i));576 return @intCast(i);
596 },577 },
597 .number_string, .string => |s| {578 .number_string, .string => |s| {
598 return sliceToInt(T, s);579 return sliceToInt(T, s);
lib/std/meta.zig+1-6
...@@ -742,12 +742,7 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {...@@ -742,12 +742,7 @@ pub fn eql(a: anytype, b: @TypeOf(a)) bool {
742 if (!eql(e, b[i])) return false;742 if (!eql(e, b[i])) return false;
743 return true;743 return true;
744 },744 },
745 .vector => |info| {745 .vector => return @reduce(.And, a == b),
746 inline for (0..info.len) |i| {
747 if (a[i] != b[i]) return false;
748 }
749 return true;
750 },
751 .pointer => |info| {746 .pointer => |info| {
752 return switch (info.size) {747 return switch (info.size) {
753 .one, .many, .c => a == b,748 .one, .many, .c => a == b,
lib/std/testing.zig+3-8
...@@ -135,14 +135,9 @@ fn expectEqualInner(comptime T: type, expected: T, actual: T) !void {...@@ -135,14 +135,9 @@ 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 inline for (0..info.len) |i| {138 const expect_array: [info.len]info.child = expected;
139 if (expected[i] != actual[i]) {139 const actual_array: [info.len]info.child = actual;
140 print("index {d} incorrect. expected {any}, found {any}\n", .{140 try expectEqualSlices(info.child, &expect_array, &actual_array);
141 i, expected[i], actual[i],
142 });
143 return error.TestExpectedEqual;
144 }
145 }
146 },141 },
147142
148 .@"struct" => |structType| {143 .@"struct" => |structType| {
lib/std/zon/Serializer.zig+15-16
...@@ -157,13 +157,11 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption...@@ -157,13 +157,11 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption
157 }157 }
158 },158 },
159 .array => {159 .array => {
160 var container = try self.beginTuple(160 try valueArbitraryDepthArray(self, @TypeOf(val), &val, options);
161 .{ .whitespace_style = .{ .fields = val.len } },161 },
162 );162 .vector => |vector| {
163 for (val) |item_val| {163 const array: [vector.len]vector.child = val;
164 try container.fieldArbitraryDepth(item_val, options);164 try valueArbitraryDepthArray(self, @TypeOf(array), &array, options);
165 }
166 try container.end();
167 },165 },
168 .@"struct" => |@"struct"| if (@"struct".is_tuple) {166 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
169 var container = try self.beginTuple(167 var container = try self.beginTuple(
...@@ -231,20 +229,21 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption...@@ -231,20 +229,21 @@ pub fn valueArbitraryDepth(self: *Serializer, val: anytype, options: ValueOption
231 } else {229 } else {
232 try self.writer.writeAll("null");230 try self.writer.writeAll("null");
233 },231 },
234 .vector => |vector| {
235 var container = try self.beginTuple(
236 .{ .whitespace_style = .{ .fields = vector.len } },
237 );
238 inline for (0..vector.len) |i| {
239 try container.fieldArbitraryDepth(val[i], options);
240 }
241 try container.end();
242 },
243232
244 else => comptime unreachable,233 else => comptime unreachable,
245 }234 }
246}235}
247236
237fn valueArbitraryDepthArray(s: *Serializer, comptime A: type, array: *const A, options: ValueOptions) Error!void {
238 var container = try s.beginTuple(
239 .{ .whitespace_style = .{ .fields = array.len } },
240 );
241 for (array) |elem| {
242 try container.fieldArbitraryDepth(elem, options);
243 }
244 try container.end();
245}
246
248/// Serialize an integer.247/// Serialize an integer.
249pub fn int(self: *Serializer, val: anytype) Error!void {248pub fn int(self: *Serializer, val: anytype) Error!void {
250 try self.writer.printInt(val, 10, .lower, .{});249 try self.writer.printInt(val, 10, .lower, .{});
lib/std/zon/parse.zig+16-33
...@@ -430,8 +430,12 @@ pub fn free(gpa: Allocator, value: anytype) void {...@@ -430,8 +430,12 @@ pub fn free(gpa: Allocator, value: anytype) void {
430 .many, .c => comptime unreachable,430 .many, .c => comptime unreachable,
431 }431 }
432 },432 },
433 .array => for (value) |item| {433 .array => {
434 free(gpa, item);434 freeArray(gpa, @TypeOf(value), &value);
435 },
436 .vector => |vector| {
437 const array: [vector.len]vector.child = value;
438 freeArray(gpa, @TypeOf(array), &array);
435 },439 },
436 .@"struct" => |@"struct"| inline for (@"struct".fields) |field| {440 .@"struct" => |@"struct"| inline for (@"struct".fields) |field| {
437 free(gpa, @field(value, field.name));441 free(gpa, @field(value, field.name));
...@@ -446,12 +450,15 @@ pub fn free(gpa: Allocator, value: anytype) void {...@@ -446,12 +450,15 @@ pub fn free(gpa: Allocator, value: anytype) void {
446 .optional => if (value) |some| {450 .optional => if (value) |some| {
447 free(gpa, some);451 free(gpa, some);
448 },452 },
449 .vector => |vector| inline for (0..vector.len) |i| free(gpa, value[i]),
450 .void => {},453 .void => {},
451 else => comptime unreachable,454 else => comptime unreachable,
452 }455 }
453}456}
454457
458fn freeArray(gpa: Allocator, comptime A: type, array: *const A) void {
459 for (array) |elem| free(gpa, elem);
460}
461
455fn requiresAllocator(T: type) bool {462fn requiresAllocator(T: type) bool {
456 _ = valid_types;463 _ = valid_types;
457 return switch (@typeInfo(T)) {464 return switch (@typeInfo(T)) {
...@@ -521,12 +528,15 @@ const Parser = struct {...@@ -521,12 +528,15 @@ const Parser = struct {
521 else => comptime unreachable,528 else => comptime unreachable,
522 },529 },
523 .array => return self.parseArray(T, node),530 .array => return self.parseArray(T, node),
531 .vector => |vector| {
532 const A = [vector.len]vector.child;
533 return try self.parseArray(A, node);
534 },
524 .@"struct" => |@"struct"| if (@"struct".is_tuple)535 .@"struct" => |@"struct"| if (@"struct".is_tuple)
525 return self.parseTuple(T, node)536 return self.parseTuple(T, node)
526 else537 else
527 return self.parseStruct(T, node),538 return self.parseStruct(T, node),
528 .@"union" => return self.parseUnion(T, node),539 .@"union" => return self.parseUnion(T, node),
529 .vector => return self.parseVector(T, node),
530540
531 else => comptime unreachable,541 else => comptime unreachable,
532 }542 }
...@@ -999,33 +1009,6 @@ const Parser = struct {...@@ -999,33 +1009,6 @@ const Parser = struct {
999 }1009 }
1000 }1010 }
10011011
1002 fn parseVector(self: *@This(), T: type, node: Zoir.Node.Index) !T {
1003 const vector_info = @typeInfo(T).vector;
1004
1005 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
1006 .array_literal => |nodes| nodes,
1007 .empty_literal => .{ .start = node, .len = 0 },
1008 else => return error.WrongType,
1009 };
1010
1011 var result: T = undefined;
1012
1013 if (nodes.len != vector_info.len) {
1014 return self.failNodeFmt(
1015 node,
1016 "expected {} vector elements; found {}",
1017 .{ vector_info.len, nodes.len },
1018 );
1019 }
1020
1021 inline for (0..vector_info.len) |i| {
1022 errdefer inline for (0..i) |j| free(self.gpa, result[j]);
1023 result[i] = try self.parseExpr(vector_info.child, nodes.at(@intCast(i)));
1024 }
1025
1026 return result;
1027 }
1028
1029 fn failTokenFmt(1012 fn failTokenFmt(
1030 self: @This(),1013 self: @This(),
1031 token: Ast.TokenIndex,1014 token: Ast.TokenIndex,
...@@ -3206,7 +3189,7 @@ test "std.zon vector" {...@@ -3206,7 +3189,7 @@ test "std.zon vector" {
3206 fromSlice(@Vector(2, f32), gpa, ".{0.5}", &diag, .{}),3189 fromSlice(@Vector(2, f32), gpa, ".{0.5}", &diag, .{}),
3207 );3190 );
3208 try std.testing.expectFmt(3191 try std.testing.expectFmt(
3209 "1:2: error: expected 2 vector elements; found 1\n",3192 "1:2: error: expected 2 array elements; found 1\n",
3210 "{f}",3193 "{f}",
3211 .{diag},3194 .{diag},
3212 );3195 );
...@@ -3221,7 +3204,7 @@ test "std.zon vector" {...@@ -3221,7 +3204,7 @@ test "std.zon vector" {
3221 fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &diag, .{}),3204 fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &diag, .{}),
3222 );3205 );
3223 try std.testing.expectFmt(3206 try std.testing.expectFmt(
3224 "1:2: error: expected 2 vector elements; found 3\n",3207 "1:13: error: index 2 outside of array of length 2\n",
3225 "{f}",3208 "{f}",
3226 .{diag},3209 .{diag},
3227 );3210 );
test/behavior/math.zig+1-4
...@@ -139,10 +139,7 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void {...@@ -139,10 +139,7 @@ fn expectVectorsEqual(a: anytype, b: anytype) !void {
139 const len_a = @typeInfo(@TypeOf(a)).vector.len;139 const len_a = @typeInfo(@TypeOf(a)).vector.len;
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 try expect(@reduce(.And, a == b));
143 inline for (0..len_a) |i| {
144 try expect(a[i] == b[i]);
145 }
146}143}
147144
148test "@ctz" {145test "@ctz" {