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 {...@@ -1804,14 +1804,16 @@ pub const FuncGen = struct {
18041804
1805 const raw_llvm_ty = try self.dg.llvmType(inst_ty);1805 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; not1807 const llvm_ty = ty: {
1808 // a pointer to it. LLVM IR allows the call instruction to use function bodies instead1808 // If the zig tag type is a function, this represents an actual function body; not
1809 // of function pointers, however the phi makes it a runtime value and therefore1809 // a pointer to it. LLVM IR allows the call instruction to use function bodies instead
1810 // the LLVM type has to be wrapped in a pointer.1810 // of function pointers, however the phi makes it a runtime value and therefore
1811 const llvm_ty = if (inst_ty.zigTypeTag() == .Fn)1811 // the LLVM type has to be wrapped in a pointer.
1812 raw_llvm_ty.pointerType(0)1812 if (inst_ty.zigTypeTag() == .Fn or isByRef(inst_ty)) {
1813 else1813 break :ty raw_llvm_ty.pointerType(0);
1814 raw_llvm_ty;1814 }
1815 break :ty raw_llvm_ty;
1816 };
18151817
1816 const phi_node = self.builder.buildPhi(llvm_ty, "");1818 const phi_node = self.builder.buildPhi(llvm_ty, "");
1817 phi_node.addIncoming(1819 phi_node.addIncoming(
...@@ -2315,7 +2317,7 @@ pub const FuncGen = struct {...@@ -2315,7 +2317,7 @@ pub const FuncGen = struct {
2315 return self.builder.buildICmp(op, loaded, zero, "");2317 return self.builder.buildICmp(op, loaded, zero, "");
2316 }2318 }
23172319
2318 if (operand_is_ptr) {2320 if (operand_is_ptr or isByRef(err_union_ty)) {
2319 const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");2321 const err_field_ptr = self.builder.buildStructGEP(operand, 0, "");
2320 const loaded = self.builder.buildLoad(err_field_ptr, "");2322 const loaded = self.builder.buildLoad(err_field_ptr, "");
2321 return self.builder.buildICmp(op, loaded, zero, "");2323 return self.builder.buildICmp(op, loaded, zero, "");
src/type.zig+25-15
...@@ -1693,15 +1693,15 @@ pub const Type = extern union {...@@ -1693,15 +1693,15 @@ pub const Type = extern union {
1693 },1693 },
16941694
1695 .error_union => {1695 .error_union => {
1696 const payload = self.castTag(.error_union).?.data;1696 const data = self.castTag(.error_union).?.data;
1697 if (!payload.error_set.hasCodeGenBits()) {1697 if (!data.error_set.hasCodeGenBits()) {
1698 return payload.payload.abiAlignment(target);1698 return data.payload.abiAlignment(target);
1699 } else if (!payload.payload.hasCodeGenBits()) {1699 } else if (!data.payload.hasCodeGenBits()) {
1700 return payload.error_set.abiAlignment(target);1700 return data.error_set.abiAlignment(target);
1701 }1701 }
1702 return std.math.max(1702 return @maximum(
1703 payload.payload.abiAlignment(target),1703 data.payload.abiAlignment(target),
1704 payload.error_set.abiAlignment(target),1704 data.error_set.abiAlignment(target),
1705 );1705 );
1706 },1706 },
17071707
...@@ -1942,15 +1942,25 @@ pub const Type = extern union {...@@ -1942,15 +1942,25 @@ pub const Type = extern union {
1942 },1942 },
19431943
1944 .error_union => {1944 .error_union => {
1945 const payload = self.castTag(.error_union).?.data;1945 const data = self.castTag(.error_union).?.data;
1946 if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) {1946 if (!data.error_set.hasCodeGenBits() and !data.payload.hasCodeGenBits()) {
1947 return 0;1947 return 0;
1948 } else if (!payload.error_set.hasCodeGenBits()) {1948 } else if (!data.error_set.hasCodeGenBits()) {
1949 return payload.payload.abiSize(target);1949 return data.payload.abiSize(target);
1950 } else if (!payload.payload.hasCodeGenBits()) {1950 } else if (!data.payload.hasCodeGenBits()) {
1951 return payload.error_set.abiSize(target);1951 return data.error_set.abiSize(target);
1952 }1952 }
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;
1954 },1964 },
1955 };1965 };
1956 }1966 }
test/behavior/error.zig+66
...@@ -49,3 +49,69 @@ pub fn baz() anyerror!i32 {...@@ -49,3 +49,69 @@ pub fn baz() anyerror!i32 {
49test "error wrapping" {49test "error wrapping" {
50 try expect((baz() catch unreachable) == 15);50 try expect((baz() catch unreachable) == 15);
51}51}
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;...@@ -4,52 +4,14 @@ const expectError = std.testing.expectError;
4const expectEqual = std.testing.expectEqual;4const expectEqual = std.testing.expectEqual;
5const mem = std.mem;5const mem = std.mem;
66
7pub fn foo() anyerror!i32 {7fn gimmeItBroke() anyerror {
8 const x = try bar();8 return error.ItBroke;
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);
27}9}
2810
29test "@errorName" {11test "@errorName" {
30 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));12 try expect(mem.eql(u8, @errorName(error.AnError), "AnError"));
31 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));13 try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName"));
32}14 try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke"));
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;
53}15}
5416
55test "error union type " {17test "error union type " {
...@@ -116,12 +78,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {...@@ -116,12 +78,6 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) !void {
116 }78 }
117}79}
11880
119test "syntax: optional operator in front of error union operator" {
120 comptime {
121 try expect(?(anyerror!i32) == ?(anyerror!i32));
122 }
123}
124
125test "comptime err to int of error set with only 1 possible value" {81test "comptime err to int of error set with only 1 possible value" {
126 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));82 testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
127 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));83 comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A));
...@@ -268,20 +224,6 @@ test "nested error union function call in optional unwrap" {...@@ -268,20 +224,6 @@ test "nested error union function call in optional unwrap" {
268 }224 }
269}225}
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
285test "return function call to error set from error union function" {227test "return function call to error set from error union function" {
286 const S = struct {228 const S = struct {
287 fn errorable() anyerror!i32 {229 fn errorable() anyerror!i32 {
...@@ -307,12 +249,6 @@ test "optional error set is the same size as error set" {...@@ -307,12 +249,6 @@ test "optional error set is the same size as error set" {
307 comptime try expect(S.returnsOptErrSet() == null);249 comptime try expect(S.returnsOptErrSet() == null);
308}250}
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
316test "nested catch" {252test "nested catch" {
317 const S = struct {253 const S = struct {
318 fn entry() !void {254 fn entry() !void {
...@@ -335,25 +271,6 @@ test "nested catch" {...@@ -335,25 +271,6 @@ test "nested catch" {
335 comptime try S.entry();271 comptime try S.entry();
336}272}
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
357test "function pointer with return type that is error union with payload which is pointer of parent struct" {274test "function pointer with return type that is error union with payload which is pointer of parent struct" {
358 const S = struct {275 const S = struct {
359 const Foo = struct {276 const Foo = struct {