diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 7ca56946d0..5935985961 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -308,6 +308,11 @@ pub const DeclGen = struct { if (ty.isPtrLikeOptional()) { return dg.renderValue(writer, payload_type, val); } + const target = dg.module.getTarget(); + if (payload_type.abiSize(target) == 0) { + const is_null = val.castTag(.opt_payload) == null; + return writer.print("{}", .{is_null}); + } try writer.writeByte('('); try dg.renderType(writer, ty); try writer.writeAll("){"); @@ -588,10 +593,13 @@ pub const DeclGen = struct { .Optional => { var opt_buf: Type.Payload.ElemType = undefined; const child_type = t.optionalChild(&opt_buf); + const target = dg.module.getTarget(); if (t.isPtrLikeOptional()) { return dg.renderType(w, child_type); } else if (dg.typedefs.get(t)) |some| { return w.writeAll(some.name); + } else if (child_type.abiSize(target) == 0) { + return w.writeAll("bool"); } var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); @@ -2100,14 +2108,21 @@ fn airIsNull( const un_op = f.air.instructions.items(.data)[inst].un_op; const writer = f.object.writer(); const operand = try f.resolveInst(un_op); + const target = f.object.dg.module.getTarget(); const local = try f.allocLocal(Type.initTag(.bool), .Const); try writer.writeAll(" = ("); try f.writeCValue(writer, operand); - if (f.air.typeOf(un_op).isPtrLikeOptional()) { + const ty = f.air.typeOf(un_op); + var opt_buf: Type.Payload.ElemType = undefined; + const payload_type = ty.optionalChild(&opt_buf); + + if (ty.isPtrLikeOptional()) { // operand is a regular pointer, test `operand !=/== NULL` try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); + } else if (payload_type.abiSize(target) == 0) { + try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); } else { try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); } diff --git a/src/type.zig b/src/type.zig index a43c80cb2e..e02ec051cf 100644 --- a/src/type.zig +++ b/src/type.zig @@ -1805,7 +1805,13 @@ pub const Type = extern union { .void, => 0, - .@"struct" => return self.structFieldOffset(self.structFieldCount(), target), + .@"struct" => { + const field_count = self.structFieldCount(); + if (field_count == 0) { + return 0; + } + return self.structFieldOffset(field_count, target); + }, .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { var buffer: Payload.Bits = undefined; const int_tag_ty = self.intTagType(&buffer); diff --git a/test/behavior.zig b/test/behavior.zig index 4b19e81199..2618abe13d 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -6,6 +6,7 @@ test { _ = @import("behavior/bool.zig"); _ = @import("behavior/if.zig"); _ = @import("behavior/truncate.zig"); + _ = @import("behavior/null.zig"); if (builtin.object_format != .c) { // Tests that pass for stage1 and stage2 but not the C backend. @@ -48,7 +49,7 @@ test { _ = @import("behavior/math.zig"); _ = @import("behavior/maximum_minimum.zig"); _ = @import("behavior/member_func.zig"); - _ = @import("behavior/null.zig"); + _ = @import("behavior/null_llvm.zig"); _ = @import("behavior/optional.zig"); _ = @import("behavior/pointers.zig"); _ = @import("behavior/popcount.zig"); diff --git a/test/behavior/null.zig b/test/behavior/null.zig index 98018c79bc..e4c4f26bb8 100644 --- a/test/behavior/null.zig +++ b/test/behavior/null.zig @@ -59,18 +59,6 @@ fn foo(x: ?i32) ?bool { return value > 1234; } -test "null literal outside function" { - const is_null = here_is_a_null_literal.context == null; - try expect(is_null); - - const is_non_null = here_is_a_null_literal.context != null; - try expect(!is_non_null); -} -const SillyStruct = struct { - context: ?i32, -}; -const here_is_a_null_literal = SillyStruct{ .context = null }; - test "test null runtime" { try testTestNullRuntime(null); } @@ -97,23 +85,23 @@ fn bar(x: ?void) ?void { } } -const StructWithOptional = struct { - field: ?i32, -}; +const Empty = struct {}; -var struct_with_optional: StructWithOptional = undefined; +test "optional struct{}" { + _ = try optionalEmptyStructImpl(); + _ = comptime try optionalEmptyStructImpl(); +} -test "unwrap optional which is field of global var" { - struct_with_optional.field = null; - if (struct_with_optional.field) |payload| { - _ = payload; - unreachable; - } - struct_with_optional.field = 1234; - if (struct_with_optional.field) |payload| { - try expect(payload == 1234); +fn optionalEmptyStructImpl() !void { + try expect(baz(null) == null); + try expect(baz(Empty{}) != null); +} + +fn baz(x: ?Empty) ?Empty { + if (x) |_| { + return Empty{}; } else { - unreachable; + return null; } } diff --git a/test/behavior/null_llvm.zig b/test/behavior/null_llvm.zig new file mode 100644 index 0000000000..25d3c0ca1c --- /dev/null +++ b/test/behavior/null_llvm.zig @@ -0,0 +1,36 @@ +const std = @import("std"); +const expect = std.testing.expect; + +test "null literal outside function" { + const is_null = here_is_a_null_literal.context == null; + try expect(is_null); + + const is_non_null = here_is_a_null_literal.context != null; + try expect(!is_non_null); +} + +const SillyStruct = struct { + context: ?i32, +}; + +const here_is_a_null_literal = SillyStruct{ .context = null }; + +const StructWithOptional = struct { + field: ?i32, +}; + +var struct_with_optional: StructWithOptional = undefined; + +test "unwrap optional which is field of global var" { + struct_with_optional.field = null; + if (struct_with_optional.field) |payload| { + _ = payload; + unreachable; + } + struct_with_optional.field = 1234; + if (struct_with_optional.field) |payload| { + try expect(payload == 1234); + } else { + unreachable; + } +}