diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 4ddd286f16..7a3a97a2a6 100644 --- a/src-self-hosted/Module.zig +++ b/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){}); diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 40d3068d3f..eda9dcfc31 100644 --- a/src-self-hosted/main.zig +++ b/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, }; diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 749d9d9c87..a00782771d 100644 --- a/src-self-hosted/zir.zig +++ b/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);