diff options
| author | mlugg <mlugg@mlugg.co.uk> | 2022-11-27 14:17:55 +0000 |
|---|---|---|
| committer | mlugg <mlugg@mlugg.co.uk> | 2023-01-22 19:00:03 +0000 |
| commit | 5f9186d0ce78ce1eb7db9634849b38d2e90d071e (patch) | |
| tree | ab46e6e77fe60874eb9f52779196ecbd8e4deabc /src/Module.zig | |
| parent | 6d71d79dc27ddd6f66913a34fd6cd40691a8c959 (diff) | |
| download | zig-5f9186d0ce78ce1eb7db9634849b38d2e90d071e.tar.gz zig-5f9186d0ce78ce1eb7db9634849b38d2e90d071e.zip | |
AstGen: detect and error on files included in multiple packages
Previously, if a source file was referenced from multiple packages, it
just became owned by the first one AstGen happened to reach; this was a
problem, because it could lead to inconsistent behaviour in the compiler
based on a race condition. This could be fixed by just analyzing such
files multiple times - however, it was pointed out by Andrew that it
might make more sense to enforce files being part of at most a single
package. Having a file in multiple packages would not only impact
compile times (due to Sema having to run multiple times on potentially a
lot of code) but is also a confusing anti-pattern which more often than
not is a mistake on the part of the user.
Resolves: #13662
Diffstat (limited to 'src/Module.zig')
| -rw-r--r-- | src/Module.zig | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/src/Module.zig b/src/Module.zig index 93325bfec5..b17c140231 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1943,6 +1943,10 @@ pub const File = struct { zir: Zir, /// Package that this file is a part of, managed externally. pkg: *Package, + /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen. + multi_pkg: bool = false, + /// List of references to this file, used for multi-package errors. + references: std.ArrayListUnmanaged(Reference) = .{}, /// Used by change detection algorithm, after astgen, contains the /// set of decls that existed in the previous ZIR but not in the new one. @@ -1958,6 +1962,14 @@ pub const File = struct { /// successful, this field is unloaded. prev_zir: ?*Zir = null, + /// A single reference to a file. + const Reference = union(enum) { + /// The file is imported directly (i.e. not as a package) with @import. + import: SrcLoc, + /// The file is the root of a package. + root: *Package, + }; + pub fn unload(file: *File, gpa: Allocator) void { file.unloadTree(gpa); file.unloadSource(gpa); @@ -1990,6 +2002,7 @@ pub const File = struct { log.debug("deinit File {s}", .{file.sub_file_path}); file.deleted_decls.deinit(gpa); file.outdated_decls.deinit(gpa); + file.references.deinit(gpa); if (file.root_decl.unwrap()) |root_decl| { mod.destroyDecl(root_decl); } @@ -2110,6 +2123,44 @@ pub const File = struct { else => true, }; } + + /// Add a reference to this file during AstGen. + pub fn addReference(file: *File, mod: Module, ref: Reference) !void { + try file.references.append(mod.gpa, ref); + + const pkg = switch (ref) { + .import => |loc| loc.file_scope.pkg, + .root => |pkg| pkg, + }; + if (pkg != file.pkg) file.multi_pkg = true; + } + + /// Mark this file and every file referenced by it as multi_pkg and report an + /// astgen_failure error for them. AstGen must have completed in its entirety. + pub fn recursiveMarkMultiPkg(file: *File, mod: *Module) void { + file.multi_pkg = true; + file.status = .astgen_failure; + + std.debug.assert(file.zir_loaded); + const imports_index = file.zir.extra[@enumToInt(Zir.ExtraIndex.imports)]; + if (imports_index == 0) return; + const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); + + var import_i: u32 = 0; + var extra_index = extra.end; + while (import_i < extra.data.imports_len) : (import_i += 1) { + const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index); + extra_index = item.end; + + const import_path = file.zir.nullTerminatedString(item.data.name); + if (mem.eql(u8, import_path, "builtin")) continue; + + const res = mod.importFile(file, import_path) catch continue; + if (!res.is_pkg and !res.file.multi_pkg) { + res.file.recursiveMarkMultiPkg(mod); + } + } + } }; /// Represents the contents of a file loaded with `@embedFile`. @@ -4690,6 +4741,7 @@ pub fn declareDeclDependency(mod: *Module, depender_index: Decl.Index, dependee_ pub const ImportFileResult = struct { file: *File, is_new: bool, + is_pkg: bool, }; pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { @@ -4709,6 +4761,7 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { if (gop.found_existing) return ImportFileResult{ .file = gop.value_ptr.*, .is_new = false, + .is_pkg = true, }; const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); @@ -4732,9 +4785,11 @@ pub fn importPkg(mod: *Module, pkg: *Package) !ImportFileResult { .pkg = pkg, .root_decl = .none, }; + try new_file.addReference(mod.*, .{ .root = pkg }); return ImportFileResult{ .file = new_file, .is_new = true, + .is_pkg = true, }; } @@ -4775,6 +4830,7 @@ pub fn importFile( if (gop.found_existing) return ImportFileResult{ .file = gop.value_ptr.*, .is_new = false, + .is_pkg = false, }; const new_file = try gpa.create(File); @@ -4820,6 +4876,7 @@ pub fn importFile( return ImportFileResult{ .file = new_file, .is_new = true, + .is_pkg = false, }; } |
