| 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 | 1804 | |
| 1805 | 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 | |
| 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 | }; | |
| 1815 | 1817 | |
| 1816 | 1818 | const phi_node = self.builder.buildPhi(llvm_ty, ""); |
| 1817 | 1819 | phi_node.addIncoming( |
| ... | ... | @@ -2315,7 +2317,7 @@ pub const FuncGen = struct { |
| 2315 | 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 | 2321 | const err_field_ptr = self.builder.buildStructGEP(operand, 0, ""); |
| 2320 | 2322 | const loaded = self.builder.buildLoad(err_field_ptr, ""); |
| 2321 | 2323 | return self.builder.buildICmp(op, loaded, zero, ""); |
src/type.zig+25-15| ... | ... | @@ -1693,15 +1693,15 @@ pub const Type = extern union { |
| 1693 | 1693 | }, |
| 1694 | 1694 | |
| 1695 | 1695 | .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); | |
| 1701 | 1701 | } |
| 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), | |
| 1705 | 1705 | ); |
| 1706 | 1706 | }, |
| 1707 | 1707 | |
| ... | ... | @@ -1942,15 +1942,25 @@ pub const Type = extern union { |
| 1942 | 1942 | }, |
| 1943 | 1943 | |
| 1944 | 1944 | .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()) { | |
| 1947 | 1947 | 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); | |
| 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 | 49 | test "error wrapping" { |
| 50 | 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 | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | const mem = std.mem; |
| 6 | 6 | |
| 7 | pub fn foo() anyerror!i32 { | |
| 8 | const x = try bar(); | |
| 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); | |
| 7 | fn gimmeItBroke() anyerror { | |
| 8 | return error.ItBroke; | |
| 27 | 9 | } |
| 28 | 10 | |
| 29 | 11 | test "@errorName" { |
| 30 | 12 | try expect(mem.eql(u8, @errorName(error.AnError), "AnError")); |
| 31 | 13 | try expect(mem.eql(u8, @errorName(error.ALongerErrorName), "ALongerErrorName")); |
| 32 | } | |
| 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; | |
| 14 | try expect(mem.eql(u8, @errorName(gimmeItBroke()), "ItBroke")); | |
| 53 | 15 | } |
| 54 | 16 | |
| 55 | 17 | test "error union type " { |
| ... | ... | @@ -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 | 81 | test "comptime err to int of error set with only 1 possible value" { |
| 126 | 82 | testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); |
| 127 | 83 | comptime testErrToIntWithOnePossibleValue(error.A, @errorToInt(error.A)); |
| ... | ... | @@ -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 | 227 | test "return function call to error set from error union function" { |
| 286 | 228 | const S = struct { |
| 287 | 229 | fn errorable() anyerror!i32 { |
| ... | ... | @@ -307,12 +249,6 @@ test "optional error set is the same size as error set" { |
| 307 | 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 | 252 | test "nested catch" { |
| 317 | 253 | const S = struct { |
| 318 | 254 | fn entry() !void { |
| ... | ... | @@ -335,25 +271,6 @@ test "nested catch" { |
| 335 | 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 | 274 | test "function pointer with return type that is error union with payload which is pointer of parent struct" { |
| 358 | 275 | const S = struct { |
| 359 | 276 | const Foo = struct { |