Commit 9d6bef49a5

Jimmi Holst Christensen <jhc@dismail.de>
2022-01-07 18:58:40
Add two more resolution status' to Struct and Union
resolveTypeForCodegen is called when we needed to resolve a type fully, even through pointer. This commit fully implements this, even through pointer fields on structs and unions. The function has now also been renamed to resolveTypeFully
1 parent d5093b6
Changed files (3)
src/Module.zig
@@ -831,6 +831,10 @@ pub const Struct = struct {
         have_field_types,
         layout_wip,
         have_layout,
+        fully_resolved_wip,
+        // The types and all its fields have had their layout resolved. Even through pointer,
+        // which `have_layout` does not ensure.
+        fully_resolved,
     },
     /// If true, definitely nonzero size at runtime. If false, resolving the fields
     /// is necessary to determine whether it has bits at runtime.
@@ -889,6 +893,22 @@ pub const Struct = struct {
             .have_field_types,
             .layout_wip,
             .have_layout,
+            .fully_resolved_wip,
+            .fully_resolved,
+            => true,
+        };
+    }
+
+    pub fn haveLayout(s: Struct) bool {
+        return switch (s.status) {
+            .none,
+            .field_types_wip,
+            .have_field_types,
+            .layout_wip,
+            => false,
+            .have_layout,
+            .fully_resolved_wip,
+            .fully_resolved,
             => true,
         };
     }
@@ -1003,6 +1023,10 @@ pub const Union = struct {
         have_field_types,
         layout_wip,
         have_layout,
+        fully_resolved_wip,
+        // The types and all its fields have had their layout resolved. Even through pointer,
+        // which `have_layout` does not ensure.
+        fully_resolved,
     },
 
     pub const Field = struct {
@@ -1033,6 +1057,8 @@ pub const Union = struct {
             .have_field_types,
             .layout_wip,
             .have_layout,
+            .fully_resolved_wip,
+            .fully_resolved,
             => true,
         };
     }
@@ -1102,8 +1128,22 @@ pub const Union = struct {
         tag_size: u64,
     };
 
+    pub fn haveLayout(u: Union) bool {
+        return switch (u.status) {
+            .none,
+            .field_types_wip,
+            .have_field_types,
+            .layout_wip,
+            => false,
+            .have_layout,
+            .fully_resolved_wip,
+            .fully_resolved,
+            => true,
+        };
+    }
+
     pub fn getLayout(u: Union, target: Target, have_tag: bool) Layout {
-        assert(u.status == .have_layout);
+        assert(u.haveLayout());
         var most_aligned_field: u32 = undefined;
         var most_aligned_field_size: u64 = undefined;
         var biggest_field: u32 = undefined;
src/Sema.zig
@@ -4503,14 +4503,14 @@ fn analyzeCall(
             const arg_src = call_src; // TODO: better source location
             if (i < fn_params_len) {
                 const param_ty = func_ty.fnParamType(i);
-                try sema.resolveTypeForCodegen(block, arg_src, param_ty);
+                try sema.resolveTypeFully(block, arg_src, param_ty);
                 args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
             } else {
                 args[i] = uncasted_arg;
             }
         }
 
-        try sema.resolveTypeForCodegen(block, call_src, func_ty_info.return_type);
+        try sema.resolveTypeFully(block, call_src, func_ty_info.return_type);
 
         try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len +
             args.len);
@@ -4580,7 +4580,7 @@ fn finishGenericCall(
                 const param_ty = new_fn_ty.fnParamType(runtime_i);
                 const arg_src = call_src; // TODO: better source location
                 const uncasted_arg = uncasted_args[total_i];
-                try sema.resolveTypeForCodegen(block, arg_src, param_ty);
+                try sema.resolveTypeFully(block, arg_src, param_ty);
                 const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src);
                 runtime_args[runtime_i] = casted_arg;
                 runtime_i += 1;
@@ -4588,7 +4588,7 @@ fn finishGenericCall(
             total_i += 1;
         }
 
-        try sema.resolveTypeForCodegen(block, call_src, new_fn_ty.fnReturnType());
+        try sema.resolveTypeFully(block, call_src, new_fn_ty.fnReturnType());
     }
     try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Call).Struct.fields.len +
         runtime_args_len);
@@ -15228,7 +15228,7 @@ fn resolveStructLayout(
         .field_types_wip, .layout_wip => {
             return sema.fail(block, src, "struct {} depends on itself", .{ty});
         },
-        .have_layout => return,
+        .have_layout, .fully_resolved_wip, .fully_resolved => return,
     }
     struct_obj.status = .layout_wip;
     for (struct_obj.fields.values()) |field| {
@@ -15250,7 +15250,7 @@ fn resolveUnionLayout(
         .field_types_wip, .layout_wip => {
             return sema.fail(block, src, "union {} depends on itself", .{ty});
         },
-        .have_layout => return,
+        .have_layout, .fully_resolved_wip, .fully_resolved => return,
     }
     union_obj.status = .layout_wip;
     for (union_obj.fields.values()) |field| {
@@ -15259,7 +15259,7 @@ fn resolveUnionLayout(
     union_obj.status = .have_layout;
 }
 
-fn resolveTypeForCodegen(
+fn resolveTypeFully(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
@@ -15268,20 +15268,67 @@ fn resolveTypeForCodegen(
     switch (ty.zigTypeTag()) {
         .Pointer => {
             const child_ty = try sema.resolveTypeFields(block, src, ty.childType());
-            return resolveTypeForCodegen(sema, block, src, child_ty);
+            return resolveTypeFully(sema, block, src, child_ty);
         },
-        .Struct => return resolveStructLayout(sema, block, src, ty),
-        .Union => return resolveUnionLayout(sema, block, src, ty),
-        .Array => return resolveTypeForCodegen(sema, block, src, ty.childType()),
+        .Struct => return resolveStructFully(sema, block, src, ty),
+        .Union => return resolveUnionFully(sema, block, src, ty),
+        .Array => return resolveTypeFully(sema, block, src, ty.childType()),
         .Optional => {
             var buf: Type.Payload.ElemType = undefined;
-            return resolveTypeForCodegen(sema, block, src, ty.optionalChild(&buf));
+            return resolveTypeFully(sema, block, src, ty.optionalChild(&buf));
         },
-        .ErrorUnion => return resolveTypeForCodegen(sema, block, src, ty.errorUnionPayload()),
+        .ErrorUnion => return resolveTypeFully(sema, block, src, ty.errorUnionPayload()),
         else => {},
     }
 }
 
+fn resolveStructFully(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    ty: Type,
+) CompileError!void {
+    try resolveStructLayout(sema, block, src, ty);
+
+    const resolved_ty = try sema.resolveTypeFields(block, src, ty);
+    const struct_obj = resolved_ty.castTag(.@"struct").?.data;
+    switch (struct_obj.status) {
+        .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
+        .fully_resolved_wip, .fully_resolved => return,
+    }
+
+    // After we have resolve struct layout we have to go over the fields again to
+    // make sure pointer fields get their child types resolved as well
+    struct_obj.status = .fully_resolved_wip;
+    for (struct_obj.fields.values()) |field| {
+        try sema.resolveTypeFully(block, src, field.ty);
+    }
+    struct_obj.status = .fully_resolved;
+}
+
+fn resolveUnionFully(
+    sema: *Sema,
+    block: *Block,
+    src: LazySrcLoc,
+    ty: Type,
+) CompileError!void {
+    try resolveUnionLayout(sema, block, src, ty);
+
+    const resolved_ty = try sema.resolveTypeFields(block, src, ty);
+    const union_obj = resolved_ty.cast(Type.Payload.Union).?.data;
+    switch (union_obj.status) {
+        .none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
+        .fully_resolved_wip, .fully_resolved => return,
+    }
+
+    // Same goes for unions (see comment about structs)
+    union_obj.status = .fully_resolved_wip;
+    for (union_obj.fields.values()) |field| {
+        try sema.resolveTypeFully(block, src, field.ty);
+    }
+    union_obj.status = .fully_resolved;
+}
+
 fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!Type {
     switch (ty.tag()) {
         .@"struct" => {
@@ -15291,7 +15338,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
                 .field_types_wip => {
                     return sema.fail(block, src, "struct {} depends on itself", .{ty});
                 },
-                .have_field_types, .have_layout, .layout_wip => return ty,
+                .have_field_types,
+                .have_layout,
+                .layout_wip,
+                .fully_resolved_wip,
+                .fully_resolved,
+                => return ty,
             }
 
             struct_obj.status = .field_types_wip;
@@ -15324,7 +15376,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
                 .field_types_wip => {
                     return sema.fail(block, src, "union {} depends on itself", .{ty});
                 },
-                .have_field_types, .have_layout, .layout_wip => return ty,
+                .have_field_types,
+                .have_layout,
+                .layout_wip,
+                .fully_resolved_wip,
+                .fully_resolved,
+                => return ty,
             }
 
             union_obj.status = .field_types_wip;
src/type.zig
@@ -1916,7 +1916,7 @@ pub const Type = extern union {
                 const fields = self.structFields();
                 const is_packed = if (self.castTag(.@"struct")) |payload| p: {
                     const struct_obj = payload.data;
-                    assert(struct_obj.status == .have_layout);
+                    assert(struct_obj.haveLayout());
                     break :p struct_obj.layout == .Packed;
                 } else false;
 
@@ -2220,7 +2220,7 @@ pub const Type = extern union {
                 if (field_count == 0) return 0;
 
                 const struct_obj = ty.castTag(.@"struct").?.data;
-                assert(struct_obj.status == .have_layout);
+                assert(struct_obj.haveLayout());
 
                 var total: u64 = 0;
                 for (struct_obj.fields.values()) |field| {
@@ -3771,7 +3771,7 @@ pub const Type = extern union {
         switch (ty.tag()) {
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
-                assert(struct_obj.status == .have_layout);
+                assert(struct_obj.haveLayout());
                 const is_packed = struct_obj.layout == .Packed;
                 if (!is_packed) {
                     var offset: u64 = 0;