Commit 85d4c8620f

Andrew Kelley <andrew@ziglang.org>
2021-12-28 06:04:21
Sema: implement array coercion
1 parent 042b770
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]);
                     }
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()) {
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();
+}
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 {
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;
+}
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,