authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-16 19:45:58-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-16 19:45:58-07:00
loge13fc6b119c5593b4fccd5ae064c6b251bbe50e7
treef54c3357f9c9b3ca0e34635dab09f4000bbf2c5b
parent415ef1be510e912ddbd3b73a4d20aac3fda27a0e

stage2: make `@import` relative to the current file

previously, it was incorrectly relative to the package directory

3 files changed, 86 insertions(+), 21 deletions(-)

src/Compilation.zig+4-2
...@@ -1535,7 +1535,8 @@ pub fn update(self: *Compilation) !void {...@@ -1535,7 +1535,8 @@ pub fn update(self: *Compilation) !void {
15351535
1536 // Make sure std.zig is inside the import_table. We unconditionally need1536 // Make sure std.zig is inside the import_table. We unconditionally need
1537 // it for start.zig.1537 // it for start.zig.
1538 _ = try module.importFile(module.root_pkg, "std");1538 const std_pkg = module.root_pkg.table.get("std").?;
1539 _ = try module.importPkg(module.root_pkg, std_pkg);
15391540
1540 // Put a work item in for every known source file to detect if1541 // Put a work item in for every known source file to detect if
1541 // it changed, and, if so, re-compute ZIR and then queue the job1542 // it changed, and, if so, re-compute ZIR and then queue the job
...@@ -2118,6 +2119,7 @@ fn workerAstGenFile(...@@ -2118,6 +2119,7 @@ fn workerAstGenFile(
2118 // there is a missing `failed_files` error message.2119 // there is a missing `failed_files` error message.
2119 error.OutOfMemory => {},2120 error.OutOfMemory => {},
2120 };2121 };
2122 return;
2121 },2123 },
2122 };2124 };
21232125
...@@ -2136,7 +2138,7 @@ fn workerAstGenFile(...@@ -2136,7 +2138,7 @@ fn workerAstGenFile(
2136 const lock = comp.mutex.acquire();2138 const lock = comp.mutex.acquire();
2137 defer lock.release();2139 defer lock.release();
21382140
2139 break :blk mod.importFile(file.pkg, import_path) catch continue;2141 break :blk mod.importFile(file, import_path) catch continue;
2140 };2142 };
2141 if (import_result.is_new) {2143 if (import_result.is_new) {
2142 wg.start();2144 wg.start();
src/Module.zig+81-18
...@@ -739,6 +739,7 @@ pub const Scope = struct {...@@ -739,6 +739,7 @@ pub const Scope = struct {
739 }739 }
740740
741 pub fn deinit(file: *File, gpa: *Allocator) void {741 pub fn deinit(file: *File, gpa: *Allocator) void {
742 gpa.free(file.sub_file_path);
742 file.unload(gpa);743 file.unload(gpa);
743 file.* = undefined;744 file.* = undefined;
744 }745 }
...@@ -746,6 +747,11 @@ pub const Scope = struct {...@@ -746,6 +747,11 @@ pub const Scope = struct {
746 pub fn getSource(file: *File, gpa: *Allocator) ![:0]const u8 {747 pub fn getSource(file: *File, gpa: *Allocator) ![:0]const u8 {
747 if (file.source_loaded) return file.source;748 if (file.source_loaded) return file.source;
748749
750 const root_dir_path = file.pkg.root_src_directory.path orelse ".";
751 log.debug("File.getSource, not cached. pkgdir={s} sub_file_path={s}", .{
752 root_dir_path, file.sub_file_path,
753 });
754
749 // Keep track of inode, file size, mtime, hash so we can detect which files755 // Keep track of inode, file size, mtime, hash so we can detect which files
750 // have been modified when an incremental update is requested.756 // have been modified when an incremental update is requested.
751 var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{});757 var f = try file.pkg.root_src_directory.handle.openFile(file.sub_file_path, .{});
...@@ -2633,20 +2639,70 @@ pub const ImportFileResult = struct {...@@ -2633,20 +2639,70 @@ pub const ImportFileResult = struct {
2633 is_new: bool,2639 is_new: bool,
2634};2640};
26352641
2642pub fn importPkg(mod: *Module, cur_pkg: *Package, pkg: *Package) !ImportFileResult {
2643 const gpa = mod.gpa;
2644
2645 // The resolved path is used as the key in the import table, to detect if
2646 // an import refers to the same as another, despite different relative paths
2647 // or differently mapped package names.
2648 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
2649 pkg.root_src_directory.path orelse ".", pkg.root_src_path,
2650 });
2651 var keep_resolved_path = false;
2652 defer if (!keep_resolved_path) gpa.free(resolved_path);
2653
2654 const gop = try mod.import_table.getOrPut(gpa, resolved_path);
2655 if (gop.found_existing) return ImportFileResult{
2656 .file = gop.entry.value,
2657 .is_new = false,
2658 };
2659 keep_resolved_path = true; // It's now owned by import_table.
2660
2661 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
2662 errdefer gpa.free(sub_file_path);
2663
2664 const new_file = try gpa.create(Scope.File);
2665 errdefer gpa.destroy(new_file);
2666
2667 gop.entry.value = new_file;
2668 new_file.* = .{
2669 .sub_file_path = sub_file_path,
2670 .source = undefined,
2671 .source_loaded = false,
2672 .tree_loaded = false,
2673 .zir_loaded = false,
2674 .stat_size = undefined,
2675 .stat_inode = undefined,
2676 .stat_mtime = undefined,
2677 .tree = undefined,
2678 .zir = undefined,
2679 .status = .never_loaded,
2680 .pkg = pkg,
2681 .namespace = undefined,
2682 };
2683 return ImportFileResult{
2684 .file = new_file,
2685 .is_new = true,
2686 };
2687}
2688
2636pub fn importFile(2689pub fn importFile(
2637 mod: *Module,2690 mod: *Module,
2638 cur_pkg: *Package,2691 cur_file: *Scope.File,
2639 import_string: []const u8,2692 import_string: []const u8,
2640) !ImportFileResult {2693) !ImportFileResult {
2694 if (cur_file.pkg.table.get(import_string)) |pkg| {
2695 return mod.importPkg(cur_file.pkg, pkg);
2696 }
2641 const gpa = mod.gpa;2697 const gpa = mod.gpa;
26422698
2643 const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse ".";2699 // The resolved path is used as the key in the import table, to detect if
2644 const found_pkg = cur_pkg.table.get(import_string);2700 // an import refers to the same as another, despite different relative paths
26452701 // or differently mapped package names.
2646 const resolved_path = if (found_pkg) |pkg|2702 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";
2647 try std.fs.path.resolve(gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path })2703 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
2648 else2704 cur_pkg_dir_path, cur_file.sub_file_path, "..", import_string,
2649 try std.fs.path.resolve(gpa, &[_][]const u8{ cur_pkg_dir_path, import_string });2705 });
2650 var keep_resolved_path = false;2706 var keep_resolved_path = false;
2651 defer if (!keep_resolved_path) gpa.free(resolved_path);2707 defer if (!keep_resolved_path) gpa.free(resolved_path);
26522708
...@@ -2655,20 +2711,28 @@ pub fn importFile(...@@ -2655,20 +2711,28 @@ pub fn importFile(
2655 .file = gop.entry.value,2711 .file = gop.entry.value,
2656 .is_new = false,2712 .is_new = false,
2657 };2713 };
2714 keep_resolved_path = true; // It's now owned by import_table.
26582715
2659 if (found_pkg == null) {2716 const new_file = try gpa.create(Scope.File);
2660 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});2717 errdefer gpa.destroy(new_file);
2661 defer gpa.free(resolved_root_path);
26622718
2663 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {2719 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});
2664 return error.ImportOutsidePkgPath;2720 defer gpa.free(resolved_root_path);
2665 }2721
2722 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {
2723 return error.ImportOutsidePkgPath;
2666 }2724 }
2725 // +1 for the directory separator here.
2726 const sub_file_path = try gpa.dupe(u8, resolved_path[resolved_root_path.len + 1 ..]);
2727 errdefer gpa.free(sub_file_path);
2728
2729 log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{
2730 resolved_root_path, resolved_path, sub_file_path, import_string,
2731 });
26672732
2668 const new_file = try gpa.create(Scope.File);
2669 gop.entry.value = new_file;2733 gop.entry.value = new_file;
2670 new_file.* = .{2734 new_file.* = .{
2671 .sub_file_path = resolved_path,2735 .sub_file_path = sub_file_path,
2672 .source = undefined,2736 .source = undefined,
2673 .source_loaded = false,2737 .source_loaded = false,
2674 .tree_loaded = false,2738 .tree_loaded = false,
...@@ -2679,10 +2743,9 @@ pub fn importFile(...@@ -2679,10 +2743,9 @@ pub fn importFile(
2679 .tree = undefined,2743 .tree = undefined,
2680 .zir = undefined,2744 .zir = undefined,
2681 .status = .never_loaded,2745 .status = .never_loaded,
2682 .pkg = found_pkg orelse cur_pkg,2746 .pkg = cur_file.pkg,
2683 .namespace = undefined,2747 .namespace = undefined,
2684 };2748 };
2685 keep_resolved_path = true;
2686 return ImportFileResult{2749 return ImportFileResult{
2687 .file = new_file,2750 .file = new_file,
2688 .is_new = true,2751 .is_new = true,
src/Sema.zig+1-1
...@@ -3904,7 +3904,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!...@@ -3904,7 +3904,7 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!
3904 const src = inst_data.src();3904 const src = inst_data.src();
3905 const operand = inst_data.get(sema.code);3905 const operand = inst_data.get(sema.code);
39063906
3907 const result = mod.importFile(block.getFileScope().pkg, operand) catch |err| switch (err) {3907 const result = mod.importFile(block.getFileScope(), operand) catch |err| switch (err) {
3908 error.ImportOutsidePkgPath => {3908 error.ImportOutsidePkgPath => {
3909 return mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand});3909 return mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand});
3910 },3910 },