authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-03 16:38:35+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-02-03 16:38:35+00:00
log317722b37b201e393aed60045f4bf9649103e63e
treed0485fccbcfd0d2951ac373bbd5d483cc8539ca2
parente61acd8eb563d3c233202ef3a1a63df384d09943
parentdc5c8278474f998360bc48e3dd0fe9a2929b4374
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20271 from MasonRemaley/zon

ZON

145 files changed, 8788 insertions(+), 432 deletions(-)

build.zig+1-1
......@@ -406,7 +406,7 @@ pub fn build(b: *std.Build) !void {
406406 const optimization_modes = chosen_opt_modes_buf[0..chosen_mode_index];
407407
408408 const fmt_include_paths = &.{ "lib", "src", "test", "tools", "build.zig", "build.zig.zon" };
409 const fmt_exclude_paths = &.{"test/cases"};
409 const fmt_exclude_paths = &.{ "test/cases", "test/behavior/zon" };
410410 const do_fmt = b.addFmt(.{
411411 .paths = fmt_include_paths,
412412 .exclude_paths = fmt_exclude_paths,
lib/compiler/aro/aro/Value.zig+1-1
......@@ -473,7 +473,7 @@ pub fn toInt(v: Value, comptime T: type, comp: *const Compilation) ?T {
473473 if (comp.interner.get(v.ref()) != .int) return null;
474474 var space: BigIntSpace = undefined;
475475 const big_int = v.toBigInt(&space, comp);
476 return big_int.to(T) catch null;
476 return big_int.toInt(T) catch null;
477477}
478478
479479const ComplexOp = enum {
lib/compiler/aro/backend/Interner.zig+2-2
......@@ -628,13 +628,13 @@ pub fn put(i: *Interner, gpa: Allocator, key: Key) !Ref {
628628 if (data.fitsInTwosComp(.unsigned, 32)) {
629629 i.items.appendAssumeCapacity(.{
630630 .tag = .u32,
631 .data = data.to(u32) catch unreachable,
631 .data = data.toInt(u32) catch unreachable,
632632 });
633633 break :int;
634634 } else if (data.fitsInTwosComp(.signed, 32)) {
635635 i.items.appendAssumeCapacity(.{
636636 .tag = .i32,
637 .data = @bitCast(data.to(i32) catch unreachable),
637 .data = @bitCast(data.toInt(i32) catch unreachable),
638638 });
639639 break :int;
640640 }
lib/std/heap/general_purpose_allocator.zig+4
......@@ -1235,6 +1235,8 @@ test "shrink large object to large object" {
12351235}
12361236
12371237test "shrink large object to large object with larger alignment" {
1238 if (!builtin.link_libc and builtin.os.tag == .wasi) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22731
1239
12381240 var gpa = GeneralPurposeAllocator(test_config){};
12391241 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12401242 const allocator = gpa.allocator();
......@@ -1307,6 +1309,8 @@ test "non-page-allocator backing allocator" {
13071309}
13081310
13091311test "realloc large object to larger alignment" {
1312 if (!builtin.link_libc and builtin.os.tag == .wasi) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/22731
1313
13101314 var gpa = GeneralPurposeAllocator(test_config){};
13111315 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13121316 const allocator = gpa.allocator();
lib/std/math/big/int.zig+36-6
......@@ -2175,10 +2175,13 @@ pub const Const = struct {
21752175 TargetTooSmall,
21762176 };
21772177
2178 /// Convert self to type T.
2178 /// Deprecated; use `toInt`.
2179 pub const to = toInt;
2180
2181 /// Convert self to integer type T.
21792182 ///
21802183 /// Returns an error if self cannot be narrowed into the requested type without truncation.
2181 pub fn to(self: Const, comptime T: type) ConvertError!T {
2184 pub fn toInt(self: Const, comptime T: type) ConvertError!T {
21822185 switch (@typeInfo(T)) {
21832186 .int => |info| {
21842187 // Make sure -0 is handled correctly.
......@@ -2216,7 +2219,26 @@ pub const Const = struct {
22162219 }
22172220 }
22182221 },
2219 else => @compileError("cannot convert Const to type " ++ @typeName(T)),
2222 else => @compileError("expected int type, found '" ++ @typeName(T) ++ "'"),
2223 }
2224 }
2225
2226 /// Convert self to float type T.
2227 pub fn toFloat(self: Const, comptime T: type) T {
2228 if (self.limbs.len == 0) return 0;
2229
2230 const base = std.math.maxInt(std.math.big.Limb) + 1;
2231 var result: f128 = 0;
2232 var i: usize = self.limbs.len;
2233 while (i != 0) {
2234 i -= 1;
2235 const limb: f128 = @floatFromInt(self.limbs[i]);
2236 result = @mulAdd(f128, base, result, limb);
2237 }
2238 if (self.positive) {
2239 return @floatCast(result);
2240 } else {
2241 return @floatCast(-result);
22202242 }
22212243 }
22222244
......@@ -2775,11 +2797,19 @@ pub const Managed = struct {
27752797
27762798 pub const ConvertError = Const.ConvertError;
27772799
2778 /// Convert self to type T.
2800 /// Deprecated; use `toInt`.
2801 pub const to = toInt;
2802
2803 /// Convert self to integer type T.
27792804 ///
27802805 /// Returns an error if self cannot be narrowed into the requested type without truncation.
2781 pub fn to(self: Managed, comptime T: type) ConvertError!T {
2782 return self.toConst().to(T);
2806 pub fn toInt(self: Managed, comptime T: type) ConvertError!T {
2807 return self.toConst().toInt(T);
2808 }
2809
2810 /// Convert self to float type T.
2811 pub fn toFloat(self: Managed, comptime T: type) T {
2812 return self.toConst().toFloat(T);
27832813 }
27842814
27852815 /// Set self from the string representation `value`.
lib/std/math/big/int_test.zig+219-219
......@@ -53,21 +53,21 @@ test "comptime_int to" {
5353 var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab);
5454 defer a.deinit();
5555
56 try testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
56 try testing.expect((try a.toInt(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab);
5757}
5858
5959test "sub-limb to" {
6060 var a = try Managed.initSet(testing.allocator, 10);
6161 defer a.deinit();
6262
63 try testing.expect((try a.to(u8)) == 10);
63 try testing.expect((try a.toInt(u8)) == 10);
6464}
6565
6666test "set negative minimum" {
6767 var a = try Managed.initSet(testing.allocator, @as(i64, minInt(i64)));
6868 defer a.deinit();
6969
70 try testing.expect((try a.to(i64)) == minInt(i64));
70 try testing.expect((try a.toInt(i64)) == minInt(i64));
7171}
7272
7373test "set double-width maximum then zero" {
......@@ -75,14 +75,14 @@ test "set double-width maximum then zero" {
7575 defer a.deinit();
7676 try a.set(@as(DoubleLimb, 0));
7777
78 try testing.expectEqual(@as(DoubleLimb, 0), try a.to(DoubleLimb));
78 try testing.expectEqual(@as(DoubleLimb, 0), try a.toInt(DoubleLimb));
7979}
8080
8181test "to target too small error" {
8282 var a = try Managed.initSet(testing.allocator, 0xffffffff);
8383 defer a.deinit();
8484
85 try testing.expectError(error.TargetTooSmall, a.to(u8));
85 try testing.expectError(error.TargetTooSmall, a.toInt(u8));
8686}
8787
8888test "normalize" {
......@@ -191,28 +191,28 @@ test "bitcount/to" {
191191 try a.set(0);
192192 try testing.expect(a.bitCountTwosComp() == 0);
193193
194 try testing.expect((try a.to(u0)) == 0);
195 try testing.expect((try a.to(i0)) == 0);
194 try testing.expect((try a.toInt(u0)) == 0);
195 try testing.expect((try a.toInt(i0)) == 0);
196196
197197 try a.set(-1);
198198 try testing.expect(a.bitCountTwosComp() == 1);
199 try testing.expect((try a.to(i1)) == -1);
199 try testing.expect((try a.toInt(i1)) == -1);
200200
201201 try a.set(-8);
202202 try testing.expect(a.bitCountTwosComp() == 4);
203 try testing.expect((try a.to(i4)) == -8);
203 try testing.expect((try a.toInt(i4)) == -8);
204204
205205 try a.set(127);
206206 try testing.expect(a.bitCountTwosComp() == 7);
207 try testing.expect((try a.to(u7)) == 127);
207 try testing.expect((try a.toInt(u7)) == 127);
208208
209209 try a.set(-128);
210210 try testing.expect(a.bitCountTwosComp() == 8);
211 try testing.expect((try a.to(i8)) == -128);
211 try testing.expect((try a.toInt(i8)) == -128);
212212
213213 try a.set(-129);
214214 try testing.expect(a.bitCountTwosComp() == 9);
215 try testing.expect((try a.to(i9)) == -129);
215 try testing.expect((try a.toInt(i9)) == -129);
216216}
217217
218218test "fits" {
......@@ -248,7 +248,7 @@ test "string set" {
248248 defer a.deinit();
249249
250250 try a.setString(10, "120317241209124781241290847124");
251 try testing.expect((try a.to(u128)) == 120317241209124781241290847124);
251 try testing.expect((try a.toInt(u128)) == 120317241209124781241290847124);
252252}
253253
254254test "string negative" {
......@@ -256,7 +256,7 @@ test "string negative" {
256256 defer a.deinit();
257257
258258 try a.setString(10, "-1023");
259 try testing.expect((try a.to(i32)) == -1023);
259 try testing.expect((try a.toInt(i32)) == -1023);
260260}
261261
262262test "string set number with underscores" {
......@@ -264,7 +264,7 @@ test "string set number with underscores" {
264264 defer a.deinit();
265265
266266 try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___");
267 try testing.expect((try a.to(u128)) == 120317241209124781241290847124);
267 try testing.expect((try a.toInt(u128)) == 120317241209124781241290847124);
268268}
269269
270270test "string set case insensitive number" {
......@@ -272,7 +272,7 @@ test "string set case insensitive number" {
272272 defer a.deinit();
273273
274274 try a.setString(16, "aB_cD_eF");
275 try testing.expect((try a.to(u32)) == 0xabcdef);
275 try testing.expect((try a.toInt(u32)) == 0xabcdef);
276276}
277277
278278test "string set bad char error" {
......@@ -306,11 +306,11 @@ fn testTwosComplementLimit(comptime T: type) !void {
306306
307307 try a.setTwosCompIntLimit(.max, int_info.signedness, int_info.bits);
308308 const max: T = maxInt(T);
309 try testing.expect(max == try a.to(T));
309 try testing.expect(max == try a.toInt(T));
310310
311311 try a.setTwosCompIntLimit(.min, int_info.signedness, int_info.bits);
312312 const min: T = minInt(T);
313 try testing.expect(min == try a.to(T));
313 try testing.expect(min == try a.toInt(T));
314314}
315315
316316test "string to" {
......@@ -381,12 +381,12 @@ test "clone" {
381381 var b = try a.clone();
382382 defer b.deinit();
383383
384 try testing.expect((try a.to(u32)) == 1234);
385 try testing.expect((try b.to(u32)) == 1234);
384 try testing.expect((try a.toInt(u32)) == 1234);
385 try testing.expect((try b.toInt(u32)) == 1234);
386386
387387 try a.set(77);
388 try testing.expect((try a.to(u32)) == 77);
389 try testing.expect((try b.to(u32)) == 1234);
388 try testing.expect((try a.toInt(u32)) == 77);
389 try testing.expect((try b.toInt(u32)) == 1234);
390390}
391391
392392test "swap" {
......@@ -395,20 +395,20 @@ test "swap" {
395395 var b = try Managed.initSet(testing.allocator, 5678);
396396 defer b.deinit();
397397
398 try testing.expect((try a.to(u32)) == 1234);
399 try testing.expect((try b.to(u32)) == 5678);
398 try testing.expect((try a.toInt(u32)) == 1234);
399 try testing.expect((try b.toInt(u32)) == 5678);
400400
401401 a.swap(&b);
402402
403 try testing.expect((try a.to(u32)) == 5678);
404 try testing.expect((try b.to(u32)) == 1234);
403 try testing.expect((try a.toInt(u32)) == 5678);
404 try testing.expect((try b.toInt(u32)) == 1234);
405405}
406406
407407test "to negative" {
408408 var a = try Managed.initSet(testing.allocator, -10);
409409 defer a.deinit();
410410
411 try testing.expect((try a.to(i32)) == -10);
411 try testing.expect((try a.toInt(i32)) == -10);
412412}
413413
414414test "compare" {
......@@ -466,10 +466,10 @@ test "abs" {
466466 defer a.deinit();
467467
468468 a.abs();
469 try testing.expect((try a.to(u32)) == 5);
469 try testing.expect((try a.toInt(u32)) == 5);
470470
471471 a.abs();
472 try testing.expect((try a.to(u32)) == 5);
472 try testing.expect((try a.toInt(u32)) == 5);
473473}
474474
475475test "negate" {
......@@ -477,10 +477,10 @@ test "negate" {
477477 defer a.deinit();
478478
479479 a.negate();
480 try testing.expect((try a.to(i32)) == -5);
480 try testing.expect((try a.toInt(i32)) == -5);
481481
482482 a.negate();
483 try testing.expect((try a.to(i32)) == 5);
483 try testing.expect((try a.toInt(i32)) == 5);
484484}
485485
486486test "add single-single" {
......@@ -493,7 +493,7 @@ test "add single-single" {
493493 defer c.deinit();
494494 try c.add(&a, &b);
495495
496 try testing.expect((try c.to(u32)) == 55);
496 try testing.expect((try c.toInt(u32)) == 55);
497497}
498498
499499test "add multi-single" {
......@@ -506,10 +506,10 @@ test "add multi-single" {
506506 defer c.deinit();
507507
508508 try c.add(&a, &b);
509 try testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
509 try testing.expect((try c.toInt(DoubleLimb)) == maxInt(Limb) + 2);
510510
511511 try c.add(&b, &a);
512 try testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2);
512 try testing.expect((try c.toInt(DoubleLimb)) == maxInt(Limb) + 2);
513513}
514514
515515test "add multi-multi" {
......@@ -527,7 +527,7 @@ test "add multi-multi" {
527527 defer c.deinit();
528528 try c.add(&a, &b);
529529
530 try testing.expect((try c.to(u128)) == op1 + op2);
530 try testing.expect((try c.toInt(u128)) == op1 + op2);
531531}
532532
533533test "add zero-zero" {
......@@ -540,7 +540,7 @@ test "add zero-zero" {
540540 defer c.deinit();
541541 try c.add(&a, &b);
542542
543 try testing.expect((try c.to(u32)) == 0);
543 try testing.expect((try c.toInt(u32)) == 0);
544544}
545545
546546test "add alias multi-limb nonzero-zero" {
......@@ -552,7 +552,7 @@ test "add alias multi-limb nonzero-zero" {
552552
553553 try a.add(&a, &b);
554554
555 try testing.expect((try a.to(u128)) == op1);
555 try testing.expect((try a.toInt(u128)) == op1);
556556}
557557
558558test "add sign" {
......@@ -569,16 +569,16 @@ test "add sign" {
569569 defer neg_two.deinit();
570570
571571 try a.add(&one, &two);
572 try testing.expect((try a.to(i32)) == 3);
572 try testing.expect((try a.toInt(i32)) == 3);
573573
574574 try a.add(&neg_one, &two);
575 try testing.expect((try a.to(i32)) == 1);
575 try testing.expect((try a.toInt(i32)) == 1);
576576
577577 try a.add(&one, &neg_two);
578 try testing.expect((try a.to(i32)) == -1);
578 try testing.expect((try a.toInt(i32)) == -1);
579579
580580 try a.add(&neg_one, &neg_two);
581 try testing.expect((try a.to(i32)) == -3);
581 try testing.expect((try a.toInt(i32)) == -3);
582582}
583583
584584test "add comptime scalar" {
......@@ -589,7 +589,7 @@ test "add comptime scalar" {
589589 defer b.deinit();
590590 try b.addScalar(&a, 5);
591591
592 try testing.expect((try b.to(u32)) == 55);
592 try testing.expect((try b.toInt(u32)) == 55);
593593}
594594
595595test "add scalar" {
......@@ -600,7 +600,7 @@ test "add scalar" {
600600 defer b.deinit();
601601 try b.addScalar(&a, @as(u32, 31));
602602
603 try testing.expect((try b.to(u32)) == 154);
603 try testing.expect((try b.toInt(u32)) == 154);
604604}
605605
606606test "addWrap single-single, unsigned" {
......@@ -613,7 +613,7 @@ test "addWrap single-single, unsigned" {
613613 const wrapped = try a.addWrap(&a, &b, .unsigned, 17);
614614
615615 try testing.expect(wrapped);
616 try testing.expect((try a.to(u17)) == 9);
616 try testing.expect((try a.toInt(u17)) == 9);
617617}
618618
619619test "subWrap single-single, unsigned" {
......@@ -626,7 +626,7 @@ test "subWrap single-single, unsigned" {
626626 const wrapped = try a.subWrap(&a, &b, .unsigned, 17);
627627
628628 try testing.expect(wrapped);
629 try testing.expect((try a.to(u17)) == 1);
629 try testing.expect((try a.toInt(u17)) == 1);
630630}
631631
632632test "addWrap multi-multi, unsigned, limb aligned" {
......@@ -639,7 +639,7 @@ test "addWrap multi-multi, unsigned, limb aligned" {
639639 const wrapped = try a.addWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb));
640640
641641 try testing.expect(wrapped);
642 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 1);
642 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb) - 1);
643643}
644644
645645test "subWrap single-multi, unsigned, limb aligned" {
......@@ -652,7 +652,7 @@ test "subWrap single-multi, unsigned, limb aligned" {
652652 const wrapped = try a.subWrap(&a, &b, .unsigned, @bitSizeOf(DoubleLimb));
653653
654654 try testing.expect(wrapped);
655 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) - 88);
655 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb) - 88);
656656}
657657
658658test "addWrap single-single, signed" {
......@@ -665,7 +665,7 @@ test "addWrap single-single, signed" {
665665 const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(i21));
666666
667667 try testing.expect(wrapped);
668 try testing.expect((try a.to(i21)) == minInt(i21));
668 try testing.expect((try a.toInt(i21)) == minInt(i21));
669669}
670670
671671test "subWrap single-single, signed" {
......@@ -678,7 +678,7 @@ test "subWrap single-single, signed" {
678678 const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(i21));
679679
680680 try testing.expect(wrapped);
681 try testing.expect((try a.to(i21)) == maxInt(i21));
681 try testing.expect((try a.toInt(i21)) == maxInt(i21));
682682}
683683
684684test "addWrap multi-multi, signed, limb aligned" {
......@@ -691,7 +691,7 @@ test "addWrap multi-multi, signed, limb aligned" {
691691 const wrapped = try a.addWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb));
692692
693693 try testing.expect(wrapped);
694 try testing.expect((try a.to(SignedDoubleLimb)) == -2);
694 try testing.expect((try a.toInt(SignedDoubleLimb)) == -2);
695695}
696696
697697test "subWrap single-multi, signed, limb aligned" {
......@@ -704,7 +704,7 @@ test "subWrap single-multi, signed, limb aligned" {
704704 const wrapped = try a.subWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb));
705705
706706 try testing.expect(wrapped);
707 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
707 try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
708708}
709709
710710test "addSat single-single, unsigned" {
......@@ -716,7 +716,7 @@ test "addSat single-single, unsigned" {
716716
717717 try a.addSat(&a, &b, .unsigned, 17);
718718
719 try testing.expect((try a.to(u17)) == maxInt(u17));
719 try testing.expect((try a.toInt(u17)) == maxInt(u17));
720720}
721721
722722test "subSat single-single, unsigned" {
......@@ -728,7 +728,7 @@ test "subSat single-single, unsigned" {
728728
729729 try a.subSat(&a, &b, .unsigned, 17);
730730
731 try testing.expect((try a.to(u17)) == 0);
731 try testing.expect((try a.toInt(u17)) == 0);
732732}
733733
734734test "addSat multi-multi, unsigned, limb aligned" {
......@@ -740,7 +740,7 @@ test "addSat multi-multi, unsigned, limb aligned" {
740740
741741 try a.addSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb));
742742
743 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb));
743 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb));
744744}
745745
746746test "subSat single-multi, unsigned, limb aligned" {
......@@ -752,7 +752,7 @@ test "subSat single-multi, unsigned, limb aligned" {
752752
753753 try a.subSat(&a, &b, .unsigned, @bitSizeOf(DoubleLimb));
754754
755 try testing.expect((try a.to(DoubleLimb)) == 0);
755 try testing.expect((try a.toInt(DoubleLimb)) == 0);
756756}
757757
758758test "addSat single-single, signed" {
......@@ -764,7 +764,7 @@ test "addSat single-single, signed" {
764764
765765 try a.addSat(&a, &b, .signed, @bitSizeOf(i14));
766766
767 try testing.expect((try a.to(i14)) == maxInt(i14));
767 try testing.expect((try a.toInt(i14)) == maxInt(i14));
768768}
769769
770770test "subSat single-single, signed" {
......@@ -776,7 +776,7 @@ test "subSat single-single, signed" {
776776
777777 try a.subSat(&a, &b, .signed, @bitSizeOf(i21));
778778
779 try testing.expect((try a.to(i21)) == minInt(i21));
779 try testing.expect((try a.toInt(i21)) == minInt(i21));
780780}
781781
782782test "addSat multi-multi, signed, limb aligned" {
......@@ -788,7 +788,7 @@ test "addSat multi-multi, signed, limb aligned" {
788788
789789 try a.addSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb));
790790
791 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
791 try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
792792}
793793
794794test "subSat single-multi, signed, limb aligned" {
......@@ -800,7 +800,7 @@ test "subSat single-multi, signed, limb aligned" {
800800
801801 try a.subSat(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb));
802802
803 try testing.expect((try a.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb));
803 try testing.expect((try a.toInt(SignedDoubleLimb)) == minInt(SignedDoubleLimb));
804804}
805805
806806test "sub single-single" {
......@@ -813,7 +813,7 @@ test "sub single-single" {
813813 defer c.deinit();
814814 try c.sub(&a, &b);
815815
816 try testing.expect((try c.to(u32)) == 45);
816 try testing.expect((try c.toInt(u32)) == 45);
817817}
818818
819819test "sub multi-single" {
......@@ -826,7 +826,7 @@ test "sub multi-single" {
826826 defer c.deinit();
827827 try c.sub(&a, &b);
828828
829 try testing.expect((try c.to(Limb)) == maxInt(Limb));
829 try testing.expect((try c.toInt(Limb)) == maxInt(Limb));
830830}
831831
832832test "sub multi-multi" {
......@@ -843,7 +843,7 @@ test "sub multi-multi" {
843843 defer c.deinit();
844844 try c.sub(&a, &b);
845845
846 try testing.expect((try c.to(u128)) == op1 - op2);
846 try testing.expect((try c.toInt(u128)) == op1 - op2);
847847}
848848
849849test "sub equal" {
......@@ -856,7 +856,7 @@ test "sub equal" {
856856 defer c.deinit();
857857 try c.sub(&a, &b);
858858
859 try testing.expect((try c.to(u32)) == 0);
859 try testing.expect((try c.toInt(u32)) == 0);
860860}
861861
862862test "sub sign" {
......@@ -873,19 +873,19 @@ test "sub sign" {
873873 defer neg_two.deinit();
874874
875875 try a.sub(&one, &two);
876 try testing.expect((try a.to(i32)) == -1);
876 try testing.expect((try a.toInt(i32)) == -1);
877877
878878 try a.sub(&neg_one, &two);
879 try testing.expect((try a.to(i32)) == -3);
879 try testing.expect((try a.toInt(i32)) == -3);
880880
881881 try a.sub(&one, &neg_two);
882 try testing.expect((try a.to(i32)) == 3);
882 try testing.expect((try a.toInt(i32)) == 3);
883883
884884 try a.sub(&neg_one, &neg_two);
885 try testing.expect((try a.to(i32)) == 1);
885 try testing.expect((try a.toInt(i32)) == 1);
886886
887887 try a.sub(&neg_two, &neg_one);
888 try testing.expect((try a.to(i32)) == -1);
888 try testing.expect((try a.toInt(i32)) == -1);
889889}
890890
891891test "mul single-single" {
......@@ -898,7 +898,7 @@ test "mul single-single" {
898898 defer c.deinit();
899899 try c.mul(&a, &b);
900900
901 try testing.expect((try c.to(u64)) == 250);
901 try testing.expect((try c.toInt(u64)) == 250);
902902}
903903
904904test "mul multi-single" {
......@@ -911,7 +911,7 @@ test "mul multi-single" {
911911 defer c.deinit();
912912 try c.mul(&a, &b);
913913
914 try testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb));
914 try testing.expect((try c.toInt(DoubleLimb)) == 2 * maxInt(Limb));
915915}
916916
917917test "mul multi-multi" {
......@@ -930,7 +930,7 @@ test "mul multi-multi" {
930930 defer c.deinit();
931931 try c.mul(&a, &b);
932932
933 try testing.expect((try c.to(u256)) == op1 * op2);
933 try testing.expect((try c.toInt(u256)) == op1 * op2);
934934}
935935
936936test "mul alias r with a" {
......@@ -941,7 +941,7 @@ test "mul alias r with a" {
941941
942942 try a.mul(&a, &b);
943943
944 try testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
944 try testing.expect((try a.toInt(DoubleLimb)) == 2 * maxInt(Limb));
945945}
946946
947947test "mul alias r with b" {
......@@ -952,7 +952,7 @@ test "mul alias r with b" {
952952
953953 try a.mul(&b, &a);
954954
955 try testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb));
955 try testing.expect((try a.toInt(DoubleLimb)) == 2 * maxInt(Limb));
956956}
957957
958958test "mul alias r with a and b" {
......@@ -961,7 +961,7 @@ test "mul alias r with a and b" {
961961
962962 try a.mul(&a, &a);
963963
964 try testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
964 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(Limb) * maxInt(Limb));
965965}
966966
967967test "mul a*0" {
......@@ -974,7 +974,7 @@ test "mul a*0" {
974974 defer c.deinit();
975975 try c.mul(&a, &b);
976976
977 try testing.expect((try c.to(u32)) == 0);
977 try testing.expect((try c.toInt(u32)) == 0);
978978}
979979
980980test "mul 0*0" {
......@@ -987,7 +987,7 @@ test "mul 0*0" {
987987 defer c.deinit();
988988 try c.mul(&a, &b);
989989
990 try testing.expect((try c.to(u32)) == 0);
990 try testing.expect((try c.toInt(u32)) == 0);
991991}
992992
993993test "mul large" {
......@@ -1021,7 +1021,7 @@ test "mulWrap single-single unsigned" {
10211021 defer c.deinit();
10221022 try c.mulWrap(&a, &b, .unsigned, 17);
10231023
1024 try testing.expect((try c.to(u17)) == 59836);
1024 try testing.expect((try c.toInt(u17)) == 59836);
10251025}
10261026
10271027test "mulWrap single-single signed" {
......@@ -1034,7 +1034,7 @@ test "mulWrap single-single signed" {
10341034 defer c.deinit();
10351035 try c.mulWrap(&a, &b, .signed, 17);
10361036
1037 try testing.expect((try c.to(i17)) == -59836);
1037 try testing.expect((try c.toInt(i17)) == -59836);
10381038}
10391039
10401040test "mulWrap multi-multi unsigned" {
......@@ -1053,7 +1053,7 @@ test "mulWrap multi-multi unsigned" {
10531053 defer c.deinit();
10541054 try c.mulWrap(&a, &b, .unsigned, 65);
10551055
1056 try testing.expect((try c.to(u256)) == (op1 * op2) & ((1 << 65) - 1));
1056 try testing.expect((try c.toInt(u256)) == (op1 * op2) & ((1 << 65) - 1));
10571057}
10581058
10591059test "mulWrap multi-multi signed" {
......@@ -1071,7 +1071,7 @@ test "mulWrap multi-multi signed" {
10711071 defer c.deinit();
10721072 try c.mulWrap(&a, &b, .signed, @bitSizeOf(SignedDoubleLimb));
10731073
1074 try testing.expect((try c.to(SignedDoubleLimb)) == minInt(SignedDoubleLimb) + 2);
1074 try testing.expect((try c.toInt(SignedDoubleLimb)) == minInt(SignedDoubleLimb) + 2);
10751075}
10761076
10771077test "mulWrap large" {
......@@ -1110,8 +1110,8 @@ test "div single-half no rem" {
11101110 defer r.deinit();
11111111 try Managed.divTrunc(&q, &r, &a, &b);
11121112
1113 try testing.expect((try q.to(u32)) == 10);
1114 try testing.expect((try r.to(u32)) == 0);
1113 try testing.expect((try q.toInt(u32)) == 10);
1114 try testing.expect((try r.toInt(u32)) == 0);
11151115}
11161116
11171117test "div single-half with rem" {
......@@ -1126,8 +1126,8 @@ test "div single-half with rem" {
11261126 defer r.deinit();
11271127 try Managed.divTrunc(&q, &r, &a, &b);
11281128
1129 try testing.expect((try q.to(u32)) == 9);
1130 try testing.expect((try r.to(u32)) == 4);
1129 try testing.expect((try q.toInt(u32)) == 9);
1130 try testing.expect((try r.toInt(u32)) == 4);
11311131}
11321132
11331133test "div single-single no rem" {
......@@ -1143,8 +1143,8 @@ test "div single-single no rem" {
11431143 defer r.deinit();
11441144 try Managed.divTrunc(&q, &r, &a, &b);
11451145
1146 try testing.expect((try q.to(u32)) == 131072);
1147 try testing.expect((try r.to(u32)) == 0);
1146 try testing.expect((try q.toInt(u32)) == 131072);
1147 try testing.expect((try r.toInt(u32)) == 0);
11481148}
11491149
11501150test "div single-single with rem" {
......@@ -1159,8 +1159,8 @@ test "div single-single with rem" {
11591159 defer r.deinit();
11601160 try Managed.divTrunc(&q, &r, &a, &b);
11611161
1162 try testing.expect((try q.to(u64)) == 131072);
1163 try testing.expect((try r.to(u64)) == 8589934592);
1162 try testing.expect((try q.toInt(u64)) == 131072);
1163 try testing.expect((try r.toInt(u64)) == 8589934592);
11641164}
11651165
11661166test "div multi-single no rem" {
......@@ -1179,8 +1179,8 @@ test "div multi-single no rem" {
11791179 defer r.deinit();
11801180 try Managed.divTrunc(&q, &r, &a, &b);
11811181
1182 try testing.expect((try q.to(u64)) == op1 / op2);
1183 try testing.expect((try r.to(u64)) == 0);
1182 try testing.expect((try q.toInt(u64)) == op1 / op2);
1183 try testing.expect((try r.toInt(u64)) == 0);
11841184}
11851185
11861186test "div multi-single with rem" {
......@@ -1199,8 +1199,8 @@ test "div multi-single with rem" {
11991199 defer r.deinit();
12001200 try Managed.divTrunc(&q, &r, &a, &b);
12011201
1202 try testing.expect((try q.to(u64)) == op1 / op2);
1203 try testing.expect((try r.to(u64)) == 3);
1202 try testing.expect((try q.toInt(u64)) == op1 / op2);
1203 try testing.expect((try r.toInt(u64)) == 3);
12041204}
12051205
12061206test "div multi>2-single" {
......@@ -1219,8 +1219,8 @@ test "div multi>2-single" {
12191219 defer r.deinit();
12201220 try Managed.divTrunc(&q, &r, &a, &b);
12211221
1222 try testing.expect((try q.to(u128)) == op1 / op2);
1223 try testing.expect((try r.to(u32)) == 0x3e4e);
1222 try testing.expect((try q.toInt(u128)) == op1 / op2);
1223 try testing.expect((try r.toInt(u32)) == 0x3e4e);
12241224}
12251225
12261226test "div single-single q < r" {
......@@ -1235,8 +1235,8 @@ test "div single-single q < r" {
12351235 defer r.deinit();
12361236 try Managed.divTrunc(&q, &r, &a, &b);
12371237
1238 try testing.expect((try q.to(u64)) == 0);
1239 try testing.expect((try r.to(u64)) == 0x0078f432);
1238 try testing.expect((try q.toInt(u64)) == 0);
1239 try testing.expect((try r.toInt(u64)) == 0x0078f432);
12401240}
12411241
12421242test "div single-single q == r" {
......@@ -1251,8 +1251,8 @@ test "div single-single q == r" {
12511251 defer r.deinit();
12521252 try Managed.divTrunc(&q, &r, &a, &b);
12531253
1254 try testing.expect((try q.to(u64)) == 1);
1255 try testing.expect((try r.to(u64)) == 0);
1254 try testing.expect((try q.toInt(u64)) == 1);
1255 try testing.expect((try r.toInt(u64)) == 0);
12561256}
12571257
12581258test "div q=0 alias" {
......@@ -1263,8 +1263,8 @@ test "div q=0 alias" {
12631263
12641264 try Managed.divTrunc(&a, &b, &a, &b);
12651265
1266 try testing.expect((try a.to(u64)) == 0);
1267 try testing.expect((try b.to(u64)) == 3);
1266 try testing.expect((try a.toInt(u64)) == 0);
1267 try testing.expect((try b.toInt(u64)) == 3);
12681268}
12691269
12701270test "div multi-multi q < r" {
......@@ -1283,8 +1283,8 @@ test "div multi-multi q < r" {
12831283 defer r.deinit();
12841284 try Managed.divTrunc(&q, &r, &a, &b);
12851285
1286 try testing.expect((try q.to(u128)) == 0);
1287 try testing.expect((try r.to(u128)) == op1);
1286 try testing.expect((try q.toInt(u128)) == 0);
1287 try testing.expect((try r.toInt(u128)) == op1);
12881288}
12891289
12901290test "div trunc single-single +/+" {
......@@ -1307,8 +1307,8 @@ test "div trunc single-single +/+" {
13071307 const eq = @divTrunc(u, v);
13081308 const er = @mod(u, v);
13091309
1310 try testing.expect((try q.to(i32)) == eq);
1311 try testing.expect((try r.to(i32)) == er);
1310 try testing.expect((try q.toInt(i32)) == eq);
1311 try testing.expect((try r.toInt(i32)) == er);
13121312}
13131313
13141314test "div trunc single-single -/+" {
......@@ -1331,8 +1331,8 @@ test "div trunc single-single -/+" {
13311331 const eq = -1;
13321332 const er = -2;
13331333
1334 try testing.expect((try q.to(i32)) == eq);
1335 try testing.expect((try r.to(i32)) == er);
1334 try testing.expect((try q.toInt(i32)) == eq);
1335 try testing.expect((try r.toInt(i32)) == er);
13361336}
13371337
13381338test "div trunc single-single +/-" {
......@@ -1355,8 +1355,8 @@ test "div trunc single-single +/-" {
13551355 const eq = -1;
13561356 const er = 2;
13571357
1358 try testing.expect((try q.to(i32)) == eq);
1359 try testing.expect((try r.to(i32)) == er);
1358 try testing.expect((try q.toInt(i32)) == eq);
1359 try testing.expect((try r.toInt(i32)) == er);
13601360}
13611361
13621362test "div trunc single-single -/-" {
......@@ -1379,8 +1379,8 @@ test "div trunc single-single -/-" {
13791379 const eq = 1;
13801380 const er = -2;
13811381
1382 try testing.expect((try q.to(i32)) == eq);
1383 try testing.expect((try r.to(i32)) == er);
1382 try testing.expect((try q.toInt(i32)) == eq);
1383 try testing.expect((try r.toInt(i32)) == er);
13841384}
13851385
13861386test "divTrunc #15535" {
......@@ -1417,7 +1417,7 @@ test "divFloor #10932" {
14171417 const ress = try res.toString(testing.allocator, 16, .lower);
14181418 defer testing.allocator.free(ress);
14191419 try testing.expect(std.mem.eql(u8, ress, "194bd136316c046d070b763396297bf8869a605030216b52597015902a172b2a752f62af1568dcd431602f03725bfa62b0be71ae86616210972c0126e173503011ca48c5747ff066d159c95e46b69cbb14c8fc0bd2bf0919f921be96463200000000000000000000000000000000000000000000000000000000000000000000000000000000"));
1420 try testing.expect((try mod.to(i32)) == 0);
1420 try testing.expect((try mod.toInt(i32)) == 0);
14211421}
14221422
14231423test "divFloor #11166" {
......@@ -1482,7 +1482,7 @@ test "bitAnd #10932" {
14821482
14831483 try res.bitAnd(&a, &b);
14841484
1485 try testing.expect((try res.to(i32)) == 0);
1485 try testing.expect((try res.toInt(i32)) == 0);
14861486}
14871487
14881488test "bit And #19235" {
......@@ -1495,7 +1495,7 @@ test "bit And #19235" {
14951495
14961496 try r.bitAnd(&a, &b);
14971497
1498 try testing.expect((try r.to(i128)) == 0x10000000000000000);
1498 try testing.expect((try r.toInt(i128)) == 0x10000000000000000);
14991499}
15001500
15011501test "div floor single-single +/+" {
......@@ -1518,8 +1518,8 @@ test "div floor single-single +/+" {
15181518 const eq = 1;
15191519 const er = 2;
15201520
1521 try testing.expect((try q.to(i32)) == eq);
1522 try testing.expect((try r.to(i32)) == er);
1521 try testing.expect((try q.toInt(i32)) == eq);
1522 try testing.expect((try r.toInt(i32)) == er);
15231523}
15241524
15251525test "div floor single-single -/+" {
......@@ -1542,8 +1542,8 @@ test "div floor single-single -/+" {
15421542 const eq = -2;
15431543 const er = 1;
15441544
1545 try testing.expect((try q.to(i32)) == eq);
1546 try testing.expect((try r.to(i32)) == er);
1545 try testing.expect((try q.toInt(i32)) == eq);
1546 try testing.expect((try r.toInt(i32)) == er);
15471547}
15481548
15491549test "div floor single-single +/-" {
......@@ -1566,8 +1566,8 @@ test "div floor single-single +/-" {
15661566 const eq = -2;
15671567 const er = -1;
15681568
1569 try testing.expect((try q.to(i32)) == eq);
1570 try testing.expect((try r.to(i32)) == er);
1569 try testing.expect((try q.toInt(i32)) == eq);
1570 try testing.expect((try r.toInt(i32)) == er);
15711571}
15721572
15731573test "div floor single-single -/-" {
......@@ -1590,8 +1590,8 @@ test "div floor single-single -/-" {
15901590 const eq = 1;
15911591 const er = -2;
15921592
1593 try testing.expect((try q.to(i32)) == eq);
1594 try testing.expect((try r.to(i32)) == er);
1593 try testing.expect((try q.toInt(i32)) == eq);
1594 try testing.expect((try r.toInt(i32)) == er);
15951595}
15961596
15971597test "div floor no remainder negative quotient" {
......@@ -1609,8 +1609,8 @@ test "div floor no remainder negative quotient" {
16091609 defer r.deinit();
16101610 try Managed.divFloor(&q, &r, &a, &b);
16111611
1612 try testing.expect((try q.to(i32)) == -0x80000000);
1613 try testing.expect((try r.to(i32)) == 0);
1612 try testing.expect((try q.toInt(i32)) == -0x80000000);
1613 try testing.expect((try r.toInt(i32)) == 0);
16141614}
16151615
16161616test "div floor negative close to zero" {
......@@ -1628,8 +1628,8 @@ test "div floor negative close to zero" {
16281628 defer r.deinit();
16291629 try Managed.divFloor(&q, &r, &a, &b);
16301630
1631 try testing.expect((try q.to(i32)) == -1);
1632 try testing.expect((try r.to(i32)) == 10);
1631 try testing.expect((try q.toInt(i32)) == -1);
1632 try testing.expect((try r.toInt(i32)) == 10);
16331633}
16341634
16351635test "div floor positive close to zero" {
......@@ -1647,8 +1647,8 @@ test "div floor positive close to zero" {
16471647 defer r.deinit();
16481648 try Managed.divFloor(&q, &r, &a, &b);
16491649
1650 try testing.expect((try q.to(i32)) == 0);
1651 try testing.expect((try r.to(i32)) == 10);
1650 try testing.expect((try q.toInt(i32)) == 0);
1651 try testing.expect((try r.toInt(i32)) == 10);
16521652}
16531653
16541654test "div multi-multi with rem" {
......@@ -1665,8 +1665,8 @@ test "div multi-multi with rem" {
16651665 defer r.deinit();
16661666 try Managed.divTrunc(&q, &r, &a, &b);
16671667
1668 try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1669 try testing.expect((try r.to(u128)) == 0x28de0acacd806823638);
1668 try testing.expect((try q.toInt(u128)) == 0xe38f38e39161aaabd03f0f1b);
1669 try testing.expect((try r.toInt(u128)) == 0x28de0acacd806823638);
16701670}
16711671
16721672test "div multi-multi no rem" {
......@@ -1683,8 +1683,8 @@ test "div multi-multi no rem" {
16831683 defer r.deinit();
16841684 try Managed.divTrunc(&q, &r, &a, &b);
16851685
1686 try testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b);
1687 try testing.expect((try r.to(u128)) == 0);
1686 try testing.expect((try q.toInt(u128)) == 0xe38f38e39161aaabd03f0f1b);
1687 try testing.expect((try r.toInt(u128)) == 0);
16881688}
16891689
16901690test "div multi-multi (2 branch)" {
......@@ -1701,8 +1701,8 @@ test "div multi-multi (2 branch)" {
17011701 defer r.deinit();
17021702 try Managed.divTrunc(&q, &r, &a, &b);
17031703
1704 try testing.expect((try q.to(u128)) == 0x10000000000000000);
1705 try testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111);
1704 try testing.expect((try q.toInt(u128)) == 0x10000000000000000);
1705 try testing.expect((try r.toInt(u128)) == 0x44444443444444431111111111111111);
17061706}
17071707
17081708test "div multi-multi (3.1/3.3 branch)" {
......@@ -1719,8 +1719,8 @@ test "div multi-multi (3.1/3.3 branch)" {
17191719 defer r.deinit();
17201720 try Managed.divTrunc(&q, &r, &a, &b);
17211721
1722 try testing.expect((try q.to(u128)) == 0xfffffffffffffffffff);
1723 try testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282);
1722 try testing.expect((try q.toInt(u128)) == 0xfffffffffffffffffff);
1723 try testing.expect((try r.toInt(u256)) == 0x1111111111111111111110b12222222222222222282);
17241724}
17251725
17261726test "div multi-single zero-limb trailing" {
......@@ -1757,7 +1757,7 @@ test "div multi-multi zero-limb trailing (with rem)" {
17571757 defer r.deinit();
17581758 try Managed.divTrunc(&q, &r, &a, &b);
17591759
1760 try testing.expect((try q.to(u128)) == 0x10000000000000000);
1760 try testing.expect((try q.toInt(u128)) == 0x10000000000000000);
17611761
17621762 const rs = try r.toString(testing.allocator, 16, .lower);
17631763 defer testing.allocator.free(rs);
......@@ -1778,7 +1778,7 @@ test "div multi-multi zero-limb trailing (with rem) and dividend zero-limb count
17781778 defer r.deinit();
17791779 try Managed.divTrunc(&q, &r, &a, &b);
17801780
1781 try testing.expect((try q.to(u128)) == 0x1);
1781 try testing.expect((try q.toInt(u128)) == 0x1);
17821782
17831783 const rs = try r.toString(testing.allocator, 16, .lower);
17841784 defer testing.allocator.free(rs);
......@@ -1862,7 +1862,7 @@ test "truncate single unsigned" {
18621862
18631863 try a.truncate(&a, .unsigned, 17);
18641864
1865 try testing.expect((try a.to(u17)) == maxInt(u17));
1865 try testing.expect((try a.toInt(u17)) == maxInt(u17));
18661866}
18671867
18681868test "truncate single signed" {
......@@ -1871,7 +1871,7 @@ test "truncate single signed" {
18711871
18721872 try a.truncate(&a, .signed, 17);
18731873
1874 try testing.expect((try a.to(i17)) == minInt(i17));
1874 try testing.expect((try a.toInt(i17)) == minInt(i17));
18751875}
18761876
18771877test "truncate multi to single unsigned" {
......@@ -1880,7 +1880,7 @@ test "truncate multi to single unsigned" {
18801880
18811881 try a.truncate(&a, .unsigned, 27);
18821882
1883 try testing.expect((try a.to(u27)) == 0x2BC_DEF0);
1883 try testing.expect((try a.toInt(u27)) == 0x2BC_DEF0);
18841884}
18851885
18861886test "truncate multi to single signed" {
......@@ -1889,7 +1889,7 @@ test "truncate multi to single signed" {
18891889
18901890 try a.truncate(&a, .signed, @bitSizeOf(i11));
18911891
1892 try testing.expect((try a.to(i11)) == minInt(i11));
1892 try testing.expect((try a.toInt(i11)) == minInt(i11));
18931893}
18941894
18951895test "truncate multi to multi unsigned" {
......@@ -1901,7 +1901,7 @@ test "truncate multi to multi unsigned" {
19011901
19021902 try a.truncate(&a, .unsigned, bits - 1);
19031903
1904 try testing.expect((try a.to(Int)) == maxInt(Int));
1904 try testing.expect((try a.toInt(Int)) == maxInt(Int));
19051905}
19061906
19071907test "truncate multi to multi signed" {
......@@ -1910,7 +1910,7 @@ test "truncate multi to multi signed" {
19101910
19111911 try a.truncate(&a, .signed, @bitSizeOf(Limb) + 1);
19121912
1913 try testing.expect((try a.to(std.meta.Int(.signed, @bitSizeOf(Limb) + 1))) == -1 << @bitSizeOf(Limb));
1913 try testing.expect((try a.toInt(std.meta.Int(.signed, @bitSizeOf(Limb) + 1))) == -1 << @bitSizeOf(Limb));
19141914}
19151915
19161916test "truncate negative multi to single" {
......@@ -1919,7 +1919,7 @@ test "truncate negative multi to single" {
19191919
19201920 try a.truncate(&a, .signed, @bitSizeOf(i17));
19211921
1922 try testing.expect((try a.to(i17)) == 0);
1922 try testing.expect((try a.toInt(i17)) == 0);
19231923}
19241924
19251925test "truncate multi unsigned many" {
......@@ -1931,7 +1931,7 @@ test "truncate multi unsigned many" {
19311931 defer b.deinit();
19321932 try b.truncate(&a, .signed, @bitSizeOf(i1));
19331933
1934 try testing.expect((try b.to(i1)) == 0);
1934 try testing.expect((try b.toInt(i1)) == 0);
19351935}
19361936
19371937test "saturate single signed positive" {
......@@ -1940,7 +1940,7 @@ test "saturate single signed positive" {
19401940
19411941 try a.saturate(&a, .signed, 17);
19421942
1943 try testing.expect((try a.to(i17)) == maxInt(i17));
1943 try testing.expect((try a.toInt(i17)) == maxInt(i17));
19441944}
19451945
19461946test "saturate single signed negative" {
......@@ -1949,7 +1949,7 @@ test "saturate single signed negative" {
19491949
19501950 try a.saturate(&a, .signed, 17);
19511951
1952 try testing.expect((try a.to(i17)) == minInt(i17));
1952 try testing.expect((try a.toInt(i17)) == minInt(i17));
19531953}
19541954
19551955test "saturate single signed" {
......@@ -1958,7 +1958,7 @@ test "saturate single signed" {
19581958
19591959 try a.saturate(&a, .signed, 17);
19601960
1961 try testing.expect((try a.to(i17)) == maxInt(i17) - 1);
1961 try testing.expect((try a.toInt(i17)) == maxInt(i17) - 1);
19621962}
19631963
19641964test "saturate multi signed" {
......@@ -1967,7 +1967,7 @@ test "saturate multi signed" {
19671967
19681968 try a.saturate(&a, .signed, @bitSizeOf(SignedDoubleLimb));
19691969
1970 try testing.expect((try a.to(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
1970 try testing.expect((try a.toInt(SignedDoubleLimb)) == maxInt(SignedDoubleLimb));
19711971}
19721972
19731973test "saturate single unsigned" {
......@@ -1976,7 +1976,7 @@ test "saturate single unsigned" {
19761976
19771977 try a.saturate(&a, .unsigned, 23);
19781978
1979 try testing.expect((try a.to(u23)) == maxInt(u23));
1979 try testing.expect((try a.toInt(u23)) == maxInt(u23));
19801980}
19811981
19821982test "saturate multi unsigned zero" {
......@@ -1994,7 +1994,7 @@ test "saturate multi unsigned" {
19941994
19951995 try a.saturate(&a, .unsigned, @bitSizeOf(DoubleLimb));
19961996
1997 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb));
1997 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb));
19981998}
19991999
20002000test "shift-right single" {
......@@ -2002,7 +2002,7 @@ test "shift-right single" {
20022002 defer a.deinit();
20032003 try a.shiftRight(&a, 16);
20042004
2005 try testing.expect((try a.to(u32)) == 0xffff);
2005 try testing.expect((try a.toInt(u32)) == 0xffff);
20062006}
20072007
20082008test "shift-right multi" {
......@@ -2010,7 +2010,7 @@ test "shift-right multi" {
20102010 defer a.deinit();
20112011 try a.shiftRight(&a, 67);
20122012
2013 try testing.expect((try a.to(u64)) == 0x1fffe0001dddc222);
2013 try testing.expect((try a.toInt(u64)) == 0x1fffe0001dddc222);
20142014
20152015 try a.set(0xffff0000eeee1111dddd2222cccc3333);
20162016 try a.shiftRight(&a, 63);
......@@ -2037,7 +2037,7 @@ test "shift-left single" {
20372037 defer a.deinit();
20382038 try a.shiftLeft(&a, 16);
20392039
2040 try testing.expect((try a.to(u64)) == 0xffff0000);
2040 try testing.expect((try a.toInt(u64)) == 0xffff0000);
20412041}
20422042
20432043test "shift-left multi" {
......@@ -2045,7 +2045,7 @@ test "shift-left multi" {
20452045 defer a.deinit();
20462046 try a.shiftLeft(&a, 67);
20472047
2048 try testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000);
2048 try testing.expect((try a.toInt(u128)) == 0xffff0000eeee11100000000000000000);
20492049}
20502050
20512051test "shift-right negative" {
......@@ -2055,43 +2055,43 @@ test "shift-right negative" {
20552055 var arg = try Managed.initSet(testing.allocator, -20);
20562056 defer arg.deinit();
20572057 try a.shiftRight(&arg, 2);
2058 try testing.expect((try a.to(i32)) == -5); // -20 >> 2 == -5
2058 try testing.expect((try a.toInt(i32)) == -5); // -20 >> 2 == -5
20592059
20602060 var arg2 = try Managed.initSet(testing.allocator, -5);
20612061 defer arg2.deinit();
20622062 try a.shiftRight(&arg2, 10);
2063 try testing.expect((try a.to(i32)) == -1); // -5 >> 10 == -1
2063 try testing.expect((try a.toInt(i32)) == -1); // -5 >> 10 == -1
20642064
20652065 var arg3 = try Managed.initSet(testing.allocator, -10);
20662066 defer arg3.deinit();
20672067 try a.shiftRight(&arg3, 1232);
2068 try testing.expect((try a.to(i32)) == -1); // -10 >> 1232 == -1
2068 try testing.expect((try a.toInt(i32)) == -1); // -10 >> 1232 == -1
20692069
20702070 var arg4 = try Managed.initSet(testing.allocator, -5);
20712071 defer arg4.deinit();
20722072 try a.shiftRight(&arg4, 2);
2073 try testing.expect(try a.to(i32) == -2); // -5 >> 2 == -2
2073 try testing.expect(try a.toInt(i32) == -2); // -5 >> 2 == -2
20742074
20752075 var arg5 = try Managed.initSet(testing.allocator, -0xffff0000eeee1111dddd2222cccc3333);
20762076 defer arg5.deinit();
20772077 try a.shiftRight(&arg5, 67);
2078 try testing.expect(try a.to(i64) == -0x1fffe0001dddc223);
2078 try testing.expect(try a.toInt(i64) == -0x1fffe0001dddc223);
20792079
20802080 var arg6 = try Managed.initSet(testing.allocator, -0x1ffffffffffffffff);
20812081 defer arg6.deinit();
20822082 try a.shiftRight(&arg6, 1);
20832083 try a.shiftRight(&a, 1);
20842084 a.setSign(true);
2085 try testing.expect(try a.to(u64) == 0x8000000000000000);
2085 try testing.expect(try a.toInt(u64) == 0x8000000000000000);
20862086
20872087 var arg7 = try Managed.initSet(testing.allocator, -32767);
20882088 defer arg7.deinit();
20892089 a.setSign(false);
20902090 try a.shiftRight(&arg7, 4);
2091 try testing.expect(try a.to(i16) == -2048);
2091 try testing.expect(try a.toInt(i16) == -2048);
20922092 a.setSign(true);
20932093 try a.shiftRight(&arg7, 4);
2094 try testing.expect(try a.to(i16) == -2048);
2094 try testing.expect(try a.toInt(i16) == -2048);
20952095}
20962096
20972097test "sat shift-left simple unsigned" {
......@@ -2099,7 +2099,7 @@ test "sat shift-left simple unsigned" {
20992099 defer a.deinit();
21002100 try a.shiftLeftSat(&a, 16, .unsigned, 21);
21012101
2102 try testing.expect((try a.to(u64)) == 0x1fffff);
2102 try testing.expect((try a.toInt(u64)) == 0x1fffff);
21032103}
21042104
21052105test "sat shift-left simple unsigned no sat" {
......@@ -2107,7 +2107,7 @@ test "sat shift-left simple unsigned no sat" {
21072107 defer a.deinit();
21082108 try a.shiftLeftSat(&a, 16, .unsigned, 21);
21092109
2110 try testing.expect((try a.to(u64)) == 0x10000);
2110 try testing.expect((try a.toInt(u64)) == 0x10000);
21112111}
21122112
21132113test "sat shift-left multi unsigned" {
......@@ -2115,7 +2115,7 @@ test "sat shift-left multi unsigned" {
21152115 defer a.deinit();
21162116 try a.shiftLeftSat(&a, @bitSizeOf(DoubleLimb) - 3, .unsigned, @bitSizeOf(DoubleLimb) - 1);
21172117
2118 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb) >> 1);
2118 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb) >> 1);
21192119}
21202120
21212121test "sat shift-left unsigned shift > bitcount" {
......@@ -2123,7 +2123,7 @@ test "sat shift-left unsigned shift > bitcount" {
21232123 defer a.deinit();
21242124 try a.shiftLeftSat(&a, 10, .unsigned, 10);
21252125
2126 try testing.expect((try a.to(u10)) == maxInt(u10));
2126 try testing.expect((try a.toInt(u10)) == maxInt(u10));
21272127}
21282128
21292129test "sat shift-left unsigned zero" {
......@@ -2131,7 +2131,7 @@ test "sat shift-left unsigned zero" {
21312131 defer a.deinit();
21322132 try a.shiftLeftSat(&a, 1, .unsigned, 0);
21332133
2134 try testing.expect((try a.to(u64)) == 0);
2134 try testing.expect((try a.toInt(u64)) == 0);
21352135}
21362136
21372137test "sat shift-left unsigned negative" {
......@@ -2139,7 +2139,7 @@ test "sat shift-left unsigned negative" {
21392139 defer a.deinit();
21402140 try a.shiftLeftSat(&a, 0, .unsigned, 0);
21412141
2142 try testing.expect((try a.to(u64)) == 0);
2142 try testing.expect((try a.toInt(u64)) == 0);
21432143}
21442144
21452145test "sat shift-left signed simple negative" {
......@@ -2147,7 +2147,7 @@ test "sat shift-left signed simple negative" {
21472147 defer a.deinit();
21482148 try a.shiftLeftSat(&a, 3, .signed, 10);
21492149
2150 try testing.expect((try a.to(i10)) == minInt(i10));
2150 try testing.expect((try a.toInt(i10)) == minInt(i10));
21512151}
21522152
21532153test "sat shift-left signed simple positive" {
......@@ -2155,7 +2155,7 @@ test "sat shift-left signed simple positive" {
21552155 defer a.deinit();
21562156 try a.shiftLeftSat(&a, 3, .signed, 10);
21572157
2158 try testing.expect((try a.to(i10)) == maxInt(i10));
2158 try testing.expect((try a.toInt(i10)) == maxInt(i10));
21592159}
21602160
21612161test "sat shift-left signed multi positive" {
......@@ -2170,7 +2170,7 @@ test "sat shift-left signed multi positive" {
21702170 defer a.deinit();
21712171 try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb));
21722172
2173 try testing.expect((try a.to(SignedDoubleLimb)) == x <<| shift);
2173 try testing.expect((try a.toInt(SignedDoubleLimb)) == x <<| shift);
21742174}
21752175
21762176test "sat shift-left signed multi negative" {
......@@ -2185,7 +2185,7 @@ test "sat shift-left signed multi negative" {
21852185 defer a.deinit();
21862186 try a.shiftLeftSat(&a, shift, .signed, @bitSizeOf(SignedDoubleLimb));
21872187
2188 try testing.expect((try a.to(SignedDoubleLimb)) == x <<| shift);
2188 try testing.expect((try a.toInt(SignedDoubleLimb)) == x <<| shift);
21892189}
21902190
21912191test "bitNotWrap unsigned simple" {
......@@ -2197,7 +2197,7 @@ test "bitNotWrap unsigned simple" {
21972197
21982198 try a.bitNotWrap(&a, .unsigned, 10);
21992199
2200 try testing.expect((try a.to(u10)) == ~x);
2200 try testing.expect((try a.toInt(u10)) == ~x);
22012201}
22022202
22032203test "bitNotWrap unsigned multi" {
......@@ -2206,7 +2206,7 @@ test "bitNotWrap unsigned multi" {
22062206
22072207 try a.bitNotWrap(&a, .unsigned, @bitSizeOf(DoubleLimb));
22082208
2209 try testing.expect((try a.to(DoubleLimb)) == maxInt(DoubleLimb));
2209 try testing.expect((try a.toInt(DoubleLimb)) == maxInt(DoubleLimb));
22102210}
22112211
22122212test "bitNotWrap signed simple" {
......@@ -2218,7 +2218,7 @@ test "bitNotWrap signed simple" {
22182218
22192219 try a.bitNotWrap(&a, .signed, 11);
22202220
2221 try testing.expect((try a.to(i11)) == ~x);
2221 try testing.expect((try a.toInt(i11)) == ~x);
22222222}
22232223
22242224test "bitNotWrap signed multi" {
......@@ -2227,7 +2227,7 @@ test "bitNotWrap signed multi" {
22272227
22282228 try a.bitNotWrap(&a, .signed, @bitSizeOf(SignedDoubleLimb));
22292229
2230 try testing.expect((try a.to(SignedDoubleLimb)) == -1);
2230 try testing.expect((try a.toInt(SignedDoubleLimb)) == -1);
22312231}
22322232
22332233test "bitNotWrap more than two limbs" {
......@@ -2249,11 +2249,11 @@ test "bitNotWrap more than two limbs" {
22492249
22502250 try res.bitNotWrap(&a, .unsigned, bits);
22512251 const Unsigned = @Type(.{ .int = .{ .signedness = .unsigned, .bits = bits } });
2252 try testing.expectEqual((try res.to(Unsigned)), ~@as(Unsigned, maxInt(Limb)));
2252 try testing.expectEqual((try res.toInt(Unsigned)), ~@as(Unsigned, maxInt(Limb)));
22532253
22542254 try res.bitNotWrap(&a, .signed, bits);
22552255 const Signed = @Type(.{ .int = .{ .signedness = .signed, .bits = bits } });
2256 try testing.expectEqual((try res.to(Signed)), ~@as(Signed, maxInt(Limb)));
2256 try testing.expectEqual((try res.toInt(Signed)), ~@as(Signed, maxInt(Limb)));
22572257}
22582258
22592259test "bitwise and simple" {
......@@ -2264,7 +2264,7 @@ test "bitwise and simple" {
22642264
22652265 try a.bitAnd(&a, &b);
22662266
2267 try testing.expect((try a.to(u64)) == 0xeeeeeeee00000000);
2267 try testing.expect((try a.toInt(u64)) == 0xeeeeeeee00000000);
22682268}
22692269
22702270test "bitwise and multi-limb" {
......@@ -2275,7 +2275,7 @@ test "bitwise and multi-limb" {
22752275
22762276 try a.bitAnd(&a, &b);
22772277
2278 try testing.expect((try a.to(u128)) == 0);
2278 try testing.expect((try a.toInt(u128)) == 0);
22792279}
22802280
22812281test "bitwise and negative-positive simple" {
......@@ -2286,7 +2286,7 @@ test "bitwise and negative-positive simple" {
22862286
22872287 try a.bitAnd(&a, &b);
22882288
2289 try testing.expect((try a.to(u64)) == 0x22222222);
2289 try testing.expect((try a.toInt(u64)) == 0x22222222);
22902290}
22912291
22922292test "bitwise and negative-positive multi-limb" {
......@@ -2308,7 +2308,7 @@ test "bitwise and positive-negative simple" {
23082308
23092309 try a.bitAnd(&a, &b);
23102310
2311 try testing.expect((try a.to(u64)) == 0x1111111111111110);
2311 try testing.expect((try a.toInt(u64)) == 0x1111111111111110);
23122312}
23132313
23142314test "bitwise and positive-negative multi-limb" {
......@@ -2330,7 +2330,7 @@ test "bitwise and negative-negative simple" {
23302330
23312331 try a.bitAnd(&a, &b);
23322332
2333 try testing.expect((try a.to(i128)) == -0xffffffff33333332);
2333 try testing.expect((try a.toInt(i128)) == -0xffffffff33333332);
23342334}
23352335
23362336test "bitwise and negative-negative multi-limb" {
......@@ -2341,7 +2341,7 @@ test "bitwise and negative-negative multi-limb" {
23412341
23422342 try a.bitAnd(&a, &b);
23432343
2344 try testing.expect((try a.to(i128)) == -maxInt(Limb) * 2 - 2);
2344 try testing.expect((try a.toInt(i128)) == -maxInt(Limb) * 2 - 2);
23452345}
23462346
23472347test "bitwise and negative overflow" {
......@@ -2352,7 +2352,7 @@ test "bitwise and negative overflow" {
23522352
23532353 try a.bitAnd(&a, &b);
23542354
2355 try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb) - 1);
2355 try testing.expect((try a.toInt(SignedDoubleLimb)) == -maxInt(Limb) - 1);
23562356}
23572357
23582358test "bitwise xor simple" {
......@@ -2363,7 +2363,7 @@ test "bitwise xor simple" {
23632363
23642364 try a.bitXor(&a, &b);
23652365
2366 try testing.expect((try a.to(u64)) == 0x1111111133333333);
2366 try testing.expect((try a.toInt(u64)) == 0x1111111133333333);
23672367}
23682368
23692369test "bitwise xor multi-limb" {
......@@ -2378,7 +2378,7 @@ test "bitwise xor multi-limb" {
23782378
23792379 try a.bitXor(&a, &b);
23802380
2381 try testing.expect((try a.to(DoubleLimb)) == x ^ y);
2381 try testing.expect((try a.toInt(DoubleLimb)) == x ^ y);
23822382}
23832383
23842384test "bitwise xor single negative simple" {
......@@ -2389,7 +2389,7 @@ test "bitwise xor single negative simple" {
23892389
23902390 try a.bitXor(&a, &b);
23912391
2392 try testing.expect((try a.to(i64)) == -0x2efed94fcb932ef9);
2392 try testing.expect((try a.toInt(i64)) == -0x2efed94fcb932ef9);
23932393}
23942394
23952395test "bitwise xor single negative multi-limb" {
......@@ -2400,7 +2400,7 @@ test "bitwise xor single negative multi-limb" {
24002400
24012401 try a.bitXor(&a, &b);
24022402
2403 try testing.expect((try a.to(i128)) == -0x6a50889abd8834a24db1f19650d3999a);
2403 try testing.expect((try a.toInt(i128)) == -0x6a50889abd8834a24db1f19650d3999a);
24042404}
24052405
24062406test "bitwise xor single negative overflow" {
......@@ -2411,7 +2411,7 @@ test "bitwise xor single negative overflow" {
24112411
24122412 try a.bitXor(&a, &b);
24132413
2414 try testing.expect((try a.to(SignedDoubleLimb)) == -(maxInt(Limb) + 1));
2414 try testing.expect((try a.toInt(SignedDoubleLimb)) == -(maxInt(Limb) + 1));
24152415}
24162416
24172417test "bitwise xor double negative simple" {
......@@ -2422,7 +2422,7 @@ test "bitwise xor double negative simple" {
24222422
24232423 try a.bitXor(&a, &b);
24242424
2425 try testing.expect((try a.to(u64)) == 0xc39c47081a6eb759);
2425 try testing.expect((try a.toInt(u64)) == 0xc39c47081a6eb759);
24262426}
24272427
24282428test "bitwise xor double negative multi-limb" {
......@@ -2433,7 +2433,7 @@ test "bitwise xor double negative multi-limb" {
24332433
24342434 try a.bitXor(&a, &b);
24352435
2436 try testing.expect((try a.to(u128)) == 0xa3492ec28e62c410dff92bf0549bf771);
2436 try testing.expect((try a.toInt(u128)) == 0xa3492ec28e62c410dff92bf0549bf771);
24372437}
24382438
24392439test "bitwise or simple" {
......@@ -2444,7 +2444,7 @@ test "bitwise or simple" {
24442444
24452445 try a.bitOr(&a, &b);
24462446
2447 try testing.expect((try a.to(u64)) == 0xffffffff33333333);
2447 try testing.expect((try a.toInt(u64)) == 0xffffffff33333333);
24482448}
24492449
24502450test "bitwise or multi-limb" {
......@@ -2455,7 +2455,7 @@ test "bitwise or multi-limb" {
24552455
24562456 try a.bitOr(&a, &b);
24572457
2458 try testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
2458 try testing.expect((try a.toInt(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb));
24592459}
24602460
24612461test "bitwise or negative-positive simple" {
......@@ -2466,7 +2466,7 @@ test "bitwise or negative-positive simple" {
24662466
24672467 try a.bitOr(&a, &b);
24682468
2469 try testing.expect((try a.to(i64)) == -0x1111111111111111);
2469 try testing.expect((try a.toInt(i64)) == -0x1111111111111111);
24702470}
24712471
24722472test "bitwise or negative-positive multi-limb" {
......@@ -2477,7 +2477,7 @@ test "bitwise or negative-positive multi-limb" {
24772477
24782478 try a.bitOr(&a, &b);
24792479
2480 try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb));
2480 try testing.expect((try a.toInt(SignedDoubleLimb)) == -maxInt(Limb));
24812481}
24822482
24832483test "bitwise or positive-negative simple" {
......@@ -2488,7 +2488,7 @@ test "bitwise or positive-negative simple" {
24882488
24892489 try a.bitOr(&a, &b);
24902490
2491 try testing.expect((try a.to(i64)) == -0x22222221);
2491 try testing.expect((try a.toInt(i64)) == -0x22222221);
24922492}
24932493
24942494test "bitwise or positive-negative multi-limb" {
......@@ -2499,7 +2499,7 @@ test "bitwise or positive-negative multi-limb" {
24992499
25002500 try a.bitOr(&a, &b);
25012501
2502 try testing.expect((try a.to(SignedDoubleLimb)) == -1);
2502 try testing.expect((try a.toInt(SignedDoubleLimb)) == -1);
25032503}
25042504
25052505test "bitwise or negative-negative simple" {
......@@ -2510,7 +2510,7 @@ test "bitwise or negative-negative simple" {
25102510
25112511 try a.bitOr(&a, &b);
25122512
2513 try testing.expect((try a.to(i128)) == -0xeeeeeeee00000001);
2513 try testing.expect((try a.toInt(i128)) == -0xeeeeeeee00000001);
25142514}
25152515
25162516test "bitwise or negative-negative multi-limb" {
......@@ -2521,7 +2521,7 @@ test "bitwise or negative-negative multi-limb" {
25212521
25222522 try a.bitOr(&a, &b);
25232523
2524 try testing.expect((try a.to(SignedDoubleLimb)) == -maxInt(Limb));
2524 try testing.expect((try a.toInt(SignedDoubleLimb)) == -maxInt(Limb));
25252525}
25262526
25272527test "var args" {
......@@ -2531,7 +2531,7 @@ test "var args" {
25312531 var b = try Managed.initSet(testing.allocator, 6);
25322532 defer b.deinit();
25332533 try a.add(&a, &b);
2534 try testing.expect((try a.to(u64)) == 11);
2534 try testing.expect((try a.toInt(u64)) == 11);
25352535
25362536 var c = try Managed.initSet(testing.allocator, 11);
25372537 defer c.deinit();
......@@ -2552,7 +2552,7 @@ test "gcd non-one small" {
25522552
25532553 try r.gcd(&a, &b);
25542554
2555 try testing.expect((try r.to(u32)) == 1);
2555 try testing.expect((try r.toInt(u32)) == 1);
25562556}
25572557
25582558test "gcd non-one medium" {
......@@ -2565,7 +2565,7 @@ test "gcd non-one medium" {
25652565
25662566 try r.gcd(&a, &b);
25672567
2568 try testing.expect((try r.to(u32)) == 38);
2568 try testing.expect((try r.toInt(u32)) == 38);
25692569}
25702570
25712571test "gcd non-one large" {
......@@ -2578,7 +2578,7 @@ test "gcd non-one large" {
25782578
25792579 try r.gcd(&a, &b);
25802580
2581 try testing.expect((try r.to(u32)) == 4369);
2581 try testing.expect((try r.toInt(u32)) == 4369);
25822582}
25832583
25842584test "gcd large multi-limb result" {
......@@ -2593,7 +2593,7 @@ test "gcd large multi-limb result" {
25932593
25942594 try r.gcd(&a, &b);
25952595
2596 const answer = (try r.to(u256));
2596 const answer = (try r.toInt(u256));
25972597 try testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
25982598}
25992599
......@@ -2607,7 +2607,7 @@ test "gcd one large" {
26072607
26082608 try r.gcd(&a, &b);
26092609
2610 try testing.expect((try r.to(u64)) == 1);
2610 try testing.expect((try r.toInt(u64)) == 1);
26112611}
26122612
26132613test "mutable to managed" {
......@@ -2637,10 +2637,10 @@ test "pow" {
26372637 defer a.deinit();
26382638
26392639 try a.pow(&a, 3);
2640 try testing.expectEqual(@as(i32, -27), try a.to(i32));
2640 try testing.expectEqual(@as(i32, -27), try a.toInt(i32));
26412641
26422642 try a.pow(&a, 4);
2643 try testing.expectEqual(@as(i32, 531441), try a.to(i32));
2643 try testing.expectEqual(@as(i32, 531441), try a.toInt(i32));
26442644 }
26452645 {
26462646 var a = try Managed.initSet(testing.allocator, 10);
......@@ -2671,18 +2671,18 @@ test "pow" {
26712671 defer a.deinit();
26722672
26732673 try a.pow(&a, 100);
2674 try testing.expectEqual(@as(i32, 0), try a.to(i32));
2674 try testing.expectEqual(@as(i32, 0), try a.toInt(i32));
26752675
26762676 try a.set(1);
26772677 try a.pow(&a, 0);
2678 try testing.expectEqual(@as(i32, 1), try a.to(i32));
2678 try testing.expectEqual(@as(i32, 1), try a.toInt(i32));
26792679 try a.pow(&a, 100);
2680 try testing.expectEqual(@as(i32, 1), try a.to(i32));
2680 try testing.expectEqual(@as(i32, 1), try a.toInt(i32));
26812681 try a.set(-1);
26822682 try a.pow(&a, 15);
2683 try testing.expectEqual(@as(i32, -1), try a.to(i32));
2683 try testing.expectEqual(@as(i32, -1), try a.toInt(i32));
26842684 try a.pow(&a, 16);
2685 try testing.expectEqual(@as(i32, 1), try a.to(i32));
2685 try testing.expectEqual(@as(i32, 1), try a.toInt(i32));
26862686 }
26872687}
26882688
......@@ -2696,24 +2696,24 @@ test "sqrt" {
26962696 try r.set(0);
26972697 try a.set(25);
26982698 try r.sqrt(&a);
2699 try testing.expectEqual(@as(i32, 5), try r.to(i32));
2699 try testing.expectEqual(@as(i32, 5), try r.toInt(i32));
27002700
27012701 // aliased
27022702 try a.set(25);
27032703 try a.sqrt(&a);
2704 try testing.expectEqual(@as(i32, 5), try a.to(i32));
2704 try testing.expectEqual(@as(i32, 5), try a.toInt(i32));
27052705
27062706 // bottom
27072707 try r.set(0);
27082708 try a.set(24);
27092709 try r.sqrt(&a);
2710 try testing.expectEqual(@as(i32, 4), try r.to(i32));
2710 try testing.expectEqual(@as(i32, 4), try r.toInt(i32));
27112711
27122712 // large number
27132713 try r.set(0);
27142714 try a.set(0x1_0000_0000_0000);
27152715 try r.sqrt(&a);
2716 try testing.expectEqual(@as(i32, 0x100_0000), try r.to(i32));
2716 try testing.expectEqual(@as(i32, 0x100_0000), try r.toInt(i32));
27172717}
27182718
27192719test "regression test for 1 limb overflow with alias" {
......@@ -3225,7 +3225,7 @@ test "Managed sqrt(0) = 0" {
32253225 try a.setString(10, "0");
32263226
32273227 try res.sqrt(&a);
3228 try testing.expectEqual(@as(i32, 0), try res.to(i32));
3228 try testing.expectEqual(@as(i32, 0), try res.toInt(i32));
32293229}
32303230
32313231test "Managed sqrt(-1) = error" {
lib/std/math/big/rational.zig+48-48
......@@ -518,28 +518,28 @@ test "set" {
518518 defer a.deinit();
519519
520520 try a.setInt(5);
521 try testing.expect((try a.p.to(u32)) == 5);
522 try testing.expect((try a.q.to(u32)) == 1);
521 try testing.expect((try a.p.toInt(u32)) == 5);
522 try testing.expect((try a.q.toInt(u32)) == 1);
523523
524524 try a.setRatio(7, 3);
525 try testing.expect((try a.p.to(u32)) == 7);
526 try testing.expect((try a.q.to(u32)) == 3);
525 try testing.expect((try a.p.toInt(u32)) == 7);
526 try testing.expect((try a.q.toInt(u32)) == 3);
527527
528528 try a.setRatio(9, 3);
529 try testing.expect((try a.p.to(i32)) == 3);
530 try testing.expect((try a.q.to(i32)) == 1);
529 try testing.expect((try a.p.toInt(i32)) == 3);
530 try testing.expect((try a.q.toInt(i32)) == 1);
531531
532532 try a.setRatio(-9, 3);
533 try testing.expect((try a.p.to(i32)) == -3);
534 try testing.expect((try a.q.to(i32)) == 1);
533 try testing.expect((try a.p.toInt(i32)) == -3);
534 try testing.expect((try a.q.toInt(i32)) == 1);
535535
536536 try a.setRatio(9, -3);
537 try testing.expect((try a.p.to(i32)) == -3);
538 try testing.expect((try a.q.to(i32)) == 1);
537 try testing.expect((try a.p.toInt(i32)) == -3);
538 try testing.expect((try a.q.toInt(i32)) == 1);
539539
540540 try a.setRatio(-9, -3);
541 try testing.expect((try a.p.to(i32)) == 3);
542 try testing.expect((try a.q.to(i32)) == 1);
541 try testing.expect((try a.p.toInt(i32)) == 3);
542 try testing.expect((try a.q.toInt(i32)) == 1);
543543}
544544
545545test "setFloat" {
......@@ -547,24 +547,24 @@ test "setFloat" {
547547 defer a.deinit();
548548
549549 try a.setFloat(f64, 2.5);
550 try testing.expect((try a.p.to(i32)) == 5);
551 try testing.expect((try a.q.to(i32)) == 2);
550 try testing.expect((try a.p.toInt(i32)) == 5);
551 try testing.expect((try a.q.toInt(i32)) == 2);
552552
553553 try a.setFloat(f32, -2.5);
554 try testing.expect((try a.p.to(i32)) == -5);
555 try testing.expect((try a.q.to(i32)) == 2);
554 try testing.expect((try a.p.toInt(i32)) == -5);
555 try testing.expect((try a.q.toInt(i32)) == 2);
556556
557557 try a.setFloat(f32, 3.141593);
558558
559559 // = 3.14159297943115234375
560 try testing.expect((try a.p.to(u32)) == 3294199);
561 try testing.expect((try a.q.to(u32)) == 1048576);
560 try testing.expect((try a.p.toInt(u32)) == 3294199);
561 try testing.expect((try a.q.toInt(u32)) == 1048576);
562562
563563 try a.setFloat(f64, 72.141593120712409172417410926841290461290467124);
564564
565565 // = 72.1415931207124145885245525278151035308837890625
566 try testing.expect((try a.p.to(u128)) == 5076513310880537);
567 try testing.expect((try a.q.to(u128)) == 70368744177664);
566 try testing.expect((try a.p.toInt(u128)) == 5076513310880537);
567 try testing.expect((try a.q.toInt(u128)) == 70368744177664);
568568}
569569
570570test "setFloatString" {
......@@ -574,8 +574,8 @@ test "setFloatString" {
574574 try a.setFloatString("72.14159312071241458852455252781510353");
575575
576576 // = 72.1415931207124145885245525278151035308837890625
577 try testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353);
578 try testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000);
577 try testing.expect((try a.p.toInt(u128)) == 7214159312071241458852455252781510353);
578 try testing.expect((try a.q.toInt(u128)) == 100000000000000000000000000000000000);
579579}
580580
581581test "toFloat" {
......@@ -612,8 +612,8 @@ test "copy" {
612612 defer b.deinit();
613613
614614 try a.copyInt(b);
615 try testing.expect((try a.p.to(u32)) == 5);
616 try testing.expect((try a.q.to(u32)) == 1);
615 try testing.expect((try a.p.toInt(u32)) == 5);
616 try testing.expect((try a.q.toInt(u32)) == 1);
617617
618618 var c = try Int.initSet(testing.allocator, 7);
619619 defer c.deinit();
......@@ -621,8 +621,8 @@ test "copy" {
621621 defer d.deinit();
622622
623623 try a.copyRatio(c, d);
624 try testing.expect((try a.p.to(u32)) == 7);
625 try testing.expect((try a.q.to(u32)) == 3);
624 try testing.expect((try a.p.toInt(u32)) == 7);
625 try testing.expect((try a.q.toInt(u32)) == 3);
626626
627627 var e = try Int.initSet(testing.allocator, 9);
628628 defer e.deinit();
......@@ -630,8 +630,8 @@ test "copy" {
630630 defer f.deinit();
631631
632632 try a.copyRatio(e, f);
633 try testing.expect((try a.p.to(u32)) == 3);
634 try testing.expect((try a.q.to(u32)) == 1);
633 try testing.expect((try a.p.toInt(u32)) == 3);
634 try testing.expect((try a.q.toInt(u32)) == 1);
635635}
636636
637637test "negate" {
......@@ -639,16 +639,16 @@ test "negate" {
639639 defer a.deinit();
640640
641641 try a.setInt(-50);
642 try testing.expect((try a.p.to(i32)) == -50);
643 try testing.expect((try a.q.to(i32)) == 1);
642 try testing.expect((try a.p.toInt(i32)) == -50);
643 try testing.expect((try a.q.toInt(i32)) == 1);
644644
645645 a.negate();
646 try testing.expect((try a.p.to(i32)) == 50);
647 try testing.expect((try a.q.to(i32)) == 1);
646 try testing.expect((try a.p.toInt(i32)) == 50);
647 try testing.expect((try a.q.toInt(i32)) == 1);
648648
649649 a.negate();
650 try testing.expect((try a.p.to(i32)) == -50);
651 try testing.expect((try a.q.to(i32)) == 1);
650 try testing.expect((try a.p.toInt(i32)) == -50);
651 try testing.expect((try a.q.toInt(i32)) == 1);
652652}
653653
654654test "abs" {
......@@ -656,16 +656,16 @@ test "abs" {
656656 defer a.deinit();
657657
658658 try a.setInt(-50);
659 try testing.expect((try a.p.to(i32)) == -50);
660 try testing.expect((try a.q.to(i32)) == 1);
659 try testing.expect((try a.p.toInt(i32)) == -50);
660 try testing.expect((try a.q.toInt(i32)) == 1);
661661
662662 a.abs();
663 try testing.expect((try a.p.to(i32)) == 50);
664 try testing.expect((try a.q.to(i32)) == 1);
663 try testing.expect((try a.p.toInt(i32)) == 50);
664 try testing.expect((try a.q.toInt(i32)) == 1);
665665
666666 a.abs();
667 try testing.expect((try a.p.to(i32)) == 50);
668 try testing.expect((try a.q.to(i32)) == 1);
667 try testing.expect((try a.p.toInt(i32)) == 50);
668 try testing.expect((try a.q.toInt(i32)) == 1);
669669}
670670
671671test "swap" {
......@@ -677,19 +677,19 @@ test "swap" {
677677 try a.setRatio(50, 23);
678678 try b.setRatio(17, 3);
679679
680 try testing.expect((try a.p.to(u32)) == 50);
681 try testing.expect((try a.q.to(u32)) == 23);
680 try testing.expect((try a.p.toInt(u32)) == 50);
681 try testing.expect((try a.q.toInt(u32)) == 23);
682682
683 try testing.expect((try b.p.to(u32)) == 17);
684 try testing.expect((try b.q.to(u32)) == 3);
683 try testing.expect((try b.p.toInt(u32)) == 17);
684 try testing.expect((try b.q.toInt(u32)) == 3);
685685
686686 a.swap(&b);
687687
688 try testing.expect((try a.p.to(u32)) == 17);
689 try testing.expect((try a.q.to(u32)) == 3);
688 try testing.expect((try a.p.toInt(u32)) == 17);
689 try testing.expect((try a.q.toInt(u32)) == 3);
690690
691 try testing.expect((try b.p.to(u32)) == 50);
692 try testing.expect((try b.q.to(u32)) == 23);
691 try testing.expect((try b.p.toInt(u32)) == 50);
692 try testing.expect((try b.q.toInt(u32)) == 23);
693693}
694694
695695test "order" {
lib/std/std.zig+1
......@@ -93,6 +93,7 @@ pub const valgrind = @import("valgrind.zig");
9393pub const wasm = @import("wasm.zig");
9494pub const zig = @import("zig.zig");
9595pub const zip = @import("zip.zig");
96pub const zon = @import("zon.zig");
9697pub const start = @import("start.zig");
9798
9899const root = @import("root");
lib/std/zig/AstGen.zig+26-3
......@@ -9448,7 +9448,18 @@ fn builtinCall(
94489448 } else if (str.len == 0) {
94499449 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
94509450 }
9451 const result = try gz.addStrTok(.import, str.index, str_lit_token);
9451 const res_ty = try ri.rl.resultType(gz, node) orelse .none;
9452 const payload_index = try addExtra(gz.astgen, Zir.Inst.Import{
9453 .res_ty = res_ty,
9454 .path = str.index,
9455 });
9456 const result = try gz.add(.{
9457 .tag = .import,
9458 .data = .{ .pl_tok = .{
9459 .src_tok = gz.tokenIndexToRelative(str_lit_token),
9460 .payload_index = payload_index,
9461 } },
9462 });
94529463 const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
94539464 if (!gop.found_existing) {
94549465 gop.value_ptr.* = str_lit_token;
......@@ -11551,9 +11562,21 @@ fn parseStrLit(
1155111562 }
1155211563}
1155311564
11554fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
11565fn failWithStrLitError(
11566 astgen: *AstGen,
11567 err: std.zig.string_literal.Error,
11568 token: Ast.TokenIndex,
11569 bytes: []const u8,
11570 offset: u32,
11571) InnerError {
1155511572 const raw_string = bytes[offset..];
11556 return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token });
11573 return failOff(
11574 astgen,
11575 token,
11576 @intCast(offset + err.offset()),
11577 "{}",
11578 .{err.fmt(raw_string)},
11579 );
1155711580}
1155811581
1155911582fn failNode(
lib/std/zig/Zir.zig+9-2
......@@ -483,7 +483,7 @@ pub const Inst = struct {
483483 /// Uses the `pl_node` union field. `payload_index` points to a `FuncFancy`.
484484 func_fancy,
485485 /// Implements the `@import` builtin.
486 /// Uses the `str_tok` field.
486 /// Uses the `pl_tok` field.
487487 import,
488488 /// Integer literal that fits in a u64. Uses the `int` union field.
489489 int,
......@@ -1673,7 +1673,7 @@ pub const Inst = struct {
16731673 .func = .pl_node,
16741674 .func_inferred = .pl_node,
16751675 .func_fancy = .pl_node,
1676 .import = .str_tok,
1676 .import = .pl_tok,
16771677 .int = .int,
16781678 .int_big = .str,
16791679 .float = .float,
......@@ -3841,6 +3841,13 @@ pub const Inst = struct {
38413841 /// If `.none`, restore unconditionally.
38423842 operand: Ref,
38433843 };
3844
3845 pub const Import = struct {
3846 /// The result type of the import, or `.none` if none was available.
3847 res_ty: Ref,
3848 /// The import path.
3849 path: NullTerminatedString,
3850 };
38443851};
38453852
38463853pub const SpecialProng = enum { none, @"else", under };
lib/std/zig/Zoir.zig+2-2
......@@ -54,7 +54,7 @@ pub const Node = union(enum) {
5454 /// A floating-point literal.
5555 float_literal: f128,
5656 /// A Unicode codepoint literal.
57 char_literal: u32,
57 char_literal: u21,
5858 /// An enum literal. The string is the literal, i.e. `foo` for `.foo`.
5959 enum_literal: NullTerminatedString,
6060 /// A string literal.
......@@ -96,7 +96,7 @@ pub const Node = union(enum) {
9696 } } },
9797 .float_literal_small => .{ .float_literal = @as(f32, @bitCast(repr.data)) },
9898 .float_literal => .{ .float_literal = @bitCast(zoir.extra[repr.data..][0..4].*) },
99 .char_literal => .{ .char_literal = repr.data },
99 .char_literal => .{ .char_literal = @intCast(repr.data) },
100100 .enum_literal => .{ .enum_literal = @enumFromInt(repr.data) },
101101 .string_literal => .{ .string_literal = s: {
102102 const start, const len = zoir.extra[repr.data..][0..2].*;
lib/std/zig/ZonGen.zig+154-54
......@@ -3,6 +3,8 @@
33gpa: Allocator,
44tree: Ast,
55
6options: Options,
7
68nodes: std.MultiArrayList(Zoir.Node.Repr),
79extra: std.ArrayListUnmanaged(u32),
810limbs: std.ArrayListUnmanaged(std.math.big.Limb),
......@@ -12,12 +14,21 @@ string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.d
1214compile_errors: std.ArrayListUnmanaged(Zoir.CompileError),
1315error_notes: std.ArrayListUnmanaged(Zoir.CompileError.Note),
1416
15pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zoir {
17pub const Options = struct {
18 /// When false, string literals are not parsed. `string_literal` nodes will contain empty
19 /// strings, and errors that normally occur during string parsing will not be raised.
20 ///
21 /// `parseStrLit` and `strLitSizeHint` may be used to parse string literals after the fact.
22 parse_str_lits: bool = true,
23};
24
25pub fn generate(gpa: Allocator, tree: Ast, options: Options) Allocator.Error!Zoir {
1626 assert(tree.mode == .zon);
1727
1828 var zg: ZonGen = .{
1929 .gpa = gpa,
2030 .tree = tree,
31 .options = options,
2132 .nodes = .empty,
2233 .extra = .empty,
2334 .limbs = .empty,
......@@ -250,7 +261,20 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
250261 .block_two_semicolon,
251262 .block,
252263 .block_semicolon,
253 => try zg.addErrorNode(node, "blocks are not allowed in ZON", .{}),
264 => {
265 const size = switch (node_tags[node]) {
266 .block_two, .block_two_semicolon => @intFromBool(node_datas[node].lhs != 0) + @intFromBool(node_datas[node].rhs != 0),
267 .block, .block_semicolon => node_datas[node].rhs - node_datas[node].lhs,
268 else => unreachable,
269 };
270 if (size == 0) {
271 try zg.addErrorNodeNotes(node, "void literals are not available in ZON", .{}, &.{
272 try zg.errNoteNode(node, "void union payloads can be represented by enum literals", .{}),
273 });
274 } else {
275 try zg.addErrorNode(node, "blocks are not allowed in ZON", .{});
276 }
277 },
254278
255279 .array_init_one,
256280 .array_init_one_comma,
......@@ -403,58 +427,37 @@ fn expr(zg: *ZonGen, node: Ast.Node.Index, dest_node: Zoir.Node.Index) Allocator
403427 .ast_node = node,
404428 });
405429
430 // For short initializers, track the names on the stack rather than going through gpa.
431 var sfba_state = std.heap.stackFallback(256, gpa);
432 const sfba = sfba_state.get();
433 var field_names: std.AutoHashMapUnmanaged(Zoir.NullTerminatedString, Ast.TokenIndex) = .empty;
434 defer field_names.deinit(sfba);
435
436 var reported_any_duplicate = false;
437
406438 for (full.ast.fields, names_start.., first_elem..) |elem_node, extra_name_idx, elem_dest_node| {
407439 const name_token = tree.firstToken(elem_node) - 2;
408 zg.extra.items[extra_name_idx] = @intFromEnum(zg.identAsString(name_token) catch |err| switch (err) {
409 error.BadString => undefined, // doesn't matter, there's an error
440 if (zg.identAsString(name_token)) |name_str| {
441 zg.extra.items[extra_name_idx] = @intFromEnum(name_str);
442 const gop = try field_names.getOrPut(sfba, name_str);
443 if (gop.found_existing and !reported_any_duplicate) {
444 reported_any_duplicate = true;
445 const earlier_token = gop.value_ptr.*;
446 try zg.addErrorTokNotes(earlier_token, "duplicate struct field name", .{}, &.{
447 try zg.errNoteTok(name_token, "duplicate name here", .{}),
448 });
449 }
450 gop.value_ptr.* = name_token;
451 } else |err| switch (err) {
452 error.BadString => {}, // there's an error, so it's fine to not populate `zg.extra`
410453 error.OutOfMemory => |e| return e,
411 });
454 }
412455 try zg.expr(elem_node, @enumFromInt(elem_dest_node));
413456 }
414457 },
415458 }
416459}
417460
418fn parseStrLit(zg: *ZonGen, token: Ast.TokenIndex, offset: u32) !u32 {
419 const raw_string = zg.tree.tokenSlice(token)[offset..];
420 const start = zg.string_bytes.items.len;
421 switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) {
422 .success => return @intCast(start),
423 .failure => |err| {
424 try zg.lowerStrLitError(err, token, raw_string, offset);
425 return error.BadString;
426 },
427 }
428}
429
430fn parseMultilineStrLit(zg: *ZonGen, node: Ast.Node.Index) !u32 {
431 const gpa = zg.gpa;
432 const tree = zg.tree;
433 const string_bytes = &zg.string_bytes;
434
435 const first_tok, const last_tok = bounds: {
436 const node_data = tree.nodes.items(.data)[node];
437 break :bounds .{ node_data.lhs, node_data.rhs };
438 };
439
440 const str_index: u32 = @intCast(string_bytes.items.len);
441
442 // First line: do not append a newline.
443 {
444 const line_bytes = tree.tokenSlice(first_tok)[2..];
445 try string_bytes.appendSlice(gpa, line_bytes);
446 }
447 // Following lines: each line prepends a newline.
448 for (first_tok + 1..last_tok + 1) |tok_idx| {
449 const line_bytes = tree.tokenSlice(@intCast(tok_idx))[2..];
450 try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
451 string_bytes.appendAssumeCapacity('\n');
452 string_bytes.appendSliceAssumeCapacity(line_bytes);
453 }
454
455 return @intCast(str_index);
456}
457
458461fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {
459462 const tree = zg.tree;
460463 assert(tree.tokens.items(.tag)[ident_token] == .identifier);
......@@ -464,7 +467,18 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {
464467 try zg.string_bytes.appendSlice(zg.gpa, ident_name);
465468 return @intCast(start);
466469 } else {
467 const start = try zg.parseStrLit(ident_token, 1);
470 const offset = 1;
471 const start: u32 = @intCast(zg.string_bytes.items.len);
472 const raw_string = zg.tree.tokenSlice(ident_token)[offset..];
473 try zg.string_bytes.ensureUnusedCapacity(zg.gpa, raw_string.len);
474 switch (try std.zig.string_literal.parseWrite(zg.string_bytes.writer(zg.gpa), raw_string)) {
475 .success => {},
476 .failure => |err| {
477 try zg.lowerStrLitError(err, ident_token, raw_string, offset);
478 return error.BadString;
479 },
480 }
481
468482 const slice = zg.string_bytes.items[start..];
469483 if (mem.indexOfScalar(u8, slice, 0) != null) {
470484 try zg.addErrorTok(ident_token, "identifier cannot contain null bytes", .{});
......@@ -477,19 +491,93 @@ fn appendIdentStr(zg: *ZonGen, ident_token: Ast.TokenIndex) !u32 {
477491 }
478492}
479493
494/// Estimates the size of a string node without parsing it.
495pub fn strLitSizeHint(tree: Ast, node: Ast.Node.Index) usize {
496 switch (tree.nodes.items(.tag)[node]) {
497 // Parsed string literals are typically around the size of the raw strings.
498 .string_literal => {
499 const token = tree.nodes.items(.main_token)[node];
500 const raw_string = tree.tokenSlice(token);
501 return raw_string.len;
502 },
503 // Multiline string literal lengths can be computed exactly.
504 .multiline_string_literal => {
505 const first_tok, const last_tok = bounds: {
506 const node_data = tree.nodes.items(.data)[node];
507 break :bounds .{ node_data.lhs, node_data.rhs };
508 };
509
510 var size = tree.tokenSlice(first_tok)[2..].len;
511 for (first_tok + 1..last_tok + 1) |tok_idx| {
512 size += 1; // Newline
513 size += tree.tokenSlice(@intCast(tok_idx))[2..].len;
514 }
515 return size;
516 },
517 else => unreachable,
518 }
519}
520
521/// Parses the given node as a string literal.
522pub fn parseStrLit(
523 tree: Ast,
524 node: Ast.Node.Index,
525 writer: anytype,
526) error{OutOfMemory}!std.zig.string_literal.Result {
527 switch (tree.nodes.items(.tag)[node]) {
528 .string_literal => {
529 const token = tree.nodes.items(.main_token)[node];
530 const raw_string = tree.tokenSlice(token);
531 return std.zig.string_literal.parseWrite(writer, raw_string);
532 },
533 .multiline_string_literal => {
534 const first_tok, const last_tok = bounds: {
535 const node_data = tree.nodes.items(.data)[node];
536 break :bounds .{ node_data.lhs, node_data.rhs };
537 };
538
539 // First line: do not append a newline.
540 {
541 const line_bytes = tree.tokenSlice(first_tok)[2..];
542 try writer.writeAll(line_bytes);
543 }
544
545 // Following lines: each line prepends a newline.
546 for (first_tok + 1..last_tok + 1) |tok_idx| {
547 const line_bytes = tree.tokenSlice(@intCast(tok_idx))[2..];
548 try writer.writeByte('\n');
549 try writer.writeAll(line_bytes);
550 }
551
552 return .success;
553 },
554 // Node must represent a string
555 else => unreachable,
556 }
557}
558
480559const StringLiteralResult = union(enum) {
481560 nts: Zoir.NullTerminatedString,
482561 slice: struct { start: u32, len: u32 },
483562};
484563
485564fn strLitAsString(zg: *ZonGen, str_node: Ast.Node.Index) !StringLiteralResult {
565 if (!zg.options.parse_str_lits) return .{ .slice = .{ .start = 0, .len = 0 } };
566
486567 const gpa = zg.gpa;
487568 const string_bytes = &zg.string_bytes;
488 const str_index = switch (zg.tree.nodes.items(.tag)[str_node]) {
489 .string_literal => try zg.parseStrLit(zg.tree.nodes.items(.main_token)[str_node], 0),
490 .multiline_string_literal => try zg.parseMultilineStrLit(str_node),
491 else => unreachable,
492 };
569 const str_index: u32 = @intCast(zg.string_bytes.items.len);
570 const size_hint = strLitSizeHint(zg.tree, str_node);
571 try string_bytes.ensureUnusedCapacity(zg.gpa, size_hint);
572 switch (try parseStrLit(zg.tree, str_node, zg.string_bytes.writer(zg.gpa))) {
573 .success => {},
574 .failure => |err| {
575 const token = zg.tree.nodes.items(.main_token)[str_node];
576 const raw_string = zg.tree.tokenSlice(token);
577 try zg.lowerStrLitError(err, token, raw_string, 0);
578 return error.BadString;
579 },
580 }
493581 const key: []const u8 = string_bytes.items[str_index..];
494582 if (std.mem.indexOfScalar(u8, key, 0) != null) return .{ .slice = .{
495583 .start = str_index,
......@@ -540,7 +628,7 @@ fn numberLiteral(zg: *ZonGen, num_node: Ast.Node.Index, src_node: Ast.Node.Index
540628 if (unsigned_num == 0 and sign == .negative) {
541629 try zg.addErrorTokNotes(num_token, "integer literal '-0' is ambiguous", .{}, &.{
542630 try zg.errNoteTok(num_token, "use '0' for an integer zero", .{}),
543 try zg.errNoteTok(num_token, "use '-0.0' for a flaoting-point signed zero", .{}),
631 try zg.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
544632 });
545633 return;
546634 }
......@@ -679,8 +767,20 @@ fn setNode(zg: *ZonGen, dest: Zoir.Node.Index, repr: Zoir.Node.Repr) void {
679767 zg.nodes.set(@intFromEnum(dest), repr);
680768}
681769
682fn lowerStrLitError(zg: *ZonGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, raw_string: []const u8, offset: u32) Allocator.Error!void {
683 return err.lower(raw_string, offset, ZonGen.addErrorTokOff, .{ zg, token });
770fn lowerStrLitError(
771 zg: *ZonGen,
772 err: std.zig.string_literal.Error,
773 token: Ast.TokenIndex,
774 raw_string: []const u8,
775 offset: u32,
776) Allocator.Error!void {
777 return ZonGen.addErrorTokOff(
778 zg,
779 token,
780 @intCast(offset + err.offset()),
781 "{}",
782 .{err.fmt(raw_string)},
783 );
684784}
685785
686786fn lowerNumberError(zg: *ZonGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) Allocator.Error!void {
lib/std/zig/string_literal.zig+72-30
......@@ -39,40 +39,82 @@ pub const Error = union(enum) {
3939 /// `''`. Not returned for string literals.
4040 empty_char_literal,
4141
42 /// Returns `func(first_args[0], ..., first_args[n], offset + bad_idx, format, args)`.
43 pub fn lower(
42 const FormatMessage = struct {
4443 err: Error,
4544 raw_string: []const u8,
46 offset: u32,
47 comptime func: anytype,
48 first_args: anytype,
49 ) @typeInfo(@TypeOf(func)).@"fn".return_type.? {
50 switch (err) {
51 inline else => |bad_index_or_void, tag| {
52 const bad_index: u32 = switch (@TypeOf(bad_index_or_void)) {
53 void => 0,
54 else => @intCast(bad_index_or_void),
55 };
56 const fmt_str: []const u8, const args = switch (tag) {
57 .invalid_escape_character => .{ "invalid escape character: '{c}'", .{raw_string[bad_index]} },
58 .expected_hex_digit => .{ "expected hex digit, found '{c}'", .{raw_string[bad_index]} },
59 .empty_unicode_escape_sequence => .{ "empty unicode escape sequence", .{} },
60 .expected_hex_digit_or_rbrace => .{ "expected hex digit or '}}', found '{c}'", .{raw_string[bad_index]} },
61 .invalid_unicode_codepoint => .{ "unicode escape does not correspond to a valid unicode scalar value", .{} },
62 .expected_lbrace => .{ "expected '{{', found '{c}'", .{raw_string[bad_index]} },
63 .expected_rbrace => .{ "expected '}}', found '{c}'", .{raw_string[bad_index]} },
64 .expected_single_quote => .{ "expected singel quote ('), found '{c}'", .{raw_string[bad_index]} },
65 .invalid_character => .{ "invalid byte in string or character literal: '{c}'", .{raw_string[bad_index]} },
66 .empty_char_literal => .{ "empty character literal", .{} },
67 };
68 return @call(.auto, func, first_args ++ .{
69 offset + bad_index,
70 fmt_str,
71 args,
72 });
73 },
45 };
46
47 fn formatMessage(
48 self: FormatMessage,
49 comptime f: []const u8,
50 options: std.fmt.FormatOptions,
51 writer: anytype,
52 ) !void {
53 _ = f;
54 _ = options;
55 switch (self.err) {
56 .invalid_escape_character => |bad_index| try writer.print(
57 "invalid escape character: '{c}'",
58 .{self.raw_string[bad_index]},
59 ),
60 .expected_hex_digit => |bad_index| try writer.print(
61 "expected hex digit, found '{c}'",
62 .{self.raw_string[bad_index]},
63 ),
64 .empty_unicode_escape_sequence => try writer.writeAll(
65 "empty unicode escape sequence",
66 ),
67 .expected_hex_digit_or_rbrace => |bad_index| try writer.print(
68 "expected hex digit or '}}', found '{c}'",
69 .{self.raw_string[bad_index]},
70 ),
71 .invalid_unicode_codepoint => try writer.writeAll(
72 "unicode escape does not correspond to a valid unicode scalar value",
73 ),
74 .expected_lbrace => |bad_index| try writer.print(
75 "expected '{{', found '{c}'",
76 .{self.raw_string[bad_index]},
77 ),
78 .expected_rbrace => |bad_index| try writer.print(
79 "expected '}}', found '{c}'",
80 .{self.raw_string[bad_index]},
81 ),
82 .expected_single_quote => |bad_index| try writer.print(
83 "expected single quote ('), found '{c}'",
84 .{self.raw_string[bad_index]},
85 ),
86 .invalid_character => |bad_index| try writer.print(
87 "invalid byte in string or character literal: '{c}'",
88 .{self.raw_string[bad_index]},
89 ),
90 .empty_char_literal => try writer.writeAll(
91 "empty character literal",
92 ),
7493 }
7594 }
95
96 pub fn fmt(self: @This(), raw_string: []const u8) std.fmt.Formatter(formatMessage) {
97 return .{ .data = .{
98 .err = self,
99 .raw_string = raw_string,
100 } };
101 }
102
103 pub fn offset(err: Error) usize {
104 return switch (err) {
105 inline .invalid_escape_character,
106 .expected_hex_digit,
107 .empty_unicode_escape_sequence,
108 .expected_hex_digit_or_rbrace,
109 .invalid_unicode_codepoint,
110 .expected_lbrace,
111 .expected_rbrace,
112 .expected_single_quote,
113 .invalid_character,
114 => |n| n,
115 .empty_char_literal => 0,
116 };
117 }
76118};
77119
78120/// Asserts the slice starts and ends with single-quotes.
lib/std/zon.zig created+45
......@@ -0,0 +1,45 @@
1//! ZON parsing and stringification.
2//!
3//! ZON ("Zig Object Notation") is a textual file format. Outside of `nan` and `inf` literals, ZON's
4//! grammar is a subset of Zig's.
5//!
6//! Supported Zig primitives:
7//! * boolean literals
8//! * number literals (including `nan` and `inf`)
9//! * character literals
10//! * enum literals
11//! * `null` literals
12//! * string literals
13//! * multiline string literals
14//!
15//! Supported Zig container types:
16//! * anonymous struct literals
17//! * anonymous tuple literals
18//!
19//! Here is an example ZON object:
20//! ```
21//! .{
22//! .a = 1.5,
23//! .b = "hello, world!",
24//! .c = .{ true, false },
25//! .d = .{ 1, 2, 3 },
26//! }
27//! ```
28//!
29//! Individual primitives are also valid ZON, for example:
30//! ```
31//! "This string is a valid ZON object."
32//! ```
33//!
34//! ZON may not contain type names.
35//!
36//! ZON does not have syntax for pointers, but the parsers will allocate as needed to match the
37//! given Zig types. Similarly, the serializer will traverse pointers.
38
39pub const parse = @import("zon/parse.zig");
40pub const stringify = @import("zon/stringify.zig");
41
42test {
43 _ = parse;
44 _ = stringify;
45}
lib/std/zon/parse.zig created+3449
......@@ -0,0 +1,3449 @@
1//! The simplest way to parse ZON at runtime is to use `fromSlice`. If you need to parse ZON at
2//! compile time, you may use `@import`.
3//!
4//! Parsing from individual Zoir nodes is also available:
5//! * `fromZoir`
6//! * `fromZoirNode`
7//!
8//! For lower level control, it is possible to operate on `std.zig.Zoir` directly.
9
10const std = @import("std");
11const builtin = @import("builtin");
12const Allocator = std.mem.Allocator;
13const Ast = std.zig.Ast;
14const Zoir = std.zig.Zoir;
15const ZonGen = std.zig.ZonGen;
16const TokenIndex = std.zig.Ast.TokenIndex;
17const Base = std.zig.number_literal.Base;
18const StrLitErr = std.zig.string_literal.Error;
19const NumberLiteralError = std.zig.number_literal.Error;
20const assert = std.debug.assert;
21const ArrayListUnmanaged = std.ArrayListUnmanaged;
22
23/// Rename when adding or removing support for a type.
24const valid_types = {};
25
26/// Configuration for the runtime parser.
27pub const Options = struct {
28 /// If true, unknown fields do not error.
29 ignore_unknown_fields: bool = false,
30 /// If true, the parser cleans up partially parsed values on error. This requires some extra
31 /// bookkeeping, so you may want to turn it off if you don't need this feature (e.g. because
32 /// you're using arena allocation.)
33 free_on_error: bool = true,
34};
35
36pub const Error = union(enum) {
37 zoir: Zoir.CompileError,
38 type_check: Error.TypeCheckFailure,
39
40 pub const Note = union(enum) {
41 zoir: Zoir.CompileError.Note,
42 type_check: TypeCheckFailure.Note,
43
44 pub const Iterator = struct {
45 index: usize = 0,
46 err: Error,
47 status: *const Status,
48
49 pub fn next(self: *@This()) ?Note {
50 switch (self.err) {
51 .zoir => |err| {
52 if (self.index >= err.note_count) return null;
53 const zoir = self.status.zoir.?;
54 const note = err.getNotes(zoir)[self.index];
55 self.index += 1;
56 return .{ .zoir = note };
57 },
58 .type_check => |err| {
59 if (self.index >= err.getNoteCount()) return null;
60 const note = err.getNote(self.index);
61 self.index += 1;
62 return .{ .type_check = note };
63 },
64 }
65 }
66 };
67
68 fn formatMessage(
69 self: []const u8,
70 comptime f: []const u8,
71 options: std.fmt.FormatOptions,
72 writer: anytype,
73 ) !void {
74 _ = f;
75 _ = options;
76
77 // Just writes the string for now, but we're keeping this behind a formatter so we have
78 // the option to extend it in the future to print more advanced messages (like `Error`
79 // does) without breaking the API.
80 try writer.writeAll(self);
81 }
82
83 pub fn fmtMessage(self: Note, status: *const Status) std.fmt.Formatter(Note.formatMessage) {
84 return .{ .data = switch (self) {
85 .zoir => |note| note.msg.get(status.zoir.?),
86 .type_check => |note| note.msg,
87 } };
88 }
89
90 pub fn getLocation(self: Note, status: *const Status) Ast.Location {
91 const ast = status.ast.?;
92 switch (self) {
93 .zoir => |note| return zoirErrorLocation(ast, note.token, note.node_or_offset),
94 .type_check => |note| return ast.tokenLocation(note.offset, note.token),
95 }
96 }
97 };
98
99 pub const Iterator = struct {
100 index: usize = 0,
101 status: *const Status,
102
103 pub fn next(self: *@This()) ?Error {
104 const zoir = self.status.zoir orelse return null;
105
106 if (self.index < zoir.compile_errors.len) {
107 const result: Error = .{ .zoir = zoir.compile_errors[self.index] };
108 self.index += 1;
109 return result;
110 }
111
112 if (self.status.type_check) |err| {
113 if (self.index == zoir.compile_errors.len) {
114 const result: Error = .{ .type_check = err };
115 self.index += 1;
116 return result;
117 }
118 }
119
120 return null;
121 }
122 };
123
124 const TypeCheckFailure = struct {
125 const Note = struct {
126 token: Ast.TokenIndex,
127 offset: u32,
128 msg: []const u8,
129 owned: bool,
130
131 fn deinit(self: @This(), gpa: Allocator) void {
132 if (self.owned) gpa.free(self.msg);
133 }
134 };
135
136 message: []const u8,
137 owned: bool,
138 token: Ast.TokenIndex,
139 offset: u32,
140 note: ?@This().Note,
141
142 fn deinit(self: @This(), gpa: Allocator) void {
143 if (self.note) |note| note.deinit(gpa);
144 if (self.owned) gpa.free(self.message);
145 }
146
147 fn getNoteCount(self: @This()) usize {
148 return @intFromBool(self.note != null);
149 }
150
151 fn getNote(self: @This(), index: usize) @This().Note {
152 assert(index == 0);
153 return self.note.?;
154 }
155 };
156
157 const FormatMessage = struct {
158 err: Error,
159 status: *const Status,
160 };
161
162 fn formatMessage(
163 self: FormatMessage,
164 comptime f: []const u8,
165 options: std.fmt.FormatOptions,
166 writer: anytype,
167 ) !void {
168 _ = f;
169 _ = options;
170 switch (self.err) {
171 .zoir => |err| try writer.writeAll(err.msg.get(self.status.zoir.?)),
172 .type_check => |tc| try writer.writeAll(tc.message),
173 }
174 }
175
176 pub fn fmtMessage(self: @This(), status: *const Status) std.fmt.Formatter(formatMessage) {
177 return .{ .data = .{
178 .err = self,
179 .status = status,
180 } };
181 }
182
183 pub fn getLocation(self: @This(), status: *const Status) Ast.Location {
184 const ast = status.ast.?;
185 return switch (self) {
186 .zoir => |err| return zoirErrorLocation(
187 status.ast.?,
188 err.token,
189 err.node_or_offset,
190 ),
191 .type_check => |err| return ast.tokenLocation(err.offset, err.token),
192 };
193 }
194
195 pub fn iterateNotes(self: @This(), status: *const Status) Note.Iterator {
196 return .{ .err = self, .status = status };
197 }
198
199 fn zoirErrorLocation(ast: Ast, maybe_token: Ast.TokenIndex, node_or_offset: u32) Ast.Location {
200 if (maybe_token == Zoir.CompileError.invalid_token) {
201 const main_tokens = ast.nodes.items(.main_token);
202 const ast_node = node_or_offset;
203 const token = main_tokens[ast_node];
204 return ast.tokenLocation(0, token);
205 } else {
206 var location = ast.tokenLocation(0, maybe_token);
207 location.column += node_or_offset;
208 return location;
209 }
210 }
211};
212
213/// Information about the success or failure of a parse.
214pub const Status = struct {
215 ast: ?Ast = null,
216 zoir: ?Zoir = null,
217 type_check: ?Error.TypeCheckFailure = null,
218
219 fn assertEmpty(self: Status) void {
220 assert(self.ast == null);
221 assert(self.zoir == null);
222 assert(self.type_check == null);
223 }
224
225 pub fn deinit(self: *Status, gpa: Allocator) void {
226 if (self.ast) |*ast| ast.deinit(gpa);
227 if (self.zoir) |*zoir| zoir.deinit(gpa);
228 if (self.type_check) |tc| tc.deinit(gpa);
229 self.* = undefined;
230 }
231
232 pub fn iterateErrors(self: *const Status) Error.Iterator {
233 return .{ .status = self };
234 }
235
236 pub fn format(
237 self: *const @This(),
238 comptime fmt: []const u8,
239 options: std.fmt.FormatOptions,
240 writer: anytype,
241 ) !void {
242 _ = fmt;
243 _ = options;
244 var errors = self.iterateErrors();
245 while (errors.next()) |err| {
246 const loc = err.getLocation(self);
247 const msg = err.fmtMessage(self);
248 try writer.print("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, msg });
249
250 var notes = err.iterateNotes(self);
251 while (notes.next()) |note| {
252 const note_loc = note.getLocation(self);
253 const note_msg = note.fmtMessage(self);
254 try writer.print("{}:{}: note: {s}\n", .{
255 note_loc.line + 1,
256 note_loc.column + 1,
257 note_msg,
258 });
259 }
260 }
261 }
262};
263
264/// Parses the given slice as ZON.
265///
266/// Returns `error.OutOfMemory` on allocation failure, or `error.ParseZon` error if the ZON is
267/// invalid or can not be deserialized into type `T`.
268///
269/// When the parser returns `error.ParseZon`, it will also store a human readable explanation in
270/// `status` if non null. If status is not null, it must be initialized to `.{}`.
271pub fn fromSlice(
272 /// The type to deserialize into. May not be or contain any of the following types:
273 /// * Any comptime-only type, except in a comptime field
274 /// * `type`
275 /// * `void`, except as a union payload
276 /// * `noreturn`
277 /// * An error set/error union
278 /// * A many-pointer or C-pointer
279 /// * An opaque type, including `anyopaque`
280 /// * An async frame type, including `anyframe` and `anyframe->T`
281 /// * A function
282 ///
283 /// All other types are valid. Unsupported types will fail at compile time.
284 T: type,
285 gpa: Allocator,
286 source: [:0]const u8,
287 status: ?*Status,
288 options: Options,
289) error{ OutOfMemory, ParseZon }!T {
290 if (status) |s| s.assertEmpty();
291
292 var ast = try std.zig.Ast.parse(gpa, source, .zon);
293 defer if (status == null) ast.deinit(gpa);
294 if (status) |s| s.ast = ast;
295
296 // If there's no status, Zoir exists for the lifetime of this function. If there is a status,
297 // ownership is transferred to status.
298 var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false });
299 defer if (status == null) zoir.deinit(gpa);
300
301 if (status) |s| s.* = .{};
302 return fromZoir(T, gpa, ast, zoir, status, options);
303}
304
305/// Like `fromSlice`, but operates on `Zoir` instead of ZON source.
306pub fn fromZoir(
307 T: type,
308 gpa: Allocator,
309 ast: Ast,
310 zoir: Zoir,
311 status: ?*Status,
312 options: Options,
313) error{ OutOfMemory, ParseZon }!T {
314 return fromZoirNode(T, gpa, ast, zoir, .root, status, options);
315}
316
317/// Like `fromZoir`, but the parse starts on `node` instead of root.
318pub fn fromZoirNode(
319 T: type,
320 gpa: Allocator,
321 ast: Ast,
322 zoir: Zoir,
323 node: Zoir.Node.Index,
324 status: ?*Status,
325 options: Options,
326) error{ OutOfMemory, ParseZon }!T {
327 comptime assert(canParseType(T));
328
329 if (status) |s| {
330 s.assertEmpty();
331 s.ast = ast;
332 s.zoir = zoir;
333 }
334
335 if (zoir.hasCompileErrors()) {
336 return error.ParseZon;
337 }
338
339 var parser: Parser = .{
340 .gpa = gpa,
341 .ast = ast,
342 .zoir = zoir,
343 .options = options,
344 .status = status,
345 };
346
347 return parser.parseExpr(T, node);
348}
349
350/// Frees ZON values.
351///
352/// Provided for convenience, you may also free these values on your own using the same allocator
353/// passed into the parser.
354///
355/// Asserts at comptime that sufficient information is available via the type system to free this
356/// value. Untagged unions, for example, will fail this assert.
357pub fn free(gpa: Allocator, value: anytype) void {
358 const Value = @TypeOf(value);
359
360 _ = valid_types;
361 switch (@typeInfo(Value)) {
362 .bool, .int, .float, .@"enum" => {},
363 .pointer => |pointer| {
364 switch (pointer.size) {
365 .one => {
366 free(gpa, value.*);
367 gpa.destroy(value);
368 },
369 .slice => {
370 for (value) |item| {
371 free(gpa, item);
372 }
373 gpa.free(value);
374 },
375 .many, .c => comptime unreachable,
376 }
377 },
378 .array => for (value) |item| {
379 free(gpa, item);
380 },
381 .@"struct" => |@"struct"| inline for (@"struct".fields) |field| {
382 free(gpa, @field(value, field.name));
383 },
384 .@"union" => |@"union"| if (@"union".tag_type == null) {
385 if (comptime requiresAllocator(Value)) unreachable;
386 } else switch (value) {
387 inline else => |_, tag| {
388 free(gpa, @field(value, @tagName(tag)));
389 },
390 },
391 .optional => if (value) |some| {
392 free(gpa, some);
393 },
394 .vector => |vector| for (0..vector.len) |i| free(gpa, value[i]),
395 .void => {},
396 else => comptime unreachable,
397 }
398}
399
400fn requiresAllocator(T: type) bool {
401 _ = valid_types;
402 return switch (@typeInfo(T)) {
403 .pointer => true,
404 .array => |array| return array.len > 0 and requiresAllocator(array.child),
405 .@"struct" => |@"struct"| inline for (@"struct".fields) |field| {
406 if (requiresAllocator(field.type)) {
407 break true;
408 }
409 } else false,
410 .@"union" => |@"union"| inline for (@"union".fields) |field| {
411 if (requiresAllocator(field.type)) {
412 break true;
413 }
414 } else false,
415 .optional => |optional| requiresAllocator(optional.child),
416 .vector => |vector| return vector.len > 0 and requiresAllocator(vector.child),
417 else => false,
418 };
419}
420
421const Parser = struct {
422 gpa: Allocator,
423 ast: Ast,
424 zoir: Zoir,
425 status: ?*Status,
426 options: Options,
427
428 fn parseExpr(self: *@This(), T: type, node: Zoir.Node.Index) error{ ParseZon, OutOfMemory }!T {
429 return self.parseExprInner(T, node) catch |err| switch (err) {
430 error.WrongType => return self.failExpectedType(T, node),
431 else => |e| return e,
432 };
433 }
434
435 fn parseExprInner(
436 self: *@This(),
437 T: type,
438 node: Zoir.Node.Index,
439 ) error{ ParseZon, OutOfMemory, WrongType }!T {
440 switch (@typeInfo(T)) {
441 .optional => |optional| if (node.get(self.zoir) == .null) {
442 return null;
443 } else {
444 return try self.parseExprInner(optional.child, node);
445 },
446 .bool => return self.parseBool(node),
447 .int => return self.parseInt(T, node),
448 .float => return self.parseFloat(T, node),
449 .@"enum" => return self.parseEnumLiteral(T, node),
450 .pointer => |pointer| switch (pointer.size) {
451 .one => {
452 const result = try self.gpa.create(pointer.child);
453 errdefer self.gpa.destroy(result);
454 result.* = try self.parseExprInner(pointer.child, node);
455 return result;
456 },
457 .slice => return self.parseSlicePointer(T, node),
458 else => comptime unreachable,
459 },
460 .array => return self.parseArray(T, node),
461 .@"struct" => |@"struct"| if (@"struct".is_tuple)
462 return self.parseTuple(T, node)
463 else
464 return self.parseStruct(T, node),
465 .@"union" => return self.parseUnion(T, node),
466 .vector => return self.parseVector(T, node),
467
468 else => comptime unreachable,
469 }
470 }
471
472 /// Prints a message of the form `expected T` where T is first converted to a ZON type. For
473 /// example, `**?**u8` becomes `?u8`, and types that involve user specified type names are just
474 /// referred to by the type of container.
475 fn failExpectedType(
476 self: @This(),
477 T: type,
478 node: Zoir.Node.Index,
479 ) error{ ParseZon, OutOfMemory } {
480 @branchHint(.cold);
481 return self.failExpectedTypeInner(T, false, node);
482 }
483
484 fn failExpectedTypeInner(
485 self: @This(),
486 T: type,
487 opt: bool,
488 node: Zoir.Node.Index,
489 ) error{ ParseZon, OutOfMemory } {
490 _ = valid_types;
491 switch (@typeInfo(T)) {
492 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
493 if (opt) {
494 return self.failNode(node, "expected optional tuple");
495 } else {
496 return self.failNode(node, "expected tuple");
497 }
498 } else {
499 if (opt) {
500 return self.failNode(node, "expected optional struct");
501 } else {
502 return self.failNode(node, "expected struct");
503 }
504 },
505 .@"union" => if (opt) {
506 return self.failNode(node, "expected optional union");
507 } else {
508 return self.failNode(node, "expected union");
509 },
510 .array => if (opt) {
511 return self.failNode(node, "expected optional array");
512 } else {
513 return self.failNode(node, "expected array");
514 },
515 .pointer => |pointer| switch (pointer.size) {
516 .one => return self.failExpectedTypeInner(pointer.child, opt, node),
517 .slice => {
518 if (pointer.child == u8 and
519 pointer.is_const and
520 (pointer.sentinel() == null or pointer.sentinel() == 0) and
521 pointer.alignment == 1)
522 {
523 if (opt) {
524 return self.failNode(node, "expected optional string");
525 } else {
526 return self.failNode(node, "expected string");
527 }
528 } else {
529 if (opt) {
530 return self.failNode(node, "expected optional array");
531 } else {
532 return self.failNode(node, "expected array");
533 }
534 }
535 },
536 else => comptime unreachable,
537 },
538 .vector, .bool, .int, .float => if (opt) {
539 return self.failNodeFmt(node, "expected type '{s}'", .{@typeName(?T)});
540 } else {
541 return self.failNodeFmt(node, "expected type '{s}'", .{@typeName(T)});
542 },
543 .@"enum" => if (opt) {
544 return self.failNode(node, "expected optional enum literal");
545 } else {
546 return self.failNode(node, "expected enum literal");
547 },
548 .optional => |optional| {
549 return self.failExpectedTypeInner(optional.child, true, node);
550 },
551 else => comptime unreachable,
552 }
553 }
554
555 fn parseBool(self: @This(), node: Zoir.Node.Index) !bool {
556 switch (node.get(self.zoir)) {
557 .true => return true,
558 .false => return false,
559 else => return error.WrongType,
560 }
561 }
562
563 fn parseInt(self: @This(), T: type, node: Zoir.Node.Index) !T {
564 switch (node.get(self.zoir)) {
565 .int_literal => |int| switch (int) {
566 .small => |val| return std.math.cast(T, val) orelse
567 self.failCannotRepresent(T, node),
568 .big => |val| return val.toInt(T) catch
569 self.failCannotRepresent(T, node),
570 },
571 .float_literal => |val| return intFromFloatExact(T, val) orelse
572 self.failCannotRepresent(T, node),
573
574 .char_literal => |val| return std.math.cast(T, val) orelse
575 self.failCannotRepresent(T, node),
576 else => return error.WrongType,
577 }
578 }
579
580 fn parseFloat(self: @This(), T: type, node: Zoir.Node.Index) !T {
581 switch (node.get(self.zoir)) {
582 .int_literal => |int| switch (int) {
583 .small => |val| return @floatFromInt(val),
584 .big => |val| return val.toFloat(T),
585 },
586 .float_literal => |val| return @floatCast(val),
587 .pos_inf => return std.math.inf(T),
588 .neg_inf => return -std.math.inf(T),
589 .nan => return std.math.nan(T),
590 .char_literal => |val| return @floatFromInt(val),
591 else => return error.WrongType,
592 }
593 }
594
595 fn parseEnumLiteral(self: @This(), T: type, node: Zoir.Node.Index) !T {
596 switch (node.get(self.zoir)) {
597 .enum_literal => |field_name| {
598 // Create a comptime string map for the enum fields
599 const enum_fields = @typeInfo(T).@"enum".fields;
600 comptime var kvs_list: [enum_fields.len]struct { []const u8, T } = undefined;
601 inline for (enum_fields, 0..) |field, i| {
602 kvs_list[i] = .{ field.name, @enumFromInt(field.value) };
603 }
604 const enum_tags = std.StaticStringMap(T).initComptime(kvs_list);
605
606 // Get the tag if it exists
607 const field_name_str = field_name.get(self.zoir);
608 return enum_tags.get(field_name_str) orelse
609 self.failUnexpected(T, "enum literal", node, null, field_name_str);
610 },
611 else => return error.WrongType,
612 }
613 }
614
615 fn parseSlicePointer(self: *@This(), T: type, node: Zoir.Node.Index) !T {
616 switch (node.get(self.zoir)) {
617 .string_literal => return self.parseString(T, node),
618 .array_literal => |nodes| return self.parseSlice(T, nodes),
619 .empty_literal => return self.parseSlice(T, .{ .start = node, .len = 0 }),
620 else => return error.WrongType,
621 }
622 }
623
624 fn parseString(self: *@This(), T: type, node: Zoir.Node.Index) !T {
625 const ast_node = node.getAstNode(self.zoir);
626 const pointer = @typeInfo(T).pointer;
627 var size_hint = ZonGen.strLitSizeHint(self.ast, ast_node);
628 if (pointer.sentinel() != null) size_hint += 1;
629
630 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, size_hint);
631 defer buf.deinit(self.gpa);
632 switch (try ZonGen.parseStrLit(self.ast, ast_node, buf.writer(self.gpa))) {
633 .success => {},
634 .failure => |err| {
635 const token = self.ast.nodes.items(.main_token)[ast_node];
636 const raw_string = self.ast.tokenSlice(token);
637 return self.failTokenFmt(token, @intCast(err.offset()), "{s}", .{err.fmt(raw_string)});
638 },
639 }
640
641 if (pointer.child != u8 or
642 pointer.size != .slice or
643 !pointer.is_const or
644 (pointer.sentinel() != null and pointer.sentinel() != 0) or
645 pointer.alignment != 1)
646 {
647 return error.WrongType;
648 }
649
650 if (pointer.sentinel() != null) {
651 return buf.toOwnedSliceSentinel(self.gpa, 0);
652 } else {
653 return buf.toOwnedSlice(self.gpa);
654 }
655 }
656
657 fn parseSlice(self: *@This(), T: type, nodes: Zoir.Node.Index.Range) !T {
658 const pointer = @typeInfo(T).pointer;
659
660 // Make sure we're working with a slice
661 switch (pointer.size) {
662 .slice => {},
663 .one, .many, .c => comptime unreachable,
664 }
665
666 // Allocate the slice
667 const slice = try self.gpa.allocWithOptions(
668 pointer.child,
669 nodes.len,
670 pointer.alignment,
671 pointer.sentinel(),
672 );
673 errdefer self.gpa.free(slice);
674
675 // Parse the elements and return the slice
676 for (slice, 0..) |*elem, i| {
677 errdefer if (self.options.free_on_error) {
678 for (slice[0..i]) |item| {
679 free(self.gpa, item);
680 }
681 };
682 elem.* = try self.parseExpr(pointer.child, nodes.at(@intCast(i)));
683 }
684
685 return slice;
686 }
687
688 fn parseArray(self: *@This(), T: type, node: Zoir.Node.Index) !T {
689 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
690 .array_literal => |nodes| nodes,
691 .empty_literal => .{ .start = node, .len = 0 },
692 else => return error.WrongType,
693 };
694
695 const array_info = @typeInfo(T).array;
696
697 // Check if the size matches
698 if (nodes.len < array_info.len) {
699 return self.failNodeFmt(
700 node,
701 "expected {} array elements; found {}",
702 .{ array_info.len, nodes.len },
703 );
704 } else if (nodes.len > array_info.len) {
705 return self.failNodeFmt(
706 nodes.at(array_info.len),
707 "index {} outside of array of length {}",
708 .{ array_info.len, array_info.len },
709 );
710 }
711
712 // Parse the elements and return the array
713 var result: T = undefined;
714 for (&result, 0..) |*elem, i| {
715 // If we fail to parse this field, free all fields before it
716 errdefer if (self.options.free_on_error) {
717 for (result[0..i]) |item| {
718 free(self.gpa, item);
719 }
720 };
721
722 elem.* = try self.parseExpr(array_info.child, nodes.at(@intCast(i)));
723 }
724 return result;
725 }
726
727 fn parseStruct(self: *@This(), T: type, node: Zoir.Node.Index) !T {
728 const repr = node.get(self.zoir);
729 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (repr) {
730 .struct_literal => |nodes| nodes,
731 .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } },
732 else => return error.WrongType,
733 };
734
735 const field_infos = @typeInfo(T).@"struct".fields;
736
737 // Build a map from field name to index.
738 // The special value `comptime_field` indicates that this is actually a comptime field.
739 const comptime_field = std.math.maxInt(usize);
740 const field_indices: std.StaticStringMap(usize) = comptime b: {
741 var kvs_list: [field_infos.len]struct { []const u8, usize } = undefined;
742 for (&kvs_list, field_infos, 0..) |*kv, field, i| {
743 kv.* = .{ field.name, if (field.is_comptime) comptime_field else i };
744 }
745 break :b .initComptime(kvs_list);
746 };
747
748 // Parse the struct
749 var result: T = undefined;
750 var field_found: [field_infos.len]bool = @splat(false);
751
752 // If we fail partway through, free all already initialized fields
753 var initialized: usize = 0;
754 errdefer if (self.options.free_on_error and field_infos.len > 0) {
755 for (fields.names[0..initialized]) |name_runtime| {
756 switch (field_indices.get(name_runtime.get(self.zoir)) orelse continue) {
757 inline 0...(field_infos.len - 1) => |name_index| {
758 const name = field_infos[name_index].name;
759 free(self.gpa, @field(result, name));
760 },
761 else => unreachable, // Can't be out of bounds
762 }
763 }
764 };
765
766 // Fill in the fields we found
767 for (0..fields.names.len) |i| {
768 const name = fields.names[i].get(self.zoir);
769 const field_index = field_indices.get(name) orelse {
770 if (self.options.ignore_unknown_fields) continue;
771 return self.failUnexpected(T, "field", node, i, name);
772 };
773 if (field_index == comptime_field) {
774 return self.failComptimeField(node, i);
775 }
776
777 // Mark the field as found. Assert that the found array is not zero length to satisfy
778 // the type checker (it can't be since we made it into an iteration of this loop.)
779 if (field_found.len == 0) unreachable;
780 field_found[field_index] = true;
781
782 switch (field_index) {
783 inline 0...(field_infos.len - 1) => |j| {
784 if (field_infos[j].is_comptime) unreachable;
785
786 @field(result, field_infos[j].name) = try self.parseExpr(
787 field_infos[j].type,
788 fields.vals.at(@intCast(i)),
789 );
790 },
791 else => unreachable, // Can't be out of bounds
792 }
793
794 initialized += 1;
795 }
796
797 // Fill in any missing default fields
798 inline for (field_found, 0..) |found, i| {
799 if (!found) {
800 const field_info = field_infos[i];
801 if (field_info.default_value_ptr) |default| {
802 const typed: *const field_info.type = @ptrCast(@alignCast(default));
803 @field(result, field_info.name) = typed.*;
804 } else {
805 return self.failNodeFmt(
806 node,
807 "missing required field {s}",
808 .{field_infos[i].name},
809 );
810 }
811 }
812 }
813
814 return result;
815 }
816
817 fn parseTuple(self: *@This(), T: type, node: Zoir.Node.Index) !T {
818 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
819 .array_literal => |nodes| nodes,
820 .empty_literal => .{ .start = node, .len = 0 },
821 else => return error.WrongType,
822 };
823
824 var result: T = undefined;
825 const field_infos = @typeInfo(T).@"struct".fields;
826
827 if (nodes.len > field_infos.len) {
828 return self.failNodeFmt(
829 nodes.at(field_infos.len),
830 "index {} outside of tuple length {}",
831 .{ field_infos.len, field_infos.len },
832 );
833 }
834
835 inline for (0..field_infos.len) |i| {
836 // Check if we're out of bounds
837 if (i >= nodes.len) {
838 if (field_infos[i].default_value_ptr) |default| {
839 const typed: *const field_infos[i].type = @ptrCast(@alignCast(default));
840 @field(result, field_infos[i].name) = typed.*;
841 } else {
842 return self.failNodeFmt(node, "missing tuple field with index {}", .{i});
843 }
844 } else {
845 // If we fail to parse this field, free all fields before it
846 errdefer if (self.options.free_on_error) {
847 inline for (0..i) |j| {
848 if (j >= i) break;
849 free(self.gpa, result[j]);
850 }
851 };
852
853 if (field_infos[i].is_comptime) {
854 return self.failComptimeField(node, i);
855 } else {
856 result[i] = try self.parseExpr(field_infos[i].type, nodes.at(i));
857 }
858 }
859 }
860
861 return result;
862 }
863
864 fn parseUnion(self: *@This(), T: type, node: Zoir.Node.Index) !T {
865 const @"union" = @typeInfo(T).@"union";
866 const field_infos = @"union".fields;
867
868 if (field_infos.len == 0) comptime unreachable;
869
870 // Gather info on the fields
871 const field_indices = b: {
872 comptime var kvs_list: [field_infos.len]struct { []const u8, usize } = undefined;
873 inline for (field_infos, 0..) |field, i| {
874 kvs_list[i] = .{ field.name, i };
875 }
876 break :b std.StaticStringMap(usize).initComptime(kvs_list);
877 };
878
879 // Parse the union
880 switch (node.get(self.zoir)) {
881 .enum_literal => |field_name| {
882 // The union must be tagged for an enum literal to coerce to it
883 if (@"union".tag_type == null) {
884 return error.WrongType;
885 }
886
887 // Get the index of the named field. We don't use `parseEnum` here as
888 // the order of the enum and the order of the union might not match!
889 const field_index = b: {
890 const field_name_str = field_name.get(self.zoir);
891 break :b field_indices.get(field_name_str) orelse
892 return self.failUnexpected(T, "field", node, null, field_name_str);
893 };
894
895 // Initialize the union from the given field.
896 switch (field_index) {
897 inline 0...field_infos.len - 1 => |i| {
898 // Fail if the field is not void
899 if (field_infos[i].type != void)
900 return self.failNode(node, "expected union");
901
902 // Instantiate the union
903 return @unionInit(T, field_infos[i].name, {});
904 },
905 else => unreachable, // Can't be out of bounds
906 }
907 },
908 .struct_literal => |struct_fields| {
909 if (struct_fields.names.len != 1) {
910 return error.WrongType;
911 }
912
913 // Fill in the field we found
914 const field_name = struct_fields.names[0];
915 const field_name_str = field_name.get(self.zoir);
916 const field_val = struct_fields.vals.at(0);
917 const field_index = field_indices.get(field_name_str) orelse
918 return self.failUnexpected(T, "field", node, 0, field_name_str);
919
920 switch (field_index) {
921 inline 0...field_infos.len - 1 => |i| {
922 if (field_infos[i].type == void) {
923 return self.failNode(field_val, "expected type 'void'");
924 } else {
925 const value = try self.parseExpr(field_infos[i].type, field_val);
926 return @unionInit(T, field_infos[i].name, value);
927 }
928 },
929 else => unreachable, // Can't be out of bounds
930 }
931 },
932 else => return error.WrongType,
933 }
934 }
935
936 fn parseVector(
937 self: *@This(),
938 T: type,
939 node: Zoir.Node.Index,
940 ) !T {
941 const vector_info = @typeInfo(T).vector;
942
943 const nodes: Zoir.Node.Index.Range = switch (node.get(self.zoir)) {
944 .array_literal => |nodes| nodes,
945 .empty_literal => .{ .start = node, .len = 0 },
946 else => return error.WrongType,
947 };
948
949 var result: T = undefined;
950
951 if (nodes.len != vector_info.len) {
952 return self.failNodeFmt(
953 node,
954 "expected {} vector elements; found {}",
955 .{ vector_info.len, nodes.len },
956 );
957 }
958
959 for (0..vector_info.len) |i| {
960 errdefer for (0..i) |j| free(self.gpa, result[j]);
961 result[i] = try self.parseExpr(vector_info.child, nodes.at(@intCast(i)));
962 }
963
964 return result;
965 }
966
967 fn failTokenFmt(
968 self: @This(),
969 token: Ast.TokenIndex,
970 offset: u32,
971 comptime fmt: []const u8,
972 args: anytype,
973 ) error{ OutOfMemory, ParseZon } {
974 @branchHint(.cold);
975 return self.failTokenFmtNote(token, offset, fmt, args, null);
976 }
977
978 fn failTokenFmtNote(
979 self: @This(),
980 token: Ast.TokenIndex,
981 offset: u32,
982 comptime fmt: []const u8,
983 args: anytype,
984 note: ?Error.TypeCheckFailure.Note,
985 ) error{ OutOfMemory, ParseZon } {
986 @branchHint(.cold);
987 comptime assert(args.len > 0);
988 if (self.status) |s| s.type_check = .{
989 .token = token,
990 .offset = offset,
991 .message = std.fmt.allocPrint(self.gpa, fmt, args) catch |err| {
992 if (note) |n| n.deinit(self.gpa);
993 return err;
994 },
995 .owned = true,
996 .note = note,
997 };
998 return error.ParseZon;
999 }
1000
1001 fn failNodeFmt(
1002 self: @This(),
1003 node: Zoir.Node.Index,
1004 comptime fmt: []const u8,
1005 args: anytype,
1006 ) error{ OutOfMemory, ParseZon } {
1007 @branchHint(.cold);
1008 const main_tokens = self.ast.nodes.items(.main_token);
1009 const token = main_tokens[node.getAstNode(self.zoir)];
1010 return self.failTokenFmt(token, 0, fmt, args);
1011 }
1012
1013 fn failToken(
1014 self: @This(),
1015 failure: Error.TypeCheckFailure,
1016 ) error{ParseZon} {
1017 @branchHint(.cold);
1018 if (self.status) |s| s.type_check = failure;
1019 return error.ParseZon;
1020 }
1021
1022 fn failNode(
1023 self: @This(),
1024 node: Zoir.Node.Index,
1025 message: []const u8,
1026 ) error{ParseZon} {
1027 @branchHint(.cold);
1028 const main_tokens = self.ast.nodes.items(.main_token);
1029 const token = main_tokens[node.getAstNode(self.zoir)];
1030 return self.failToken(.{
1031 .token = token,
1032 .offset = 0,
1033 .message = message,
1034 .owned = false,
1035 .note = null,
1036 });
1037 }
1038
1039 fn failCannotRepresent(
1040 self: @This(),
1041 T: type,
1042 node: Zoir.Node.Index,
1043 ) error{ OutOfMemory, ParseZon } {
1044 @branchHint(.cold);
1045 return self.failNodeFmt(node, "type '{s}' cannot represent value", .{@typeName(T)});
1046 }
1047
1048 fn failUnexpected(
1049 self: @This(),
1050 T: type,
1051 item_kind: []const u8,
1052 node: Zoir.Node.Index,
1053 field: ?usize,
1054 name: []const u8,
1055 ) error{ OutOfMemory, ParseZon } {
1056 @branchHint(.cold);
1057 const token = if (field) |f| b: {
1058 var buf: [2]Ast.Node.Index = undefined;
1059 const struct_init = self.ast.fullStructInit(&buf, node.getAstNode(self.zoir)).?;
1060 const field_node = struct_init.ast.fields[f];
1061 break :b self.ast.firstToken(field_node) - 2;
1062 } else b: {
1063 const main_tokens = self.ast.nodes.items(.main_token);
1064 break :b main_tokens[node.getAstNode(self.zoir)];
1065 };
1066 switch (@typeInfo(T)) {
1067 inline .@"struct", .@"union", .@"enum" => |info| {
1068 const note: Error.TypeCheckFailure.Note = if (info.fields.len == 0) b: {
1069 break :b .{
1070 .token = token,
1071 .offset = 0,
1072 .msg = "none expected",
1073 .owned = false,
1074 };
1075 } else b: {
1076 const msg = "supported: ";
1077 var buf: std.ArrayListUnmanaged(u8) = try .initCapacity(self.gpa, 64);
1078 defer buf.deinit(self.gpa);
1079 const writer = buf.writer(self.gpa);
1080 try writer.writeAll(msg);
1081 inline for (info.fields, 0..) |field_info, i| {
1082 if (i != 0) try writer.writeAll(", ");
1083 try writer.print("'{p_}'", .{std.zig.fmtId(field_info.name)});
1084 }
1085 break :b .{
1086 .token = token,
1087 .offset = 0,
1088 .msg = try buf.toOwnedSlice(self.gpa),
1089 .owned = true,
1090 };
1091 };
1092 return self.failTokenFmtNote(
1093 token,
1094 0,
1095 "unexpected {s} '{s}'",
1096 .{ item_kind, name },
1097 note,
1098 );
1099 },
1100 else => comptime unreachable,
1101 }
1102 }
1103
1104 // Technically we could do this if we were willing to do a deep equal to verify
1105 // the value matched, but doing so doesn't seem to support any real use cases
1106 // so isn't worth the complexity at the moment.
1107 fn failComptimeField(
1108 self: @This(),
1109 node: Zoir.Node.Index,
1110 field: usize,
1111 ) error{ OutOfMemory, ParseZon } {
1112 @branchHint(.cold);
1113 const ast_node = node.getAstNode(self.zoir);
1114 var buf: [2]Ast.Node.Index = undefined;
1115 const token = if (self.ast.fullStructInit(&buf, ast_node)) |struct_init| b: {
1116 const field_node = struct_init.ast.fields[field];
1117 break :b self.ast.firstToken(field_node);
1118 } else b: {
1119 const array_init = self.ast.fullArrayInit(&buf, ast_node).?;
1120 const value_node = array_init.ast.elements[field];
1121 break :b self.ast.firstToken(value_node);
1122 };
1123 return self.failToken(.{
1124 .token = token,
1125 .offset = 0,
1126 .message = "cannot initialize comptime field",
1127 .owned = false,
1128 .note = null,
1129 });
1130 }
1131};
1132
1133fn intFromFloatExact(T: type, value: anytype) ?T {
1134 if (value > std.math.maxInt(T) or value < std.math.minInt(T)) {
1135 return null;
1136 }
1137
1138 if (std.math.isNan(value) or std.math.trunc(value) != value) {
1139 return null;
1140 }
1141
1142 return @intFromFloat(value);
1143}
1144
1145fn canParseType(T: type) bool {
1146 comptime return canParseTypeInner(T, &.{}, false);
1147}
1148
1149fn canParseTypeInner(
1150 T: type,
1151 /// Visited structs and unions, to avoid infinite recursion.
1152 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
1153 visited: []const type,
1154 parent_is_optional: bool,
1155) bool {
1156 return switch (@typeInfo(T)) {
1157 .bool,
1158 .int,
1159 .float,
1160 .null,
1161 .@"enum",
1162 => true,
1163
1164 .noreturn,
1165 .void,
1166 .type,
1167 .undefined,
1168 .error_union,
1169 .error_set,
1170 .@"fn",
1171 .frame,
1172 .@"anyframe",
1173 .@"opaque",
1174 .comptime_int,
1175 .comptime_float,
1176 .enum_literal,
1177 => false,
1178
1179 .pointer => |pointer| switch (pointer.size) {
1180 .one => canParseTypeInner(pointer.child, visited, parent_is_optional),
1181 .slice => canParseTypeInner(pointer.child, visited, false),
1182 .many, .c => false,
1183 },
1184
1185 .optional => |optional| if (parent_is_optional)
1186 false
1187 else
1188 canParseTypeInner(optional.child, visited, true),
1189
1190 .array => |array| canParseTypeInner(array.child, visited, false),
1191 .vector => |vector| canParseTypeInner(vector.child, visited, false),
1192
1193 .@"struct" => |@"struct"| {
1194 for (visited) |V| if (T == V) return true;
1195 const new_visited = visited ++ .{T};
1196 for (@"struct".fields) |field| {
1197 if (!field.is_comptime and !canParseTypeInner(field.type, new_visited, false)) {
1198 return false;
1199 }
1200 }
1201 return true;
1202 },
1203 .@"union" => |@"union"| {
1204 for (visited) |V| if (T == V) return true;
1205 const new_visited = visited ++ .{T};
1206 for (@"union".fields) |field| {
1207 if (field.type != void and !canParseTypeInner(field.type, new_visited, false)) {
1208 return false;
1209 }
1210 }
1211 return true;
1212 },
1213 };
1214}
1215
1216test "std.zon parse canParseType" {
1217 try std.testing.expect(!comptime canParseType(void));
1218 try std.testing.expect(!comptime canParseType(struct { f: [*]u8 }));
1219 try std.testing.expect(!comptime canParseType(struct { error{foo} }));
1220 try std.testing.expect(!comptime canParseType(union(enum) { a: void, b: [*c]u8 }));
1221 try std.testing.expect(!comptime canParseType(@Vector(0, [*c]u8)));
1222 try std.testing.expect(!comptime canParseType(*?[*c]u8));
1223 try std.testing.expect(comptime canParseType(enum(u8) { _ }));
1224 try std.testing.expect(comptime canParseType(union { foo: void }));
1225 try std.testing.expect(comptime canParseType(union(enum) { foo: void }));
1226 try std.testing.expect(!comptime canParseType(comptime_float));
1227 try std.testing.expect(!comptime canParseType(comptime_int));
1228 try std.testing.expect(comptime canParseType(struct { comptime foo: ??u8 = null }));
1229 try std.testing.expect(!comptime canParseType(@TypeOf(.foo)));
1230 try std.testing.expect(comptime canParseType(?u8));
1231 try std.testing.expect(comptime canParseType(*?*u8));
1232 try std.testing.expect(comptime canParseType(?struct {
1233 foo: ?struct {
1234 ?union(enum) {
1235 a: ?@Vector(0, ?*u8),
1236 },
1237 ?struct {
1238 f: ?[]?u8,
1239 },
1240 },
1241 }));
1242 try std.testing.expect(!comptime canParseType(??u8));
1243 try std.testing.expect(!comptime canParseType(?*?u8));
1244 try std.testing.expect(!comptime canParseType(*?*?*u8));
1245 try std.testing.expect(!comptime canParseType(struct { x: comptime_int = 2 }));
1246 try std.testing.expect(!comptime canParseType(struct { x: comptime_float = 2 }));
1247 try std.testing.expect(comptime canParseType(struct { comptime x: @TypeOf(.foo) = .foo }));
1248 try std.testing.expect(!comptime canParseType(struct { comptime_int }));
1249 const Recursive = struct { foo: ?*@This() };
1250 try std.testing.expect(comptime canParseType(Recursive));
1251
1252 // Make sure we validate nested optional before we early out due to already having seen
1253 // a type recursion!
1254 try std.testing.expect(!comptime canParseType(struct {
1255 add_to_visited: ?u8,
1256 retrieve_from_visited: ??u8,
1257 }));
1258}
1259
1260test "std.zon requiresAllocator" {
1261 try std.testing.expect(!requiresAllocator(u8));
1262 try std.testing.expect(!requiresAllocator(f32));
1263 try std.testing.expect(!requiresAllocator(enum { foo }));
1264 try std.testing.expect(!requiresAllocator(struct { f32 }));
1265 try std.testing.expect(!requiresAllocator(struct { x: f32 }));
1266 try std.testing.expect(!requiresAllocator([0][]const u8));
1267 try std.testing.expect(!requiresAllocator([2]u8));
1268 try std.testing.expect(!requiresAllocator(union { x: f32, y: f32 }));
1269 try std.testing.expect(!requiresAllocator(union(enum) { x: f32, y: f32 }));
1270 try std.testing.expect(!requiresAllocator(?f32));
1271 try std.testing.expect(!requiresAllocator(void));
1272 try std.testing.expect(!requiresAllocator(@TypeOf(null)));
1273 try std.testing.expect(!requiresAllocator(@Vector(3, u8)));
1274 try std.testing.expect(!requiresAllocator(@Vector(0, *const u8)));
1275
1276 try std.testing.expect(requiresAllocator([]u8));
1277 try std.testing.expect(requiresAllocator(*struct { u8, u8 }));
1278 try std.testing.expect(requiresAllocator([1][]const u8));
1279 try std.testing.expect(requiresAllocator(struct { x: i32, y: []u8 }));
1280 try std.testing.expect(requiresAllocator(union { x: i32, y: []u8 }));
1281 try std.testing.expect(requiresAllocator(union(enum) { x: i32, y: []u8 }));
1282 try std.testing.expect(requiresAllocator(?[]u8));
1283 try std.testing.expect(requiresAllocator(@Vector(3, *const u8)));
1284}
1285
1286test "std.zon ast errors" {
1287 const gpa = std.testing.allocator;
1288 var status: Status = .{};
1289 defer status.deinit(gpa);
1290 try std.testing.expectError(
1291 error.ParseZon,
1292 fromSlice(struct {}, gpa, ".{.x = 1 .y = 2}", &status, .{}),
1293 );
1294 try std.testing.expectFmt("1:13: error: expected ',' after initializer\n", "{}", .{status});
1295}
1296
1297test "std.zon comments" {
1298 const gpa = std.testing.allocator;
1299
1300 try std.testing.expectEqual(@as(u8, 10), fromSlice(u8, gpa,
1301 \\// comment
1302 \\10 // comment
1303 \\// comment
1304 , null, .{}));
1305
1306 {
1307 var status: Status = .{};
1308 defer status.deinit(gpa);
1309 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa,
1310 \\//! comment
1311 \\10 // comment
1312 \\// comment
1313 , &status, .{}));
1314 try std.testing.expectFmt(
1315 "1:1: error: expected expression, found 'a document comment'\n",
1316 "{}",
1317 .{status},
1318 );
1319 }
1320}
1321
1322test "std.zon failure/oom formatting" {
1323 const gpa = std.testing.allocator;
1324 var failing_allocator = std.testing.FailingAllocator.init(gpa, .{
1325 .fail_index = 0,
1326 .resize_fail_index = 0,
1327 });
1328 var status: Status = .{};
1329 defer status.deinit(gpa);
1330 try std.testing.expectError(error.OutOfMemory, fromSlice(
1331 []const u8,
1332 failing_allocator.allocator(),
1333 "\"foo\"",
1334 &status,
1335 .{},
1336 ));
1337 try std.testing.expectFmt("", "{}", .{status});
1338}
1339
1340test "std.zon fromSlice syntax error" {
1341 try std.testing.expectError(
1342 error.ParseZon,
1343 fromSlice(u8, std.testing.allocator, ".{", null, .{}),
1344 );
1345}
1346
1347test "std.zon optional" {
1348 const gpa = std.testing.allocator;
1349
1350 // Basic usage
1351 {
1352 const none = try fromSlice(?u32, gpa, "null", null, .{});
1353 try std.testing.expect(none == null);
1354 const some = try fromSlice(?u32, gpa, "1", null, .{});
1355 try std.testing.expect(some.? == 1);
1356 }
1357
1358 // Deep free
1359 {
1360 const none = try fromSlice(?[]const u8, gpa, "null", null, .{});
1361 try std.testing.expect(none == null);
1362 const some = try fromSlice(?[]const u8, gpa, "\"foo\"", null, .{});
1363 defer free(gpa, some);
1364 try std.testing.expectEqualStrings("foo", some.?);
1365 }
1366}
1367
1368test "std.zon unions" {
1369 const gpa = std.testing.allocator;
1370
1371 // Unions
1372 {
1373 const Tagged = union(enum) { x: f32, @"y y": bool, z, @"z z" };
1374 const Untagged = union { x: f32, @"y y": bool, z: void, @"z z": void };
1375
1376 const tagged_x = try fromSlice(Tagged, gpa, ".{.x = 1.5}", null, .{});
1377 try std.testing.expectEqual(Tagged{ .x = 1.5 }, tagged_x);
1378 const tagged_y = try fromSlice(Tagged, gpa, ".{.@\"y y\" = true}", null, .{});
1379 try std.testing.expectEqual(Tagged{ .@"y y" = true }, tagged_y);
1380 const tagged_z_shorthand = try fromSlice(Tagged, gpa, ".z", null, .{});
1381 try std.testing.expectEqual(@as(Tagged, .z), tagged_z_shorthand);
1382 const tagged_zz_shorthand = try fromSlice(Tagged, gpa, ".@\"z z\"", null, .{});
1383 try std.testing.expectEqual(@as(Tagged, .@"z z"), tagged_zz_shorthand);
1384
1385 const untagged_x = try fromSlice(Untagged, gpa, ".{.x = 1.5}", null, .{});
1386 try std.testing.expect(untagged_x.x == 1.5);
1387 const untagged_y = try fromSlice(Untagged, gpa, ".{.@\"y y\" = true}", null, .{});
1388 try std.testing.expect(untagged_y.@"y y");
1389 }
1390
1391 // Deep free
1392 {
1393 const Union = union(enum) { bar: []const u8, baz: bool };
1394
1395 const noalloc = try fromSlice(Union, gpa, ".{.baz = false}", null, .{});
1396 try std.testing.expectEqual(Union{ .baz = false }, noalloc);
1397
1398 const alloc = try fromSlice(Union, gpa, ".{.bar = \"qux\"}", null, .{});
1399 defer free(gpa, alloc);
1400 try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc);
1401 }
1402
1403 // Unknown field
1404 {
1405 const Union = union { x: f32, y: f32 };
1406 var status: Status = .{};
1407 defer status.deinit(gpa);
1408 try std.testing.expectError(
1409 error.ParseZon,
1410 fromSlice(Union, gpa, ".{.z=2.5}", &status, .{}),
1411 );
1412 try std.testing.expectFmt(
1413 \\1:4: error: unexpected field 'z'
1414 \\1:4: note: supported: 'x', 'y'
1415 \\
1416 ,
1417 "{}",
1418 .{status},
1419 );
1420 }
1421
1422 // Explicit void field
1423 {
1424 const Union = union(enum) { x: void };
1425 var status: Status = .{};
1426 defer status.deinit(gpa);
1427 try std.testing.expectError(
1428 error.ParseZon,
1429 fromSlice(Union, gpa, ".{.x=1}", &status, .{}),
1430 );
1431 try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{}", .{status});
1432 }
1433
1434 // Extra field
1435 {
1436 const Union = union { x: f32, y: bool };
1437 var status: Status = .{};
1438 defer status.deinit(gpa);
1439 try std.testing.expectError(
1440 error.ParseZon,
1441 fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &status, .{}),
1442 );
1443 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status});
1444 }
1445
1446 // No fields
1447 {
1448 const Union = union { x: f32, y: bool };
1449 var status: Status = .{};
1450 defer status.deinit(gpa);
1451 try std.testing.expectError(
1452 error.ParseZon,
1453 fromSlice(Union, gpa, ".{}", &status, .{}),
1454 );
1455 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status});
1456 }
1457
1458 // Enum literals cannot coerce into untagged unions
1459 {
1460 const Union = union { x: void };
1461 var status: Status = .{};
1462 defer status.deinit(gpa);
1463 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &status, .{}));
1464 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status});
1465 }
1466
1467 // Unknown field for enum literal coercion
1468 {
1469 const Union = union(enum) { x: void };
1470 var status: Status = .{};
1471 defer status.deinit(gpa);
1472 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".y", &status, .{}));
1473 try std.testing.expectFmt(
1474 \\1:2: error: unexpected field 'y'
1475 \\1:2: note: supported: 'x'
1476 \\
1477 ,
1478 "{}",
1479 .{status},
1480 );
1481 }
1482
1483 // Non void field for enum literal coercion
1484 {
1485 const Union = union(enum) { x: f32 };
1486 var status: Status = .{};
1487 defer status.deinit(gpa);
1488 try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &status, .{}));
1489 try std.testing.expectFmt("1:2: error: expected union\n", "{}", .{status});
1490 }
1491}
1492
1493test "std.zon structs" {
1494 const gpa = std.testing.allocator;
1495
1496 // Structs (various sizes tested since they're parsed differently)
1497 {
1498 const Vec0 = struct {};
1499 const Vec1 = struct { x: f32 };
1500 const Vec2 = struct { x: f32, y: f32 };
1501 const Vec3 = struct { x: f32, y: f32, z: f32 };
1502
1503 const zero = try fromSlice(Vec0, gpa, ".{}", null, .{});
1504 try std.testing.expectEqual(Vec0{}, zero);
1505
1506 const one = try fromSlice(Vec1, gpa, ".{.x = 1.2}", null, .{});
1507 try std.testing.expectEqual(Vec1{ .x = 1.2 }, one);
1508
1509 const two = try fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 3.4}", null, .{});
1510 try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 3.4 }, two);
1511
1512 const three = try fromSlice(Vec3, gpa, ".{.x = 1.2, .y = 3.4, .z = 5.6}", null, .{});
1513 try std.testing.expectEqual(Vec3{ .x = 1.2, .y = 3.4, .z = 5.6 }, three);
1514 }
1515
1516 // Deep free (structs and arrays)
1517 {
1518 const Foo = struct { bar: []const u8, baz: []const []const u8 };
1519
1520 const parsed = try fromSlice(
1521 Foo,
1522 gpa,
1523 ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}",
1524 null,
1525 .{},
1526 );
1527 defer free(gpa, parsed);
1528 try std.testing.expectEqualDeep(Foo{ .bar = "qux", .baz = &.{ "a", "b" } }, parsed);
1529 }
1530
1531 // Unknown field
1532 {
1533 const Vec2 = struct { x: f32, y: f32 };
1534 var status: Status = .{};
1535 defer status.deinit(gpa);
1536 try std.testing.expectError(
1537 error.ParseZon,
1538 fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &status, .{}),
1539 );
1540 try std.testing.expectFmt(
1541 \\1:12: error: unexpected field 'z'
1542 \\1:12: note: supported: 'x', 'y'
1543 \\
1544 ,
1545 "{}",
1546 .{status},
1547 );
1548 }
1549
1550 // Duplicate field
1551 {
1552 const Vec2 = struct { x: f32, y: f32 };
1553 var status: Status = .{};
1554 defer status.deinit(gpa);
1555 try std.testing.expectError(
1556 error.ParseZon,
1557 fromSlice(Vec2, gpa, ".{.x=1.5, .x=2.5, .x=3.5}", &status, .{}),
1558 );
1559 try std.testing.expectFmt(
1560 \\1:4: error: duplicate struct field name
1561 \\1:12: note: duplicate name here
1562 \\
1563 , "{}", .{status});
1564 }
1565
1566 // Ignore unknown fields
1567 {
1568 const Vec2 = struct { x: f32, y: f32 = 2.0 };
1569 const parsed = try fromSlice(Vec2, gpa, ".{ .x = 1.0, .z = 3.0 }", null, .{
1570 .ignore_unknown_fields = true,
1571 });
1572 try std.testing.expectEqual(Vec2{ .x = 1.0, .y = 2.0 }, parsed);
1573 }
1574
1575 // Unknown field when struct has no fields (regression test)
1576 {
1577 const Vec2 = struct {};
1578 var status: Status = .{};
1579 defer status.deinit(gpa);
1580 try std.testing.expectError(
1581 error.ParseZon,
1582 fromSlice(Vec2, gpa, ".{.x=1.5, .z=2.5}", &status, .{}),
1583 );
1584 try std.testing.expectFmt(
1585 \\1:4: error: unexpected field 'x'
1586 \\1:4: note: none expected
1587 \\
1588 , "{}", .{status});
1589 }
1590
1591 // Missing field
1592 {
1593 const Vec2 = struct { x: f32, y: f32 };
1594 var status: Status = .{};
1595 defer status.deinit(gpa);
1596 try std.testing.expectError(
1597 error.ParseZon,
1598 fromSlice(Vec2, gpa, ".{.x=1.5}", &status, .{}),
1599 );
1600 try std.testing.expectFmt("1:2: error: missing required field y\n", "{}", .{status});
1601 }
1602
1603 // Default field
1604 {
1605 const Vec2 = struct { x: f32, y: f32 = 1.5 };
1606 const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{});
1607 try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed);
1608 }
1609
1610 // Comptime field
1611 {
1612 const Vec2 = struct { x: f32, comptime y: f32 = 1.5 };
1613 const parsed = try fromSlice(Vec2, gpa, ".{.x = 1.2}", null, .{});
1614 try std.testing.expectEqual(Vec2{ .x = 1.2, .y = 1.5 }, parsed);
1615 }
1616
1617 // Comptime field assignment
1618 {
1619 const Vec2 = struct { x: f32, comptime y: f32 = 1.5 };
1620 var status: Status = .{};
1621 defer status.deinit(gpa);
1622 const parsed = fromSlice(Vec2, gpa, ".{.x = 1.2, .y = 1.5}", &status, .{});
1623 try std.testing.expectError(error.ParseZon, parsed);
1624 try std.testing.expectFmt(
1625 \\1:18: error: cannot initialize comptime field
1626 \\
1627 , "{}", .{status});
1628 }
1629
1630 // Enum field (regression test, we were previously getting the field name in an
1631 // incorrect way that broke for enum values)
1632 {
1633 const Vec0 = struct { x: enum { x } };
1634 const parsed = try fromSlice(Vec0, gpa, ".{ .x = .x }", null, .{});
1635 try std.testing.expectEqual(Vec0{ .x = .x }, parsed);
1636 }
1637
1638 // Enum field and struct field with @
1639 {
1640 const Vec0 = struct { @"x x": enum { @"x x" } };
1641 const parsed = try fromSlice(Vec0, gpa, ".{ .@\"x x\" = .@\"x x\" }", null, .{});
1642 try std.testing.expectEqual(Vec0{ .@"x x" = .@"x x" }, parsed);
1643 }
1644
1645 // Type expressions are not allowed
1646 {
1647 // Structs
1648 {
1649 var status: Status = .{};
1650 defer status.deinit(gpa);
1651 const parsed = fromSlice(struct {}, gpa, "Empty{}", &status, .{});
1652 try std.testing.expectError(error.ParseZon, parsed);
1653 try std.testing.expectFmt(
1654 \\1:1: error: types are not available in ZON
1655 \\1:1: note: replace the type with '.'
1656 \\
1657 , "{}", .{status});
1658 }
1659
1660 // Arrays
1661 {
1662 var status: Status = .{};
1663 defer status.deinit(gpa);
1664 const parsed = fromSlice([3]u8, gpa, "[3]u8{1, 2, 3}", &status, .{});
1665 try std.testing.expectError(error.ParseZon, parsed);
1666 try std.testing.expectFmt(
1667 \\1:1: error: types are not available in ZON
1668 \\1:1: note: replace the type with '.'
1669 \\
1670 , "{}", .{status});
1671 }
1672
1673 // Slices
1674 {
1675 var status: Status = .{};
1676 defer status.deinit(gpa);
1677 const parsed = fromSlice([]u8, gpa, "[]u8{1, 2, 3}", &status, .{});
1678 try std.testing.expectError(error.ParseZon, parsed);
1679 try std.testing.expectFmt(
1680 \\1:1: error: types are not available in ZON
1681 \\1:1: note: replace the type with '.'
1682 \\
1683 , "{}", .{status});
1684 }
1685
1686 // Tuples
1687 {
1688 var status: Status = .{};
1689 defer status.deinit(gpa);
1690 const parsed = fromSlice(
1691 struct { u8, u8, u8 },
1692 gpa,
1693 "Tuple{1, 2, 3}",
1694 &status,
1695 .{},
1696 );
1697 try std.testing.expectError(error.ParseZon, parsed);
1698 try std.testing.expectFmt(
1699 \\1:1: error: types are not available in ZON
1700 \\1:1: note: replace the type with '.'
1701 \\
1702 , "{}", .{status});
1703 }
1704
1705 // Nested
1706 {
1707 var status: Status = .{};
1708 defer status.deinit(gpa);
1709 const parsed = fromSlice(struct {}, gpa, ".{ .x = Tuple{1, 2, 3} }", &status, .{});
1710 try std.testing.expectError(error.ParseZon, parsed);
1711 try std.testing.expectFmt(
1712 \\1:9: error: types are not available in ZON
1713 \\1:9: note: replace the type with '.'
1714 \\
1715 , "{}", .{status});
1716 }
1717 }
1718}
1719
1720test "std.zon tuples" {
1721 const gpa = std.testing.allocator;
1722
1723 // Structs (various sizes tested since they're parsed differently)
1724 {
1725 const Tuple0 = struct {};
1726 const Tuple1 = struct { f32 };
1727 const Tuple2 = struct { f32, bool };
1728 const Tuple3 = struct { f32, bool, u8 };
1729
1730 const zero = try fromSlice(Tuple0, gpa, ".{}", null, .{});
1731 try std.testing.expectEqual(Tuple0{}, zero);
1732
1733 const one = try fromSlice(Tuple1, gpa, ".{1.2}", null, .{});
1734 try std.testing.expectEqual(Tuple1{1.2}, one);
1735
1736 const two = try fromSlice(Tuple2, gpa, ".{1.2, true}", null, .{});
1737 try std.testing.expectEqual(Tuple2{ 1.2, true }, two);
1738
1739 const three = try fromSlice(Tuple3, gpa, ".{1.2, false, 3}", null, .{});
1740 try std.testing.expectEqual(Tuple3{ 1.2, false, 3 }, three);
1741 }
1742
1743 // Deep free
1744 {
1745 const Tuple = struct { []const u8, []const u8 };
1746 const parsed = try fromSlice(Tuple, gpa, ".{\"hello\", \"world\"}", null, .{});
1747 defer free(gpa, parsed);
1748 try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed);
1749 }
1750
1751 // Extra field
1752 {
1753 const Tuple = struct { f32, bool };
1754 var status: Status = .{};
1755 defer status.deinit(gpa);
1756 try std.testing.expectError(
1757 error.ParseZon,
1758 fromSlice(Tuple, gpa, ".{0.5, true, 123}", &status, .{}),
1759 );
1760 try std.testing.expectFmt("1:14: error: index 2 outside of tuple length 2\n", "{}", .{status});
1761 }
1762
1763 // Extra field
1764 {
1765 const Tuple = struct { f32, bool };
1766 var status: Status = .{};
1767 defer status.deinit(gpa);
1768 try std.testing.expectError(
1769 error.ParseZon,
1770 fromSlice(Tuple, gpa, ".{0.5}", &status, .{}),
1771 );
1772 try std.testing.expectFmt(
1773 "1:2: error: missing tuple field with index 1\n",
1774 "{}",
1775 .{status},
1776 );
1777 }
1778
1779 // Tuple with unexpected field names
1780 {
1781 const Tuple = struct { f32 };
1782 var status: Status = .{};
1783 defer status.deinit(gpa);
1784 try std.testing.expectError(
1785 error.ParseZon,
1786 fromSlice(Tuple, gpa, ".{.foo = 10.0}", &status, .{}),
1787 );
1788 try std.testing.expectFmt("1:2: error: expected tuple\n", "{}", .{status});
1789 }
1790
1791 // Struct with missing field names
1792 {
1793 const Struct = struct { foo: f32 };
1794 var status: Status = .{};
1795 defer status.deinit(gpa);
1796 try std.testing.expectError(
1797 error.ParseZon,
1798 fromSlice(Struct, gpa, ".{10.0}", &status, .{}),
1799 );
1800 try std.testing.expectFmt("1:2: error: expected struct\n", "{}", .{status});
1801 }
1802
1803 // Comptime field
1804 {
1805 const Vec2 = struct { f32, comptime f32 = 1.5 };
1806 const parsed = try fromSlice(Vec2, gpa, ".{ 1.2 }", null, .{});
1807 try std.testing.expectEqual(Vec2{ 1.2, 1.5 }, parsed);
1808 }
1809
1810 // Comptime field assignment
1811 {
1812 const Vec2 = struct { f32, comptime f32 = 1.5 };
1813 var status: Status = .{};
1814 defer status.deinit(gpa);
1815 const parsed = fromSlice(Vec2, gpa, ".{ 1.2, 1.5}", &status, .{});
1816 try std.testing.expectError(error.ParseZon, parsed);
1817 try std.testing.expectFmt(
1818 \\1:9: error: cannot initialize comptime field
1819 \\
1820 , "{}", .{status});
1821 }
1822}
1823
1824// Test sizes 0 to 3 since small sizes get parsed differently
1825test "std.zon arrays and slices" {
1826 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/20881
1827
1828 const gpa = std.testing.allocator;
1829
1830 // Literals
1831 {
1832 // Arrays
1833 {
1834 const zero = try fromSlice([0]u8, gpa, ".{}", null, .{});
1835 try std.testing.expectEqualSlices(u8, &@as([0]u8, .{}), &zero);
1836
1837 const one = try fromSlice([1]u8, gpa, ".{'a'}", null, .{});
1838 try std.testing.expectEqualSlices(u8, &@as([1]u8, .{'a'}), &one);
1839
1840 const two = try fromSlice([2]u8, gpa, ".{'a', 'b'}", null, .{});
1841 try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two);
1842
1843 const two_comma = try fromSlice([2]u8, gpa, ".{'a', 'b',}", null, .{});
1844 try std.testing.expectEqualSlices(u8, &@as([2]u8, .{ 'a', 'b' }), &two_comma);
1845
1846 const three = try fromSlice([3]u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1847 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, &three);
1848
1849 const sentinel = try fromSlice([3:'z']u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1850 const expected_sentinel: [3:'z']u8 = .{ 'a', 'b', 'c' };
1851 try std.testing.expectEqualSlices(u8, &expected_sentinel, &sentinel);
1852 }
1853
1854 // Slice literals
1855 {
1856 const zero = try fromSlice([]const u8, gpa, ".{}", null, .{});
1857 defer free(gpa, zero);
1858 try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero);
1859
1860 const one = try fromSlice([]u8, gpa, ".{'a'}", null, .{});
1861 defer free(gpa, one);
1862 try std.testing.expectEqualSlices(u8, &.{'a'}, one);
1863
1864 const two = try fromSlice([]const u8, gpa, ".{'a', 'b'}", null, .{});
1865 defer free(gpa, two);
1866 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two);
1867
1868 const two_comma = try fromSlice([]const u8, gpa, ".{'a', 'b',}", null, .{});
1869 defer free(gpa, two_comma);
1870 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma);
1871
1872 const three = try fromSlice([]u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1873 defer free(gpa, three);
1874 try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three);
1875
1876 const sentinel = try fromSlice([:'z']const u8, gpa, ".{'a', 'b', 'c'}", null, .{});
1877 defer free(gpa, sentinel);
1878 const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' };
1879 try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel);
1880 }
1881 }
1882
1883 // Deep free
1884 {
1885 // Arrays
1886 {
1887 const parsed = try fromSlice([1][]const u8, gpa, ".{\"abc\"}", null, .{});
1888 defer free(gpa, parsed);
1889 const expected: [1][]const u8 = .{"abc"};
1890 try std.testing.expectEqualDeep(expected, parsed);
1891 }
1892
1893 // Slice literals
1894 {
1895 const parsed = try fromSlice([]const []const u8, gpa, ".{\"abc\"}", null, .{});
1896 defer free(gpa, parsed);
1897 const expected: []const []const u8 = &.{"abc"};
1898 try std.testing.expectEqualDeep(expected, parsed);
1899 }
1900 }
1901
1902 // Sentinels and alignment
1903 {
1904 // Arrays
1905 {
1906 const sentinel = try fromSlice([1:2]u8, gpa, ".{1}", null, .{});
1907 try std.testing.expectEqual(@as(usize, 1), sentinel.len);
1908 try std.testing.expectEqual(@as(u8, 1), sentinel[0]);
1909 try std.testing.expectEqual(@as(u8, 2), sentinel[1]);
1910 }
1911
1912 // Slice literals
1913 {
1914 const sentinel = try fromSlice([:2]align(4) u8, gpa, ".{1}", null, .{});
1915 defer free(gpa, sentinel);
1916 try std.testing.expectEqual(@as(usize, 1), sentinel.len);
1917 try std.testing.expectEqual(@as(u8, 1), sentinel[0]);
1918 try std.testing.expectEqual(@as(u8, 2), sentinel[1]);
1919 }
1920 }
1921
1922 // Expect 0 find 3
1923 {
1924 var status: Status = .{};
1925 defer status.deinit(gpa);
1926 try std.testing.expectError(
1927 error.ParseZon,
1928 fromSlice([0]u8, gpa, ".{'a', 'b', 'c'}", &status, .{}),
1929 );
1930 try std.testing.expectFmt(
1931 "1:3: error: index 0 outside of array of length 0\n",
1932 "{}",
1933 .{status},
1934 );
1935 }
1936
1937 // Expect 1 find 2
1938 {
1939 var status: Status = .{};
1940 defer status.deinit(gpa);
1941 try std.testing.expectError(
1942 error.ParseZon,
1943 fromSlice([1]u8, gpa, ".{'a', 'b'}", &status, .{}),
1944 );
1945 try std.testing.expectFmt(
1946 "1:8: error: index 1 outside of array of length 1\n",
1947 "{}",
1948 .{status},
1949 );
1950 }
1951
1952 // Expect 2 find 1
1953 {
1954 var status: Status = .{};
1955 defer status.deinit(gpa);
1956 try std.testing.expectError(
1957 error.ParseZon,
1958 fromSlice([2]u8, gpa, ".{'a'}", &status, .{}),
1959 );
1960 try std.testing.expectFmt(
1961 "1:2: error: expected 2 array elements; found 1\n",
1962 "{}",
1963 .{status},
1964 );
1965 }
1966
1967 // Expect 3 find 0
1968 {
1969 var status: Status = .{};
1970 defer status.deinit(gpa);
1971 try std.testing.expectError(
1972 error.ParseZon,
1973 fromSlice([3]u8, gpa, ".{}", &status, .{}),
1974 );
1975 try std.testing.expectFmt(
1976 "1:2: error: expected 3 array elements; found 0\n",
1977 "{}",
1978 .{status},
1979 );
1980 }
1981
1982 // Wrong inner type
1983 {
1984 // Array
1985 {
1986 var status: Status = .{};
1987 defer status.deinit(gpa);
1988 try std.testing.expectError(
1989 error.ParseZon,
1990 fromSlice([3]bool, gpa, ".{'a', 'b', 'c'}", &status, .{}),
1991 );
1992 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{status});
1993 }
1994
1995 // Slice
1996 {
1997 var status: Status = .{};
1998 defer status.deinit(gpa);
1999 try std.testing.expectError(
2000 error.ParseZon,
2001 fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &status, .{}),
2002 );
2003 try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{}", .{status});
2004 }
2005 }
2006
2007 // Complete wrong type
2008 {
2009 // Array
2010 {
2011 var status: Status = .{};
2012 defer status.deinit(gpa);
2013 try std.testing.expectError(
2014 error.ParseZon,
2015 fromSlice([3]u8, gpa, "'a'", &status, .{}),
2016 );
2017 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2018 }
2019
2020 // Slice
2021 {
2022 var status: Status = .{};
2023 defer status.deinit(gpa);
2024 try std.testing.expectError(
2025 error.ParseZon,
2026 fromSlice([]u8, gpa, "'a'", &status, .{}),
2027 );
2028 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2029 }
2030 }
2031
2032 // Address of is not allowed (indirection for slices in ZON is implicit)
2033 {
2034 var status: Status = .{};
2035 defer status.deinit(gpa);
2036 try std.testing.expectError(
2037 error.ParseZon,
2038 fromSlice([]u8, gpa, " &.{'a', 'b', 'c'}", &status, .{}),
2039 );
2040 try std.testing.expectFmt(
2041 "1:3: error: pointers are not available in ZON\n",
2042 "{}",
2043 .{status},
2044 );
2045 }
2046}
2047
2048test "std.zon string literal" {
2049 const gpa = std.testing.allocator;
2050
2051 // Basic string literal
2052 {
2053 const parsed = try fromSlice([]const u8, gpa, "\"abc\"", null, .{});
2054 defer free(gpa, parsed);
2055 try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed);
2056 }
2057
2058 // String literal with escape characters
2059 {
2060 const parsed = try fromSlice([]const u8, gpa, "\"ab\\nc\"", null, .{});
2061 defer free(gpa, parsed);
2062 try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed);
2063 }
2064
2065 // String literal with embedded null
2066 {
2067 const parsed = try fromSlice([]const u8, gpa, "\"ab\\x00c\"", null, .{});
2068 defer free(gpa, parsed);
2069 try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed);
2070 }
2071
2072 // Passing string literal to a mutable slice
2073 {
2074 {
2075 var status: Status = .{};
2076 defer status.deinit(gpa);
2077 try std.testing.expectError(
2078 error.ParseZon,
2079 fromSlice([]u8, gpa, "\"abcd\"", &status, .{}),
2080 );
2081 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2082 }
2083
2084 {
2085 var status: Status = .{};
2086 defer status.deinit(gpa);
2087 try std.testing.expectError(
2088 error.ParseZon,
2089 fromSlice([]u8, gpa, "\\\\abcd", &status, .{}),
2090 );
2091 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2092 }
2093 }
2094
2095 // Passing string literal to a array
2096 {
2097 {
2098 var ast = try std.zig.Ast.parse(gpa, "\"abcd\"", .zon);
2099 defer ast.deinit(gpa);
2100 var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false });
2101 defer zoir.deinit(gpa);
2102 var status: Status = .{};
2103 defer status.deinit(gpa);
2104 try std.testing.expectError(
2105 error.ParseZon,
2106 fromSlice([4:0]u8, gpa, "\"abcd\"", &status, .{}),
2107 );
2108 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2109 }
2110
2111 {
2112 var status: Status = .{};
2113 defer status.deinit(gpa);
2114 try std.testing.expectError(
2115 error.ParseZon,
2116 fromSlice([4:0]u8, gpa, "\\\\abcd", &status, .{}),
2117 );
2118 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2119 }
2120 }
2121
2122 // Zero terminated slices
2123 {
2124 {
2125 const parsed: [:0]const u8 = try fromSlice(
2126 [:0]const u8,
2127 gpa,
2128 "\"abc\"",
2129 null,
2130 .{},
2131 );
2132 defer free(gpa, parsed);
2133 try std.testing.expectEqualStrings("abc", parsed);
2134 try std.testing.expectEqual(@as(u8, 0), parsed[3]);
2135 }
2136
2137 {
2138 const parsed: [:0]const u8 = try fromSlice(
2139 [:0]const u8,
2140 gpa,
2141 "\\\\abc",
2142 null,
2143 .{},
2144 );
2145 defer free(gpa, parsed);
2146 try std.testing.expectEqualStrings("abc", parsed);
2147 try std.testing.expectEqual(@as(u8, 0), parsed[3]);
2148 }
2149 }
2150
2151 // Other value terminated slices
2152 {
2153 {
2154 var status: Status = .{};
2155 defer status.deinit(gpa);
2156 try std.testing.expectError(
2157 error.ParseZon,
2158 fromSlice([:1]const u8, gpa, "\"foo\"", &status, .{}),
2159 );
2160 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2161 }
2162
2163 {
2164 var status: Status = .{};
2165 defer status.deinit(gpa);
2166 try std.testing.expectError(
2167 error.ParseZon,
2168 fromSlice([:1]const u8, gpa, "\\\\foo", &status, .{}),
2169 );
2170 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2171 }
2172 }
2173
2174 // Expecting string literal, getting something else
2175 {
2176 var status: Status = .{};
2177 defer status.deinit(gpa);
2178 try std.testing.expectError(
2179 error.ParseZon,
2180 fromSlice([]const u8, gpa, "true", &status, .{}),
2181 );
2182 try std.testing.expectFmt("1:1: error: expected string\n", "{}", .{status});
2183 }
2184
2185 // Expecting string literal, getting an incompatible tuple
2186 {
2187 var status: Status = .{};
2188 defer status.deinit(gpa);
2189 try std.testing.expectError(
2190 error.ParseZon,
2191 fromSlice([]const u8, gpa, ".{false}", &status, .{}),
2192 );
2193 try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{}", .{status});
2194 }
2195
2196 // Invalid string literal
2197 {
2198 var status: Status = .{};
2199 defer status.deinit(gpa);
2200 try std.testing.expectError(
2201 error.ParseZon,
2202 fromSlice([]const i8, gpa, "\"\\a\"", &status, .{}),
2203 );
2204 try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{}", .{status});
2205 }
2206
2207 // Slice wrong child type
2208 {
2209 {
2210 var status: Status = .{};
2211 defer status.deinit(gpa);
2212 try std.testing.expectError(
2213 error.ParseZon,
2214 fromSlice([]const i8, gpa, "\"a\"", &status, .{}),
2215 );
2216 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2217 }
2218
2219 {
2220 var status: Status = .{};
2221 defer status.deinit(gpa);
2222 try std.testing.expectError(
2223 error.ParseZon,
2224 fromSlice([]const i8, gpa, "\\\\a", &status, .{}),
2225 );
2226 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2227 }
2228 }
2229
2230 // Bad alignment
2231 {
2232 {
2233 var status: Status = .{};
2234 defer status.deinit(gpa);
2235 try std.testing.expectError(
2236 error.ParseZon,
2237 fromSlice([]align(2) const u8, gpa, "\"abc\"", &status, .{}),
2238 );
2239 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2240 }
2241
2242 {
2243 var status: Status = .{};
2244 defer status.deinit(gpa);
2245 try std.testing.expectError(
2246 error.ParseZon,
2247 fromSlice([]align(2) const u8, gpa, "\\\\abc", &status, .{}),
2248 );
2249 try std.testing.expectFmt("1:1: error: expected array\n", "{}", .{status});
2250 }
2251 }
2252
2253 // Multi line strings
2254 inline for (.{ []const u8, [:0]const u8 }) |String| {
2255 // Nested
2256 {
2257 const S = struct {
2258 message: String,
2259 message2: String,
2260 message3: String,
2261 };
2262 const parsed = try fromSlice(S, gpa,
2263 \\.{
2264 \\ .message =
2265 \\ \\hello, world!
2266 \\
2267 \\ \\this is a multiline string!
2268 \\ \\
2269 \\ \\...
2270 \\
2271 \\ ,
2272 \\ .message2 =
2273 \\ \\this too...sort of.
2274 \\ ,
2275 \\ .message3 =
2276 \\ \\
2277 \\ \\and this.
2278 \\}
2279 , null, .{});
2280 defer free(gpa, parsed);
2281 try std.testing.expectEqualStrings(
2282 "hello, world!\nthis is a multiline string!\n\n...",
2283 parsed.message,
2284 );
2285 try std.testing.expectEqualStrings("this too...sort of.", parsed.message2);
2286 try std.testing.expectEqualStrings("\nand this.", parsed.message3);
2287 }
2288 }
2289}
2290
2291test "std.zon enum literals" {
2292 const gpa = std.testing.allocator;
2293
2294 const Enum = enum {
2295 foo,
2296 bar,
2297 baz,
2298 @"ab\nc",
2299 };
2300
2301 // Tags that exist
2302 try std.testing.expectEqual(Enum.foo, try fromSlice(Enum, gpa, ".foo", null, .{}));
2303 try std.testing.expectEqual(Enum.bar, try fromSlice(Enum, gpa, ".bar", null, .{}));
2304 try std.testing.expectEqual(Enum.baz, try fromSlice(Enum, gpa, ".baz", null, .{}));
2305 try std.testing.expectEqual(
2306 Enum.@"ab\nc",
2307 try fromSlice(Enum, gpa, ".@\"ab\\nc\"", null, .{}),
2308 );
2309
2310 // Bad tag
2311 {
2312 var status: Status = .{};
2313 defer status.deinit(gpa);
2314 try std.testing.expectError(
2315 error.ParseZon,
2316 fromSlice(Enum, gpa, ".qux", &status, .{}),
2317 );
2318 try std.testing.expectFmt(
2319 \\1:2: error: unexpected enum literal 'qux'
2320 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
2321 \\
2322 ,
2323 "{}",
2324 .{status},
2325 );
2326 }
2327
2328 // Bad tag that's too long for parser
2329 {
2330 var status: Status = .{};
2331 defer status.deinit(gpa);
2332 try std.testing.expectError(
2333 error.ParseZon,
2334 fromSlice(Enum, gpa, ".@\"foobarbaz\"", &status, .{}),
2335 );
2336 try std.testing.expectFmt(
2337 \\1:2: error: unexpected enum literal 'foobarbaz'
2338 \\1:2: note: supported: 'foo', 'bar', 'baz', '@"ab\nc"'
2339 \\
2340 ,
2341 "{}",
2342 .{status},
2343 );
2344 }
2345
2346 // Bad type
2347 {
2348 var status: Status = .{};
2349 defer status.deinit(gpa);
2350 try std.testing.expectError(
2351 error.ParseZon,
2352 fromSlice(Enum, gpa, "true", &status, .{}),
2353 );
2354 try std.testing.expectFmt("1:1: error: expected enum literal\n", "{}", .{status});
2355 }
2356
2357 // Test embedded nulls in an identifier
2358 {
2359 var status: Status = .{};
2360 defer status.deinit(gpa);
2361 try std.testing.expectError(
2362 error.ParseZon,
2363 fromSlice(Enum, gpa, ".@\"\\x00\"", &status, .{}),
2364 );
2365 try std.testing.expectFmt(
2366 "1:2: error: identifier cannot contain null bytes\n",
2367 "{}",
2368 .{status},
2369 );
2370 }
2371}
2372
2373test "std.zon parse bool" {
2374 const gpa = std.testing.allocator;
2375
2376 // Correct floats
2377 try std.testing.expectEqual(true, try fromSlice(bool, gpa, "true", null, .{}));
2378 try std.testing.expectEqual(false, try fromSlice(bool, gpa, "false", null, .{}));
2379
2380 // Errors
2381 {
2382 var status: Status = .{};
2383 defer status.deinit(gpa);
2384 try std.testing.expectError(
2385 error.ParseZon,
2386 fromSlice(bool, gpa, " foo", &status, .{}),
2387 );
2388 try std.testing.expectFmt(
2389 \\1:2: error: invalid expression
2390 \\1:2: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
2391 \\1:2: note: precede identifier with '.' for an enum literal
2392 \\
2393 , "{}", .{status});
2394 }
2395 {
2396 var status: Status = .{};
2397 defer status.deinit(gpa);
2398 try std.testing.expectError(error.ParseZon, fromSlice(bool, gpa, "123", &status, .{}));
2399 try std.testing.expectFmt("1:1: error: expected type 'bool'\n", "{}", .{status});
2400 }
2401}
2402
2403test "std.zon intFromFloatExact" {
2404 // Valid conversions
2405 try std.testing.expectEqual(@as(u8, 10), intFromFloatExact(u8, @as(f32, 10.0)).?);
2406 try std.testing.expectEqual(@as(i8, -123), intFromFloatExact(i8, @as(f64, @as(f64, -123.0))).?);
2407 try std.testing.expectEqual(@as(i16, 45), intFromFloatExact(i16, @as(f128, @as(f128, 45.0))).?);
2408
2409 // Out of range
2410 try std.testing.expectEqual(@as(?u4, null), intFromFloatExact(u4, @as(f32, 16.0)));
2411 try std.testing.expectEqual(@as(?i4, null), intFromFloatExact(i4, @as(f64, -17.0)));
2412 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, @as(f128, -2.0)));
2413
2414 // Not a whole number
2415 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, @as(f32, 0.5)));
2416 try std.testing.expectEqual(@as(?i8, null), intFromFloatExact(i8, @as(f64, 0.01)));
2417
2418 // Infinity and NaN
2419 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, std.math.inf(f32)));
2420 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, -std.math.inf(f32)));
2421 try std.testing.expectEqual(@as(?u8, null), intFromFloatExact(u8, std.math.nan(f32)));
2422}
2423
2424test "std.zon parse int" {
2425 const gpa = std.testing.allocator;
2426
2427 // Test various numbers and types
2428 try std.testing.expectEqual(@as(u8, 10), try fromSlice(u8, gpa, "10", null, .{}));
2429 try std.testing.expectEqual(@as(i16, 24), try fromSlice(i16, gpa, "24", null, .{}));
2430 try std.testing.expectEqual(@as(i14, -4), try fromSlice(i14, gpa, "-4", null, .{}));
2431 try std.testing.expectEqual(@as(i32, -123), try fromSlice(i32, gpa, "-123", null, .{}));
2432
2433 // Test limits
2434 try std.testing.expectEqual(@as(i8, 127), try fromSlice(i8, gpa, "127", null, .{}));
2435 try std.testing.expectEqual(@as(i8, -128), try fromSlice(i8, gpa, "-128", null, .{}));
2436
2437 // Test characters
2438 try std.testing.expectEqual(@as(u8, 'a'), try fromSlice(u8, gpa, "'a'", null, .{}));
2439 try std.testing.expectEqual(@as(u8, 'z'), try fromSlice(u8, gpa, "'z'", null, .{}));
2440
2441 // Test big integers
2442 try std.testing.expectEqual(
2443 @as(u65, 36893488147419103231),
2444 try fromSlice(u65, gpa, "36893488147419103231", null, .{}),
2445 );
2446 try std.testing.expectEqual(
2447 @as(u65, 36893488147419103231),
2448 try fromSlice(u65, gpa, "368934_881_474191032_31", null, .{}),
2449 );
2450
2451 // Test big integer limits
2452 try std.testing.expectEqual(
2453 @as(i66, 36893488147419103231),
2454 try fromSlice(i66, gpa, "36893488147419103231", null, .{}),
2455 );
2456 try std.testing.expectEqual(
2457 @as(i66, -36893488147419103232),
2458 try fromSlice(i66, gpa, "-36893488147419103232", null, .{}),
2459 );
2460 {
2461 var status: Status = .{};
2462 defer status.deinit(gpa);
2463 try std.testing.expectError(error.ParseZon, fromSlice(
2464 i66,
2465 gpa,
2466 "36893488147419103232",
2467 &status,
2468 .{},
2469 ));
2470 try std.testing.expectFmt(
2471 "1:1: error: type 'i66' cannot represent value\n",
2472 "{}",
2473 .{status},
2474 );
2475 }
2476 {
2477 var status: Status = .{};
2478 defer status.deinit(gpa);
2479 try std.testing.expectError(error.ParseZon, fromSlice(
2480 i66,
2481 gpa,
2482 "-36893488147419103233",
2483 &status,
2484 .{},
2485 ));
2486 try std.testing.expectFmt(
2487 "1:1: error: type 'i66' cannot represent value\n",
2488 "{}",
2489 .{status},
2490 );
2491 }
2492
2493 // Test parsing whole number floats as integers
2494 try std.testing.expectEqual(@as(i8, -1), try fromSlice(i8, gpa, "-1.0", null, .{}));
2495 try std.testing.expectEqual(@as(i8, 123), try fromSlice(i8, gpa, "123.0", null, .{}));
2496
2497 // Test non-decimal integers
2498 try std.testing.expectEqual(@as(i16, 0xff), try fromSlice(i16, gpa, "0xff", null, .{}));
2499 try std.testing.expectEqual(@as(i16, -0xff), try fromSlice(i16, gpa, "-0xff", null, .{}));
2500 try std.testing.expectEqual(@as(i16, 0o77), try fromSlice(i16, gpa, "0o77", null, .{}));
2501 try std.testing.expectEqual(@as(i16, -0o77), try fromSlice(i16, gpa, "-0o77", null, .{}));
2502 try std.testing.expectEqual(@as(i16, 0b11), try fromSlice(i16, gpa, "0b11", null, .{}));
2503 try std.testing.expectEqual(@as(i16, -0b11), try fromSlice(i16, gpa, "-0b11", null, .{}));
2504
2505 // Test non-decimal big integers
2506 try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(
2507 u65,
2508 gpa,
2509 "0x1ffffffffffffffff",
2510 null,
2511 .{},
2512 ));
2513 try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(
2514 i66,
2515 gpa,
2516 "0x1ffffffffffffffff",
2517 null,
2518 .{},
2519 ));
2520 try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(
2521 i66,
2522 gpa,
2523 "-0x1ffffffffffffffff",
2524 null,
2525 .{},
2526 ));
2527 try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(
2528 u65,
2529 gpa,
2530 "0o3777777777777777777777",
2531 null,
2532 .{},
2533 ));
2534 try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(
2535 i66,
2536 gpa,
2537 "0o3777777777777777777777",
2538 null,
2539 .{},
2540 ));
2541 try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(
2542 i66,
2543 gpa,
2544 "-0o3777777777777777777777",
2545 null,
2546 .{},
2547 ));
2548 try std.testing.expectEqual(@as(u65, 0x1ffffffffffffffff), try fromSlice(
2549 u65,
2550 gpa,
2551 "0b11111111111111111111111111111111111111111111111111111111111111111",
2552 null,
2553 .{},
2554 ));
2555 try std.testing.expectEqual(@as(i66, 0x1ffffffffffffffff), try fromSlice(
2556 i66,
2557 gpa,
2558 "0b11111111111111111111111111111111111111111111111111111111111111111",
2559 null,
2560 .{},
2561 ));
2562 try std.testing.expectEqual(@as(i66, -0x1ffffffffffffffff), try fromSlice(
2563 i66,
2564 gpa,
2565 "-0b11111111111111111111111111111111111111111111111111111111111111111",
2566 null,
2567 .{},
2568 ));
2569
2570 // Number with invalid character in the middle
2571 {
2572 var status: Status = .{};
2573 defer status.deinit(gpa);
2574 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "32a32", &status, .{}));
2575 try std.testing.expectFmt(
2576 "1:3: error: invalid digit 'a' for decimal base\n",
2577 "{}",
2578 .{status},
2579 );
2580 }
2581
2582 // Failing to parse as int
2583 {
2584 var status: Status = .{};
2585 defer status.deinit(gpa);
2586 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "true", &status, .{}));
2587 try std.testing.expectFmt("1:1: error: expected type 'u8'\n", "{}", .{status});
2588 }
2589
2590 // Failing because an int is out of range
2591 {
2592 var status: Status = .{};
2593 defer status.deinit(gpa);
2594 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "256", &status, .{}));
2595 try std.testing.expectFmt(
2596 "1:1: error: type 'u8' cannot represent value\n",
2597 "{}",
2598 .{status},
2599 );
2600 }
2601
2602 // Failing because a negative int is out of range
2603 {
2604 var status: Status = .{};
2605 defer status.deinit(gpa);
2606 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-129", &status, .{}));
2607 try std.testing.expectFmt(
2608 "1:1: error: type 'i8' cannot represent value\n",
2609 "{}",
2610 .{status},
2611 );
2612 }
2613
2614 // Failing because an unsigned int is negative
2615 {
2616 var status: Status = .{};
2617 defer status.deinit(gpa);
2618 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1", &status, .{}));
2619 try std.testing.expectFmt(
2620 "1:1: error: type 'u8' cannot represent value\n",
2621 "{}",
2622 .{status},
2623 );
2624 }
2625
2626 // Failing because a float is non-whole
2627 {
2628 var status: Status = .{};
2629 defer status.deinit(gpa);
2630 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "1.5", &status, .{}));
2631 try std.testing.expectFmt(
2632 "1:1: error: type 'u8' cannot represent value\n",
2633 "{}",
2634 .{status},
2635 );
2636 }
2637
2638 // Failing because a float is negative
2639 {
2640 var status: Status = .{};
2641 defer status.deinit(gpa);
2642 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "-1.0", &status, .{}));
2643 try std.testing.expectFmt(
2644 "1:1: error: type 'u8' cannot represent value\n",
2645 "{}",
2646 .{status},
2647 );
2648 }
2649
2650 // Negative integer zero
2651 {
2652 var status: Status = .{};
2653 defer status.deinit(gpa);
2654 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-0", &status, .{}));
2655 try std.testing.expectFmt(
2656 \\1:2: error: integer literal '-0' is ambiguous
2657 \\1:2: note: use '0' for an integer zero
2658 \\1:2: note: use '-0.0' for a floating-point signed zero
2659 \\
2660 , "{}", .{status});
2661 }
2662
2663 // Negative integer zero casted to float
2664 {
2665 var status: Status = .{};
2666 defer status.deinit(gpa);
2667 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-0", &status, .{}));
2668 try std.testing.expectFmt(
2669 \\1:2: error: integer literal '-0' is ambiguous
2670 \\1:2: note: use '0' for an integer zero
2671 \\1:2: note: use '-0.0' for a floating-point signed zero
2672 \\
2673 , "{}", .{status});
2674 }
2675
2676 // Negative float 0 is allowed
2677 try std.testing.expect(
2678 std.math.isNegativeZero(try fromSlice(f32, gpa, "-0.0", null, .{})),
2679 );
2680 try std.testing.expect(std.math.isPositiveZero(try fromSlice(f32, gpa, "0.0", null, .{})));
2681
2682 // Double negation is not allowed
2683 {
2684 var status: Status = .{};
2685 defer status.deinit(gpa);
2686 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "--2", &status, .{}));
2687 try std.testing.expectFmt(
2688 "1:1: error: expected number or 'inf' after '-'\n",
2689 "{}",
2690 .{status},
2691 );
2692 }
2693
2694 {
2695 var status: Status = .{};
2696 defer status.deinit(gpa);
2697 try std.testing.expectError(
2698 error.ParseZon,
2699 fromSlice(f32, gpa, "--2.0", &status, .{}),
2700 );
2701 try std.testing.expectFmt(
2702 "1:1: error: expected number or 'inf' after '-'\n",
2703 "{}",
2704 .{status},
2705 );
2706 }
2707
2708 // Invalid int literal
2709 {
2710 var status: Status = .{};
2711 defer status.deinit(gpa);
2712 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0xg", &status, .{}));
2713 try std.testing.expectFmt("1:3: error: invalid digit 'g' for hex base\n", "{}", .{status});
2714 }
2715
2716 // Notes on invalid int literal
2717 {
2718 var status: Status = .{};
2719 defer status.deinit(gpa);
2720 try std.testing.expectError(error.ParseZon, fromSlice(u8, gpa, "0123", &status, .{}));
2721 try std.testing.expectFmt(
2722 \\1:1: error: number '0123' has leading zero
2723 \\1:1: note: use '0o' prefix for octal literals
2724 \\
2725 , "{}", .{status});
2726 }
2727}
2728
2729test "std.zon negative char" {
2730 const gpa = std.testing.allocator;
2731
2732 {
2733 var status: Status = .{};
2734 defer status.deinit(gpa);
2735 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-'a'", &status, .{}));
2736 try std.testing.expectFmt(
2737 "1:1: error: expected number or 'inf' after '-'\n",
2738 "{}",
2739 .{status},
2740 );
2741 }
2742 {
2743 var status: Status = .{};
2744 defer status.deinit(gpa);
2745 try std.testing.expectError(error.ParseZon, fromSlice(i16, gpa, "-'a'", &status, .{}));
2746 try std.testing.expectFmt(
2747 "1:1: error: expected number or 'inf' after '-'\n",
2748 "{}",
2749 .{status},
2750 );
2751 }
2752}
2753
2754test "std.zon parse float" {
2755 const gpa = std.testing.allocator;
2756
2757 // Test decimals
2758 try std.testing.expectEqual(@as(f16, 0.5), try fromSlice(f16, gpa, "0.5", null, .{}));
2759 try std.testing.expectEqual(
2760 @as(f32, 123.456),
2761 try fromSlice(f32, gpa, "123.456", null, .{}),
2762 );
2763 try std.testing.expectEqual(
2764 @as(f64, -123.456),
2765 try fromSlice(f64, gpa, "-123.456", null, .{}),
2766 );
2767 try std.testing.expectEqual(@as(f128, 42.5), try fromSlice(f128, gpa, "42.5", null, .{}));
2768
2769 // Test whole numbers with and without decimals
2770 try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5.0", null, .{}));
2771 try std.testing.expectEqual(@as(f16, 5.0), try fromSlice(f16, gpa, "5", null, .{}));
2772 try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102.0", null, .{}));
2773 try std.testing.expectEqual(@as(f32, -102), try fromSlice(f32, gpa, "-102", null, .{}));
2774
2775 // Test characters and negated characters
2776 try std.testing.expectEqual(@as(f32, 'a'), try fromSlice(f32, gpa, "'a'", null, .{}));
2777 try std.testing.expectEqual(@as(f32, 'z'), try fromSlice(f32, gpa, "'z'", null, .{}));
2778
2779 // Test big integers
2780 try std.testing.expectEqual(
2781 @as(f32, 36893488147419103231),
2782 try fromSlice(f32, gpa, "36893488147419103231", null, .{}),
2783 );
2784 try std.testing.expectEqual(
2785 @as(f32, -36893488147419103231),
2786 try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),
2787 );
2788 try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(
2789 f128,
2790 gpa,
2791 "0x1ffffffffffffffff",
2792 null,
2793 .{},
2794 ));
2795 try std.testing.expectEqual(@as(f32, 0x1ffffffffffffffff), try fromSlice(
2796 f32,
2797 gpa,
2798 "0x1ffffffffffffffff",
2799 null,
2800 .{},
2801 ));
2802
2803 // Exponents, underscores
2804 try std.testing.expectEqual(
2805 @as(f32, 123.0E+77),
2806 try fromSlice(f32, gpa, "12_3.0E+77", null, .{}),
2807 );
2808
2809 // Hexadecimal
2810 try std.testing.expectEqual(
2811 @as(f32, 0x103.70p-5),
2812 try fromSlice(f32, gpa, "0x103.70p-5", null, .{}),
2813 );
2814 try std.testing.expectEqual(
2815 @as(f32, -0x103.70),
2816 try fromSlice(f32, gpa, "-0x103.70", null, .{}),
2817 );
2818 try std.testing.expectEqual(
2819 @as(f32, 0x1234_5678.9ABC_CDEFp-10),
2820 try fromSlice(f32, gpa, "0x1234_5678.9ABC_CDEFp-10", null, .{}),
2821 );
2822
2823 // inf, nan
2824 try std.testing.expect(std.math.isPositiveInf(try fromSlice(f32, gpa, "inf", null, .{})));
2825 try std.testing.expect(std.math.isNegativeInf(try fromSlice(f32, gpa, "-inf", null, .{})));
2826 try std.testing.expect(std.math.isNan(try fromSlice(f32, gpa, "nan", null, .{})));
2827
2828 // Negative nan not allowed
2829 {
2830 var status: Status = .{};
2831 defer status.deinit(gpa);
2832 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-nan", &status, .{}));
2833 try std.testing.expectFmt(
2834 "1:1: error: expected number or 'inf' after '-'\n",
2835 "{}",
2836 .{status},
2837 );
2838 }
2839
2840 // nan as int not allowed
2841 {
2842 var status: Status = .{};
2843 defer status.deinit(gpa);
2844 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &status, .{}));
2845 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status});
2846 }
2847
2848 // nan as int not allowed
2849 {
2850 var status: Status = .{};
2851 defer status.deinit(gpa);
2852 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "nan", &status, .{}));
2853 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status});
2854 }
2855
2856 // inf as int not allowed
2857 {
2858 var status: Status = .{};
2859 defer status.deinit(gpa);
2860 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "inf", &status, .{}));
2861 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status});
2862 }
2863
2864 // -inf as int not allowed
2865 {
2866 var status: Status = .{};
2867 defer status.deinit(gpa);
2868 try std.testing.expectError(error.ParseZon, fromSlice(i8, gpa, "-inf", &status, .{}));
2869 try std.testing.expectFmt("1:1: error: expected type 'i8'\n", "{}", .{status});
2870 }
2871
2872 // Bad identifier as float
2873 {
2874 var status: Status = .{};
2875 defer status.deinit(gpa);
2876 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "foo", &status, .{}));
2877 try std.testing.expectFmt(
2878 \\1:1: error: invalid expression
2879 \\1:1: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
2880 \\1:1: note: precede identifier with '.' for an enum literal
2881 \\
2882 , "{}", .{status});
2883 }
2884
2885 {
2886 var status: Status = .{};
2887 defer status.deinit(gpa);
2888 try std.testing.expectError(error.ParseZon, fromSlice(f32, gpa, "-foo", &status, .{}));
2889 try std.testing.expectFmt(
2890 "1:1: error: expected number or 'inf' after '-'\n",
2891 "{}",
2892 .{status},
2893 );
2894 }
2895
2896 // Non float as float
2897 {
2898 var status: Status = .{};
2899 defer status.deinit(gpa);
2900 try std.testing.expectError(
2901 error.ParseZon,
2902 fromSlice(f32, gpa, "\"foo\"", &status, .{}),
2903 );
2904 try std.testing.expectFmt("1:1: error: expected type 'f32'\n", "{}", .{status});
2905 }
2906}
2907
2908test "std.zon free on error" {
2909 // Test freeing partially allocated structs
2910 {
2911 const Struct = struct {
2912 x: []const u8,
2913 y: []const u8,
2914 z: bool,
2915 };
2916 try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator,
2917 \\.{
2918 \\ .x = "hello",
2919 \\ .y = "world",
2920 \\ .z = "fail",
2921 \\}
2922 , null, .{}));
2923 }
2924
2925 // Test freeing partially allocated tuples
2926 {
2927 const Struct = struct {
2928 []const u8,
2929 []const u8,
2930 bool,
2931 };
2932 try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator,
2933 \\.{
2934 \\ "hello",
2935 \\ "world",
2936 \\ "fail",
2937 \\}
2938 , null, .{}));
2939 }
2940
2941 // Test freeing structs with missing fields
2942 {
2943 const Struct = struct {
2944 x: []const u8,
2945 y: bool,
2946 };
2947 try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator,
2948 \\.{
2949 \\ .x = "hello",
2950 \\}
2951 , null, .{}));
2952 }
2953
2954 // Test freeing partially allocated arrays
2955 {
2956 try std.testing.expectError(error.ParseZon, fromSlice(
2957 [3][]const u8,
2958 std.testing.allocator,
2959 \\.{
2960 \\ "hello",
2961 \\ false,
2962 \\ false,
2963 \\}
2964 ,
2965 null,
2966 .{},
2967 ));
2968 }
2969
2970 // Test freeing partially allocated slices
2971 {
2972 try std.testing.expectError(error.ParseZon, fromSlice(
2973 [][]const u8,
2974 std.testing.allocator,
2975 \\.{
2976 \\ "hello",
2977 \\ "world",
2978 \\ false,
2979 \\}
2980 ,
2981 null,
2982 .{},
2983 ));
2984 }
2985
2986 // We can parse types that can't be freed, as long as they contain no allocations, e.g. untagged
2987 // unions.
2988 try std.testing.expectEqual(
2989 @as(f32, 1.5),
2990 (try fromSlice(union { x: f32 }, std.testing.allocator, ".{ .x = 1.5 }", null, .{})).x,
2991 );
2992
2993 // We can also parse types that can't be freed if it's impossible for an error to occur after
2994 // the allocation, as is the case here.
2995 {
2996 const result = try fromSlice(
2997 union { x: []const u8 },
2998 std.testing.allocator,
2999 ".{ .x = \"foo\" }",
3000 null,
3001 .{},
3002 );
3003 defer free(std.testing.allocator, result.x);
3004 try std.testing.expectEqualStrings("foo", result.x);
3005 }
3006
3007 // However, if it's possible we could get an error requiring we free the value, but the value
3008 // cannot be freed (e.g. untagged unions) then we need to turn off `free_on_error` for it to
3009 // compile.
3010 {
3011 const S = struct {
3012 union { x: []const u8 },
3013 bool,
3014 };
3015 const result = try fromSlice(
3016 S,
3017 std.testing.allocator,
3018 ".{ .{ .x = \"foo\" }, true }",
3019 null,
3020 .{ .free_on_error = false },
3021 );
3022 defer free(std.testing.allocator, result[0].x);
3023 try std.testing.expectEqualStrings("foo", result[0].x);
3024 try std.testing.expect(result[1]);
3025 }
3026
3027 // Again but for structs.
3028 {
3029 const S = struct {
3030 a: union { x: []const u8 },
3031 b: bool,
3032 };
3033 const result = try fromSlice(
3034 S,
3035 std.testing.allocator,
3036 ".{ .a = .{ .x = \"foo\" }, .b = true }",
3037 null,
3038 .{
3039 .free_on_error = false,
3040 },
3041 );
3042 defer free(std.testing.allocator, result.a.x);
3043 try std.testing.expectEqualStrings("foo", result.a.x);
3044 try std.testing.expect(result.b);
3045 }
3046
3047 // Again but for arrays.
3048 {
3049 const S = [2]union { x: []const u8 };
3050 const result = try fromSlice(
3051 S,
3052 std.testing.allocator,
3053 ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }",
3054 null,
3055 .{
3056 .free_on_error = false,
3057 },
3058 );
3059 defer free(std.testing.allocator, result[0].x);
3060 defer free(std.testing.allocator, result[1].x);
3061 try std.testing.expectEqualStrings("foo", result[0].x);
3062 try std.testing.expectEqualStrings("bar", result[1].x);
3063 }
3064
3065 // Again but for slices.
3066 {
3067 const S = []union { x: []const u8 };
3068 const result = try fromSlice(
3069 S,
3070 std.testing.allocator,
3071 ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }",
3072 null,
3073 .{
3074 .free_on_error = false,
3075 },
3076 );
3077 defer std.testing.allocator.free(result);
3078 defer free(std.testing.allocator, result[0].x);
3079 defer free(std.testing.allocator, result[1].x);
3080 try std.testing.expectEqualStrings("foo", result[0].x);
3081 try std.testing.expectEqualStrings("bar", result[1].x);
3082 }
3083}
3084
3085test "std.zon vector" {
3086 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/15330
3087 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // https://github.com/ziglang/zig/issues/15329
3088
3089 const gpa = std.testing.allocator;
3090
3091 // Passing cases
3092 try std.testing.expectEqual(
3093 @Vector(0, bool){},
3094 try fromSlice(@Vector(0, bool), gpa, ".{}", null, .{}),
3095 );
3096 try std.testing.expectEqual(
3097 @Vector(3, bool){ true, false, true },
3098 try fromSlice(@Vector(3, bool), gpa, ".{true, false, true}", null, .{}),
3099 );
3100
3101 try std.testing.expectEqual(
3102 @Vector(0, f32){},
3103 try fromSlice(@Vector(0, f32), gpa, ".{}", null, .{}),
3104 );
3105 try std.testing.expectEqual(
3106 @Vector(3, f32){ 1.5, 2.5, 3.5 },
3107 try fromSlice(@Vector(3, f32), gpa, ".{1.5, 2.5, 3.5}", null, .{}),
3108 );
3109
3110 try std.testing.expectEqual(
3111 @Vector(0, u8){},
3112 try fromSlice(@Vector(0, u8), gpa, ".{}", null, .{}),
3113 );
3114 try std.testing.expectEqual(
3115 @Vector(3, u8){ 2, 4, 6 },
3116 try fromSlice(@Vector(3, u8), gpa, ".{2, 4, 6}", null, .{}),
3117 );
3118
3119 {
3120 try std.testing.expectEqual(
3121 @Vector(0, *const u8){},
3122 try fromSlice(@Vector(0, *const u8), gpa, ".{}", null, .{}),
3123 );
3124 const pointers = try fromSlice(@Vector(3, *const u8), gpa, ".{2, 4, 6}", null, .{});
3125 defer free(gpa, pointers);
3126 try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers);
3127 }
3128
3129 {
3130 try std.testing.expectEqual(
3131 @Vector(0, ?*const u8){},
3132 try fromSlice(@Vector(0, ?*const u8), gpa, ".{}", null, .{}),
3133 );
3134 const pointers = try fromSlice(@Vector(3, ?*const u8), gpa, ".{2, null, 6}", null, .{});
3135 defer free(gpa, pointers);
3136 try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers);
3137 }
3138
3139 // Too few fields
3140 {
3141 var status: Status = .{};
3142 defer status.deinit(gpa);
3143 try std.testing.expectError(
3144 error.ParseZon,
3145 fromSlice(@Vector(2, f32), gpa, ".{0.5}", &status, .{}),
3146 );
3147 try std.testing.expectFmt(
3148 "1:2: error: expected 2 vector elements; found 1\n",
3149 "{}",
3150 .{status},
3151 );
3152 }
3153
3154 // Too many fields
3155 {
3156 var status: Status = .{};
3157 defer status.deinit(gpa);
3158 try std.testing.expectError(
3159 error.ParseZon,
3160 fromSlice(@Vector(2, f32), gpa, ".{0.5, 1.5, 2.5}", &status, .{}),
3161 );
3162 try std.testing.expectFmt(
3163 "1:2: error: expected 2 vector elements; found 3\n",
3164 "{}",
3165 .{status},
3166 );
3167 }
3168
3169 // Wrong type fields
3170 {
3171 var status: Status = .{};
3172 defer status.deinit(gpa);
3173 try std.testing.expectError(
3174 error.ParseZon,
3175 fromSlice(@Vector(3, f32), gpa, ".{0.5, true, 2.5}", &status, .{}),
3176 );
3177 try std.testing.expectFmt(
3178 "1:8: error: expected type 'f32'\n",
3179 "{}",
3180 .{status},
3181 );
3182 }
3183
3184 // Wrong type
3185 {
3186 var status: Status = .{};
3187 defer status.deinit(gpa);
3188 try std.testing.expectError(
3189 error.ParseZon,
3190 fromSlice(@Vector(3, u8), gpa, "true", &status, .{}),
3191 );
3192 try std.testing.expectFmt("1:1: error: expected type '@Vector(3, u8)'\n", "{}", .{status});
3193 }
3194
3195 // Elements should get freed on error
3196 {
3197 var status: Status = .{};
3198 defer status.deinit(gpa);
3199 try std.testing.expectError(
3200 error.ParseZon,
3201 fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &status, .{}),
3202 );
3203 try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{}", .{status});
3204 }
3205}
3206
3207test "std.zon add pointers" {
3208 const gpa = std.testing.allocator;
3209
3210 // Primitive with varying levels of pointers
3211 {
3212 const result = try fromSlice(*u32, gpa, "10", null, .{});
3213 defer free(gpa, result);
3214 try std.testing.expectEqual(@as(u32, 10), result.*);
3215 }
3216
3217 {
3218 const result = try fromSlice(**u32, gpa, "10", null, .{});
3219 defer free(gpa, result);
3220 try std.testing.expectEqual(@as(u32, 10), result.*.*);
3221 }
3222
3223 {
3224 const result = try fromSlice(***u32, gpa, "10", null, .{});
3225 defer free(gpa, result);
3226 try std.testing.expectEqual(@as(u32, 10), result.*.*.*);
3227 }
3228
3229 // Primitive optional with varying levels of pointers
3230 {
3231 const some = try fromSlice(?*u32, gpa, "10", null, .{});
3232 defer free(gpa, some);
3233 try std.testing.expectEqual(@as(u32, 10), some.?.*);
3234
3235 const none = try fromSlice(?*u32, gpa, "null", null, .{});
3236 defer free(gpa, none);
3237 try std.testing.expectEqual(null, none);
3238 }
3239
3240 {
3241 const some = try fromSlice(*?u32, gpa, "10", null, .{});
3242 defer free(gpa, some);
3243 try std.testing.expectEqual(@as(u32, 10), some.*.?);
3244
3245 const none = try fromSlice(*?u32, gpa, "null", null, .{});
3246 defer free(gpa, none);
3247 try std.testing.expectEqual(null, none.*);
3248 }
3249
3250 {
3251 const some = try fromSlice(?**u32, gpa, "10", null, .{});
3252 defer free(gpa, some);
3253 try std.testing.expectEqual(@as(u32, 10), some.?.*.*);
3254
3255 const none = try fromSlice(?**u32, gpa, "null", null, .{});
3256 defer free(gpa, none);
3257 try std.testing.expectEqual(null, none);
3258 }
3259
3260 {
3261 const some = try fromSlice(*?*u32, gpa, "10", null, .{});
3262 defer free(gpa, some);
3263 try std.testing.expectEqual(@as(u32, 10), some.*.?.*);
3264
3265 const none = try fromSlice(*?*u32, gpa, "null", null, .{});
3266 defer free(gpa, none);
3267 try std.testing.expectEqual(null, none.*);
3268 }
3269
3270 {
3271 const some = try fromSlice(**?u32, gpa, "10", null, .{});
3272 defer free(gpa, some);
3273 try std.testing.expectEqual(@as(u32, 10), some.*.*.?);
3274
3275 const none = try fromSlice(**?u32, gpa, "null", null, .{});
3276 defer free(gpa, none);
3277 try std.testing.expectEqual(null, none.*.*);
3278 }
3279
3280 // Pointer to an array
3281 {
3282 const result = try fromSlice(*[3]u8, gpa, ".{ 1, 2, 3 }", null, .{});
3283 defer free(gpa, result);
3284 try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*);
3285 }
3286
3287 // A complicated type with nested internal pointers and string allocations
3288 {
3289 const Inner = struct {
3290 f1: *const ?*const []const u8,
3291 f2: *const ?*const []const u8,
3292 };
3293 const Outer = struct {
3294 f1: *const ?*const Inner,
3295 f2: *const ?*const Inner,
3296 };
3297 const expected: Outer = .{
3298 .f1 = &&.{
3299 .f1 = &null,
3300 .f2 = &&"foo",
3301 },
3302 .f2 = &null,
3303 };
3304
3305 const found = try fromSlice(?*Outer, gpa,
3306 \\.{
3307 \\ .f1 = .{
3308 \\ .f1 = null,
3309 \\ .f2 = "foo",
3310 \\ },
3311 \\ .f2 = null,
3312 \\}
3313 , null, .{});
3314 defer free(gpa, found);
3315
3316 try std.testing.expectEqualDeep(expected, found.?.*);
3317 }
3318
3319 // Test that optional types are flattened correctly in errors
3320 {
3321 var status: Status = .{};
3322 defer status.deinit(gpa);
3323 try std.testing.expectError(
3324 error.ParseZon,
3325 fromSlice(*const ?*const u8, gpa, "true", &status, .{}),
3326 );
3327 try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{}", .{status});
3328 }
3329
3330 {
3331 var status: Status = .{};
3332 defer status.deinit(gpa);
3333 try std.testing.expectError(
3334 error.ParseZon,
3335 fromSlice(*const ?*const f32, gpa, "true", &status, .{}),
3336 );
3337 try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{}", .{status});
3338 }
3339
3340 {
3341 var status: Status = .{};
3342 defer status.deinit(gpa);
3343 try std.testing.expectError(
3344 error.ParseZon,
3345 fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &status, .{}),
3346 );
3347 try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{}", .{status});
3348 }
3349
3350 {
3351 var status: Status = .{};
3352 defer status.deinit(gpa);
3353 try std.testing.expectError(
3354 error.ParseZon,
3355 fromSlice(*const ?*const bool, gpa, "10", &status, .{}),
3356 );
3357 try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{}", .{status});
3358 }
3359
3360 {
3361 var status: Status = .{};
3362 defer status.deinit(gpa);
3363 try std.testing.expectError(
3364 error.ParseZon,
3365 fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &status, .{}),
3366 );
3367 try std.testing.expectFmt("1:1: error: expected optional struct\n", "{}", .{status});
3368 }
3369
3370 {
3371 var status: Status = .{};
3372 defer status.deinit(gpa);
3373 try std.testing.expectError(
3374 error.ParseZon,
3375 fromSlice(*const ?*const struct { i32 }, gpa, "true", &status, .{}),
3376 );
3377 try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{}", .{status});
3378 }
3379
3380 {
3381 var status: Status = .{};
3382 defer status.deinit(gpa);
3383 try std.testing.expectError(
3384 error.ParseZon,
3385 fromSlice(*const ?*const union { x: void }, gpa, "true", &status, .{}),
3386 );
3387 try std.testing.expectFmt("1:1: error: expected optional union\n", "{}", .{status});
3388 }
3389
3390 {
3391 var status: Status = .{};
3392 defer status.deinit(gpa);
3393 try std.testing.expectError(
3394 error.ParseZon,
3395 fromSlice(*const ?*const [3]u8, gpa, "true", &status, .{}),
3396 );
3397 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status});
3398 }
3399
3400 {
3401 var status: Status = .{};
3402 defer status.deinit(gpa);
3403 try std.testing.expectError(
3404 error.ParseZon,
3405 fromSlice(?[3]u8, gpa, "true", &status, .{}),
3406 );
3407 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status});
3408 }
3409
3410 {
3411 var status: Status = .{};
3412 defer status.deinit(gpa);
3413 try std.testing.expectError(
3414 error.ParseZon,
3415 fromSlice(*const ?*const []u8, gpa, "true", &status, .{}),
3416 );
3417 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status});
3418 }
3419
3420 {
3421 var status: Status = .{};
3422 defer status.deinit(gpa);
3423 try std.testing.expectError(
3424 error.ParseZon,
3425 fromSlice(?[]u8, gpa, "true", &status, .{}),
3426 );
3427 try std.testing.expectFmt("1:1: error: expected optional array\n", "{}", .{status});
3428 }
3429
3430 {
3431 var status: Status = .{};
3432 defer status.deinit(gpa);
3433 try std.testing.expectError(
3434 error.ParseZon,
3435 fromSlice(*const ?*const []const u8, gpa, "true", &status, .{}),
3436 );
3437 try std.testing.expectFmt("1:1: error: expected optional string\n", "{}", .{status});
3438 }
3439
3440 {
3441 var status: Status = .{};
3442 defer status.deinit(gpa);
3443 try std.testing.expectError(
3444 error.ParseZon,
3445 fromSlice(*const ?*const enum { foo }, gpa, "true", &status, .{}),
3446 );
3447 try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{}", .{status});
3448 }
3449}
lib/std/zon/stringify.zig created+2306
......@@ -0,0 +1,2306 @@
1//! ZON can be serialized with `serialize`.
2//!
3//! The following functions are provided for serializing recursive types:
4//! * `serializeMaxDepth`
5//! * `serializeArbitraryDepth`
6//!
7//! For additional control over serialization, see `Serializer`.
8//!
9//! The following types and any types that contain them may not be serialized:
10//! * `type`
11//! * `void`, except as a union payload
12//! * `noreturn`
13//! * Error sets/error unions
14//! * Untagged unions
15//! * Many-pointers or C-pointers
16//! * Opaque types, including `anyopaque`
17//! * Async frame types, including `anyframe` and `anyframe->T`
18//! * Functions
19//!
20//! All other types are valid. Unsupported types will fail to serialize at compile time. Pointers
21//! are followed.
22
23const std = @import("std");
24const assert = std.debug.assert;
25
26/// Options for `serialize`.
27pub const SerializeOptions = struct {
28 /// If false, whitespace is omitted. Otherwise whitespace is emitted in standard Zig style.
29 whitespace: bool = true,
30 /// Determines when to emit Unicode code point literals as opposed to integer literals.
31 emit_codepoint_literals: EmitCodepointLiterals = .never,
32 /// If true, slices of `u8`s, and pointers to arrays of `u8` are serialized as containers.
33 /// Otherwise they are serialized as string literals.
34 emit_strings_as_containers: bool = false,
35 /// If false, struct fields are not written if they are equal to their default value. Comparison
36 /// is done by `std.meta.eql`.
37 emit_default_optional_fields: bool = true,
38};
39
40/// Serialize the given value as ZON.
41///
42/// It is asserted at comptime that `@TypeOf(val)` is not a recursive type.
43pub fn serialize(
44 val: anytype,
45 options: SerializeOptions,
46 writer: anytype,
47) @TypeOf(writer).Error!void {
48 var sz = serializer(writer, .{
49 .whitespace = options.whitespace,
50 });
51 try sz.value(val, .{
52 .emit_codepoint_literals = options.emit_codepoint_literals,
53 .emit_strings_as_containers = options.emit_strings_as_containers,
54 .emit_default_optional_fields = options.emit_default_optional_fields,
55 });
56}
57
58/// Like `serialize`, but recursive types are allowed.
59///
60/// Returns `error.ExceededMaxDepth` if `depth` is exceeded. Every nested value adds one to a
61/// value's depth.
62pub fn serializeMaxDepth(
63 val: anytype,
64 options: SerializeOptions,
65 writer: anytype,
66 depth: usize,
67) (@TypeOf(writer).Error || error{ExceededMaxDepth})!void {
68 var sz = serializer(writer, .{
69 .whitespace = options.whitespace,
70 });
71 try sz.valueMaxDepth(val, .{
72 .emit_codepoint_literals = options.emit_codepoint_literals,
73 .emit_strings_as_containers = options.emit_strings_as_containers,
74 .emit_default_optional_fields = options.emit_default_optional_fields,
75 }, depth);
76}
77
78/// Like `serialize`, but recursive types are allowed.
79///
80/// It is the caller's responsibility to ensure that `val` does not contain cycles.
81pub fn serializeArbitraryDepth(
82 val: anytype,
83 options: SerializeOptions,
84 writer: anytype,
85) @TypeOf(writer).Error!void {
86 var sz = serializer(writer, .{
87 .whitespace = options.whitespace,
88 });
89 try sz.valueArbitraryDepth(val, .{
90 .emit_codepoint_literals = options.emit_codepoint_literals,
91 .emit_strings_as_containers = options.emit_strings_as_containers,
92 .emit_default_optional_fields = options.emit_default_optional_fields,
93 });
94}
95
96fn typeIsRecursive(comptime T: type) bool {
97 return comptime typeIsRecursiveImpl(T, &.{});
98}
99
100fn typeIsRecursiveImpl(comptime T: type, comptime prev_visited: []const type) bool {
101 for (prev_visited) |V| {
102 if (V == T) return true;
103 }
104 const visited = prev_visited ++ .{T};
105
106 return switch (@typeInfo(T)) {
107 .pointer => |pointer| typeIsRecursiveImpl(pointer.child, visited),
108 .optional => |optional| typeIsRecursiveImpl(optional.child, visited),
109 .array => |array| typeIsRecursiveImpl(array.child, visited),
110 .vector => |vector| typeIsRecursiveImpl(vector.child, visited),
111 .@"struct" => |@"struct"| for (@"struct".fields) |field| {
112 if (typeIsRecursiveImpl(field.type, visited)) break true;
113 } else false,
114 .@"union" => |@"union"| inline for (@"union".fields) |field| {
115 if (typeIsRecursiveImpl(field.type, visited)) break true;
116 } else false,
117 else => false,
118 };
119}
120
121fn canSerializeType(T: type) bool {
122 comptime return canSerializeTypeInner(T, &.{}, false);
123}
124
125fn canSerializeTypeInner(
126 T: type,
127 /// Visited structs and unions, to avoid infinite recursion.
128 /// Tracking more types is unnecessary, and a little complex due to optional nesting.
129 visited: []const type,
130 parent_is_optional: bool,
131) bool {
132 return switch (@typeInfo(T)) {
133 .bool,
134 .int,
135 .float,
136 .comptime_float,
137 .comptime_int,
138 .null,
139 .enum_literal,
140 => true,
141
142 .noreturn,
143 .void,
144 .type,
145 .undefined,
146 .error_union,
147 .error_set,
148 .@"fn",
149 .frame,
150 .@"anyframe",
151 .@"opaque",
152 => false,
153
154 .@"enum" => |@"enum"| @"enum".is_exhaustive,
155
156 .pointer => |pointer| switch (pointer.size) {
157 .one => canSerializeTypeInner(pointer.child, visited, parent_is_optional),
158 .slice => canSerializeTypeInner(pointer.child, visited, false),
159 .many, .c => false,
160 },
161
162 .optional => |optional| if (parent_is_optional)
163 false
164 else
165 canSerializeTypeInner(optional.child, visited, true),
166
167 .array => |array| canSerializeTypeInner(array.child, visited, false),
168 .vector => |vector| canSerializeTypeInner(vector.child, visited, false),
169
170 .@"struct" => |@"struct"| {
171 for (visited) |V| if (T == V) return true;
172 const new_visited = visited ++ .{T};
173 for (@"struct".fields) |field| {
174 if (!canSerializeTypeInner(field.type, new_visited, false)) return false;
175 }
176 return true;
177 },
178 .@"union" => |@"union"| {
179 for (visited) |V| if (T == V) return true;
180 const new_visited = visited ++ .{T};
181 if (@"union".tag_type == null) return false;
182 for (@"union".fields) |field| {
183 if (field.type != void and !canSerializeTypeInner(field.type, new_visited, false)) {
184 return false;
185 }
186 }
187 return true;
188 },
189 };
190}
191
192fn isNestedOptional(T: type) bool {
193 comptime switch (@typeInfo(T)) {
194 .optional => |optional| return isNestedOptionalInner(optional.child),
195 else => return false,
196 };
197}
198
199fn isNestedOptionalInner(T: type) bool {
200 switch (@typeInfo(T)) {
201 .pointer => |pointer| {
202 if (pointer.size == .one) {
203 return isNestedOptionalInner(pointer.child);
204 } else {
205 return false;
206 }
207 },
208 .optional => return true,
209 else => return false,
210 }
211}
212
213test "std.zon stringify canSerializeType" {
214 try std.testing.expect(!comptime canSerializeType(void));
215 try std.testing.expect(!comptime canSerializeType(struct { f: [*]u8 }));
216 try std.testing.expect(!comptime canSerializeType(struct { error{foo} }));
217 try std.testing.expect(!comptime canSerializeType(union(enum) { a: void, f: [*c]u8 }));
218 try std.testing.expect(!comptime canSerializeType(@Vector(0, [*c]u8)));
219 try std.testing.expect(!comptime canSerializeType(*?[*c]u8));
220 try std.testing.expect(!comptime canSerializeType(enum(u8) { _ }));
221 try std.testing.expect(!comptime canSerializeType(union { foo: void }));
222 try std.testing.expect(comptime canSerializeType(union(enum) { foo: void }));
223 try std.testing.expect(comptime canSerializeType(comptime_float));
224 try std.testing.expect(comptime canSerializeType(comptime_int));
225 try std.testing.expect(!comptime canSerializeType(struct { comptime foo: ??u8 = null }));
226 try std.testing.expect(comptime canSerializeType(@TypeOf(.foo)));
227 try std.testing.expect(comptime canSerializeType(?u8));
228 try std.testing.expect(comptime canSerializeType(*?*u8));
229 try std.testing.expect(comptime canSerializeType(?struct {
230 foo: ?struct {
231 ?union(enum) {
232 a: ?@Vector(0, ?*u8),
233 },
234 ?struct {
235 f: ?[]?u8,
236 },
237 },
238 }));
239 try std.testing.expect(!comptime canSerializeType(??u8));
240 try std.testing.expect(!comptime canSerializeType(?*?u8));
241 try std.testing.expect(!comptime canSerializeType(*?*?*u8));
242 try std.testing.expect(comptime canSerializeType(struct { x: comptime_int = 2 }));
243 try std.testing.expect(comptime canSerializeType(struct { x: comptime_float = 2 }));
244 try std.testing.expect(comptime canSerializeType(struct { comptime_int }));
245 try std.testing.expect(comptime canSerializeType(struct { comptime x: @TypeOf(.foo) = .foo }));
246 const Recursive = struct { foo: ?*@This() };
247 try std.testing.expect(comptime canSerializeType(Recursive));
248
249 // Make sure we validate nested optional before we early out due to already having seen
250 // a type recursion!
251 try std.testing.expect(!comptime canSerializeType(struct {
252 add_to_visited: ?u8,
253 retrieve_from_visited: ??u8,
254 }));
255}
256
257test "std.zon typeIsRecursive" {
258 try std.testing.expect(!typeIsRecursive(bool));
259 try std.testing.expect(!typeIsRecursive(struct { x: i32, y: i32 }));
260 try std.testing.expect(!typeIsRecursive(struct { i32, i32 }));
261 try std.testing.expect(typeIsRecursive(struct { x: i32, y: i32, z: *@This() }));
262 try std.testing.expect(typeIsRecursive(struct {
263 a: struct {
264 const A = @This();
265 b: struct {
266 c: *struct {
267 a: ?A,
268 },
269 },
270 },
271 }));
272 try std.testing.expect(typeIsRecursive(struct {
273 a: [3]*@This(),
274 }));
275 try std.testing.expect(typeIsRecursive(struct {
276 a: union { a: i32, b: *@This() },
277 }));
278}
279
280fn checkValueDepth(val: anytype, depth: usize) error{ExceededMaxDepth}!void {
281 if (depth == 0) return error.ExceededMaxDepth;
282 const child_depth = depth - 1;
283
284 switch (@typeInfo(@TypeOf(val))) {
285 .pointer => |pointer| switch (pointer.size) {
286 .one => try checkValueDepth(val.*, child_depth),
287 .slice => for (val) |item| {
288 try checkValueDepth(item, child_depth);
289 },
290 .c, .many => {},
291 },
292 .array => for (val) |item| {
293 try checkValueDepth(item, child_depth);
294 },
295 .@"struct" => |@"struct"| inline for (@"struct".fields) |field_info| {
296 try checkValueDepth(@field(val, field_info.name), child_depth);
297 },
298 .@"union" => |@"union"| if (@"union".tag_type == null) {
299 return;
300 } else switch (val) {
301 inline else => |payload| {
302 return checkValueDepth(payload, child_depth);
303 },
304 },
305 .optional => if (val) |inner| try checkValueDepth(inner, child_depth),
306 else => {},
307 }
308}
309
310fn expectValueDepthEquals(expected: usize, value: anytype) !void {
311 try checkValueDepth(value, expected);
312 try std.testing.expectError(error.ExceededMaxDepth, checkValueDepth(value, expected - 1));
313}
314
315test "std.zon checkValueDepth" {
316 try expectValueDepthEquals(1, 10);
317 try expectValueDepthEquals(2, .{ .x = 1, .y = 2 });
318 try expectValueDepthEquals(2, .{ 1, 2 });
319 try expectValueDepthEquals(3, .{ 1, .{ 2, 3 } });
320 try expectValueDepthEquals(3, .{ .{ 1, 2 }, 3 });
321 try expectValueDepthEquals(3, .{ .x = 0, .y = 1, .z = .{ .x = 3 } });
322 try expectValueDepthEquals(3, .{ .x = 0, .y = .{ .x = 1 }, .z = 2 });
323 try expectValueDepthEquals(3, .{ .x = .{ .x = 0 }, .y = 1, .z = 2 });
324 try expectValueDepthEquals(2, @as(?u32, 1));
325 try expectValueDepthEquals(1, @as(?u32, null));
326 try expectValueDepthEquals(1, null);
327 try expectValueDepthEquals(2, &1);
328 try expectValueDepthEquals(3, &@as(?u32, 1));
329
330 const Union = union(enum) {
331 x: u32,
332 y: struct { x: u32 },
333 };
334 try expectValueDepthEquals(2, Union{ .x = 1 });
335 try expectValueDepthEquals(3, Union{ .y = .{ .x = 1 } });
336
337 const Recurse = struct { r: ?*const @This() };
338 try expectValueDepthEquals(2, Recurse{ .r = null });
339 try expectValueDepthEquals(5, Recurse{ .r = &Recurse{ .r = null } });
340 try expectValueDepthEquals(8, Recurse{ .r = &Recurse{ .r = &Recurse{ .r = null } } });
341
342 try expectValueDepthEquals(2, @as([]const u8, &.{ 1, 2, 3 }));
343 try expectValueDepthEquals(3, @as([]const []const u8, &.{&.{ 1, 2, 3 }}));
344}
345
346/// Options for `Serializer`.
347pub const SerializerOptions = struct {
348 /// If false, only syntactically necessary whitespace is emitted.
349 whitespace: bool = true,
350};
351
352/// Determines when to emit Unicode code point literals as opposed to integer literals.
353pub const EmitCodepointLiterals = enum {
354 /// Never emit Unicode code point literals.
355 never,
356 /// Emit Unicode code point literals for any `u8` in the printable ASCII range.
357 printable_ascii,
358 /// Emit Unicode code point literals for any unsigned integer with 21 bits or fewer
359 /// whose value is a valid non-surrogate code point.
360 always,
361
362 /// If the value should be emitted as a Unicode codepoint, return it as a u21.
363 fn emitAsCodepoint(self: @This(), val: anytype) ?u21 {
364 // Rule out incompatible integer types
365 switch (@typeInfo(@TypeOf(val))) {
366 .int => |int_info| if (int_info.signedness == .signed or int_info.bits > 21) {
367 return null;
368 },
369 .comptime_int => {},
370 else => comptime unreachable,
371 }
372
373 // Return null if the value shouldn't be printed as a Unicode codepoint, or the value casted
374 // to a u21 if it should.
375 switch (self) {
376 .always => {
377 const c = std.math.cast(u21, val) orelse return null;
378 if (!std.unicode.utf8ValidCodepoint(c)) return null;
379 return c;
380 },
381 .printable_ascii => {
382 const c = std.math.cast(u8, val) orelse return null;
383 if (!std.ascii.isPrint(c)) return null;
384 return c;
385 },
386 .never => {
387 return null;
388 },
389 }
390 }
391};
392
393/// Options for serialization of an individual value.
394///
395/// See `SerializeOptions` for more information on these options.
396pub const ValueOptions = struct {
397 emit_codepoint_literals: EmitCodepointLiterals = .never,
398 emit_strings_as_containers: bool = false,
399 emit_default_optional_fields: bool = true,
400};
401
402/// Options for manual serialization of container types.
403pub const SerializeContainerOptions = struct {
404 /// The whitespace style that should be used for this container. Ignored if whitespace is off.
405 whitespace_style: union(enum) {
406 /// If true, wrap every field. If false do not.
407 wrap: bool,
408 /// Automatically decide whether to wrap or not based on the number of fields. Following
409 /// the standard rule of thumb, containers with more than two fields are wrapped.
410 fields: usize,
411 } = .{ .wrap = true },
412
413 fn shouldWrap(self: SerializeContainerOptions) bool {
414 return switch (self.whitespace_style) {
415 .wrap => |wrap| wrap,
416 .fields => |fields| fields > 2,
417 };
418 }
419};
420
421/// Lower level control over serialization, you can create a new instance with `serializer`.
422///
423/// Useful when you want control over which fields are serialized, how they're represented,
424/// or want to write a ZON object that does not exist in memory.
425///
426/// You can serialize values with `value`. To serialize recursive types, the following are provided:
427/// * `valueMaxDepth`
428/// * `valueArbitraryDepth`
429///
430/// You can also serialize values using specific notations:
431/// * `int`
432/// * `float`
433/// * `codePoint`
434/// * `tuple`
435/// * `tupleMaxDepth`
436/// * `tupleArbitraryDepth`
437/// * `string`
438/// * `multilineString`
439///
440/// For manual serialization of containers, see:
441/// * `startStruct`
442/// * `startTuple`
443///
444/// # Example
445/// ```zig
446/// var sz = serializer(writer, .{});
447/// var vec2 = try sz.startStruct(.{});
448/// try vec2.field("x", 1.5, .{});
449/// try vec2.fieldPrefix();
450/// try sz.value(2.5);
451/// try vec2.finish();
452/// ```
453pub fn Serializer(Writer: type) type {
454 return struct {
455 const Self = @This();
456
457 options: SerializerOptions,
458 indent_level: u8,
459 writer: Writer,
460
461 /// Initialize a serializer.
462 fn init(writer: Writer, options: SerializerOptions) Self {
463 return .{
464 .options = options,
465 .writer = writer,
466 .indent_level = 0,
467 };
468 }
469
470 /// Serialize a value, similar to `serialize`.
471 pub fn value(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {
472 comptime assert(!typeIsRecursive(@TypeOf(val)));
473 return self.valueArbitraryDepth(val, options);
474 }
475
476 /// Serialize a value, similar to `serializeMaxDepth`.
477 pub fn valueMaxDepth(
478 self: *Self,
479 val: anytype,
480 options: ValueOptions,
481 depth: usize,
482 ) (Writer.Error || error{ExceededMaxDepth})!void {
483 try checkValueDepth(val, depth);
484 return self.valueArbitraryDepth(val, options);
485 }
486
487 /// Serialize a value, similar to `serializeArbitraryDepth`.
488 pub fn valueArbitraryDepth(
489 self: *Self,
490 val: anytype,
491 options: ValueOptions,
492 ) Writer.Error!void {
493 comptime assert(canSerializeType(@TypeOf(val)));
494 switch (@typeInfo(@TypeOf(val))) {
495 .int, .comptime_int => if (options.emit_codepoint_literals.emitAsCodepoint(val)) |c| {
496 self.codePoint(c) catch |err| switch (err) {
497 error.InvalidCodepoint => unreachable, // Already validated
498 else => |e| return e,
499 };
500 } else {
501 try self.int(val);
502 },
503 .float, .comptime_float => try self.float(val),
504 .bool, .null => try std.fmt.format(self.writer, "{}", .{val}),
505 .enum_literal => try self.ident(@tagName(val)),
506 .@"enum" => try self.ident(@tagName(val)),
507 .void => try self.writer.writeAll("{}"),
508 .pointer => |pointer| {
509 // Try to serialize as a string
510 const item: ?type = switch (@typeInfo(pointer.child)) {
511 .array => |array| array.child,
512 else => if (pointer.size == .slice) pointer.child else null,
513 };
514 if (item == u8 and
515 (pointer.sentinel() == null or pointer.sentinel() == 0) and
516 !options.emit_strings_as_containers)
517 {
518 return try self.string(val);
519 }
520
521 // Serialize as either a tuple or as the child type
522 switch (pointer.size) {
523 .slice => try self.tupleImpl(val, options),
524 .one => try self.valueArbitraryDepth(val.*, options),
525 else => comptime unreachable,
526 }
527 },
528 .array => {
529 var container = try self.startTuple(
530 .{ .whitespace_style = .{ .fields = val.len } },
531 );
532 for (val) |item_val| {
533 try container.fieldArbitraryDepth(item_val, options);
534 }
535 try container.finish();
536 },
537 .@"struct" => |@"struct"| if (@"struct".is_tuple) {
538 var container = try self.startTuple(
539 .{ .whitespace_style = .{ .fields = @"struct".fields.len } },
540 );
541 inline for (val) |field_value| {
542 try container.fieldArbitraryDepth(field_value, options);
543 }
544 try container.finish();
545 } else {
546 // Decide which fields to emit
547 const fields, const skipped: [@"struct".fields.len]bool = if (options.emit_default_optional_fields) b: {
548 break :b .{ @"struct".fields.len, @splat(false) };
549 } else b: {
550 var fields = @"struct".fields.len;
551 var skipped: [@"struct".fields.len]bool = @splat(false);
552 inline for (@"struct".fields, &skipped) |field_info, *skip| {
553 if (field_info.default_value_ptr) |ptr| {
554 const default: *const field_info.type = @ptrCast(@alignCast(ptr));
555 const field_value = @field(val, field_info.name);
556 if (std.meta.eql(field_value, default.*)) {
557 skip.* = true;
558 fields -= 1;
559 }
560 }
561 }
562 break :b .{ fields, skipped };
563 };
564
565 // Emit those fields
566 var container = try self.startStruct(
567 .{ .whitespace_style = .{ .fields = fields } },
568 );
569 inline for (@"struct".fields, skipped) |field_info, skip| {
570 if (!skip) {
571 try container.fieldArbitraryDepth(
572 field_info.name,
573 @field(val, field_info.name),
574 options,
575 );
576 }
577 }
578 try container.finish();
579 },
580 .@"union" => |@"union"| {
581 comptime assert(@"union".tag_type != null);
582 var container = try self.startStruct(.{ .whitespace_style = .{ .fields = 1 } });
583 switch (val) {
584 inline else => |pl, tag| try container.fieldArbitraryDepth(
585 @tagName(tag),
586 pl,
587 options,
588 ),
589 }
590 try container.finish();
591 },
592 .optional => if (val) |inner| {
593 try self.valueArbitraryDepth(inner, options);
594 } else {
595 try self.writer.writeAll("null");
596 },
597 .vector => |vector| {
598 var container = try self.startTuple(
599 .{ .whitespace_style = .{ .fields = vector.len } },
600 );
601 for (0..vector.len) |i| {
602 try container.fieldArbitraryDepth(val[i], options);
603 }
604 try container.finish();
605 },
606
607 else => comptime unreachable,
608 }
609 }
610
611 /// Serialize an integer.
612 pub fn int(self: *Self, val: anytype) Writer.Error!void {
613 try std.fmt.formatInt(val, 10, .lower, .{}, self.writer);
614 }
615
616 /// Serialize a float.
617 pub fn float(self: *Self, val: anytype) Writer.Error!void {
618 switch (@typeInfo(@TypeOf(val))) {
619 .float => if (std.math.isNan(val)) {
620 return self.writer.writeAll("nan");
621 } else if (std.math.isPositiveInf(val)) {
622 return self.writer.writeAll("inf");
623 } else if (std.math.isNegativeInf(val)) {
624 return self.writer.writeAll("-inf");
625 } else {
626 try std.fmt.format(self.writer, "{d}", .{val});
627 },
628 .comptime_float => try std.fmt.format(self.writer, "{d}", .{val}),
629 else => comptime unreachable,
630 }
631 }
632
633 /// Serialize `name` as an identifier prefixed with `.`.
634 ///
635 /// Escapes the identifier if necessary.
636 pub fn ident(self: *Self, name: []const u8) Writer.Error!void {
637 try self.writer.print(".{p_}", .{std.zig.fmtId(name)});
638 }
639
640 /// Serialize `val` as a Unicode codepoint.
641 ///
642 /// Returns `error.InvalidCodepoint` if `val` is not a valid Unicode codepoint.
643 pub fn codePoint(
644 self: *Self,
645 val: u21,
646 ) (Writer.Error || error{InvalidCodepoint})!void {
647 var buf: [8]u8 = undefined;
648 const len = std.unicode.utf8Encode(val, &buf) catch return error.InvalidCodepoint;
649 const str = buf[0..len];
650 try std.fmt.format(self.writer, "'{'}'", .{std.zig.fmtEscapes(str)});
651 }
652
653 /// Like `value`, but always serializes `val` as a tuple.
654 ///
655 /// Will fail at comptime if `val` is not a tuple, array, pointer to an array, or slice.
656 pub fn tuple(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {
657 comptime assert(!typeIsRecursive(@TypeOf(val)));
658 try self.tupleArbitraryDepth(val, options);
659 }
660
661 /// Like `tuple`, but recursive types are allowed.
662 ///
663 /// Returns `error.ExceededMaxDepth` if `depth` is exceeded.
664 pub fn tupleMaxDepth(
665 self: *Self,
666 val: anytype,
667 options: ValueOptions,
668 depth: usize,
669 ) (Writer.Error || error{ExceededMaxDepth})!void {
670 try checkValueDepth(val, depth);
671 try self.tupleArbitraryDepth(val, options);
672 }
673
674 /// Like `tuple`, but recursive types are allowed.
675 ///
676 /// It is the caller's responsibility to ensure that `val` does not contain cycles.
677 pub fn tupleArbitraryDepth(
678 self: *Self,
679 val: anytype,
680 options: ValueOptions,
681 ) Writer.Error!void {
682 try self.tupleImpl(val, options);
683 }
684
685 fn tupleImpl(self: *Self, val: anytype, options: ValueOptions) Writer.Error!void {
686 comptime assert(canSerializeType(@TypeOf(val)));
687 switch (@typeInfo(@TypeOf(val))) {
688 .@"struct" => {
689 var container = try self.startTuple(.{ .whitespace_style = .{ .fields = val.len } });
690 inline for (val) |item_val| {
691 try container.fieldArbitraryDepth(item_val, options);
692 }
693 try container.finish();
694 },
695 .pointer, .array => {
696 var container = try self.startTuple(.{ .whitespace_style = .{ .fields = val.len } });
697 for (val) |item_val| {
698 try container.fieldArbitraryDepth(item_val, options);
699 }
700 try container.finish();
701 },
702 else => comptime unreachable,
703 }
704 }
705
706 /// Like `value`, but always serializes `val` as a string.
707 pub fn string(self: *Self, val: []const u8) Writer.Error!void {
708 try std.fmt.format(self.writer, "\"{}\"", .{std.zig.fmtEscapes(val)});
709 }
710
711 /// Options for formatting multiline strings.
712 pub const MultilineStringOptions = struct {
713 /// If top level is true, whitespace before and after the multiline string is elided.
714 /// If it is true, a newline is printed, then the value, followed by a newline, and if
715 /// whitespace is true any necessary indentation follows.
716 top_level: bool = false,
717 };
718
719 /// Like `value`, but always serializes to a multiline string literal.
720 ///
721 /// Returns `error.InnerCarriageReturn` if `val` contains a CR not followed by a newline,
722 /// since multiline strings cannot represent CR without a following newline.
723 pub fn multilineString(
724 self: *Self,
725 val: []const u8,
726 options: MultilineStringOptions,
727 ) (Writer.Error || error{InnerCarriageReturn})!void {
728 // Make sure the string does not contain any carriage returns not followed by a newline
729 var i: usize = 0;
730 while (i < val.len) : (i += 1) {
731 if (val[i] == '\r') {
732 if (i + 1 < val.len) {
733 if (val[i + 1] == '\n') {
734 i += 1;
735 continue;
736 }
737 }
738 return error.InnerCarriageReturn;
739 }
740 }
741
742 if (!options.top_level) {
743 try self.newline();
744 try self.indent();
745 }
746
747 try self.writer.writeAll("\\\\");
748 for (val) |c| {
749 if (c != '\r') {
750 try self.writer.writeByte(c); // We write newlines here even if whitespace off
751 if (c == '\n') {
752 try self.indent();
753 try self.writer.writeAll("\\\\");
754 }
755 }
756 }
757
758 if (!options.top_level) {
759 try self.writer.writeByte('\n'); // Even if whitespace off
760 try self.indent();
761 }
762 }
763
764 /// Create a `Struct` for writing ZON structs field by field.
765 pub fn startStruct(
766 self: *Self,
767 options: SerializeContainerOptions,
768 ) Writer.Error!Struct {
769 return Struct.start(self, options);
770 }
771
772 /// Creates a `Tuple` for writing ZON tuples field by field.
773 pub fn startTuple(
774 self: *Self,
775 options: SerializeContainerOptions,
776 ) Writer.Error!Tuple {
777 return Tuple.start(self, options);
778 }
779
780 fn indent(self: *Self) Writer.Error!void {
781 if (self.options.whitespace) {
782 try self.writer.writeByteNTimes(' ', 4 * self.indent_level);
783 }
784 }
785
786 fn newline(self: *Self) Writer.Error!void {
787 if (self.options.whitespace) {
788 try self.writer.writeByte('\n');
789 }
790 }
791
792 fn newlineOrSpace(self: *Self, len: usize) Writer.Error!void {
793 if (self.containerShouldWrap(len)) {
794 try self.newline();
795 } else {
796 try self.space();
797 }
798 }
799
800 fn space(self: *Self) Writer.Error!void {
801 if (self.options.whitespace) {
802 try self.writer.writeByte(' ');
803 }
804 }
805
806 /// Writes ZON tuples field by field.
807 pub const Tuple = struct {
808 container: Container,
809
810 fn start(parent: *Self, options: SerializeContainerOptions) Writer.Error!Tuple {
811 return .{
812 .container = try Container.start(parent, .anon, options),
813 };
814 }
815
816 /// Finishes serializing the tuple.
817 ///
818 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
819 pub fn finish(self: *Tuple) Writer.Error!void {
820 try self.container.finish();
821 self.* = undefined;
822 }
823
824 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
825 pub fn field(
826 self: *Tuple,
827 val: anytype,
828 options: ValueOptions,
829 ) Writer.Error!void {
830 try self.container.field(null, val, options);
831 }
832
833 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
834 pub fn fieldMaxDepth(
835 self: *Tuple,
836 val: anytype,
837 options: ValueOptions,
838 depth: usize,
839 ) (Writer.Error || error{ExceededMaxDepth})!void {
840 try self.container.fieldMaxDepth(null, val, options, depth);
841 }
842
843 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
844 /// `valueArbitraryDepth`.
845 pub fn fieldArbitraryDepth(
846 self: *Tuple,
847 val: anytype,
848 options: ValueOptions,
849 ) Writer.Error!void {
850 try self.container.fieldArbitraryDepth(null, val, options);
851 }
852
853 /// Print a field prefix. This prints any necessary commas, and whitespace as
854 /// configured. Useful if you want to serialize the field value yourself.
855 pub fn fieldPrefix(self: *Tuple) Writer.Error!void {
856 try self.container.fieldPrefix(null);
857 }
858 };
859
860 /// Writes ZON structs field by field.
861 pub const Struct = struct {
862 container: Container,
863
864 fn start(parent: *Self, options: SerializeContainerOptions) Writer.Error!Struct {
865 return .{
866 .container = try Container.start(parent, .named, options),
867 };
868 }
869
870 /// Finishes serializing the struct.
871 ///
872 /// Prints a trailing comma as configured when appropriate, and the closing bracket.
873 pub fn finish(self: *Struct) Writer.Error!void {
874 try self.container.finish();
875 self.* = undefined;
876 }
877
878 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `value`.
879 pub fn field(
880 self: *Struct,
881 name: []const u8,
882 val: anytype,
883 options: ValueOptions,
884 ) Writer.Error!void {
885 try self.container.field(name, val, options);
886 }
887
888 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by `valueMaxDepth`.
889 pub fn fieldMaxDepth(
890 self: *Struct,
891 name: []const u8,
892 val: anytype,
893 options: ValueOptions,
894 depth: usize,
895 ) (Writer.Error || error{ExceededMaxDepth})!void {
896 try self.container.fieldMaxDepth(name, val, options, depth);
897 }
898
899 /// Serialize a field. Equivalent to calling `fieldPrefix` followed by
900 /// `valueArbitraryDepth`.
901 pub fn fieldArbitraryDepth(
902 self: *Struct,
903 name: []const u8,
904 val: anytype,
905 options: ValueOptions,
906 ) Writer.Error!void {
907 try self.container.fieldArbitraryDepth(name, val, options);
908 }
909
910 /// Print a field prefix. This prints any necessary commas, the field name (escaped if
911 /// necessary) and whitespace as configured. Useful if you want to serialize the field
912 /// value yourself.
913 pub fn fieldPrefix(self: *Struct, name: []const u8) Writer.Error!void {
914 try self.container.fieldPrefix(name);
915 }
916 };
917
918 const Container = struct {
919 const FieldStyle = enum { named, anon };
920
921 serializer: *Self,
922 field_style: FieldStyle,
923 options: SerializeContainerOptions,
924 empty: bool,
925
926 fn start(
927 sz: *Self,
928 field_style: FieldStyle,
929 options: SerializeContainerOptions,
930 ) Writer.Error!Container {
931 if (options.shouldWrap()) sz.indent_level +|= 1;
932 try sz.writer.writeAll(".{");
933 return .{
934 .serializer = sz,
935 .field_style = field_style,
936 .options = options,
937 .empty = true,
938 };
939 }
940
941 fn finish(self: *Container) Writer.Error!void {
942 if (self.options.shouldWrap()) self.serializer.indent_level -|= 1;
943 if (!self.empty) {
944 if (self.options.shouldWrap()) {
945 if (self.serializer.options.whitespace) {
946 try self.serializer.writer.writeByte(',');
947 }
948 try self.serializer.newline();
949 try self.serializer.indent();
950 } else if (!self.shouldElideSpaces()) {
951 try self.serializer.space();
952 }
953 }
954 try self.serializer.writer.writeByte('}');
955 self.* = undefined;
956 }
957
958 fn fieldPrefix(self: *Container, name: ?[]const u8) Writer.Error!void {
959 if (!self.empty) {
960 try self.serializer.writer.writeByte(',');
961 }
962 self.empty = false;
963 if (self.options.shouldWrap()) {
964 try self.serializer.newline();
965 } else if (!self.shouldElideSpaces()) {
966 try self.serializer.space();
967 }
968 if (self.options.shouldWrap()) try self.serializer.indent();
969 if (name) |n| {
970 try self.serializer.ident(n);
971 try self.serializer.space();
972 try self.serializer.writer.writeByte('=');
973 try self.serializer.space();
974 }
975 }
976
977 fn field(
978 self: *Container,
979 name: ?[]const u8,
980 val: anytype,
981 options: ValueOptions,
982 ) Writer.Error!void {
983 comptime assert(!typeIsRecursive(@TypeOf(val)));
984 try self.fieldArbitraryDepth(name, val, options);
985 }
986
987 fn fieldMaxDepth(
988 self: *Container,
989 name: ?[]const u8,
990 val: anytype,
991 options: ValueOptions,
992 depth: usize,
993 ) (Writer.Error || error{ExceededMaxDepth})!void {
994 try checkValueDepth(val, depth);
995 try self.fieldArbitraryDepth(name, val, options);
996 }
997
998 fn fieldArbitraryDepth(
999 self: *Container,
1000 name: ?[]const u8,
1001 val: anytype,
1002 options: ValueOptions,
1003 ) Writer.Error!void {
1004 try self.fieldPrefix(name);
1005 try self.serializer.valueArbitraryDepth(val, options);
1006 }
1007
1008 fn shouldElideSpaces(self: *const Container) bool {
1009 return switch (self.options.whitespace_style) {
1010 .fields => |fields| self.field_style != .named and fields == 1,
1011 else => false,
1012 };
1013 }
1014 };
1015 };
1016}
1017
1018/// Creates a new `Serializer` with the given writer and options.
1019pub fn serializer(writer: anytype, options: SerializerOptions) Serializer(@TypeOf(writer)) {
1020 return .init(writer, options);
1021}
1022
1023fn expectSerializeEqual(
1024 expected: []const u8,
1025 value: anytype,
1026 options: SerializeOptions,
1027) !void {
1028 var buf = std.ArrayList(u8).init(std.testing.allocator);
1029 defer buf.deinit();
1030 try serialize(value, options, buf.writer());
1031 try std.testing.expectEqualStrings(expected, buf.items);
1032}
1033
1034test "std.zon stringify whitespace, high level API" {
1035 try expectSerializeEqual(".{}", .{}, .{});
1036 try expectSerializeEqual(".{}", .{}, .{ .whitespace = false });
1037
1038 try expectSerializeEqual(".{1}", .{1}, .{});
1039 try expectSerializeEqual(".{1}", .{1}, .{ .whitespace = false });
1040
1041 try expectSerializeEqual(".{1}", @as([1]u32, .{1}), .{});
1042 try expectSerializeEqual(".{1}", @as([1]u32, .{1}), .{ .whitespace = false });
1043
1044 try expectSerializeEqual(".{1}", @as([]const u32, &.{1}), .{});
1045 try expectSerializeEqual(".{1}", @as([]const u32, &.{1}), .{ .whitespace = false });
1046
1047 try expectSerializeEqual(".{ .x = 1 }", .{ .x = 1 }, .{});
1048 try expectSerializeEqual(".{.x=1}", .{ .x = 1 }, .{ .whitespace = false });
1049
1050 try expectSerializeEqual(".{ 1, 2 }", .{ 1, 2 }, .{});
1051 try expectSerializeEqual(".{1,2}", .{ 1, 2 }, .{ .whitespace = false });
1052
1053 try expectSerializeEqual(".{ 1, 2 }", @as([2]u32, .{ 1, 2 }), .{});
1054 try expectSerializeEqual(".{1,2}", @as([2]u32, .{ 1, 2 }), .{ .whitespace = false });
1055
1056 try expectSerializeEqual(".{ 1, 2 }", @as([]const u32, &.{ 1, 2 }), .{});
1057 try expectSerializeEqual(".{1,2}", @as([]const u32, &.{ 1, 2 }), .{ .whitespace = false });
1058
1059 try expectSerializeEqual(".{ .x = 1, .y = 2 }", .{ .x = 1, .y = 2 }, .{});
1060 try expectSerializeEqual(".{.x=1,.y=2}", .{ .x = 1, .y = 2 }, .{ .whitespace = false });
1061
1062 try expectSerializeEqual(
1063 \\.{
1064 \\ 1,
1065 \\ 2,
1066 \\ 3,
1067 \\}
1068 , .{ 1, 2, 3 }, .{});
1069 try expectSerializeEqual(".{1,2,3}", .{ 1, 2, 3 }, .{ .whitespace = false });
1070
1071 try expectSerializeEqual(
1072 \\.{
1073 \\ 1,
1074 \\ 2,
1075 \\ 3,
1076 \\}
1077 , @as([3]u32, .{ 1, 2, 3 }), .{});
1078 try expectSerializeEqual(".{1,2,3}", @as([3]u32, .{ 1, 2, 3 }), .{ .whitespace = false });
1079
1080 try expectSerializeEqual(
1081 \\.{
1082 \\ 1,
1083 \\ 2,
1084 \\ 3,
1085 \\}
1086 , @as([]const u32, &.{ 1, 2, 3 }), .{});
1087 try expectSerializeEqual(
1088 ".{1,2,3}",
1089 @as([]const u32, &.{ 1, 2, 3 }),
1090 .{ .whitespace = false },
1091 );
1092
1093 try expectSerializeEqual(
1094 \\.{
1095 \\ .x = 1,
1096 \\ .y = 2,
1097 \\ .z = 3,
1098 \\}
1099 , .{ .x = 1, .y = 2, .z = 3 }, .{});
1100 try expectSerializeEqual(
1101 ".{.x=1,.y=2,.z=3}",
1102 .{ .x = 1, .y = 2, .z = 3 },
1103 .{ .whitespace = false },
1104 );
1105
1106 const Union = union(enum) { a: bool, b: i32, c: u8 };
1107
1108 try expectSerializeEqual(".{ .b = 1 }", Union{ .b = 1 }, .{});
1109 try expectSerializeEqual(".{.b=1}", Union{ .b = 1 }, .{ .whitespace = false });
1110
1111 // Nested indentation where outer object doesn't wrap
1112 try expectSerializeEqual(
1113 \\.{ .inner = .{
1114 \\ 1,
1115 \\ 2,
1116 \\ 3,
1117 \\} }
1118 , .{ .inner = .{ 1, 2, 3 } }, .{});
1119}
1120
1121test "std.zon stringify whitespace, low level API" {
1122 var buf = std.ArrayList(u8).init(std.testing.allocator);
1123 defer buf.deinit();
1124 var sz = serializer(buf.writer(), .{});
1125
1126 inline for (.{ true, false }) |whitespace| {
1127 sz.options = .{ .whitespace = whitespace };
1128
1129 // Empty containers
1130 {
1131 var container = try sz.startStruct(.{});
1132 try container.finish();
1133 try std.testing.expectEqualStrings(".{}", buf.items);
1134 buf.clearRetainingCapacity();
1135 }
1136
1137 {
1138 var container = try sz.startTuple(.{});
1139 try container.finish();
1140 try std.testing.expectEqualStrings(".{}", buf.items);
1141 buf.clearRetainingCapacity();
1142 }
1143
1144 {
1145 var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } });
1146 try container.finish();
1147 try std.testing.expectEqualStrings(".{}", buf.items);
1148 buf.clearRetainingCapacity();
1149 }
1150
1151 {
1152 var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } });
1153 try container.finish();
1154 try std.testing.expectEqualStrings(".{}", buf.items);
1155 buf.clearRetainingCapacity();
1156 }
1157
1158 {
1159 var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 0 } });
1160 try container.finish();
1161 try std.testing.expectEqualStrings(".{}", buf.items);
1162 buf.clearRetainingCapacity();
1163 }
1164
1165 {
1166 var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 0 } });
1167 try container.finish();
1168 try std.testing.expectEqualStrings(".{}", buf.items);
1169 buf.clearRetainingCapacity();
1170 }
1171
1172 // Size 1
1173 {
1174 var container = try sz.startStruct(.{});
1175 try container.field("a", 1, .{});
1176 try container.finish();
1177 if (whitespace) {
1178 try std.testing.expectEqualStrings(
1179 \\.{
1180 \\ .a = 1,
1181 \\}
1182 , buf.items);
1183 } else {
1184 try std.testing.expectEqualStrings(".{.a=1}", buf.items);
1185 }
1186 buf.clearRetainingCapacity();
1187 }
1188
1189 {
1190 var container = try sz.startTuple(.{});
1191 try container.field(1, .{});
1192 try container.finish();
1193 if (whitespace) {
1194 try std.testing.expectEqualStrings(
1195 \\.{
1196 \\ 1,
1197 \\}
1198 , buf.items);
1199 } else {
1200 try std.testing.expectEqualStrings(".{1}", buf.items);
1201 }
1202 buf.clearRetainingCapacity();
1203 }
1204
1205 {
1206 var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } });
1207 try container.field("a", 1, .{});
1208 try container.finish();
1209 if (whitespace) {
1210 try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items);
1211 } else {
1212 try std.testing.expectEqualStrings(".{.a=1}", buf.items);
1213 }
1214 buf.clearRetainingCapacity();
1215 }
1216
1217 {
1218 // We get extra spaces here, since we didn't know up front that there would only be one
1219 // field.
1220 var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } });
1221 try container.field(1, .{});
1222 try container.finish();
1223 if (whitespace) {
1224 try std.testing.expectEqualStrings(".{ 1 }", buf.items);
1225 } else {
1226 try std.testing.expectEqualStrings(".{1}", buf.items);
1227 }
1228 buf.clearRetainingCapacity();
1229 }
1230
1231 {
1232 var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 1 } });
1233 try container.field("a", 1, .{});
1234 try container.finish();
1235 if (whitespace) {
1236 try std.testing.expectEqualStrings(".{ .a = 1 }", buf.items);
1237 } else {
1238 try std.testing.expectEqualStrings(".{.a=1}", buf.items);
1239 }
1240 buf.clearRetainingCapacity();
1241 }
1242
1243 {
1244 var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 1 } });
1245 try container.field(1, .{});
1246 try container.finish();
1247 try std.testing.expectEqualStrings(".{1}", buf.items);
1248 buf.clearRetainingCapacity();
1249 }
1250
1251 // Size 2
1252 {
1253 var container = try sz.startStruct(.{});
1254 try container.field("a", 1, .{});
1255 try container.field("b", 2, .{});
1256 try container.finish();
1257 if (whitespace) {
1258 try std.testing.expectEqualStrings(
1259 \\.{
1260 \\ .a = 1,
1261 \\ .b = 2,
1262 \\}
1263 , buf.items);
1264 } else {
1265 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);
1266 }
1267 buf.clearRetainingCapacity();
1268 }
1269
1270 {
1271 var container = try sz.startTuple(.{});
1272 try container.field(1, .{});
1273 try container.field(2, .{});
1274 try container.finish();
1275 if (whitespace) {
1276 try std.testing.expectEqualStrings(
1277 \\.{
1278 \\ 1,
1279 \\ 2,
1280 \\}
1281 , buf.items);
1282 } else {
1283 try std.testing.expectEqualStrings(".{1,2}", buf.items);
1284 }
1285 buf.clearRetainingCapacity();
1286 }
1287
1288 {
1289 var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } });
1290 try container.field("a", 1, .{});
1291 try container.field("b", 2, .{});
1292 try container.finish();
1293 if (whitespace) {
1294 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items);
1295 } else {
1296 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);
1297 }
1298 buf.clearRetainingCapacity();
1299 }
1300
1301 {
1302 var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } });
1303 try container.field(1, .{});
1304 try container.field(2, .{});
1305 try container.finish();
1306 if (whitespace) {
1307 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
1308 } else {
1309 try std.testing.expectEqualStrings(".{1,2}", buf.items);
1310 }
1311 buf.clearRetainingCapacity();
1312 }
1313
1314 {
1315 var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 2 } });
1316 try container.field("a", 1, .{});
1317 try container.field("b", 2, .{});
1318 try container.finish();
1319 if (whitespace) {
1320 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2 }", buf.items);
1321 } else {
1322 try std.testing.expectEqualStrings(".{.a=1,.b=2}", buf.items);
1323 }
1324 buf.clearRetainingCapacity();
1325 }
1326
1327 {
1328 var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 2 } });
1329 try container.field(1, .{});
1330 try container.field(2, .{});
1331 try container.finish();
1332 if (whitespace) {
1333 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
1334 } else {
1335 try std.testing.expectEqualStrings(".{1,2}", buf.items);
1336 }
1337 buf.clearRetainingCapacity();
1338 }
1339
1340 // Size 3
1341 {
1342 var container = try sz.startStruct(.{});
1343 try container.field("a", 1, .{});
1344 try container.field("b", 2, .{});
1345 try container.field("c", 3, .{});
1346 try container.finish();
1347 if (whitespace) {
1348 try std.testing.expectEqualStrings(
1349 \\.{
1350 \\ .a = 1,
1351 \\ .b = 2,
1352 \\ .c = 3,
1353 \\}
1354 , buf.items);
1355 } else {
1356 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items);
1357 }
1358 buf.clearRetainingCapacity();
1359 }
1360
1361 {
1362 var container = try sz.startTuple(.{});
1363 try container.field(1, .{});
1364 try container.field(2, .{});
1365 try container.field(3, .{});
1366 try container.finish();
1367 if (whitespace) {
1368 try std.testing.expectEqualStrings(
1369 \\.{
1370 \\ 1,
1371 \\ 2,
1372 \\ 3,
1373 \\}
1374 , buf.items);
1375 } else {
1376 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);
1377 }
1378 buf.clearRetainingCapacity();
1379 }
1380
1381 {
1382 var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } });
1383 try container.field("a", 1, .{});
1384 try container.field("b", 2, .{});
1385 try container.field("c", 3, .{});
1386 try container.finish();
1387 if (whitespace) {
1388 try std.testing.expectEqualStrings(".{ .a = 1, .b = 2, .c = 3 }", buf.items);
1389 } else {
1390 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items);
1391 }
1392 buf.clearRetainingCapacity();
1393 }
1394
1395 {
1396 var container = try sz.startTuple(.{ .whitespace_style = .{ .wrap = false } });
1397 try container.field(1, .{});
1398 try container.field(2, .{});
1399 try container.field(3, .{});
1400 try container.finish();
1401 if (whitespace) {
1402 try std.testing.expectEqualStrings(".{ 1, 2, 3 }", buf.items);
1403 } else {
1404 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);
1405 }
1406 buf.clearRetainingCapacity();
1407 }
1408
1409 {
1410 var container = try sz.startStruct(.{ .whitespace_style = .{ .fields = 3 } });
1411 try container.field("a", 1, .{});
1412 try container.field("b", 2, .{});
1413 try container.field("c", 3, .{});
1414 try container.finish();
1415 if (whitespace) {
1416 try std.testing.expectEqualStrings(
1417 \\.{
1418 \\ .a = 1,
1419 \\ .b = 2,
1420 \\ .c = 3,
1421 \\}
1422 , buf.items);
1423 } else {
1424 try std.testing.expectEqualStrings(".{.a=1,.b=2,.c=3}", buf.items);
1425 }
1426 buf.clearRetainingCapacity();
1427 }
1428
1429 {
1430 var container = try sz.startTuple(.{ .whitespace_style = .{ .fields = 3 } });
1431 try container.field(1, .{});
1432 try container.field(2, .{});
1433 try container.field(3, .{});
1434 try container.finish();
1435 if (whitespace) {
1436 try std.testing.expectEqualStrings(
1437 \\.{
1438 \\ 1,
1439 \\ 2,
1440 \\ 3,
1441 \\}
1442 , buf.items);
1443 } else {
1444 try std.testing.expectEqualStrings(".{1,2,3}", buf.items);
1445 }
1446 buf.clearRetainingCapacity();
1447 }
1448
1449 // Nested objects where the outer container doesn't wrap but the inner containers do
1450 {
1451 var container = try sz.startStruct(.{ .whitespace_style = .{ .wrap = false } });
1452 try container.field("first", .{ 1, 2, 3 }, .{});
1453 try container.field("second", .{ 4, 5, 6 }, .{});
1454 try container.finish();
1455 if (whitespace) {
1456 try std.testing.expectEqualStrings(
1457 \\.{ .first = .{
1458 \\ 1,
1459 \\ 2,
1460 \\ 3,
1461 \\}, .second = .{
1462 \\ 4,
1463 \\ 5,
1464 \\ 6,
1465 \\} }
1466 , buf.items);
1467 } else {
1468 try std.testing.expectEqualStrings(
1469 ".{.first=.{1,2,3},.second=.{4,5,6}}",
1470 buf.items,
1471 );
1472 }
1473 buf.clearRetainingCapacity();
1474 }
1475 }
1476}
1477
1478test "std.zon stringify utf8 codepoints" {
1479 var buf = std.ArrayList(u8).init(std.testing.allocator);
1480 defer buf.deinit();
1481 var sz = serializer(buf.writer(), .{});
1482
1483 // Printable ASCII
1484 try sz.int('a');
1485 try std.testing.expectEqualStrings("97", buf.items);
1486 buf.clearRetainingCapacity();
1487
1488 try sz.codePoint('a');
1489 try std.testing.expectEqualStrings("'a'", buf.items);
1490 buf.clearRetainingCapacity();
1491
1492 try sz.value('a', .{ .emit_codepoint_literals = .always });
1493 try std.testing.expectEqualStrings("'a'", buf.items);
1494 buf.clearRetainingCapacity();
1495
1496 try sz.value('a', .{ .emit_codepoint_literals = .printable_ascii });
1497 try std.testing.expectEqualStrings("'a'", buf.items);
1498 buf.clearRetainingCapacity();
1499
1500 try sz.value('a', .{ .emit_codepoint_literals = .never });
1501 try std.testing.expectEqualStrings("97", buf.items);
1502 buf.clearRetainingCapacity();
1503
1504 // Short escaped codepoint
1505 try sz.int('\n');
1506 try std.testing.expectEqualStrings("10", buf.items);
1507 buf.clearRetainingCapacity();
1508
1509 try sz.codePoint('\n');
1510 try std.testing.expectEqualStrings("'\\n'", buf.items);
1511 buf.clearRetainingCapacity();
1512
1513 try sz.value('\n', .{ .emit_codepoint_literals = .always });
1514 try std.testing.expectEqualStrings("'\\n'", buf.items);
1515 buf.clearRetainingCapacity();
1516
1517 try sz.value('\n', .{ .emit_codepoint_literals = .printable_ascii });
1518 try std.testing.expectEqualStrings("10", buf.items);
1519 buf.clearRetainingCapacity();
1520
1521 try sz.value('\n', .{ .emit_codepoint_literals = .never });
1522 try std.testing.expectEqualStrings("10", buf.items);
1523 buf.clearRetainingCapacity();
1524
1525 // Large codepoint
1526 try sz.int('âš¡');
1527 try std.testing.expectEqualStrings("9889", buf.items);
1528 buf.clearRetainingCapacity();
1529
1530 try sz.codePoint('âš¡');
1531 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items);
1532 buf.clearRetainingCapacity();
1533
1534 try sz.value('âš¡', .{ .emit_codepoint_literals = .always });
1535 try std.testing.expectEqualStrings("'\\xe2\\x9a\\xa1'", buf.items);
1536 buf.clearRetainingCapacity();
1537
1538 try sz.value('âš¡', .{ .emit_codepoint_literals = .printable_ascii });
1539 try std.testing.expectEqualStrings("9889", buf.items);
1540 buf.clearRetainingCapacity();
1541
1542 try sz.value('âš¡', .{ .emit_codepoint_literals = .never });
1543 try std.testing.expectEqualStrings("9889", buf.items);
1544 buf.clearRetainingCapacity();
1545
1546 // Invalid codepoint
1547 try std.testing.expectError(error.InvalidCodepoint, sz.codePoint(0x110000 + 1));
1548
1549 try sz.int(0x110000 + 1);
1550 try std.testing.expectEqualStrings("1114113", buf.items);
1551 buf.clearRetainingCapacity();
1552
1553 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .always });
1554 try std.testing.expectEqualStrings("1114113", buf.items);
1555 buf.clearRetainingCapacity();
1556
1557 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .printable_ascii });
1558 try std.testing.expectEqualStrings("1114113", buf.items);
1559 buf.clearRetainingCapacity();
1560
1561 try sz.value(0x110000 + 1, .{ .emit_codepoint_literals = .never });
1562 try std.testing.expectEqualStrings("1114113", buf.items);
1563 buf.clearRetainingCapacity();
1564
1565 // Valid codepoint, not a codepoint type
1566 try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .always });
1567 try std.testing.expectEqualStrings("97", buf.items);
1568 buf.clearRetainingCapacity();
1569
1570 try sz.value(@as(u22, 'a'), .{ .emit_codepoint_literals = .printable_ascii });
1571 try std.testing.expectEqualStrings("97", buf.items);
1572 buf.clearRetainingCapacity();
1573
1574 try sz.value(@as(i32, 'a'), .{ .emit_codepoint_literals = .never });
1575 try std.testing.expectEqualStrings("97", buf.items);
1576 buf.clearRetainingCapacity();
1577
1578 // Make sure value options are passed to children
1579 try sz.value(.{ .c = 'âš¡' }, .{ .emit_codepoint_literals = .always });
1580 try std.testing.expectEqualStrings(".{ .c = '\\xe2\\x9a\\xa1' }", buf.items);
1581 buf.clearRetainingCapacity();
1582
1583 try sz.value(.{ .c = 'âš¡' }, .{ .emit_codepoint_literals = .never });
1584 try std.testing.expectEqualStrings(".{ .c = 9889 }", buf.items);
1585 buf.clearRetainingCapacity();
1586}
1587
1588test "std.zon stringify strings" {
1589 var buf = std.ArrayList(u8).init(std.testing.allocator);
1590 defer buf.deinit();
1591 var sz = serializer(buf.writer(), .{});
1592
1593 // Minimal case
1594 try sz.string("abcâš¡\n");
1595 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items);
1596 buf.clearRetainingCapacity();
1597
1598 try sz.tuple("abcâš¡\n", .{});
1599 try std.testing.expectEqualStrings(
1600 \\.{
1601 \\ 97,
1602 \\ 98,
1603 \\ 99,
1604 \\ 226,
1605 \\ 154,
1606 \\ 161,
1607 \\ 10,
1608 \\}
1609 , buf.items);
1610 buf.clearRetainingCapacity();
1611
1612 try sz.value("abcâš¡\n", .{});
1613 try std.testing.expectEqualStrings("\"abc\\xe2\\x9a\\xa1\\n\"", buf.items);
1614 buf.clearRetainingCapacity();
1615
1616 try sz.value("abcâš¡\n", .{ .emit_strings_as_containers = true });
1617 try std.testing.expectEqualStrings(
1618 \\.{
1619 \\ 97,
1620 \\ 98,
1621 \\ 99,
1622 \\ 226,
1623 \\ 154,
1624 \\ 161,
1625 \\ 10,
1626 \\}
1627 , buf.items);
1628 buf.clearRetainingCapacity();
1629
1630 // Value options are inherited by children
1631 try sz.value(.{ .str = "abc" }, .{});
1632 try std.testing.expectEqualStrings(".{ .str = \"abc\" }", buf.items);
1633 buf.clearRetainingCapacity();
1634
1635 try sz.value(.{ .str = "abc" }, .{ .emit_strings_as_containers = true });
1636 try std.testing.expectEqualStrings(
1637 \\.{ .str = .{
1638 \\ 97,
1639 \\ 98,
1640 \\ 99,
1641 \\} }
1642 , buf.items);
1643 buf.clearRetainingCapacity();
1644
1645 // Arrays (rather than pointers to arrays) of u8s are not considered strings, so that data can
1646 // round trip correctly.
1647 try sz.value("abc".*, .{});
1648 try std.testing.expectEqualStrings(
1649 \\.{
1650 \\ 97,
1651 \\ 98,
1652 \\ 99,
1653 \\}
1654 , buf.items);
1655 buf.clearRetainingCapacity();
1656}
1657
1658test "std.zon stringify multiline strings" {
1659 var buf = std.ArrayList(u8).init(std.testing.allocator);
1660 defer buf.deinit();
1661 var sz = serializer(buf.writer(), .{});
1662
1663 inline for (.{ true, false }) |whitespace| {
1664 sz.options.whitespace = whitespace;
1665
1666 {
1667 try sz.multilineString("", .{ .top_level = true });
1668 try std.testing.expectEqualStrings("\\\\", buf.items);
1669 buf.clearRetainingCapacity();
1670 }
1671
1672 {
1673 try sz.multilineString("abcâš¡", .{ .top_level = true });
1674 try std.testing.expectEqualStrings("\\\\abcâš¡", buf.items);
1675 buf.clearRetainingCapacity();
1676 }
1677
1678 {
1679 try sz.multilineString("abcâš¡\ndef", .{ .top_level = true });
1680 try std.testing.expectEqualStrings("\\\\abcâš¡\n\\\\def", buf.items);
1681 buf.clearRetainingCapacity();
1682 }
1683
1684 {
1685 try sz.multilineString("abcâš¡\r\ndef", .{ .top_level = true });
1686 try std.testing.expectEqualStrings("\\\\abcâš¡\n\\\\def", buf.items);
1687 buf.clearRetainingCapacity();
1688 }
1689
1690 {
1691 try sz.multilineString("\nabcâš¡", .{ .top_level = true });
1692 try std.testing.expectEqualStrings("\\\\\n\\\\abcâš¡", buf.items);
1693 buf.clearRetainingCapacity();
1694 }
1695
1696 {
1697 try sz.multilineString("\r\nabcâš¡", .{ .top_level = true });
1698 try std.testing.expectEqualStrings("\\\\\n\\\\abcâš¡", buf.items);
1699 buf.clearRetainingCapacity();
1700 }
1701
1702 {
1703 try sz.multilineString("abc\ndef", .{});
1704 if (whitespace) {
1705 try std.testing.expectEqualStrings("\n\\\\abc\n\\\\def\n", buf.items);
1706 } else {
1707 try std.testing.expectEqualStrings("\\\\abc\n\\\\def\n", buf.items);
1708 }
1709 buf.clearRetainingCapacity();
1710 }
1711
1712 {
1713 const str: []const u8 = &.{ 'a', '\r', 'c' };
1714 try sz.string(str);
1715 try std.testing.expectEqualStrings("\"a\\rc\"", buf.items);
1716 buf.clearRetainingCapacity();
1717 }
1718
1719 {
1720 try std.testing.expectError(
1721 error.InnerCarriageReturn,
1722 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c' }), .{}),
1723 );
1724 try std.testing.expectError(
1725 error.InnerCarriageReturn,
1726 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\n' }), .{}),
1727 );
1728 try std.testing.expectError(
1729 error.InnerCarriageReturn,
1730 sz.multilineString(@as([]const u8, &.{ 'a', '\r', 'c', '\r', '\n' }), .{}),
1731 );
1732 try std.testing.expectEqualStrings("", buf.items);
1733 buf.clearRetainingCapacity();
1734 }
1735 }
1736}
1737
1738test "std.zon stringify skip default fields" {
1739 const Struct = struct {
1740 x: i32 = 2,
1741 y: i8,
1742 z: u32 = 4,
1743 inner1: struct { a: u8 = 'z', b: u8 = 'y', c: u8 } = .{
1744 .a = '1',
1745 .b = '2',
1746 .c = '3',
1747 },
1748 inner2: struct { u8, u8, u8 } = .{
1749 'a',
1750 'b',
1751 'c',
1752 },
1753 inner3: struct { u8, u8, u8 } = .{
1754 'a',
1755 'b',
1756 'c',
1757 },
1758 };
1759
1760 // Not skipping if not set
1761 try expectSerializeEqual(
1762 \\.{
1763 \\ .x = 2,
1764 \\ .y = 3,
1765 \\ .z = 4,
1766 \\ .inner1 = .{
1767 \\ .a = '1',
1768 \\ .b = '2',
1769 \\ .c = '3',
1770 \\ },
1771 \\ .inner2 = .{
1772 \\ 'a',
1773 \\ 'b',
1774 \\ 'c',
1775 \\ },
1776 \\ .inner3 = .{
1777 \\ 'a',
1778 \\ 'b',
1779 \\ 'd',
1780 \\ },
1781 \\}
1782 ,
1783 Struct{
1784 .y = 3,
1785 .z = 4,
1786 .inner1 = .{
1787 .a = '1',
1788 .b = '2',
1789 .c = '3',
1790 },
1791 .inner3 = .{
1792 'a',
1793 'b',
1794 'd',
1795 },
1796 },
1797 .{ .emit_codepoint_literals = .always },
1798 );
1799
1800 // Top level defaults
1801 try expectSerializeEqual(
1802 \\.{ .y = 3, .inner3 = .{
1803 \\ 'a',
1804 \\ 'b',
1805 \\ 'd',
1806 \\} }
1807 ,
1808 Struct{
1809 .y = 3,
1810 .z = 4,
1811 .inner1 = .{
1812 .a = '1',
1813 .b = '2',
1814 .c = '3',
1815 },
1816 .inner3 = .{
1817 'a',
1818 'b',
1819 'd',
1820 },
1821 },
1822 .{
1823 .emit_default_optional_fields = false,
1824 .emit_codepoint_literals = .always,
1825 },
1826 );
1827
1828 // Inner types having defaults, and defaults changing the number of fields affecting the
1829 // formatting
1830 try expectSerializeEqual(
1831 \\.{
1832 \\ .y = 3,
1833 \\ .inner1 = .{ .b = '2', .c = '3' },
1834 \\ .inner3 = .{
1835 \\ 'a',
1836 \\ 'b',
1837 \\ 'd',
1838 \\ },
1839 \\}
1840 ,
1841 Struct{
1842 .y = 3,
1843 .z = 4,
1844 .inner1 = .{
1845 .a = 'z',
1846 .b = '2',
1847 .c = '3',
1848 },
1849 .inner3 = .{
1850 'a',
1851 'b',
1852 'd',
1853 },
1854 },
1855 .{
1856 .emit_default_optional_fields = false,
1857 .emit_codepoint_literals = .always,
1858 },
1859 );
1860
1861 const DefaultStrings = struct {
1862 foo: []const u8 = "abc",
1863 };
1864 try expectSerializeEqual(
1865 \\.{}
1866 ,
1867 DefaultStrings{ .foo = "abc" },
1868 .{ .emit_default_optional_fields = false },
1869 );
1870 try expectSerializeEqual(
1871 \\.{ .foo = "abcd" }
1872 ,
1873 DefaultStrings{ .foo = "abcd" },
1874 .{ .emit_default_optional_fields = false },
1875 );
1876}
1877
1878test "std.zon depth limits" {
1879 var buf = std.ArrayList(u8).init(std.testing.allocator);
1880 defer buf.deinit();
1881
1882 const Recurse = struct { r: []const @This() };
1883
1884 // Normal operation
1885 try serializeMaxDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer(), 16);
1886 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items);
1887 buf.clearRetainingCapacity();
1888
1889 try serializeArbitraryDepth(.{ 1, .{ 2, 3 } }, .{}, buf.writer());
1890 try std.testing.expectEqualStrings(".{ 1, .{ 2, 3 } }", buf.items);
1891 buf.clearRetainingCapacity();
1892
1893 // Max depth failing on non recursive type
1894 try std.testing.expectError(
1895 error.ExceededMaxDepth,
1896 serializeMaxDepth(.{ 1, .{ 2, .{ 3, 4 } } }, .{}, buf.writer(), 3),
1897 );
1898 try std.testing.expectEqualStrings("", buf.items);
1899 buf.clearRetainingCapacity();
1900
1901 // Max depth passing on recursive type
1902 {
1903 const maybe_recurse = Recurse{ .r = &.{} };
1904 try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2);
1905 try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items);
1906 buf.clearRetainingCapacity();
1907 }
1908
1909 // Unchecked passing on recursive type
1910 {
1911 const maybe_recurse = Recurse{ .r = &.{} };
1912 try serializeArbitraryDepth(maybe_recurse, .{}, buf.writer());
1913 try std.testing.expectEqualStrings(".{ .r = .{} }", buf.items);
1914 buf.clearRetainingCapacity();
1915 }
1916
1917 // Max depth failing on recursive type due to depth
1918 {
1919 var maybe_recurse = Recurse{ .r = &.{} };
1920 maybe_recurse.r = &.{.{ .r = &.{} }};
1921 try std.testing.expectError(
1922 error.ExceededMaxDepth,
1923 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2),
1924 );
1925 try std.testing.expectEqualStrings("", buf.items);
1926 buf.clearRetainingCapacity();
1927 }
1928
1929 // Same but for a slice
1930 {
1931 var temp: [1]Recurse = .{.{ .r = &.{} }};
1932 const maybe_recurse: []const Recurse = &temp;
1933
1934 try std.testing.expectError(
1935 error.ExceededMaxDepth,
1936 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 2),
1937 );
1938 try std.testing.expectEqualStrings("", buf.items);
1939 buf.clearRetainingCapacity();
1940
1941 var sz = serializer(buf.writer(), .{});
1942
1943 try std.testing.expectError(
1944 error.ExceededMaxDepth,
1945 sz.tupleMaxDepth(maybe_recurse, .{}, 2),
1946 );
1947 try std.testing.expectEqualStrings("", buf.items);
1948 buf.clearRetainingCapacity();
1949
1950 try sz.tupleArbitraryDepth(maybe_recurse, .{});
1951 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
1952 buf.clearRetainingCapacity();
1953 }
1954
1955 // A slice succeeding
1956 {
1957 var temp: [1]Recurse = .{.{ .r = &.{} }};
1958 const maybe_recurse: []const Recurse = &temp;
1959
1960 try serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 3);
1961 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
1962 buf.clearRetainingCapacity();
1963
1964 var sz = serializer(buf.writer(), .{});
1965
1966 try sz.tupleMaxDepth(maybe_recurse, .{}, 3);
1967 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
1968 buf.clearRetainingCapacity();
1969
1970 try sz.tupleArbitraryDepth(maybe_recurse, .{});
1971 try std.testing.expectEqualStrings(".{.{ .r = .{} }}", buf.items);
1972 buf.clearRetainingCapacity();
1973 }
1974
1975 // Max depth failing on recursive type due to recursion
1976 {
1977 var temp: [1]Recurse = .{.{ .r = &.{} }};
1978 temp[0].r = &temp;
1979 const maybe_recurse: []const Recurse = &temp;
1980
1981 try std.testing.expectError(
1982 error.ExceededMaxDepth,
1983 serializeMaxDepth(maybe_recurse, .{}, buf.writer(), 128),
1984 );
1985 try std.testing.expectEqualStrings("", buf.items);
1986 buf.clearRetainingCapacity();
1987
1988 var sz = serializer(buf.writer(), .{});
1989 try std.testing.expectError(
1990 error.ExceededMaxDepth,
1991 sz.tupleMaxDepth(maybe_recurse, .{}, 128),
1992 );
1993 try std.testing.expectEqualStrings("", buf.items);
1994 buf.clearRetainingCapacity();
1995 }
1996
1997 // Max depth on other parts of the lower level API
1998 {
1999 var sz = serializer(buf.writer(), .{});
2000
2001 const maybe_recurse: []const Recurse = &.{};
2002
2003 try std.testing.expectError(error.ExceededMaxDepth, sz.valueMaxDepth(1, .{}, 0));
2004 try sz.valueMaxDepth(2, .{}, 1);
2005 try sz.value(3, .{});
2006 try sz.valueArbitraryDepth(maybe_recurse, .{});
2007
2008 var s = try sz.startStruct(.{});
2009 try std.testing.expectError(error.ExceededMaxDepth, s.fieldMaxDepth("a", 1, .{}, 0));
2010 try s.fieldMaxDepth("b", 4, .{}, 1);
2011 try s.field("c", 5, .{});
2012 try s.fieldArbitraryDepth("d", maybe_recurse, .{});
2013 try s.finish();
2014
2015 var t = try sz.startTuple(.{});
2016 try std.testing.expectError(error.ExceededMaxDepth, t.fieldMaxDepth(1, .{}, 0));
2017 try t.fieldMaxDepth(6, .{}, 1);
2018 try t.field(7, .{});
2019 try t.fieldArbitraryDepth(maybe_recurse, .{});
2020 try t.finish();
2021
2022 var a = try sz.startTuple(.{});
2023 try std.testing.expectError(error.ExceededMaxDepth, a.fieldMaxDepth(1, .{}, 0));
2024 try a.fieldMaxDepth(8, .{}, 1);
2025 try a.field(9, .{});
2026 try a.fieldArbitraryDepth(maybe_recurse, .{});
2027 try a.finish();
2028
2029 try std.testing.expectEqualStrings(
2030 \\23.{}.{
2031 \\ .b = 4,
2032 \\ .c = 5,
2033 \\ .d = .{},
2034 \\}.{
2035 \\ 6,
2036 \\ 7,
2037 \\ .{},
2038 \\}.{
2039 \\ 8,
2040 \\ 9,
2041 \\ .{},
2042 \\}
2043 , buf.items);
2044 }
2045}
2046
2047test "std.zon stringify primitives" {
2048 // Issue: https://github.com/ziglang/zig/issues/20880
2049 if (@import("builtin").zig_backend == .stage2_c) return error.SkipZigTest;
2050
2051 try expectSerializeEqual(
2052 \\.{
2053 \\ .a = 1.5,
2054 \\ .b = 0.3333333333333333333333333333333333,
2055 \\ .c = 3.1415926535897932384626433832795028,
2056 \\ .d = 0,
2057 \\ .e = -0,
2058 \\ .f = inf,
2059 \\ .g = -inf,
2060 \\ .h = nan,
2061 \\}
2062 ,
2063 .{
2064 .a = @as(f128, 1.5), // Make sure explicit f128s work
2065 .b = 1.0 / 3.0,
2066 .c = std.math.pi,
2067 .d = 0.0,
2068 .e = -0.0,
2069 .f = std.math.inf(f32),
2070 .g = -std.math.inf(f32),
2071 .h = std.math.nan(f32),
2072 },
2073 .{},
2074 );
2075
2076 try expectSerializeEqual(
2077 \\.{
2078 \\ .a = 18446744073709551616,
2079 \\ .b = -18446744073709551616,
2080 \\ .c = 680564733841876926926749214863536422912,
2081 \\ .d = -680564733841876926926749214863536422912,
2082 \\ .e = 0,
2083 \\}
2084 ,
2085 .{
2086 .a = 18446744073709551616,
2087 .b = -18446744073709551616,
2088 .c = 680564733841876926926749214863536422912,
2089 .d = -680564733841876926926749214863536422912,
2090 .e = 0,
2091 },
2092 .{},
2093 );
2094
2095 try expectSerializeEqual(
2096 \\.{
2097 \\ .a = true,
2098 \\ .b = false,
2099 \\ .c = .foo,
2100 \\ .e = null,
2101 \\}
2102 ,
2103 .{
2104 .a = true,
2105 .b = false,
2106 .c = .foo,
2107 .e = null,
2108 },
2109 .{},
2110 );
2111
2112 const Struct = struct { x: f32, y: f32 };
2113 try expectSerializeEqual(
2114 ".{ .a = .{ .x = 1, .y = 2 }, .b = null }",
2115 .{
2116 .a = @as(?Struct, .{ .x = 1, .y = 2 }),
2117 .b = @as(?Struct, null),
2118 },
2119 .{},
2120 );
2121
2122 const E = enum(u8) {
2123 foo,
2124 bar,
2125 };
2126 try expectSerializeEqual(
2127 ".{ .a = .foo, .b = .foo }",
2128 .{
2129 .a = .foo,
2130 .b = E.foo,
2131 },
2132 .{},
2133 );
2134}
2135
2136test "std.zon stringify ident" {
2137 var buf = std.ArrayList(u8).init(std.testing.allocator);
2138 defer buf.deinit();
2139 var sz = serializer(buf.writer(), .{});
2140
2141 try expectSerializeEqual(".{ .a = 0 }", .{ .a = 0 }, .{});
2142 try sz.ident("a");
2143 try std.testing.expectEqualStrings(".a", buf.items);
2144 buf.clearRetainingCapacity();
2145
2146 try sz.ident("foo_1");
2147 try std.testing.expectEqualStrings(".foo_1", buf.items);
2148 buf.clearRetainingCapacity();
2149
2150 try sz.ident("_foo_1");
2151 try std.testing.expectEqualStrings("._foo_1", buf.items);
2152 buf.clearRetainingCapacity();
2153
2154 try sz.ident("foo bar");
2155 try std.testing.expectEqualStrings(".@\"foo bar\"", buf.items);
2156 buf.clearRetainingCapacity();
2157
2158 try sz.ident("1foo");
2159 try std.testing.expectEqualStrings(".@\"1foo\"", buf.items);
2160 buf.clearRetainingCapacity();
2161
2162 try sz.ident("var");
2163 try std.testing.expectEqualStrings(".@\"var\"", buf.items);
2164 buf.clearRetainingCapacity();
2165
2166 try sz.ident("true");
2167 try std.testing.expectEqualStrings(".true", buf.items);
2168 buf.clearRetainingCapacity();
2169
2170 try sz.ident("_");
2171 try std.testing.expectEqualStrings("._", buf.items);
2172 buf.clearRetainingCapacity();
2173
2174 const Enum = enum {
2175 @"foo bar",
2176 };
2177 try expectSerializeEqual(".{ .@\"var\" = .@\"foo bar\", .@\"1\" = .@\"foo bar\" }", .{
2178 .@"var" = .@"foo bar",
2179 .@"1" = Enum.@"foo bar",
2180 }, .{});
2181}
2182
2183test "std.zon stringify as tuple" {
2184 var buf = std.ArrayList(u8).init(std.testing.allocator);
2185 defer buf.deinit();
2186 var sz = serializer(buf.writer(), .{});
2187
2188 // Tuples
2189 try sz.tuple(.{ 1, 2 }, .{});
2190 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
2191 buf.clearRetainingCapacity();
2192
2193 // Slice
2194 try sz.tuple(@as([]const u8, &.{ 1, 2 }), .{});
2195 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
2196 buf.clearRetainingCapacity();
2197
2198 // Array
2199 try sz.tuple([2]u8{ 1, 2 }, .{});
2200 try std.testing.expectEqualStrings(".{ 1, 2 }", buf.items);
2201 buf.clearRetainingCapacity();
2202}
2203
2204test "std.zon stringify as float" {
2205 var buf = std.ArrayList(u8).init(std.testing.allocator);
2206 defer buf.deinit();
2207 var sz = serializer(buf.writer(), .{});
2208
2209 // Comptime float
2210 try sz.float(2.5);
2211 try std.testing.expectEqualStrings("2.5", buf.items);
2212 buf.clearRetainingCapacity();
2213
2214 // Sized float
2215 try sz.float(@as(f32, 2.5));
2216 try std.testing.expectEqualStrings("2.5", buf.items);
2217 buf.clearRetainingCapacity();
2218}
2219
2220test "std.zon stringify vector" {
2221 try expectSerializeEqual(
2222 \\.{
2223 \\ .{},
2224 \\ .{
2225 \\ true,
2226 \\ false,
2227 \\ true,
2228 \\ },
2229 \\ .{},
2230 \\ .{
2231 \\ 1.5,
2232 \\ 2.5,
2233 \\ 3.5,
2234 \\ },
2235 \\ .{},
2236 \\ .{
2237 \\ 2,
2238 \\ 4,
2239 \\ 6,
2240 \\ },
2241 \\ .{ 1, 2 },
2242 \\ .{
2243 \\ 3,
2244 \\ 4,
2245 \\ null,
2246 \\ },
2247 \\}
2248 ,
2249 .{
2250 @Vector(0, bool){},
2251 @Vector(3, bool){ true, false, true },
2252 @Vector(0, f32){},
2253 @Vector(3, f32){ 1.5, 2.5, 3.5 },
2254 @Vector(0, u8){},
2255 @Vector(3, u8){ 2, 4, 6 },
2256 @Vector(2, *const u8){ &1, &2 },
2257 @Vector(3, ?*const u8){ &3, &4, null },
2258 },
2259 .{},
2260 );
2261}
2262
2263test "std.zon pointers" {
2264 // Primitive with varying levels of pointers
2265 try expectSerializeEqual("10", &@as(u32, 10), .{});
2266 try expectSerializeEqual("10", &&@as(u32, 10), .{});
2267 try expectSerializeEqual("10", &&&@as(u32, 10), .{});
2268
2269 // Primitive optional with varying levels of pointers
2270 try expectSerializeEqual("10", @as(?*const u32, &10), .{});
2271 try expectSerializeEqual("null", @as(?*const u32, null), .{});
2272 try expectSerializeEqual("10", @as(?*const u32, &10), .{});
2273 try expectSerializeEqual("null", @as(*const ?u32, &null), .{});
2274
2275 try expectSerializeEqual("10", @as(?*const *const u32, &&10), .{});
2276 try expectSerializeEqual("null", @as(?*const *const u32, null), .{});
2277 try expectSerializeEqual("10", @as(*const ?*const u32, &&10), .{});
2278 try expectSerializeEqual("null", @as(*const ?*const u32, &null), .{});
2279 try expectSerializeEqual("10", @as(*const *const ?u32, &&10), .{});
2280 try expectSerializeEqual("null", @as(*const *const ?u32, &&null), .{});
2281
2282 try expectSerializeEqual(".{ 1, 2 }", &[2]u32{ 1, 2 }, .{});
2283
2284 // A complicated type with nested internal pointers and string allocations
2285 {
2286 const Inner = struct {
2287 f1: *const ?*const []const u8,
2288 f2: *const ?*const []const u8,
2289 };
2290 const Outer = struct {
2291 f1: *const ?*const Inner,
2292 f2: *const ?*const Inner,
2293 };
2294 const val: ?*const Outer = &.{
2295 .f1 = &&.{
2296 .f1 = &null,
2297 .f2 = &&"foo",
2298 },
2299 .f2 = &null,
2300 };
2301
2302 try expectSerializeEqual(
2303 \\.{ .f1 = .{ .f1 = null, .f2 = "foo" }, .f2 = null }
2304 , val, .{});
2305 }
2306}
src/Compilation.zig+25-6
......@@ -2220,7 +2220,10 @@ pub fn update(comp: *Compilation, main_progress_node: std.Progress.Node) !void {
22202220 try comp.astgen_work_queue.ensureUnusedCapacity(zcu.import_table.count());
22212221 for (zcu.import_table.values()) |file_index| {
22222222 if (zcu.fileByIndex(file_index).mod.isBuiltin()) continue;
2223 comp.astgen_work_queue.writeItemAssumeCapacity(file_index);
2223 const file = zcu.fileByIndex(file_index);
2224 if (file.getMode() == .zig) {
2225 comp.astgen_work_queue.writeItemAssumeCapacity(file_index);
2226 }
22242227 }
22252228 if (comp.file_system_inputs) |fsi| {
22262229 for (zcu.import_table.values()) |file_index| {
......@@ -3206,10 +3209,16 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
32063209 if (error_msg) |msg| {
32073210 try addModuleErrorMsg(zcu, &bundle, msg.*);
32083211 } else {
3209 // Must be ZIR errors. Note that this may include AST errors.
3210 // addZirErrorMessages asserts that the tree is loaded.
3211 _ = try file.getTree(gpa);
3212 try addZirErrorMessages(&bundle, file);
3212 // Must be ZIR or Zoir errors. Note that this may include AST errors.
3213 _ = try file.getTree(gpa); // Tree must be loaded.
3214 if (file.zir_loaded) {
3215 try addZirErrorMessages(&bundle, file);
3216 } else if (file.zoir != null) {
3217 try addZoirErrorMessages(&bundle, file);
3218 } else {
3219 // Either Zir or Zoir must have been loaded.
3220 unreachable;
3221 }
32133222 }
32143223 }
32153224 var sorted_failed_analysis: std.AutoArrayHashMapUnmanaged(InternPool.AnalUnit, *Zcu.ErrorMsg).DataList.Slice = s: {
......@@ -3623,6 +3632,15 @@ pub fn addZirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
36233632 return eb.addZirErrorMessages(file.zir, file.tree, file.source, src_path);
36243633}
36253634
3635pub fn addZoirErrorMessages(eb: *ErrorBundle.Wip, file: *Zcu.File) !void {
3636 assert(file.source_loaded);
3637 assert(file.tree_loaded);
3638 const gpa = eb.gpa;
3639 const src_path = try file.fullPath(gpa);
3640 defer gpa.free(src_path);
3641 return eb.addZoirErrorMessages(file.zoir.?, file.tree, file.source, src_path);
3642}
3643
36263644pub fn performAllTheWork(
36273645 comp: *Compilation,
36283646 main_progress_node: std.Progress.Node,
......@@ -4272,6 +4290,7 @@ fn workerAstGenFile(
42724290 wg: *WaitGroup,
42734291 src: Zcu.AstGenSrc,
42744292) void {
4293 assert(file.getMode() == .zig);
42754294 const child_prog_node = prog_node.start(file.sub_file_path, 0);
42764295 defer child_prog_node.end();
42774296
......@@ -4325,7 +4344,7 @@ fn workerAstGenFile(
43254344 const imported_path_digest = pt.zcu.filePathDigest(res.file_index);
43264345 break :blk .{ res, imported_path_digest };
43274346 };
4328 if (import_result.is_new) {
4347 if (import_result.is_new and import_result.file.getMode() == .zig) {
43294348 log.debug("AstGen of {s} has import '{s}'; queuing AstGen of {s}", .{
43304349 file.sub_file_path, import_path, import_result.file.sub_file_path,
43314350 });
src/InternPool.zig+9-9
......@@ -4389,7 +4389,7 @@ pub const LoadedEnumType = struct {
43894389 // Auto-numbered enum. Convert `int_tag_val` to field index.
43904390 const field_index = switch (ip.indexToKey(int_tag_val).int.storage) {
43914391 inline .u64, .i64 => |x| std.math.cast(u32, x) orelse return null,
4392 .big_int => |x| x.to(u32) catch return null,
4392 .big_int => |x| x.toInt(u32) catch return null,
43934393 .lazy_align, .lazy_size => unreachable,
43944394 };
43954395 return if (field_index < self.names.len) field_index else null;
......@@ -7957,7 +7957,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
79577957 .big_int => |big_int| {
79587958 items.appendAssumeCapacity(.{
79597959 .tag = .int_u8,
7960 .data = big_int.to(u8) catch unreachable,
7960 .data = big_int.toInt(u8) catch unreachable,
79617961 });
79627962 break :b;
79637963 },
......@@ -7974,7 +7974,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
79747974 .big_int => |big_int| {
79757975 items.appendAssumeCapacity(.{
79767976 .tag = .int_u16,
7977 .data = big_int.to(u16) catch unreachable,
7977 .data = big_int.toInt(u16) catch unreachable,
79787978 });
79797979 break :b;
79807980 },
......@@ -7991,7 +7991,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
79917991 .big_int => |big_int| {
79927992 items.appendAssumeCapacity(.{
79937993 .tag = .int_u32,
7994 .data = big_int.to(u32) catch unreachable,
7994 .data = big_int.toInt(u32) catch unreachable,
79957995 });
79967996 break :b;
79977997 },
......@@ -8006,7 +8006,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
80068006 },
80078007 .i32_type => switch (int.storage) {
80088008 .big_int => |big_int| {
8009 const casted = big_int.to(i32) catch unreachable;
8009 const casted = big_int.toInt(i32) catch unreachable;
80108010 items.appendAssumeCapacity(.{
80118011 .tag = .int_i32,
80128012 .data = @as(u32, @bitCast(casted)),
......@@ -8024,7 +8024,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
80248024 },
80258025 .usize_type => switch (int.storage) {
80268026 .big_int => |big_int| {
8027 if (big_int.to(u32)) |casted| {
8027 if (big_int.toInt(u32)) |casted| {
80288028 items.appendAssumeCapacity(.{
80298029 .tag = .int_usize,
80308030 .data = casted,
......@@ -8045,14 +8045,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
80458045 },
80468046 .comptime_int_type => switch (int.storage) {
80478047 .big_int => |big_int| {
8048 if (big_int.to(u32)) |casted| {
8048 if (big_int.toInt(u32)) |casted| {
80498049 items.appendAssumeCapacity(.{
80508050 .tag = .int_comptime_int_u32,
80518051 .data = casted,
80528052 });
80538053 break :b;
80548054 } else |_| {}
8055 if (big_int.to(i32)) |casted| {
8055 if (big_int.toInt(i32)) |casted| {
80568056 items.appendAssumeCapacity(.{
80578057 .tag = .int_comptime_int_i32,
80588058 .data = @as(u32, @bitCast(casted)),
......@@ -8082,7 +8082,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
80828082 }
80838083 switch (int.storage) {
80848084 .big_int => |big_int| {
8085 if (big_int.to(u32)) |casted| {
8085 if (big_int.toInt(u32)) |casted| {
80868086 items.appendAssumeCapacity(.{
80878087 .tag = .int_small,
80888088 .data = try addExtra(extra, IntSmall{
src/Sema.zig+41-9
......@@ -187,6 +187,7 @@ const Alignment = InternPool.Alignment;
187187const AnalUnit = InternPool.AnalUnit;
188188const ComptimeAllocIndex = InternPool.ComptimeAllocIndex;
189189const Cache = std.Build.Cache;
190const LowerZon = @import("Sema/LowerZon.zig");
190191
191192pub const default_branch_quota = 1000;
192193pub const default_reference_trace_len = 2;
......@@ -5790,7 +5791,7 @@ fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString)
57905791 return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool));
57915792}
57925793
5793fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref {
5794pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref {
57945795 const pt = sema.pt;
57955796 const array_ty = try pt.arrayType(.{
57965797 .len = len,
......@@ -13964,9 +13965,10 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1396413965
1396513966 const pt = sema.pt;
1396613967 const zcu = pt.zcu;
13967 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
13968 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
13969 const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data;
1396813970 const operand_src = block.tokenOffset(inst_data.src_tok);
13969 const operand = inst_data.get(sema.code);
13971 const operand = sema.code.nullTerminatedString(extra.path);
1397013972
1397113973 const result = pt.importFile(block.getFileScope(zcu), operand) catch |err| switch (err) {
1397213974 error.ImportOutsideModulePath => {
......@@ -13983,12 +13985,42 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
1398313985 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ operand, @errorName(err) });
1398413986 },
1398513987 };
13986 try sema.declareDependency(.{ .file = result.file_index });
13987 try pt.ensureFileAnalyzed(result.file_index);
13988 const ty = zcu.fileRootType(result.file_index);
13989 try sema.declareDependency(.{ .interned = ty });
13990 try sema.addTypeReferenceEntry(operand_src, ty);
13991 return Air.internedToRef(ty);
13988 switch (result.file.getMode()) {
13989 .zig => {
13990 try sema.declareDependency(.{ .file = result.file_index });
13991 try pt.ensureFileAnalyzed(result.file_index);
13992 const ty = zcu.fileRootType(result.file_index);
13993 try sema.declareDependency(.{ .interned = ty });
13994 try sema.addTypeReferenceEntry(operand_src, ty);
13995 return Air.internedToRef(ty);
13996 },
13997 .zon => {
13998 _ = result.file.getTree(zcu.gpa) catch |err| {
13999 // TODO: these errors are file system errors; make sure an update() will
14000 // retry this and not cache the file system error, which may be transient.
14001 return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ result.file.sub_file_path, @errorName(err) });
14002 };
14003
14004 if (extra.res_ty == .none) {
14005 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
14006 }
14007 const res_ty_inst = try sema.resolveInst(extra.res_ty);
14008 const res_ty = try sema.analyzeAsType(block, operand_src, res_ty_inst);
14009 if (res_ty.isGenericPoison()) {
14010 return sema.fail(block, operand_src, "'@import' of ZON must have a known result type", .{});
14011 }
14012
14013 const interned = try LowerZon.run(
14014 sema,
14015 result.file,
14016 result.file_index,
14017 res_ty,
14018 operand_src,
14019 block,
14020 );
14021 return Air.internedToRef(interned);
14022 },
14023 }
1399214024}
1399314025
1399414026fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
src/Sema/LowerZon.zig created+858
......@@ -0,0 +1,858 @@
1const std = @import("std");
2const Zcu = @import("../Zcu.zig");
3const Sema = @import("../Sema.zig");
4const Air = @import("../Air.zig");
5const InternPool = @import("../InternPool.zig");
6const Type = @import("../Type.zig");
7const Value = @import("../Value.zig");
8const Zir = std.zig.Zir;
9const AstGen = std.zig.AstGen;
10const CompileError = Zcu.CompileError;
11const Ast = std.zig.Ast;
12const Allocator = std.mem.Allocator;
13const assert = std.debug.assert;
14const File = Zcu.File;
15const LazySrcLoc = Zcu.LazySrcLoc;
16const Ref = std.zig.Zir.Inst.Ref;
17const NullTerminatedString = InternPool.NullTerminatedString;
18const NumberLiteralError = std.zig.number_literal.Error;
19const NodeIndex = std.zig.Ast.Node.Index;
20const Zoir = std.zig.Zoir;
21
22const LowerZon = @This();
23
24sema: *Sema,
25file: *File,
26file_index: Zcu.File.Index,
27import_loc: LazySrcLoc,
28block: *Sema.Block,
29base_node_inst: InternPool.TrackedInst.Index,
30
31/// Lowers the given file as ZON.
32pub fn run(
33 sema: *Sema,
34 file: *File,
35 file_index: Zcu.File.Index,
36 res_ty: Type,
37 import_loc: LazySrcLoc,
38 block: *Sema.Block,
39) CompileError!InternPool.Index {
40 const pt = sema.pt;
41
42 _ = try file.getZoir(pt.zcu);
43
44 const tracked_inst = try pt.zcu.intern_pool.trackZir(pt.zcu.gpa, pt.tid, .{
45 .file = file_index,
46 .inst = .main_struct_inst, // this is the only trackable instruction in a ZON file
47 });
48
49 var lower_zon: LowerZon = .{
50 .sema = sema,
51 .file = file,
52 .file_index = file_index,
53 .import_loc = import_loc,
54 .block = block,
55 .base_node_inst = tracked_inst,
56 };
57
58 try lower_zon.checkType(res_ty);
59
60 return lower_zon.lowerExpr(.root, res_ty);
61}
62
63/// Validate that `ty` is a valid ZON type. If not, emit a compile error.
64/// i.e. no nested optionals, no error sets, etc.
65fn checkType(self: *LowerZon, ty: Type) !void {
66 var visited: std.AutoHashMapUnmanaged(InternPool.Index, void) = .empty;
67 try self.checkTypeInner(ty, null, &visited);
68}
69
70fn checkTypeInner(
71 self: *LowerZon,
72 ty: Type,
73 parent_opt_ty: ?Type,
74 /// Visited structs and unions (not tuples). These are tracked because they are the only way in
75 /// which a type can be self-referential, so must be tracked to avoid loops. Tracking more types
76 /// consumes memory unnecessarily, and would be complicated by optionals.
77 /// Allocated into `self.sema.arena`.
78 visited: *std.AutoHashMapUnmanaged(InternPool.Index, void),
79) !void {
80 const sema = self.sema;
81 const pt = sema.pt;
82 const zcu = pt.zcu;
83 const ip = &zcu.intern_pool;
84
85 switch (ty.zigTypeTag(zcu)) {
86 .bool,
87 .int,
88 .float,
89 .null,
90 .@"enum",
91 .comptime_float,
92 .comptime_int,
93 .enum_literal,
94 => {},
95
96 .noreturn,
97 .void,
98 .type,
99 .undefined,
100 .error_union,
101 .error_set,
102 .@"fn",
103 .frame,
104 .@"anyframe",
105 .@"opaque",
106 => return self.failUnsupportedResultType(ty, null),
107
108 .pointer => {
109 const ptr_info = ty.ptrInfo(zcu);
110 if (!ptr_info.flags.is_const) {
111 return self.failUnsupportedResultType(
112 ty,
113 "ZON does not allow mutable pointers",
114 );
115 }
116 switch (ptr_info.flags.size) {
117 .one => try self.checkTypeInner(
118 .fromInterned(ptr_info.child),
119 parent_opt_ty, // preserved
120 visited,
121 ),
122 .slice => try self.checkTypeInner(
123 .fromInterned(ptr_info.child),
124 null,
125 visited,
126 ),
127 .many => return self.failUnsupportedResultType(ty, "ZON does not allow many-pointers"),
128 .c => return self.failUnsupportedResultType(ty, "ZON does not allow C pointers"),
129 }
130 },
131 .optional => if (parent_opt_ty) |p| {
132 return self.failUnsupportedResultType(p, "ZON does not allow nested optionals");
133 } else try self.checkTypeInner(
134 ty.optionalChild(zcu),
135 ty,
136 visited,
137 ),
138 .array, .vector => {
139 try self.checkTypeInner(ty.childType(zcu), null, visited);
140 },
141 .@"struct" => if (ty.isTuple(zcu)) {
142 const tuple_info = ip.indexToKey(ty.toIntern()).tuple_type;
143 const field_types = tuple_info.types.get(ip);
144 for (field_types) |field_type| {
145 try self.checkTypeInner(.fromInterned(field_type), null, visited);
146 }
147 } else {
148 const gop = try visited.getOrPut(sema.arena, ty.toIntern());
149 if (gop.found_existing) return;
150 try ty.resolveFields(pt);
151 const struct_info = zcu.typeToStruct(ty).?;
152 for (struct_info.field_types.get(ip)) |field_type| {
153 try self.checkTypeInner(.fromInterned(field_type), null, visited);
154 }
155 },
156 .@"union" => {
157 const gop = try visited.getOrPut(sema.arena, ty.toIntern());
158 if (gop.found_existing) return;
159 try ty.resolveFields(pt);
160 const union_info = zcu.typeToUnion(ty).?;
161 for (union_info.field_types.get(ip)) |field_type| {
162 if (field_type != .void_type) {
163 try self.checkTypeInner(.fromInterned(field_type), null, visited);
164 }
165 }
166 },
167 }
168}
169
170fn nodeSrc(self: *LowerZon, node: Zoir.Node.Index) LazySrcLoc {
171 return .{
172 .base_node_inst = self.base_node_inst,
173 .offset = .{ .node_abs = node.getAstNode(self.file.zoir.?) },
174 };
175}
176
177fn failUnsupportedResultType(
178 self: *LowerZon,
179 ty: Type,
180 opt_note: ?[]const u8,
181) error{ AnalysisFail, OutOfMemory } {
182 @branchHint(.cold);
183 const sema = self.sema;
184 const gpa = sema.gpa;
185 const pt = sema.pt;
186 return sema.failWithOwnedErrorMsg(self.block, msg: {
187 const msg = try sema.errMsg(self.import_loc, "type '{}' is not available in ZON", .{ty.fmt(pt)});
188 errdefer msg.destroy(gpa);
189 if (opt_note) |n| try sema.errNote(self.import_loc, msg, "{s}", .{n});
190 break :msg msg;
191 });
192}
193
194fn fail(
195 self: *LowerZon,
196 node: Zoir.Node.Index,
197 comptime format: []const u8,
198 args: anytype,
199) error{ AnalysisFail, OutOfMemory } {
200 @branchHint(.cold);
201 const err_msg = try Zcu.ErrorMsg.create(self.sema.pt.zcu.gpa, self.nodeSrc(node), format, args);
202 try self.sema.pt.zcu.errNote(self.import_loc, err_msg, "imported here", .{});
203 return self.sema.failWithOwnedErrorMsg(self.block, err_msg);
204}
205
206fn lowerExpr(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) CompileError!InternPool.Index {
207 const pt = self.sema.pt;
208 return self.lowerExprInner(node, res_ty) catch |err| switch (err) {
209 error.WrongType => return self.fail(
210 node,
211 "expected type '{}'",
212 .{res_ty.fmt(pt)},
213 ),
214 else => |e| return e,
215 };
216}
217
218fn lowerExprInner(
219 self: *LowerZon,
220 node: Zoir.Node.Index,
221 res_ty: Type,
222) (CompileError || error{WrongType})!InternPool.Index {
223 const pt = self.sema.pt;
224 switch (res_ty.zigTypeTag(pt.zcu)) {
225 .optional => return pt.intern(.{
226 .opt = .{
227 .ty = res_ty.toIntern(),
228 .val = if (node.get(self.file.zoir.?) == .null) b: {
229 break :b .none;
230 } else b: {
231 const child_type = res_ty.optionalChild(pt.zcu);
232 break :b try self.lowerExprInner(node, child_type);
233 },
234 },
235 }),
236 .pointer => {
237 const ptr_info = res_ty.ptrInfo(pt.zcu);
238 switch (ptr_info.flags.size) {
239 .one => return pt.intern(.{ .ptr = .{
240 .ty = res_ty.toIntern(),
241 .base_addr = .{
242 .uav = .{
243 .orig_ty = res_ty.toIntern(),
244 .val = try self.lowerExprInner(node, .fromInterned(ptr_info.child)),
245 },
246 },
247 .byte_offset = 0,
248 } }),
249 .slice => return self.lowerSlice(node, res_ty),
250 else => {
251 // Unsupported pointer type, checked in `lower`
252 unreachable;
253 },
254 }
255 },
256 .bool => return self.lowerBool(node),
257 .int, .comptime_int => return self.lowerInt(node, res_ty),
258 .float, .comptime_float => return self.lowerFloat(node, res_ty),
259 .null => return self.lowerNull(node),
260 .@"enum" => return self.lowerEnum(node, res_ty),
261 .enum_literal => return self.lowerEnumLiteral(node),
262 .array => return self.lowerArray(node, res_ty),
263 .@"struct" => return self.lowerStructOrTuple(node, res_ty),
264 .@"union" => return self.lowerUnion(node, res_ty),
265 .vector => return self.lowerVector(node, res_ty),
266
267 .type,
268 .noreturn,
269 .undefined,
270 .error_union,
271 .error_set,
272 .@"fn",
273 .@"opaque",
274 .frame,
275 .@"anyframe",
276 .void,
277 => return self.fail(node, "type '{}' not available in ZON", .{res_ty.fmt(pt)}),
278 }
279}
280
281fn lowerBool(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
282 return switch (node.get(self.file.zoir.?)) {
283 .true => .bool_true,
284 .false => .bool_false,
285 else => return error.WrongType,
286 };
287}
288
289fn lowerInt(
290 self: *LowerZon,
291 node: Zoir.Node.Index,
292 res_ty: Type,
293) !InternPool.Index {
294 @setFloatMode(.strict);
295 return switch (node.get(self.file.zoir.?)) {
296 .int_literal => |int| switch (int) {
297 .small => |val| {
298 const rhs: i32 = val;
299
300 // If our result is a fixed size integer, check that our value is not out of bounds
301 if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
302 const lhs_info = res_ty.intInfo(self.sema.pt.zcu);
303
304 // If lhs is unsigned and rhs is less than 0, we're out of bounds
305 if (lhs_info.signedness == .unsigned and rhs < 0) return self.fail(
306 node,
307 "type '{}' cannot represent integer value '{}'",
308 .{ res_ty.fmt(self.sema.pt), rhs },
309 );
310
311 // If lhs has less than the 32 bits rhs can hold, we need to check the max and
312 // min values
313 if (std.math.cast(u5, lhs_info.bits)) |bits| {
314 const min_int: i32 = if (lhs_info.signedness == .unsigned or bits == 0) b: {
315 break :b 0;
316 } else b: {
317 break :b -(@as(i32, 1) << (bits - 1));
318 };
319 const max_int: i32 = if (bits == 0) b: {
320 break :b 0;
321 } else b: {
322 break :b (@as(i32, 1) << (bits - @intFromBool(lhs_info.signedness == .signed))) - 1;
323 };
324 if (rhs < min_int or rhs > max_int) {
325 return self.fail(
326 node,
327 "type '{}' cannot represent integer value '{}'",
328 .{ res_ty.fmt(self.sema.pt), rhs },
329 );
330 }
331 }
332 }
333
334 return self.sema.pt.intern(.{ .int = .{
335 .ty = res_ty.toIntern(),
336 .storage = .{ .i64 = rhs },
337 } });
338 },
339 .big => |val| {
340 if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
341 const int_info = res_ty.intInfo(self.sema.pt.zcu);
342 if (!val.fitsInTwosComp(int_info.signedness, int_info.bits)) {
343 return self.fail(
344 node,
345 "type '{}' cannot represent integer value '{}'",
346 .{ res_ty.fmt(self.sema.pt), val },
347 );
348 }
349 }
350
351 return self.sema.pt.intern(.{ .int = .{
352 .ty = res_ty.toIntern(),
353 .storage = .{ .big_int = val },
354 } });
355 },
356 },
357 .float_literal => |val| {
358 // Check for fractional components
359 if (@rem(val, 1) != 0) {
360 return self.fail(
361 node,
362 "fractional component prevents float value '{}' from coercion to type '{}'",
363 .{ val, res_ty.fmt(self.sema.pt) },
364 );
365 }
366
367 // Create a rational representation of the float
368 var rational = try std.math.big.Rational.init(self.sema.arena);
369 rational.setFloat(f128, val) catch |err| switch (err) {
370 error.NonFiniteFloat => unreachable,
371 error.OutOfMemory => return error.OutOfMemory,
372 };
373
374 // The float is reduced in rational.setFloat, so we assert that denominator is equal to
375 // one
376 const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
377 assert(rational.q.toConst().eqlAbs(big_one));
378
379 // Check that the result is in range of the result type
380 const int_info = res_ty.intInfo(self.sema.pt.zcu);
381 if (!rational.p.fitsInTwosComp(int_info.signedness, int_info.bits)) {
382 return self.fail(
383 node,
384 "type '{}' cannot represent integer value '{}'",
385 .{ val, res_ty.fmt(self.sema.pt) },
386 );
387 }
388
389 return self.sema.pt.intern(.{
390 .int = .{
391 .ty = res_ty.toIntern(),
392 .storage = .{ .big_int = rational.p.toConst() },
393 },
394 });
395 },
396 .char_literal => |val| {
397 // If our result is a fixed size integer, check that our value is not out of bounds
398 if (res_ty.zigTypeTag(self.sema.pt.zcu) == .int) {
399 const dest_info = res_ty.intInfo(self.sema.pt.zcu);
400 const unsigned_bits = dest_info.bits - @intFromBool(dest_info.signedness == .signed);
401 if (unsigned_bits < 21) {
402 const out_of_range: u21 = @as(u21, 1) << @intCast(unsigned_bits);
403 if (val >= out_of_range) {
404 return self.fail(
405 node,
406 "type '{}' cannot represent integer value '{}'",
407 .{ res_ty.fmt(self.sema.pt), val },
408 );
409 }
410 }
411 }
412 return self.sema.pt.intern(.{
413 .int = .{
414 .ty = res_ty.toIntern(),
415 .storage = .{ .i64 = val },
416 },
417 });
418 },
419
420 else => return error.WrongType,
421 };
422}
423
424fn lowerFloat(
425 self: *LowerZon,
426 node: Zoir.Node.Index,
427 res_ty: Type,
428) !InternPool.Index {
429 @setFloatMode(.strict);
430 const value = switch (node.get(self.file.zoir.?)) {
431 .int_literal => |int| switch (int) {
432 .small => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
433 .big => |val| try self.sema.pt.floatValue(res_ty, val.toFloat(f128)),
434 },
435 .float_literal => |val| try self.sema.pt.floatValue(res_ty, val),
436 .char_literal => |val| try self.sema.pt.floatValue(res_ty, @as(f128, @floatFromInt(val))),
437 .pos_inf => b: {
438 if (res_ty.toIntern() == .comptime_float_type) return self.fail(
439 node,
440 "expected type '{}'",
441 .{res_ty.fmt(self.sema.pt)},
442 );
443 break :b try self.sema.pt.floatValue(res_ty, std.math.inf(f128));
444 },
445 .neg_inf => b: {
446 if (res_ty.toIntern() == .comptime_float_type) return self.fail(
447 node,
448 "expected type '{}'",
449 .{res_ty.fmt(self.sema.pt)},
450 );
451 break :b try self.sema.pt.floatValue(res_ty, -std.math.inf(f128));
452 },
453 .nan => b: {
454 if (res_ty.toIntern() == .comptime_float_type) return self.fail(
455 node,
456 "expected type '{}'",
457 .{res_ty.fmt(self.sema.pt)},
458 );
459 break :b try self.sema.pt.floatValue(res_ty, std.math.nan(f128));
460 },
461 else => return error.WrongType,
462 };
463 return value.toIntern();
464}
465
466fn lowerNull(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
467 switch (node.get(self.file.zoir.?)) {
468 .null => return .null_value,
469 else => return error.WrongType,
470 }
471}
472
473fn lowerArray(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
474 const array_info = res_ty.arrayInfo(self.sema.pt.zcu);
475 const nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
476 .array_literal => |nodes| nodes,
477 .empty_literal => .{ .start = node, .len = 0 },
478 else => return error.WrongType,
479 };
480
481 if (nodes.len != array_info.len) {
482 return error.WrongType;
483 }
484
485 const elems = try self.sema.arena.alloc(
486 InternPool.Index,
487 nodes.len + @intFromBool(array_info.sentinel != null),
488 );
489
490 for (0..nodes.len) |i| {
491 elems[i] = try self.lowerExpr(nodes.at(@intCast(i)), array_info.elem_type);
492 }
493
494 if (array_info.sentinel) |sentinel| {
495 elems[elems.len - 1] = sentinel.toIntern();
496 }
497
498 return self.sema.pt.intern(.{ .aggregate = .{
499 .ty = res_ty.toIntern(),
500 .storage = .{ .elems = elems },
501 } });
502}
503
504fn lowerEnum(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
505 const ip = &self.sema.pt.zcu.intern_pool;
506 switch (node.get(self.file.zoir.?)) {
507 .enum_literal => |field_name| {
508 const field_name_interned = try ip.getOrPutString(
509 self.sema.gpa,
510 self.sema.pt.tid,
511 field_name.get(self.file.zoir.?),
512 .no_embedded_nulls,
513 );
514 const field_index = res_ty.enumFieldIndex(field_name_interned, self.sema.pt.zcu) orelse {
515 return self.fail(
516 node,
517 "enum {} has no member named '{}'",
518 .{
519 res_ty.fmt(self.sema.pt),
520 std.zig.fmtId(field_name.get(self.file.zoir.?)),
521 },
522 );
523 };
524
525 const value = try self.sema.pt.enumValueFieldIndex(res_ty, field_index);
526
527 return value.toIntern();
528 },
529 else => return error.WrongType,
530 }
531}
532
533fn lowerEnumLiteral(self: *LowerZon, node: Zoir.Node.Index) !InternPool.Index {
534 const ip = &self.sema.pt.zcu.intern_pool;
535 switch (node.get(self.file.zoir.?)) {
536 .enum_literal => |field_name| {
537 const field_name_interned = try ip.getOrPutString(
538 self.sema.gpa,
539 self.sema.pt.tid,
540 field_name.get(self.file.zoir.?),
541 .no_embedded_nulls,
542 );
543 return self.sema.pt.intern(.{ .enum_literal = field_name_interned });
544 },
545 else => return error.WrongType,
546 }
547}
548
549fn lowerStructOrTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
550 const ip = &self.sema.pt.zcu.intern_pool;
551 return switch (ip.indexToKey(res_ty.toIntern())) {
552 .tuple_type => self.lowerTuple(node, res_ty),
553 .struct_type => self.lowerStruct(node, res_ty),
554 else => unreachable,
555 };
556}
557
558fn lowerTuple(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
559 const ip = &self.sema.pt.zcu.intern_pool;
560
561 const tuple_info = ip.indexToKey(res_ty.toIntern()).tuple_type;
562
563 const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
564 .array_literal => |nodes| nodes,
565 .empty_literal => .{ .start = node, .len = 0 },
566 else => return error.WrongType,
567 };
568
569 const field_types = tuple_info.types.get(ip);
570 const elems = try self.sema.arena.alloc(InternPool.Index, field_types.len);
571
572 const field_comptime_vals = tuple_info.values.get(ip);
573 if (field_comptime_vals.len > 0) {
574 @memcpy(elems, field_comptime_vals);
575 } else {
576 @memset(elems, .none);
577 }
578
579 for (0..elem_nodes.len) |i| {
580 if (i >= elems.len) {
581 const elem_node = elem_nodes.at(@intCast(i));
582 return self.fail(
583 elem_node,
584 "index {} outside tuple of length {}",
585 .{
586 elems.len,
587 elem_nodes.at(@intCast(i)).getAstNode(self.file.zoir.?),
588 },
589 );
590 }
591
592 const val = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(field_types[i]));
593
594 if (elems[i] != .none and val != elems[i]) {
595 const elem_node = elem_nodes.at(@intCast(i));
596 return self.fail(
597 elem_node,
598 "value stored in comptime field does not match the default value of the field",
599 .{},
600 );
601 }
602
603 elems[i] = val;
604 }
605
606 for (elems, 0..) |val, i| {
607 if (val == .none) {
608 return self.fail(node, "missing tuple field with index {}", .{i});
609 }
610 }
611
612 return self.sema.pt.intern(.{ .aggregate = .{
613 .ty = res_ty.toIntern(),
614 .storage = .{ .elems = elems },
615 } });
616}
617
618fn lowerStruct(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
619 const ip = &self.sema.pt.zcu.intern_pool;
620 const gpa = self.sema.gpa;
621
622 try res_ty.resolveFields(self.sema.pt);
623 try res_ty.resolveStructFieldInits(self.sema.pt);
624 const struct_info = self.sema.pt.zcu.typeToStruct(res_ty).?;
625
626 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
627 .struct_literal => |fields| fields,
628 .empty_literal => .{ .names = &.{}, .vals = .{ .start = node, .len = 0 } },
629 else => return error.WrongType,
630 };
631
632 const field_values = try self.sema.arena.alloc(InternPool.Index, struct_info.field_names.len);
633
634 const field_defaults = struct_info.field_inits.get(ip);
635 if (field_defaults.len > 0) {
636 @memcpy(field_values, field_defaults);
637 } else {
638 @memset(field_values, .none);
639 }
640
641 for (0..fields.names.len) |i| {
642 const field_name = try ip.getOrPutString(
643 gpa,
644 self.sema.pt.tid,
645 fields.names[i].get(self.file.zoir.?),
646 .no_embedded_nulls,
647 );
648 const field_node = fields.vals.at(@intCast(i));
649
650 const name_index = struct_info.nameIndex(ip, field_name) orelse {
651 return self.fail(field_node, "unexpected field '{}'", .{field_name.fmt(ip)});
652 };
653
654 const field_type: Type = .fromInterned(struct_info.field_types.get(ip)[name_index]);
655 field_values[name_index] = try self.lowerExpr(field_node, field_type);
656
657 if (struct_info.comptime_bits.getBit(ip, name_index)) {
658 const val = ip.indexToKey(field_values[name_index]);
659 const default = ip.indexToKey(field_defaults[name_index]);
660 if (!val.eql(default, ip)) {
661 return self.fail(
662 field_node,
663 "value stored in comptime field does not match the default value of the field",
664 .{},
665 );
666 }
667 }
668 }
669
670 const field_names = struct_info.field_names.get(ip);
671 for (field_values, field_names) |*value, name| {
672 if (value.* == .none) return self.fail(node, "missing field '{}'", .{name.fmt(ip)});
673 }
674
675 return self.sema.pt.intern(.{ .aggregate = .{
676 .ty = res_ty.toIntern(),
677 .storage = .{
678 .elems = field_values,
679 },
680 } });
681}
682
683fn lowerSlice(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
684 const ip = &self.sema.pt.zcu.intern_pool;
685 const gpa = self.sema.gpa;
686
687 const ptr_info = res_ty.ptrInfo(self.sema.pt.zcu);
688
689 assert(ptr_info.flags.size == .slice);
690
691 // String literals
692 const string_alignment = ptr_info.flags.alignment == .none or ptr_info.flags.alignment == .@"1";
693 const string_sentinel = ptr_info.sentinel == .none or ptr_info.sentinel == .zero_u8;
694 if (string_alignment and ptr_info.child == .u8_type and string_sentinel) {
695 switch (node.get(self.file.zoir.?)) {
696 .string_literal => |val| {
697 const ip_str = try ip.getOrPutString(gpa, self.sema.pt.tid, val, .maybe_embedded_nulls);
698 const str_ref = try self.sema.addStrLit(ip_str, val.len);
699 return (try self.sema.coerce(
700 self.block,
701 res_ty,
702 str_ref,
703 self.nodeSrc(node),
704 )).toInterned().?;
705 },
706 else => {},
707 }
708 }
709
710 // Slice literals
711 const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
712 .array_literal => |nodes| nodes,
713 .empty_literal => .{ .start = node, .len = 0 },
714 else => return error.WrongType,
715 };
716
717 const elems = try self.sema.arena.alloc(InternPool.Index, elem_nodes.len + @intFromBool(ptr_info.sentinel != .none));
718
719 for (elems, 0..) |*elem, i| {
720 elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(ptr_info.child));
721 }
722
723 if (ptr_info.sentinel != .none) {
724 elems[elems.len - 1] = ptr_info.sentinel;
725 }
726
727 const array_ty = try self.sema.pt.intern(.{ .array_type = .{
728 .len = elems.len,
729 .sentinel = ptr_info.sentinel,
730 .child = ptr_info.child,
731 } });
732
733 const array = try self.sema.pt.intern(.{ .aggregate = .{
734 .ty = array_ty,
735 .storage = .{ .elems = elems },
736 } });
737
738 const many_item_ptr_type = try self.sema.pt.intern(.{ .ptr_type = .{
739 .child = ptr_info.child,
740 .sentinel = ptr_info.sentinel,
741 .flags = b: {
742 var flags = ptr_info.flags;
743 flags.size = .many;
744 break :b flags;
745 },
746 .packed_offset = ptr_info.packed_offset,
747 } });
748
749 const many_item_ptr = try self.sema.pt.intern(.{
750 .ptr = .{
751 .ty = many_item_ptr_type,
752 .base_addr = .{
753 .uav = .{
754 .orig_ty = (try self.sema.pt.singleConstPtrType(.fromInterned(array_ty))).toIntern(),
755 .val = array,
756 },
757 },
758 .byte_offset = 0,
759 },
760 });
761
762 const len = (try self.sema.pt.intValue(.usize, elems.len)).toIntern();
763
764 return self.sema.pt.intern(.{ .slice = .{
765 .ty = res_ty.toIntern(),
766 .ptr = many_item_ptr,
767 .len = len,
768 } });
769}
770
771fn lowerUnion(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
772 const ip = &self.sema.pt.zcu.intern_pool;
773 try res_ty.resolveFields(self.sema.pt);
774 const union_info = self.sema.pt.zcu.typeToUnion(res_ty).?;
775 const enum_tag_info = union_info.loadTagType(ip);
776
777 const field_name, const maybe_field_node = switch (node.get(self.file.zoir.?)) {
778 .enum_literal => |name| b: {
779 const field_name = try ip.getOrPutString(
780 self.sema.gpa,
781 self.sema.pt.tid,
782 name.get(self.file.zoir.?),
783 .no_embedded_nulls,
784 );
785 break :b .{ field_name, null };
786 },
787 .struct_literal => b: {
788 const fields: @FieldType(Zoir.Node, "struct_literal") = switch (node.get(self.file.zoir.?)) {
789 .struct_literal => |fields| fields,
790 else => return self.fail(node, "expected type '{}'", .{res_ty.fmt(self.sema.pt)}),
791 };
792 if (fields.names.len != 1) {
793 return error.WrongType;
794 }
795 const field_name = try ip.getOrPutString(
796 self.sema.gpa,
797 self.sema.pt.tid,
798 fields.names[0].get(self.file.zoir.?),
799 .no_embedded_nulls,
800 );
801 break :b .{ field_name, fields.vals.at(0) };
802 },
803 else => return error.WrongType,
804 };
805
806 const name_index = enum_tag_info.nameIndex(ip, field_name) orelse {
807 return error.WrongType;
808 };
809 const tag = try self.sema.pt.enumValueFieldIndex(.fromInterned(union_info.enum_tag_ty), name_index);
810 const field_type: Type = .fromInterned(union_info.field_types.get(ip)[name_index]);
811 const val = if (maybe_field_node) |field_node| b: {
812 if (field_type.toIntern() == .void_type) {
813 return self.fail(field_node, "expected type 'void'", .{});
814 }
815 break :b try self.lowerExpr(field_node, field_type);
816 } else b: {
817 if (field_type.toIntern() != .void_type) {
818 return error.WrongType;
819 }
820 break :b .void_value;
821 };
822 return ip.getUnion(self.sema.pt.zcu.gpa, self.sema.pt.tid, .{
823 .ty = res_ty.toIntern(),
824 .tag = tag.toIntern(),
825 .val = val,
826 });
827}
828
829fn lowerVector(self: *LowerZon, node: Zoir.Node.Index, res_ty: Type) !InternPool.Index {
830 const ip = &self.sema.pt.zcu.intern_pool;
831
832 const vector_info = ip.indexToKey(res_ty.toIntern()).vector_type;
833
834 const elem_nodes: Zoir.Node.Index.Range = switch (node.get(self.file.zoir.?)) {
835 .array_literal => |nodes| nodes,
836 .empty_literal => .{ .start = node, .len = 0 },
837 else => return error.WrongType,
838 };
839
840 const elems = try self.sema.arena.alloc(InternPool.Index, vector_info.len);
841
842 if (elem_nodes.len != vector_info.len) {
843 return self.fail(
844 node,
845 "expected {} vector elements; found {}",
846 .{ vector_info.len, elem_nodes.len },
847 );
848 }
849
850 for (elems, 0..) |*elem, i| {
851 elem.* = try self.lowerExpr(elem_nodes.at(@intCast(i)), .fromInterned(vector_info.child));
852 }
853
854 return self.sema.pt.intern(.{ .aggregate = .{
855 .ty = res_ty.toIntern(),
856 .storage = .{ .elems = elems },
857 } });
858}
src/Value.zig+5-24
......@@ -270,7 +270,7 @@ pub fn getUnsignedIntInner(
270270 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
271271 .undef => unreachable,
272272 .int => |int| switch (int.storage) {
273 .big_int => |big_int| big_int.to(u64) catch null,
273 .big_int => |big_int| big_int.toInt(u64) catch null,
274274 .u64 => |x| x,
275275 .i64 => |x| std.math.cast(u64, x),
276276 .lazy_align => |ty| (try Type.fromInterned(ty).abiAlignmentInner(strat.toLazy(), zcu, tid)).scalar.toByteUnits() orelse 0,
......@@ -311,7 +311,7 @@ pub fn toSignedInt(val: Value, zcu: *const Zcu) i64 {
311311 .bool_true => 1,
312312 else => switch (zcu.intern_pool.indexToKey(val.toIntern())) {
313313 .int => |int| switch (int.storage) {
314 .big_int => |big_int| big_int.to(i64) catch unreachable,
314 .big_int => |big_int| big_int.toInt(i64) catch unreachable,
315315 .i64 => |x| x,
316316 .u64 => |x| @intCast(x),
317317 .lazy_align => |ty| @intCast(Type.fromInterned(ty).abiAlignment(zcu).toByteUnits() orelse 0),
......@@ -898,7 +898,7 @@ pub fn readFromPackedMemory(
898898pub fn toFloat(val: Value, comptime T: type, zcu: *Zcu) T {
899899 return switch (zcu.intern_pool.indexToKey(val.toIntern())) {
900900 .int => |int| switch (int.storage) {
901 .big_int => |big_int| @floatCast(bigIntToFloat(big_int.limbs, big_int.positive)),
901 .big_int => |big_int| big_int.toFloat(T),
902902 inline .u64, .i64 => |x| {
903903 if (T == f80) {
904904 @panic("TODO we can't lower this properly on non-x86 llvm backend yet");
......@@ -915,25 +915,6 @@ pub fn toFloat(val: Value, comptime T: type, zcu: *Zcu) T {
915915 };
916916}
917917
918/// TODO move this to std lib big int code
919fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 {
920 if (limbs.len == 0) return 0;
921
922 const base = std.math.maxInt(std.math.big.Limb) + 1;
923 var result: f128 = 0;
924 var i: usize = limbs.len;
925 while (i != 0) {
926 i -= 1;
927 const limb: f128 = @floatFromInt(limbs[i]);
928 result = @mulAdd(f128, base, result, limb);
929 }
930 if (positive) {
931 return result;
932 } else {
933 return -result;
934 }
935}
936
937918pub fn clz(val: Value, ty: Type, zcu: *Zcu) u64 {
938919 var bigint_buf: BigIntSpace = undefined;
939920 const bigint = val.toBigInt(&bigint_buf, zcu);
......@@ -1548,7 +1529,7 @@ pub fn floatFromIntScalar(val: Value, float_ty: Type, pt: Zcu.PerThread, comptim
15481529 .undef => try pt.undefValue(float_ty),
15491530 .int => |int| switch (int.storage) {
15501531 .big_int => |big_int| {
1551 const float = bigIntToFloat(big_int.limbs, big_int.positive);
1532 const float = big_int.toFloat(f128);
15521533 return pt.floatValue(float_ty, float);
15531534 },
15541535 inline .u64, .i64 => |x| floatFromIntInner(x, float_ty, pt),
......@@ -4583,7 +4564,7 @@ pub fn interpret(val: Value, comptime T: type, pt: Zcu.PerThread) error{ OutOfMe
45834564 .int => switch (ip.indexToKey(val.toIntern()).int.storage) {
45844565 .lazy_align, .lazy_size => unreachable, // `val` is fully resolved
45854566 inline .u64, .i64 => |x| std.math.cast(T, x) orelse return error.TypeMismatch,
4586 .big_int => |big| big.to(T) catch return error.TypeMismatch,
4567 .big_int => |big| big.toInt(T) catch return error.TypeMismatch,
45874568 },
45884569
45894570 .float => val.toFloat(T, zcu),
src/Zcu.zig+37-3
......@@ -39,6 +39,8 @@ const AnalUnit = InternPool.AnalUnit;
3939const BuiltinFn = std.zig.BuiltinFn;
4040const LlvmObject = @import("codegen/llvm.zig").Object;
4141const dev = @import("dev.zig");
42const Zoir = std.zig.Zoir;
43const ZonGen = std.zig.ZonGen;
4244
4345comptime {
4446 @setEvalBranchQuota(4000);
......@@ -672,6 +674,8 @@ pub const File = struct {
672674 tree: Ast,
673675 /// Whether this is populated or not depends on `zir_loaded`.
674676 zir: Zir,
677 /// Cached Zoir, generated lazily.
678 zoir: ?Zoir = null,
675679 /// Module that this file is a part of, managed externally.
676680 mod: *Package.Module,
677681 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen.
......@@ -704,7 +708,19 @@ pub const File = struct {
704708 root: *Package.Module,
705709 };
706710
711 pub fn getMode(self: File) Ast.Mode {
712 if (std.mem.endsWith(u8, self.sub_file_path, ".zon")) {
713 return .zon;
714 } else if (std.mem.endsWith(u8, self.sub_file_path, ".zig")) {
715 return .zig;
716 } else {
717 // `Module.importFile` rejects all other extensions
718 unreachable;
719 }
720 }
721
707722 pub fn unload(file: *File, gpa: Allocator) void {
723 if (file.zoir) |zoir| zoir.deinit(gpa);
708724 file.unloadTree(gpa);
709725 file.unloadSource(gpa);
710726 file.unloadZir(gpa);
......@@ -778,11 +794,24 @@ pub const File = struct {
778794 if (file.tree_loaded) return &file.tree;
779795
780796 const source = try file.getSource(gpa);
781 file.tree = try Ast.parse(gpa, source.bytes, .zig);
797 file.tree = try Ast.parse(gpa, source.bytes, file.getMode());
782798 file.tree_loaded = true;
783799 return &file.tree;
784800 }
785801
802 pub fn getZoir(file: *File, zcu: *Zcu) !*const Zoir {
803 if (file.zoir) |*zoir| return zoir;
804
805 assert(file.tree_loaded);
806 assert(file.tree.mode == .zon);
807 file.zoir = try ZonGen.generate(zcu.gpa, file.tree, .{});
808 if (file.zoir.?.hasCompileErrors()) {
809 try zcu.failed_files.putNoClobber(zcu.gpa, file, null);
810 return error.AnalysisFail;
811 }
812 return &file.zoir.?;
813 }
814
786815 pub fn fullyQualifiedNameLen(file: File) usize {
787816 const ext = std.fs.path.extension(file.sub_file_path);
788817 return file.sub_file_path.len - ext.len;
......@@ -895,6 +924,7 @@ pub const File = struct {
895924 pub const Index = InternPool.FileIndex;
896925};
897926
927/// Represents the contents of a file loaded with `@embedFile`.
898928pub const EmbedFile = struct {
899929 /// Module that this file is a part of, managed externally.
900930 owner: *Package.Module,
......@@ -2372,6 +2402,12 @@ pub const LazySrcLoc = struct {
23722402 break :inst .{ info.file, info.inst };
23732403 };
23742404 const file = zcu.fileByIndex(file_index);
2405
2406 // If we're relative to .main_struct_inst, we know the ast node is the root and don't need to resolve the ZIR,
2407 // which may not exist e.g. in the case of errors in ZON files.
2408 if (zir_inst == .main_struct_inst) return .{ file, 0 };
2409
2410 // Otherwise, make sure ZIR is loaded.
23752411 assert(file.zir_loaded);
23762412
23772413 const zir = file.zir;
......@@ -3461,8 +3497,6 @@ pub fn atomicPtrAlignment(
34613497}
34623498
34633499/// Returns null in the following cases:
3464/// * `@TypeOf(.{})`
3465/// * A struct which has no fields (`struct {}`).
34663500/// * Not a struct.
34673501pub fn typeToStruct(zcu: *const Zcu, ty: Type) ?InternPool.LoadedStructType {
34683502 if (ty.ip_index == .none) return null;
src/Zcu/PerThread.zig+4-1
......@@ -1867,6 +1867,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
18671867 const zcu = pt.zcu;
18681868 const gpa = zcu.gpa;
18691869 const file = zcu.fileByIndex(file_index);
1870 assert(file.getMode() == .zig);
18701871 assert(zcu.fileRootType(file_index) == .none);
18711872
18721873 if (file.status != .success_zir) {
......@@ -2022,7 +2023,9 @@ pub fn importFile(
20222023 if (mod.deps.get(import_string)) |pkg| {
20232024 return pt.importPkg(pkg);
20242025 }
2025 if (!std.mem.endsWith(u8, import_string, ".zig")) {
2026 if (!std.mem.endsWith(u8, import_string, ".zig") and
2027 !std.mem.endsWith(u8, import_string, ".zon"))
2028 {
20262029 return error.ModuleNotFound;
20272030 }
20282031 const gpa = zcu.gpa;
src/codegen/llvm/Builder.zig+4-4
......@@ -13776,8 +13776,8 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1377613776 };
1377713777 const bit_count = extra.type.scalarBits(self);
1377813778 const val: i64 = if (bit_count <= 64)
13779 bigint.to(i64) catch unreachable
13780 else if (bigint.to(u64)) |val|
13779 bigint.toInt(i64) catch unreachable
13780 else if (bigint.toInt(u64)) |val|
1378113781 @bitCast(val)
1378213782 else |_| {
1378313783 const limbs = try record.addManyAsSlice(
......@@ -14276,9 +14276,9 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co
1427614276 else => unreachable,
1427714277 },
1427814278 };
14279 const val: i64 = if (bigint.to(i64)) |val|
14279 const val: i64 = if (bigint.toInt(i64)) |val|
1428014280 val
14281 else |_| if (bigint.to(u64)) |val|
14281 else |_| if (bigint.toInt(u64)) |val|
1428214282 @bitCast(val)
1428314283 else |_| {
1428414284 const limbs_len = std.math.divCeil(u32, extra.bit_width, 64) catch unreachable;
src/fmt.zig+2-2
......@@ -120,7 +120,7 @@ pub fn run(
120120 process.exit(2);
121121 }
122122 } else {
123 const zoir = try std.zig.ZonGen.generate(gpa, tree);
123 const zoir = try std.zig.ZonGen.generate(gpa, tree, .{});
124124 defer zoir.deinit(gpa);
125125
126126 if (zoir.hasCompileErrors()) {
......@@ -335,7 +335,7 @@ fn fmtPathFile(
335335 }
336336 },
337337 .zon => {
338 var zoir = try std.zig.ZonGen.generate(gpa, tree);
338 var zoir = try std.zig.ZonGen.generate(gpa, tree, .{});
339339 defer zoir.deinit(gpa);
340340
341341 if (zoir.hasCompileErrors()) {
src/main.zig+1-1
......@@ -6278,7 +6278,7 @@ fn cmdAstCheck(
62786278 }
62796279 },
62806280 .zon => {
6281 const zoir = try ZonGen.generate(gpa, file.tree);
6281 const zoir = try ZonGen.generate(gpa, file.tree, .{});
62826282 defer zoir.deinit(gpa);
62836283
62846284 if (zoir.hasCompileErrors()) {
src/print_zir.zig+11-1
......@@ -488,7 +488,6 @@ const Writer = struct {
488488 .enum_literal,
489489 .decl_ref,
490490 .decl_val,
491 .import,
492491 .ret_err_value,
493492 .ret_err_value_code,
494493 .param_anytype,
......@@ -515,6 +514,8 @@ const Writer = struct {
515514 .declaration => try self.writeDeclaration(stream, inst),
516515
517516 .extended => try self.writeExtended(stream, inst),
517
518 .import => try self.writeImport(stream, inst),
518519 }
519520 }
520521
......@@ -2842,4 +2843,13 @@ const Writer = struct {
28422843 try stream.writeByte('\n');
28432844 }
28442845 }
2846
2847 fn writeImport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
2848 const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
2849 const extra = self.code.extraData(Zir.Inst.Import, inst_data.payload_index).data;
2850 try self.writeInstRef(stream, extra.res_ty);
2851 const import_path = self.code.nullTerminatedString(extra.path);
2852 try stream.print(", \"{}\") ", .{std.zig.fmtEscapes(import_path)});
2853 try self.writeSrcTok(stream, inst_data.src_tok);
2854 }
28452855};
test/behavior/zon.zig created+519
......@@ -0,0 +1,519 @@
1const std = @import("std");
2
3const expect = std.testing.expect;
4const expectEqual = std.testing.expectEqual;
5const expectEqualDeep = std.testing.expectEqualDeep;
6const expectEqualSlices = std.testing.expectEqualSlices;
7const expectEqualStrings = std.testing.expectEqualStrings;
8
9test "bool" {
10 try expectEqual(true, @as(bool, @import("zon/true.zon")));
11 try expectEqual(false, @as(bool, @import("zon/false.zon")));
12}
13
14test "optional" {
15 const some: ?u32 = @import("zon/some.zon");
16 const none: ?u32 = @import("zon/none.zon");
17 const @"null": @TypeOf(null) = @import("zon/none.zon");
18 try expectEqual(@as(u32, 10), some);
19 try expectEqual(@as(?u32, null), none);
20 try expectEqual(null, @"null");
21}
22
23test "union" {
24 // No tag
25 {
26 const Union = union {
27 x: f32,
28 y: bool,
29 z: void,
30 };
31
32 const union1: Union = @import("zon/union1.zon");
33 const union2: Union = @import("zon/union2.zon");
34 const union3: Union = @import("zon/union3.zon");
35
36 try expectEqual(1.5, union1.x);
37 try expectEqual(true, union2.y);
38 try expectEqual({}, union3.z);
39 }
40
41 // Inferred tag
42 {
43 const Union = union(enum) {
44 x: f32,
45 y: bool,
46 z: void,
47 };
48
49 const union1: Union = comptime @import("zon/union1.zon");
50 const union2: Union = @import("zon/union2.zon");
51 const union3: Union = @import("zon/union3.zon");
52
53 try expectEqual(1.5, union1.x);
54 try expectEqual(true, union2.y);
55 try expectEqual({}, union3.z);
56 }
57
58 // Explicit tag
59 {
60 const Tag = enum(i128) {
61 x = -1,
62 y = 2,
63 z = 1,
64 };
65 const Union = union(Tag) {
66 x: f32,
67 y: bool,
68 z: void,
69 };
70
71 const union1: Union = @import("zon/union1.zon");
72 const union2: Union = @import("zon/union2.zon");
73 const union3: Union = @import("zon/union3.zon");
74
75 try expectEqual(1.5, union1.x);
76 try expectEqual(true, union2.y);
77 try expectEqual({}, union3.z);
78 }
79}
80
81test "struct" {
82 const Vec0 = struct {};
83 const Vec1 = struct { x: f32 };
84 const Vec2 = struct { x: f32, y: f32 };
85 const Escaped = struct { @"0": f32, foo: f32 };
86 try expectEqual(Vec0{}, @as(Vec0, @import("zon/vec0.zon")));
87 try expectEqual(Vec1{ .x = 1.5 }, @as(Vec1, @import("zon/vec1.zon")));
88 try expectEqual(Vec2{ .x = 1.5, .y = 2 }, @as(Vec2, @import("zon/vec2.zon")));
89 try expectEqual(Escaped{ .@"0" = 1.5, .foo = 2 }, @as(Escaped, @import("zon/escaped_struct.zon")));
90}
91
92test "struct default fields" {
93 const Vec3 = struct {
94 x: f32,
95 y: f32,
96 z: f32 = 123.4,
97 };
98 try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, @as(Vec3, @import("zon/vec2.zon")));
99 const ascribed: Vec3 = @import("zon/vec2.zon");
100 try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, ascribed);
101
102 const Vec2 = struct {
103 x: f32 = 20.0,
104 y: f32 = 10.0,
105 };
106 try expectEqual(Vec2{ .x = 1.5, .y = 2.0 }, @as(Vec2, @import("zon/vec2.zon")));
107}
108
109test "struct enum field" {
110 const Struct = struct {
111 x: enum { x, y, z },
112 };
113 try expectEqual(Struct{ .x = .z }, @as(Struct, @import("zon/enum_field.zon")));
114}
115
116test "tuple" {
117 const Tuple = struct { f32, bool, []const u8, u16 };
118 try expectEqualDeep(Tuple{ 1.2, true, "hello", 3 }, @as(Tuple, @import("zon/tuple.zon")));
119}
120
121test "comptime fields" {
122 // Test setting comptime tuple fields to the correct value
123 {
124 const Tuple = struct {
125 comptime f32 = 1.2,
126 comptime bool = true,
127 comptime []const u8 = "hello",
128 comptime u16 = 3,
129 };
130 try expectEqualDeep(Tuple{ 1.2, true, "hello", 3 }, @as(Tuple, @import("zon/tuple.zon")));
131 }
132
133 // Test setting comptime struct fields to the correct value
134 {
135 const Vec2 = struct {
136 comptime x: f32 = 1.5,
137 comptime y: f32 = 2.0,
138 };
139 try expectEqualDeep(Vec2{}, @as(Vec2, @import("zon/vec2.zon")));
140 }
141
142 // Test allowing comptime tuple fields to be set to their defaults
143 {
144 const Tuple = struct {
145 f32,
146 bool,
147 []const u8,
148 u16,
149 comptime u8 = 255,
150 };
151 try expectEqualDeep(Tuple{ 1.2, true, "hello", 3 }, @as(Tuple, @import("zon/tuple.zon")));
152 }
153
154 // Test allowing comptime struct fields to be set to their defaults
155 {
156 const Vec2 = struct {
157 comptime x: f32 = 1.5,
158 comptime y: f32 = 2.0,
159 };
160 try expectEqualDeep(Vec2{}, @as(Vec2, @import("zon/slice-empty.zon")));
161 }
162}
163
164test "char" {
165 try expectEqual(@as(u8, 'a'), @as(u8, @import("zon/a.zon")));
166 try expectEqual(@as(u8, 'z'), @as(u8, @import("zon/z.zon")));
167}
168
169test "arrays" {
170 try expectEqual([0]u8{}, @as([0]u8, @import("zon/vec0.zon")));
171 try expectEqual([0:1]u8{}, @as([0:1]u8, @import("zon/vec0.zon")));
172 try expectEqual(1, @as([0:1]u8, @import("zon/vec0.zon"))[0]);
173 try expectEqual([4]u8{ 'a', 'b', 'c', 'd' }, @as([4]u8, @import("zon/array.zon")));
174 try expectEqual([4:2]u8{ 'a', 'b', 'c', 'd' }, @as([4:2]u8, @import("zon/array.zon")));
175 try expectEqual(2, @as([4:2]u8, @import("zon/array.zon"))[4]);
176}
177
178test "slices, arrays, tuples" {
179 {
180 const expected_slice: []const u8 = &.{};
181 const found_slice: []const u8 = @import("zon/slice-empty.zon");
182 try expectEqualSlices(u8, expected_slice, found_slice);
183
184 const expected_array: [0]u8 = .{};
185 const found_array: [0]u8 = @import("zon/slice-empty.zon");
186 try expectEqual(expected_array, found_array);
187
188 const T = struct {};
189 const expected_tuple: T = .{};
190 const found_tuple: T = @import("zon/slice-empty.zon");
191 try expectEqual(expected_tuple, found_tuple);
192 }
193
194 {
195 const expected_slice: []const u8 = &.{1};
196 const found_slice: []const u8 = @import("zon/slice1_no_newline.zon");
197 try expectEqualSlices(u8, expected_slice, found_slice);
198
199 const expected_array: [1]u8 = .{1};
200 const found_array: [1]u8 = @import("zon/slice1_no_newline.zon");
201 try expectEqual(expected_array, found_array);
202
203 const T = struct { u8 };
204 const expected_tuple: T = .{1};
205 const found_tuple: T = @import("zon/slice1_no_newline.zon");
206 try expectEqual(expected_tuple, found_tuple);
207 }
208
209 {
210 const expected_slice: []const u8 = &.{ 'a', 'b', 'c' };
211 const found_slice: []const u8 = @import("zon/slice-abc.zon");
212 try expectEqualSlices(u8, expected_slice, found_slice);
213
214 const expected_array: [3]u8 = .{ 'a', 'b', 'c' };
215 const found_array: [3]u8 = @import("zon/slice-abc.zon");
216 try expectEqual(expected_array, found_array);
217
218 const T = struct { u8, u8, u8 };
219 const expected_tuple: T = .{ 'a', 'b', 'c' };
220 const found_tuple: T = @import("zon/slice-abc.zon");
221 try expectEqual(expected_tuple, found_tuple);
222 }
223}
224
225test "string literals" {
226 try expectEqualSlices(u8, "abc", @import("zon/abc.zon"));
227 try expectEqualSlices(u8, "ab\\c", @import("zon/abc-escaped.zon"));
228 const zero_terminated: [:0]const u8 = @import("zon/abc.zon");
229 try expectEqualDeep(zero_terminated, "abc");
230 try expectEqual(0, zero_terminated[zero_terminated.len]);
231 try expectEqualStrings(
232 \\Hello, world!
233 \\This is a multiline string!
234 \\ There are no escapes, we can, for example, include \n in the string
235 , @import("zon/multiline_string.zon"));
236 try expectEqualStrings("a\nb\x00c", @import("zon/string_embedded_null.zon"));
237}
238
239test "enum literals" {
240 const Enum = enum {
241 foo,
242 bar,
243 baz,
244 @"0\na",
245 };
246 try expectEqual(Enum.foo, @as(Enum, @import("zon/foo.zon")));
247 try expectEqual(.foo, @as(@TypeOf(.foo), @import("zon/foo.zon")));
248 try expectEqual(Enum.@"0\na", @as(Enum, @import("zon/escaped_enum.zon")));
249}
250
251test "int" {
252 const T = struct {
253 u8,
254 i16,
255 i14,
256 i32,
257 i8,
258 i8,
259 u8,
260 u8,
261 u65,
262 u65,
263 i128,
264 i128,
265 i66,
266 i66,
267 i8,
268 i8,
269 i16,
270 i16,
271 i16,
272 i16,
273 i16,
274 i16,
275 u65,
276 i66,
277 i66,
278 u65,
279 i66,
280 i66,
281 u65,
282 i66,
283 i66,
284 };
285 const expected: T = .{
286 // Test various numbers and types
287 10,
288 24,
289 -4,
290 -123,
291
292 // Test limits
293 127,
294 -128,
295
296 // Test characters
297 'a',
298 'z',
299
300 // Test big integers
301 36893488147419103231,
302 36893488147419103231,
303 -18446744073709551615, // Only a big int due to negation
304 -9223372036854775809, // Only a big int due to negation
305
306 // Test big integer limits
307 36893488147419103231,
308 -36893488147419103232,
309
310 // Test parsing whole number floats as integers
311 -1,
312 123,
313
314 // Test non-decimal integers
315 0xff,
316 -0xff,
317 0o77,
318 -0o77,
319 0b11,
320 -0b11,
321
322 // Test non-decimal big integers
323 0x1ffffffffffffffff,
324 0x1ffffffffffffffff,
325 -0x1ffffffffffffffff,
326 0x1ffffffffffffffff,
327 0x1ffffffffffffffff,
328 -0x1ffffffffffffffff,
329 0x1ffffffffffffffff,
330 0x1ffffffffffffffff,
331 -0x1ffffffffffffffff,
332 };
333 const actual: T = @import("zon/ints.zon");
334 try expectEqual(expected, actual);
335}
336
337test "floats" {
338 const T = struct {
339 f16,
340 f32,
341 f64,
342 f128,
343 f16,
344 f16,
345 f32,
346 f32,
347 f32,
348 f32,
349 f32,
350 f32,
351 f128,
352 f32,
353 f32,
354 f32,
355 f32,
356 f32,
357 };
358 const expected: T = .{
359 // Test decimals
360 0.5,
361 123.456,
362 -123.456,
363 42.5,
364
365 // Test whole numbers with and without decimals
366 5.0,
367 5.0,
368 -102,
369 -102,
370
371 // Test characters and negated characters
372 'a',
373 'z',
374
375 // Test big integers
376 36893488147419103231,
377 -36893488147419103231,
378 0x1ffffffffffffffff,
379 0x1ffffffffffffffff,
380
381 // Exponents, underscores
382 123.0E+77,
383
384 // Hexadecimal
385 0x103.70p-5,
386 -0x103.70,
387 0x1234_5678.9ABC_CDEFp-10,
388 };
389 const actual: T = @import("zon/floats.zon");
390 try expectEqual(expected, actual);
391}
392
393test "inf and nan" {
394 // f32
395 {
396 const actual: struct { f32, f32, f32 } = @import("zon/inf_and_nan.zon");
397 try expect(std.math.isNan(actual[0]));
398 try expect(std.math.isPositiveInf(actual[1]));
399 try expect(std.math.isNegativeInf(actual[2]));
400 }
401
402 // f128
403 {
404 const actual: struct { f128, f128, f128 } = @import("zon/inf_and_nan.zon");
405 try expect(std.math.isNan(actual[0]));
406 try expect(std.math.isPositiveInf(actual[1]));
407 try expect(std.math.isNegativeInf(actual[2]));
408 }
409}
410
411test "vector" {
412 {
413 const actual: @Vector(0, bool) = @import("zon/vec0.zon");
414 const expected: @Vector(0, bool) = .{};
415 try expectEqual(expected, actual);
416 }
417 {
418 const actual: @Vector(3, bool) = @import("zon/vec3_bool.zon");
419 const expected: @Vector(3, bool) = .{ false, false, true };
420 try expectEqual(expected, actual);
421 }
422
423 {
424 const actual: @Vector(0, f32) = @import("zon/vec0.zon");
425 const expected: @Vector(0, f32) = .{};
426 try expectEqual(expected, actual);
427 }
428 {
429 const actual: @Vector(3, f32) = @import("zon/vec3_float.zon");
430 const expected: @Vector(3, f32) = .{ 1.5, 2.5, 3.5 };
431 try expectEqual(expected, actual);
432 }
433
434 {
435 const actual: @Vector(0, u8) = @import("zon/vec0.zon");
436 const expected: @Vector(0, u8) = .{};
437 try expectEqual(expected, actual);
438 }
439 {
440 const actual: @Vector(3, u8) = @import("zon/vec3_int.zon");
441 const expected: @Vector(3, u8) = .{ 2, 4, 6 };
442 try expectEqual(expected, actual);
443 }
444
445 {
446 const actual: @Vector(0, *const u8) = @import("zon/vec0.zon");
447 const expected: @Vector(0, *const u8) = .{};
448 try expectEqual(expected, actual);
449 }
450 {
451 const actual: @Vector(3, *const u8) = @import("zon/vec3_int.zon");
452 const expected: @Vector(3, *const u8) = .{ &2, &4, &6 };
453 try expectEqual(expected, actual);
454 }
455
456 {
457 const actual: @Vector(0, ?*const u8) = @import("zon/vec0.zon");
458 const expected: @Vector(0, ?*const u8) = .{};
459 try expectEqual(expected, actual);
460 }
461 {
462 const actual: @Vector(3, ?*const u8) = @import("zon/vec3_int_opt.zon");
463 const expected: @Vector(3, ?*const u8) = .{ &2, null, &6 };
464 try expectEqual(expected, actual);
465 }
466}
467
468test "pointers" {
469 // Primitive with varying levels of pointers
470 try expectEqual(@as(u8, 'a'), @as(*const u8, @import("zon/a.zon")).*);
471 try expectEqual(@as(u8, 'a'), @as(*const *const u8, @import("zon/a.zon")).*.*);
472 try expectEqual(@as(u8, 'a'), @as(*const *const *const u8, @import("zon/a.zon")).*.*.*);
473
474 // Primitive optional with varying levels of pointers
475 try expectEqual(@as(u8, 'a'), @as(?*const u8, @import("zon/a.zon")).?.*);
476 try expectEqual(null, @as(?*const u8, @import("zon/none.zon")));
477
478 try expectEqual(@as(u8, 'a'), @as(*const ?u8, @import("zon/a.zon")).*.?);
479 try expectEqual(null, @as(*const ?u8, @import("zon/none.zon")).*);
480
481 try expectEqual(@as(u8, 'a'), @as(?*const *const u8, @import("zon/a.zon")).?.*.*);
482 try expectEqual(null, @as(?*const *const u8, @import("zon/none.zon")));
483
484 try expectEqual(@as(u8, 'a'), @as(*const ?*const u8, @import("zon/a.zon")).*.?.*);
485 try expectEqual(null, @as(*const ?*const u8, @import("zon/none.zon")).*);
486
487 try expectEqual(@as(u8, 'a'), @as(*const *const ?u8, @import("zon/a.zon")).*.*.?);
488 try expectEqual(null, @as(*const *const ?u8, @import("zon/none.zon")).*.*);
489
490 try expectEqual([3]u8{ 2, 4, 6 }, @as(*const [3]u8, @import("zon/vec3_int.zon")).*);
491
492 // A complicated type with nested internal pointers and string allocations
493 {
494 const Inner = struct {
495 f1: *const ?*const []const u8,
496 f2: *const ?*const []const u8,
497 };
498 const Outer = struct {
499 f1: *const ?*const Inner,
500 f2: *const ?*const Inner,
501 };
502 const expected: Outer = .{
503 .f1 = &&.{
504 .f1 = &null,
505 .f2 = &&"foo",
506 },
507 .f2 = &null,
508 };
509
510 const found: ?*const Outer = @import("zon/complex.zon");
511 try std.testing.expectEqualDeep(expected, found.?.*);
512 }
513}
514
515test "recursive" {
516 const Recursive = struct { foo: ?*const @This() };
517 const expected: Recursive = .{ .foo = &.{ .foo = null } };
518 try expectEqualDeep(expected, @as(Recursive, @import("zon/recursive.zon")));
519}
test/behavior/zon/a.zon created+1
......@@ -0,0 +1 @@
1'a'
test/behavior/zon/abc-escaped.zon created+1
......@@ -0,0 +1 @@
1"ab\\c"
test/behavior/zon/abc.zon created+1
......@@ -0,0 +1 @@
1"abc"
test/behavior/zon/array.zon created+1
......@@ -0,0 +1 @@
1.{ 'a', 'b', 'c', 'd' }
test/behavior/zon/complex.zon created+7
......@@ -0,0 +1,7 @@
1.{
2 .f1 = .{
3 .f1 = null,
4 .f2 = "foo",
5 },
6 .f2 = null,
7}
test/behavior/zon/enum_field.zon created+1
......@@ -0,0 +1 @@
1.{ .x = .z }
test/behavior/zon/escaped_enum.zon created+1
......@@ -0,0 +1 @@
1.@"0\na"
test/behavior/zon/escaped_struct.zon created+2
......@@ -0,0 +1,2 @@
1
2.{ .@"0" = 1.5, .@"foo" = 2 }
test/behavior/zon/false.zon created+4
......@@ -0,0 +1,4 @@
1// Comment
2false // Another comment
3// Yet another comment
4
test/behavior/zon/floats.zon created+25
......@@ -0,0 +1,25 @@
1.{
2 0.5,
3 123.456,
4 -123.456,
5 42.5,
6
7 5.0,
8 5,
9 -102.0,
10 -102,
11
12 'a',
13 'z',
14
15 36893488147419103231,
16 -36893488147419103231,
17 0x1ffffffffffffffff,
18 0x1ffffffffffffffff,
19
20 12_3.0E+77,
21
22 0x103.70p-5,
23 -0x103.70,
24 0x1234_5678.9ABC_CDEFp-10,
25}
test/behavior/zon/foo.zon created+1
......@@ -0,0 +1 @@
1.foo
test/behavior/zon/inf_and_nan.zon created+5
......@@ -0,0 +1,5 @@
1.{
2 nan,
3 inf,
4 -inf,
5}
test/behavior/zon/ints.zon created+40
......@@ -0,0 +1,40 @@
1.{
2 10,
3 24,
4 -4,
5 -123,
6
7 127,
8 -128,
9
10 'a',
11 'z',
12
13 36893488147419103231,
14 368934_881_474191032_31,
15 -18446744073709551615,
16 -9223372036854775809,
17
18 36893488147419103231,
19 -36893488147419103232,
20
21 -1.0,
22 123.0,
23
24 0xff,
25 -0xff,
26 0o77,
27 -0o77,
28 0b11,
29 -0b11,
30
31 0x1ffffffffffffffff,
32 0x1ffffffffffffffff,
33 -0x1ffffffffffffffff,
34 0o3777777777777777777777,
35 0o3777777777777777777777,
36 -0o3777777777777777777777,
37 0b11111111111111111111111111111111111111111111111111111111111111111,
38 0b11111111111111111111111111111111111111111111111111111111111111111,
39 -0b11111111111111111111111111111111111111111111111111111111111111111,
40}
test/behavior/zon/multiline_string.zon created+4
......@@ -0,0 +1,4 @@
1// zig fmt: off
2 \\Hello, world!
3\\This is a multiline string!
4 \\ There are no escapes, we can, for example, include \n in the string
test/behavior/zon/none.zon created+1
......@@ -0,0 +1 @@
1null
test/behavior/zon/recursive.zon created+1
......@@ -0,0 +1 @@
1.{ .foo = .{ .foo = null } }
test/behavior/zon/slice-abc.zon created+1
......@@ -0,0 +1 @@
1.{'a', 'b', 'c'}
\ No newline at end of file
test/behavior/zon/slice-empty.zon created+1
......@@ -0,0 +1 @@
1.{}
\ No newline at end of file
test/behavior/zon/slice1_no_newline.zon created+1
......@@ -0,0 +1 @@
1.{ 1 }
\ No newline at end of file
test/behavior/zon/some.zon created+1
......@@ -0,0 +1 @@
110
test/behavior/zon/string_embedded_null.zon created+1
......@@ -0,0 +1 @@
1"a\nb\x00c"
test/behavior/zon/true.zon created+1
......@@ -0,0 +1 @@
1true
test/behavior/zon/tuple.zon created+1
......@@ -0,0 +1 @@
1.{ 1.2, true, "hello", 3 }
test/behavior/zon/union1.zon created+1
......@@ -0,0 +1 @@
1.{ .x = 1.5 }
test/behavior/zon/union2.zon created+1
......@@ -0,0 +1 @@
1.{ .y = true }
test/behavior/zon/union3.zon created+1
......@@ -0,0 +1 @@
1.z
test/behavior/zon/vec0.zon created+1
......@@ -0,0 +1 @@
1.{}
test/behavior/zon/vec1.zon created+1
......@@ -0,0 +1 @@
1.{ .x = 1.5 }
test/behavior/zon/vec2.zon created+1
......@@ -0,0 +1 @@
1.{ .x = 1.5, .y = 2 }
test/behavior/zon/vec3_bool.zon created+1
......@@ -0,0 +1 @@
1.{ false, false, true }
test/behavior/zon/vec3_float.zon created+1
......@@ -0,0 +1 @@
1.{ 1.5, 2.5, 3.5 }
test/behavior/zon/vec3_int.zon created+1
......@@ -0,0 +1 @@
1.{ 2, 4, 6 }
test/behavior/zon/vec3_int_opt.zon created+1
......@@ -0,0 +1 @@
1.{ 2, null, 6 }
test/behavior/zon/z.zon created+1
......@@ -0,0 +1 @@
1'z'
test/cases/compile_errors/@import_zon_addr_slice.zig created+9
......@@ -0,0 +1,9 @@
1pub fn main() void {
2 const f: struct { value: []const i32 } = @import("zon/addr_slice.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/addr_slice.zon
8//
9// addr_slice.zon:2:14: error: pointers are not available in ZON
test/cases/compile_errors/@import_zon_array_len.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: [4]u8 = @import("zon/array.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/array.zon
8//
9// array.zon:1:2: error: expected type '[4]u8'
10// tmp.zig:2:30: note: imported here
test/cases/compile_errors/@import_zon_bad_import.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 _ = @import(
3 "bogus-does-not-exist.zon",
4 );
5}
6
7// error
8//
9// :3:9: error: unable to open 'bogus-does-not-exist.zon': FileNotFound
test/cases/compile_errors/@import_zon_bad_type.zig created+125
......@@ -0,0 +1,125 @@
1export fn testVoid() void {
2 const f: void = @import("zon/neg_inf.zon");
3 _ = f;
4}
5
6export fn testInStruct() void {
7 const f: struct { f: [*]const u8 } = @import("zon/neg_inf.zon");
8 _ = f;
9}
10
11export fn testError() void {
12 const f: struct { error{foo} } = @import("zon/neg_inf.zon");
13 _ = f;
14}
15
16export fn testInUnion() void {
17 const f: union(enum) { a: void, b: [*c]const u8 } = @import("zon/neg_inf.zon");
18 _ = f;
19}
20
21export fn testInVector() void {
22 const f: @Vector(0, [*c]const u8) = @import("zon/neg_inf.zon");
23 _ = f;
24}
25
26export fn testInOpt() void {
27 const f: *const ?[*c]const u8 = @import("zon/neg_inf.zon");
28 _ = f;
29}
30
31export fn testComptimeField() void {
32 const f: struct { comptime foo: ??u8 = null } = @import("zon/neg_inf.zon");
33 _ = f;
34}
35
36export fn testEnumLiteral() void {
37 const f: @TypeOf(.foo) = @import("zon/neg_inf.zon");
38 _ = f;
39}
40
41export fn testNestedOpt1() void {
42 const f: ??u8 = @import("zon/neg_inf.zon");
43 _ = f;
44}
45
46export fn testNestedOpt2() void {
47 const f: ?*const ?u8 = @import("zon/neg_inf.zon");
48 _ = f;
49}
50
51export fn testNestedOpt3() void {
52 const f: *const ?*const ?*const u8 = @import("zon/neg_inf.zon");
53 _ = f;
54}
55
56export fn testOpt() void {
57 const f: ?u8 = @import("zon/neg_inf.zon");
58 _ = f;
59}
60
61export fn testNonExhaustiveEnum() void {
62 const f: enum(u8) { _ } = @import("zon/neg_inf.zon");
63 _ = f;
64}
65
66export fn testUntaggedUnion() void {
67 const f: union { foo: void } = @import("zon/neg_inf.zon");
68 _ = f;
69}
70
71export fn testTaggedUnionVoid() void {
72 const f: union(enum) { foo: void } = @import("zon/neg_inf.zon");
73 _ = f;
74}
75
76export fn testVisited() void {
77 const V = struct {
78 ?f32, // Adds `?f32` to the visited list
79 ??f32, // `?f32` is already visited, we need to detect the nested opt anyway
80 f32,
81 };
82 const f: V = @import("zon/neg_inf.zon");
83 _ = f;
84}
85
86export fn testMutablePointer() void {
87 const f: *i32 = @import("zon/neg_inf.zon");
88 _ = f;
89}
90
91// error
92// imports=zon/neg_inf.zon
93//
94// tmp.zig:2:29: error: type 'void' is not available in ZON
95// tmp.zig:7:50: error: type '[*]const u8' is not available in ZON
96// tmp.zig:7:50: note: ZON does not allow many-pointers
97// tmp.zig:12:46: error: type 'error{foo}' is not available in ZON
98// tmp.zig:17:65: error: type '[*c]const u8' is not available in ZON
99// tmp.zig:17:65: note: ZON does not allow C pointers
100// tmp.zig:22:49: error: type '[*c]const u8' is not available in ZON
101// tmp.zig:22:49: note: ZON does not allow C pointers
102// tmp.zig:27:45: error: type '[*c]const u8' is not available in ZON
103// tmp.zig:27:45: note: ZON does not allow C pointers
104// tmp.zig:32:61: error: type '??u8' is not available in ZON
105// tmp.zig:32:61: note: ZON does not allow nested optionals
106// tmp.zig:42:29: error: type '??u8' is not available in ZON
107// tmp.zig:42:29: note: ZON does not allow nested optionals
108// tmp.zig:47:36: error: type '?*const ?u8' is not available in ZON
109// tmp.zig:47:36: note: ZON does not allow nested optionals
110// tmp.zig:52:50: error: type '?*const ?*const u8' is not available in ZON
111// tmp.zig:52:50: note: ZON does not allow nested optionals
112// tmp.zig:82:26: error: type '??f32' is not available in ZON
113// tmp.zig:82:26: note: ZON does not allow nested optionals
114// tmp.zig:87:29: error: type '*i32' is not available in ZON
115// tmp.zig:87:29: note: ZON does not allow mutable pointers
116// neg_inf.zon:1:1: error: expected type '@Type(.enum_literal)'
117// tmp.zig:37:38: note: imported here
118// neg_inf.zon:1:1: error: expected type '?u8'
119// tmp.zig:57:28: note: imported here
120// neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_490'
121// tmp.zig:62:39: note: imported here
122// neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_492'
123// tmp.zig:67:44: note: imported here
124// neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_495'
125// tmp.zig:72:50: note: imported here
test/cases/compile_errors/@import_zon_comptime_inf.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: comptime_float = @import("zon/inf.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/inf.zon
8//
9// inf.zon:1:1: error: expected type 'comptime_float'
10// tmp.zig:2:39: note: imported here
test/cases/compile_errors/@import_zon_comptime_nan.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: comptime_float = @import("zon/nan.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/nan.zon
8//
9// nan.zon:1:1: error: expected type 'comptime_float'
10// tmp.zig:2:39: note: imported here
test/cases/compile_errors/@import_zon_comptime_neg_inf.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: comptime_float = @import("zon/neg_inf.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/neg_inf.zon
8//
9// neg_inf.zon:1:1: error: expected type 'comptime_float'
10// tmp.zig:2:39: note: imported here
test/cases/compile_errors/@import_zon_doc_comment.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: struct { foo: type } = @import("zon/doc_comment.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/doc_comment.zon
8//
9// doc_comment.zon:1:1: error: expected expression, found 'a document comment'
test/cases/compile_errors/@import_zon_double_negation_float.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: f32 = @import("zon/double_negation_float.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/double_negation_float.zon
8//
9// double_negation_float.zon:1:1: error: expected number or 'inf' after '-'
test/cases/compile_errors/@import_zon_double_negation_int.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: i32 = @import("zon/double_negation_int.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/double_negation_int.zon
8//
9// double_negation_int.zon:1:1: error: expected number or 'inf' after '-'
test/cases/compile_errors/@import_zon_enum_embedded_null.zig created+11
......@@ -0,0 +1,11 @@
1const std = @import("std");
2export fn entry() void {
3 const E = enum { foo };
4 const f: struct { E, E } = @import("zon/enum_embedded_null.zon");
5 _ = f;
6}
7
8// error
9// imports=zon/enum_embedded_null.zon
10//
11// enum_embedded_null.zon:2:6: error: identifier cannot contain null bytes
test/cases/compile_errors/@import_zon_expected_void.zig created+11
......@@ -0,0 +1,11 @@
1export fn entry() void {
2 const U = union(enum) { a: void };
3 const f: U = @import("zon/simple_union.zon");
4 _ = f;
5}
6
7// error
8// imports=zon/simple_union.zon
9//
10// simple_union.zon:1:9: error: expected type 'void'
11// tmp.zig:3:26: note: imported here
test/cases/compile_errors/@import_zon_invalid_character.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: u8 = @import("zon/invalid_character.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/invalid_character.zon
8//
9// invalid_character.zon:1:3: error: invalid escape character: 'a'
test/cases/compile_errors/@import_zon_invalid_number.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: u128 = @import("zon/invalid_number.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/invalid_number.zon
8//
9// invalid_number.zon:1:19: error: invalid digit 'a' for decimal base
test/cases/compile_errors/@import_zon_invalid_string.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: []const u8 = @import("zon/invalid_string.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/invalid_string.zon
8//
9// invalid_string.zon:1:5: error: invalid escape character: 'a'
test/cases/compile_errors/@import_zon_leading_zero_in_integer.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: u128 = @import("zon/leading_zero_in_integer.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/leading_zero_in_integer.zon
8//
9// leading_zero_in_integer.zon:1:1: error: number '0012' has leading zero
10// leading_zero_in_integer.zon:1:1: note: use '0o' prefix for octal literals
test/cases/compile_errors/@import_zon_neg_char.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: u8 = @import("zon/neg_char.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/neg_char.zon
8//
9// neg_char.zon:1:1: error: expected number or 'inf' after '-'
test/cases/compile_errors/@import_zon_neg_nan.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: u8 = @import("zon/neg_nan.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/neg_nan.zon
8//
9// neg_nan.zon:1:1: error: expected number or 'inf' after '-'
test/cases/compile_errors/@import_zon_negative_zero.zig created+11
......@@ -0,0 +1,11 @@
1export fn entry() void {
2 const f: i8 = @import("zon/negative_zero.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/negative_zero.zon
8//
9// negative_zero.zon:1:2: error: integer literal '-0' is ambiguous
10// negative_zero.zon:1:2: note: use '0' for an integer zero
11// negative_zero.zon:1:2: note: use '-0.0' for a floating-point signed zero
test/cases/compile_errors/@import_zon_no_rt.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f = @import("zon/simple_union.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/simple_union.zon
8//
9// tmp.zig:2:23: error: '@import' of ZON must have a known result type
test/cases/compile_errors/@import_zon_number_fail_limits.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: i66 = @import("zon/large_number.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/large_number.zon
8//
9// large_number.zon:1:1: error: type 'i66' cannot represent integer value '36893488147419103232'
10// tmp.zig:2:28: note: imported here
test/cases/compile_errors/@import_zon_oob_char_0.zig created+16
......@@ -0,0 +1,16 @@
1export fn entry() void {
2 {
3 const f: u6 = @import("zon/char_32.zon");
4 _ = f;
5 }
6 {
7 const f: u5 = @import("zon/char_32.zon");
8 _ = f;
9 }
10}
11
12// error
13// imports=zon/char_32.zon
14//
15// char_32.zon:1:1: error: type 'u5' cannot represent integer value '32'
16// tmp.zig:7:31: note: imported here
test/cases/compile_errors/@import_zon_oob_char_1.zig created+16
......@@ -0,0 +1,16 @@
1export fn entry() void {
2 {
3 const f: i7 = @import("zon/char_32.zon");
4 _ = f;
5 }
6 {
7 const f: i6 = @import("zon/char_32.zon");
8 _ = f;
9 }
10}
11
12// error
13// imports=zon/char_32.zon
14//
15// char_32.zon:1:1: error: type 'i6' cannot represent integer value '32'
16// tmp.zig:7:31: note: imported here
test/cases/compile_errors/@import_zon_oob_int_0.zig created+16
......@@ -0,0 +1,16 @@
1export fn entry() void {
2 {
3 const f: u6 = @import("zon/int_32.zon");
4 _ = f;
5 }
6 {
7 const f: u5 = @import("zon/int_32.zon");
8 _ = f;
9 }
10}
11
12// error
13// imports=zon/int_32.zon
14//
15// int_32.zon:1:1: error: type 'u5' cannot represent integer value '32'
16// tmp.zig:7:31: note: imported here
test/cases/compile_errors/@import_zon_oob_int_1.zig created+16
......@@ -0,0 +1,16 @@
1export fn entry() void {
2 {
3 const f: i7 = @import("zon/int_32.zon");
4 _ = f;
5 }
6 {
7 const f: i6 = @import("zon/int_32.zon");
8 _ = f;
9 }
10}
11
12// error
13// imports=zon/int_32.zon
14//
15// int_32.zon:1:1: error: type 'i6' cannot represent integer value '32'
16// tmp.zig:7:31: note: imported here
test/cases/compile_errors/@import_zon_oob_int_2.zig created+16
......@@ -0,0 +1,16 @@
1export fn entry() void {
2 {
3 const f: i7 = @import("zon/int_neg_33.zon");
4 _ = f;
5 }
6 {
7 const f: i6 = @import("zon/int_neg_33.zon");
8 _ = f;
9 }
10}
11
12// error
13// imports=zon/int_neg_33.zon
14//
15// int_neg_33.zon:1:1: error: type 'i6' cannot represent integer value '-33'
16// tmp.zig:7:31: note: imported here
test/cases/compile_errors/@import_zon_oob_int_3.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: u64 = @import("zon/int_neg_33.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/int_neg_33.zon
8//
9// int_neg_33.zon:1:1: error: type 'u64' cannot represent integer value '-33'
10// tmp.zig:2:28: note: imported here
test/cases/compile_errors/@import_zon_opt_in_err.zig created+82
......@@ -0,0 +1,82 @@
1export fn testFloatA() void {
2 const f: ?f32 = @import("zon/vec2.zon");
3 _ = f;
4}
5
6export fn testFloatB() void {
7 const f: *const ?f32 = @import("zon/vec2.zon");
8 _ = f;
9}
10
11export fn testFloatC() void {
12 const f: ?*const f32 = @import("zon/vec2.zon");
13 _ = f;
14}
15
16export fn testBool() void {
17 const f: ?bool = @import("zon/vec2.zon");
18 _ = f;
19}
20
21export fn testInt() void {
22 const f: ?i32 = @import("zon/vec2.zon");
23 _ = f;
24}
25
26const Enum = enum { foo };
27export fn testEnum() void {
28 const f: ?Enum = @import("zon/vec2.zon");
29 _ = f;
30}
31
32export fn testEnumLit() void {
33 const f: ?@TypeOf(.foo) = @import("zon/vec2.zon");
34 _ = f;
35}
36
37export fn testArray() void {
38 const f: ?[1]u8 = @import("zon/vec2.zon");
39 _ = f;
40}
41
42const Union = union {};
43export fn testUnion() void {
44 const f: ?Union = @import("zon/vec2.zon");
45 _ = f;
46}
47
48export fn testSlice() void {
49 const f: ?[]const u8 = @import("zon/vec2.zon");
50 _ = f;
51}
52
53export fn testVector() void {
54 const f: ?@Vector(3, f32) = @import("zon/vec2.zon");
55 _ = f;
56}
57
58// error
59// imports=zon/vec2.zon
60//
61// vec2.zon:1:2: error: expected type '?f32'
62// tmp.zig:2:29: note: imported here
63// vec2.zon:1:2: error: expected type '*const ?f32'
64// tmp.zig:7:36: note: imported here
65// vec2.zon:1:2: error: expected type '?*const f32'
66// tmp.zig:12:36: note: imported here
67// vec2.zon:1:2: error: expected type '?bool'
68// tmp.zig:17:30: note: imported here
69// vec2.zon:1:2: error: expected type '?i32'
70// tmp.zig:22:29: note: imported here
71// vec2.zon:1:2: error: expected type '?tmp.Enum'
72// tmp.zig:28:30: note: imported here
73// vec2.zon:1:2: error: expected type '?@Type(.enum_literal)'
74// tmp.zig:33:39: note: imported here
75// vec2.zon:1:2: error: expected type '?[1]u8'
76// tmp.zig:38:31: note: imported here
77// vec2.zon:1:2: error: expected type '?tmp.Union'
78// tmp.zig:44:31: note: imported here
79// vec2.zon:1:2: error: expected type '?[]const u8'
80// tmp.zig:49:36: note: imported here
81// vec2.zon:1:2: error: expected type '?@Vector(3, f32)'
82// tmp.zig:54:41: note: imported here
test/cases/compile_errors/@import_zon_opt_in_err_struct.zig created+19
......@@ -0,0 +1,19 @@
1const Struct = struct { f: bool };
2export fn testStruct() void {
3 const f: ?Struct = @import("zon/nan.zon");
4 _ = f;
5}
6
7const Tuple = struct { bool };
8export fn testTuple() void {
9 const f: ?Tuple = @import("zon/nan.zon");
10 _ = f;
11}
12
13// error
14// imports=zon/nan.zon
15//
16//nan.zon:1:1: error: expected type '?tmp.Struct'
17//tmp.zig:3:32: note: imported here
18//nan.zon:1:1: error: expected type '?struct { bool }'
19//tmp.zig:9:31: note: imported here
test/cases/compile_errors/@import_zon_string_as_array.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: [5]u8 = @import("zon/hello.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/hello.zon
8//
9// hello.zon:1:1: error: expected type '[5]u8'
10// tmp.zig:2:30: note: imported here
test/cases/compile_errors/@import_zon_struct_dup_field.zig created+11
......@@ -0,0 +1,11 @@
1const std = @import("std");
2export fn entry() void {
3 const f: struct { name: u8 } = @import("zon/struct_dup_field.zon");
4 _ = f;
5}
6
7// error
8// imports=zon/struct_dup_field.zon
9//
10// struct_dup_field.zon:2:6: error: duplicate struct field name
11// struct_dup_field.zon:3:6: note: duplicate name here
test/cases/compile_errors/@import_zon_struct_wrong_comptime_field.zig created+14
......@@ -0,0 +1,14 @@
1export fn entry() void {
2 const Vec2 = struct {
3 comptime x: f32 = 1.5,
4 comptime y: f32 = 2.5,
5 };
6 const f: Vec2 = @import("zon/vec2.zon");
7 _ = f;
8}
9
10// error
11// imports=zon/vec2.zon
12//
13// vec2.zon:1:19: error: value stored in comptime field does not match the default value of the field
14// tmp.zig:6:29: note: imported here
test/cases/compile_errors/@import_zon_syntax_error.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: bool = @import("zon/syntax_error.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/syntax_error.zon
8//
9// syntax_error.zon:3:13: error: expected ',' after initializer
test/cases/compile_errors/@import_zon_tuple_wrong_comptime_field.zig created+14
......@@ -0,0 +1,14 @@
1export fn entry() void {
2 const T = struct {
3 comptime f32 = 1.5,
4 comptime f32 = 2.5,
5 };
6 const f: T = @import("zon/tuple.zon");
7 _ = f;
8}
9
10// error
11// imports=zon/tuple.zon
12//
13// tuple.zon:1:9: error: value stored in comptime field does not match the default value of the field
14// tmp.zig:6:26: note: imported here
test/cases/compile_errors/@import_zon_type_decl.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: struct { foo: type } = @import("zon/type_decl.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/type_decl.zon
8//
9// type_decl.zon:2:12: error: types are not available in ZON
test/cases/compile_errors/@import_zon_type_expr_array.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: [3]i32 = @import("zon/type_expr_array.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/type_expr_array.zon
8//
9// type_expr_array.zon:1:1: error: types are not available in ZON
10// type_expr_array.zon:1:1: note: replace the type with '.'
test/cases/compile_errors/@import_zon_type_expr_fn.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: i32 = @import("zon/type_expr_fn.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/type_expr_fn.zon
8//
9// type_expr_fn.zon:1:1: error: types are not available in ZON
10// type_expr_fn.zon:1:1: note: replace the type with '.'
test/cases/compile_errors/@import_zon_type_expr_struct.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: struct { x: f32, y: f32 } = @import("zon/type_expr_struct.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/type_expr_struct.zon
8//
9// type_expr_struct.zon:1:1: error: types are not available in ZON
10// type_expr_struct.zon:1:1: note: replace the type with '.'
test/cases/compile_errors/@import_zon_type_expr_tuple.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: struct { f32, f32 } = @import("zon/type_expr_tuple.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/type_expr_tuple.zon
8//
9// type_expr_tuple.zon:1:1: error: types are not available in ZON
10// type_expr_tuple.zon:1:1: note: replace the type with '.'
test/cases/compile_errors/@import_zon_type_mismatch.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: bool = @import("zon/struct.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/struct.zon
8//
9// struct.zon:1:2: error: expected type 'bool'
10// tmp.zig:2:29: note: imported here
test/cases/compile_errors/@import_zon_unescaped_newline.zig created+9
......@@ -0,0 +1,9 @@
1export fn entry() void {
2 const f: i8 = @import("zon/unescaped_newline.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/unescaped_newline.zon
8//
9// unescaped_newline.zon:1:1: error: expected expression, found 'invalid token'
test/cases/compile_errors/@import_zon_unknown_ident.zig created+11
......@@ -0,0 +1,11 @@
1export fn entry() void {
2 const f: struct { value: bool } = @import("zon/unknown_ident.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/unknown_ident.zon
8//
9// unknown_ident.zon:2:14: error: invalid expression
10// unknown_ident.zon:2:14: note: ZON allows identifiers 'true', 'false', 'null', 'inf', and 'nan'
11// unknown_ident.zon:2:14: note: precede identifier with '.' for an enum literal
test/cases/compile_errors/@import_zon_vec_too_few.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: @Vector(3, f32) = @import("zon/tuple.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/tuple.zon
8//
9// tuple.zon:1:2: error: expected 3 vector elements; found 2
10// tmp.zig:2:40: note: imported here
test/cases/compile_errors/@import_zon_vec_too_many.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: @Vector(1, f32) = @import("zon/tuple.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/tuple.zon
8//
9// tuple.zon:1:2: error: expected 1 vector elements; found 2
10// tmp.zig:2:40: note: imported here
test/cases/compile_errors/@import_zon_vec_wrong_type.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: @Vector(2, bool) = @import("zon/tuple.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/tuple.zon
8//
9// tuple.zon:1:4: error: expected type 'bool'
10// tmp.zig:2:41: note: imported here
test/cases/compile_errors/@import_zon_void.zig created+10
......@@ -0,0 +1,10 @@
1export fn entry() void {
2 const f: union { foo: void } = @import("zon/void.zon");
3 _ = f;
4}
5
6// error
7// imports=zon/void.zon
8//
9// void.zon:1:11: error: void literals are not available in ZON
10// void.zon:1:11: note: void union payloads can be represented by enum literals
test/cases/compile_errors/zon/addr_slice.zon created+3
......@@ -0,0 +1,3 @@
1.{
2 .value = &.{ 1, 2, 3 },
3}
test/cases/compile_errors/zon/array.zon created+1
......@@ -0,0 +1 @@
1.{ 'a', 'b', 'c' }
test/cases/compile_errors/zon/char_32.zon created+1
......@@ -0,0 +1 @@
1' '
\ No newline at end of file
test/cases/compile_errors/zon/desktop.ini created+3
......@@ -0,0 +1,3 @@
1[LocalizedFileNames]
2invalid_zon_2.zig=@invalid_zon_2.zig,0
3invalid_zon_1.zig=@invalid_zon_1.zig,0
test/cases/compile_errors/zon/doc_comment.zon created+2
......@@ -0,0 +1,2 @@
1//! Doc comments aren't allowed in ZON
2.{}
test/cases/compile_errors/zon/double_negation_float.zon created+1
......@@ -0,0 +1 @@
1--1.0
\ No newline at end of file
test/cases/compile_errors/zon/double_negation_int.zon created+1
......@@ -0,0 +1 @@
1--1
\ No newline at end of file
test/cases/compile_errors/zon/enum_embedded_null.zon created+4
......@@ -0,0 +1,4 @@
1.{
2 .@"\x00",
3 10,
4}
test/cases/compile_errors/zon/hello.zon created+1
......@@ -0,0 +1 @@
1"hello"
test/cases/compile_errors/zon/inf.zon created+1
......@@ -0,0 +1 @@
1inf
\ No newline at end of file
test/cases/compile_errors/zon/int_32.zon created+1
......@@ -0,0 +1 @@
132
\ No newline at end of file
test/cases/compile_errors/zon/int_neg_33.zon created+1
......@@ -0,0 +1 @@
1-33
\ No newline at end of file
test/cases/compile_errors/zon/invalid_character.zon created+1
......@@ -0,0 +1 @@
1'\a'
test/cases/compile_errors/zon/invalid_number.zon created+1
......@@ -0,0 +1 @@
1368934881474191032a32
test/cases/compile_errors/zon/invalid_string.zon created+1
......@@ -0,0 +1 @@
1"\"\a\""
test/cases/compile_errors/zon/large_number.zon created+1
......@@ -0,0 +1 @@
136893488147419103232
test/cases/compile_errors/zon/leading_zero_in_integer.zon created+1
......@@ -0,0 +1 @@
10012
\ No newline at end of file
test/cases/compile_errors/zon/nan.zon created+1
......@@ -0,0 +1 @@
1nan
\ No newline at end of file
test/cases/compile_errors/zon/neg_char.zon created+1
......@@ -0,0 +1 @@
1-'a'
test/cases/compile_errors/zon/neg_inf.zon created+1
......@@ -0,0 +1 @@
1-inf
\ No newline at end of file
test/cases/compile_errors/zon/neg_nan.zon created+1
......@@ -0,0 +1 @@
1-nan
test/cases/compile_errors/zon/negative_zero.zon created+1
......@@ -0,0 +1 @@
1-0
\ No newline at end of file
test/cases/compile_errors/zon/simple_union.zon created+1
......@@ -0,0 +1 @@
1.{ .a = 10 }
test/cases/compile_errors/zon/struct.zon created+4
......@@ -0,0 +1,4 @@
1.{
2 .boolean = true,
3 .number = 123,
4}
test/cases/compile_errors/zon/struct_dup_field.zon created+4
......@@ -0,0 +1,4 @@
1.{
2 .name = 10,
3 .name = 20,
4}
test/cases/compile_errors/zon/syntax_error.zon created+4
......@@ -0,0 +1,4 @@
1.{
2 .boolean = true
3 .number = 123,
4}
test/cases/compile_errors/zon/tuple.zon created+1
......@@ -0,0 +1 @@
1.{ 1.5, 2 }
test/cases/compile_errors/zon/type_decl.zon created+3
......@@ -0,0 +1,3 @@
1.{
2 .foo = struct {},
3}
test/cases/compile_errors/zon/type_expr_array.zon created+1
......@@ -0,0 +1 @@
1[3]i32{1, 2, 3}
\ No newline at end of file
test/cases/compile_errors/zon/type_expr_fn.zon created+1
......@@ -0,0 +1 @@
1fn foo() void {}
test/cases/compile_errors/zon/type_expr_struct.zon created+1
......@@ -0,0 +1 @@
1Vec2{ .x = 1.0, .y = 2.0 }
\ No newline at end of file
test/cases/compile_errors/zon/type_expr_tuple.zon created+1
......@@ -0,0 +1 @@
1Vec2{1.0, 2.0}
\ No newline at end of file
test/cases/compile_errors/zon/unescaped_newline.zon created+2
......@@ -0,0 +1,2 @@
1"a
2b"
\ No newline at end of file
test/cases/compile_errors/zon/unknown_ident.zon created+3
......@@ -0,0 +1,3 @@
1.{
2 .value = truefalse,
3}
test/cases/compile_errors/zon/vec2.zon created+1
......@@ -0,0 +1 @@
1.{ .x = 1.5, .y = 2 }
test/cases/compile_errors/zon/void.zon created+1
......@@ -0,0 +1 @@
1.{ .foo = {} }
test/src/Cases.zig+40-4
......@@ -90,6 +90,11 @@ pub const Case = struct {
9090 link_libc: bool = false,
9191 pic: ?bool = null,
9292 pie: ?bool = null,
93 /// A list of imports to cache alongside the source file.
94 imports: []const []const u8 = &.{},
95 /// Where to look for imports relative to the `cases_dir_path` given to
96 /// `lower_to_build_steps`. If null, file imports will assert.
97 import_path: ?[]const u8 = null,
9398
9499 deps: std.ArrayList(DepModule),
95100
......@@ -413,6 +418,7 @@ fn addFromDirInner(
413418 const pic = try manifest.getConfigForKeyAssertSingle("pic", ?bool);
414419 const pie = try manifest.getConfigForKeyAssertSingle("pie", ?bool);
415420 const emit_bin = try manifest.getConfigForKeyAssertSingle("emit_bin", bool);
421 const imports = try manifest.getConfigForKeyAlloc(ctx.arena, "imports", []const u8);
416422
417423 if (manifest.type == .translate_c) {
418424 for (c_frontends) |c_frontend| {
......@@ -470,7 +476,7 @@ fn addFromDirInner(
470476 const next = ctx.cases.items.len;
471477 try ctx.cases.append(.{
472478 .name = std.fs.path.stem(filename),
473 .target = resolved_target,
479 .import_path = std.fs.path.dirname(filename),
474480 .backend = backend,
475481 .updates = std.ArrayList(Cases.Update).init(ctx.cases.allocator),
476482 .emit_bin = emit_bin,
......@@ -480,6 +486,8 @@ fn addFromDirInner(
480486 .pic = pic,
481487 .pie = pie,
482488 .deps = std.ArrayList(DepModule).init(ctx.cases.allocator),
489 .imports = imports,
490 .target = resolved_target,
483491 });
484492 try cases.append(next);
485493 }
......@@ -619,6 +627,7 @@ pub fn lowerToBuildSteps(
619627) void {
620628 const host = std.zig.system.resolveTargetQuery(.{}) catch |err|
621629 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
630 const cases_dir_path = b.build_root.join(b.allocator, &.{ "test", "cases" }) catch @panic("OOM");
622631
623632 for (self.incremental_cases.items) |incr_case| {
624633 if (true) {
......@@ -662,11 +671,21 @@ pub fn lowerToBuildSteps(
662671 file_sources.put(file.path, writefiles.add(file.path, file.src)) catch @panic("OOM");
663672 }
664673
674 for (case.imports) |import_rel| {
675 const import_abs = std.fs.path.join(b.allocator, &.{
676 cases_dir_path,
677 case.import_path orelse @panic("import_path not set"),
678 import_rel,
679 }) catch @panic("OOM");
680 _ = writefiles.addCopyFile(.{ .cwd_relative = import_abs }, import_rel);
681 }
682
665683 const mod = b.createModule(.{
666684 .root_source_file = root_source_file,
667685 .target = case.target,
668686 .optimize = case.optimize_mode,
669687 });
688
670689 if (case.link_libc) mod.link_libc = true;
671690 if (case.pic) |pic| mod.pic = pic;
672691 for (case.deps.items) |dep| {
......@@ -962,6 +981,8 @@ const TestManifestConfigDefaults = struct {
962981 return "null";
963982 } else if (std.mem.eql(u8, key, "pie")) {
964983 return "null";
984 } else if (std.mem.eql(u8, key, "imports")) {
985 return "";
965986 } else unreachable;
966987 }
967988};
......@@ -998,6 +1019,7 @@ const TestManifest = struct {
9981019 .{ "backend", {} },
9991020 .{ "pic", {} },
10001021 .{ "pie", {} },
1022 .{ "imports", {} },
10011023 });
10021024
10031025 const Type = enum {
......@@ -1020,7 +1042,7 @@ const TestManifest = struct {
10201042
10211043 fn ConfigValueIterator(comptime T: type) type {
10221044 return struct {
1023 inner: std.mem.SplitIterator(u8, .scalar),
1045 inner: std.mem.TokenIterator(u8, .scalar),
10241046
10251047 fn next(self: *@This()) !?T {
10261048 const next_raw = self.inner.next() orelse return null;
......@@ -1098,7 +1120,9 @@ const TestManifest = struct {
10981120 // Parse key=value(s)
10991121 var kv_it = std.mem.splitScalar(u8, trimmed, '=');
11001122 const key = kv_it.first();
1101 if (!valid_keys.has(key)) return error.InvalidKey;
1123 if (!valid_keys.has(key)) {
1124 return error.InvalidKey;
1125 }
11021126 try manifest.config_map.putNoClobber(key, kv_it.next() orelse return error.MissingValuesForConfig);
11031127 }
11041128
......@@ -1115,7 +1139,7 @@ const TestManifest = struct {
11151139 ) ConfigValueIterator(T) {
11161140 const bytes = self.config_map.get(key) orelse TestManifestConfigDefaults.get(self.type, key);
11171141 return ConfigValueIterator(T){
1118 .inner = std.mem.splitScalar(u8, bytes, ','),
1142 .inner = std.mem.tokenizeScalar(u8, bytes, ','),
11191143 };
11201144 }
11211145
......@@ -1232,6 +1256,18 @@ const TestManifest = struct {
12321256 return try getDefaultParser(o.child)(str);
12331257 }
12341258 }.parse,
1259 .@"struct" => @compileError("no default parser for " ++ @typeName(T)),
1260 .pointer => {
1261 if (T == []const u8) {
1262 return struct {
1263 fn parse(str: []const u8) anyerror!T {
1264 return str;
1265 }
1266 }.parse;
1267 } else {
1268 @compileError("no default parser for " ++ @typeName(T));
1269 }
1270 },
12351271 else => @compileError("no default parser for " ++ @typeName(T)),
12361272 }
12371273 }