From 5a0cf217756491169579b301a676a655b04f489b Mon Sep 17 00:00:00 2001 From: Mason Remaley Date: Mon, 25 Aug 2025 06:03:08 -1000 Subject: [PATCH] Adds non allocating alternatives to ZON parse functions (#22916) * Adds "flat" alternatives to zon.parse.from* that don't support pointers * Fixes documentation * Removes flat postfix from non allocating functions, adds alloc to others * Stops using alloc variant in tests where not needed --- lib/std/zon/parse.zig | 288 ++++++++++++++++++++++++++++-------------- 1 file changed, 192 insertions(+), 96 deletions(-) diff --git a/lib/std/zon/parse.zig b/lib/std/zon/parse.zig index 5f74400c29..6644f1114e 100644 --- a/lib/std/zon/parse.zig +++ b/lib/std/zon/parse.zig @@ -1,11 +1,12 @@ -//! The simplest way to parse ZON at runtime is to use `fromSlice`. If you need to parse ZON at -//! compile time, you may use `@import`. +//! The simplest way to parse ZON at runtime is to use `fromSlice`/`fromSliceAlloc`. +//! +//! Note that if you need to parse ZON at compile time, you may use `@import`. //! //! Parsing from individual Zoir nodes is also available: -//! * `fromZoir` -//! * `fromZoirNode` +//! * `fromZoir`/`fromZoirAlloc` +//! * `fromZoirNode`/`fromZoirNodeAlloc` //! -//! For lower level control, it is possible to operate on `std.zig.Zoir` directly. +//! For lower level control over parsing, see `std.zig.Zoir`. const std = @import("std"); const builtin = @import("builtin"); @@ -254,7 +255,25 @@ pub const Diagnostics = struct { /// /// When the parser returns `error.ParseZon`, it will also store a human readable explanation in /// `diag` if non null. If diag is not null, it must be initialized to `.{}`. +/// +/// Asserts at compile time that the result type doesn't contain pointers. As such, the result +/// doesn't need to be freed. +/// +/// An allocator is still required for temporary allocations made during parsing. pub fn fromSlice( + T: type, + gpa: Allocator, + source: [:0]const u8, + diag: ?*Diagnostics, + options: Options, +) error{ OutOfMemory, ParseZon }!T { + comptime assert(!requiresAllocator(T)); + return fromSliceAlloc(T, gpa, source, diag, options); +} + +/// Like `fromSlice`, but the result may contain pointers. To automatically free the result, see +/// `free`. +pub fn fromSliceAlloc( /// The type to deserialize into. May not be or contain any of the following types: /// * Any comptime-only type, except in a comptime field /// * `type` @@ -285,11 +304,35 @@ pub fn fromSlice( defer if (diag == null) zoir.deinit(gpa); if (diag) |s| s.* = .{}; - return fromZoir(T, gpa, ast, zoir, diag, options); + return fromZoirAlloc(T, gpa, ast, zoir, diag, options); } /// Like `fromSlice`, but operates on `Zoir` instead of ZON source. pub fn fromZoir( + T: type, + ast: Ast, + zoir: Zoir, + diag: ?*Diagnostics, + options: Options, +) error{ParseZon}!T { + comptime assert(!requiresAllocator(T)); + var buf: [0]u8 = .{}; + var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); + return fromZoirAlloc( + T, + failing_allocator.allocator(), + ast, + zoir, + diag, + options, + ) catch |err| switch (err) { + error.OutOfMemory => unreachable, // Checked by comptime assertion above + else => |e| return e, + }; +} + +/// Like `fromSliceAlloc`, but operates on `Zoir` instead of ZON source. +pub fn fromZoirAlloc( T: type, gpa: Allocator, ast: Ast, @@ -297,11 +340,37 @@ pub fn fromZoir( diag: ?*Diagnostics, options: Options, ) error{ OutOfMemory, ParseZon }!T { - return fromZoirNode(T, gpa, ast, zoir, .root, diag, options); + return fromZoirNodeAlloc(T, gpa, ast, zoir, .root, diag, options); } -/// Like `fromZoir`, but the parse starts on `node` instead of root. +/// Like `fromZoir`, but the parse starts at `node` instead of root. pub fn fromZoirNode( + T: type, + ast: Ast, + zoir: Zoir, + node: Zoir.Node.Index, + diag: ?*Diagnostics, + options: Options, +) error{ParseZon}!T { + comptime assert(!requiresAllocator(T)); + var buf: [0]u8 = .{}; + var failing_allocator = std.heap.FixedBufferAllocator.init(&buf); + return fromZoirNodeAlloc( + T, + failing_allocator.allocator(), + ast, + zoir, + node, + diag, + options, + ) catch |err| switch (err) { + error.OutOfMemory => unreachable, // Checked by comptime assertion above + else => |e| return e, + }; +} + +/// Like `fromZoirAlloc`, but the parse starts at `node` instead of root. +pub fn fromZoirNodeAlloc( T: type, gpa: Allocator, ast: Ast, @@ -1321,7 +1390,7 @@ test "std.zon failure/oom formatting" { }); var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.OutOfMemory, fromSlice( + try std.testing.expectError(error.OutOfMemory, fromSliceAlloc( []const u8, failing_allocator.allocator(), "\"foo\"", @@ -1331,7 +1400,7 @@ test "std.zon failure/oom formatting" { try std.testing.expectFmt("", "{f}", .{diag}); } -test "std.zon fromSlice syntax error" { +test "std.zon fromSliceAlloc syntax error" { try std.testing.expectError( error.ParseZon, fromSlice(u8, std.testing.allocator, ".{", null, .{}), @@ -1351,9 +1420,9 @@ test "std.zon optional" { // Deep free { - const none = try fromSlice(?[]const u8, gpa, "null", null, .{}); + const none = try fromSliceAlloc(?[]const u8, gpa, "null", null, .{}); try std.testing.expect(none == null); - const some = try fromSlice(?[]const u8, gpa, "\"foo\"", null, .{}); + const some = try fromSliceAlloc(?[]const u8, gpa, "\"foo\"", null, .{}); defer free(gpa, some); try std.testing.expectEqualStrings("foo", some.?); } @@ -1386,10 +1455,10 @@ test "std.zon unions" { { const Union = union(enum) { bar: []const u8, baz: bool }; - const noalloc = try fromSlice(Union, gpa, ".{.baz = false}", null, .{}); + const noalloc = try fromSliceAlloc(Union, gpa, ".{.baz = false}", null, .{}); try std.testing.expectEqual(Union{ .baz = false }, noalloc); - const alloc = try fromSlice(Union, gpa, ".{.bar = \"qux\"}", null, .{}); + const alloc = try fromSliceAlloc(Union, gpa, ".{.bar = \"qux\"}", null, .{}); defer free(gpa, alloc); try std.testing.expectEqualDeep(Union{ .bar = "qux" }, alloc); } @@ -1401,7 +1470,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Union, gpa, ".{.z=2.5}", &diag, .{}), + fromSliceAlloc(Union, gpa, ".{.z=2.5}", &diag, .{}), ); try std.testing.expectFmt( \\1:4: error: unexpected field 'z' @@ -1420,7 +1489,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Union, gpa, ".{.x=1}", &diag, .{}), + fromSliceAlloc(Union, gpa, ".{.x=1}", &diag, .{}), ); try std.testing.expectFmt("1:6: error: expected type 'void'\n", "{f}", .{diag}); } @@ -1432,7 +1501,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}), + fromSliceAlloc(Union, gpa, ".{.x = 1.5, .y = true}", &diag, .{}), ); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1444,7 +1513,7 @@ test "std.zon unions" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(Union, gpa, ".{}", &diag, .{}), + fromSliceAlloc(Union, gpa, ".{}", &diag, .{}), ); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1454,7 +1523,7 @@ test "std.zon unions" { const Union = union { x: void }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".x", &diag, .{})); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } @@ -1463,7 +1532,7 @@ test "std.zon unions" { const Union = union(enum) { x: void }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".y", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".y", &diag, .{})); try std.testing.expectFmt( \\1:2: error: unexpected field 'y' \\1:2: note: supported: 'x' @@ -1479,7 +1548,7 @@ test "std.zon unions" { const Union = union(enum) { x: f32 }; var diag: Diagnostics = .{}; defer diag.deinit(gpa); - try std.testing.expectError(error.ParseZon, fromSlice(Union, gpa, ".x", &diag, .{})); + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Union, gpa, ".x", &diag, .{})); try std.testing.expectFmt("1:2: error: expected union\n", "{f}", .{diag}); } } @@ -1511,7 +1580,7 @@ test "std.zon structs" { { const Foo = struct { bar: []const u8, baz: []const []const u8 }; - const parsed = try fromSlice( + const parsed = try fromSliceAlloc( Foo, gpa, ".{.bar = \"qux\", .baz = .{\"a\", \"b\"}}", @@ -1668,7 +1737,7 @@ test "std.zon structs" { { var diag: Diagnostics = .{}; defer diag.deinit(gpa); - const parsed = fromSlice([]u8, gpa, "[]u8{1, 2, 3}", &diag, .{}); + const parsed = fromSliceAlloc([]u8, gpa, "[]u8{1, 2, 3}", &diag, .{}); try std.testing.expectError(error.ParseZon, parsed); try std.testing.expectFmt( \\1:1: error: types are not available in ZON @@ -1737,7 +1806,7 @@ test "std.zon tuples" { // Deep free { const Tuple = struct { []const u8, []const u8 }; - const parsed = try fromSlice(Tuple, gpa, ".{\"hello\", \"world\"}", null, .{}); + const parsed = try fromSliceAlloc(Tuple, gpa, ".{\"hello\", \"world\"}", null, .{}); defer free(gpa, parsed); try std.testing.expectEqualDeep(Tuple{ "hello", "world" }, parsed); } @@ -1847,27 +1916,27 @@ test "std.zon arrays and slices" { // Slice literals { - const zero = try fromSlice([]const u8, gpa, ".{}", null, .{}); + const zero = try fromSliceAlloc([]const u8, gpa, ".{}", null, .{}); defer free(gpa, zero); try std.testing.expectEqualSlices(u8, @as([]const u8, &.{}), zero); - const one = try fromSlice([]u8, gpa, ".{'a'}", null, .{}); + const one = try fromSliceAlloc([]u8, gpa, ".{'a'}", null, .{}); defer free(gpa, one); try std.testing.expectEqualSlices(u8, &.{'a'}, one); - const two = try fromSlice([]const u8, gpa, ".{'a', 'b'}", null, .{}); + const two = try fromSliceAlloc([]const u8, gpa, ".{'a', 'b'}", null, .{}); defer free(gpa, two); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two); - const two_comma = try fromSlice([]const u8, gpa, ".{'a', 'b',}", null, .{}); + const two_comma = try fromSliceAlloc([]const u8, gpa, ".{'a', 'b',}", null, .{}); defer free(gpa, two_comma); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b' }, two_comma); - const three = try fromSlice([]u8, gpa, ".{'a', 'b', 'c'}", null, .{}); + const three = try fromSliceAlloc([]u8, gpa, ".{'a', 'b', 'c'}", null, .{}); defer free(gpa, three); try std.testing.expectEqualSlices(u8, &.{ 'a', 'b', 'c' }, three); - const sentinel = try fromSlice([:'z']const u8, gpa, ".{'a', 'b', 'c'}", null, .{}); + const sentinel = try fromSliceAlloc([:'z']const u8, gpa, ".{'a', 'b', 'c'}", null, .{}); defer free(gpa, sentinel); const expected_sentinel: [:'z']const u8 = &.{ 'a', 'b', 'c' }; try std.testing.expectEqualSlices(u8, expected_sentinel, sentinel); @@ -1878,7 +1947,7 @@ test "std.zon arrays and slices" { { // Arrays { - const parsed = try fromSlice([1][]const u8, gpa, ".{\"abc\"}", null, .{}); + const parsed = try fromSliceAlloc([1][]const u8, gpa, ".{\"abc\"}", null, .{}); defer free(gpa, parsed); const expected: [1][]const u8 = .{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -1886,7 +1955,7 @@ test "std.zon arrays and slices" { // Slice literals { - const parsed = try fromSlice([]const []const u8, gpa, ".{\"abc\"}", null, .{}); + const parsed = try fromSliceAlloc([]const []const u8, gpa, ".{\"abc\"}", null, .{}); defer free(gpa, parsed); const expected: []const []const u8 = &.{"abc"}; try std.testing.expectEqualDeep(expected, parsed); @@ -1905,7 +1974,7 @@ test "std.zon arrays and slices" { // Slice literals { - const sentinel = try fromSlice([:2]align(4) u8, gpa, ".{1}", null, .{}); + const sentinel = try fromSliceAlloc([:2]align(4) u8, gpa, ".{1}", null, .{}); defer free(gpa, sentinel); try std.testing.expectEqual(@as(usize, 1), sentinel.len); try std.testing.expectEqual(@as(u8, 1), sentinel[0]); @@ -1992,7 +2061,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}), + fromSliceAlloc([]bool, gpa, ".{'a', 'b', 'c'}", &diag, .{}), ); try std.testing.expectFmt("1:3: error: expected type 'bool'\n", "{f}", .{diag}); } @@ -2017,7 +2086,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]u8, gpa, "'a'", &diag, .{}), + fromSliceAlloc([]u8, gpa, "'a'", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2029,7 +2098,7 @@ test "std.zon arrays and slices" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]u8, gpa, " &.{'a', 'b', 'c'}", &diag, .{}), + fromSliceAlloc([]u8, gpa, " &.{'a', 'b', 'c'}", &diag, .{}), ); try std.testing.expectFmt( "1:3: error: pointers are not available in ZON\n", @@ -2044,21 +2113,21 @@ test "std.zon string literal" { // Basic string literal { - const parsed = try fromSlice([]const u8, gpa, "\"abc\"", null, .{}); + const parsed = try fromSliceAlloc([]const u8, gpa, "\"abc\"", null, .{}); defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "abc"), parsed); } // String literal with escape characters { - const parsed = try fromSlice([]const u8, gpa, "\"ab\\nc\"", null, .{}); + const parsed = try fromSliceAlloc([]const u8, gpa, "\"ab\\nc\"", null, .{}); defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "ab\nc"), parsed); } // String literal with embedded null { - const parsed = try fromSlice([]const u8, gpa, "\"ab\\x00c\"", null, .{}); + const parsed = try fromSliceAlloc([]const u8, gpa, "\"ab\\x00c\"", null, .{}); defer free(gpa, parsed); try std.testing.expectEqualStrings(@as([]const u8, "ab\x00c"), parsed); } @@ -2070,7 +2139,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]u8, gpa, "\"abcd\"", &diag, .{}), + fromSliceAlloc([]u8, gpa, "\"abcd\"", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2080,7 +2149,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]u8, gpa, "\\\\abcd", &diag, .{}), + fromSliceAlloc([]u8, gpa, "\\\\abcd", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2116,7 +2185,7 @@ test "std.zon string literal" { // Zero terminated slices { { - const parsed: [:0]const u8 = try fromSlice( + const parsed: [:0]const u8 = try fromSliceAlloc( [:0]const u8, gpa, "\"abc\"", @@ -2129,7 +2198,7 @@ test "std.zon string literal" { } { - const parsed: [:0]const u8 = try fromSlice( + const parsed: [:0]const u8 = try fromSliceAlloc( [:0]const u8, gpa, "\\\\abc", @@ -2149,7 +2218,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([:1]const u8, gpa, "\"foo\"", &diag, .{}), + fromSliceAlloc([:1]const u8, gpa, "\"foo\"", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2159,7 +2228,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([:1]const u8, gpa, "\\\\foo", &diag, .{}), + fromSliceAlloc([:1]const u8, gpa, "\\\\foo", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2171,7 +2240,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]const u8, gpa, "true", &diag, .{}), + fromSliceAlloc([]const u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected string\n", "{f}", .{diag}); } @@ -2182,7 +2251,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]const u8, gpa, ".{false}", &diag, .{}), + fromSliceAlloc([]const u8, gpa, ".{false}", &diag, .{}), ); try std.testing.expectFmt("1:3: error: expected type 'u8'\n", "{f}", .{diag}); } @@ -2193,7 +2262,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]const i8, gpa, "\"\\a\"", &diag, .{}), + fromSliceAlloc([]const i8, gpa, "\"\\a\"", &diag, .{}), ); try std.testing.expectFmt("1:3: error: invalid escape character: 'a'\n", "{f}", .{diag}); } @@ -2205,7 +2274,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]const i8, gpa, "\"a\"", &diag, .{}), + fromSliceAlloc([]const i8, gpa, "\"a\"", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2215,7 +2284,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]const i8, gpa, "\\\\a", &diag, .{}), + fromSliceAlloc([]const i8, gpa, "\\\\a", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2228,7 +2297,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]align(2) const u8, gpa, "\"abc\"", &diag, .{}), + fromSliceAlloc([]align(2) const u8, gpa, "\"abc\"", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2238,7 +2307,7 @@ test "std.zon string literal" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice([]align(2) const u8, gpa, "\\\\abc", &diag, .{}), + fromSliceAlloc([]align(2) const u8, gpa, "\\\\abc", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected array\n", "{f}", .{diag}); } @@ -2253,7 +2322,7 @@ test "std.zon string literal" { message2: String, message3: String, }; - const parsed = try fromSlice(S, gpa, + const parsed = try fromSliceAlloc(S, gpa, \\.{ \\ .message = \\ \\hello, world! @@ -2909,7 +2978,7 @@ test "std.zon free on error" { y: []const u8, z: bool, }; - try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator, \\.{ \\ .x = "hello", \\ .y = "world", @@ -2925,7 +2994,7 @@ test "std.zon free on error" { []const u8, bool, }; - try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator, \\.{ \\ "hello", \\ "world", @@ -2940,7 +3009,7 @@ test "std.zon free on error" { x: []const u8, y: bool, }; - try std.testing.expectError(error.ParseZon, fromSlice(Struct, std.testing.allocator, + try std.testing.expectError(error.ParseZon, fromSliceAlloc(Struct, std.testing.allocator, \\.{ \\ .x = "hello", \\} @@ -2949,7 +3018,7 @@ test "std.zon free on error" { // Test freeing partially allocated arrays { - try std.testing.expectError(error.ParseZon, fromSlice( + try std.testing.expectError(error.ParseZon, fromSliceAlloc( [3][]const u8, std.testing.allocator, \\.{ @@ -2965,7 +3034,7 @@ test "std.zon free on error" { // Test freeing partially allocated slices { - try std.testing.expectError(error.ParseZon, fromSlice( + try std.testing.expectError(error.ParseZon, fromSliceAlloc( [][]const u8, std.testing.allocator, \\.{ @@ -2989,7 +3058,7 @@ test "std.zon free on error" { // We can also parse types that can't be freed if it's impossible for an error to occur after // the allocation, as is the case here. { - const result = try fromSlice( + const result = try fromSliceAlloc( union { x: []const u8 }, std.testing.allocator, ".{ .x = \"foo\" }", @@ -3008,7 +3077,7 @@ test "std.zon free on error" { union { x: []const u8 }, bool, }; - const result = try fromSlice( + const result = try fromSliceAlloc( S, std.testing.allocator, ".{ .{ .x = \"foo\" }, true }", @@ -3026,7 +3095,7 @@ test "std.zon free on error" { a: union { x: []const u8 }, b: bool, }; - const result = try fromSlice( + const result = try fromSliceAlloc( S, std.testing.allocator, ".{ .a = .{ .x = \"foo\" }, .b = true }", @@ -3043,7 +3112,7 @@ test "std.zon free on error" { // Again but for arrays. { const S = [2]union { x: []const u8 }; - const result = try fromSlice( + const result = try fromSliceAlloc( S, std.testing.allocator, ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", @@ -3061,7 +3130,7 @@ test "std.zon free on error" { // Again but for slices. { const S = []union { x: []const u8 }; - const result = try fromSlice( + const result = try fromSliceAlloc( S, std.testing.allocator, ".{ .{ .x = \"foo\" }, .{ .x = \"bar\" } }", @@ -3114,9 +3183,9 @@ test "std.zon vector" { { try std.testing.expectEqual( @Vector(0, *const u8){}, - try fromSlice(@Vector(0, *const u8), gpa, ".{}", null, .{}), + try fromSliceAlloc(@Vector(0, *const u8), gpa, ".{}", null, .{}), ); - const pointers = try fromSlice(@Vector(3, *const u8), gpa, ".{2, 4, 6}", null, .{}); + const pointers = try fromSliceAlloc(@Vector(3, *const u8), gpa, ".{2, 4, 6}", null, .{}); defer free(gpa, pointers); try std.testing.expectEqualDeep(@Vector(3, *const u8){ &2, &4, &6 }, pointers); } @@ -3124,9 +3193,9 @@ test "std.zon vector" { { try std.testing.expectEqual( @Vector(0, ?*const u8){}, - try fromSlice(@Vector(0, ?*const u8), gpa, ".{}", null, .{}), + try fromSliceAlloc(@Vector(0, ?*const u8), gpa, ".{}", null, .{}), ); - const pointers = try fromSlice(@Vector(3, ?*const u8), gpa, ".{2, null, 6}", null, .{}); + const pointers = try fromSliceAlloc(@Vector(3, ?*const u8), gpa, ".{2, null, 6}", null, .{}); defer free(gpa, pointers); try std.testing.expectEqualDeep(@Vector(3, ?*const u8){ &2, null, &6 }, pointers); } @@ -3193,7 +3262,7 @@ test "std.zon vector" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}), + fromSliceAlloc(@Vector(3, *u8), gpa, ".{1, true, 3}", &diag, .{}), ); try std.testing.expectFmt("1:6: error: expected type 'u8'\n", "{f}", .{diag}); } @@ -3204,77 +3273,77 @@ test "std.zon add pointers" { // Primitive with varying levels of pointers { - const result = try fromSlice(*u32, gpa, "10", null, .{}); + const result = try fromSliceAlloc(*u32, gpa, "10", null, .{}); defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*); } { - const result = try fromSlice(**u32, gpa, "10", null, .{}); + const result = try fromSliceAlloc(**u32, gpa, "10", null, .{}); defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*.*); } { - const result = try fromSlice(***u32, gpa, "10", null, .{}); + const result = try fromSliceAlloc(***u32, gpa, "10", null, .{}); defer free(gpa, result); try std.testing.expectEqual(@as(u32, 10), result.*.*.*); } // Primitive optional with varying levels of pointers { - const some = try fromSlice(?*u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(?*u32, gpa, "10", null, .{}); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.?.*); - const none = try fromSlice(?*u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(?*u32, gpa, "null", null, .{}); defer free(gpa, none); try std.testing.expectEqual(null, none); } { - const some = try fromSlice(*?u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(*?u32, gpa, "10", null, .{}); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.?); - const none = try fromSlice(*?u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(*?u32, gpa, "null", null, .{}); defer free(gpa, none); try std.testing.expectEqual(null, none.*); } { - const some = try fromSlice(?**u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(?**u32, gpa, "10", null, .{}); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.?.*.*); - const none = try fromSlice(?**u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(?**u32, gpa, "null", null, .{}); defer free(gpa, none); try std.testing.expectEqual(null, none); } { - const some = try fromSlice(*?*u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(*?*u32, gpa, "10", null, .{}); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.?.*); - const none = try fromSlice(*?*u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(*?*u32, gpa, "null", null, .{}); defer free(gpa, none); try std.testing.expectEqual(null, none.*); } { - const some = try fromSlice(**?u32, gpa, "10", null, .{}); + const some = try fromSliceAlloc(**?u32, gpa, "10", null, .{}); defer free(gpa, some); try std.testing.expectEqual(@as(u32, 10), some.*.*.?); - const none = try fromSlice(**?u32, gpa, "null", null, .{}); + const none = try fromSliceAlloc(**?u32, gpa, "null", null, .{}); defer free(gpa, none); try std.testing.expectEqual(null, none.*.*); } // Pointer to an array { - const result = try fromSlice(*[3]u8, gpa, ".{ 1, 2, 3 }", null, .{}); + const result = try fromSliceAlloc(*[3]u8, gpa, ".{ 1, 2, 3 }", null, .{}); defer free(gpa, result); try std.testing.expectEqual([3]u8{ 1, 2, 3 }, result.*); } @@ -3297,7 +3366,7 @@ test "std.zon add pointers" { .f2 = &null, }; - const found = try fromSlice(?*Outer, gpa, + const found = try fromSliceAlloc(?*Outer, gpa, \\.{ \\ .f1 = .{ \\ .f1 = null, @@ -3317,7 +3386,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected type '?u8'\n", "{f}", .{diag}); } @@ -3327,7 +3396,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const f32, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const f32, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected type '?f32'\n", "{f}", .{diag}); } @@ -3337,7 +3406,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const @Vector(3, u8), gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected type '?@Vector(3, u8)'\n", "{f}", .{diag}); } @@ -3347,7 +3416,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const bool, gpa, "10", &diag, .{}), + fromSliceAlloc(*const ?*const bool, gpa, "10", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected type '?bool'\n", "{f}", .{diag}); } @@ -3357,7 +3426,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const struct { a: i32 }, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional struct\n", "{f}", .{diag}); } @@ -3367,7 +3436,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const struct { i32 }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const struct { i32 }, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional tuple\n", "{f}", .{diag}); } @@ -3377,7 +3446,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const union { x: void }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const union { x: void }, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional union\n", "{f}", .{diag}); } @@ -3387,7 +3456,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const [3]u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const [3]u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3397,7 +3466,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(?[3]u8, gpa, "true", &diag, .{}), + fromSliceAlloc(?[3]u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3407,7 +3476,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const []u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const []u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3417,7 +3486,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(?[]u8, gpa, "true", &diag, .{}), + fromSliceAlloc(?[]u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional array\n", "{f}", .{diag}); } @@ -3427,7 +3496,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const []const u8, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const []const u8, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional string\n", "{f}", .{diag}); } @@ -3437,7 +3506,7 @@ test "std.zon add pointers" { defer diag.deinit(gpa); try std.testing.expectError( error.ParseZon, - fromSlice(*const ?*const enum { foo }, gpa, "true", &diag, .{}), + fromSliceAlloc(*const ?*const enum { foo }, gpa, "true", &diag, .{}), ); try std.testing.expectFmt("1:1: error: expected optional enum literal\n", "{f}", .{diag}); } @@ -3466,3 +3535,30 @@ test "std.zon stop on node" { try std.testing.expectEqual(Zoir.Node{ .float_literal = 1.23 }, result.get(diag.zoir)); } } + +test "std.zon no alloc" { + const gpa = std.testing.allocator; + + try std.testing.expectEqual( + [3]u8{ 1, 2, 3 }, + try fromSlice([3]u8, gpa, ".{ 1, 2, 3 }", null, .{}), + ); + + const Nested = struct { u8, u8, struct { u8, u8 } }; + + var ast = try std.zig.Ast.parse(gpa, ".{ 1, 2, .{ 3, 4 } }", .zon); + defer ast.deinit(gpa); + + var zoir = try ZonGen.generate(gpa, ast, .{ .parse_str_lits = false }); + defer zoir.deinit(gpa); + + try std.testing.expectEqual( + Nested{ 1, 2, .{ 3, 4 } }, + try fromZoir(Nested, ast, zoir, null, .{}), + ); + + try std.testing.expectEqual( + Nested{ 1, 2, .{ 3, 4 } }, + try fromZoirNode(Nested, ast, zoir, .root, null, .{}), + ); +}