authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-17 15:36:12-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-17 15:36:12-07:00
log07691db3ae061d8122f6c53c1f34c2fb0df2f7ef
tree379046dbe83b30f007433360ad08d3bdee7e2234
parent6534f2ef4f8161f4121326f19bc3cf89324f62c5

stage2: fix handling of error unions as return type

* LLVM backend: fix phi instruction not respecting `isByRef` - Also fix `is_non_null` not respecting `isByRef` * Type: implement abiSize for error unions

4 files changed, 105 insertions(+), 110 deletions(-)

src/codegen/llvm.zig+11-9
......@@ -1804,14 +1804,16 @@ pub const FuncGen = struct {
18041804
18051805 const raw_llvm_ty = try self.dg.llvmType(inst_ty);
18061806
1807 // If the zig tag type is a function, this represents an actual function body; not
1808 // a pointer to it. LLVM IR allows the call instruction to use function bodies instead
1809 // of function pointers, however the phi makes it a runtime value and therefore
1810 // the LLVM type has to be wrapped in a pointer.
1811 const llvm_ty = if (inst_ty.zigTypeTag() == .Fn)
1812 raw_llvm_ty.pointerType(0)
1813 else
1814 raw_llvm_ty;
1807 const llvm_ty = ty: {
1808 // If the zig tag type is a function, this represents an actual function body; not
1809 // a pointer to it. LLVM IR allows the call instruction to use function bodies instead
1810 // of function pointers, however the phi makes it a runtime value and therefore
1811 // the LLVM type has to be wrapped in a pointer.
1812 if (inst_ty.zigTypeTag() == .Fn or isByRef(inst_ty)) {
1813 break :ty raw_llvm_ty.pointerType(0);
1814 }
1815 break :ty raw_llvm_ty;
1816 };
18151817
18161818 const phi_node = self.builder.buildPhi(llvm_ty, "");
18171819 phi_node.addIncoming(
......@@ -2315,7 +2317,7 @@ pub const FuncGen = struct {
23152317 return self.builder.buildICmp(op, loaded, zero, "");
23162318 }
23172319
2318 if (operand_is_ptr) {
2320 if (operand_is_ptr or isByRef(err_union_ty)) {
23192321 const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");
23202322 const loaded = self.builder.buildLoad(err_field_ptr, "");
23212323 return self.builder.buildICmp(op, loaded, zero, "");
src/type.zig+25-15
......@@ -1693,15 +1693,15 @@ pub const Type = extern union {
16931693 },
16941694
16951695 .error_union => {
1696 const payload = self.castTag(.error_union).?.data;
1697 if (!payload.error_set.hasCodeGenBits()) {
1698 return payload.payload.abiAlignment(target);
1699 } else if (!payload.payload.hasCodeGenBits()) {
1700 return payload.error_set.abiAlignment(target);
1696 const data = self.castTag(.error_union).?.data;
1697 if (!data.error_set.hasCodeGenBits()) {
1698 return data.payload.abiAlignment(target);
1699 } else if (!data.payload.hasCodeGenBits()) {
1700 return data.error_set.abiAlignment(target);
17011701 }
1702 return std.math.max(
1703 payload.payload.abiAlignment(target),
1704 payload.error_set.abiAlignment(target),
1702 return @maximum(
1703 data.payload.abiAlignment(target),
1704 data.error_set.abiAlignment(target),
17051705 );
17061706 },
17071707
......@@ -1942,15 +1942,25 @@ pub const Type = extern union {
19421942 },
19431943
19441944 .error_union => {
1945 const payload = self.castTag(.error_union).?.data;
1946 if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {
1945 const data = self.castTag(.error_union).?.data;
1946 if (!data.error_set.hasCodeGenBits() and !data.payload.hasCodeGenBits()) {
19471947 return 0;
1948 } else if (!payload.error_set.hasCodeGenBits()) {
1949 return payload.payload.abiSize(target);
1950 } else if (!payload.payload.hasCodeGenBits()) {
1951 return payload.error_set.abiSize(target);
1948 } else if (!data.error_set.hasCodeGenBits()) {
1949 return data.payload.abiSize(target);
1950 } else if (!data.payload.hasCodeGenBits()) {
1951 return data.error_set.abiSize(target);
19521952 }
1953 std.debug.panic("TODO abiSize error union {}", .{self});
1953 const code_align = abiAlignment(data.error_set, target);
1954 const payload_align = abiAlignment(data.payload, target);
1955 const big_align = @maximum(code_align, payload_align);
1956 const payload_size = abiSize(data.payload, target);
1957
1958 var size: u64 = 0;
1959 size += abiSize(data.error_set, target);
1960 size = std.mem.alignForwardGeneric(u64, size, payload_align);
1961 size += payload_size;
1962 size = std.mem.alignForwardGeneric(u64, size, big_align);
1963 return size;
19541964 },
19551965 };
19561966 }
test/behavior/error.zig+66
......@@ -49,3 +49,69 @@ pub fn baz() anyerror!i32 {
4949test "error wrapping" {
5050 try expect((baz() catch unreachable) == 15);
5151}
52
53test "unwrap simple value from error" {
54 const i = unwrapSimpleValueFromErrorDo() catch unreachable;
55 try expect(i == 13);
56}
57fn unwrapSimpleValueFromErrorDo() anyerror!isize {
58 return 13;
59}
60
61test "error return in assignment" {
62 doErrReturnInAssignment() catch unreachable;
63}
64
65fn doErrReturnInAssignment() anyerror!void {
66 var x: i32 = undefined;
67 x = try makeANonErr();
68}
69
70fn makeANonErr() anyerror!i32 {
71 return 1;
72}
73
74test "syntax: optional operator in front of error union operator" {
75 comptime {
76 try expect(?(anyerror!i32) == ?(anyerror!i32));
77 }
78}
79
80test "widen cast integer payload of error union function call" {
81 const S = struct {
82 fn errorable() !u64 {
83 var x = @as(u64, try number());
84 return x;
85 }
86
87 fn number() anyerror!u32 {
88 return 1234;
89 }
90 };
91 try expect((try S.errorable()) == 1234);
92}
93
94test "debug info for optional error set" {
95 const SomeError = error{Hello};
96 var a_local_variable: ?SomeError = null;
97 _ = a_local_variable;
98}
99
100test "implicit cast to optional to error union to return result loc" {
101 const S = struct {
102 fn entry() !void {
103 var x: Foo = undefined;
104 if (func(&x)) |opt| {
105 try expect(opt != null);
106 } else |_| @panic("expected non error");
107 }
108 fn func(f: *Foo) anyerror!?*Foo {
109 return f;
110 }
111 const Foo = struct {
112 field: i32,
113 };
114 };
115 try S.entry();
116 //comptime S.entry(); TODO
117}
test/behavior/error_stage1.zig+3-86
......@@ -4,52 +4,14 @@ const expectError = std.testing.expectError;
44const expectEqual = std.testing.expectEqual;
55const mem = std.mem;
66
7pub fn foo() anyerror!i32 {
8 const x = try bar();
9 return x + 1;
10}
11
12pub fn bar() anyerror!i32 {
13 return 13;
14}
15
16pub fn baz() anyerror!i32 {
17 const y = foo() catch 1234;
18 return y + 1;
19}
20
21test "error wrapping" {
22 try expect((baz() catch unreachable) == 15);
23}
24
25fn gimmeItBroke() []const u8 {
26 return @errorName(error.ItBroke);
7fn gimmeItBroke() anyerror {
8 return error.ItBroke;
279}
2810
2911test "@errorName" {
3012 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
3113 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
32}
33
34test "unwrap simple value from error" {
35 const i = unwrapSimpleValueFromErrorDo() catch unreachable;
36 try expect(i == 13);
37}
38fn unwrapSimpleValueFromErrorDo() anyerror!isize {
39 return 13;
40}
41
42test "error return in assignment" {
43 doErrReturnInAssignment() catch unreachable;
44}
45
46fn doErrReturnInAssignment() anyerror!void {
47 var x: i32 = undefined;
48 x = try makeANonErr();
49}
50
51fn makeANonErr() anyerror!i32 {
52 return 1;
14 try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke"));
5315}
5416
5517test "error union type " {
......@@ -116,12 +78,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
11678 }
11779}
11880
119test "syntax: optional operator in front of error union operator" {
120 comptime {
121 try expect(?(anyerror!i32) == ?(anyerror!i32));
122 }
123}
124
12581test "comptime err to int of error set with only 1 possible value" {
12682 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
12783 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
......@@ -268,20 +224,6 @@ test "nested error union function call in optional unwrap" {
268224 }
269225}
270226
271test "widen cast integer payload of error union function call" {
272 const S = struct {
273 fn errorable() !u64 {
274 var x = @as(u64, try number());
275 return x;
276 }
277
278 fn number() anyerror!u32 {
279 return 1234;
280 }
281 };
282 try expect((try S.errorable()) == 1234);
283}
284
285227test "return function call to error set from error union function" {
286228 const S = struct {
287229 fn errorable() anyerror!i32 {
......@@ -307,12 +249,6 @@ test "optional error set is the same size as error set" {
307249 comptime try expect(S.returnsOptErrSet() == null);
308250}
309251
310test "debug info for optional error set" {
311 const SomeError = error{Hello};
312 var a_local_variable: ?SomeError = null;
313 _ = a_local_variable;
314}
315
316252test "nested catch" {
317253 const S = struct {
318254 fn entry() !void {
......@@ -335,25 +271,6 @@ test "nested catch" {
335271 comptime try S.entry();
336272}
337273
338test "implicit cast to optional to error union to return result loc" {
339 const S = struct {
340 fn entry() !void {
341 var x: Foo = undefined;
342 if (func(&x)) |opt| {
343 try expect(opt != null);
344 } else |_| @panic("expected non error");
345 }
346 fn func(f: *Foo) anyerror!?*Foo {
347 return f;
348 }
349 const Foo = struct {
350 field: i32,
351 };
352 };
353 try S.entry();
354 //comptime S.entry(); TODO
355}
356
357274test "function pointer with return type that is error union with payload which is pointer of parent struct" {
358275 const S = struct {
359276 const Foo = struct {