| author | |
| committer | |
| log | dd437ae39948031dc04836f245c8b77d459a428a |
| tree | ea2cf8ea5dce382549deaf9a2c8801ec1c7403d1 |
| parent | 9db293492bbbc5b8d70638bd9c59dea19d13596c |
5 files changed, 45 insertions(+), 45 deletions(-)
src/codegen/c.zig+9-5| ... | @@ -726,7 +726,11 @@ pub const DeclGen = struct { | ... | @@ -726,7 +726,11 @@ pub const DeclGen = struct { |
| 726 | } | 726 | } |
| 727 | 727 | ||
| 728 | if (ty.optionalReprIsPayload()) { | 728 | if (ty.optionalReprIsPayload()) { |
| 729 | return dg.renderValue(writer, payload_ty, val, location); | 729 | if (val.castTag(.opt_payload)) |payload| { |
| 730 | return dg.renderValue(writer, payload_ty, payload.data, location); | ||
| 731 | } else { | ||
| 732 | return dg.renderValue(writer, payload_ty, val, location); | ||
| 733 | } | ||
| 730 | } | 734 | } |
| 731 | 735 | ||
| 732 | try writer.writeByte('('); | 736 | try writer.writeByte('('); |
| ... | @@ -3263,11 +3267,9 @@ fn airIsNull( | ... | @@ -3263,11 +3267,9 @@ fn airIsNull( |
| 3263 | try f.writeCValue(writer, operand); | 3267 | try f.writeCValue(writer, operand); |
| 3264 | 3268 | ||
| 3265 | const ty = f.air.typeOf(un_op); | 3269 | const ty = f.air.typeOf(un_op); |
| 3270 | const opt_ty = if (deref_suffix[0] != 0) ty.childType() else ty; | ||
| 3266 | var opt_buf: Type.Payload.ElemType = undefined; | 3271 | var opt_buf: Type.Payload.ElemType = undefined; |
| 3267 | const payload_ty = if (deref_suffix[0] != 0) | 3272 | const payload_ty = opt_ty.optionalChild(&opt_buf); |
| 3268 | ty.childType().optionalChild(&opt_buf) | ||
| 3269 | else | ||
| 3270 | ty.optionalChild(&opt_buf); | ||
| 3271 | 3273 | ||
| 3272 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 3274 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 3273 | try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); | 3275 | try writer.print("){s} {s} true;\n", .{ deref_suffix, operator }); |
| ... | @@ -3276,6 +3278,8 @@ fn airIsNull( | ... | @@ -3276,6 +3278,8 @@ fn airIsNull( |
| 3276 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); | 3278 | try writer.print("){s} {s} NULL;\n", .{ deref_suffix, operator }); |
| 3277 | } else if (payload_ty.zigTypeTag() == .ErrorSet) { | 3279 | } else if (payload_ty.zigTypeTag() == .ErrorSet) { |
| 3278 | try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); | 3280 | try writer.print("){s} {s} 0;\n", .{ deref_suffix, operator }); |
| 3281 | } else if (payload_ty.isSlice() and opt_ty.optionalReprIsPayload()) { | ||
| 3282 | try writer.print("){s}.ptr {s} NULL;\n", .{ deref_suffix, operator }); | ||
| 3279 | } else { | 3283 | } else { |
| 3280 | try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); | 3284 | try writer.print("){s}.is_null {s} true;\n", .{ deref_suffix, operator }); |
| 3281 | } | 3285 | } |
src/codegen/llvm.zig+8-2| ... | @@ -6316,18 +6316,24 @@ pub const FuncGen = struct { | ... | @@ -6316,18 +6316,24 @@ pub const FuncGen = struct { |
| 6316 | const operand_ty = self.air.typeOf(un_op); | 6316 | const operand_ty = self.air.typeOf(un_op); |
| 6317 | const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; | 6317 | const optional_ty = if (operand_is_ptr) operand_ty.childType() else operand_ty; |
| 6318 | const optional_llvm_ty = try self.dg.lowerType(optional_ty); | 6318 | const optional_llvm_ty = try self.dg.lowerType(optional_ty); |
| 6319 | var buf: Type.Payload.ElemType = undefined; | ||
| 6320 | const payload_ty = optional_ty.optionalChild(&buf); | ||
| 6319 | if (optional_ty.optionalReprIsPayload()) { | 6321 | if (optional_ty.optionalReprIsPayload()) { |
| 6320 | const loaded = if (operand_is_ptr) | 6322 | const loaded = if (operand_is_ptr) |
| 6321 | self.builder.buildLoad(optional_llvm_ty, operand, "") | 6323 | self.builder.buildLoad(optional_llvm_ty, operand, "") |
| 6322 | else | 6324 | else |
| 6323 | operand; | 6325 | operand; |
| 6326 | if (payload_ty.isSlice()) { | ||
| 6327 | const slice_ptr = self.builder.buildExtractValue(loaded, 0, ""); | ||
| 6328 | var slice_buf: Type.SlicePtrFieldTypeBuffer = undefined; | ||
| 6329 | const ptr_ty = try self.dg.lowerType(payload_ty.slicePtrFieldType(&slice_buf)); | ||
| 6330 | return self.builder.buildICmp(pred, slice_ptr, ptr_ty.constNull(), ""); | ||
| 6331 | } | ||
| 6324 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); | 6332 | return self.builder.buildICmp(pred, loaded, optional_llvm_ty.constNull(), ""); |
| 6325 | } | 6333 | } |
| 6326 | 6334 | ||
| 6327 | comptime assert(optional_layout_version == 3); | 6335 | comptime assert(optional_layout_version == 3); |
| 6328 | 6336 | ||
| 6329 | var buf: Type.Payload.ElemType = undefined; | ||
| 6330 | const payload_ty = optional_ty.optionalChild(&buf); | ||
| 6331 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 6337 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 6332 | const loaded = if (operand_is_ptr) | 6338 | const loaded = if (operand_is_ptr) |
| 6333 | self.builder.buildLoad(optional_llvm_ty, operand, "") | 6339 | self.builder.buildLoad(optional_llvm_ty, operand, "") |
src/type.zig+8-38| ... | @@ -3469,20 +3469,8 @@ pub const Type = extern union { | ... | @@ -3469,20 +3469,8 @@ pub const Type = extern union { |
| 3469 | 3469 | ||
| 3470 | if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; | 3470 | if (!child_type.hasRuntimeBits()) return AbiSizeAdvanced{ .scalar = 1 }; |
| 3471 | 3471 | ||
| 3472 | switch (child_type.zigTypeTag()) { | 3472 | if (ty.optionalReprIsPayload()) { |
| 3473 | .Pointer => { | 3473 | return abiSizeAdvanced(child_type, target, strat); |
| 3474 | const ptr_info = child_type.ptrInfo().data; | ||
| 3475 | const has_null = switch (ptr_info.size) { | ||
| 3476 | .Slice, .C => true, | ||
| 3477 | else => ptr_info.@"allowzero", | ||
| 3478 | }; | ||
| 3479 | if (!has_null) { | ||
| 3480 | const ptr_size_bytes = @divExact(target.cpu.arch.ptrBitWidth(), 8); | ||
| 3481 | return AbiSizeAdvanced{ .scalar = ptr_size_bytes }; | ||
| 3482 | } | ||
| 3483 | }, | ||
| 3484 | .ErrorSet => return abiSizeAdvanced(Type.anyerror, target, strat), | ||
| 3485 | else => {}, | ||
| 3486 | } | 3474 | } |
| 3487 | 3475 | ||
| 3488 | const payload_size = switch (try child_type.abiSizeAdvanced(target, strat)) { | 3476 | const payload_size = switch (try child_type.abiSizeAdvanced(target, strat)) { |
| ... | @@ -3747,28 +3735,10 @@ pub const Type = extern union { | ... | @@ -3747,28 +3735,10 @@ pub const Type = extern union { |
| 3747 | 3735 | ||
| 3748 | .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data, | 3736 | .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data, |
| 3749 | 3737 | ||
| 3750 | .optional => { | 3738 | .optional, .error_union => { |
| 3751 | var buf: Payload.ElemType = undefined; | 3739 | // Optionals and error unions are not packed so their bitsize |
| 3752 | const child_type = ty.optionalChild(&buf); | 3740 | // includes padding bits. |
| 3753 | if (!child_type.hasRuntimeBits()) return 8; | 3741 | return (try abiSizeAdvanced(ty, target, if (sema_kit) |sk| .{ .sema_kit = sk } else .eager)).scalar * 8; |
| 3754 | |||
| 3755 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr() and !child_type.isSlice()) | ||
| 3756 | return target.cpu.arch.ptrBitWidth(); | ||
| 3757 | |||
| 3758 | // Optional types are represented as a struct with the child type as the first | ||
| 3759 | // field and a boolean as the second. Since the child type's abi alignment is | ||
| 3760 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal | ||
| 3761 | // to the child type's ABI alignment. | ||
| 3762 | const child_bit_size = try bitSizeAdvanced(child_type, target, sema_kit); | ||
| 3763 | return child_bit_size + 1; | ||
| 3764 | }, | ||
| 3765 | |||
| 3766 | .error_union => { | ||
| 3767 | const payload = ty.castTag(.error_union).?.data; | ||
| 3768 | if (!payload.payload.hasRuntimeBits()) { | ||
| 3769 | return payload.error_set.bitSizeAdvanced(target, sema_kit); | ||
| 3770 | } | ||
| 3771 | @panic("TODO bitSize error union"); | ||
| 3772 | }, | 3742 | }, |
| 3773 | 3743 | ||
| 3774 | .atomic_order, | 3744 | .atomic_order, |
| ... | @@ -4045,8 +4015,8 @@ pub const Type = extern union { | ... | @@ -4045,8 +4015,8 @@ pub const Type = extern union { |
| 4045 | .Pointer => { | 4015 | .Pointer => { |
| 4046 | const info = child_ty.ptrInfo().data; | 4016 | const info = child_ty.ptrInfo().data; |
| 4047 | switch (info.size) { | 4017 | switch (info.size) { |
| 4048 | .Slice, .C => return false, | 4018 | .C => return false, |
| 4049 | .Many, .One => return !info.@"allowzero", | 4019 | .Slice, .Many, .One => return !info.@"allowzero", |
| 4050 | } | 4020 | } |
| 4051 | }, | 4021 | }, |
| 4052 | .ErrorSet => return true, | 4022 | .ErrorSet => return true, |
test/behavior/cast.zig+4| ... | @@ -1170,6 +1170,7 @@ test "implicitly cast from [N]T to ?[]const T" { | ... | @@ -1170,6 +1170,7 @@ test "implicitly cast from [N]T to ?[]const T" { |
| 1170 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 1170 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1171 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 1171 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1172 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1172 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1173 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1173 | 1174 | ||
| 1174 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | 1175 | try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); |
| 1175 | comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); | 1176 | comptime try expect(mem.eql(u8, castToOptionalSlice().?, "hi")); |
| ... | @@ -1256,6 +1257,7 @@ test "*const [N]null u8 to ?[]const u8" { | ... | @@ -1256,6 +1257,7 @@ test "*const [N]null u8 to ?[]const u8" { |
| 1256 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 1257 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 1257 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | 1258 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO |
| 1258 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1259 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1260 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1259 | 1261 | ||
| 1260 | const S = struct { | 1262 | const S = struct { |
| 1261 | fn doTheTest() !void { | 1263 | fn doTheTest() !void { |
| ... | @@ -1394,6 +1396,8 @@ test "cast i8 fn call peers to i32 result" { | ... | @@ -1394,6 +1396,8 @@ test "cast i8 fn call peers to i32 result" { |
| 1394 | test "cast compatible optional types" { | 1396 | test "cast compatible optional types" { |
| 1395 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO | 1397 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO |
| 1396 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO | 1398 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 1399 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO | ||
| 1400 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO | ||
| 1397 | 1401 | ||
| 1398 | var a: ?[:0]const u8 = null; | 1402 | var a: ?[:0]const u8 = null; |
| 1399 | var b: ?[]const u8 = a; | 1403 | var b: ?[]const u8 = a; |
test/behavior/optional.zig+16| ... | @@ -3,6 +3,7 @@ const std = @import("std"); | ... | @@ -3,6 +3,7 @@ const std = @import("std"); |
| 3 | const testing = std.testing; | 3 | const testing = std.testing; |
| 4 | const expect = testing.expect; | 4 | const expect = testing.expect; |
| 5 | const expectEqual = testing.expectEqual; | 5 | const expectEqual = testing.expectEqual; |
| 6 | const expectEqualStrings = std.testing.expectEqualStrings; | ||
| 6 | 7 | ||
| 7 | test "passing an optional integer as a parameter" { | 8 | test "passing an optional integer as a parameter" { |
| 8 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | 9 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| ... | @@ -428,3 +429,18 @@ test "alignment of wrapping an optional payload" { | ... | @@ -428,3 +429,18 @@ test "alignment of wrapping an optional payload" { |
| 428 | }; | 429 | }; |
| 429 | try expect(S.foo().?.x == 1234); | 430 | try expect(S.foo().?.x == 1234); |
| 430 | } | 431 | } |
| 432 | |||
| 433 | test "Optional slice size is optimized" { | ||
| 434 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 435 | if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; | ||
| 436 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | ||
| 437 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 438 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 439 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | ||
| 440 | |||
| 441 | try expect(@sizeOf(?[]u8) == @sizeOf([]u8)); | ||
| 442 | var a: ?[]const u8 = null; | ||
| 443 | try expect(a == null); | ||
| 444 | a = "hello"; | ||
| 445 | try expectEqualStrings(a.?, "hello"); | ||
| 446 | } |