Commit 256ab68a97

Andrew Kelley <andrew@ziglang.org>
2023-10-28 04:22:40
frontend: make Decl.zir_decl_index typed
This field had the wrong type. It's not a `Zir.Inst.Index`, it's actually a `Zir.OptionalExtraIndex`. Also, the former is currently aliased to `u32` while the latter is a nonexhaustive enum that gives us more type checking. This commit is preparation for making this field non-optional. Now it can be changed to `Zir.ExtraIndex` and then the compiler will point out all the places that the non-optional assumption is being violated.
1 parent 9ff9ea3
src/Autodoc.zig
@@ -3925,7 +3925,7 @@ fn analyzeAllDecls(
     {
         var it = original_it;
         while (it.next()) |d| {
-            const decl_name_index = file.zir.extra[d.sub_index + 5];
+            const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
             switch (decl_name_index) {
                 0, 1, 2 => continue,
                 else => if (file.zir.string_bytes[decl_name_index] == 0) {
@@ -3942,7 +3942,7 @@ fn analyzeAllDecls(
         var it = original_it;
         var decl_indexes_slot = first_decl_indexes_slot;
         while (it.next()) |d| : (decl_indexes_slot += 1) {
-            const decl_name_index = file.zir.extra[d.sub_index + 5];
+            const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
             switch (decl_name_index) {
                 0 => {
                     const is_exported = @as(u1, @truncate(d.flags >> 1));
@@ -3969,7 +3969,7 @@ fn analyzeAllDecls(
     // Third loop to analyze all remaining decls
     var it = original_it;
     while (it.next()) |d| {
-        const decl_name_index = file.zir.extra[d.sub_index + 5];
+        const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
         switch (decl_name_index) {
             0, 1 => continue, // skip over usingnamespace decls
             2 => continue, // skip decltests
@@ -3993,7 +3993,7 @@ fn analyzeAllDecls(
     // Fourth loop to analyze decltests
     it = original_it;
     while (it.next()) |d| {
-        const decl_name_index = file.zir.extra[d.sub_index + 5];
+        const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 5];
         switch (decl_name_index) {
             0, 1 => continue, // skip over usingnamespace decls
             2 => {},
@@ -4028,7 +4028,7 @@ fn analyzeDecl(
     const has_align = @as(u1, @truncate(d.flags >> 2)) != 0;
     const has_section_or_addrspace = @as(u1, @truncate(d.flags >> 3)) != 0;
 
-    var extra_index = d.sub_index;
+    var extra_index = @intFromEnum(d.sub_index);
     // const hash_u32s = file.zir.extra[extra_index..][0..4];
 
     extra_index += 4;
@@ -4158,8 +4158,8 @@ fn analyzeUsingnamespaceDecl(
     const data = file.zir.instructions.items(.data);
 
     const is_pub = @as(u1, @truncate(d.flags)) != 0;
-    const value_index = file.zir.extra[d.sub_index + 6];
-    const doc_comment_index = file.zir.extra[d.sub_index + 7];
+    const value_index = file.zir.extra[@intFromEnum(d.sub_index) + 6];
+    const doc_comment_index = file.zir.extra[@intFromEnum(d.sub_index) + 7];
 
     // This is known to work because decl values are always block_inlines
     const value_pl_node = data[value_index].pl_node;
@@ -4218,8 +4218,8 @@ fn analyzeDecltest(
 ) AutodocErrors!void {
     const data = file.zir.instructions.items(.data);
 
-    const value_index = file.zir.extra[d.sub_index + 6];
-    const decl_name_index = file.zir.extra[d.sub_index + 7];
+    const value_index = file.zir.extra[@intFromEnum(d.sub_index) + 6];
+    const decl_name_index = file.zir.extra[@intFromEnum(d.sub_index) + 7];
 
     const value_pl_node = data[value_index].pl_node;
     const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
src/Compilation.zig
@@ -2267,7 +2267,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
             const decl = module.declPtr(decl_index);
             assert(decl.deletion_flag);
             assert(decl.dependants.count() == 0);
-            assert(decl.zir_decl_index != 0);
+            assert(decl.zir_decl_index != .none);
 
             try module.clearDecl(decl_index, null);
         }
src/main.zig
@@ -6632,7 +6632,7 @@ pub fn cmdChangelist(
     var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{};
     defer inst_map.deinit(gpa);
 
-    var extra_map: std.AutoHashMapUnmanaged(u32, u32) = .{};
+    var extra_map: std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex) = .{};
     defer extra_map.deinit(gpa);
 
     try Module.mapOldZirToNew(gpa, old_zir, file.zir, &inst_map, &extra_map);
src/Module.zig
@@ -390,7 +390,7 @@ pub const Decl = struct {
     /// (the part that says "for every decls_len"). The first item at this index is
     /// the contents hash, followed by line, name, etc.
     /// For anonymous decls and also the root Decl for a File, this is 0.
-    zir_decl_index: Zir.Inst.Index,
+    zir_decl_index: Zir.OptionalExtraIndex,
 
     /// Represents the "shallow" analysis status. For example, for decls that are functions,
     /// the function type is analyzed with this set to `in_progress`, however, the semantic
@@ -526,8 +526,8 @@ pub const Decl = struct {
     }
 
     pub fn getNameZir(decl: Decl, zir: Zir) ?[:0]const u8 {
-        assert(decl.zir_decl_index != 0);
-        const name_index = zir.extra[decl.zir_decl_index + 5];
+        assert(decl.zir_decl_index != .none);
+        const name_index = zir.extra[@intFromEnum(decl.zir_decl_index) + 5];
         if (name_index <= 1) return null;
         return zir.nullTerminatedString(name_index);
     }
@@ -538,39 +538,39 @@ pub const Decl = struct {
     }
 
     pub fn contentsHashZir(decl: Decl, zir: Zir) std.zig.SrcHash {
-        assert(decl.zir_decl_index != 0);
-        const hash_u32s = zir.extra[decl.zir_decl_index..][0..4];
+        assert(decl.zir_decl_index != .none);
+        const hash_u32s = zir.extra[@intFromEnum(decl.zir_decl_index)..][0..4];
         const contents_hash = @as(std.zig.SrcHash, @bitCast(hash_u32s.*));
         return contents_hash;
     }
 
     pub fn zirBlockIndex(decl: *const Decl, mod: *Module) Zir.Inst.Index {
-        assert(decl.zir_decl_index != 0);
+        assert(decl.zir_decl_index != .none);
         const zir = decl.getFileScope(mod).zir;
-        return zir.extra[decl.zir_decl_index + 6];
+        return zir.extra[@intFromEnum(decl.zir_decl_index) + 6];
     }
 
     pub fn zirAlignRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
         if (!decl.has_align) return .none;
-        assert(decl.zir_decl_index != 0);
+        assert(decl.zir_decl_index != .none);
         const zir = decl.getFileScope(mod).zir;
-        return @as(Zir.Inst.Ref, @enumFromInt(zir.extra[decl.zir_decl_index + 8]));
+        return @enumFromInt(zir.extra[@intFromEnum(decl.zir_decl_index) + 8]);
     }
 
     pub fn zirLinksectionRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
         if (!decl.has_linksection_or_addrspace) return .none;
-        assert(decl.zir_decl_index != 0);
+        assert(decl.zir_decl_index != .none);
         const zir = decl.getFileScope(mod).zir;
-        const extra_index = decl.zir_decl_index + 8 + @intFromBool(decl.has_align);
-        return @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
+        const extra_index = @intFromEnum(decl.zir_decl_index) + 8 + @intFromBool(decl.has_align);
+        return @enumFromInt(zir.extra[extra_index]);
     }
 
     pub fn zirAddrspaceRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
         if (!decl.has_linksection_or_addrspace) return .none;
-        assert(decl.zir_decl_index != 0);
+        assert(decl.zir_decl_index != .none);
         const zir = decl.getFileScope(mod).zir;
-        const extra_index = decl.zir_decl_index + 8 + @intFromBool(decl.has_align) + 1;
-        return @as(Zir.Inst.Ref, @enumFromInt(zir.extra[extra_index]));
+        const extra_index = @intFromEnum(decl.zir_decl_index) + 8 + @intFromBool(decl.has_align) + 1;
+        return @enumFromInt(zir.extra[extra_index]);
     }
 
     pub fn relativeToLine(decl: Decl, offset: u32) u32 {
@@ -578,7 +578,7 @@ pub const Decl = struct {
     }
 
     pub fn relativeToNodeIndex(decl: Decl, offset: i32) Ast.Node.Index {
-        return @as(Ast.Node.Index, @bitCast(offset + @as(i32, @bitCast(decl.src_node))));
+        return @bitCast(offset + @as(i32, @bitCast(decl.src_node)));
     }
 
     pub fn nodeIndexToRelative(decl: Decl, node_index: Ast.Node.Index) i32 {
@@ -3051,7 +3051,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
     defer inst_map.deinit(gpa);
     // Maps from old ZIR to new ZIR, the extra data index for the sub-decl item.
     // e.g. the thing that Decl.zir_decl_index points to.
-    var extra_map: std.AutoHashMapUnmanaged(u32, u32) = .{};
+    var extra_map: std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex) = .{};
     defer extra_map.deinit(gpa);
 
     try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map, &extra_map);
@@ -3079,14 +3079,13 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
         // to walk them but we do not need to modify this value.
         // Anonymous decls should not be marked outdated. They will be re-generated
         // if their owner decl is marked outdated.
-        if (decl.zir_decl_index != 0) {
-            const old_zir_decl_index = decl.zir_decl_index;
+        if (decl.zir_decl_index.unwrap()) |old_zir_decl_index| {
             const new_zir_decl_index = extra_map.get(old_zir_decl_index) orelse {
                 try file.deleted_decls.append(gpa, decl_index);
                 continue;
             };
             const old_hash = decl.contentsHashZir(old_zir);
-            decl.zir_decl_index = new_zir_decl_index;
+            decl.zir_decl_index = new_zir_decl_index.toOptional();
             const new_hash = decl.contentsHashZir(new_zir);
             if (!std.zig.srcHashEql(old_hash, new_hash)) {
                 try file.outdated_decls.append(gpa, decl_index);
@@ -3195,7 +3194,7 @@ pub fn mapOldZirToNew(
     old_zir: Zir,
     new_zir: Zir,
     inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index),
-    extra_map: *std.AutoHashMapUnmanaged(u32, u32),
+    extra_map: *std.AutoHashMapUnmanaged(Zir.ExtraIndex, Zir.ExtraIndex),
 ) Allocator.Error!void {
     // Contain ZIR indexes of declaration instructions.
     const MatchedZirDecl = struct {
@@ -3220,7 +3219,7 @@ pub fn mapOldZirToNew(
         try inst_map.put(gpa, match_item.old_inst, match_item.new_inst);
 
         // Maps name to extra index of decl sub item.
-        var decl_map: std.StringHashMapUnmanaged(u32) = .{};
+        var decl_map: std.StringHashMapUnmanaged(Zir.ExtraIndex) = .{};
         defer decl_map.deinit(gpa);
 
         {
@@ -3301,7 +3300,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
     defer decl_prog_node.end();
 
     const type_changed = blk: {
-        if (decl.zir_decl_index == 0 and !mod.declIsRoot(decl_index)) {
+        if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) {
             // Anonymous decl. We don't semantically analyze these.
             break :blk false; // tv unchanged
         }
@@ -4451,7 +4450,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
         new_decl.is_exported = is_exported;
         new_decl.has_align = has_align;
         new_decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
-        new_decl.zir_decl_index = @as(u32, @intCast(decl_sub_index));
+        new_decl.zir_decl_index = @enumFromInt(decl_sub_index);
         new_decl.alive = true; // This Decl corresponds to an AST node and therefore always alive.
         return;
     }
@@ -4485,7 +4484,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
     decl.kind = kind;
     decl.has_align = has_align;
     decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
-    decl.zir_decl_index = @as(u32, @intCast(decl_sub_index));
+    decl.zir_decl_index = @enumFromInt(decl_sub_index);
     if (decl.getOwnedFunction(mod) != null) {
         switch (comp.bin_file.tag) {
             .coff, .elf, .macho, .plan9 => {
@@ -4982,7 +4981,7 @@ pub fn allocateNewDecl(
         .@"addrspace" = .generic,
         .analysis = .unreferenced,
         .deletion_flag = false,
-        .zir_decl_index = 0,
+        .zir_decl_index = .none,
         .src_scope = src_scope,
         .generation = 0,
         .is_pub = false,
@@ -5507,7 +5506,7 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
             const decl = mod.declPtr(decl_index);
 
             // Remove from the namespace it resides in, preserving declaration order.
-            assert(decl.zir_decl_index != 0);
+            assert(decl.zir_decl_index != .none);
             _ = mod.namespacePtr(decl.src_namespace).decls.orderedRemoveAdapted(
                 decl.name,
                 DeclAdapter{ .mod = mod },
src/Zir.zig
@@ -63,6 +63,21 @@ pub const ExtraIndex = enum(u32) {
     imports,
 
     _,
+
+    pub fn toOptional(i: ExtraIndex) OptionalExtraIndex {
+        return @enumFromInt(@intFromEnum(i));
+    }
+};
+
+pub const OptionalExtraIndex = enum(u32) {
+    compile_errors,
+    imports,
+    none = std.math.maxInt(u32),
+    _,
+
+    pub fn unwrap(oi: OptionalExtraIndex) ?ExtraIndex {
+        return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
+    }
 };
 
 fn ExtraData(comptime T: type) type {
@@ -3325,7 +3340,7 @@ pub const DeclIterator = struct {
 
     pub const Item = struct {
         name: [:0]const u8,
-        sub_index: u32,
+        sub_index: ExtraIndex,
         flags: u4,
     };
 
@@ -3341,7 +3356,7 @@ pub const DeclIterator = struct {
         const flags: u4 = @truncate(it.cur_bit_bag);
         it.cur_bit_bag >>= 4;
 
-        const sub_index: u32 = @intCast(it.extra_index);
+        const sub_index: ExtraIndex = @enumFromInt(it.extra_index);
         it.extra_index += 5; // src_hash(4) + line(1)
         const name = it.zir.nullTerminatedString(it.zir.extra[it.extra_index]);
         it.extra_index += 3; // name(1) + value(1) + doc_comment(1)
@@ -3453,8 +3468,8 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera
 
 /// The iterator would have to allocate memory anyway to iterate. So here we populate
 /// an ArrayList as the result.
-pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: u32) !void {
-    const block_inst = zir.extra[decl_sub_index + 6];
+pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: ExtraIndex) !void {
+    const block_inst = zir.extra[@intFromEnum(decl_sub_index) + 6];
     list.clearRetainingCapacity();
 
     return zir.findDeclsInner(list, block_inst);