Commit e5a3eb9777

Veikka Tuominen <git@vexu.eu>
2022-11-16 14:46:43
Type: make `hasRuntimeBitsAdvanced` take `AbiAlignmentAdvancedStrat`
I wasn't able to create a reduced test case for this but the reasoning can be seen in `abiAlignmentAdvancedUnion` where if `strat` was lazy `hasRuntimeBitsAdvanced` would be given `null` instead of `sema` which would cause eager evaluation when it is not valid or desired.
1 parent 44f8714
src/print_zir.zig
@@ -262,9 +262,10 @@ const Writer = struct {
             => try self.writeBreak(stream, inst),
             .array_init,
             .array_init_ref,
+            => try self.writeArrayInit(stream, inst),
             .array_init_anon,
             .array_init_anon_ref,
-            => try self.writeArrayInit(stream, inst),
+            => try self.writeArrayInitAnon(stream, inst),
 
             .slice_start => try self.writeSliceStart(stream, inst),
             .slice_end => try self.writeSliceEnd(stream, inst),
@@ -2316,6 +2317,21 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
+    fn writeArrayInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
+        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+
+        const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
+        const args = self.code.refSlice(extra.end, extra.data.operands_len);
+
+        try stream.writeAll("{");
+        for (args) |arg, i| {
+            if (i != 0) try stream.writeAll(", ");
+            try self.writeInstRef(stream, arg);
+        }
+        try stream.writeAll("}) ");
+        try self.writeSrc(stream, inst_data.src());
+    }
+
     fn writeArrayInitSent(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].pl_node;
 
src/Sema.zig
@@ -128,7 +128,7 @@ pub const Block = struct {
     /// Shared among all child blocks.
     sema: *Sema,
     /// The namespace to use for lookups from this source block
-    /// When analyzing fields, this is different from src_decl.src_namepsace.
+    /// When analyzing fields, this is different from src_decl.src_namespace.
     namespace: *Namespace,
     /// The AIR instructions generated for this block.
     instructions: std.ArrayListUnmanaged(Air.Inst.Index),
@@ -31298,7 +31298,10 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
 }
 
 pub fn typeHasRuntimeBits(sema: *Sema, ty: Type) CompileError!bool {
-    return ty.hasRuntimeBitsAdvanced(false, sema);
+    return ty.hasRuntimeBitsAdvanced(false, .{ .sema = sema }) catch |err| switch (err) {
+        error.NeedLazy => unreachable,
+        else => |e| return e,
+    };
 }
 
 fn typeAbiSize(sema: *Sema, ty: Type) !u64 {
src/type.zig
@@ -2312,6 +2312,8 @@ pub const Type = extern union {
         }
     }
 
+    const RuntimeBitsError = Module.CompileError || error{NeedLazy};
+
     /// true if and only if the type takes up space in memory at runtime.
     /// There are two reasons a type will return false:
     /// * the type is a comptime-only type. For example, the type `type` itself.
@@ -2326,8 +2328,8 @@ pub const Type = extern union {
     pub fn hasRuntimeBitsAdvanced(
         ty: Type,
         ignore_comptime_only: bool,
-        opt_sema: ?*Sema,
-    ) Module.CompileError!bool {
+        strat: AbiAlignmentAdvancedStrat,
+    ) RuntimeBitsError!bool {
         switch (ty.tag()) {
             .u1,
             .u8,
@@ -2406,8 +2408,8 @@ pub const Type = extern union {
                     return true;
                 } else if (ty.childType().zigTypeTag() == .Fn) {
                     return !ty.childType().fnInfo().is_generic;
-                } else if (opt_sema) |sema| {
-                    return !(try sema.typeRequiresComptime(ty));
+                } else if (strat == .sema) {
+                    return !(try strat.sema.typeRequiresComptime(ty));
                 } else {
                     return !comptimeOnly(ty);
                 }
@@ -2445,8 +2447,8 @@ pub const Type = extern union {
                 }
                 if (ignore_comptime_only) {
                     return true;
-                } else if (opt_sema) |sema| {
-                    return !(try sema.typeRequiresComptime(child_ty));
+                } else if (strat == .sema) {
+                    return !(try strat.sema.typeRequiresComptime(child_ty));
                 } else {
                     return !comptimeOnly(child_ty);
                 }
@@ -2459,13 +2461,14 @@ pub const Type = extern union {
                     // and then later if our guess was incorrect, we emit a compile error.
                     return true;
                 }
-                if (opt_sema) |sema| {
-                    _ = try sema.resolveTypeFields(ty);
+                switch (strat) {
+                    .sema => |sema| _ = try sema.resolveTypeFields(ty),
+                    .eager => assert(struct_obj.haveFieldTypes()),
+                    .lazy => if (!struct_obj.haveFieldTypes()) return error.NeedLazy,
                 }
-                assert(struct_obj.haveFieldTypes());
                 for (struct_obj.fields.values()) |field| {
                     if (field.is_comptime) continue;
-                    if (try field.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema))
+                    if (try field.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat))
                         return true;
                 } else {
                     return false;
@@ -2474,7 +2477,7 @@ pub const Type = extern union {
 
             .enum_full => {
                 const enum_full = ty.castTag(.enum_full).?.data;
-                return enum_full.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema);
+                return enum_full.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat);
             },
             .enum_simple => {
                 const enum_simple = ty.castTag(.enum_simple).?.data;
@@ -2483,17 +2486,18 @@ pub const Type = extern union {
             .enum_numbered, .enum_nonexhaustive => {
                 var buffer: Payload.Bits = undefined;
                 const int_tag_ty = ty.intTagType(&buffer);
-                return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema);
+                return int_tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat);
             },
 
             .@"union" => {
                 const union_obj = ty.castTag(.@"union").?.data;
-                if (opt_sema) |sema| {
-                    _ = try sema.resolveTypeFields(ty);
+                switch (strat) {
+                    .sema => |sema| _ = try sema.resolveTypeFields(ty),
+                    .eager => assert(union_obj.haveFieldTypes()),
+                    .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy,
                 }
-                assert(union_obj.haveFieldTypes());
                 for (union_obj.fields.values()) |value| {
-                    if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema))
+                    if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat))
                         return true;
                 } else {
                     return false;
@@ -2501,16 +2505,17 @@ pub const Type = extern union {
             },
             .union_safety_tagged, .union_tagged => {
                 const union_obj = ty.cast(Payload.Union).?.data;
-                if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) {
+                if (try union_obj.tag_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) {
                     return true;
                 }
 
-                if (opt_sema) |sema| {
-                    _ = try sema.resolveTypeFields(ty);
+                switch (strat) {
+                    .sema => |sema| _ = try sema.resolveTypeFields(ty),
+                    .eager => assert(union_obj.haveFieldTypes()),
+                    .lazy => if (!union_obj.haveFieldTypes()) return error.NeedLazy,
                 }
-                assert(union_obj.haveFieldTypes());
                 for (union_obj.fields.values()) |value| {
-                    if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema))
+                    if (try value.ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat))
                         return true;
                 } else {
                     return false;
@@ -2518,9 +2523,9 @@ pub const Type = extern union {
             },
 
             .array, .vector => return ty.arrayLen() != 0 and
-                try ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema),
+                try ty.elemType().hasRuntimeBitsAdvanced(ignore_comptime_only, strat),
             .array_u8 => return ty.arrayLen() != 0,
-            .array_sentinel => return ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema),
+            .array_sentinel => return ty.childType().hasRuntimeBitsAdvanced(ignore_comptime_only, strat),
 
             .int_signed, .int_unsigned => return ty.cast(Payload.Bits).?.data != 0,
 
@@ -2529,7 +2534,7 @@ pub const Type = extern union {
                 for (tuple.types) |field_ty, i| {
                     const val = tuple.values[i];
                     if (val.tag() != .unreachable_value) continue; // comptime field
-                    if (try field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, opt_sema)) return true;
+                    if (try field_ty.hasRuntimeBitsAdvanced(ignore_comptime_only, strat)) return true;
                 }
                 return false;
             },
@@ -2665,11 +2670,11 @@ pub const Type = extern union {
     }
 
     pub fn hasRuntimeBits(ty: Type) bool {
-        return hasRuntimeBitsAdvanced(ty, false, null) catch unreachable;
+        return hasRuntimeBitsAdvanced(ty, false, .eager) catch unreachable;
     }
 
     pub fn hasRuntimeBitsIgnoreComptime(ty: Type) bool {
-        return hasRuntimeBitsAdvanced(ty, true, null) catch unreachable;
+        return hasRuntimeBitsAdvanced(ty, true, .eager) catch unreachable;
     }
 
     pub fn isFnOrHasRuntimeBits(ty: Type) bool {
@@ -2812,12 +2817,12 @@ pub const Type = extern union {
         }
     }
 
-    const AbiAlignmentAdvanced = union(enum) {
+    pub const AbiAlignmentAdvanced = union(enum) {
         scalar: u32,
         val: Value,
     };
 
-    const AbiAlignmentAdvancedStrat = union(enum) {
+    pub const AbiAlignmentAdvancedStrat = union(enum) {
         eager,
         lazy: Allocator,
         sema: *Sema,
@@ -2971,7 +2976,10 @@ pub const Type = extern union {
 
                 switch (strat) {
                     .eager, .sema => {
-                        if (!(try child_type.hasRuntimeBitsAdvanced(false, opt_sema))) {
+                        if (!(child_type.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
+                            error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) },
+                            else => |e| return e,
+                        })) {
                             return AbiAlignmentAdvanced{ .scalar = 1 };
                         }
                         return child_type.abiAlignmentAdvanced(target, strat);
@@ -2990,7 +2998,10 @@ pub const Type = extern union {
                 const code_align = abiAlignment(Type.anyerror, target);
                 switch (strat) {
                     .eager, .sema => {
-                        if (!(try data.payload.hasRuntimeBitsAdvanced(false, opt_sema))) {
+                        if (!(data.payload.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
+                            error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) },
+                            else => |e| return e,
+                        })) {
                             return AbiAlignmentAdvanced{ .scalar = code_align };
                         }
                         return AbiAlignmentAdvanced{ .scalar = @max(
@@ -3044,7 +3055,10 @@ pub const Type = extern union {
                 const fields = ty.structFields();
                 var big_align: u32 = 0;
                 for (fields.values()) |field| {
-                    if (!(try field.ty.hasRuntimeBitsAdvanced(false, opt_sema))) continue;
+                    if (!(field.ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
+                        error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) },
+                        else => |e| return e,
+                    })) continue;
 
                     const field_align = if (field.abi_align != 0)
                         field.abi_align
@@ -3161,7 +3175,10 @@ pub const Type = extern union {
         var max_align: u32 = 0;
         if (have_tag) max_align = union_obj.tag_ty.abiAlignment(target);
         for (union_obj.fields.values()) |field| {
-            if (!(try field.ty.hasRuntimeBitsAdvanced(false, opt_sema))) continue;
+            if (!(field.ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
+                error.NeedLazy => return AbiAlignmentAdvanced{ .val = try Value.Tag.lazy_align.create(strat.lazy, ty) },
+                else => |e| return e,
+            })) continue;
 
             const field_align = if (field.abi_align != 0)
                 field.abi_align
src/value.zig
@@ -1911,7 +1911,11 @@ pub const Value = extern union {
 
             .lazy_align => {
                 const ty = lhs.castTag(.lazy_align).?.data;
-                if (try ty.hasRuntimeBitsAdvanced(false, opt_sema)) {
+                const strat: Type.AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager;
+                if (ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
+                    error.NeedLazy => unreachable,
+                    else => |e| return e,
+                }) {
                     return .gt;
                 } else {
                     return .eq;
@@ -1919,7 +1923,11 @@ pub const Value = extern union {
             },
             .lazy_size => {
                 const ty = lhs.castTag(.lazy_size).?.data;
-                if (try ty.hasRuntimeBitsAdvanced(false, opt_sema)) {
+                const strat: Type.AbiAlignmentAdvancedStrat = if (opt_sema) |sema| .{ .sema = sema } else .eager;
+                if (ty.hasRuntimeBitsAdvanced(false, strat) catch |err| switch (err) {
+                    error.NeedLazy => unreachable,
+                    else => |e| return e,
+                }) {
                     return .gt;
                 } else {
                     return .eq;