| author | |
| committer | |
| log | 07691db3ae061d8122f6c53c1f34c2fb0df2f7ef |
| tree | 379046dbe83b30f007433360ad08d3bdee7e2234 |
| parent | 6534f2ef4f8161f4121326f19bc3cf89324f62c5 |
* LLVM backend: fix phi instruction not respecting `isByRef`
- Also fix `is_non_null` not respecting `isByRef`
* Type: implement abiSize for error unions4 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 { |
| 1804 | 1804 | ||
| 1805 | const raw_llvm_ty = try self.dg.llvmType(inst_ty); | 1805 | const raw_llvm_ty = try self.dg.llvmType(inst_ty); |
| 1806 | 1806 | ||
| 1807 | // If the zig tag type is a function, this represents an actual function body; not | 1807 | const llvm_ty = ty: { |
| 1808 | // a pointer to it. LLVM IR allows the call instruction to use function bodies instead | 1808 | // 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 therefore | 1809 | // 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 | else | 1813 | break :ty raw_llvm_ty.pointerType(0); |
| 1814 | raw_llvm_ty; | 1814 | } |
| 1815 | break :ty raw_llvm_ty; | ||
| 1816 | }; | ||
| 1815 | 1817 | ||
| 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 | } |
| 2317 | 2319 | ||
| 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 | }, |
| 1694 | 1694 | ||
| 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 | }, |
| 1707 | 1707 | ||
| ... | @@ -1942,15 +1942,25 @@ pub const Type = extern union { | ... | @@ -1942,15 +1942,25 @@ pub const Type = extern union { |
| 1942 | }, | 1942 | }, |
| 1943 | 1943 | ||
| 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 { |
| 49 | test "error wrapping" { | 49 | test "error wrapping" { |
| 50 | try expect((baz() catch unreachable) == 15); | 50 | try expect((baz() catch unreachable) == 15); |
| 51 | } | 51 | } |
| 52 | |||
| 53 | test "unwrap simple value from error" { | ||
| 54 | const i = unwrapSimpleValueFromErrorDo() catch unreachable; | ||
| 55 | try expect(i == 13); | ||
| 56 | } | ||
| 57 | fn unwrapSimpleValueFromErrorDo() anyerror!isize { | ||
| 58 | return 13; | ||
| 59 | } | ||
| 60 | |||
| 61 | test "error return in assignment" { | ||
| 62 | doErrReturnInAssignment() catch unreachable; | ||
| 63 | } | ||
| 64 | |||
| 65 | fn doErrReturnInAssignment() anyerror!void { | ||
| 66 | var x: i32 = undefined; | ||
| 67 | x = try makeANonErr(); | ||
| 68 | } | ||
| 69 | |||
| 70 | fn makeANonErr() anyerror!i32 { | ||
| 71 | return 1; | ||
| 72 | } | ||
| 73 | |||
| 74 | test "syntax: optional operator in front of error union operator" { | ||
| 75 | comptime { | ||
| 76 | try expect(?(anyerror!i32) == ?(anyerror!i32)); | ||
| 77 | } | ||
| 78 | } | ||
| 79 | |||
| 80 | test "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 | |||
| 94 | test "debug info for optional error set" { | ||
| 95 | const SomeError = error{Hello}; | ||
| 96 | var a_local_variable: ?SomeError = null; | ||
| 97 | _ = a_local_variable; | ||
| 98 | } | ||
| 99 | |||
| 100 | test "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; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const mem = std.mem; | 5 | const mem = std.mem; |
| 6 | 6 | ||
| 7 | pub fn foo() anyerror!i32 { | 7 | fn gimmeItBroke() anyerror { |
| 8 | const x = try bar(); | 8 | return error.ItBroke; |
| 9 | return x + 1; | ||
| 10 | } | ||
| 11 | |||
| 12 | pub fn bar() anyerror!i32 { | ||
| 13 | return 13; | ||
| 14 | } | ||
| 15 | |||
| 16 | pub fn baz() anyerror!i32 { | ||
| 17 | const y = foo() catch 1234; | ||
| 18 | return y + 1; | ||
| 19 | } | ||
| 20 | |||
| 21 | test "error wrapping" { | ||
| 22 | try expect((baz() catch unreachable) == 15); | ||
| 23 | } | ||
| 24 | |||
| 25 | fn gimmeItBroke() []const u8 { | ||
| 26 | return @errorName(error.ItBroke); | ||
| 27 | } | 9 | } |
| 28 | 10 | ||
| 29 | test "@errorName" { | 11 | test "@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 | |||
| 34 | test "unwrap simple value from error" { | ||
| 35 | const i = unwrapSimpleValueFromErrorDo() catch unreachable; | ||
| 36 | try expect(i == 13); | ||
| 37 | } | ||
| 38 | fn unwrapSimpleValueFromErrorDo() anyerror!isize { | ||
| 39 | return 13; | ||
| 40 | } | ||
| 41 | |||
| 42 | test "error return in assignment" { | ||
| 43 | doErrReturnInAssignment() catch unreachable; | ||
| 44 | } | ||
| 45 | |||
| 46 | fn doErrReturnInAssignment() anyerror!void { | ||
| 47 | var x: i32 = undefined; | ||
| 48 | x = try makeANonErr(); | ||
| 49 | } | ||
| 50 | |||
| 51 | fn makeANonErr() anyerror!i32 { | ||
| 52 | return 1; | ||
| 53 | } | 15 | } |
| 54 | 16 | ||
| 55 | test "error union type " { | 17 | test "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 | } |
| 118 | 80 | ||
| 119 | test "syntax: optional operator in front of error union operator" { | ||
| 120 | comptime { | ||
| 121 | try expect(?(anyerror!i32) == ?(anyerror!i32)); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | |||
| 125 | test "comptime err to int of error set with only 1 possible value" { | 81 | test "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 | } |
| 270 | 226 | ||
| 271 | test "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 | |||
| 285 | test "return function call to error set from error union function" { | 227 | test "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 | } |
| 309 | 251 | ||
| 310 | test "debug info for optional error set" { | ||
| 311 | const SomeError = error{Hello}; | ||
| 312 | var a_local_variable: ?SomeError = null; | ||
| 313 | _ = a_local_variable; | ||
| 314 | } | ||
| 315 | |||
| 316 | test "nested catch" { | 252 | test "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 | } |
| 337 | 273 | ||
| 338 | test "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 | |||
| 357 | test "function pointer with return type that is error union with payload which is pointer of parent struct" { | 274 | test "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 { |