From 85d4c8620f602726b159efe1fe2ea0e07e3c5b59 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 27 Dec 2021 22:04:21 -0700 Subject: [PATCH] Sema: implement array coercion --- src/Sema.zig | 27 ++++++++++++++++-- src/type.zig | 40 ++++++++++++++++---------- test/behavior/array.zig | 50 +++++++++++++++++++++++++++++++++ test/behavior/array_stage1.zig | 50 --------------------------------- test/behavior/struct_llvm.zig | 39 +++++++++++++++++++++++++ test/behavior/struct_stage1.zig | 39 ------------------------- 6 files changed, 138 insertions(+), 107 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index 544681f5f9..8cf9145281 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -13014,7 +13014,25 @@ fn coerceInMemoryAllowed( return try sema.coerceInMemoryAllowedErrorSets(dest_ty, src_ty); } - // TODO: arrays + // Arrays + if (dest_tag == .Array and src_tag == .Array) arrays: { + const dest_info = dest_ty.arrayInfo(); + const src_info = src_ty.arrayInfo(); + if (dest_info.len != src_info.len) break :arrays; + + const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src); + if (child == .no_match) { + return child; + } + const ok_sent = dest_info.sentinel == null or + (src_info.sentinel != null and + dest_info.sentinel.?.eql(src_info.sentinel.?, dest_info.elem_type)); + if (!ok_sent) { + return .no_match; + } + return .ok; + } + // TODO: non-pointer-like optionals // TODO: vectors @@ -13399,8 +13417,11 @@ fn beginComptimePtrMutation( defer parent.finishArena(); const bytes = parent.val.castTag(.bytes).?.data; - assert(bytes.len == parent.ty.arrayLenIncludingSentinel()); - const elems = try arena.alloc(Value, bytes.len); + const dest_len = parent.ty.arrayLenIncludingSentinel(); + // bytes.len may be one greater than dest_len because of the case when + // assigning `[N:S]T` to `[N]T`. This is allowed; the sentinel is omitted. + assert(bytes.len >= dest_len); + const elems = try arena.alloc(Value, dest_len); for (elems) |*elem, i| { elem.* = try Value.Tag.int_u64.create(arena, bytes[i]); } diff --git a/src/type.zig b/src/type.zig index fb543e1f2c..feea1fe2c1 100644 --- a/src/type.zig +++ b/src/type.zig @@ -2189,8 +2189,8 @@ pub const Type = extern union { } /// Asserts the type has the bit size already resolved. - pub fn bitSize(self: Type, target: Target) u64 { - return switch (self.tag()) { + pub fn bitSize(ty: Type, target: Target) u64 { + return switch (ty.tag()) { .fn_noreturn_no_args => unreachable, // represents machine code; not a pointer .fn_void_no_args => unreachable, // represents machine code; not a pointer .fn_naked_noreturn_no_args => unreachable, // represents machine code; not a pointer @@ -2216,11 +2216,21 @@ pub const Type = extern union { .bound_fn => unreachable, .@"struct" => { - @panic("TODO bitSize struct"); + const field_count = ty.structFieldCount(); + if (field_count == 0) return 0; + + const struct_obj = ty.castTag(.@"struct").?.data; + assert(struct_obj.status == .have_layout); + + var total: u64 = 0; + for (struct_obj.fields.values()) |field| { + total += field.ty.bitSize(target); + } + return total; }, .enum_simple, .enum_full, .enum_nonexhaustive, .enum_numbered => { var buffer: Payload.Bits = undefined; - const int_tag_ty = self.intTagType(&buffer); + const int_tag_ty = ty.intTagType(&buffer); return int_tag_ty.bitSize(target); }, .@"union", .union_tagged => { @@ -2232,21 +2242,21 @@ pub const Type = extern union { .bool, .u1 => 1, .vector => { - const payload = self.castTag(.vector).?.data; + const payload = ty.castTag(.vector).?.data; const elem_bit_size = payload.elem_type.bitSize(target); return elem_bit_size * payload.len; }, - .array_u8 => 8 * self.castTag(.array_u8).?.data, - .array_u8_sentinel_0 => 8 * (self.castTag(.array_u8_sentinel_0).?.data + 1), + .array_u8 => 8 * ty.castTag(.array_u8).?.data, + .array_u8_sentinel_0 => 8 * (ty.castTag(.array_u8_sentinel_0).?.data + 1), .array => { - const payload = self.castTag(.array).?.data; + const payload = ty.castTag(.array).?.data; const elem_size = std.math.max(payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target)); if (elem_size == 0 or payload.len == 0) return 0; return (payload.len - 1) * 8 * elem_size + payload.elem_type.bitSize(target); }, .array_sentinel => { - const payload = self.castTag(.array_sentinel).?.data; + const payload = ty.castTag(.array_sentinel).?.data; const elem_size = std.math.max( payload.elem_type.abiAlignment(target), payload.elem_type.abiSize(target), @@ -2267,7 +2277,7 @@ pub const Type = extern union { .const_slice, .mut_slice, => { - if (self.elemType().hasCodeGenBits()) { + if (ty.elemType().hasCodeGenBits()) { return target.cpu.arch.ptrBitWidth() * 2; } else { return target.cpu.arch.ptrBitWidth(); @@ -2280,7 +2290,7 @@ pub const Type = extern union { .optional_single_const_pointer, .optional_single_mut_pointer, => { - if (self.elemType().hasCodeGenBits()) { + if (ty.elemType().hasCodeGenBits()) { return target.cpu.arch.ptrBitWidth(); } else { return 1; @@ -2295,7 +2305,7 @@ pub const Type = extern union { .c_mut_pointer, .pointer, => { - if (self.elemType().hasCodeGenBits()) { + if (ty.elemType().hasCodeGenBits()) { return target.cpu.arch.ptrBitWidth(); } else { return 0; @@ -2325,11 +2335,11 @@ pub const Type = extern union { .error_set_merged, => return 16, // TODO revisit this when we have the concept of the error tag type - .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data, + .int_signed, .int_unsigned => ty.cast(Payload.Bits).?.data, .optional => { var buf: Payload.ElemType = undefined; - const child_type = self.optionalChild(&buf); + const child_type = ty.optionalChild(&buf); if (!child_type.hasCodeGenBits()) return 8; if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) @@ -2343,7 +2353,7 @@ pub const Type = extern union { }, .error_union => { - const payload = self.castTag(.error_union).?.data; + const payload = ty.castTag(.error_union).?.data; if (!payload.error_set.hasCodeGenBits() and !payload.payload.hasCodeGenBits()) { return 0; } else if (!payload.error_set.hasCodeGenBits()) { diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 3ff339b140..c9c376be87 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -164,3 +164,53 @@ test "read/write through global variable array of struct fields initialized via }; try S.doTheTest(); } + +test "single-item pointer to array indexing and slicing" { + try testSingleItemPtrArrayIndexSlice(); + comptime try testSingleItemPtrArrayIndexSlice(); +} + +fn testSingleItemPtrArrayIndexSlice() !void { + { + var array: [4]u8 = "aaaa".*; + doSomeMangling(&array); + try expect(mem.eql(u8, "azya", &array)); + } + { + var array = "aaaa".*; + doSomeMangling(&array); + try expect(mem.eql(u8, "azya", &array)); + } +} + +fn doSomeMangling(array: *[4]u8) void { + array[1] = 'z'; + array[2..3][0] = 'y'; +} + +test "implicit cast zero sized array ptr to slice" { + { + var b = "".*; + const c: []const u8 = &b; + try expect(c.len == 0); + } + { + var b: [0]u8 = "".*; + const c: []const u8 = &b; + try expect(c.len == 0); + } +} + +test "anonymous list literal syntax" { + const S = struct { + fn doTheTest() !void { + var array: [4]u8 = .{ 1, 2, 3, 4 }; + try expect(array[0] == 1); + try expect(array[1] == 2); + try expect(array[2] == 3); + try expect(array[3] == 4); + } + }; + try S.doTheTest(); + comptime try S.doTheTest(); +} diff --git a/test/behavior/array_stage1.zig b/test/behavior/array_stage1.zig index b374c9dd06..771f08acb3 100644 --- a/test/behavior/array_stage1.zig +++ b/test/behavior/array_stage1.zig @@ -4,29 +4,6 @@ const mem = std.mem; const expect = testing.expect; const expectEqual = testing.expectEqual; -test "single-item pointer to array indexing and slicing" { - try testSingleItemPtrArrayIndexSlice(); - comptime try testSingleItemPtrArrayIndexSlice(); -} - -fn testSingleItemPtrArrayIndexSlice() !void { - { - var array: [4]u8 = "aaaa".*; - doSomeMangling(&array); - try expect(mem.eql(u8, "azya", &array)); - } - { - var array = "aaaa".*; - doSomeMangling(&array); - try expect(mem.eql(u8, "azya", &array)); - } -} - -fn doSomeMangling(array: *[4]u8) void { - array[1] = 'z'; - array[2..3][0] = 'y'; -} - test "implicit cast single-item pointer" { try testImplicitCastSingleItemPtr(); comptime try testImplicitCastSingleItemPtr(); @@ -136,33 +113,6 @@ test "double nested array to const slice cast in array literal" { comptime try S.entry(2); } -test "implicit cast zero sized array ptr to slice" { - { - var b = "".*; - const c: []const u8 = &b; - try expect(c.len == 0); - } - { - var b: [0]u8 = "".*; - const c: []const u8 = &b; - try expect(c.len == 0); - } -} - -test "anonymous list literal syntax" { - const S = struct { - fn doTheTest() !void { - var array: [4]u8 = .{ 1, 2, 3, 4 }; - try expect(array[0] == 1); - try expect(array[1] == 2); - try expect(array[2] == 3); - try expect(array[3] == 4); - } - }; - try S.doTheTest(); - comptime try S.doTheTest(); -} - test "anonymous literal in array" { const S = struct { const Foo = struct { diff --git a/test/behavior/struct_llvm.zig b/test/behavior/struct_llvm.zig index 9b19abf030..36310d797b 100644 --- a/test/behavior/struct_llvm.zig +++ b/test/behavior/struct_llvm.zig @@ -246,3 +246,42 @@ test "packed struct with non-ABI-aligned field" { try expect(s.x == 1); try expect(s.y == 42); } + +const BitField1 = packed struct { + a: u3, + b: u3, + c: u2, +}; + +const bit_field_1 = BitField1{ + .a = 1, + .b = 2, + .c = 3, +}; + +test "bit field access" { + var data = bit_field_1; + try expect(getA(&data) == 1); + try expect(getB(&data) == 2); + try expect(getC(&data) == 3); + comptime try expect(@sizeOf(BitField1) == 1); + + data.b += 1; + try expect(data.b == 3); + + data.a += 1; + try expect(data.a == 2); + try expect(data.b == 3); +} + +fn getA(data: *const BitField1) u3 { + return data.a; +} + +fn getB(data: *const BitField1) u3 { + return data.b; +} + +fn getC(data: *const BitField1) u2 { + return data.c; +} diff --git a/test/behavior/struct_stage1.zig b/test/behavior/struct_stage1.zig index ea076d5d24..66549d1767 100644 --- a/test/behavior/struct_stage1.zig +++ b/test/behavior/struct_stage1.zig @@ -6,45 +6,6 @@ const expectEqual = std.testing.expectEqual; const expectEqualSlices = std.testing.expectEqualSlices; const maxInt = std.math.maxInt; -const BitField1 = packed struct { - a: u3, - b: u3, - c: u2, -}; - -const bit_field_1 = BitField1{ - .a = 1, - .b = 2, - .c = 3, -}; - -test "bit field access" { - var data = bit_field_1; - try expect(getA(&data) == 1); - try expect(getB(&data) == 2); - try expect(getC(&data) == 3); - comptime try expect(@sizeOf(BitField1) == 1); - - data.b += 1; - try expect(data.b == 3); - - data.a += 1; - try expect(data.a == 2); - try expect(data.b == 3); -} - -fn getA(data: *const BitField1) u3 { - return data.a; -} - -fn getB(data: *const BitField1) u3 { - return data.b; -} - -fn getC(data: *const BitField1) u2 { - return data.c; -} - const Foo32Bits = packed struct { field: u24, pad: u8,