authorgravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2021-02-01 00:22:00+11:00
committergravatar for quae@daurnimator.comdaurnimator <quae@daurnimator.com> 2021-02-01 01:00:15+11:00
logf88bb56ee5be01b83ab35bd7b6c95539a4e04a9d
tree28a116a31040264537bf90c268df8d7b000f34a0
parent33c0a01b08262d5ad0c666d8fbeb35dfcb36b5ef
signaturelock-open Commit is signed but in an unrecognized format.

std.json union handling should bubble up AllocationRequired


1 files changed, 21 insertions(+), 12 deletions(-)

lib/std/json.zig+21-12
...@@ -1454,6 +1454,8 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:...@@ -1454,6 +1454,8 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
1454 // Parsing some types won't have OutOfMemory in their1454 // Parsing some types won't have OutOfMemory in their
1455 // error-sets, for the condition to be valid, merge it in.1455 // error-sets, for the condition to be valid, merge it in.
1456 if (@as(@TypeOf(err) || error{OutOfMemory}, err) == error.OutOfMemory) return err;1456 if (@as(@TypeOf(err) || error{OutOfMemory}, err) == error.OutOfMemory) return err;
1457 // Bubble up AllocatorRequired, as it indicates missing option
1458 if (@as(@TypeOf(err) || error{AllocatorRequired}, err) == error.AllocatorRequired) return err;
1457 // otherwise continue through the `inline for`1459 // otherwise continue through the `inline for`
1458 }1460 }
1459 }1461 }
...@@ -1741,18 +1743,6 @@ test "parse into tagged union" {...@@ -1741,18 +1743,6 @@ test "parse into tagged union" {
1741 testing.expectEqual(T{ .float = 1.5 }, try parse(T, &TokenStream.init("1.5"), ParseOptions{}));1743 testing.expectEqual(T{ .float = 1.5 }, try parse(T, &TokenStream.init("1.5"), ParseOptions{}));
1742 }1744 }
17431745
1744 { // if union matches string member, fails with NoUnionMembersMatched rather than AllocatorRequired
1745 // Note that this behaviour wasn't necessarily by design, but was
1746 // what fell out of the implementation and may result in interesting
1747 // API breakage if changed
1748 const T = union(enum) {
1749 int: i32,
1750 float: f64,
1751 string: []const u8,
1752 };
1753 testing.expectError(error.NoUnionMembersMatched, parse(T, &TokenStream.init("\"foo\""), ParseOptions{}));
1754 }
1755
1756 { // failing allocations should be bubbled up instantly without trying next member1746 { // failing allocations should be bubbled up instantly without trying next member
1757 var fail_alloc = testing.FailingAllocator.init(testing.allocator, 0);1747 var fail_alloc = testing.FailingAllocator.init(testing.allocator, 0);
1758 const options = ParseOptions{ .allocator = &fail_alloc.allocator };1748 const options = ParseOptions{ .allocator = &fail_alloc.allocator };
...@@ -1782,6 +1772,25 @@ test "parse into tagged union" {...@@ -1782,6 +1772,25 @@ test "parse into tagged union" {
1782 }1772 }
1783}1773}
17841774
1775test "parse union bubbles up AllocatorRequired" {
1776 { // string member first in union (and not matching)
1777 const T = union(enum) {
1778 string: []const u8,
1779 int: i32,
1780 };
1781 testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("42"), ParseOptions{}));
1782 }
1783
1784 { // string member not first in union (and matching)
1785 const T = union(enum) {
1786 int: i32,
1787 float: f64,
1788 string: []const u8,
1789 };
1790 testing.expectError(error.AllocatorRequired, parse(T, &TokenStream.init("\"foo\""), ParseOptions{}));
1791 }
1792}
1793
1785test "parseFree descends into tagged union" {1794test "parseFree descends into tagged union" {
1786 var fail_alloc = testing.FailingAllocator.init(testing.allocator, 1);1795 var fail_alloc = testing.FailingAllocator.init(testing.allocator, 1);
1787 const options = ParseOptions{ .allocator = &fail_alloc.allocator };1796 const options = ParseOptions{ .allocator = &fail_alloc.allocator };