Commit f32a77b30d

Andrew Kelley <andrew@ziglang.org>
2022-03-10 01:33:01
Sema: implement pointer-to-tuple coercion to slice and struct
1 parent 3b6e8fa
Changed files (5)
src/Sema.zig
@@ -16052,13 +16052,38 @@ fn coerce(
                 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
             }
 
-            // cast from pointer to anonymous struct to pointer to union
-            if (dest_info.pointee_type.zigTypeTag() == .Union and
-                inst_ty.zigTypeTag() == .Pointer and
-                inst_ty.childType().tag() == .anon_struct and
-                !dest_info.mutable)
-            {
-                return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+            switch (dest_info.size) {
+                .C, .Many => {},
+                .One => switch (dest_info.pointee_type.zigTypeTag()) {
+                    .Union => {
+                        // cast from pointer to anonymous struct to pointer to union
+                        if (inst_ty.isSinglePointer() and
+                            inst_ty.childType().isAnonStruct() and
+                            !dest_info.mutable)
+                        {
+                            return sema.coerceAnonStructToUnionPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+                        }
+                    },
+                    .Struct => {
+                        // cast from pointer to anonymous struct to pointer to struct
+                        if (inst_ty.isSinglePointer() and
+                            inst_ty.childType().isAnonStruct() and
+                            !dest_info.mutable)
+                        {
+                            return sema.coerceAnonStructToStructPtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+                        }
+                    },
+                    else => {},
+                },
+                .Slice => {
+                    // pointer to tuple to slice
+                    if (inst_ty.isSinglePointer() and
+                        inst_ty.childType().isTuple() and
+                        !dest_info.mutable and dest_info.size == .Slice)
+                    {
+                        return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src);
+                    }
+                },
             }
 
             // This will give an extra hint on top of what the bottom of this func would provide.
@@ -17365,6 +17390,20 @@ fn coerceAnonStructToUnionPtrs(
     return sema.analyzeRef(block, union_ty_src, union_inst);
 }
 
+fn coerceAnonStructToStructPtrs(
+    sema: *Sema,
+    block: *Block,
+    ptr_struct_ty: Type,
+    struct_ty_src: LazySrcLoc,
+    ptr_anon_struct: Air.Inst.Ref,
+    anon_struct_src: LazySrcLoc,
+) !Air.Inst.Ref {
+    const struct_ty = ptr_struct_ty.childType();
+    const anon_struct = try sema.analyzeLoad(block, anon_struct_src, ptr_anon_struct, anon_struct_src);
+    const struct_inst = try sema.coerceTupleToStruct(block, struct_ty, struct_ty_src, anon_struct, anon_struct_src);
+    return sema.analyzeRef(block, struct_ty_src, struct_inst);
+}
+
 /// If the lengths match, coerces element-wise.
 fn coerceArrayLike(
     sema: *Sema,
@@ -17494,6 +17533,27 @@ fn coerceTupleToArray(
     );
 }
 
+/// If the lengths match, coerces element-wise.
+fn coerceTupleToSlicePtrs(
+    sema: *Sema,
+    block: *Block,
+    slice_ty: Type,
+    slice_ty_src: LazySrcLoc,
+    ptr_tuple: Air.Inst.Ref,
+    tuple_src: LazySrcLoc,
+) !Air.Inst.Ref {
+    const tuple_ty = sema.typeOf(ptr_tuple).childType();
+    const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src);
+    const slice_info = slice_ty.ptrInfo().data;
+    const array_ty = try Type.array(sema.arena, tuple_ty.structFieldCount(), slice_info.sentinel, slice_info.pointee_type);
+    const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src);
+    if (slice_info.@"align" != 0) {
+        return sema.fail(block, slice_ty_src, "TODO: override the alignment of the array decl we create here", .{});
+    }
+    const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst);
+    return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src);
+}
+
 /// Handles both tuples and anon struct literals. Coerces field-wise. Reports
 /// errors for both extra fields and missing fields.
 fn coerceTupleToStruct(
@@ -17504,11 +17564,13 @@ fn coerceTupleToStruct(
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
 ) !Air.Inst.Ref {
-    if (dest_ty.isTupleOrAnonStruct()) {
+    const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty);
+
+    if (struct_ty.isTupleOrAnonStruct()) {
         return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
     }
 
-    const fields = dest_ty.structFields();
+    const fields = struct_ty.structFields();
     const field_vals = try sema.arena.alloc(Value, fields.count());
     const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
     mem.set(Air.Inst.Ref, field_refs, .none);
@@ -17523,7 +17585,7 @@ fn coerceTupleToStruct(
             payload.data.names[i]
         else
             try std.fmt.allocPrint(sema.arena, "{d}", .{i});
-        const field_index = try sema.structFieldIndex(block, dest_ty, field_name, field_src);
+        const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src);
         const field = fields.values()[field_index];
         if (field.is_comptime) {
             return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to structs when one of the destination struct fields is comptime", .{});
@@ -17567,17 +17629,17 @@ fn coerceTupleToStruct(
     }
 
     if (root_msg) |msg| {
-        try sema.addDeclaredHereNote(msg, dest_ty);
+        try sema.addDeclaredHereNote(msg, struct_ty);
         return sema.failWithOwnedErrorMsg(block, msg);
     }
 
     if (runtime_src) |rs| {
         try sema.requireRuntimeBlock(block, rs);
-        return block.addAggregateInit(dest_ty, field_refs);
+        return block.addAggregateInit(struct_ty, field_refs);
     }
 
     return sema.addConstant(
-        dest_ty,
+        struct_ty,
         try Value.Tag.@"struct".create(sema.arena, field_vals),
     );
 }
src/type.zig
@@ -3502,6 +3502,7 @@ pub const Type = extern union {
             .tuple => ty.castTag(.tuple).?.data.types.len,
             .anon_struct => ty.castTag(.anon_struct).?.data.types.len,
             .@"struct" => ty.castTag(.@"struct").?.data.fields.count(),
+            .empty_struct, .empty_struct_literal => 0,
 
             else => unreachable,
         };
@@ -5070,7 +5071,7 @@ pub const Type = extern union {
 
     pub fn isAnonStruct(ty: Type) bool {
         return switch (ty.tag()) {
-            .anon_struct => true,
+            .anon_struct, .empty_struct_literal => true,
             else => false,
         };
     }
test/behavior/align.zig
@@ -6,17 +6,15 @@ const native_arch = builtin.target.cpu.arch;
 var foo: u8 align(4) = 100;
 
 test "global variable alignment" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
-
     comptime try expect(@typeInfo(@TypeOf(&foo)).Pointer.alignment == 4);
     comptime try expect(@TypeOf(&foo) == *align(4) u8);
     {
-        const slice = @as(*[1]u8, &foo)[0..];
+        const slice = @as(*align(4) [1]u8, &foo)[0..];
         comptime try expect(@TypeOf(slice) == *align(4) [1]u8);
     }
     {
         var runtime_zero: usize = 0;
-        const slice = @as(*[1]u8, &foo)[runtime_zero..];
+        const slice = @as(*align(4) [1]u8, &foo)[runtime_zero..];
         comptime try expect(@TypeOf(slice) == []align(4) u8);
     }
 }
@@ -86,8 +84,6 @@ test "size of extern struct with 128-bit field" {
 }
 
 test "@ptrCast preserves alignment of bigger source" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
-
     var x: u32 align(16) = 1234;
     const ptr = @ptrCast(*u8, &x);
     try expect(@TypeOf(ptr) == *align(16) u8);
test/behavior/struct.zig
@@ -1072,7 +1072,11 @@ test "type coercion of anon struct literal to struct" {
 }
 
 test "type coercion of pointer to anon struct literal to pointer to struct" {
-    if (builtin.zig_backend != .stage1) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
+    if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
 
     const S = struct {
         const S2 = struct {
test/behavior/tuple.zig
@@ -159,8 +159,8 @@ test "array-like initializer for tuple types" {
     const S = struct {
         fn doTheTest() !void {
             var obj: T = .{ -1234, 128 };
-            try testing.expectEqual(@as(i32, -1234), obj[0]);
-            try testing.expectEqual(@as(u8, 128), obj[1]);
+            try expect(@as(i32, -1234) == obj[0]);
+            try expect(@as(u8, 128) == obj[1]);
         }
     };