Commit 59dad43de2

Veikka Tuominen <git@vexu.eu>
2022-12-02 17:46:59
Sema: add error for failed assumption about struct having runtime bits
1 parent 86e6acb
src/Module.zig
@@ -940,6 +940,7 @@ pub const Struct = struct {
     requires_comptime: PropertyBoolean = .unknown,
     have_field_inits: bool = false,
     is_tuple: bool,
+    assumed_runtime_bits: bool = false,
 
     pub const Fields = std.StringArrayHashMapUnmanaged(Field);
 
@@ -1205,6 +1206,7 @@ pub const Union = struct {
         fully_resolved,
     },
     requires_comptime: PropertyBoolean = .unknown,
+    assumed_runtime_bits: bool = false,
 
     pub const Field = struct {
         /// undefined until `status` is `have_field_types` or `have_layout`.
src/Sema.zig
@@ -29237,6 +29237,16 @@ fn resolveStructLayout(sema: *Sema, ty: Type) CompileError!void {
 
         struct_obj.status = .have_layout;
         _ = try sema.resolveTypeRequiresComptime(resolved_ty);
+
+        if (struct_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) {
+            const msg = try Module.ErrorMsg.create(
+                sema.gpa,
+                struct_obj.srcLoc(sema.mod),
+                "struct layout depends on it having runtime bits",
+                .{},
+            );
+            return sema.failWithOwnedErrorMsg(msg);
+        }
     }
     // otherwise it's a tuple; no need to resolve anything
 }
@@ -29401,6 +29411,16 @@ fn resolveUnionLayout(sema: *Sema, ty: Type) CompileError!void {
     }
     union_obj.status = .have_layout;
     _ = try sema.resolveTypeRequiresComptime(resolved_ty);
+
+    if (union_obj.assumed_runtime_bits and !resolved_ty.hasRuntimeBits()) {
+        const msg = try Module.ErrorMsg.create(
+            sema.gpa,
+            union_obj.srcLoc(sema.mod),
+            "union layout depends on it having runtime bits",
+            .{},
+        );
+        return sema.failWithOwnedErrorMsg(msg);
+    }
 }
 
 // In case of querying the ABI alignment of this struct, we will ask
src/type.zig
@@ -2459,6 +2459,7 @@ pub const Type = extern union {
                 if (struct_obj.status == .field_types_wip) {
                     // In this case, we guess that hasRuntimeBits() for this type is true,
                     // and then later if our guess was incorrect, we emit a compile error.
+                    struct_obj.assumed_runtime_bits = true;
                     return true;
                 }
                 switch (strat) {
@@ -2491,6 +2492,12 @@ pub const Type = extern union {
 
             .@"union" => {
                 const union_obj = ty.castTag(.@"union").?.data;
+                if (union_obj.status == .field_types_wip) {
+                    // In this case, we guess that hasRuntimeBits() for this type is true,
+                    // and then later if our guess was incorrect, we emit a compile error.
+                    union_obj.assumed_runtime_bits = true;
+                    return true;
+                }
                 switch (strat) {
                     .sema => |sema| _ = try sema.resolveTypeFields(ty),
                     .eager => assert(union_obj.haveFieldTypes()),
@@ -3027,8 +3034,9 @@ pub const Type = extern union {
                 const struct_obj = ty.castTag(.@"struct").?.data;
                 if (opt_sema) |sema| {
                     if (struct_obj.status == .field_types_wip) {
-                        // We'll guess "pointer-aligned" and if we guess wrong, emit
-                        // a compile error later.
+                        // We'll guess "pointer-aligned", if the struct has an
+                        // underaligned pointer field then some allocations
+                        // might require explicit alignment.
                         return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
                     }
                     _ = try sema.resolveTypeFields(ty);
@@ -3153,8 +3161,9 @@ pub const Type = extern union {
         };
         if (opt_sema) |sema| {
             if (union_obj.status == .field_types_wip) {
-                // We'll guess "pointer-aligned" and if we guess wrong, emit
-                // a compile error later.
+                // We'll guess "pointer-aligned", if the union has an
+                // underaligned pointer field then some allocations
+                // might require explicit alignment.
                 return AbiAlignmentAdvanced{ .scalar = @divExact(target.cpu.arch.ptrBitWidth(), 8) };
             }
             _ = try sema.resolveTypeFields(ty);
@@ -5234,7 +5243,12 @@ pub const Type = extern union {
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
                 switch (struct_obj.requires_comptime) {
-                    .wip, .unknown => unreachable, // This function asserts types already resolved.
+                    .wip, .unknown => {
+                        // Return false to avoid incorrect dependency loops.
+                        // This will be handled correctly once merged with
+                        // `Sema.typeRequiresComptime`.
+                        return false;
+                    },
                     .no => return false,
                     .yes => return true,
                 }
@@ -5243,7 +5257,12 @@ pub const Type = extern union {
             .@"union", .union_safety_tagged, .union_tagged => {
                 const union_obj = ty.cast(Type.Payload.Union).?.data;
                 switch (union_obj.requires_comptime) {
-                    .wip, .unknown => unreachable, // This function asserts types already resolved.
+                    .wip, .unknown => {
+                        // Return false to avoid incorrect dependency loops.
+                        // This will be handled correctly once merged with
+                        // `Sema.typeRequiresComptime`.
+                        return false;
+                    },
                     .no => return false,
                     .yes => return true,
                 }
src/value.zig
@@ -187,7 +187,7 @@ pub const Value = extern union {
         bound_fn,
         /// The ABI alignment of the payload type.
         lazy_align,
-        /// The ABI alignment of the payload type.
+        /// The ABI size of the payload type.
         lazy_size,
 
         pub const last_no_payload_tag = Tag.empty_array;
test/cases/compile_errors/invalid_dependency_on_struct_size.zig
@@ -0,0 +1,19 @@
+comptime {
+    const S = struct {
+        const Foo = struct {
+            y: Bar,
+        };
+        const Bar = struct {
+            y: if (@sizeOf(Foo) == 0) u64 else void,
+        };
+    };
+
+    _ = @sizeOf(S.Foo) + 1;
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :6:21: error: struct layout depends on it having runtime bits
+// :4:13: note: while checking this field