Commit 4c05a9a892

mlugg <mlugg@mlugg.co.uk>
2024-03-05 14:12:28
Sema: do not destroy enum type if field analysis fails
1 parent e1d8187
Changed files (2)
src/InternPool.zig
@@ -6810,17 +6810,13 @@ pub const WipEnumType = struct {
     names_start: u32,
     values_map: OptionalMapIndex,
     values_start: u32,
-    expected_fields_len: if (std.debug.runtime_safety) u32 else void,
 
     pub fn prepare(
         wip: WipEnumType,
         ip: *InternPool,
         decl: DeclIndex,
         namespace: OptionalNamespaceIndex,
-        tag_ty: Index,
     ) void {
-        assert(ip.isIntegerType(tag_ty));
-        ip.extra.items[wip.tag_ty_index] = @intFromEnum(tag_ty);
         ip.extra.items[wip.decl_index] = @intFromEnum(decl);
         if (wip.namespace_index) |i| {
             ip.extra.items[i] = @intFromEnum(namespace.unwrap().?);
@@ -6829,6 +6825,11 @@ pub const WipEnumType = struct {
         }
     }
 
+    pub fn setTagTy(wip: WipEnumType, ip: *InternPool, tag_ty: Index) void {
+        assert(ip.isIntegerType(tag_ty));
+        ip.extra.items[wip.tag_ty_index] = @intFromEnum(tag_ty);
+    }
+
     pub const FieldConflict = struct {
         kind: enum { name, value },
         prev_field_idx: u32,
@@ -6858,18 +6859,6 @@ pub const WipEnumType = struct {
         return null;
     }
 
-    pub fn finish(wip: WipEnumType, ip: *InternPool) Index {
-        if (std.debug.runtime_safety) {
-            const names_map = &ip.maps.items[@intFromEnum(wip.names_map)];
-            assert(names_map.count() == wip.expected_fields_len);
-            if (wip.values_map.unwrap()) |v| {
-                const values_map = &ip.maps.items[@intFromEnum(v)];
-                assert(values_map.count() == wip.expected_fields_len);
-            }
-        }
-        return wip.index;
-    }
-
     pub fn cancel(wip: WipEnumType, ip: *InternPool) void {
         ip.remove(wip.index);
     }
@@ -6951,7 +6940,6 @@ pub fn getEnumType(
                 .names_start = @intCast(names_start),
                 .values_map = .none,
                 .values_start = undefined,
-                .expected_fields_len = if (std.debug.runtime_safety) ini.fields_len else {},
             } };
         },
         .explicit, .nonexhaustive => {
@@ -7016,7 +7004,6 @@ pub fn getEnumType(
                 .names_start = @intCast(names_start),
                 .values_map = values_map,
                 .values_start = @intCast(values_start),
-                .expected_fields_len = if (std.debug.runtime_safety) ini.fields_len else {},
             } };
         },
     }
src/Sema.zig
@@ -2993,7 +2993,12 @@ fn zirEnumDecl(
         },
         .existing => |ty| return Air.internedToRef(ty),
     };
-    errdefer wip_ty.cancel(ip);
+
+    // Once this is `true`, we will not delete the decl or type even upon failure, since we
+    // have finished constructing the type and are in the process of analyzing it.
+    var done = false;
+
+    errdefer if (!done) wip_ty.cancel(ip);
 
     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
         .ty = Type.type,
@@ -3001,7 +3006,7 @@ fn zirEnumDecl(
     }, small.name_strategy, "enum", inst);
     const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    errdefer mod.abortAnonDecl(new_decl_index);
+    errdefer if (!done) mod.abortAnonDecl(new_decl_index);
 
     if (sema.mod.comp.debug_incremental) {
         try mod.intern_pool.addDependency(
@@ -3017,12 +3022,17 @@ fn zirEnumDecl(
         .decl_index = new_decl_index,
         .file_scope = block.getFileScope(mod),
     })).toOptional() else .none;
-    errdefer if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns);
+    errdefer if (!done) if (new_namespace_index.unwrap()) |ns| mod.destroyNamespace(ns);
 
     if (new_namespace_index.unwrap()) |ns| {
         try mod.scanNamespace(ns, decls, new_decl);
     }
 
+    // We've finished the initial construction of this type, and are about to perform analysis.
+    // Set the decl and namespace appropriately, and don't destroy anything on failure.
+    wip_ty.prepare(ip, new_decl_index, new_namespace_index);
+    done = true;
+
     const int_tag_ty = ty: {
         // We create a block for the field type instructions because they
         // may need to reference Decls from inside the enum namespace.
@@ -3075,7 +3085,7 @@ fn zirEnumDecl(
         }
     };
 
-    wip_ty.prepare(ip, new_decl_index, new_namespace_index, int_tag_ty.toIntern());
+    wip_ty.setTagTy(ip, int_tag_ty.toIntern());
 
     if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
         if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(mod)) {
@@ -3177,7 +3187,7 @@ fn zirEnumDecl(
     }
 
     try mod.finalizeAnonDecl(new_decl_index);
-    return Air.internedToRef(wip_ty.finish(ip));
+    return Air.internedToRef(wip_ty.index);
 }
 
 fn zirUnionDecl(
@@ -21537,7 +21547,8 @@ fn reifyEnum(
     mod.declPtr(new_decl_index).owns_tv = true;
     errdefer mod.abortAnonDecl(new_decl_index);
 
-    wip_ty.prepare(ip, new_decl_index, .none, tag_ty.toIntern());
+    wip_ty.prepare(ip, new_decl_index, .none);
+    wip_ty.setTagTy(ip, tag_ty.toIntern());
 
     for (0..fields_len) |field_idx| {
         const field_info = try fields_val.elemValue(mod, field_idx);
@@ -21582,7 +21593,7 @@ fn reifyEnum(
     }
 
     try mod.finalizeAnonDecl(new_decl_index);
-    return Air.internedToRef(wip_ty.finish(ip));
+    return Air.internedToRef(wip_ty.index);
 }
 
 fn reifyUnion(