Commit 91930a4ff0

Andrew Kelley <andrew@ziglang.org>
2020-06-05 21:49:23
stage2: fix not re-loading source file for updates after errors
1 parent cf654b5
Changed files (3)
src-self-hosted/main.zig
@@ -407,7 +407,21 @@ fn buildOutputType(
             std.debug.warn("-fno-emit-bin not supported yet", .{});
             process.exit(1);
         },
-        .yes_default_path => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
+        .yes_default_path => switch (output_mode) {
+            .Exe => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.exeFileExt() }),
+            .Lib => blk: {
+                const suffix = switch (link_mode orelse .Static) {
+                    .Static => target_info.target.staticLibSuffix(),
+                    .Dynamic => target_info.target.dynamicLibSuffix(),
+                };
+                break :blk try std.fmt.allocPrint(arena, "{}{}{}", .{
+                    target_info.target.libPrefix(),
+                    root_name,
+                    suffix,
+                });
+            },
+            .Obj => try std.fmt.allocPrint(arena, "{}{}", .{ root_name, target_info.target.oFileExt() }),
+        },
         .yes => |p| p,
     };
 
src-self-hosted/Module.zig
@@ -576,6 +576,8 @@ pub fn update(self: *Module) !void {
     // TODO Use the cache hash file system to detect which source files changed.
     // Here we simulate a full cache miss.
     // Analyze the root source file now.
+    // Source files could have been loaded for any reason; to force a refresh we unload now.
+    self.root_scope.unload(self.allocator);
     self.analyzeRoot(self.root_scope) catch |err| switch (err) {
         error.AnalysisFail => {
             assert(self.totalErrorCount() != 0);
@@ -594,8 +596,11 @@ pub fn update(self: *Module) !void {
         try self.deleteDecl(decl);
     }
 
-    // Unload all the source files from memory.
-    self.root_scope.unload(self.allocator);
+    // If there are any errors, we anticipate the source files being loaded
+    // to report error messages. Otherwise we unload all source files to save memory.
+    if (self.totalErrorCount() == 0) {
+        self.root_scope.unload(self.allocator);
+    }
 
     try self.bin_file.flush();
     self.link_error_flags = self.bin_file.error_flags;
@@ -878,11 +883,11 @@ fn analyzeRoot(self: *Module, root_scope: *Scope.ZIRModule) !void {
                     const decl = kv.value;
                     deleted_decls.removeAssertDiscard(decl);
                     const new_contents_hash = Decl.hashSimpleName(src_decl.contents);
+                    //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents });
                     if (!mem.eql(u8, &new_contents_hash, &decl.contents_hash)) {
-                        //std.debug.warn("noticed '{}' source changed\n", .{src_decl.name});
-                        decl.analysis = .outdated;
+                        //std.debug.warn("'{}' {x} => {x}\n", .{ src_decl.name, decl.contents_hash, new_contents_hash });
+                        try self.markOutdatedDecl(decl);
                         decl.contents_hash = new_contents_hash;
-                        try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
                     }
                 } else if (src_decl.cast(zir.Inst.Export)) |export_inst| {
                     try exports_to_resolve.append(&export_inst.base);
@@ -923,8 +928,7 @@ fn deleteDecl(self: *Module, decl: *Decl) !void {
     for (decl.dependants.items) |dep| {
         dep.removeDependency(decl);
         if (dep.analysis != .outdated) {
-            dep.analysis = .outdated;
-            try self.work_queue.writeItem(.{ .re_analyze_decl = dep });
+            try self.markOutdatedDecl(dep);
         }
     }
     self.deleteDeclExports(decl);
@@ -1083,14 +1087,22 @@ fn reAnalyzeDecl(self: *Module, decl: *Decl, old_inst: *zir.Inst) InnerError!voi
                 .codegen_failure_retryable,
                 .complete,
                 => if (dep.generation != self.generation) {
-                    dep.analysis = .outdated;
-                    try self.work_queue.writeItem(.{ .re_analyze_decl = dep });
+                    try self.markOutdatedDecl(dep);
                 },
             }
         }
     }
 }
 
+fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
+    //std.debug.warn("mark {} outdated\n", .{decl.name});
+    try self.work_queue.writeItem(.{ .re_analyze_decl = decl });
+    if (self.failed_decls.remove(decl)) |entry| {
+        self.allocator.destroy(entry.value);
+    }
+    decl.analysis = .outdated;
+}
+
 fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*Decl {
     const hash = Decl.hashSimpleName(old_inst.name);
     if (self.decl_table.get(hash)) |kv| {
@@ -1445,6 +1457,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
     switch (old_inst.tag) {
         .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?),
         .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?),
+        .compileerror => return self.analyzeInstCompileError(scope, old_inst.cast(zir.Inst.CompileError).?),
         .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?),
         .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?),
         .str => {
@@ -1484,6 +1497,10 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In
     }
 }
 
+fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.CompileError) InnerError!*Inst {
+    return self.fail(scope, inst.base.src, "{}", .{inst.positionals.msg});
+}
+
 fn analyzeInstBreakpoint(self: *Module, scope: *Scope, inst: *zir.Inst.Breakpoint) InnerError!*Inst {
     const b = try self.requireRuntimeBlock(scope, inst.base.src);
     return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){});
src-self-hosted/zir.zig
@@ -27,6 +27,7 @@ pub const Inst = struct {
     pub const Tag = enum {
         breakpoint,
         call,
+        compileerror,
         /// Represents a pointer to a global decl by name.
         declref,
         /// The syntax `@foo` is equivalent to `declval("foo")`.
@@ -62,6 +63,7 @@ pub const Inst = struct {
             .call => Call,
             .declref => DeclRef,
             .declval => DeclVal,
+            .compileerror => CompileError,
             .str => Str,
             .int => Int,
             .ptrtoint => PtrToInt,
@@ -135,6 +137,16 @@ pub const Inst = struct {
         kw_args: struct {},
     };
 
+    pub const CompileError = struct {
+        pub const base_tag = Tag.compileerror;
+        base: Inst,
+
+        positionals: struct {
+            msg: []const u8,
+        },
+        kw_args: struct {},
+    };
+
     pub const Str = struct {
         pub const base_tag = Tag.str;
         base: Inst,
@@ -513,6 +525,7 @@ pub const Module = struct {
             .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table),
             .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table),
             .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table),
+            .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, decl, inst_table),
             .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table),
             .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table),
             .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table),
@@ -917,6 +930,7 @@ const Parser = struct {
         try requireEatBytes(self, ")");
 
         inst_specific.base.contents = self.source[contents_start..self.i];
+        //std.debug.warn("parsed {} = '{}'\n", .{ inst_specific.base.name, inst_specific.base.contents });
 
         return &inst_specific.base;
     }
@@ -1230,7 +1244,44 @@ const EmitZIR = struct {
                 var instructions = std.ArrayList(*Inst).init(self.allocator);
                 defer instructions.deinit();
 
-                try self.emitBody(module_fn.analysis.success, &inst_table, &instructions);
+                switch (module_fn.analysis) {
+                    .queued => unreachable,
+                    .in_progress => unreachable,
+                    .success => |body| {
+                        try self.emitBody(body, &inst_table, &instructions);
+                    },
+                    .sema_failure => {
+                        const err_msg = self.old_module.failed_decls.getValue(module_fn.owner_decl).?;
+                        const fail_inst = try self.arena.allocator.create(Inst.CompileError);
+                        fail_inst.* = .{
+                            .base = .{
+                                .name = try self.autoName(),
+                                .src = src,
+                                .tag = Inst.CompileError.base_tag,
+                            },
+                            .positionals = .{
+                                .msg = try self.arena.allocator.dupe(u8, err_msg.msg),
+                            },
+                            .kw_args = .{},
+                        };
+                        try instructions.append(&fail_inst.base);
+                    },
+                    .dependency_failure => {
+                        const fail_inst = try self.arena.allocator.create(Inst.CompileError);
+                        fail_inst.* = .{
+                            .base = .{
+                                .name = try self.autoName(),
+                                .src = src,
+                                .tag = Inst.CompileError.base_tag,
+                            },
+                            .positionals = .{
+                                .msg = try self.arena.allocator.dupe(u8, "depends on another failed Decl"),
+                            },
+                            .kw_args = .{},
+                        };
+                        try instructions.append(&fail_inst.base);
+                    },
+                }
 
                 const fn_type = try self.emitType(src, module_fn.fn_type);