aboutsummaryrefslogtreecommitdiff
path: root/src/Module.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-01-16 22:51:01 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-01-16 22:51:01 -0700
commit8c9ac4db978c80246b4872c899b1618b1b195ec2 (patch)
treee6bddfb9df54fc6d60518978506e6869c1b2be80 /src/Module.zig
parent8436134499c623485aeb20374a9928685db4211e (diff)
downloadzig-8c9ac4db978c80246b4872c899b1618b1b195ec2.tar.gz
zig-8c9ac4db978c80246b4872c899b1618b1b195ec2.zip
stage2: implement error notes and regress -femit-zir
* Implement error notes - note: other symbol exported here - note: previous else prong is here - note: previous '_' prong is here * Add Compilation.CObject.ErrorMsg. This object properly converts to AllErrors.Message when the time comes. * Add Compilation.CObject.failure_retryable. Properly handles out-of-memory and other transient failures. * Introduce Module.SrcLoc which has not only a byte offset but also references the file which the byte offset applies to. * Scope.Block now contains both a pointer to the "owner" Decl and the "source" Decl. As an example, during inline function call, the "owner" will be the Decl of the caller and the "source" will be the Decl of the callee. * Module.ErrorMsg now sports a `file_scope` field so that notes can refer to source locations in a file other than the parent error message. * Some instances where a `*Scope` was stored, now store a `*Scope.Container`. * Some methods in the `Scope` namespace were moved to the more specific type, since there was only an implementation for one particular tag. - `removeDecl` moved to `Scope.Container` - `destroy` moved to `Scope.File` * Two kinds of Scope deleted: - zir_module - decl * astgen: properly use DeclVal / DeclRef. DeclVal was incorrectly changed to be a reference; this commit fixes it. Fewer ZIR instructions processed as a result. - declval_in_module is renamed to declval - previous declval ZIR instruction is deleted; it was only for .zir files. * Test harness: friendlier diagnostics when an unexpected set of errors is encountered. * zir_sema: fix analyzeInstBlockFlat by properly calling resolvingInst on the last zir instruction in the block. Compile log implementation: * Write to a buffer rather than directly to stderr. * Only keep track of 1 callsite per Decl. * No longer mutate the ZIR Inst struct data. * "Compile log statement found" errors are only emitted when there are no other compile errors. -femit-zir and support for .zir source files is regressed. If we wanted to support this again, outputting .zir would need to be done as yet another backend rather than in the haphazard way it was previously implemented. For parsing .zir, it was implemented previously in a way that was not helpful for debugging. We need tighter integration with the test harness for it to be useful; so clearly a rewrite is needed. Given that a rewrite is needed, and it was getting in the way of progress and organization of the rest of stage2, I regressed the feature.
Diffstat (limited to 'src/Module.zig')
-rw-r--r--src/Module.zig764
1 files changed, 281 insertions, 483 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 0bdeab68d0..e612f8f759 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -35,8 +35,7 @@ zig_cache_artifact_directory: Compilation.Directory,
/// Pointer to externally managed resource. `null` if there is no zig file being compiled.
root_pkg: *Package,
/// Module owns this resource.
-/// The `Scope` is either a `Scope.ZIRModule` or `Scope.File`.
-root_scope: *Scope,
+root_scope: *Scope.File,
/// It's rare for a decl to be exported, so we save memory by having a sparse map of
/// Decl pointers to details about them being exported.
/// The Export memory is owned by the `export_owners` table; the slice itself is owned by this table.
@@ -57,19 +56,19 @@ decl_table: std.ArrayHashMapUnmanaged(Scope.NameHash, *Decl, Scope.name_hash_has
/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
/// Note that a Decl can succeed but the Fn it represents can fail. In this case,
/// a Decl can have a failed_decls entry but have analysis status of success.
-failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
+failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
/// When emit_h is non-null, each Decl gets one more compile error slot for
/// emit-h failing for that Decl. This table is also how we tell if a Decl has
/// failed emit-h or succeeded.
-emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *Compilation.ErrorMsg) = .{},
-/// A Decl can have multiple compileLogs, but only one error, so we map a Decl to a the src locs of all the compileLogs
-compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, ArrayListUnmanaged(usize)) = .{},
+emit_h_failed_decls: std.AutoArrayHashMapUnmanaged(*Decl, *ErrorMsg) = .{},
+/// Keep track of one `@compileLog` callsite per owner Decl.
+compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, SrcLoc) = .{},
/// Using a map here for consistency with the other fields here.
/// The ErrorMsg memory is owned by the `Scope`, using Module's general purpose allocator.
-failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *Compilation.ErrorMsg) = .{},
+failed_files: std.AutoArrayHashMapUnmanaged(*Scope, *ErrorMsg) = .{},
/// Using a map here for consistency with the other fields here.
/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
-failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *Compilation.ErrorMsg) = .{},
+failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
next_anon_name_index: usize = 0,
@@ -103,6 +102,8 @@ stage1_flags: packed struct {
emit_h: ?Compilation.EmitLoc,
+compile_log_text: std.ArrayListUnmanaged(u8) = .{},
+
pub const Export = struct {
options: std.builtin.ExportOptions,
/// Byte offset into the file that contains the export directive.
@@ -138,9 +139,9 @@ pub const Decl = struct {
/// mapping them to an address in the output file.
/// Memory owned by this decl, using Module's allocator.
name: [*:0]const u8,
- /// The direct parent container of the Decl. This is either a `Scope.Container` or `Scope.ZIRModule`.
+ /// The direct parent container of the Decl.
/// Reference to externally owned memory.
- scope: *Scope,
+ container: *Scope.Container,
/// The AST Node decl index or ZIR Inst index that contains this declaration.
/// Must be recomputed when the corresponding source file is modified.
src_index: usize,
@@ -235,31 +236,21 @@ pub const Decl = struct {
}
}
+ pub fn srcLoc(self: Decl) SrcLoc {
+ return .{
+ .byte_offset = self.src(),
+ .file_scope = self.getFileScope(),
+ };
+ }
+
pub fn src(self: Decl) usize {
- switch (self.scope.tag) {
- .container => {
- const container = @fieldParentPtr(Scope.Container, "base", self.scope);
- const tree = container.file_scope.contents.tree;
- // TODO Container should have its own decls()
- const decl_node = tree.root_node.decls()[self.src_index];
- return tree.token_locs[decl_node.firstToken()].start;
- },
- .zir_module => {
- const zir_module = @fieldParentPtr(Scope.ZIRModule, "base", self.scope);
- const module = zir_module.contents.module;
- const src_decl = module.decls[self.src_index];
- return src_decl.inst.src;
- },
- .file, .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
+ const tree = self.container.file_scope.contents.tree;
+ const decl_node = tree.root_node.decls()[self.src_index];
+ return tree.token_locs[decl_node.firstToken()].start;
}
pub fn fullyQualifiedNameHash(self: Decl) Scope.NameHash {
- return self.scope.fullyQualifiedNameHash(mem.spanZ(self.name));
+ return self.container.fullyQualifiedNameHash(mem.spanZ(self.name));
}
pub fn typedValue(self: *Decl) error{AnalysisFail}!TypedValue {
@@ -293,9 +284,8 @@ pub const Decl = struct {
}
}
- /// Asserts that the `Decl` is part of AST and not ZIRModule.
- pub fn getFileScope(self: *Decl) *Scope.File {
- return self.scope.cast(Scope.Container).?.file_scope;
+ pub fn getFileScope(self: Decl) *Scope.File {
+ return self.container.file_scope;
}
pub fn getEmitH(decl: *Decl, module: *Module) *EmitH {
@@ -326,7 +316,7 @@ pub const Fn = struct {
/// Contains un-analyzed ZIR instructions generated from Zig source AST.
/// Even after we finish analysis, the ZIR is kept in memory, so that
/// comptime and inline function calls can happen.
- zir: zir.Module.Body,
+ zir: zir.Body,
/// undefined unless analysis state is `success`.
body: Body,
state: Analysis,
@@ -373,47 +363,49 @@ pub const Scope = struct {
return @fieldParentPtr(T, "base", base);
}
- /// Asserts the scope has a parent which is a DeclAnalysis and
- /// returns the arena Allocator.
+ /// Returns the arena Allocator associated with the Decl of the Scope.
pub fn arena(self: *Scope) *Allocator {
switch (self.tag) {
.block => return self.cast(Block).?.arena,
- .decl => return &self.cast(DeclAnalysis).?.arena.allocator,
.gen_zir => return self.cast(GenZIR).?.arena,
.local_val => return self.cast(LocalVal).?.gen_zir.arena,
.local_ptr => return self.cast(LocalPtr).?.gen_zir.arena,
- .zir_module => return &self.cast(ZIRModule).?.contents.module.arena.allocator,
.file => unreachable,
.container => unreachable,
}
}
- /// If the scope has a parent which is a `DeclAnalysis`,
- /// returns the `Decl`, otherwise returns `null`.
- pub fn decl(self: *Scope) ?*Decl {
+ pub fn ownerDecl(self: *Scope) ?*Decl {
+ return switch (self.tag) {
+ .block => self.cast(Block).?.owner_decl,
+ .gen_zir => self.cast(GenZIR).?.decl,
+ .local_val => self.cast(LocalVal).?.gen_zir.decl,
+ .local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
+ .file => null,
+ .container => null,
+ };
+ }
+
+ pub fn srcDecl(self: *Scope) ?*Decl {
return switch (self.tag) {
- .block => self.cast(Block).?.decl,
+ .block => self.cast(Block).?.src_decl,
.gen_zir => self.cast(GenZIR).?.decl,
.local_val => self.cast(LocalVal).?.gen_zir.decl,
.local_ptr => self.cast(LocalPtr).?.gen_zir.decl,
- .decl => self.cast(DeclAnalysis).?.decl,
- .zir_module => null,
.file => null,
.container => null,
};
}
- /// Asserts the scope has a parent which is a ZIRModule or Container and
- /// returns it.
- pub fn namespace(self: *Scope) *Scope {
+ /// Asserts the scope has a parent which is a Container and returns it.
+ pub fn namespace(self: *Scope) *Container {
switch (self.tag) {
- .block => return self.cast(Block).?.decl.scope,
- .gen_zir => return self.cast(GenZIR).?.decl.scope,
- .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope,
- .decl => return self.cast(DeclAnalysis).?.decl.scope,
- .file => return &self.cast(File).?.root_container.base,
- .zir_module, .container => return self,
+ .block => return self.cast(Block).?.owner_decl.container,
+ .gen_zir => return self.cast(GenZIR).?.decl.container,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.container,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container,
+ .file => return &self.cast(File).?.root_container,
+ .container => return self.cast(Container).?,
}
}
@@ -426,9 +418,7 @@ pub const Scope = struct {
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
- .decl => unreachable,
.file => unreachable,
- .zir_module => return self.cast(ZIRModule).?.fullyQualifiedNameHash(name),
.container => return self.cast(Container).?.fullyQualifiedNameHash(name),
}
}
@@ -437,12 +427,10 @@ pub const Scope = struct {
pub fn tree(self: *Scope) *ast.Tree {
switch (self.tag) {
.file => return self.cast(File).?.contents.tree,
- .zir_module => unreachable,
- .decl => return self.cast(DeclAnalysis).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .block => return self.cast(Block).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .gen_zir => return self.cast(GenZIR).?.decl.scope.cast(Container).?.file_scope.contents.tree,
- .local_val => return self.cast(LocalVal).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
- .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.scope.cast(Container).?.file_scope.contents.tree,
+ .block => return self.cast(Block).?.src_decl.container.file_scope.contents.tree,
+ .gen_zir => return self.cast(GenZIR).?.decl.container.file_scope.contents.tree,
+ .local_val => return self.cast(LocalVal).?.gen_zir.decl.container.file_scope.contents.tree,
+ .local_ptr => return self.cast(LocalPtr).?.gen_zir.decl.container.file_scope.contents.tree,
.container => return self.cast(Container).?.file_scope.contents.tree,
}
}
@@ -454,38 +442,21 @@ pub const Scope = struct {
.gen_zir => self.cast(GenZIR).?,
.local_val => return self.cast(LocalVal).?.gen_zir,
.local_ptr => return self.cast(LocalPtr).?.gen_zir,
- .decl => unreachable,
- .zir_module => unreachable,
.file => unreachable,
.container => unreachable,
};
}
- /// Asserts the scope has a parent which is a ZIRModule, Container or File and
+ /// Asserts the scope has a parent which is a Container or File and
/// returns the sub_file_path field.
pub fn subFilePath(base: *Scope) []const u8 {
switch (base.tag) {
.container => return @fieldParentPtr(Container, "base", base).file_scope.sub_file_path,
.file => return @fieldParentPtr(File, "base", base).sub_file_path,
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).sub_file_path,
.block => unreachable,
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- pub fn unload(base: *Scope, gpa: *Allocator) void {
- switch (base.tag) {
- .file => return @fieldParentPtr(File, "base", base).unload(gpa),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).unload(gpa),
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .container => unreachable,
}
}
@@ -493,67 +464,28 @@ pub const Scope = struct {
switch (base.tag) {
.container => return @fieldParentPtr(Container, "base", base).file_scope.getSource(module),
.file => return @fieldParentPtr(File, "base", base).getSource(module),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).getSource(module),
.gen_zir => unreachable,
.local_val => unreachable,
.local_ptr => unreachable,
.block => unreachable,
- .decl => unreachable,
}
}
+ /// When called from inside a Block Scope, chases the src_decl, not the owner_decl.
pub fn getFileScope(base: *Scope) *Scope.File {
var cur = base;
while (true) {
cur = switch (cur.tag) {
.container => return @fieldParentPtr(Container, "base", cur).file_scope,
.file => return @fieldParentPtr(File, "base", cur),
- .zir_module => unreachable, // TODO are zir modules allowed to import packages?
.gen_zir => @fieldParentPtr(GenZIR, "base", cur).parent,
.local_val => @fieldParentPtr(LocalVal, "base", cur).parent,
.local_ptr => @fieldParentPtr(LocalPtr, "base", cur).parent,
- .block => @fieldParentPtr(Block, "base", cur).decl.scope,
- .decl => @fieldParentPtr(DeclAnalysis, "base", cur).decl.scope,
+ .block => return @fieldParentPtr(Block, "base", cur).src_decl.container.file_scope,
};
}
}
- /// Asserts the scope is a namespace Scope and removes the Decl from the namespace.
- pub fn removeDecl(base: *Scope, child: *Decl) void {
- switch (base.tag) {
- .container => return @fieldParentPtr(Container, "base", base).removeDecl(child),
- .zir_module => return @fieldParentPtr(ZIRModule, "base", base).removeDecl(child),
- .file => unreachable,
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- }
- }
-
- /// Asserts the scope is a File or ZIRModule and deinitializes it, then deallocates it.
- pub fn destroy(base: *Scope, gpa: *Allocator) void {
- switch (base.tag) {
- .file => {
- const scope_file = @fieldParentPtr(File, "base", base);
- scope_file.deinit(gpa);
- gpa.destroy(scope_file);
- },
- .zir_module => {
- const scope_zir_module = @fieldParentPtr(ZIRModule, "base", base);
- scope_zir_module.deinit(gpa);
- gpa.destroy(scope_zir_module);
- },
- .block => unreachable,
- .gen_zir => unreachable,
- .local_val => unreachable,
- .local_ptr => unreachable,
- .decl => unreachable,
- .container => unreachable,
- }
- }
-
fn name_hash_hash(x: NameHash) u32 {
return @truncate(u32, @bitCast(u128, x));
}
@@ -563,14 +495,11 @@ pub const Scope = struct {
}
pub const Tag = enum {
- /// .zir source code.
- zir_module,
/// .zig source code.
file,
/// struct, enum or union, every .file contains one of these.
container,
block,
- decl,
gen_zir,
local_val,
local_ptr,
@@ -657,6 +586,11 @@ pub const Scope = struct {
self.* = undefined;
}
+ pub fn destroy(self: *File, gpa: *Allocator) void {
+ self.deinit(gpa);
+ gpa.destroy(self);
+ }
+
pub fn dumpSrc(self: *File, src: usize) void {
const loc = std.zig.findLineColumn(self.source.bytes, src);
std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
@@ -681,109 +615,6 @@ pub const Scope = struct {
}
};
- pub const ZIRModule = struct {
- pub const base_tag: Tag = .zir_module;
- base: Scope = Scope{ .tag = base_tag },
- /// Relative to the owning package's root_src_dir.
- /// Reference to external memory, not owned by ZIRModule.
- sub_file_path: []const u8,
- source: union(enum) {
- unloaded: void,
- bytes: [:0]const u8,
- },
- contents: union {
- not_available: void,
- module: *zir.Module,
- },
- status: enum {
- never_loaded,
- unloaded_success,
- unloaded_parse_failure,
- unloaded_sema_failure,
-
- loaded_sema_failure,
- loaded_success,
- },
-
- /// Even though .zir files only have 1 module, this set is still needed
- /// because of anonymous Decls, which can exist in the global set, but
- /// not this one.
- decls: ArrayListUnmanaged(*Decl),
-
- pub fn unload(self: *ZIRModule, gpa: *Allocator) void {
- switch (self.status) {
- .never_loaded,
- .unloaded_parse_failure,
- .unloaded_sema_failure,
- .unloaded_success,
- => {},
-
- .loaded_success => {
- self.contents.module.deinit(gpa);
- gpa.destroy(self.contents.module);
- self.contents = .{ .not_available = {} };
- self.status = .unloaded_success;
- },
- .loaded_sema_failure => {
- self.contents.module.deinit(gpa);
- gpa.destroy(self.contents.module);
- self.contents = .{ .not_available = {} };
- self.status = .unloaded_sema_failure;
- },
- }
- switch (self.source) {
- .bytes => |bytes| {
- gpa.free(bytes);
- self.source = .{ .unloaded = {} };
- },
- .unloaded => {},
- }
- }
-
- pub fn deinit(self: *ZIRModule, gpa: *Allocator) void {
- self.decls.deinit(gpa);
- self.unload(gpa);
- self.* = undefined;
- }
-
- pub fn removeDecl(self: *ZIRModule, child: *Decl) void {
- for (self.decls.items) |item, i| {
- if (item == child) {
- _ = self.decls.swapRemove(i);
- return;
- }
- }
- }
-
- pub fn dumpSrc(self: *ZIRModule, src: usize) void {
- const loc = std.zig.findLineColumn(self.source.bytes, src);
- std.debug.print("{s}:{d}:{d}\n", .{ self.sub_file_path, loc.line + 1, loc.column + 1 });
- }
-
- pub fn getSource(self: *ZIRModule, module: *Module) ![:0]const u8 {
- switch (self.source) {
- .unloaded => {
- const source = try module.root_pkg.root_src_directory.handle.readFileAllocOptions(
- module.gpa,
- self.sub_file_path,
- std.math.maxInt(u32),
- null,
- 1,
- 0,
- );
- self.source = .{ .bytes = source };
- return source;
- },
- .bytes => |bytes| return bytes,
- }
- }
-
- pub fn fullyQualifiedNameHash(self: *ZIRModule, name: []const u8) NameHash {
- // ZIR modules only have 1 file with all decls global in the same namespace.
- return std.zig.hashSrc(name);
- }
- };
-
/// This is a temporary structure, references to it are valid only
/// during semantic analysis of the block.
pub const Block = struct {
@@ -794,9 +625,14 @@ pub const Scope = struct {
/// Maps ZIR to TZIR. Shared to sub-blocks.
inst_table: *InstTable,
func: ?*Fn,
- decl: *Decl,
+ /// When analyzing an inline function call, owner_decl is the Decl of the caller
+ /// and src_decl is the Decl of the callee.
+ /// This Decl owns the arena memory of this Block.
+ owner_decl: *Decl,
+ /// This Decl is the Decl according to the Zig source code corresponding to this Block.
+ src_decl: *Decl,
instructions: ArrayListUnmanaged(*Inst),
- /// Points to the arena allocator of DeclAnalysis
+ /// Points to the arena allocator of the Decl.
arena: *Allocator,
label: ?Label = null,
inlining: ?*Inlining,
@@ -847,19 +683,10 @@ pub const Scope = struct {
/// This is a temporary structure, references to it are valid only
/// during semantic analysis of the decl.
- pub const DeclAnalysis = struct {
- pub const base_tag: Tag = .decl;
- base: Scope = Scope{ .tag = base_tag },
- decl: *Decl,
- arena: std.heap.ArenaAllocator,
- };
-
- /// This is a temporary structure, references to it are valid only
- /// during semantic analysis of the decl.
pub const GenZIR = struct {
pub const base_tag: Tag = .gen_zir;
base: Scope = Scope{ .tag = base_tag },
- /// Parents can be: `GenZIR`, `ZIRModule`, `File`
+ /// Parents can be: `GenZIR`, `File`
parent: *Scope,
decl: *Decl,
arena: *Allocator,
@@ -905,11 +732,73 @@ pub const Scope = struct {
};
};
+/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
+/// Its memory is managed with the general purpose allocator so that they
+/// can be created and destroyed in response to incremental updates.
+/// In some cases, the Scope.File could have been inferred from where the ErrorMsg
+/// is stored. For example, if it is stored in Module.failed_decls, then the Scope.File
+/// would be determined by the Decl Scope. However, the data structure contains the field
+/// anyway so that `ErrorMsg` can be reused for error notes, which may be in a different
+/// file than the parent error message. It also simplifies processing of error messages.
+pub const ErrorMsg = struct {
+ src_loc: SrcLoc,
+ msg: []const u8,
+ notes: []ErrorMsg = &.{},
+
+ pub fn create(
+ gpa: *Allocator,
+ src_loc: SrcLoc,
+ comptime format: []const u8,
+ args: anytype,
+ ) !*ErrorMsg {
+ const self = try gpa.create(ErrorMsg);
+ errdefer gpa.destroy(self);
+ self.* = try init(gpa, src_loc, format, args);
+ return self;
+ }
+
+ /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`,
+ /// as well as all notes.
+ pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void {
+ self.deinit(gpa);
+ gpa.destroy(self);
+ }
+
+ pub fn init(
+ gpa: *Allocator,
+ src_loc: SrcLoc,
+ comptime format: []const u8,
+ args: anytype,
+ ) !ErrorMsg {
+ return ErrorMsg{
+ .src_loc = src_loc,
+ .msg = try std.fmt.allocPrint(gpa, format, args),
+ };
+ }
+
+ pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void {
+ for (self.notes) |*note| {
+ note.deinit(gpa);
+ }
+ gpa.free(self.notes);
+ gpa.free(self.msg);
+ self.* = undefined;
+ }
+};
+
+/// Canonical reference to a position within a source file.
+pub const SrcLoc = struct {
+ file_scope: *Scope.File,
+ byte_offset: usize,
+};
+
pub const InnerError = error{ OutOfMemory, AnalysisFail };
pub fn deinit(self: *Module) void {
const gpa = self.gpa;
+ self.compile_log_text.deinit(gpa);
+
self.zig_cache_artifact_directory.handle.close();
self.deletion_set.deinit(gpa);
@@ -939,9 +828,6 @@ pub fn deinit(self: *Module) void {
}
self.failed_exports.deinit(gpa);
- for (self.compile_log_decls.items()) |*entry| {
- entry.value.deinit(gpa);
- }
self.compile_log_decls.deinit(gpa);
for (self.decl_exports.items()) |entry| {
@@ -965,7 +851,7 @@ pub fn deinit(self: *Module) void {
self.global_error_set.deinit(gpa);
for (self.import_table.items()) |entry| {
- entry.value.base.destroy(gpa);
+ entry.value.destroy(gpa);
}
self.import_table.deinit(gpa);
}
@@ -978,7 +864,7 @@ fn freeExportList(gpa: *Allocator, export_list: []*Export) void {
gpa.free(export_list);
}
-pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
+pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void {
const tracy = trace(@src());
defer tracy.end();
@@ -999,7 +885,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
// The exports this Decl performs will be re-discovered, so we remove them here
// prior to re-analysis.
- self.deleteDeclExports(decl);
+ mod.deleteDeclExports(decl);
// Dependencies will be re-discovered, so we remove them here prior to re-analysis.
for (decl.dependencies.items()) |entry| {
const dep = entry.key;
@@ -1008,7 +894,7 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
// We don't perform a deletion here, because this Decl or another one
// may end up referencing it before the update is complete.
dep.deletion_flag = true;
- try self.deletion_set.append(self.gpa, dep);
+ try mod.deletion_set.append(mod.gpa, dep);
}
}
decl.dependencies.clearRetainingCapacity();
@@ -1019,24 +905,21 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
.unreferenced => false,
};
- const type_changed = if (self.root_scope.cast(Scope.ZIRModule)) |zir_module|
- try zir_sema.analyzeZirDecl(self, decl, zir_module.contents.module.decls[decl.src_index])
- else
- self.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => return error.AnalysisFail,
- else => {
- try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
- self.failed_decls.putAssumeCapacityNoClobber(decl, try Compilation.ErrorMsg.create(
- self.gpa,
- decl.src(),
- "unable to analyze: {s}",
- .{@errorName(err)},
- ));
- decl.analysis = .sema_failure_retryable;
- return error.AnalysisFail;
- },
- };
+ const type_changed = mod.astGenAndAnalyzeDecl(decl) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => return error.AnalysisFail,
+ else => {
+ decl.analysis = .sema_failure_retryable;
+ try mod.failed_decls.ensureCapacity(mod.gpa, mod.failed_decls.items().len + 1);
+ mod.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create(
+ mod.gpa,
+ decl.srcLoc(),
+ "unable to analyze: {s}",
+ .{@errorName(err)},
+ ));
+ return error.AnalysisFail;
+ },
+ };
if (subsequent_analysis) {
// We may need to chase the dependants and re-analyze them.
@@ -1055,8 +938,8 @@ pub fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void {
.codegen_failure,
.codegen_failure_retryable,
.complete,
- => if (dep.generation != self.generation) {
- try self.markOutdatedDecl(dep);
+ => if (dep.generation != mod.generation) {
+ try mod.markOutdatedDecl(dep);
},
}
}
@@ -1068,8 +951,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
const tracy = trace(@src());
defer tracy.end();
- const container_scope = decl.scope.cast(Scope.Container).?;
- const tree = try self.getAstTree(container_scope.file_scope);
+ const tree = try self.getAstTree(decl.container.file_scope);
const ast_node = tree.root_node.decls()[decl.src_index];
switch (ast_node.tag) {
.FnProto => {
@@ -1085,7 +967,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
var fn_type_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &fn_type_scope_arena.allocator,
- .parent = decl.scope,
+ .parent = &decl.container.base,
};
defer fn_type_scope.instructions.deinit(self.gpa);
@@ -1197,7 +1079,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.parent = null,
.inst_table = &inst_table,
.func = null,
- .decl = decl,
+ .owner_decl = decl,
+ .src_decl = decl,
.instructions = .{},
.arena = &decl_arena.allocator,
.inlining = null,
@@ -1242,12 +1125,12 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
const new_func = try decl_arena.allocator.create(Fn);
const fn_payload = try decl_arena.allocator.create(Value.Payload.Function);
- const fn_zir: zir.Module.Body = blk: {
+ const fn_zir: zir.Body = blk: {
// We put the ZIR inside the Decl arena.
var gen_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &decl_arena.allocator,
- .parent = decl.scope,
+ .parent = &decl.container.base,
};
defer gen_scope.instructions.deinit(self.gpa);
@@ -1400,7 +1283,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.parent = null,
.inst_table = &decl_inst_table,
.func = null,
- .decl = decl,
+ .owner_decl = decl,
+ .src_decl = decl,
.instructions = .{},
.arena = &decl_arena.allocator,
.inlining = null,
@@ -1444,7 +1328,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
var gen_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &gen_scope_arena.allocator,
- .parent = decl.scope,
+ .parent = &decl.container.base,
};
defer gen_scope.instructions.deinit(self.gpa);
@@ -1472,7 +1356,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.parent = null,
.inst_table = &var_inst_table,
.func = null,
- .decl = decl,
+ .owner_decl = decl,
+ .src_decl = decl,
.instructions = .{},
.arena = &gen_scope_arena.allocator,
.inlining = null,
@@ -1503,7 +1388,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
var type_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &type_scope_arena.allocator,
- .parent = decl.scope,
+ .parent = &decl.container.base,
};
defer type_scope.instructions.deinit(self.gpa);
@@ -1584,7 +1469,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
var gen_scope: Scope.GenZIR = .{
.decl = decl,
.arena = &analysis_arena.allocator,
- .parent = decl.scope,
+ .parent = &decl.container.base,
};
defer gen_scope.instructions.deinit(self.gpa);
@@ -1602,7 +1487,8 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool {
.parent = null,
.inst_table = &inst_table,
.func = null,
- .decl = decl,
+ .owner_decl = decl,
+ .src_decl = decl,
.instructions = .{},
.arena = &analysis_arena.allocator,
.inlining = null,
@@ -1632,44 +1518,6 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void
dependee.dependants.putAssumeCapacity(depender, {});
}
-fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module {
- switch (root_scope.status) {
- .never_loaded, .unloaded_success => {
- try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
-
- const source = try root_scope.getSource(self);
-
- var keep_zir_module = false;
- const zir_module = try self.gpa.create(zir.Module);
- defer if (!keep_zir_module) self.gpa.destroy(zir_module);
-
- zir_module.* = try zir.parse(self.gpa, source);
- defer if (!keep_zir_module) zir_module.deinit(self.gpa);
-
- if (zir_module.error_msg) |src_err_msg| {
- self.failed_files.putAssumeCapacityNoClobber(
- &root_scope.base,
- try Compilation.ErrorMsg.create(self.gpa, src_err_msg.byte_offset, "{s}", .{src_err_msg.msg}),
- );
- root_scope.status = .unloaded_parse_failure;
- return error.AnalysisFail;
- }
-
- root_scope.status = .loaded_success;
- root_scope.contents = .{ .module = zir_module };
- keep_zir_module = true;
-
- return zir_module;
- },
-
- .unloaded_parse_failure,
- .unloaded_sema_failure,
- => return error.AnalysisFail,
-
- .loaded_success, .loaded_sema_failure => return root_scope.contents.module,
- }
-}
-
pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
const tracy = trace(@src());
defer tracy.end();
@@ -1691,10 +1539,13 @@ pub fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree {
defer msg.deinit();
try parse_err.render(tree.token_ids, msg.writer());
- const err_msg = try self.gpa.create(Compilation.ErrorMsg);
+ const err_msg = try self.gpa.create(ErrorMsg);
err_msg.* = .{
+ .src_loc = .{
+ .file_scope = root_scope,
+ .byte_offset = tree.token_locs[parse_err.loc()].start,
+ },
.msg = msg.toOwnedSlice(),
- .byte_offset = tree.token_locs[parse_err.loc()].start,
};
self.failed_files.putAssumeCapacityNoClobber(&root_scope.base, err_msg);
@@ -1753,9 +1604,12 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
decl.src_index = decl_i;
if (deleted_decls.swapRemove(decl) == null) {
decl.analysis = .sema_failure;
- const err_msg = try Compilation.ErrorMsg.create(self.gpa, tree.token_locs[name_tok].start, "redefinition of '{s}'", .{decl.name});
- errdefer err_msg.destroy(self.gpa);
- try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
+ const msg = try ErrorMsg.create(self.gpa, .{
+ .file_scope = container_scope.file_scope,
+ .byte_offset = tree.token_locs[name_tok].start,
+ }, "redefinition of '{s}'", .{decl.name});
+ errdefer msg.destroy(self.gpa);
+ try self.failed_decls.putNoClobber(self.gpa, decl, msg);
} else {
if (!srcHashEql(decl.contents_hash, contents_hash)) {
try self.markOutdatedDecl(decl);
@@ -1795,7 +1649,10 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
decl.src_index = decl_i;
if (deleted_decls.swapRemove(decl) == null) {
decl.analysis = .sema_failure;
- const err_msg = try Compilation.ErrorMsg.create(self.gpa, name_loc.start, "redefinition of '{s}'", .{decl.name});
+ const err_msg = try ErrorMsg.create(self.gpa, .{
+ .file_scope = container_scope.file_scope,
+ .byte_offset = name_loc.start,
+ }, "redefinition of '{s}'", .{decl.name});
errdefer err_msg.destroy(self.gpa);
try self.failed_decls.putNoClobber(self.gpa, decl, err_msg);
} else if (!srcHashEql(decl.contents_hash, contents_hash)) {
@@ -1840,65 +1697,12 @@ pub fn analyzeContainer(self: *Module, container_scope: *Scope.Container) !void
}
}
-pub fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void {
- // We may be analyzing it for the first time, or this may be
- // an incremental update. This code handles both cases.
- const src_module = try self.getSrcModule(root_scope);
-
- try self.comp.work_queue.ensureUnusedCapacity(src_module.decls.len);
- try root_scope.decls.ensureCapacity(self.gpa, src_module.decls.len);
-
- var exports_to_resolve = std.ArrayList(*zir.Decl).init(self.gpa);
- defer exports_to_resolve.deinit();
-
- // Keep track of the decls that we expect to see in this file so that
- // we know which ones have been deleted.
- var deleted_decls = std.AutoArrayHashMap(*Decl, void).init(self.gpa);
- defer deleted_decls.deinit();
- try deleted_decls.ensureCapacity(self.decl_table.items().len);
- for (self.decl_table.items()) |entry| {
- deleted_decls.putAssumeCapacityNoClobber(entry.value, {});
- }
-
- for (src_module.decls) |src_decl, decl_i| {
- const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name);
- if (self.decl_table.get(name_hash)) |decl| {
- deleted_decls.removeAssertDiscard(decl);
- if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) {
- try self.markOutdatedDecl(decl);
- decl.contents_hash = src_decl.contents_hash;
- }
- } else {
- const new_decl = try self.createNewDecl(
- &root_scope.base,
- src_decl.name,
- decl_i,
- name_hash,
- src_decl.contents_hash,
- );
- root_scope.decls.appendAssumeCapacity(new_decl);
- if (src_decl.inst.cast(zir.Inst.Export)) |export_inst| {
- try exports_to_resolve.append(src_decl);
- }
- }
- }
- for (exports_to_resolve.items) |export_decl| {
- _ = try zir_sema.resolveZirDecl(self, &root_scope.base, export_decl);
- }
- // Handle explicitly deleted decls from the source code. Not to be confused
- // with when we delete decls because they are no longer referenced.
- for (deleted_decls.items()) |entry| {
- log.debug("noticed '{s}' deleted from source\n", .{entry.key.name});
- try self.deleteDecl(entry.key);
- }
-}
-
pub fn deleteDecl(self: *Module, decl: *Decl) !void {
try self.deletion_set.ensureCapacity(self.gpa, self.deletion_set.items.len + decl.dependencies.items().len);
// Remove from the namespace it resides in. In the case of an anonymous Decl it will
// not be present in the set, and this does nothing.
- decl.scope.removeDecl(decl);
+ decl.container.removeDecl(decl);
log.debug("deleting decl '{s}'\n", .{decl.name});
const name_hash = decl.fullyQualifiedNameHash();
@@ -1929,9 +1733,7 @@ pub fn deleteDecl(self: *Module, decl: *Decl) !void {
if (self.emit_h_failed_decls.swapRemove(decl)) |entry| {
entry.value.destroy(self.gpa);
}
- if (self.compile_log_decls.swapRemove(decl)) |*entry| {
- entry.value.deinit(self.gpa);
- }
+ _ = self.compile_log_decls.swapRemove(decl);
self.deleteDeclExports(decl);
self.comp.bin_file.freeDecl(decl);
@@ -1993,7 +1795,8 @@ pub fn analyzeFnBody(self: *Module, decl: *Decl, func: *Fn) !void {
.parent = null,
.inst_table = &inst_table,
.func = func,
- .decl = decl,
+ .owner_decl = decl,
+ .src_decl = decl,
.instructions = .{},
.arena = &arena.allocator,
.inlining = null,
@@ -2022,9 +1825,7 @@ fn markOutdatedDecl(self: *Module, decl: *Decl) !void {
if (self.emit_h_failed_decls.swapRemove(decl)) |entry| {
entry.value.destroy(self.gpa);
}
- if (self.compile_log_decls.swapRemove(decl)) |*entry| {
- entry.value.deinit(self.gpa);
- }
+ _ = self.compile_log_decls.swapRemove(decl);
decl.analysis = .outdated;
}
@@ -2046,7 +1847,7 @@ fn allocateNewDecl(
new_decl.* = .{
.name = "",
- .scope = scope.namespace(),
+ .container = scope.namespace(),
.src_index = src_index,
.typed_value = .{ .never_succeeded = {} },
.analysis = .unreferenced,
@@ -2129,34 +1930,34 @@ pub fn resolveDefinedValue(self: *Module, scope: *Scope, base: *Inst) !?Value {
}
pub fn analyzeExport(
- self: *Module,
+ mod: *Module,
scope: *Scope,
src: usize,
borrowed_symbol_name: []const u8,
exported_decl: *Decl,
) !void {
- try self.ensureDeclAnalyzed(exported_decl);
+ try mod.ensureDeclAnalyzed(exported_decl);
const typed_value = exported_decl.typed_value.most_recent.typed_value;
switch (typed_value.ty.zigTypeTag()) {
.Fn => {},
- else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
+ else => return mod.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}),
}
- try self.decl_exports.ensureCapacity(self.gpa, self.decl_exports.items().len + 1);
- try self.export_owners.ensureCapacity(self.gpa, self.export_owners.items().len + 1);
+ try mod.decl_exports.ensureCapacity(mod.gpa, mod.decl_exports.items().len + 1);
+ try mod.export_owners.ensureCapacity(mod.gpa, mod.export_owners.items().len + 1);
- const new_export = try self.gpa.create(Export);
- errdefer self.gpa.destroy(new_export);
+ const new_export = try mod.gpa.create(Export);
+ errdefer mod.gpa.destroy(new_export);
- const symbol_name = try self.gpa.dupe(u8, borrowed_symbol_name);
- errdefer self.gpa.free(symbol_name);
+ const symbol_name = try mod.gpa.dupe(u8, borrowed_symbol_name);
+ errdefer mod.gpa.free(symbol_name);
- const owner_decl = scope.decl().?;
+ const owner_decl = scope.ownerDecl().?;
new_export.* = .{
.options = .{ .name = symbol_name },
.src = src,
- .link = switch (self.comp.bin_file.tag) {
+ .link = switch (mod.comp.bin_file.tag) {
.coff => .{ .coff = {} },
.elf => .{ .elf = link.File.Elf.Export{} },
.macho => .{ .macho = link.File.MachO.Export{} },
@@ -2169,48 +1970,53 @@ pub fn analyzeExport(
};
// Add to export_owners table.
- const eo_gop = self.export_owners.getOrPutAssumeCapacity(owner_decl);
+ const eo_gop = mod.export_owners.getOrPutAssumeCapacity(owner_decl);
if (!eo_gop.found_existing) {
eo_gop.entry.value = &[0]*Export{};
}
- eo_gop.entry.value = try self.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
+ eo_gop.entry.value = try mod.gpa.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1);
eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export;
- errdefer eo_gop.entry.value = self.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
+ errdefer eo_gop.entry.value = mod.gpa.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1);
// Add to exported_decl table.
- const de_gop = self.decl_exports.getOrPutAssumeCapacity(exported_decl);
+ const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl);
if (!de_gop.found_existing) {
de_gop.entry.value = &[0]*Export{};
}
- de_gop.entry.value = try self.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
+ de_gop.entry.value = try mod.gpa.realloc(de_gop.entry.value, de_gop.entry.value.len + 1);
de_gop.entry.value[de_gop.entry.value.len - 1] = new_export;
- errdefer de_gop.entry.value = self.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
+ errdefer de_gop.entry.value = mod.gpa.shrink(de_gop.entry.value, de_gop.entry.value.len - 1);
- if (self.symbol_exports.get(symbol_name)) |_| {
- try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
- self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
- self.gpa,
+ if (mod.symbol_exports.get(symbol_name)) |other_export| {
+ new_export.status = .failed_retryable;
+ try mod.failed_exports.ensureCapacity(mod.gpa, mod.failed_exports.items().len + 1);
+ const msg = try mod.errMsg(
+ scope,
src,
"exported symbol collision: {s}",
.{symbol_name},
- ));
- // TODO: add a note
+ );
+ errdefer msg.destroy(mod.gpa);
+ try mod.errNote(
+ &other_export.owner_decl.container.base,
+ other_export.src,
+ msg,
+ "other symbol here",
+ .{},
+ );
+ mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
new_export.status = .failed;
return;
}
- try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export);
- self.comp.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) {
+ try mod.symbol_exports.putNoClobber(mod.gpa, symbol_name, new_export);
+ mod.comp.bin_file.updateDeclExports(mod, exported_decl, de_gop.entry.value) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => {
- try self.failed_exports.ensureCapacity(self.gpa, self.failed_exports.items().len + 1);
- self.failed_exports.putAssumeCapacityNoClobber(new_export, try Compilation.ErrorMsg.create(
- self.gpa,
- src,
- "unable to export: {s}",
- .{@errorName(err)},
- ));
new_export.status = .failed_retryable;
+ try mod.failed_exports.ensureCapacity(mod.gpa, mod.failed_exports.items().len + 1);
+ const msg = try mod.errMsg(scope, src, "unable to export: {s}", .{@errorName(err)});
+ mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
},
};
}
@@ -2476,7 +2282,7 @@ pub fn createAnonymousDecl(
typed_value: TypedValue,
) !*Decl {
const name_index = self.getNextAnonNameIndex();
- const scope_decl = scope.decl().?;
+ const scope_decl = scope.ownerDecl().?;
const name = try std.fmt.allocPrint(self.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index });
defer self.gpa.free(name);
const name_hash = scope.namespace().fullyQualifiedNameHash(name);
@@ -2512,7 +2318,7 @@ pub fn createContainerDecl(
decl_arena: *std.heap.ArenaAllocator,
typed_value: TypedValue,
) !*Decl {
- const scope_decl = scope.decl().?;
+ const scope_decl = scope.ownerDecl().?;
const name = try self.getAnonTypeName(scope, base_token);
defer self.gpa.free(name);
const name_hash = scope.namespace().fullyQualifiedNameHash(name);
@@ -2558,14 +2364,14 @@ pub fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*De
}
pub fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst {
- const scope_decl = scope.decl().?;
+ const scope_decl = scope.ownerDecl().?;
try self.declareDeclDependency(scope_decl, decl);
self.ensureDeclAnalyzed(decl) catch |err| {
if (scope.cast(Scope.Block)) |block| {
if (block.func) |func| {
func.state = .dependency_failure;
} else {
- block.decl.analysis = .dependency_failure;
+ block.owner_decl.analysis = .dependency_failure;
}
} else {
scope_decl.analysis = .dependency_failure;
@@ -3217,10 +3023,51 @@ fn coerceArrayPtrToMany(self: *Module, scope: *Scope, dest_type: Type, inst: *In
return self.fail(scope, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{});
}
-pub fn fail(self: *Module, scope: *Scope, src: usize, comptime format: []const u8, args: anytype) InnerError {
- @setCold(true);
- const err_msg = try Compilation.ErrorMsg.create(self.gpa, src, format, args);
- return self.failWithOwnedErrorMsg(scope, src, err_msg);
+/// We don't return a pointer to the new error note because the pointer
+/// becomes invalid when you add another one.
+pub fn errNote(
+ mod: *Module,
+ scope: *Scope,
+ src: usize,
+ parent: *ErrorMsg,
+ comptime format: []const u8,
+ args: anytype,
+) error{OutOfMemory}!void {
+ const msg = try std.fmt.allocPrint(mod.gpa, format, args);
+ errdefer mod.gpa.free(msg);
+
+ parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1);
+ parent.notes[parent.notes.len - 1] = .{
+ .src_loc = .{
+ .file_scope = scope.getFileScope(),
+ .byte_offset = src,
+ },
+ .msg = msg,
+ };
+}
+
+pub fn errMsg(
+ mod: *Module,
+ scope: *Scope,
+ src_byte_offset: usize,
+ comptime format: []const u8,
+ args: anytype,
+) error{OutOfMemory}!*ErrorMsg {
+ return ErrorMsg.create(mod.gpa, .{
+ .file_scope = scope.getFileScope(),
+ .byte_offset = src_byte_offset,
+ }, format, args);
+}
+
+pub fn fail(
+ mod: *Module,
+ scope: *Scope,
+ src_byte_offset: usize,
+ comptime format: []const u8,
+ args: anytype,
+) InnerError {
+ const err_msg = try mod.errMsg(scope, src_byte_offset, format, args);
+ return mod.failWithOwnedErrorMsg(scope, err_msg);
}
pub fn failTok(
@@ -3230,7 +3077,6 @@ pub fn failTok(
comptime format: []const u8,
args: anytype,
) InnerError {
- @setCold(true);
const src = scope.tree().token_locs[token_index].start;
return self.fail(scope, src, format, args);
}
@@ -3242,80 +3088,36 @@ pub fn failNode(
comptime format: []const u8,
args: anytype,
) InnerError {
- @setCold(true);
const src = scope.tree().token_locs[ast_node.firstToken()].start;
return self.fail(scope, src, format, args);
}
-fn addCompileLog(self: *Module, decl: *Decl, src: usize) error{OutOfMemory}!void {
- const entry = try self.compile_log_decls.getOrPutValue(self.gpa, decl, .{});
- try entry.value.append(self.gpa, src);
-}
-
-pub fn failCompileLog(
- self: *Module,
- scope: *Scope,
- src: usize,
-) InnerError!void {
- switch (scope.tag) {
- .decl => {
- const decl = scope.cast(Scope.DeclAnalysis).?.decl;
- try self.addCompileLog(decl, src);
- },
- .block => {
- const block = scope.cast(Scope.Block).?;
- try self.addCompileLog(block.decl, src);
- },
- .gen_zir => {
- const gen_zir = scope.cast(Scope.GenZIR).?;
- try self.addCompileLog(gen_zir.decl, src);
- },
- .local_val => {
- const gen_zir = scope.cast(Scope.LocalVal).?.gen_zir;
- try self.addCompileLog(gen_zir.decl, src);
- },
- .local_ptr => {
- const gen_zir = scope.cast(Scope.LocalPtr).?.gen_zir;
- try self.addCompileLog(gen_zir.decl, src);
- },
- .zir_module,
- .file,
- .container,
- => unreachable,
- }
-}
-
-fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Compilation.ErrorMsg) InnerError {
+pub fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, err_msg: *ErrorMsg) InnerError {
+ @setCold(true);
{
errdefer err_msg.destroy(self.gpa);
try self.failed_decls.ensureCapacity(self.gpa, self.failed_decls.items().len + 1);
try self.failed_files.ensureCapacity(self.gpa, self.failed_files.items().len + 1);
}
switch (scope.tag) {
- .decl => {
- const decl = scope.cast(Scope.DeclAnalysis).?.decl;
- decl.analysis = .sema_failure;
- decl.generation = self.generation;
- self.failed_decls.putAssumeCapacityNoClobber(decl, err_msg);
- },
.block => {
const block = scope.cast(Scope.Block).?;
if (block.inlining) |inlining| {
if (inlining.shared.caller) |func| {
func.state = .sema_failure;
} else {
- block.decl.analysis = .sema_failure;
- block.decl.generation = self.generation;
+ block.owner_decl.analysis = .sema_failure;
+ block.owner_decl.generation = self.generation;
}
} else {
if (block.func) |func| {
func.state = .sema_failure;
} else {
- block.decl.analysis = .sema_failure;
- block.decl.generation = self.generation;
+ block.owner_decl.analysis = .sema_failure;
+ block.owner_decl.generation = self.generation;
}
}
- self.failed_decls.putAssumeCapacityNoClobber(block.decl, err_msg);
+ self.failed_decls.putAssumeCapacityNoClobber(block.owner_decl, err_msg);
},
.gen_zir => {
const gen_zir = scope.cast(Scope.GenZIR).?;
@@ -3335,11 +3137,6 @@ fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *Com
gen_zir.decl.generation = self.generation;
self.failed_decls.putAssumeCapacityNoClobber(gen_zir.decl, err_msg);
},
- .zir_module => {
- const zir_module = scope.cast(Scope.ZIRModule).?;
- zir_module.status = .loaded_sema_failure;
- self.failed_files.putAssumeCapacityNoClobber(scope, err_msg);
- },
.file => unreachable,
.container => unreachable,
}
@@ -3671,7 +3468,8 @@ pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic
.parent = parent_block,
.inst_table = parent_block.inst_table,
.func = parent_block.func,
- .decl = parent_block.decl,
+ .owner_decl = parent_block.owner_decl,
+ .src_decl = parent_block.src_decl,
.instructions = .{},
.arena = parent_block.arena,
.inlining = parent_block.inlining,