Commit 71afc30880

Andrew Kelley <andrew@ziglang.org>
2021-05-12 08:20:22
stage2: more Decl lifetime fixes
* File stores `root_decl: Decl` instead of `namespace: *Namespace`. This maps more cleanly to the actual ownership, since the `File` does own the root decl, but it does not directly own the `Namespace`. * `semaFile` completes the creation of the `Decl` even when semantic analysis fails. The `analysis` field of the `Decl` will contain the results of semantic analysis. This prevents cleaning up of memory still referenced by other Decl objects. * `semaDecl` sets `Struct.zir_index` of the root struct decl, which fixes use of undefined value in case the first update contained a ZIR compile error.
1 parent 1ab1a96
Changed files (3)
src/main.zig
@@ -3656,7 +3656,7 @@ pub fn cmdAstgen(
         .tree = undefined,
         .zir = undefined,
         .pkg = undefined,
-        .namespace = undefined,
+        .root_decl = null,
     };
 
     const source = try arena.allocSentinel(u8, stat.size, 0);
@@ -3766,7 +3766,7 @@ pub fn cmdChangelist(
         .tree = undefined,
         .zir = undefined,
         .pkg = undefined,
-        .namespace = undefined,
+        .root_decl = null,
     };
 
     const source = try arena.allocSentinel(u8, stat.size, 0);
src/Module.zig
@@ -822,7 +822,7 @@ pub const Scope = struct {
     pub fn namespace(scope: *Scope) *Namespace {
         switch (scope.tag) {
             .block => return scope.cast(Block).?.sema.owner_decl.namespace,
-            .file => return scope.cast(File).?.namespace.?,
+            .file => return scope.cast(File).?.root_decl.?.namespace,
             .namespace => return scope.cast(Namespace).?,
             .decl_ref => return scope.cast(DeclRef).?.decl.namespace,
         }
@@ -998,10 +998,8 @@ pub const Scope = struct {
         zir: Zir,
         /// Package that this file is a part of, managed externally.
         pkg: *Package,
-        /// The namespace of the struct that represents this file.
-        /// Populated only when `have_decl` is true.
-        /// Owned by its owner Decl Value.
-        namespace: ?*Namespace,
+        /// The Decl of the struct that represents this File.
+        root_decl: ?*Decl,
 
         /// Used by change detection algorithm, after astgen, contains the
         /// set of decls that existed in the previous ZIR but not in the new one.
@@ -1049,8 +1047,8 @@ pub const Scope = struct {
             log.debug("deinit File {s}", .{file.sub_file_path});
             file.deleted_decls.deinit(gpa);
             file.outdated_decls.deinit(gpa);
-            if (file.namespace) |ns| {
-                ns.getDecl().destroy(mod);
+            if (file.root_decl) |root_decl| {
+                root_decl.destroy(mod);
             }
             gpa.free(file.sub_file_path);
             file.unload(gpa);
@@ -2576,11 +2574,11 @@ pub fn astGenFile(mod: *Module, file: *Scope.File, prog_node: *std.Progress.Node
                 gpa.destroy(file_prev_zir);
                 file.prev_zir = null;
                 try updateZirRefs(gpa, file, prev_zir);
-            } else if (file.namespace) |ns| {
+            } else if (file.root_decl) |root_decl| {
                 // First time the File has succeeded ZIR. We must mark it outdated since
                 // we have already tried to semantically analyze it.
                 try file.outdated_decls.resize(gpa, 1);
-                file.outdated_decls.items[0] = ns.getDecl();
+                file.outdated_decls.items[0] = root_decl;
             }
         } else {
             try updateZirRefs(gpa, file, prev_zir);
@@ -2626,7 +2624,7 @@ fn updateZirRefs(gpa: *Allocator, file: *Scope.File, old_zir: Zir) !void {
     var decl_stack: std.ArrayListUnmanaged(*Decl) = .{};
     defer decl_stack.deinit(gpa);
 
-    const root_decl = file.namespace.?.getDecl();
+    const root_decl = file.root_decl.?;
     try decl_stack.append(gpa, root_decl);
 
     file.deleted_decls.clearRetainingCapacity();
@@ -2848,7 +2846,7 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
     const tracy = trace(@src());
     defer tracy.end();
 
-    if (file.namespace != null) return;
+    if (file.root_decl != null) return;
 
     const gpa = mod.gpa;
     var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
@@ -2870,8 +2868,8 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
             .file_scope = file,
         },
     };
-    file.namespace = &struct_obj.namespace;
     const new_decl = try mod.allocateNewDecl(&struct_obj.namespace, 0);
+    file.root_decl = new_decl;
     struct_obj.owner_decl = new_decl;
     new_decl.src_line = 0;
     new_decl.name = try file.fullyQualifiedNameZ(gpa);
@@ -2917,9 +2915,12 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void {
         };
         defer block_scope.instructions.deinit(gpa);
 
-        try sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj);
-
-        new_decl.analysis = .complete;
+        if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_obj)) |_| {
+            new_decl.analysis = .complete;
+        } else |err| switch (err) {
+            error.OutOfMemory => return error.OutOfMemory,
+            error.AnalysisFail => {},
+        }
     } else {
         new_decl.analysis = .file_failure;
     }
@@ -2964,6 +2965,9 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool {
         log.debug("semaDecl root {*} ({s})", .{ decl, decl.name });
         const main_struct_inst = zir.getMainStruct();
         const struct_obj = decl.getStruct().?;
+        // This might not have gotten set in `semaFile` if the first time had
+        // a ZIR failure, so we set it here in case.
+        struct_obj.zir_index = main_struct_inst;
         try sema.analyzeStructDecl(decl, main_struct_inst, struct_obj);
         decl.analysis = .complete;
         decl.generation = mod.generation;
@@ -3165,7 +3169,7 @@ pub fn importPkg(mod: *Module, cur_pkg: *Package, pkg: *Package) !ImportFileResu
         .zir = undefined,
         .status = .never_loaded,
         .pkg = pkg,
-        .namespace = null,
+        .root_decl = null,
     };
     return ImportFileResult{
         .file = new_file,
@@ -3231,7 +3235,7 @@ pub fn importFile(
         .zir = undefined,
         .status = .never_loaded,
         .pkg = cur_file.pkg,
-        .namespace = null,
+        .root_decl = null,
     };
     return ImportFileResult{
         .file = new_file,
src/Sema.zig
@@ -4409,7 +4409,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
         },
     };
     try mod.semaFile(result.file);
-    return mod.constType(sema.arena, src, result.file.namespace.?.ty);
+    return mod.constType(sema.arena, src, result.file.root_decl.?.ty);
 }
 
 fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
@@ -7307,7 +7307,7 @@ fn getBuiltinType(
     const opt_builtin_inst = try sema.analyzeNamespaceLookup(
         block,
         src,
-        std_file.namespace.?,
+        std_file.root_decl.?.namespace,
         "builtin",
     );
     const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst.?, src);