| author | |
| committer | |
| log | 674932e503e5b3a929f5a17eb4aa2fd2866219c3 |
| tree | 996ee589e424fde8f61fe6edd1a02001c49001e2 |
| parent | 325bae7fc0025f7153bd1de1c28e642e896190b1 |
5 files changed, 75 insertions(+), 29 deletions(-)
src/codegen/c.zig+16-1| ... | ... | @@ -308,6 +308,11 @@ pub const DeclGen = struct { |
| 308 | 308 | if (ty.isPtrLikeOptional()) { |
| 309 | 309 | return dg.renderValue(writer, payload_type, val); |
| 310 | 310 | } |
| 311 | const target = dg.module.getTarget(); | |
| 312 | if (payload_type.abiSize(target) == 0) { | |
| 313 | const is_null = val.castTag(.opt_payload) == null; | |
| 314 | return writer.print("{}", .{is_null}); | |
| 315 | } | |
| 311 | 316 | try writer.writeByte('('); |
| 312 | 317 | try dg.renderType(writer, ty); |
| 313 | 318 | try writer.writeAll("){"); |
| ... | ... | @@ -588,10 +593,13 @@ pub const DeclGen = struct { |
| 588 | 593 | .Optional => { |
| 589 | 594 | var opt_buf: Type.Payload.ElemType = undefined; |
| 590 | 595 | const child_type = t.optionalChild(&opt_buf); |
| 596 | const target = dg.module.getTarget(); | |
| 591 | 597 | if (t.isPtrLikeOptional()) { |
| 592 | 598 | return dg.renderType(w, child_type); |
| 593 | 599 | } else if (dg.typedefs.get(t)) |some| { |
| 594 | 600 | return w.writeAll(some.name); |
| 601 | } else if (child_type.abiSize(target) == 0) { | |
| 602 | return w.writeAll("bool"); | |
| 595 | 603 | } |
| 596 | 604 | |
| 597 | 605 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); |
| ... | ... | @@ -2100,14 +2108,21 @@ fn airIsNull( |
| 2100 | 2108 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 2101 | 2109 | const writer = f.object.writer(); |
| 2102 | 2110 | const operand = try f.resolveInst(un_op); |
| 2111 | const target = f.object.dg.module.getTarget(); | |
| 2103 | 2112 | |
| 2104 | 2113 | const local = try f.allocLocal(Type.initTag(.bool), .Const); |
| 2105 | 2114 | try writer.writeAll(" = ("); |
| 2106 | 2115 | try f.writeCValue(writer, operand); |
| 2107 | 2116 | |
| 2108 | if (f.air.typeOf(un_op).isPtrLikeOptional()) { | |
| 2117 | const ty = f.air.typeOf(un_op); | |
| 2118 | var opt_buf: Type.Payload.ElemType = undefined; | |
| 2119 | const payload_type = ty.optionalChild(&opt_buf); | |
| 2120 | ||
| 2121 | if (ty.isPtrLikeOptional()) { | |
| 2109 | 2122 | // operand is a regular pointer, test `operand !=/== NULL` |
| 2110 | 2123 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); |
| 2124 | } else if (payload_type.abiSize(target) == 0) { | |
| 2125 | try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); | |
| 2111 | 2126 | } else { |
| 2112 | 2127 | try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); |
| 2113 | 2128 | } |
src/type.zig+7-1| ... | ... | @@ -1805,7 +1805,13 @@ pub const Type = extern union { |
| 1805 | 1805 | .void, |
| 1806 | 1806 | => 0, |
| 1807 | 1807 | |
| 1808 | .@"struct" => return self.structFieldOffset(self.structFieldCount(), target), | |
| 1808 | .@"struct" => { | |
| 1809 | const field_count = self.structFieldCount(); | |
| 1810 | if (field_count == 0) { | |
| 1811 | return 0; | |
| 1812 | } | |
| 1813 | return self.structFieldOffset(field_count, target); | |
| 1814 | }, | |
| 1809 | 1815 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 1810 | 1816 | var buffer: Payload.Bits = undefined; |
| 1811 | 1817 | const int_tag_ty = self.intTagType(&buffer); |
test/behavior.zig+2-1| ... | ... | @@ -6,6 +6,7 @@ test { |
| 6 | 6 | _ = @import("behavior/bool.zig"); |
| 7 | 7 | _ = @import("behavior/if.zig"); |
| 8 | 8 | _ = @import("behavior/truncate.zig"); |
| 9 | _ = @import("behavior/null.zig"); | |
| 9 | 10 | |
| 10 | 11 | if (builtin.object_format != .c) { |
| 11 | 12 | // Tests that pass for stage1 and stage2 but not the C backend. |
| ... | ... | @@ -48,7 +49,7 @@ test { |
| 48 | 49 | _ = @import("behavior/math.zig"); |
| 49 | 50 | _ = @import("behavior/maximum_minimum.zig"); |
| 50 | 51 | _ = @import("behavior/member_func.zig"); |
| 51 | _ = @import("behavior/null.zig"); | |
| 52 | _ = @import("behavior/null_llvm.zig"); | |
| 52 | 53 | _ = @import("behavior/optional.zig"); |
| 53 | 54 | _ = @import("behavior/pointers.zig"); |
| 54 | 55 | _ = @import("behavior/popcount.zig"); |
test/behavior/null.zig+14-26| ... | ... | @@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool { |
| 59 | 59 | return value > 1234; |
| 60 | 60 | } |
| 61 | 61 | |
| 62 | test "null literal outside function" { | |
| 63 | const is_null = here_is_a_null_literal.context == null; | |
| 64 | try expect(is_null); | |
| 65 | ||
| 66 | const is_non_null = here_is_a_null_literal.context != null; | |
| 67 | try expect(!is_non_null); | |
| 68 | } | |
| 69 | const SillyStruct = struct { | |
| 70 | context: ?i32, | |
| 71 | }; | |
| 72 | const here_is_a_null_literal = SillyStruct{ .context = null }; | |
| 73 | ||
| 74 | 62 | test "test null runtime" { |
| 75 | 63 | try testTestNullRuntime(null); |
| 76 | 64 | } |
| ... | ... | @@ -97,23 +85,23 @@ fn bar(x: ?void) ?void { |
| 97 | 85 | } |
| 98 | 86 | } |
| 99 | 87 | |
| 100 | const StructWithOptional = struct { | |
| 101 | field: ?i32, | |
| 102 | }; | |
| 88 | const Empty = struct {}; | |
| 103 | 89 | |
| 104 | var struct_with_optional: StructWithOptional = undefined; | |
| 90 | test "optional struct{}" { | |
| 91 | _ = try optionalEmptyStructImpl(); | |
| 92 | _ = comptime try optionalEmptyStructImpl(); | |
| 93 | } | |
| 105 | 94 | |
| 106 | test "unwrap optional which is field of global var" { | |
| 107 | struct_with_optional.field = null; | |
| 108 | if (struct_with_optional.field) |payload| { | |
| 109 | _ = payload; | |
| 110 | unreachable; | |
| 111 | } | |
| 112 | struct_with_optional.field = 1234; | |
| 113 | if (struct_with_optional.field) |payload| { | |
| 114 | try expect(payload == 1234); | |
| 95 | fn optionalEmptyStructImpl() !void { | |
| 96 | try expect(baz(null) == null); | |
| 97 | try expect(baz(Empty{}) != null); | |
| 98 | } | |
| 99 | ||
| 100 | fn baz(x: ?Empty) ?Empty { | |
| 101 | if (x) |_| { | |
| 102 | return Empty{}; | |
| 115 | 103 | } else { |
| 116 | unreachable; | |
| 104 | return null; | |
| 117 | 105 | } |
| 118 | 106 | } |
| 119 | 107 |
test/behavior/null_llvm.zig created+36| ... | ... | @@ -0,0 +1,36 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | ||
| 4 | test "null literal outside function" { | |
| 5 | const is_null = here_is_a_null_literal.context == null; | |
| 6 | try expect(is_null); | |
| 7 | ||
| 8 | const is_non_null = here_is_a_null_literal.context != null; | |
| 9 | try expect(!is_non_null); | |
| 10 | } | |
| 11 | ||
| 12 | const SillyStruct = struct { | |
| 13 | context: ?i32, | |
| 14 | }; | |
| 15 | ||
| 16 | const here_is_a_null_literal = SillyStruct{ .context = null }; | |
| 17 | ||
| 18 | const StructWithOptional = struct { | |
| 19 | field: ?i32, | |
| 20 | }; | |
| 21 | ||
| 22 | var struct_with_optional: StructWithOptional = undefined; | |
| 23 | ||
| 24 | test "unwrap optional which is field of global var" { | |
| 25 | struct_with_optional.field = null; | |
| 26 | if (struct_with_optional.field) |payload| { | |
| 27 | _ = payload; | |
| 28 | unreachable; | |
| 29 | } | |
| 30 | struct_with_optional.field = 1234; | |
| 31 | if (struct_with_optional.field) |payload| { | |
| 32 | try expect(payload == 1234); | |
| 33 | } else { | |
| 34 | unreachable; | |
| 35 | } | |
| 36 | } |