| author | |
| committer | |
| log | e5dac0a0b391f227605e496a09f32b453ac3280d |
| tree | df9fc28a45b801727a28ef56a8abc08deff43bc9 |
| parent | ad17108bddc3bc198190407ab5b00820b2c17cd5 |
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 | 55 | /// since the last compilation, as well as scan for `@import` and queue up |
| 56 | 56 | /// additional jobs corresponding to those new files. |
| 57 | 57 | astgen_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. | |
| 61 | embed_file_work_queue: std.fifo.LinearFifo(*Module.EmbedFile, .Dynamic), | |
| 58 | 62 | |
| 59 | 63 | /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. |
| 60 | 64 | /// This data is accessed by multiple threads and is protected by `mutex`. |
| ... | ... | @@ -181,6 +185,10 @@ const Job = union(enum) { |
| 181 | 185 | /// It may have already be analyzed, or it may have been determined |
| 182 | 186 | /// to be outdated; in this case perform semantic analysis again. |
| 183 | 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 | 192 | /// The source file containing the Decl has been updated, and so the |
| 185 | 193 | /// Decl may need its line number information updated in the debug info. |
| 186 | 194 | update_line_number: *Module.Decl, |
| ... | ... | @@ -1447,6 +1455,7 @@ pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { |
| 1447 | 1455 | .work_queue = std.fifo.LinearFifo(Job, .Dynamic).init(gpa), |
| 1448 | 1456 | .c_object_work_queue = std.fifo.LinearFifo(*CObject, .Dynamic).init(gpa), |
| 1449 | 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 | 1459 | .keep_source_files_loaded = options.keep_source_files_loaded, |
| 1451 | 1460 | .use_clang = use_clang, |
| 1452 | 1461 | .clang_argv = options.clang_argv, |
| ... | ... | @@ -1632,6 +1641,7 @@ pub fn destroy(self: *Compilation) void { |
| 1632 | 1641 | self.work_queue.deinit(); |
| 1633 | 1642 | self.c_object_work_queue.deinit(); |
| 1634 | 1643 | self.astgen_work_queue.deinit(); |
| 1644 | self.embed_file_work_queue.deinit(); | |
| 1635 | 1645 | |
| 1636 | 1646 | { |
| 1637 | 1647 | var it = self.crt_files.iterator(); |
| ... | ... | @@ -1747,6 +1757,16 @@ pub fn update(self: *Compilation) !void { |
| 1747 | 1757 | } |
| 1748 | 1758 | |
| 1749 | 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 | 1770 | try self.work_queue.writeItem(.{ .analyze_pkg = std_pkg }); |
| 1751 | 1771 | if (self.bin_file.options.is_test) { |
| 1752 | 1772 | try self.work_queue.writeItem(.{ .analyze_pkg = module.main_pkg }); |
| ... | ... | @@ -1870,6 +1890,7 @@ pub fn totalErrorCount(self: *Compilation) usize { |
| 1870 | 1890 | |
| 1871 | 1891 | if (self.bin_file.options.module) |module| { |
| 1872 | 1892 | total += module.failed_exports.count(); |
| 1893 | total += module.failed_embed_files.count(); | |
| 1873 | 1894 | |
| 1874 | 1895 | { |
| 1875 | 1896 | var it = module.failed_files.iterator(); |
| ... | ... | @@ -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 | 1998 | var it = module.failed_decls.iterator(); |
| 1971 | 1999 | while (it.next()) |entry| { |
| ... | ... | @@ -2065,6 +2093,9 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2065 | 2093 | var c_obj_prog_node = main_progress_node.start("Compile C Objects", self.c_source_files.len); |
| 2066 | 2094 | defer c_obj_prog_node.end(); |
| 2067 | 2095 | |
| 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 | 2099 | self.work_queue_wait_group.reset(); |
| 2069 | 2100 | defer self.work_queue_wait_group.wait(); |
| 2070 | 2101 | |
| ... | ... | @@ -2079,6 +2110,13 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2079 | 2110 | }); |
| 2080 | 2111 | } |
| 2081 | 2112 | |
| 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 | 2120 | while (self.c_object_work_queue.readItem()) |c_object| { |
| 2083 | 2121 | self.work_queue_wait_group.start(); |
| 2084 | 2122 | try self.thread_pool.spawn(workerUpdateCObject, .{ |
| ... | ... | @@ -2260,6 +2298,15 @@ pub fn performAllTheWork(self: *Compilation) error{ TimerUnsupported, OutOfMemor |
| 2260 | 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 | 2310 | .update_line_number => |decl| { |
| 2264 | 2311 | if (build_options.omit_stage2) |
| 2265 | 2312 | @panic("sadly stage2 is omitted from this build to save memory on the CI server"); |
| ... | ... | @@ -2542,6 +2589,29 @@ fn workerAstGenFile( |
| 2542 | 2589 | } |
| 2543 | 2590 | } |
| 2544 | 2591 | |
| 2592 | fn 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 | ||
| 2545 | 2615 | pub fn obtainCObjectCacheManifest(comp: *const Compilation) Cache.Manifest { |
| 2546 | 2616 | var man = comp.cache_parent.obtain(); |
| 2547 | 2617 | |
| ... | ... | @@ -2790,6 +2860,36 @@ fn reportRetryableAstGenError( |
| 2790 | 2860 | } |
| 2791 | 2861 | } |
| 2792 | 2862 | |
| 2863 | fn 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 | ||
| 2793 | 2893 | fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.Progress.Node) !void { |
| 2794 | 2894 | if (!build_options.have_llvm) { |
| 2795 | 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 | 55 | /// is performing the export of another Decl. |
| 56 | 56 | /// This table owns the Export memory. |
| 57 | 57 | export_owners: std.AutoArrayHashMapUnmanaged(*Decl, []*Export) = .{}, |
| 58 | /// The set of all the files in the Module. We keep track of this in order to iterate | |
| 59 | /// over it and check which source files have been modified on the file system when | |
| 58 | /// The set of all the Zig source files in the Module. We keep track of this in order | |
| 59 | /// to iterate over it and check which source files have been modified on the file system when | |
| 60 | 60 | /// an update is requested, as well as to cache `@import` results. |
| 61 | 61 | /// Keys are fully resolved file paths. This table owns the keys and values. |
| 62 | 62 | import_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. | |
| 68 | embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{}, | |
| 63 | 69 | |
| 64 | 70 | /// The set of all the generic function instantiations. This is used so that when a generic |
| 65 | 71 | /// 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 | 93 | /// Using a map here for consistency with the other fields here. |
| 88 | 94 | /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator. |
| 89 | 95 | failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{}, |
| 96 | /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. | |
| 97 | failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{}, | |
| 90 | 98 | /// Using a map here for consistency with the other fields here. |
| 91 | 99 | /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. |
| 92 | 100 | failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, |
| ... | ... | @@ -1534,6 +1542,23 @@ pub const File = struct { |
| 1534 | 1542 | } |
| 1535 | 1543 | }; |
| 1536 | 1544 | |
| 1545 | /// Represents the contents of a file loaded with `@embedFile`. | |
| 1546 | pub 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 | 1562 | /// This struct holds data necessary to construct API-facing `AllErrors.Message`. |
| 1538 | 1563 | /// Its memory is managed with the general purpose allocator so that they |
| 1539 | 1564 | /// can be created and destroyed in response to incremental updates. |
| ... | ... | @@ -2364,6 +2389,11 @@ pub fn deinit(mod: *Module) void { |
| 2364 | 2389 | } |
| 2365 | 2390 | mod.failed_files.deinit(gpa); |
| 2366 | 2391 | |
| 2392 | for (mod.failed_embed_files.values()) |msg| { | |
| 2393 | msg.destroy(gpa); | |
| 2394 | } | |
| 2395 | mod.failed_embed_files.deinit(gpa); | |
| 2396 | ||
| 2367 | 2397 | for (mod.failed_exports.values()) |value| { |
| 2368 | 2398 | value.destroy(gpa); |
| 2369 | 2399 | } |
| ... | ... | @@ -3060,6 +3090,32 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) SemaError!void { |
| 3060 | 3090 | } |
| 3061 | 3091 | } |
| 3062 | 3092 | |
| 3093 | pub 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 | ||
| 3063 | 3119 | pub fn semaPkg(mod: *Module, pkg: *Package) !void { |
| 3064 | 3120 | const file = (try mod.importPkg(pkg)).file; |
| 3065 | 3121 | return mod.semaFile(file); |
| ... | ... | @@ -3551,6 +3607,84 @@ pub fn importFile( |
| 3551 | 3607 | }; |
| 3552 | 3608 | } |
| 3553 | 3609 | |
| 3610 | pub 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 | ||
| 3662 | pub 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 | ||
| 3554 | 3688 | pub fn scanNamespace( |
| 3555 | 3689 | mod: *Module, |
| 3556 | 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 | 6467 | return sema.addConstant(file_root_decl.ty, file_root_decl.val); |
| 6468 | 6468 | } |
| 6469 | 6469 | |
| 6470 | fn 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 | ||
| 6470 | 6509 | fn zirRetErrValueCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 6471 | 6510 | _ = block; |
| 6472 | 6511 | _ = inst; |
| ... | ... | @@ -9020,12 +9059,6 @@ fn zirBoolToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 9020 | 9059 | return block.addUnOp(.bool_to_int, operand); |
| 9021 | 9060 | } |
| 9022 | 9061 | |
| 9023 | fn 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 | ||
| 9029 | 9062 | fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9030 | 9063 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 9031 | 9064 | const src = inst_data.src(); |