authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-17 18:57:54-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-17 18:59:11-07:00
loge5dac0a0b391f227605e496a09f32b453ac3280d
treedf9fc28a45b801727a28ef56a8abc08deff43bc9
parentad17108bddc3bc198190407ab5b00820b2c17cd5

stage2: implement `@embedFile`


3 files changed, 275 insertions(+), 8 deletions(-)

src/Compilation.zig+100
...@@ -55,6 +55,10 @@ c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),...@@ -55,6 +55,10 @@ c_object_work_queue: std.fifo.LinearFifo(*CObject, .Dynamic),
55/// since the last compilation, as well as scan for `@import` and queue up55/// since the last compilation, as well as scan for `@import` and queue up
56/// additional jobs corresponding to those new files.56/// additional jobs corresponding to those new files.
57astgen_work_queue: std.fifo.LinearFifo(*Module.File, .Dynamic),57astgen_work_queue: std.fifo.LinearFifo(*Module.File, .Dynamic),
58/// These jobs are to inspect the file system stat() and if the embedded file has changed
59/// on disk, mark the corresponding Decl outdated and queue up an `analyze_decl`
60/// task for it.
61embed_file_work_queue: std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic),
5862
59/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.63/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
60/// This data is accessed by multiple threads and is protected by `mutex`.64/// This data is accessed by multiple threads and is protected by `mutex`.
...@@ -181,6 +185,10 @@ const Job = union(enum) {...@@ -181,6 +185,10 @@ const Job = union(enum) {
181 /// It may have already be analyzed, or it may have been determined185 /// It may have already be analyzed, or it may have been determined
182 /// to be outdated; in this case perform semantic analysis again.186 /// to be outdated; in this case perform semantic analysis again.
183 analyze_decl: *Module.Decl,187 analyze_decl: *Module.Decl,
188 /// The file that was loaded with `@embedFile` has changed on disk
189 /// and has been re-loaded into memory. All Decls that depend on it
190 /// need to be re-analyzed.
191 update_embed_file: *Module.EmbedFile,
184 /// The source file containing the Decl has been updated, and so the192 /// The source file containing the Decl has been updated, and so the
185 /// Decl may need its line number information updated in the debug info.193 /// Decl may need its line number information updated in the debug info.
186 update_line_number: *Module.Decl,194 update_line_number: *Module.Decl,
...@@ -1447,6 +1455,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {...@@ -1447,6 +1455,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation {
1447 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),1455 .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa),
1448 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),1456 .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa),
1449 .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),1457 .astgen_work_queue = std.fifo.LinearFifo(*Module.File, .Dynamic).init(gpa),
1458 .embed_file_work_queue = std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic).init(gpa),
1450 .keep_source_files_loaded = options.keep_source_files_loaded,1459 .keep_source_files_loaded = options.keep_source_files_loaded,
1451 .use_clang = use_clang,1460 .use_clang = use_clang,
1452 .clang_argv = options.clang_argv,1461 .clang_argv = options.clang_argv,
...@@ -1632,6 +1641,7 @@ pub fn destroy(self: *Compilation) void {...@@ -1632,6 +1641,7 @@ pub fn destroy(self: *Compilation) void {
1632 self.work_queue.deinit();1641 self.work_queue.deinit();
1633 self.c_object_work_queue.deinit();1642 self.c_object_work_queue.deinit();
1634 self.astgen_work_queue.deinit();1643 self.astgen_work_queue.deinit();
1644 self.embed_file_work_queue.deinit();
16351645
1636 {1646 {
1637 var it = self.crt_files.iterator();1647 var it = self.crt_files.iterator();
...@@ -1747,6 +1757,16 @@ pub fn update(self: *Compilation) !void {...@@ -1747,6 +1757,16 @@ pub fn update(self: *Compilation) !void {
1747 }1757 }
17481758
1749 if (!use_stage1) {1759 if (!use_stage1) {
1760 // Put a work item in for checking if any files used with `@embedFile` changed.
1761 {
1762 try self.embed_file_work_queue.ensureUnusedCapacity(module.embed_table.count());
1763 var it = module.embed_table.iterator();
1764 while (it.next()) |entry| {
1765 const embed_file = entry.value_ptr.*;
1766 self.embed_file_work_queue.writeItemAssumeCapacity(embed_file);
1767 }
1768 }
1769
1750 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });1770 try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg });
1751 if (self.bin_file.options.is_test) {1771 if (self.bin_file.options.is_test) {
1752 try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });1772 try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg });
...@@ -1870,6 +1890,7 @@ pub fn totalErrorCount(self: *Compilation) usize {...@@ -1870,6 +1890,7 @@ pub fn totalErrorCount(self: *Compilation) usize {
18701890
1871 if (self.bin_file.options.module) |module| {1891 if (self.bin_file.options.module) |module| {
1872 total += module.failed_exports.count();1892 total += module.failed_exports.count();
1893 total += module.failed_embed_files.count();
18731894
1874 {1895 {
1875 var it = module.failed_files.iterator();1896 var it = module.failed_files.iterator();
...@@ -1966,6 +1987,13 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {...@@ -1966,6 +1987,13 @@ pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors {
1966 }1987 }
1967 }1988 }
1968 }1989 }
1990 {
1991 var it = module.failed_embed_files.iterator();
1992 while (it.next()) |entry| {
1993 const msg = entry.value_ptr.*;
1994 try AllErrors.add(module, &arena, &errors, msg.*);
1995 }
1996 }
1969 {1997 {
1970 var it = module.failed_decls.iterator();1998 var it = module.failed_decls.iterator();
1971 while (it.next()) |entry| {1999 while (it.next()) |entry| {
...@@ -2065,6 +2093,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2065,6 +2093,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2065 var c_obj_prog_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);2093 var c_obj_prog_node = main_progress_node.start("Compile C Objects", self.c_source_files.len);
2066 defer c_obj_prog_node.end();2094 defer c_obj_prog_node.end();
20672095
2096 var embed_file_prog_node = main_progress_node.start("Detect @embedFile updates", self.embed_file_work_queue.count);
2097 defer embed_file_prog_node.end();
2098
2068 self.work_queue_wait_group.reset();2099 self.work_queue_wait_group.reset();
2069 defer self.work_queue_wait_group.wait();2100 defer self.work_queue_wait_group.wait();
20702101
...@@ -2079,6 +2110,13 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2079,6 +2110,13 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2079 });2110 });
2080 }2111 }
20812112
2113 while (self.embed_file_work_queue.readItem()) |embed_file| {
2114 self.astgen_wait_group.start();
2115 try self.thread_pool.spawn(workerCheckEmbedFile, .{
2116 self, embed_file, &embed_file_prog_node, &self.astgen_wait_group,
2117 });
2118 }
2119
2082 while (self.c_object_work_queue.readItem()) |c_object| {2120 while (self.c_object_work_queue.readItem()) |c_object| {
2083 self.work_queue_wait_group.start();2121 self.work_queue_wait_group.start();
2084 try self.thread_pool.spawn(workerUpdateCObject, .{2122 try self.thread_pool.spawn(workerUpdateCObject, .{
...@@ -2260,6 +2298,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor...@@ -2260,6 +2298,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor
2260 error.AnalysisFail => continue,2298 error.AnalysisFail => continue,
2261 };2299 };
2262 },2300 },
2301 .update_embed_file => |embed_file| {
2302 if (build_options.omit_stage2)
2303 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
2304 const module = self.bin_file.options.module.?;
2305 module.updateEmbedFile(embed_file) catch |err| switch (err) {
2306 error.OutOfMemory => return error.OutOfMemory,
2307 error.AnalysisFail => continue,
2308 };
2309 },
2263 .update_line_number => |decl| {2310 .update_line_number => |decl| {
2264 if (build_options.omit_stage2)2311 if (build_options.omit_stage2)
2265 @panic("sadly stage2 is omitted from this build to save memory on the CI server");2312 @panic("sadly stage2 is omitted from this build to save memory on the CI server");
...@@ -2542,6 +2589,29 @@ fn workerAstGenFile(...@@ -2542,6 +2589,29 @@ fn workerAstGenFile(
2542 }2589 }
2543}2590}
25442591
2592fn workerCheckEmbedFile(
2593 comp: *Compilation,
2594 embed_file: *Module.EmbedFile,
2595 prog_node: *std.Progress.Node,
2596 wg: *WaitGroup,
2597) void {
2598 defer wg.finish();
2599
2600 var child_prog_node = prog_node.start(embed_file.sub_file_path, 0);
2601 child_prog_node.activate();
2602 defer child_prog_node.end();
2603
2604 const mod = comp.bin_file.options.module.?;
2605 mod.detectEmbedFileUpdate(embed_file) catch |err| {
2606 comp.reportRetryableEmbedFileError(embed_file, err) catch |oom| switch (oom) {
2607 // Swallowing this error is OK because it's implied to be OOM when
2608 // there is a missing `failed_embed_files` error message.
2609 error.OutOfMemory => {},
2610 };
2611 return;
2612 };
2613}
2614
2545pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest {2615pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest {
2546 var man = comp.cache_parent.obtain();2616 var man = comp.cache_parent.obtain();
25472617
...@@ -2790,6 +2860,36 @@ fn reportRetryableAstGenError(...@@ -2790,6 +2860,36 @@ fn reportRetryableAstGenError(
2790 }2860 }
2791}2861}
27922862
2863fn reportRetryableEmbedFileError(
2864 comp: *Compilation,
2865 embed_file: *Module.EmbedFile,
2866 err: anyerror,
2867) error{OutOfMemory}!void {
2868 const mod = comp.bin_file.options.module.?;
2869 const gpa = mod.gpa;
2870
2871 const src_loc: Module.SrcLoc = embed_file.owner_decl.srcLoc();
2872
2873 const err_msg = if (embed_file.pkg.root_src_directory.path) |dir_path|
2874 try Module.ErrorMsg.create(
2875 gpa,
2876 src_loc,
2877 "unable to load '{s}" ++ std.fs.path.sep_str ++ "{s}': {s}",
2878 .{ dir_path, embed_file.sub_file_path, @errorName(err) },
2879 )
2880 else
2881 try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{s}': {s}", .{
2882 embed_file.sub_file_path, @errorName(err),
2883 });
2884 errdefer err_msg.destroy(gpa);
2885
2886 {
2887 const lock = comp.mutex.acquire();
2888 defer lock.release();
2889 try mod.failed_embed_files.putNoClobber(gpa, embed_file, err_msg);
2890 }
2891}
2892
2793fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void {2893fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void {
2794 if (!build_options.have_llvm) {2894 if (!build_options.have_llvm) {
2795 return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});2895 return comp.failCObj(c_object, "clang not available: compiler built without LLVM extensions", .{});
src/Module.zig+136-2
...@@ -55,11 +55,17 @@ decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},...@@ -55,11 +55,17 @@ decl_exports: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
55/// is performing the export of another Decl.55/// is performing the export of another Decl.
56/// This table owns the Export memory.56/// This table owns the Export memory.
57export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},57export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{},
58/// The set of all the files in the Module. We keep track of this in order to iterate58/// The set of all the Zig source files in the Module. We keep track of this in order
59/// over it and check which source files have been modified on the file system when59/// to iterate over it and check which source files have been modified on the file system when
60/// an update is requested, as well as to cache `@import` results.60/// an update is requested, as well as to cache `@import` results.
61/// Keys are fully resolved file paths. This table owns the keys and values.61/// Keys are fully resolved file paths. This table owns the keys and values.
62import_table: std.StringArrayHashMapUnmanaged(*File) = .{},62import_table: std.StringArrayHashMapUnmanaged(*File) = .{},
63/// The set of all the files which have been loaded with `@embedFile` in the Module.
64/// We keep track of this in order to iterate over it and check which files have been
65/// modified on the file system when an update is requested, as well as to cache
66/// `@embedFile` results.
67/// Keys are fully resolved file paths. This table owns the keys and values.
68embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{},
6369
64/// The set of all the generic function instantiations. This is used so that when a generic70/// The set of all the generic function instantiations. This is used so that when a generic
65/// function is called twice with the same comptime parameter arguments, both calls dispatch71/// function is called twice with the same comptime parameter arguments, both calls dispatch
...@@ -87,6 +93,8 @@ compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, i32) = .{},...@@ -87,6 +93,8 @@ compile_log_decls: std.AutoArrayHashMapUnmanaged(*Decl, i32) = .{},
87/// Using a map here for consistency with the other fields here.93/// Using a map here for consistency with the other fields here.
88/// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator.94/// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator.
89failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{},95failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{},
96/// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator.
97failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{},
90/// Using a map here for consistency with the other fields here.98/// Using a map here for consistency with the other fields here.
91/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.99/// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator.
92failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},100failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{},
...@@ -1534,6 +1542,23 @@ pub const File = struct {...@@ -1534,6 +1542,23 @@ pub const File = struct {
1534 }1542 }
1535};1543};
15361544
1545/// Represents the contents of a file loaded with `@embedFile`.
1546pub const EmbedFile = struct {
1547 /// Relative to the owning package's root_src_dir.
1548 /// Memory is stored in gpa, owned by EmbedFile.
1549 sub_file_path: []const u8,
1550 bytes: [:0]const u8,
1551 stat_size: u64,
1552 stat_inode: std.fs.File.INode,
1553 stat_mtime: i128,
1554 /// Package that this file is a part of, managed externally.
1555 pkg: *Package,
1556 /// The Decl that was created from the `@embedFile` to own this resource.
1557 /// This is how zig knows what other Decl objects to invalidate if the file
1558 /// changes on disk.
1559 owner_decl: *Decl,
1560};
1561
1537/// This struct holds data necessary to construct API-facing `AllErrors.Message`.1562/// This struct holds data necessary to construct API-facing `AllErrors.Message`.
1538/// Its memory is managed with the general purpose allocator so that they1563/// Its memory is managed with the general purpose allocator so that they
1539/// can be created and destroyed in response to incremental updates.1564/// can be created and destroyed in response to incremental updates.
...@@ -2364,6 +2389,11 @@ pub fn deinit(mod: *Module) void {...@@ -2364,6 +2389,11 @@ pub fn deinit(mod: *Module) void {
2364 }2389 }
2365 mod.failed_files.deinit(gpa);2390 mod.failed_files.deinit(gpa);
23662391
2392 for (mod.failed_embed_files.values()) |msg| {
2393 msg.destroy(gpa);
2394 }
2395 mod.failed_embed_files.deinit(gpa);
2396
2367 for (mod.failed_exports.values()) |value| {2397 for (mod.failed_exports.values()) |value| {
2368 value.destroy(gpa);2398 value.destroy(gpa);
2369 }2399 }
...@@ -3060,6 +3090,32 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {...@@ -3060,6 +3090,32 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void {
3060 }3090 }
3061}3091}
30623092
3093pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
3094 const tracy = trace(@src());
3095 defer tracy.end();
3096
3097 // TODO we can potentially relax this if we store some more information along
3098 // with decl dependency edges
3099 for (embed_file.owner_decl.dependants.keys()) |dep| {
3100 switch (dep.analysis) {
3101 .unreferenced => unreachable,
3102 .in_progress => continue, // already doing analysis, ok
3103 .outdated => continue, // already queued for update
3104
3105 .file_failure,
3106 .dependency_failure,
3107 .sema_failure,
3108 .sema_failure_retryable,
3109 .codegen_failure,
3110 .codegen_failure_retryable,
3111 .complete,
3112 => if (dep.generation != mod.generation) {
3113 try mod.markOutdatedDecl(dep);
3114 },
3115 }
3116 }
3117}
3118
3063pub fn semaPkg(mod: *Module, pkg: *Package) !void {3119pub fn semaPkg(mod: *Module, pkg: *Package) !void {
3064 const file = (try mod.importPkg(pkg)).file;3120 const file = (try mod.importPkg(pkg)).file;
3065 return mod.semaFile(file);3121 return mod.semaFile(file);
...@@ -3551,6 +3607,84 @@ pub fn importFile(...@@ -3551,6 +3607,84 @@ pub fn importFile(
3551 };3607 };
3552}3608}
35533609
3610pub fn embedFile(mod: *Module, cur_file: *File, rel_file_path: []const u8) !*EmbedFile {
3611 const gpa = mod.gpa;
3612
3613 // The resolved path is used as the key in the table, to detect if
3614 // a file refers to the same as another, despite different relative paths.
3615 const cur_pkg_dir_path = cur_file.pkg.root_src_directory.path orelse ".";
3616 const resolved_path = try std.fs.path.resolve(gpa, &[_][]const u8{
3617 cur_pkg_dir_path, cur_file.sub_file_path, "..", rel_file_path,
3618 });
3619 var keep_resolved_path = false;
3620 defer if (!keep_resolved_path) gpa.free(resolved_path);
3621
3622 const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
3623 if (gop.found_existing) return gop.value_ptr.*;
3624 keep_resolved_path = true; // It's now owned by embed_table.
3625
3626 const new_file = try gpa.create(EmbedFile);
3627 errdefer gpa.destroy(new_file);
3628
3629 const resolved_root_path = try std.fs.path.resolve(gpa, &[_][]const u8{cur_pkg_dir_path});
3630 defer gpa.free(resolved_root_path);
3631
3632 if (!mem.startsWith(u8, resolved_path, resolved_root_path)) {
3633 return error.ImportOutsidePkgPath;
3634 }
3635 // +1 for the directory separator here.
3636 const sub_file_path = try gpa.dupe(u8, resolved_path[resolved_root_path.len + 1 ..]);
3637 errdefer gpa.free(sub_file_path);
3638
3639 var file = try cur_file.pkg.root_src_directory.handle.openFile(sub_file_path, .{});
3640 defer file.close();
3641
3642 const stat = try file.stat();
3643 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), stat.size, 1, 0);
3644
3645 log.debug("new embedFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, rel_file_path={s}", .{
3646 resolved_root_path, resolved_path, sub_file_path, rel_file_path,
3647 });
3648
3649 gop.value_ptr.* = new_file;
3650 new_file.* = .{
3651 .sub_file_path = sub_file_path,
3652 .bytes = bytes,
3653 .stat_size = stat.size,
3654 .stat_inode = stat.inode,
3655 .stat_mtime = stat.mtime,
3656 .pkg = cur_file.pkg,
3657 .owner_decl = undefined, // Set by Sema immediately after this function returns.
3658 };
3659 return new_file;
3660}
3661
3662pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void {
3663 var file = try embed_file.pkg.root_src_directory.handle.openFile(embed_file.sub_file_path, .{});
3664 defer file.close();
3665
3666 const stat = try file.stat();
3667
3668 const unchanged_metadata =
3669 stat.size == embed_file.stat_size and
3670 stat.mtime == embed_file.stat_mtime and
3671 stat.inode == embed_file.stat_inode;
3672
3673 if (unchanged_metadata) return;
3674
3675 const gpa = mod.gpa;
3676 const bytes = try file.readToEndAllocOptions(gpa, std.math.maxInt(u32), stat.size, 1, 0);
3677 gpa.free(embed_file.bytes);
3678 embed_file.bytes = bytes;
3679 embed_file.stat_size = stat.size;
3680 embed_file.stat_mtime = stat.mtime;
3681 embed_file.stat_inode = stat.inode;
3682
3683 const lock = mod.comp.mutex.acquire();
3684 defer lock.release();
3685 try mod.comp.work_queue.writeItem(.{ .update_embed_file = embed_file });
3686}
3687
3554pub fn scanNamespace(3688pub fn scanNamespace(
3555 mod: *Module,3689 mod: *Module,
3556 namespace: *Namespace,3690 namespace: *Namespace,
src/Sema.zig+39-6
...@@ -6467,6 +6467,45 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air....@@ -6467,6 +6467,45 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
6467 return sema.addConstant(file_root_decl.ty, file_root_decl.val);6467 return sema.addConstant(file_root_decl.ty, file_root_decl.val);
6468}6468}
64696469
6470fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6471 const tracy = trace(@src());
6472 defer tracy.end();
6473
6474 const mod = sema.mod;
6475 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
6476 const src = inst_data.src();
6477 const name = try sema.resolveConstString(block, src, inst_data.operand);
6478
6479 const embed_file = mod.embedFile(block.getFileScope(), name) catch |err| switch (err) {
6480 error.ImportOutsidePkgPath => {
6481 return sema.fail(block, src, "embed of file outside package path: '{s}'", .{name});
6482 },
6483 else => {
6484 // TODO: these errors are file system errors; make sure an update() will
6485 // retry this and not cache the file system error, which may be transient.
6486 return sema.fail(block, src, "unable to open '{s}': {s}", .{ name, @errorName(err) });
6487 },
6488 };
6489
6490 var anon_decl = try block.startAnonDecl();
6491 defer anon_decl.deinit();
6492
6493 const bytes_including_null = embed_file.bytes[0 .. embed_file.bytes.len + 1];
6494
6495 // TODO instead of using `Value.Tag.bytes`, create a new value tag for pointing at
6496 // a `*Module.EmbedFile`. The purpose of this would be:
6497 // - If only the length is read and the bytes are not inspected by comptime code,
6498 // there can be an optimization where the codegen backend does a copy_file_range
6499 // into the final binary, and never loads the data into memory.
6500 // - When a Decl is destroyed, it can free the `*Module.EmbedFile`.
6501 embed_file.owner_decl = try anon_decl.finish(
6502 try Type.Tag.array_u8_sentinel_0.create(anon_decl.arena(), embed_file.bytes.len),
6503 try Value.Tag.bytes.create(anon_decl.arena(), bytes_including_null),
6504 );
6505
6506 return sema.analyzeDeclRef(embed_file.owner_decl);
6507}
6508
6470fn zirRetErrValueCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {6509fn zirRetErrValueCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
6471 _ = block;6510 _ = block;
6472 _ = inst;6511 _ = inst;
...@@ -9020,12 +9059,6 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A...@@ -9020,12 +9059,6 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
9020 return block.addUnOp(.bool_to_int, operand);9059 return block.addUnOp(.bool_to_int, operand);
9021}9060}
90229061
9023fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9024 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
9025 const src = inst_data.src();
9026 return sema.fail(block, src, "TODO: Sema.zirEmbedFile", .{});
9027}
9028
9029fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9062fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9030 const inst_data = sema.code.instructions.items(.data)[inst].un_node;9063 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
9031 const src = inst_data.src();9064 const src = inst_data.src();