| 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,6 +308,11 @@ pub const DeclGen = struct { |
| 308 | if (ty.isPtrLikeOptional()) { | 308 | if (ty.isPtrLikeOptional()) { |
| 309 | return dg.renderValue(writer, payload_type, val); | 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 | try writer.writeByte('('); | 316 | try writer.writeByte('('); |
| 312 | try dg.renderType(writer, ty); | 317 | try dg.renderType(writer, ty); |
| 313 | try writer.writeAll("){"); | 318 | try writer.writeAll("){"); |
| ... | @@ -588,10 +593,13 @@ pub const DeclGen = struct { | ... | @@ -588,10 +593,13 @@ pub const DeclGen = struct { |
| 588 | .Optional => { | 593 | .Optional => { |
| 589 | var opt_buf: Type.Payload.ElemType = undefined; | 594 | var opt_buf: Type.Payload.ElemType = undefined; |
| 590 | const child_type = t.optionalChild(&opt_buf); | 595 | const child_type = t.optionalChild(&opt_buf); |
| 596 | const target = dg.module.getTarget(); | ||
| 591 | if (t.isPtrLikeOptional()) { | 597 | if (t.isPtrLikeOptional()) { |
| 592 | return dg.renderType(w, child_type); | 598 | return dg.renderType(w, child_type); |
| 593 | } else if (dg.typedefs.get(t)) |some| { | 599 | } else if (dg.typedefs.get(t)) |some| { |
| 594 | return w.writeAll(some.name); | 600 | return w.writeAll(some.name); |
| 601 | } else if (child_type.abiSize(target) == 0) { | ||
| 602 | return w.writeAll("bool"); | ||
| 595 | } | 603 | } |
| 596 | 604 | ||
| 597 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | 605 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); |
| ... | @@ -2100,14 +2108,21 @@ fn airIsNull( | ... | @@ -2100,14 +2108,21 @@ fn airIsNull( |
| 2100 | const un_op = f.air.instructions.items(.data)[inst].un_op; | 2108 | const un_op = f.air.instructions.items(.data)[inst].un_op; |
| 2101 | const writer = f.object.writer(); | 2109 | const writer = f.object.writer(); |
| 2102 | const operand = try f.resolveInst(un_op); | 2110 | const operand = try f.resolveInst(un_op); |
| 2111 | const target = f.object.dg.module.getTarget(); | ||
| 2103 | 2112 | ||
| 2104 | const local = try f.allocLocal(Type.initTag(.bool), .Const); | 2113 | const local = try f.allocLocal(Type.initTag(.bool), .Const); |
| 2105 | try writer.writeAll(" = ("); | 2114 | try writer.writeAll(" = ("); |
| 2106 | try f.writeCValue(writer, operand); | 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 | // operand is a regular pointer, test `operand !=/== NULL` | 2122 | // operand is a regular pointer, test `operand !=/== NULL` |
| 2110 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); | 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 | } else { | 2126 | } else { |
| 2112 | try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); | 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,7 +1805,13 @@ pub const Type = extern union { |
| 1805 | .void, | 1805 | .void, |
| 1806 | => 0, | 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 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { | 1815 | .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { |
| 1810 | var buffer: Payload.Bits = undefined; | 1816 | var buffer: Payload.Bits = undefined; |
| 1811 | const int_tag_ty = self.intTagType(&buffer); | 1817 | const int_tag_ty = self.intTagType(&buffer); |
test/behavior.zig+2-1| ... | @@ -6,6 +6,7 @@ test { | ... | @@ -6,6 +6,7 @@ test { |
| 6 | _ = @import("behavior/bool.zig"); | 6 | _ = @import("behavior/bool.zig"); |
| 7 | _ = @import("behavior/if.zig"); | 7 | _ = @import("behavior/if.zig"); |
| 8 | _ = @import("behavior/truncate.zig"); | 8 | _ = @import("behavior/truncate.zig"); |
| 9 | _ = @import("behavior/null.zig"); | ||
| 9 | 10 | ||
| 10 | if (builtin.object_format != .c) { | 11 | if (builtin.object_format != .c) { |
| 11 | // Tests that pass for stage1 and stage2 but not the C backend. | 12 | // Tests that pass for stage1 and stage2 but not the C backend. |
| ... | @@ -48,7 +49,7 @@ test { | ... | @@ -48,7 +49,7 @@ test { |
| 48 | _ = @import("behavior/math.zig"); | 49 | _ = @import("behavior/math.zig"); |
| 49 | _ = @import("behavior/maximum_minimum.zig"); | 50 | _ = @import("behavior/maximum_minimum.zig"); |
| 50 | _ = @import("behavior/member_func.zig"); | 51 | _ = @import("behavior/member_func.zig"); |
| 51 | _ = @import("behavior/null.zig"); | 52 | _ = @import("behavior/null_llvm.zig"); |
| 52 | _ = @import("behavior/optional.zig"); | 53 | _ = @import("behavior/optional.zig"); |
| 53 | _ = @import("behavior/pointers.zig"); | 54 | _ = @import("behavior/pointers.zig"); |
| 54 | _ = @import("behavior/popcount.zig"); | 55 | _ = @import("behavior/popcount.zig"); |
test/behavior/null.zig+14-26| ... | @@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool { | ... | @@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool { |
| 59 | return value > 1234; | 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 | test "test null runtime" { | 62 | test "test null runtime" { |
| 75 | try testTestNullRuntime(null); | 63 | try testTestNullRuntime(null); |
| 76 | } | 64 | } |
| ... | @@ -97,23 +85,23 @@ fn bar(x: ?void) ?void { | ... | @@ -97,23 +85,23 @@ fn bar(x: ?void) ?void { |
| 97 | } | 85 | } |
| 98 | } | 86 | } |
| 99 | 87 | ||
| 100 | const StructWithOptional = struct { | 88 | const Empty = struct {}; |
| 101 | field: ?i32, | ||
| 102 | }; | ||
| 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" { | 95 | fn optionalEmptyStructImpl() !void { |
| 107 | struct_with_optional.field = null; | 96 | try expect(baz(null) == null); |
| 108 | if (struct_with_optional.field) |payload| { | 97 | try expect(baz(Empty{}) != null); |
| 109 | _ = payload; | 98 | } |
| 110 | unreachable; | 99 | |
| 111 | } | 100 | fn baz(x: ?Empty) ?Empty { |
| 112 | struct_with_optional.field = 1234; | 101 | if (x) |_| { |
| 113 | if (struct_with_optional.field) |payload| { | 102 | return Empty{}; |
| 114 | try expect(payload == 1234); | ||
| 115 | } else { | 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 | } | ||