diff options
160 files changed, 6411 insertions, 4832 deletions
@@ -34,7 +34,7 @@ pub fn build(b: *std.Build) !void { const docgen_exe = b.addExecutable(.{ .name = "docgen", - .root_source_file = .{ .path = "tools/docgen.zig" }, + .root_source_file = b.path("tools/docgen.zig"), .target = b.host, .optimize = .Debug, .single_threaded = single_threaded, @@ -46,7 +46,7 @@ pub fn build(b: *std.Build) !void { docgen_cmd.addArg("--zig-lib-dir"); docgen_cmd.addDirectoryArg(p); } - docgen_cmd.addFileArg(.{ .path = "doc/langref.html.in" }); + docgen_cmd.addFileArg(b.path("doc/langref.html.in")); const langref_file = docgen_cmd.addOutputFileArg("langref.html"); const install_langref = b.addInstallFileWithDir(langref_file, .prefix, "doc/langref.html"); if (!skip_install_langref) { @@ -55,9 +55,9 @@ pub fn build(b: *std.Build) !void { const autodoc_test = b.addObject(.{ .name = "std", - .root_source_file = .{ .path = "lib/std/std.zig" }, + .root_source_file = b.path("lib/std/std.zig"), .target = target, - .zig_lib_dir = .{ .path = "lib" }, + .zig_lib_dir = b.path("lib"), .optimize = .Debug, }); const install_std_docs = b.addInstallDirectory(.{ @@ -86,7 +86,7 @@ pub fn build(b: *std.Build) !void { const check_case_exe = b.addExecutable(.{ .name = "check-case", - .root_source_file = .{ .path = "test/src/Cases.zig" }, + .root_source_file = b.path("test/src/Cases.zig"), .target = b.host, .optimize = optimize, .single_threaded = single_threaded, @@ -135,7 +135,7 @@ pub fn build(b: *std.Build) !void { if (!skip_install_lib_files) { b.installDirectory(.{ - .source_dir = .{ .path = "lib" }, + .source_dir = b.path("lib"), .install_dir = if (flat) .prefix else .lib, .install_subdir = if (flat) "lib" else "zig", .exclude_extensions = &[_][]const u8{ @@ -552,10 +552,10 @@ pub fn build(b: *std.Build) !void { const update_mingw_exe = b.addExecutable(.{ .name = "update_mingw", .target = b.host, - .root_source_file = .{ .path = "tools/update_mingw.zig" }, + .root_source_file = b.path("tools/update_mingw.zig"), }); const update_mingw_run = b.addRunArtifact(update_mingw_exe); - update_mingw_run.addDirectoryArg(.{ .path = "lib" }); + update_mingw_run.addDirectoryArg(b.path("lib")); if (opt_mingw_src_path) |mingw_src_path| { update_mingw_run.addDirectoryArg(.{ .cwd_relative = mingw_src_path }); } else { @@ -606,10 +606,10 @@ fn addWasiUpdateStep(b: *std.Build, version: [:0]const u8) !void { }); run_opt.addArtifactArg(exe); run_opt.addArg("-o"); - run_opt.addFileArg(.{ .path = "stage1/zig1.wasm" }); + run_opt.addFileArg(b.path("stage1/zig1.wasm")); const copy_zig_h = b.addWriteFiles(); - copy_zig_h.addCopyFileToSource(.{ .path = "lib/zig.h" }, "stage1/zig.h"); + copy_zig_h.addCopyFileToSource(b.path("lib/zig.h"), "stage1/zig.h"); const update_zig1_step = b.step("update-zig1", "Update stage1/zig1.wasm"); update_zig1_step.dependOn(&run_opt.step); @@ -627,7 +627,7 @@ const AddCompilerStepOptions = struct { fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.Step.Compile { const exe = b.addExecutable(.{ .name = "zig", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = options.target, .optimize = options.optimize, .max_rss = 7_000_000_000, @@ -638,11 +638,11 @@ fn addCompilerStep(b: *std.Build, options: AddCompilerStepOptions) *std.Build.St exe.stack_size = stack_size; const aro_module = b.createModule(.{ - .root_source_file = .{ .path = "lib/compiler/aro/aro.zig" }, + .root_source_file = b.path("lib/compiler/aro/aro.zig"), }); const aro_translate_c_module = b.createModule(.{ - .root_source_file = .{ .path = "lib/compiler/aro_translate_c.zig" }, + .root_source_file = b.path("lib/compiler/aro_translate_c.zig"), .imports = &.{ .{ .name = "aro", @@ -747,6 +747,9 @@ fn addCmakeCfgOptionsToExe( try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes); try addCxxKnownPath(b, cfg, exe, b.fmt("libgcc_eh.{s}", .{lib_suffix}), null, need_cpp_includes); }, + .haiku => { + try addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), null, need_cpp_includes); + }, else => {}, } } diff --git a/doc/build.zig.zon.md b/doc/build.zig.zon.md index ad2ff61b64..de5ec5fe9c 100644 --- a/doc/build.zig.zon.md +++ b/doc/build.zig.zon.md @@ -63,6 +63,13 @@ When this is provided, the package is found in a directory relative to the build root. In this case the package's hash is irrelevant and therefore not computed. This field and `url` are mutually exclusive. +#### `lazy` + +Boolean. + +When this is set to `true`, a package is declared to be lazily fetched. This +makes the dependency only get fetched if it is actually used. + ### `paths` List. Required. diff --git a/doc/langref.html.in b/doc/langref.html.in index 1d1468ab95..6f3e9961c3 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -3323,6 +3323,16 @@ fn doTheTest() !void { } {#code_end#} <p> + The backing integer is inferred from the fields' total bit width. + Optionally, it can be explicitly provided and enforced at compile time: + </p> + {#code_begin|test_err|test_missized_packed_struct|backing integer type 'u32' has bit size 32 but the struct fields have a total bit size of 24#} +test "missized packed struct" { + const S = packed struct(u32) { a: u16, b: u8 }; + _ = S{ .a = 4, .b = 2 }; +} + {#code_end#} + <p> Zig allows the address to be taken of a non-byte-aligned field: </p> {#code_begin|test|test_pointer_to_non-byte_aligned_field#} diff --git a/lib/docs/wasm/markdown/Parser.zig b/lib/docs/wasm/markdown/Parser.zig index 7f463224be..fe4fc2f9b0 100644 --- a/lib/docs/wasm/markdown/Parser.zig +++ b/lib/docs/wasm/markdown/Parser.zig @@ -1540,7 +1540,7 @@ const InlineParser = struct { iter.pos += 1; return .{ .text = replacement }; }; - const is_valid = iter.pos + cp_len < iter.content.len and + const is_valid = iter.pos + cp_len <= iter.content.len and std.unicode.utf8ValidateSlice(iter.content[iter.pos..][0..cp_len]); const cp_encoded = if (is_valid) iter.content[iter.pos..][0..cp_len] diff --git a/lib/init/build.zig b/lib/init/build.zig index e513acdf25..d53cbbb982 100644 --- a/lib/init/build.zig +++ b/lib/init/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { .name = "$", // In this case the main source file is merely a path, however, in more // complicated build scripts, this could be a generated file. - .root_source_file = .{ .path = "src/root.zig" }, + .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, }); @@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "$", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); @@ -67,7 +67,7 @@ pub fn build(b: *std.Build) void { // Creates a step for unit testing. This only builds the test executable // but does not run it. const lib_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/root.zig" }, + .root_source_file = b.path("src/root.zig"), .target = target, .optimize = optimize, }); @@ -75,7 +75,7 @@ pub fn build(b: *std.Build) void { const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests); const exe_unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); diff --git a/lib/init/build.zig.zon b/lib/init/build.zig.zon index dc9d138ba5..50da5587b1 100644 --- a/lib/init/build.zig.zon +++ b/lib/init/build.zig.zon @@ -37,6 +37,11 @@ // // build root. In this case the package's hash is irrelevant and therefore not // // computed. This field and `url` are mutually exclusive. // .path = "foo", + + // // When this is set to `true`, a package is declared to be lazily + // // fetched. This makes the dependency only get fetched if it is + // // actually used. + // .lazy = false, //}, }, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 2c644ce4b0..a3d091858a 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -607,17 +607,17 @@ pub fn resolveInstallPrefix(self: *Build, install_prefix: ?[]const u8, dir_list: var h_list = [_][]const u8{ self.install_path, "include" }; if (dir_list.lib_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; + if (fs.path.isAbsolute(dir)) lib_list[0] = self.dest_dir orelse ""; lib_list[1] = dir; } if (dir_list.exe_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; + if (fs.path.isAbsolute(dir)) exe_list[0] = self.dest_dir orelse ""; exe_list[1] = dir; } if (dir_list.include_dir) |dir| { - if (std.fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; + if (fs.path.isAbsolute(dir)) h_list[0] = self.dest_dir orelse ""; h_list[1] = dir; } @@ -858,7 +858,7 @@ pub const TestOptions = struct { /// deprecated: use `.filters = &.{filter}` instead of `.filter = filter`. filter: ?[]const u8 = null, filters: []const []const u8 = &.{}, - test_runner: ?[]const u8 = null, + test_runner: ?LazyPath = null, link_libc: ?bool = null, single_threaded: ?bool = null, pic: ?bool = null, @@ -1546,22 +1546,22 @@ pub fn addInstallArtifact( } ///`dest_rel_path` is relative to prefix path -pub fn installFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .prefix, dest_rel_path).step); +pub fn installFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + b.getInstallStep().dependOn(&b.addInstallFileWithDir(b.path(src_path), .prefix, dest_rel_path).step); } -pub fn installDirectory(self: *Build, options: Step.InstallDir.Options) void { - self.getInstallStep().dependOn(&self.addInstallDirectory(options).step); +pub fn installDirectory(b: *Build, options: Step.InstallDir.Options) void { + b.getInstallStep().dependOn(&b.addInstallDirectory(options).step); } ///`dest_rel_path` is relative to bin path -pub fn installBinFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .bin, dest_rel_path).step); +pub fn installBinFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + b.getInstallStep().dependOn(&b.addInstallFileWithDir(b.path(src_path), .bin, dest_rel_path).step); } ///`dest_rel_path` is relative to lib path -pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const u8) void { - self.getInstallStep().dependOn(&self.addInstallFileWithDir(.{ .path = src_path }, .lib, dest_rel_path).step); +pub fn installLibFile(b: *Build, src_path: []const u8, dest_rel_path: []const u8) void { + b.getInstallStep().dependOn(&b.addInstallFileWithDir(b.path(src_path), .lib, dest_rel_path).step); } pub fn addObjCopy(b: *Build, source: LazyPath, options: Step.ObjCopy.Options) *Step.ObjCopy { @@ -1635,6 +1635,22 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void { src_file.close(); } +/// References a file or directory relative to the source root. +pub fn path(b: *Build, sub_path: []const u8) LazyPath { + if (fs.path.isAbsolute(sub_path)) { + std.debug.panic("sub_path is expected to be relative to the build root, but was this absolute path: '{s}'. It is best avoid absolute paths, but if you must, it is supported by LazyPath.cwd_relative", .{ + sub_path, + }); + } + return .{ .src_path = .{ + .owner = b, + .sub_path = sub_path, + } }; +} + +/// This is low-level implementation details of the build system, not meant to +/// be called by users' build scripts. Even in the build system itself it is a +/// code smell to call this function. pub fn pathFromRoot(b: *Build, p: []const u8) []u8 { return fs.path.resolve(b.allocator, &.{ b.build_root.path orelse ".", p }) catch @panic("OOM"); } @@ -1674,10 +1690,9 @@ pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []con return name; } var it = mem.tokenizeScalar(u8, PATH, fs.path.delimiter); - while (it.next()) |path| { + while (it.next()) |p| { const full_path = self.pathJoin(&.{ - path, - self.fmt("{s}{s}", .{ name, exe_extension }), + p, self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1687,10 +1702,9 @@ pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []con if (fs.path.isAbsolute(name)) { return name; } - for (paths) |path| { + for (paths) |p| { const full_path = self.pathJoin(&.{ - path, - self.fmt("{s}{s}", .{ name, exe_extension }), + p, self.fmt("{s}{s}", .{ name, exe_extension }), }); return fs.realpathAlloc(self.allocator, full_path) catch continue; } @@ -1771,7 +1785,7 @@ pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) .bin => self.exe_dir, .lib => self.lib_dir, .header => self.h_dir, - .custom => |path| self.pathJoin(&.{ self.install_path, path }), + .custom => |p| self.pathJoin(&.{ self.install_path, p }), }; return fs.path.resolve( self.allocator, @@ -2032,7 +2046,7 @@ fn dependencyInner( const build_root: std.Build.Cache.Directory = .{ .path = build_root_string, - .handle = std.fs.cwd().openDir(build_root_string, .{}) catch |err| { + .handle = fs.cwd().openDir(build_root_string, .{}) catch |err| { std.debug.print("unable to open '{s}': {s}\n", .{ build_root_string, @errorName(err), }); @@ -2093,9 +2107,9 @@ pub const GeneratedFile = struct { // so that we can join it with another path (e.g. build root, cache root, etc.) // // dirname("") should still be null, because we can't go up any further. -fn dirnameAllowEmpty(path: []const u8) ?[]const u8 { - return fs.path.dirname(path) orelse { - if (fs.path.isAbsolute(path) or path.len == 0) return null; +fn dirnameAllowEmpty(full_path: []const u8) ?[]const u8 { + return fs.path.dirname(full_path) orelse { + if (fs.path.isAbsolute(full_path) or full_path.len == 0) return null; return ""; }; @@ -2117,11 +2131,15 @@ test dirnameAllowEmpty { /// A reference to an existing or future path. pub const LazyPath = union(enum) { - /// A source file path relative to build root. - /// This should not be an absolute path, but in an older iteration of the zig build - /// system API, it was allowed to be absolute. Absolute paths should use `cwd_relative`. + /// Deprecated; use the `path` function instead. path: []const u8, + /// A source file path relative to build root. + src_path: struct { + owner: *std.Build, + sub_path: []const u8, + }, + /// A file that is generated by an interface. Those files usually are /// not available until built by a build step. generated: *const GeneratedFile, @@ -2150,11 +2168,10 @@ pub const LazyPath = union(enum) { sub_path: []const u8, }, - /// Returns a new file source that will have a relative path to the build root guaranteed. - /// Asserts the parameter is not an absolute path. - pub fn relative(path: []const u8) LazyPath { - std.debug.assert(!std.fs.path.isAbsolute(path)); - return LazyPath{ .path = path }; + /// Deprecated. Call `path` instead. + pub fn relative(p: []const u8) LazyPath { + std.log.warn("deprecated. call std.Build.path instead", .{}); + return .{ .path = p }; } /// Returns a lazy path referring to the directory containing this path. @@ -2168,13 +2185,16 @@ pub const LazyPath = union(enum) { return switch (self) { .generated => |gen| .{ .generated_dirname = .{ .generated = gen, .up = 0 } }, .generated_dirname => |gen| .{ .generated_dirname = .{ .generated = gen.generated, .up = gen.up + 1 } }, + .src_path => |sp| .{ .src_path = .{ + .owner = sp.owner, + .sub_path = dirnameAllowEmpty(sp.sub_path) orelse { + dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {}; + @panic("misconfigured build script"); + }, + } }, .path => |p| .{ .path = dirnameAllowEmpty(p) orelse { - dumpBadDirnameHelp(null, null, - \\dirname() attempted to traverse outside the build root. - \\This is not allowed. - \\ - , .{}) catch {}; + dumpBadDirnameHelp(null, null, "dirname() attempted to traverse outside the build root\n", .{}) catch {}; @panic("misconfigured build script"); }, }, @@ -2195,7 +2215,6 @@ pub const LazyPath = union(enum) { } else { dumpBadDirnameHelp(null, null, \\dirname() attempted to traverse outside the current working directory. - \\This is not allowed. \\ , .{}) catch {}; @panic("misconfigured build script"); @@ -2207,7 +2226,6 @@ pub const LazyPath = union(enum) { .sub_path = dirnameAllowEmpty(dep.sub_path) orelse { dumpBadDirnameHelp(null, null, \\dirname() attempted to traverse outside the dependency root. - \\This is not allowed. \\ , .{}) catch {}; @panic("misconfigured build script"); @@ -2220,7 +2238,8 @@ pub const LazyPath = union(enum) { /// Either returns the path or `"generated"`. pub fn getDisplayName(self: LazyPath) []const u8 { return switch (self) { - .path, .cwd_relative => self.path, + .src_path => |sp| sp.sub_path, + .path, .cwd_relative => |p| p, .generated => "generated", .generated_dirname => "generated", .dependency => "dependency", @@ -2230,7 +2249,7 @@ pub const LazyPath = union(enum) { /// Adds dependencies this file source implies to the given step. pub fn addStepDependencies(self: LazyPath, other_step: *Step) void { switch (self) { - .path, .cwd_relative, .dependency => {}, + .src_path, .path, .cwd_relative, .dependency => {}, .generated => |gen| other_step.dependOn(gen.step), .generated_dirname => |gen| other_step.dependOn(gen.generated.step), } @@ -2250,6 +2269,7 @@ pub const LazyPath = union(enum) { pub fn getPath2(self: LazyPath, src_builder: *Build, asking_step: ?*Step) []const u8 { switch (self) { .path => |p| return src_builder.pathFromRoot(p), + .src_path => |sp| return sp.owner.pathFromRoot(sp.sub_path), .cwd_relative => |p| return src_builder.pathFromCwd(p), .generated => |gen| return gen.path orelse { std.debug.getStderrMutex().lock(); @@ -2262,13 +2282,13 @@ pub const LazyPath = union(enum) { (src_builder.cache_root.join(src_builder.allocator, &.{"."}) catch @panic("OOM")); const gen_step = gen.generated.step; - var path = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step); + var p = getPath2(LazyPath{ .generated = gen.generated }, src_builder, asking_step); var i: usize = 0; while (i <= gen.up) : (i += 1) { // path is absolute. // dirname will return null only if we're at root. // Typically, we'll stop well before that at the cache root. - path = fs.path.dirname(path) orelse { + p = fs.path.dirname(p) orelse { dumpBadDirnameHelp(gen_step, asking_step, \\dirname() reached root. \\No more directories left to go up. @@ -2277,7 +2297,7 @@ pub const LazyPath = union(enum) { @panic("misconfigured build script"); }; - if (mem.eql(u8, path, cache_root_path) and i < gen.up) { + if (mem.eql(u8, p, cache_root_path) and i < gen.up) { // If we hit the cache root and there's still more to go, // the script attempted to go too far. dumpBadDirnameHelp(gen_step, asking_step, @@ -2288,7 +2308,7 @@ pub const LazyPath = union(enum) { @panic("misconfigured build script"); } } - return path; + return p; }, .dependency => |dep| { return dep.dependency.builder.pathJoin(&[_][]const u8{ @@ -2299,9 +2319,16 @@ pub const LazyPath = union(enum) { } } - /// Duplicates the file source for a given builder. + /// Copies the internal strings. + /// + /// The `b` parameter is only used for its allocator. All *Build instances + /// share the same allocator. pub fn dupe(self: LazyPath, b: *Build) LazyPath { return switch (self) { + .src_path => |sp| .{ .src_path = .{ + .owner = sp.owner, + .sub_path = sp.owner.dupePath(sp.sub_path), + } }, .path => |p| .{ .path = b.dupePath(p) }, .cwd_relative => |p| .{ .cwd_relative = b.dupePath(p) }, .generated => |gen| .{ .generated = gen }, diff --git a/lib/std/Build/Module.zig b/lib/std/Build/Module.zig index 02386de430..d19f0a781e 100644 --- a/lib/std/Build/Module.zig +++ b/lib/std/Build/Module.zig @@ -451,7 +451,7 @@ pub fn linkFramework(m: *Module, name: []const u8, options: LinkFrameworkOptions pub const AddCSourceFilesOptions = struct { /// When provided, `files` are relative to `root` rather than the /// package that owns the `Compile` step. - root: LazyPath = .{ .path = "" }, + root: ?LazyPath = null, files: []const []const u8, flags: []const []const u8 = &.{}, }; @@ -472,7 +472,7 @@ pub fn addCSourceFiles(m: *Module, options: AddCSourceFilesOptions) void { const c_source_files = allocator.create(CSourceFiles) catch @panic("OOM"); c_source_files.* = .{ - .root = options.root, + .root = options.root orelse b.path(""), .files = b.dupeStrings(options.files), .flags = b.dupeStrings(options.flags), }; @@ -573,17 +573,6 @@ pub fn addLibraryPath(m: *Module, directory_path: LazyPath) void { pub fn addRPath(m: *Module, directory_path: LazyPath) void { const b = m.owner; - switch (directory_path) { - .path, .cwd_relative => |path| { - // TODO: remove this check after people upgrade and stop expecting it to work - if (std.mem.startsWith(u8, path, "@executable_path") or - std.mem.startsWith(u8, path, "@loader_path")) - { - @panic("this function is for adding directory paths. It does not support special rpaths. use addRPathSpecial for that."); - } - }, - else => {}, - } m.rpaths.append(b.allocator, .{ .lazy_path = directory_path.dupe(b) }) catch @panic("OOM"); addLazyPathDependenciesOnly(m, directory_path); } diff --git a/lib/std/Build/Step/CheckObject.zig b/lib/std/Build/Step/CheckObject.zig index c39a487649..f8f0fcd584 100644 --- a/lib/std/Build/Step/CheckObject.zig +++ b/lib/std/Build/Step/CheckObject.zig @@ -247,15 +247,27 @@ const ComputeCompareExpected = struct { const Check = struct { kind: Kind, + payload: Payload, + data: std.ArrayList(u8), actions: std.ArrayList(Action), fn create(allocator: Allocator, kind: Kind) Check { return .{ .kind = kind, + .payload = .{ .none = {} }, + .data = std.ArrayList(u8).init(allocator), .actions = std.ArrayList(Action).init(allocator), }; } + fn dumpSection(allocator: Allocator, name: [:0]const u8) Check { + var check = Check.create(allocator, .dump_section); + const off: u32 = @intCast(check.data.items.len); + check.data.writer().print("{s}\x00", .{name}) catch @panic("OOM"); + check.payload = .{ .dump_section = off }; + return check; + } + fn extract(self: *Check, phrase: SearchPhrase) void { self.actions.append(.{ .tag = .extract, @@ -305,6 +317,13 @@ const Check = struct { dyld_lazy_bind, exports, compute_compare, + dump_section, + }; + + const Payload = union { + none: void, + /// Null-delimited string in the 'data' buffer. + dump_section: u32, }; }; @@ -513,6 +532,11 @@ pub fn checkInArchiveSymtab(self: *CheckObject) void { self.checkExact(label); } +pub fn dumpSection(self: *CheckObject, name: [:0]const u8) void { + const new_check = Check.dumpSection(self.step.owner.allocator, name); + self.checks.append(new_check) catch @panic("OOM"); +} + /// Creates a new standalone, singular check which allows running simple binary operations /// on the extracted variables. It will then compare the reduced program with the value of /// the expected variable. @@ -564,13 +588,44 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } const output = switch (self.obj_format) { - .macho => try MachODumper.parseAndDump(step, chk.kind, contents), - .elf => try ElfDumper.parseAndDump(step, chk.kind, contents), + .macho => try MachODumper.parseAndDump(step, chk, contents), + .elf => try ElfDumper.parseAndDump(step, chk, contents), .coff => return step.fail("TODO coff parser", .{}), - .wasm => try WasmDumper.parseAndDump(step, chk.kind, contents), + .wasm => try WasmDumper.parseAndDump(step, chk, contents), else => unreachable, }; + // Depending on whether we requested dumping section verbatim or not, + // we either format message string with escaped codes, or not to aid debugging + // the failed test. + const fmtMessageString = struct { + fn fmtMessageString(kind: Check.Kind, msg: []const u8) std.fmt.Formatter(formatMessageString) { + return .{ .data = .{ + .kind = kind, + .msg = msg, + } }; + } + + const Ctx = struct { + kind: Check.Kind, + msg: []const u8, + }; + + fn formatMessageString( + ctx: Ctx, + comptime unused_fmt_string: []const u8, + options: std.fmt.FormatOptions, + writer: anytype, + ) !void { + _ = unused_fmt_string; + _ = options; + switch (ctx.kind) { + .dump_section => try writer.print("{s}", .{std.fmt.fmtSliceEscapeLower(ctx.msg)}), + else => try writer.writeAll(ctx.msg), + } + } + }.fmtMessageString; + var it = mem.tokenizeAny(u8, output, "\r\n"); for (chk.actions.items) |act| { switch (act.tag) { @@ -585,7 +640,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -600,7 +655,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -614,7 +669,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does contain it: ======== \\{s} \\=================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ fmtMessageString(chk.kind, act.phrase.resolve(b, step)), fmtMessageString(chk.kind, output) }); } }, @@ -629,7 +684,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { \\========= but parsed file does not contain it: ======= \\{s} \\====================================================== - , .{ act.phrase.resolve(b, step), output }); + , .{ act.phrase.resolve(b, step), fmtMessageString(chk.kind, output) }); } }, @@ -660,7 +715,7 @@ const MachODumper = struct { } }; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -731,7 +786,7 @@ const MachODumper = struct { } } - switch (kind) { + switch (check.kind) { .headers => { try dumpHeader(hdr, writer); @@ -764,7 +819,7 @@ const MachODumper = struct { if (dyld_info_lc == null) return step.fail("no dyld info found", .{}); const lc = dyld_info_lc.?; - switch (kind) { + switch (check.kind) { .dyld_rebase => if (lc.rebase_size > 0) { const data = bytes[lc.rebase_off..][0..lc.rebase_size]; try writer.writeAll(dyld_rebase_label ++ "\n"); @@ -805,7 +860,7 @@ const MachODumper = struct { return step.fail("no exports data found", .{}); }, - else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(kind)}), + else => return step.fail("invalid check kind for MachO file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -1633,14 +1688,14 @@ const ElfDumper = struct { const dynamic_section_label = "dynamic section"; const archive_symtab_label = "archive symbol table"; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { - return parseAndDumpArchive(step, kind, bytes) catch |err| switch (err) { - error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, kind, bytes), + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { + return parseAndDumpArchive(step, check, bytes) catch |err| switch (err) { + error.InvalidArchiveMagicNumber => try parseAndDumpObject(step, check, bytes), else => |e| return e, }; } - fn parseAndDumpArchive(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDumpArchive(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1702,13 +1757,13 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .archive_symtab => if (ctx.symtab.items.len > 0) { try ctx.dumpSymtab(writer); } else return step.fail("no archive symbol table found", .{}), else => if (ctx.objects.items.len > 0) { - try ctx.dumpObjects(step, kind, writer); + try ctx.dumpObjects(step, check, writer); } else return step.fail("empty archive", .{}), } @@ -1785,10 +1840,10 @@ const ElfDumper = struct { } } - fn dumpObjects(ctx: ArchiveContext, step: *Step, kind: Check.Kind, writer: anytype) !void { + fn dumpObjects(ctx: ArchiveContext, step: *Step, check: Check, writer: anytype) !void { for (ctx.objects.items) |object| { try writer.print("object {s}\n", .{object.name}); - const output = try parseAndDumpObject(step, kind, ctx.data[object.off..][0..object.len]); + const output = try parseAndDumpObject(step, check, ctx.data[object.off..][0..object.len]); defer ctx.gpa.free(output); try writer.print("{s}\n", .{output}); } @@ -1806,7 +1861,7 @@ const ElfDumper = struct { }; }; - fn parseAndDumpObject(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDumpObject(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var stream = std.io.fixedBufferStream(bytes); const reader = stream.reader(); @@ -1859,7 +1914,7 @@ const ElfDumper = struct { var output = std.ArrayList(u8).init(gpa); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .headers => { try ctx.dumpHeader(writer); try ctx.dumpShdrs(writer); @@ -1878,7 +1933,13 @@ const ElfDumper = struct { try ctx.dumpDynamicSection(shndx, writer); } else return step.fail("no .dynamic section found", .{}), - else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(kind)}), + .dump_section => { + const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(check.data.items.ptr + check.payload.dump_section)), 0); + const shndx = ctx.getSectionByName(name) orelse return step.fail("no '{s}' section found", .{name}); + try ctx.dumpSection(shndx, writer); + }, + + else => return step.fail("invalid check kind for ELF file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -2176,6 +2237,11 @@ const ElfDumper = struct { } } + fn dumpSection(ctx: ObjectContext, shndx: usize, writer: anytype) !void { + const data = ctx.getSectionContents(shndx); + try writer.print("{s}", .{data}); + } + inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { const shdr = ctx.shdrs[shndx]; return getString(ctx.shstrtab, shdr.sh_name); @@ -2300,7 +2366,7 @@ const ElfDumper = struct { const WasmDumper = struct { const symtab_label = "symbols"; - fn parseAndDump(step: *Step, kind: Check.Kind, bytes: []const u8) ![]const u8 { + fn parseAndDump(step: *Step, check: Check, bytes: []const u8) ![]const u8 { const gpa = step.owner.allocator; var fbs = std.io.fixedBufferStream(bytes); const reader = fbs.reader(); @@ -2317,7 +2383,7 @@ const WasmDumper = struct { errdefer output.deinit(); const writer = output.writer(); - switch (kind) { + switch (check.kind) { .headers => { while (reader.readByte()) |current_byte| { const section = std.meta.intToEnum(std.wasm.Section, current_byte) catch { @@ -2330,7 +2396,7 @@ const WasmDumper = struct { } else |_| {} // reached end of stream }, - else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(kind)}), + else => return step.fail("invalid check kind for Wasm file format: {s}", .{@tagName(check.kind)}), } return output.toOwnedSlice(); @@ -2364,7 +2430,7 @@ const WasmDumper = struct { => { const entries = try std.leb.readULEB128(u32, reader); try writer.print("\nentries {d}\n", .{entries}); - try dumpSection(step, section, data[fbs.pos..], entries, writer); + try parseSection(step, section, data[fbs.pos..], entries, writer); }, .custom => { const name_length = try std.leb.readULEB128(u32, reader); @@ -2393,7 +2459,7 @@ const WasmDumper = struct { } } - fn dumpSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { + fn parseSection(step: *Step, section: std.wasm.Section, data: []const u8, entries: u32, writer: anytype) !void { var fbs = std.io.fixedBufferStream(data); const reader = fbs.reader(); diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 4a5364176a..c36fd8c4ca 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -55,7 +55,7 @@ global_base: ?u64 = null, zig_lib_dir: ?LazyPath, exec_cmd_args: ?[]const ?[]const u8, filters: []const []const u8, -test_runner: ?[]const u8, +test_runner: ?LazyPath, test_server_mode: bool, wasi_exec_model: ?std.builtin.WasiExecModel = null, @@ -236,7 +236,7 @@ pub const Options = struct { version: ?std.SemanticVersion = null, max_rss: usize = 0, filters: []const []const u8 = &.{}, - test_runner: ?[]const u8 = null, + test_runner: ?LazyPath = null, use_llvm: ?bool = null, use_lld: ?bool = null, zig_lib_dir: ?LazyPath = null, @@ -264,20 +264,8 @@ pub const HeaderInstallation = union(enum) { dest_rel_path: []const u8, pub fn dupe(self: File, b: *std.Build) File { - // 'path' lazy paths are relative to the build root of some step, inferred from the step - // in which they are used. This means that we can't dupe such paths, because they may - // come from dependencies with their own build roots and duping the paths as is might - // cause the build script to search for the file relative to the wrong root. - // As a temporary workaround, we convert build root-relative paths to absolute paths. - // If/when the build-root relative paths are updated to encode which build root they are - // relative to, this workaround should be removed. - const duped_source: LazyPath = switch (self.source) { - .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, - else => self.source.dupe(b), - }; - return .{ - .source = duped_source, + .source = self.source.dupe(b), .dest_rel_path = b.dupePath(self.dest_rel_path), }; } @@ -305,20 +293,8 @@ pub const HeaderInstallation = union(enum) { }; pub fn dupe(self: Directory, b: *std.Build) Directory { - // 'path' lazy paths are relative to the build root of some step, inferred from the step - // in which they are used. This means that we can't dupe such paths, because they may - // come from dependencies with their own build roots and duping the paths as is might - // cause the build script to search for the file relative to the wrong root. - // As a temporary workaround, we convert build root-relative paths to absolute paths. - // If/when the build-root relative paths are updated to encode which build root they are - // relative to, this workaround should be removed. - const duped_source: LazyPath = switch (self.source) { - .path => |root_rel| .{ .cwd_relative = b.pathFromRoot(root_rel) }, - else => self.source.dupe(b), - }; - return .{ - .source = duped_source, + .source = self.source.dupe(b), .dest_rel_path = b.dupePath(self.dest_rel_path), .options = self.options.dupe(b), }; @@ -402,7 +378,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .zig_lib_dir = null, .exec_cmd_args = null, .filters = options.filters, - .test_runner = options.test_runner, + .test_runner = null, .test_server_mode = options.test_runner == null, .rdynamic = false, .installed_path = null, @@ -429,6 +405,11 @@ pub fn create(owner: *std.Build, options: Options) *Compile { lp.addStepDependencies(&self.step); } + if (options.test_runner) |lp| { + self.test_runner = lp.dupe(self.step.owner); + lp.addStepDependencies(&self.step); + } + // Only the PE/COFF format has a Resource Table which is where the manifest // gets embedded, so for any other target the manifest file is just ignored. if (target.ofmt == .coff) { @@ -1402,7 +1383,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (self.test_runner) |test_runner| { try zig_args.append("--test-runner"); - try zig_args.append(b.pathFromRoot(test_runner)); + try zig_args.append(test_runner.getPath(b)); } for (b.debug_log_scopes) |log_scope| { diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index 46631cac24..e547f8082e 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -58,6 +58,7 @@ pub fn create(owner: *std.Build, options: Options) *ConfigHeader { if (options.style.getPath()) |s| default_include_path: { const sub_path = switch (s) { + .src_path => |sp| sp.sub_path, .path => |path| path, .generated, .generated_dirname => break :default_include_path, .cwd_relative => |sub_path| sub_path, @@ -192,13 +193,21 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { .autoconf => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); - const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); + const contents = std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes) catch |err| { + return step.fail("unable to read autoconf input file '{s}': {s}", .{ + src_path, @errorName(err), + }); + }; try render_autoconf(step, contents, &output, self.values, src_path); }, .cmake => |file_source| { try output.appendSlice(c_generated_line); const src_path = file_source.getPath(b); - const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes); + const contents = std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes) catch |err| { + return step.fail("unable to read cmake input file '{s}': {s}", .{ + src_path, @errorName(err), + }); + }; try render_cmake(step, contents, &output, self.values, src_path); }, .blank => { diff --git a/lib/std/Target.zig b/lib/std/Target.zig index 842442e37b..55cde46308 100644 --- a/lib/std/Target.zig +++ b/lib/std/Target.zig @@ -2740,6 +2740,13 @@ pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool { return true; } + if (target.os.tag == .haiku) { + if (eqlIgnoreCase(ignore_case, name, "root")) + return true; + if (eqlIgnoreCase(ignore_case, name, "network")) + return true; + } + return false; } diff --git a/lib/std/Uri.zig b/lib/std/Uri.zig index cbd3d42741..ee0c602125 100644 --- a/lib/std/Uri.zig +++ b/lib/std/Uri.zig @@ -1,156 +1,157 @@ //! Uniform Resource Identifier (URI) parsing roughly adhering to <https://tools.ietf.org/html/rfc3986>. //! Does not do perfect grammar and character class checking, but should be robust against URIs in the wild. -const Uri = @This(); -const std = @import("std.zig"); -const testing = std.testing; -const Allocator = std.mem.Allocator; - scheme: []const u8, -user: ?[]const u8 = null, -password: ?[]const u8 = null, -host: ?[]const u8 = null, +user: ?Component = null, +password: ?Component = null, +host: ?Component = null, port: ?u16 = null, -path: []const u8, -query: ?[]const u8 = null, -fragment: ?[]const u8 = null, - -/// Applies URI encoding and replaces all reserved characters with their respective %XX code. -pub fn escapeString(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - return escapeStringWithFn(allocator, input, isUnreserved); -} - -pub fn escapePath(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - return escapeStringWithFn(allocator, input, isPathChar); -} - -pub fn escapeQuery(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - return escapeStringWithFn(allocator, input, isQueryChar); -} - -pub fn writeEscapedString(writer: anytype, input: []const u8) !void { - return writeEscapedStringWithFn(writer, input, isUnreserved); -} - -pub fn writeEscapedPath(writer: anytype, input: []const u8) !void { - return writeEscapedStringWithFn(writer, input, isPathChar); -} - -pub fn writeEscapedQuery(writer: anytype, input: []const u8) !void { - return writeEscapedStringWithFn(writer, input, isQueryChar); -} - -pub fn escapeStringWithFn(allocator: Allocator, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) Allocator.Error![]u8 { - var outsize: usize = 0; - for (input) |c| { - outsize += if (keepUnescaped(c)) @as(usize, 1) else 3; +path: Component = Component.empty, +query: ?Component = null, +fragment: ?Component = null, + +pub const Component = union(enum) { + /// Invalid characters in this component must be percent encoded + /// before being printed as part of a URI. + raw: []const u8, + /// This component is already percent-encoded, it can be printed + /// directly as part of a URI. + percent_encoded: []const u8, + + pub const empty: Component = .{ .percent_encoded = "" }; + + pub fn isEmpty(component: Component) bool { + return switch (component) { + .raw, .percent_encoded => |string| string.len == 0, + }; } - var output = try allocator.alloc(u8, outsize); - var outptr: usize = 0; - for (input) |c| { - if (keepUnescaped(c)) { - output[outptr] = c; - outptr += 1; - } else { - var buf: [2]u8 = undefined; - _ = std.fmt.bufPrint(&buf, "{X:0>2}", .{c}) catch unreachable; - - output[outptr + 0] = '%'; - output[outptr + 1] = buf[0]; - output[outptr + 2] = buf[1]; - outptr += 3; - } + /// Allocates the result with `arena` only if needed, so the result should not be freed. + pub fn toRawMaybeAlloc( + component: Component, + arena: std.mem.Allocator, + ) std.mem.Allocator.Error![]const u8 { + return switch (component) { + .raw => |raw| raw, + .percent_encoded => |percent_encoded| if (std.mem.indexOfScalar(u8, percent_encoded, '%')) |_| + try std.fmt.allocPrint(arena, "{raw}", .{component}) + else + percent_encoded, + }; } - return output; -} -pub fn writeEscapedStringWithFn(writer: anytype, input: []const u8, comptime keepUnescaped: fn (c: u8) bool) @TypeOf(writer).Error!void { - for (input) |c| { - if (keepUnescaped(c)) { - try writer.writeByte(c); - } else { - try writer.print("%{X:0>2}", .{c}); - } + pub fn format( + component: Component, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (fmt_str.len == 0) { + try writer.print("std.Uri.Component{{ .{s} = \"{}\" }}", .{ + @tagName(component), + std.zig.fmtEscapes(switch (component) { + .raw, .percent_encoded => |string| string, + }), + }); + } else if (comptime std.mem.eql(u8, fmt_str, "raw")) switch (component) { + .raw => |raw| try writer.writeAll(raw), + .percent_encoded => |percent_encoded| { + var start: usize = 0; + var index: usize = 0; + while (std.mem.indexOfScalarPos(u8, percent_encoded, index, '%')) |percent| { + index = percent + 1; + if (percent_encoded.len - index < 2) continue; + const percent_encoded_char = + std.fmt.parseInt(u8, percent_encoded[index..][0..2], 16) catch continue; + try writer.print("{s}{c}", .{ + percent_encoded[start..percent], + percent_encoded_char, + }); + start = percent + 3; + index = percent + 3; + } + try writer.writeAll(percent_encoded[start..]); + }, + } else if (comptime std.mem.eql(u8, fmt_str, "%")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isUnreserved), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "user")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isUserChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "password")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isPasswordChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "host")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isHostChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "path")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isPathChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "query")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isQueryChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else if (comptime std.mem.eql(u8, fmt_str, "fragment")) switch (component) { + .raw => |raw| try percentEncode(writer, raw, isFragmentChar), + .percent_encoded => |percent_encoded| try writer.writeAll(percent_encoded), + } else @compileError("invalid format string '" ++ fmt_str ++ "'"); } -} -/// Parses a URI string and unescapes all %XX where XX is a valid hex number. Otherwise, verbatim copies -/// them to the output. -pub fn unescapeString(allocator: Allocator, input: []const u8) error{OutOfMemory}![]u8 { - var outsize: usize = 0; - var inptr: usize = 0; - while (inptr < input.len) { - if (input[inptr] == '%') { - inptr += 1; - if (inptr + 2 <= input.len) { - _ = std.fmt.parseInt(u8, input[inptr..][0..2], 16) catch { - outsize += 3; - inptr += 2; - continue; - }; - inptr += 2; - outsize += 1; - } else { - outsize += 1; - } - } else { - inptr += 1; - outsize += 1; + pub fn percentEncode( + writer: anytype, + raw: []const u8, + comptime isValidChar: fn (u8) bool, + ) @TypeOf(writer).Error!void { + var start: usize = 0; + for (raw, 0..) |char, index| { + if (isValidChar(char)) continue; + try writer.print("{s}%{X:0>2}", .{ raw[start..index], char }); + start = index + 1; } + try writer.writeAll(raw[start..]); } +}; - var output = try allocator.alloc(u8, outsize); - var outptr: usize = 0; - inptr = 0; - while (inptr < input.len) { - if (input[inptr] == '%') { - inptr += 1; - if (inptr + 2 <= input.len) { - const value = std.fmt.parseInt(u8, input[inptr..][0..2], 16) catch { - output[outptr + 0] = input[inptr + 0]; - output[outptr + 1] = input[inptr + 1]; - inptr += 2; - outptr += 2; +/// Percent decodes all %XX where XX is a valid hex number. +/// `output` may alias `input` if `output.ptr <= input.ptr`. +/// Mutates and returns a subslice of `output`. +pub fn percentDecodeBackwards(output: []u8, input: []const u8) []u8 { + var input_index = input.len; + var output_index = output.len; + while (input_index > 0) { + if (input_index >= 3) { + const maybe_percent_encoded = input[input_index - 3 ..][0..3]; + if (maybe_percent_encoded[0] == '%') { + if (std.fmt.parseInt(u8, maybe_percent_encoded[1..], 16)) |percent_encoded_char| { + input_index -= maybe_percent_encoded.len; + output_index -= 1; + output[output_index] = percent_encoded_char; continue; - }; - - output[outptr] = value; - - inptr += 2; - outptr += 1; - } else { - output[outptr] = input[inptr - 1]; - outptr += 1; + } else |_| {} } - } else { - output[outptr] = input[inptr]; - inptr += 1; - outptr += 1; } + input_index -= 1; + output_index -= 1; + output[output_index] = input[input_index]; } - return output; + return output[output_index..]; +} + +/// Percent decodes all %XX where XX is a valid hex number. +/// Mutates and returns a subslice of `buffer`. +pub fn percentDecodeInPlace(buffer: []u8) []u8 { + return percentDecodeBackwards(buffer, buffer); } pub const ParseError = error{ UnexpectedCharacter, InvalidFormat, InvalidPort }; /// Parses the URI or returns an error. This function is not compliant, but is required to parse /// some forms of URIs in the wild, such as HTTP Location headers. -/// The return value will contain unescaped strings pointing into the -/// original `text`. Each component that is provided, will be non-`null`. -pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { +/// The return value will contain strings pointing into the original `text`. +/// Each component that is provided, will be non-`null`. +pub fn parseAfterScheme(scheme: []const u8, text: []const u8) ParseError!Uri { var reader = SliceReader{ .slice = text }; - var uri = Uri{ - .scheme = "", - .user = null, - .password = null, - .host = null, - .port = null, - .path = "", // path is always set, but empty by default. - .query = null, - .fragment = null, - }; + var uri: Uri = .{ .scheme = scheme, .path = undefined }; if (reader.peekPrefix("//")) a: { // authority part std.debug.assert(reader.get().? == '/'); @@ -167,12 +168,12 @@ pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { const user_info = authority[0..index]; if (std.mem.indexOf(u8, user_info, ":")) |idx| { - uri.user = user_info[0..idx]; + uri.user = .{ .percent_encoded = user_info[0..idx] }; if (idx < user_info.len - 1) { // empty password is also "no password" - uri.password = user_info[idx + 1 ..]; + uri.password = .{ .percent_encoded = user_info[idx + 1 ..] }; } } else { - uri.user = user_info; + uri.user = .{ .percent_encoded = user_info }; uri.password = null; } } @@ -205,19 +206,19 @@ pub fn parseWithoutScheme(text: []const u8) ParseError!Uri { } if (start_of_host >= end_of_host) return error.InvalidFormat; - uri.host = authority[start_of_host..end_of_host]; + uri.host = .{ .percent_encoded = authority[start_of_host..end_of_host] }; } - uri.path = reader.readUntil(isPathSeparator); + uri.path = .{ .percent_encoded = reader.readUntil(isPathSeparator) }; if ((reader.peek() orelse 0) == '?') { // query part std.debug.assert(reader.get().? == '?'); - uri.query = reader.readUntil(isQuerySeparator); + uri.query = .{ .percent_encoded = reader.readUntil(isQuerySeparator) }; } if ((reader.peek() orelse 0) == '#') { // fragment part std.debug.assert(reader.get().? == '#'); - uri.fragment = reader.readUntilEof(); + uri.fragment = .{ .percent_encoded = reader.readUntilEof() }; } return uri; @@ -242,8 +243,8 @@ pub const WriteToStreamOptions = struct { /// When true, include the fragment part of the URI. Ignored when `path` is false. fragment: bool = false, - /// When true, do not escape any part of the URI. - raw: bool = false, + /// When true, include the port part of the URI. Ignored when `port` is null. + port: bool = true, }; pub fn writeToStream( @@ -252,80 +253,53 @@ pub fn writeToStream( writer: anytype, ) @TypeOf(writer).Error!void { if (options.scheme) { - try writer.writeAll(uri.scheme); - try writer.writeAll(":"); - + try writer.print("{s}:", .{uri.scheme}); if (options.authority and uri.host != null) { try writer.writeAll("//"); } } - if (options.authority) { if (options.authentication and uri.host != null) { if (uri.user) |user| { - try writer.writeAll(user); + try writer.print("{user}", .{user}); if (uri.password) |password| { - try writer.writeAll(":"); - try writer.writeAll(password); + try writer.print(":{password}", .{password}); } - try writer.writeAll("@"); + try writer.writeByte('@'); } } - if (uri.host) |host| { - try writer.writeAll(host); - - if (uri.port) |port| { - try writer.writeAll(":"); - try std.fmt.formatInt(port, 10, .lower, .{}, writer); + try writer.print("{host}", .{host}); + if (options.port) { + if (uri.port) |port| try writer.print(":{d}", .{port}); } } } - if (options.path) { - if (uri.path.len == 0) { - try writer.writeAll("/"); - } else if (options.raw) { - try writer.writeAll(uri.path); - } else { - try writeEscapedPath(writer, uri.path); + try writer.print("{path}", .{ + if (uri.path.isEmpty()) Uri.Component{ .percent_encoded = "/" } else uri.path, + }); + if (options.query) { + if (uri.query) |query| try writer.print("?{query}", .{query}); + } + if (options.fragment) { + if (uri.fragment) |fragment| try writer.print("#{fragment}", .{fragment}); } - - if (options.query) if (uri.query) |q| { - try writer.writeAll("?"); - if (options.raw) { - try writer.writeAll(q); - } else { - try writeEscapedQuery(writer, q); - } - }; - - if (options.fragment) if (uri.fragment) |f| { - try writer.writeAll("#"); - if (options.raw) { - try writer.writeAll(f); - } else { - try writeEscapedQuery(writer, f); - } - }; } } pub fn format( uri: Uri, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - _ = options; - - const scheme = comptime std.mem.indexOf(u8, fmt, ";") != null or fmt.len == 0; - const authentication = comptime std.mem.indexOf(u8, fmt, "@") != null or fmt.len == 0; - const authority = comptime std.mem.indexOf(u8, fmt, "+") != null or fmt.len == 0; - const path = comptime std.mem.indexOf(u8, fmt, "/") != null or fmt.len == 0; - const query = comptime std.mem.indexOf(u8, fmt, "?") != null or fmt.len == 0; - const fragment = comptime std.mem.indexOf(u8, fmt, "#") != null or fmt.len == 0; - const raw = comptime std.mem.indexOf(u8, fmt, "r") != null or fmt.len == 0; + const scheme = comptime std.mem.indexOfScalar(u8, fmt_str, ';') != null or fmt_str.len == 0; + const authentication = comptime std.mem.indexOfScalar(u8, fmt_str, '@') != null or fmt_str.len == 0; + const authority = comptime std.mem.indexOfScalar(u8, fmt_str, '+') != null or fmt_str.len == 0; + const path = comptime std.mem.indexOfScalar(u8, fmt_str, '/') != null or fmt_str.len == 0; + const query = comptime std.mem.indexOfScalar(u8, fmt_str, '?') != null or fmt_str.len == 0; + const fragment = comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null or fmt_str.len == 0; return writeToStream(uri, .{ .scheme = scheme, @@ -334,12 +308,11 @@ pub fn format( .path = path, .query = query, .fragment = fragment, - .raw = raw, }, writer); } /// Parses the URI or returns an error. -/// The return value will contain unescaped strings pointing into the +/// The return value will contain strings pointing into the /// original `text`. Each component that is provided, will be non-`null`. pub fn parse(text: []const u8) ParseError!Uri { var reader: SliceReader = .{ .slice = text }; @@ -353,42 +326,32 @@ pub fn parse(text: []const u8) ParseError!Uri { return error.InvalidFormat; } - var uri = try parseWithoutScheme(reader.readUntilEof()); - uri.scheme = scheme; - - return uri; + return parseAfterScheme(scheme, reader.readUntilEof()); } -pub const ResolveInplaceError = ParseError || error{OutOfMemory}; +pub const ResolveInPlaceError = ParseError || error{NoSpaceLeft}; /// Resolves a URI against a base URI, conforming to RFC 3986, Section 5. -/// Copies `new` to the beginning of `aux_buf`, allowing the slices to overlap, +/// Copies `new` to the beginning of `aux_buf.*`, allowing the slices to overlap, /// then parses `new` as a URI, and then resolves the path in place. /// If a merge needs to take place, the newly constructed path will be stored -/// in `aux_buf` just after the copied `new`. -pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplaceError!Uri { - std.mem.copyForwards(u8, aux_buf, new); +/// in `aux_buf.*` just after the copied `new`, and `aux_buf.*` will be modified +/// to only contain the remaining unused space. +pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: *[]u8) ResolveInPlaceError!Uri { + std.mem.copyForwards(u8, aux_buf.*, new); // At this point, new is an invalid pointer. - const new_mut = aux_buf[0..new.len]; - - const new_parsed, const has_scheme = p: { - break :p .{ - parse(new_mut) catch |first_err| { - break :p .{ - parseWithoutScheme(new_mut) catch return first_err, - false, - }; - }, - true, - }; - }; + const new_mut = aux_buf.*[0..new.len]; + aux_buf.* = aux_buf.*[new.len..]; + const new_parsed = parse(new_mut) catch |err| + (parseAfterScheme("", new_mut) catch return err); // As you can see above, `new_mut` is not a const pointer. - const new_path: []u8 = @constCast(new_parsed.path); + const new_path: []u8 = @constCast(new_parsed.path.percent_encoded); - if (has_scheme) return .{ + if (new_parsed.scheme.len > 0) return .{ .scheme = new_parsed.scheme, .user = new_parsed.user, + .password = new_parsed.password, .host = new_parsed.host, .port = new_parsed.port, .path = remove_dot_segments(new_path), @@ -399,6 +362,7 @@ pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplace if (new_parsed.host) |host| return .{ .scheme = base.scheme, .user = new_parsed.user, + .password = new_parsed.password, .host = host, .port = new_parsed.port, .path = remove_dot_segments(new_path), @@ -406,28 +370,21 @@ pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplace .fragment = new_parsed.fragment, }; - const path, const query = b: { - if (new_path.len == 0) - break :b .{ - base.path, - new_parsed.query orelse base.query, - }; - - if (new_path[0] == '/') - break :b .{ - remove_dot_segments(new_path), - new_parsed.query, - }; - - break :b .{ - try merge_paths(base.path, new_path, aux_buf[new_mut.len..]), - new_parsed.query, - }; + const path, const query = if (new_path.len == 0) .{ + base.path, + new_parsed.query orelse base.query, + } else if (new_path[0] == '/') .{ + remove_dot_segments(new_path), + new_parsed.query, + } else .{ + try merge_paths(base.path, new_path, aux_buf), + new_parsed.query, }; return .{ .scheme = base.scheme, .user = base.user, + .password = base.password, .host = base.host, .port = base.port, .path = path, @@ -437,7 +394,7 @@ pub fn resolve_inplace(base: Uri, new: []const u8, aux_buf: []u8) ResolveInplace } /// In-place implementation of RFC 3986, Section 5.2.4. -fn remove_dot_segments(path: []u8) []u8 { +fn remove_dot_segments(path: []u8) Component { var in_i: usize = 0; var out_i: usize = 0; while (in_i < path.len) { @@ -476,28 +433,28 @@ fn remove_dot_segments(path: []u8) []u8 { } } } - return path[0..out_i]; + return .{ .percent_encoded = path[0..out_i] }; } test remove_dot_segments { { var buffer = "/a/b/c/./../../g".*; - try std.testing.expectEqualStrings("/a/g", remove_dot_segments(&buffer)); + try std.testing.expectEqualStrings("/a/g", remove_dot_segments(&buffer).percent_encoded); } } /// 5.2.3. Merge Paths -fn merge_paths(base: []const u8, new: []u8, aux: []u8) error{OutOfMemory}![]u8 { - if (aux.len < base.len + 1 + new.len) return error.OutOfMemory; - if (base.len == 0) { - aux[0] = '/'; - @memcpy(aux[1..][0..new.len], new); - return remove_dot_segments(aux[0 .. new.len + 1]); +fn merge_paths(base: Component, new: []u8, aux_buf: *[]u8) error{NoSpaceLeft}!Component { + var aux = std.io.fixedBufferStream(aux_buf.*); + if (!base.isEmpty()) { + try aux.writer().print("{path}", .{base}); + aux.pos = std.mem.lastIndexOfScalar(u8, aux.getWritten(), '/') orelse + return remove_dot_segments(new); } - const pos = std.mem.lastIndexOfScalar(u8, base, '/') orelse return remove_dot_segments(new); - @memcpy(aux[0 .. pos + 1], base[0 .. pos + 1]); - @memcpy(aux[pos + 1 ..][0..new.len], new); - return remove_dot_segments(aux[0 .. pos + 1 + new.len]); + try aux.writer().print("/{s}", .{new}); + const merged_path = remove_dot_segments(aux.getWritten()); + aux_buf.* = aux_buf.*[merged_path.percent_encoded.len..]; + return merged_path; } const SliceReader = struct { @@ -561,13 +518,6 @@ fn isSchemeChar(c: u8) bool { }; } -fn isAuthoritySeparator(c: u8) bool { - return switch (c) { - '/', '?', '#' => true, - else => false, - }; -} - /// reserved = gen-delims / sub-delims fn isReserved(c: u8) bool { return isGenLimit(c) or isSubLimit(c); @@ -598,19 +548,40 @@ fn isUnreserved(c: u8) bool { }; } -fn isPathSeparator(c: u8) bool { - return switch (c) { - '?', '#' => true, - else => false, - }; +fn isUserChar(c: u8) bool { + return isUnreserved(c) or isSubLimit(c); +} + +fn isPasswordChar(c: u8) bool { + return isUserChar(c) or c == ':'; +} + +fn isHostChar(c: u8) bool { + return isPasswordChar(c) or c == '[' or c == ']'; } fn isPathChar(c: u8) bool { - return isUnreserved(c) or isSubLimit(c) or c == '/' or c == ':' or c == '@'; + return isUserChar(c) or c == '/' or c == ':' or c == '@'; } fn isQueryChar(c: u8) bool { - return isPathChar(c) or c == '?' or c == '%'; + return isPathChar(c) or c == '?'; +} + +const isFragmentChar = isQueryChar; + +fn isAuthoritySeparator(c: u8) bool { + return switch (c) { + '/', '?', '#' => true, + else => false, + }; +} + +fn isPathSeparator(c: u8) bool { + return switch (c) { + '?', '#' => true, + else => false, + }; } fn isQuerySeparator(c: u8) bool { @@ -623,92 +594,92 @@ fn isQuerySeparator(c: u8) bool { test "basic" { const parsed = try parse("https://ziglang.org/download"); try testing.expectEqualStrings("https", parsed.scheme); - try testing.expectEqualStrings("ziglang.org", parsed.host orelse return error.UnexpectedNull); - try testing.expectEqualStrings("/download", parsed.path); + try testing.expectEqualStrings("ziglang.org", parsed.host.?.percent_encoded); + try testing.expectEqualStrings("/download", parsed.path.percent_encoded); try testing.expectEqual(@as(?u16, null), parsed.port); } test "with port" { const parsed = try parse("http://example:1337/"); try testing.expectEqualStrings("http", parsed.scheme); - try testing.expectEqualStrings("example", parsed.host orelse return error.UnexpectedNull); - try testing.expectEqualStrings("/", parsed.path); + try testing.expectEqualStrings("example", parsed.host.?.percent_encoded); + try testing.expectEqualStrings("/", parsed.path.percent_encoded); try testing.expectEqual(@as(?u16, 1337), parsed.port); } test "should fail gracefully" { - try std.testing.expectEqual(@as(ParseError!Uri, error.InvalidFormat), parse("foobar://")); + try std.testing.expectError(error.InvalidFormat, parse("foobar://")); } test "file" { const parsed = try parse("file:///"); - try std.testing.expectEqualSlices(u8, "file", parsed.scheme); - try std.testing.expectEqual(@as(?[]const u8, null), parsed.host); - try std.testing.expectEqualSlices(u8, "/", parsed.path); + try std.testing.expectEqualStrings("file", parsed.scheme); + try std.testing.expectEqual(@as(?Component, null), parsed.host); + try std.testing.expectEqualStrings("/", parsed.path.percent_encoded); const parsed2 = try parse("file:///an/absolute/path/to/something"); - try std.testing.expectEqualSlices(u8, "file", parsed2.scheme); - try std.testing.expectEqual(@as(?[]const u8, null), parsed2.host); - try std.testing.expectEqualSlices(u8, "/an/absolute/path/to/something", parsed2.path); + try std.testing.expectEqualStrings("file", parsed2.scheme); + try std.testing.expectEqual(@as(?Component, null), parsed2.host); + try std.testing.expectEqualStrings("/an/absolute/path/to/something", parsed2.path.percent_encoded); const parsed3 = try parse("file://localhost/an/absolute/path/to/another/thing/"); - try std.testing.expectEqualSlices(u8, "file", parsed3.scheme); - try std.testing.expectEqualSlices(u8, "localhost", parsed3.host.?); - try std.testing.expectEqualSlices(u8, "/an/absolute/path/to/another/thing/", parsed3.path); + try std.testing.expectEqualStrings("file", parsed3.scheme); + try std.testing.expectEqualStrings("localhost", parsed3.host.?.percent_encoded); + try std.testing.expectEqualStrings("/an/absolute/path/to/another/thing/", parsed3.path.percent_encoded); } test "scheme" { - try std.testing.expectEqualSlices(u8, "http", (try parse("http:_")).scheme); - try std.testing.expectEqualSlices(u8, "scheme-mee", (try parse("scheme-mee:_")).scheme); - try std.testing.expectEqualSlices(u8, "a.b.c", (try parse("a.b.c:_")).scheme); - try std.testing.expectEqualSlices(u8, "ab+", (try parse("ab+:_")).scheme); - try std.testing.expectEqualSlices(u8, "X+++", (try parse("X+++:_")).scheme); - try std.testing.expectEqualSlices(u8, "Y+-.", (try parse("Y+-.:_")).scheme); + try std.testing.expectEqualStrings("http", (try parse("http:_")).scheme); + try std.testing.expectEqualStrings("scheme-mee", (try parse("scheme-mee:_")).scheme); + try std.testing.expectEqualStrings("a.b.c", (try parse("a.b.c:_")).scheme); + try std.testing.expectEqualStrings("ab+", (try parse("ab+:_")).scheme); + try std.testing.expectEqualStrings("X+++", (try parse("X+++:_")).scheme); + try std.testing.expectEqualStrings("Y+-.", (try parse("Y+-.:_")).scheme); } test "authority" { - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://hostname")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://hostname")).host.?.percent_encoded); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://userinfo@hostname")).host.?); - try std.testing.expectEqualSlices(u8, "userinfo", (try parse("scheme://userinfo@hostname")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://userinfo@hostname")).password); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://userinfo@")).host); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://userinfo@hostname")).host.?.percent_encoded); + try std.testing.expectEqualStrings("userinfo", (try parse("scheme://userinfo@hostname")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://userinfo@hostname")).password); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://userinfo@")).host); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://user:password@hostname")).host.?); - try std.testing.expectEqualSlices(u8, "user", (try parse("scheme://user:password@hostname")).user.?); - try std.testing.expectEqualSlices(u8, "password", (try parse("scheme://user:password@hostname")).password.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://user:password@hostname")).host.?.percent_encoded); + try std.testing.expectEqualStrings("user", (try parse("scheme://user:password@hostname")).user.?.percent_encoded); + try std.testing.expectEqualStrings("password", (try parse("scheme://user:password@hostname")).password.?.percent_encoded); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://hostname:0")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://hostname:0")).host.?.percent_encoded); try std.testing.expectEqual(@as(u16, 1234), (try parse("scheme://hostname:1234")).port.?); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://userinfo@hostname:1234")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://userinfo@hostname:1234")).host.?.percent_encoded); try std.testing.expectEqual(@as(u16, 1234), (try parse("scheme://userinfo@hostname:1234")).port.?); - try std.testing.expectEqualSlices(u8, "userinfo", (try parse("scheme://userinfo@hostname:1234")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://userinfo@hostname:1234")).password); + try std.testing.expectEqualStrings("userinfo", (try parse("scheme://userinfo@hostname:1234")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://userinfo@hostname:1234")).password); - try std.testing.expectEqualSlices(u8, "hostname", (try parse("scheme://user:password@hostname:1234")).host.?); + try std.testing.expectEqualStrings("hostname", (try parse("scheme://user:password@hostname:1234")).host.?.percent_encoded); try std.testing.expectEqual(@as(u16, 1234), (try parse("scheme://user:password@hostname:1234")).port.?); - try std.testing.expectEqualSlices(u8, "user", (try parse("scheme://user:password@hostname:1234")).user.?); - try std.testing.expectEqualSlices(u8, "password", (try parse("scheme://user:password@hostname:1234")).password.?); + try std.testing.expectEqualStrings("user", (try parse("scheme://user:password@hostname:1234")).user.?.percent_encoded); + try std.testing.expectEqualStrings("password", (try parse("scheme://user:password@hostname:1234")).password.?.percent_encoded); } test "authority.password" { - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username@a")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://username@a")).password); + try std.testing.expectEqualStrings("username", (try parse("scheme://username@a")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://username@a")).password); - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username:@a")).user.?); - try std.testing.expectEqual(@as(?[]const u8, null), (try parse("scheme://username:@a")).password); + try std.testing.expectEqualStrings("username", (try parse("scheme://username:@a")).user.?.percent_encoded); + try std.testing.expectEqual(@as(?Component, null), (try parse("scheme://username:@a")).password); - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username:password@a")).user.?); - try std.testing.expectEqualSlices(u8, "password", (try parse("scheme://username:password@a")).password.?); + try std.testing.expectEqualStrings("username", (try parse("scheme://username:password@a")).user.?.percent_encoded); + try std.testing.expectEqualStrings("password", (try parse("scheme://username:password@a")).password.?.percent_encoded); - try std.testing.expectEqualSlices(u8, "username", (try parse("scheme://username::@a")).user.?); - try std.testing.expectEqualSlices(u8, ":", (try parse("scheme://username::@a")).password.?); + try std.testing.expectEqualStrings("username", (try parse("scheme://username::@a")).user.?.percent_encoded); + try std.testing.expectEqualStrings(":", (try parse("scheme://username::@a")).password.?.percent_encoded); } fn testAuthorityHost(comptime hostlist: anytype) !void { inline for (hostlist) |hostname| { - try std.testing.expectEqualSlices(u8, hostname, (try parse("scheme://" ++ hostname)).host.?); + try std.testing.expectEqualStrings(hostname, (try parse("scheme://" ++ hostname)).host.?.percent_encoded); } } @@ -761,11 +732,11 @@ test "RFC example 1" { .scheme = uri[0..3], .user = null, .password = null, - .host = uri[6..17], + .host = .{ .percent_encoded = uri[6..17] }, .port = 8042, - .path = uri[22..33], - .query = uri[34..45], - .fragment = uri[46..50], + .path = .{ .percent_encoded = uri[22..33] }, + .query = .{ .percent_encoded = uri[34..45] }, + .fragment = .{ .percent_encoded = uri[46..50] }, }, try parse(uri)); } @@ -777,7 +748,7 @@ test "RFC example 2" { .password = null, .host = null, .port = null, - .path = uri[4..], + .path = .{ .percent_encoded = uri[4..] }, .query = null, .fragment = null, }, try parse(uri)); @@ -838,55 +809,60 @@ test "Special test" { _ = try parse("https://www.youtube.com/watch?v=dQw4w9WgXcQ&feature=youtu.be&t=0"); } -test "URI escaping" { - const input = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; - const expected = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad"; +test "URI percent encoding" { + try std.testing.expectFmt( + "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad", + "{%}", + .{Component{ .raw = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad" }}, + ); +} - const actual = try escapeString(std.testing.allocator, input); - defer std.testing.allocator.free(actual); +test "URI percent decoding" { + { + const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; + var input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad".*; - try std.testing.expectEqualSlices(u8, expected, actual); -} + try std.testing.expectFmt(expected, "{raw}", .{Component{ .percent_encoded = &input }}); + + var output: [expected.len]u8 = undefined; + try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); + + try std.testing.expectEqualStrings(expected, percentDecodeInPlace(&input)); + } -test "URI unescaping" { - const input = "%5C%C3%B6%2F%20%C3%A4%C3%B6%C3%9F%20~~.adas-https%3A%2F%2Fcanvas%3A123%2F%23ads%26%26sad"; - const expected = "\\ö/ äöß ~~.adas-https://canvas:123/#ads&&sad"; + { + const expected = "/abc%"; + var input = expected.*; - const actual = try unescapeString(std.testing.allocator, input); - defer std.testing.allocator.free(actual); + try std.testing.expectFmt(expected, "{raw}", .{Component{ .percent_encoded = &input }}); - try std.testing.expectEqualSlices(u8, expected, actual); + var output: [expected.len]u8 = undefined; + try std.testing.expectEqualStrings(percentDecodeBackwards(&output, &input), expected); - const decoded = try unescapeString(std.testing.allocator, "/abc%"); - defer std.testing.allocator.free(decoded); - try std.testing.expectEqualStrings("/abc%", decoded); + try std.testing.expectEqualStrings(expected, percentDecodeInPlace(&input)); + } } -test "URI query escaping" { +test "URI query encoding" { const address = "https://objects.githubusercontent.com/?response-content-type=application%2Foctet-stream"; const parsed = try Uri.parse(address); - // format the URI to escape it - const formatted_uri = try std.fmt.allocPrint(std.testing.allocator, "{/?}", .{parsed}); - defer std.testing.allocator.free(formatted_uri); - try std.testing.expectEqualStrings("/?response-content-type=application%2Foctet-stream", formatted_uri); + // format the URI to percent encode it + try std.testing.expectFmt("/?response-content-type=application%2Foctet-stream", "{/?}", .{parsed}); } test "format" { - const uri = Uri{ + const uri: Uri = .{ .scheme = "file", .user = null, .password = null, .host = null, .port = null, - .path = "/foo/bar/baz", + .path = .{ .raw = "/foo/bar/baz" }, .query = null, .fragment = null, }; - var buf = std.ArrayList(u8).init(std.testing.allocator); - defer buf.deinit(); - try buf.writer().print("{;/?#}", .{uri}); - try std.testing.expectEqualSlices(u8, "file:/foo/bar/baz", buf.items); + try std.testing.expectFmt("file:/foo/bar/baz", "{;/?#}", .{uri}); } test "URI malformed input" { @@ -894,3 +870,7 @@ test "URI malformed input" { try std.testing.expectError(error.InvalidFormat, std.Uri.parse("http://]@[")); try std.testing.expectError(error.InvalidFormat, std.Uri.parse("http://lo]s\x85hc@[/8\x10?0Q")); } + +const std = @import("std.zig"); +const testing = std.testing; +const Uri = @This(); diff --git a/lib/std/c/haiku.zig b/lib/std/c/haiku.zig index d75dd3bf00..cd2e4bc296 100644 --- a/lib/std/c/haiku.zig +++ b/lib/std/c/haiku.zig @@ -5,28 +5,18 @@ const maxInt = std.math.maxInt; const iovec = std.posix.iovec; const iovec_const = std.posix.iovec_const; -extern "c" fn _errnop() *c_int; +pub extern "root" fn find_directory(which: directory_which, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) u64; -pub const _errno = _errnop; - -pub extern "c" fn find_directory(which: directory_which, volume: i32, createIt: bool, path_ptr: [*]u8, length: i32) u64; - -pub extern "c" fn find_thread(thread_name: ?*anyopaque) i32; +pub extern "root" fn find_thread(thread_name: ?*anyopaque) i32; -pub extern "c" fn get_system_info(system_info: *system_info) usize; +pub extern "root" fn get_system_info(system_info: *system_info) usize; -pub extern "c" fn _get_team_info(team: c_int, team_info: *team_info, size: usize) i32; +pub extern "root" fn _get_team_info(team: i32, team_info: *team_info, size: usize) i32; -pub extern "c" fn _get_next_area_info(team: c_int, cookie: *i64, area_info: *area_info, size: usize) i32; +pub extern "root" fn _get_next_area_info(team: i32, cookie: *i64, area_info: *area_info, size: usize) i32; // TODO revisit if abi changes or better option becomes apparent -pub extern "c" fn _get_next_image_info(team: c_int, cookie: *i32, image_info: *image_info, size: usize) i32; - -pub extern "c" fn _kern_read_dir(fd: c_int, buf_ptr: [*]u8, nbytes: usize, maxcount: u32) usize; - -pub extern "c" fn _kern_read_stat(fd: c_int, path_ptr: [*]u8, traverse_link: bool, st: *Stat, stat_size: i32) usize; - -pub extern "c" fn _kern_get_current_team() i32; +pub extern "root" fn _get_next_image_info(team: i32, cookie: *i32, image_info: *image_info, size: usize) i32; pub const sem_t = extern struct { type: i32, @@ -45,7 +35,7 @@ pub const pthread_attr_t = extern struct { __stack_address: ?*anyopaque, }; -pub const EAI = enum(c_int) { +pub const EAI = enum(i32) { /// address family for hostname not supported ADDRFAMILY = 1, @@ -99,11 +89,7 @@ pub const AI = struct { pub const AI_NUMERICSERV = AI.NUMERICSERV; -pub const fd_t = c_int; -pub const pid_t = c_int; -pub const uid_t = u32; -pub const gid_t = u32; -pub const mode_t = c_uint; +pub const fd_t = i32; pub const socklen_t = u32; @@ -129,8 +115,8 @@ pub const dl_phdr_info = extern struct { }; pub const Flock = extern struct { - type: c_short, - whence: c_short, + type: i16, + whence: i16, start: off_t, len: off_t, pid: pid_t, @@ -159,54 +145,6 @@ pub const msghdr = extern struct { msg_flags: i32, }; -pub const off_t = i64; -pub const ino_t = u64; - -pub const Stat = extern struct { - dev: i32, - ino: u64, - mode: u32, - nlink: i32, - uid: i32, - gid: i32, - size: i64, - rdev: i32, - blksize: i32, - atim: timespec, - mtim: timespec, - ctim: timespec, - crtim: timespec, - st_type: u32, - blocks: i64, - - pub fn atime(self: @This()) timespec { - return self.atim; - } - pub fn mtime(self: @This()) timespec { - return self.mtim; - } - pub fn ctime(self: @This()) timespec { - return self.ctim; - } - pub fn birthtime(self: @This()) timespec { - return self.crtim; - } -}; - -pub const timespec = extern struct { - tv_sec: isize, - tv_nsec: isize, -}; - -pub const dirent = extern struct { - dev: i32, - pdev: i32, - ino: i64, - pino: i64, - reclen: u16, - name: [256]u8, -}; - pub const B_OS_NAME_LENGTH = 32; // OS.h pub const area_info = extern struct { @@ -354,17 +292,6 @@ pub const PROT = struct { pub const NONE = 0x00; }; -pub const CLOCK = struct { - /// system-wide monotonic clock (aka system time) - pub const MONOTONIC = 0; - /// system-wide real time clock - pub const REALTIME = -1; - /// clock measuring the used CPU time of the current process - pub const PROCESS_CPUTIME_ID = -2; - /// clock measuring the used CPU time of the current thread - pub const THREAD_CPUTIME_ID = -3; -}; - pub const MSF = struct { pub const ASYNC = 1; pub const INVALIDATE = 2; @@ -404,159 +331,526 @@ pub const W = struct { } }; -// /system/develop/headers/posix/poll.h +// access function +pub const F_OK = 0; // test for existence of file +pub const X_OK = 1; // test for execute or search permission +pub const W_OK = 2; // test for write permission +pub const R_OK = 4; // test for read permission -pub const nfds_t = usize; +pub const F = struct { + pub const DUPFD = 0x0001; + pub const GETFD = 0x0002; + pub const SETFD = 0x0004; + pub const GETFL = 0x0008; + pub const SETFL = 0x0010; -pub const pollfd = extern struct { - fd: i32, - events: i16, - revents: i16, -}; + pub const GETLK = 0x0020; + pub const SETLK = 0x0080; + pub const SETLKW = 0x0100; + pub const DUPFD_CLOEXEC = 0x0200; -pub const POLL = struct { - /// any readable data available - pub const IN = 0x0001; - /// file descriptor is writeable - pub const OUT = 0x0002; - pub const RDNORM = IN; - pub const WRNORM = OUT; - /// priority readable data - pub const RDBAND = 0x0008; - /// priority data can be written - pub const WRBAND = 0x0010; - /// high priority readable data - pub const PRI = 0x0020; + pub const RDLCK = 0x0040; + pub const UNLCK = 0x0200; + pub const WRLCK = 0x0400; +}; - /// errors pending - pub const ERR = 0x0004; - /// disconnected - pub const HUP = 0x0080; - /// invalid file descriptor - pub const NVAL = 0x1000; +pub const LOCK = struct { + pub const SH = 0x01; + pub const EX = 0x02; + pub const NB = 0x04; + pub const UN = 0x08; }; -// /system/develop/headers/posix/signal.h +pub const FD_CLOEXEC = 1; -pub const sigset_t = u64; -pub const empty_sigset: sigset_t = 0; -pub const filled_sigset = ~@as(sigset_t, 0); +pub const SEEK = struct { + pub const SET = 0; + pub const CUR = 1; + pub const END = 2; +}; -pub const SIG = struct { - pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); - pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); - pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); +pub const SOCK = struct { + pub const STREAM = 1; + pub const DGRAM = 2; + pub const RAW = 3; + pub const SEQPACKET = 5; - pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(3); + /// WARNING: this flag is not supported by windows socket functions directly, + /// it is only supported by std.os.socket. Be sure that this value does + /// not share any bits with any of the `SOCK` values. + pub const CLOEXEC = 0x10000; + /// WARNING: this flag is not supported by windows socket functions directly, + /// it is only supported by std.os.socket. Be sure that this value does + /// not share any bits with any of the `SOCK` values. + pub const NONBLOCK = 0x20000; +}; - pub const HUP = 1; - pub const INT = 2; - pub const QUIT = 3; - pub const ILL = 4; - pub const CHLD = 5; - pub const ABRT = 6; - pub const IOT = ABRT; - pub const PIPE = 7; - pub const FPE = 8; - pub const KILL = 9; - pub const STOP = 10; - pub const SEGV = 11; - pub const CONT = 12; - pub const TSTP = 13; - pub const ALRM = 14; - pub const TERM = 15; - pub const TTIN = 16; - pub const TTOU = 17; - pub const USR1 = 18; - pub const USR2 = 19; - pub const WINCH = 20; - pub const KILLTHR = 21; - pub const TRAP = 22; - pub const POLL = 23; - pub const PROF = 24; - pub const SYS = 25; - pub const URG = 26; - pub const VTALRM = 27; - pub const XCPU = 28; - pub const XFSZ = 29; - pub const BUS = 30; - pub const RESERVED1 = 31; - pub const RESERVED2 = 32; +pub const SO = struct { + pub const ACCEPTCONN = 0x00000001; + pub const BROADCAST = 0x00000002; + pub const DEBUG = 0x00000004; + pub const DONTROUTE = 0x00000008; + pub const KEEPALIVE = 0x00000010; + pub const OOBINLINE = 0x00000020; + pub const REUSEADDR = 0x00000040; + pub const REUSEPORT = 0x00000080; + pub const USELOOPBACK = 0x00000100; + pub const LINGER = 0x00000200; - pub const BLOCK = 1; - pub const UNBLOCK = 2; - pub const SETMASK = 3; + pub const SNDBUF = 0x40000001; + pub const SNDLOWAT = 0x40000002; + pub const SNDTIMEO = 0x40000003; + pub const RCVBUF = 0x40000004; + pub const RCVLOWAT = 0x40000005; + pub const RCVTIMEO = 0x40000006; + pub const ERROR = 0x40000007; + pub const TYPE = 0x40000008; + pub const NONBLOCK = 0x40000009; + pub const BINDTODEVICE = 0x4000000a; + pub const PEERCRED = 0x4000000b; }; -pub const siginfo_t = extern struct { - signo: c_int, - code: c_int, - errno: c_int, +pub const SOL = struct { + pub const SOCKET = -1; +}; - pid: pid_t, - uid: uid_t, - addr: *allowzero anyopaque, +pub const PF = struct { + pub const UNSPEC = AF.UNSPEC; + pub const INET = AF.INET; + pub const ROUTE = AF.ROUTE; + pub const LINK = AF.LINK; + pub const INET6 = AF.INET6; + pub const LOCAL = AF.LOCAL; + pub const UNIX = AF.UNIX; + pub const BLUETOOTH = AF.BLUETOOTH; }; -/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. -pub const Sigaction = extern struct { - pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; - pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void; +pub const AF = struct { + pub const UNSPEC = 0; + pub const INET = 1; + pub const APPLETALK = 2; + pub const ROUTE = 3; + pub const LINK = 4; + pub const INET6 = 5; + pub const DLI = 6; + pub const IPX = 7; + pub const NOTIFY = 8; + pub const LOCAL = 9; + pub const UNIX = LOCAL; + pub const BLUETOOTH = 10; + pub const MAX = 11; +}; - /// signal handler - handler: extern union { - handler: handler_fn, - sigaction: sigaction_fn, - }, +pub const DT = struct {}; - /// signal mask to apply - mask: sigset_t, +/// add event to kq (implies enable) +pub const EV_ADD = 0x0001; - /// see signal options - flags: c_int, +/// delete event from kq +pub const EV_DELETE = 0x0002; - /// will be passed to the signal handler, BeOS extension - userdata: *allowzero anyopaque = undefined, +/// enable event +pub const EV_ENABLE = 0x0004; + +/// disable event (not reported) +pub const EV_DISABLE = 0x0008; + +/// only report one occurrence +pub const EV_ONESHOT = 0x0010; + +/// clear event state after reporting +pub const EV_CLEAR = 0x0020; + +/// force immediate event output +/// ... with or without EV_ERROR +/// ... use KEVENT_FLAG_ERROR_EVENTS +/// on syscalls supporting flags +pub const EV_RECEIPT = 0x0040; + +/// disable event after reporting +pub const EV_DISPATCH = 0x0080; + +pub const EVFILT_READ = -1; +pub const EVFILT_WRITE = -2; + +/// attached to aio requests +pub const EVFILT_AIO = -3; + +/// attached to vnodes +pub const EVFILT_VNODE = -4; + +/// attached to struct proc +pub const EVFILT_PROC = -5; + +/// attached to struct proc +pub const EVFILT_SIGNAL = -6; + +/// timers +pub const EVFILT_TIMER = -7; + +/// Process descriptors +pub const EVFILT_PROCDESC = -8; + +/// Filesystem events +pub const EVFILT_FS = -9; + +pub const EVFILT_LIO = -10; + +/// User events +pub const EVFILT_USER = -11; + +/// Sendfile events +pub const EVFILT_SENDFILE = -12; + +pub const EVFILT_EMPTY = -13; + +pub const T = struct { + pub const CGETA = 0x8000; + pub const CSETA = 0x8001; + pub const CSETAF = 0x8002; + pub const CSETAW = 0x8003; + pub const CWAITEVENT = 0x8004; + pub const CSBRK = 0x8005; + pub const CFLSH = 0x8006; + pub const CXONC = 0x8007; + pub const CQUERYCONNECTED = 0x8008; + pub const CGETBITS = 0x8009; + pub const CSETDTR = 0x8010; + pub const CSETRTS = 0x8011; + pub const IOCGWINSZ = 0x8012; + pub const IOCSWINSZ = 0x8013; + pub const CVTIME = 0x8014; + pub const IOCGPGRP = 0x8015; + pub const IOCSPGRP = 0x8016; + pub const IOCSCTTY = 0x8017; + pub const IOCMGET = 0x8018; + pub const IOCMSET = 0x8019; + pub const IOCSBRK = 0x8020; + pub const IOCCBRK = 0x8021; + pub const IOCMBIS = 0x8022; + pub const IOCMBIC = 0x8023; + pub const IOCGSID = 0x8024; + + pub const FIONREAD = 0xbe000001; + pub const FIONBIO = 0xbe000000; }; -pub const SA = struct { - pub const NOCLDSTOP = 0x01; - pub const NOCLDWAIT = 0x02; - pub const RESETHAND = 0x04; - pub const NODEFER = 0x08; - pub const RESTART = 0x10; - pub const ONSTACK = 0x20; - pub const SIGINFO = 0x40; - pub const NOMASK = NODEFER; - pub const STACK = ONSTACK; - pub const ONESHOT = RESETHAND; +pub const winsize = extern struct { + ws_row: u16, + ws_col: u16, + ws_xpixel: u16, + ws_ypixel: u16, }; -pub const SS = struct { - pub const ONSTACK = 0x1; - pub const DISABLE = 0x2; +pub const S = struct { + pub const IFMT = 0o170000; + pub const IFSOCK = 0o140000; + pub const IFLNK = 0o120000; + pub const IFREG = 0o100000; + pub const IFBLK = 0o060000; + pub const IFDIR = 0o040000; + pub const IFCHR = 0o020000; + pub const IFIFO = 0o010000; + pub const INDEX_DIR = 0o4000000000; + + pub const IUMSK = 0o7777; + pub const ISUID = 0o4000; + pub const ISGID = 0o2000; + pub const ISVTX = 0o1000; + pub const IRWXU = 0o700; + pub const IRUSR = 0o400; + pub const IWUSR = 0o200; + pub const IXUSR = 0o100; + pub const IRWXG = 0o070; + pub const IRGRP = 0o040; + pub const IWGRP = 0o020; + pub const IXGRP = 0o010; + pub const IRWXO = 0o007; + pub const IROTH = 0o004; + pub const IWOTH = 0o002; + pub const IXOTH = 0o001; + + pub fn ISREG(m: u32) bool { + return m & IFMT == IFREG; + } + + pub fn ISLNK(m: u32) bool { + return m & IFMT == IFLNK; + } + + pub fn ISBLK(m: u32) bool { + return m & IFMT == IFBLK; + } + + pub fn ISDIR(m: u32) bool { + return m & IFMT == IFDIR; + } + + pub fn ISCHR(m: u32) bool { + return m & IFMT == IFCHR; + } + + pub fn ISFIFO(m: u32) bool { + return m & IFMT == IFIFO; + } + + pub fn ISSOCK(m: u32) bool { + return m & IFMT == IFSOCK; + } + + pub fn ISINDEX(m: u32) bool { + return m & INDEX_DIR == INDEX_DIR; + } }; -pub const MINSIGSTKSZ = 8192; -pub const SIGSTKSZ = 16384; +pub const HOST_NAME_MAX = 255; -pub const stack_t = extern struct { - sp: [*]u8, - size: isize, +pub const addrinfo = extern struct { flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: socklen_t, + canonname: ?[*:0]u8, + addr: ?*sockaddr, + next: ?*addrinfo, }; -pub const NSIG = 65; +pub const IPPROTO = struct { + pub const IP = 0; + pub const HOPOPTS = 0; + pub const ICMP = 1; + pub const IGMP = 2; + pub const TCP = 6; + pub const UDP = 17; + pub const IPV6 = 41; + pub const ROUTING = 43; + pub const FRAGMENT = 44; + pub const ESP = 50; + pub const AH = 51; + pub const ICMPV6 = 58; + pub const NONE = 59; + pub const DSTOPTS = 60; + pub const ETHERIP = 97; + pub const RAW = 255; + pub const MAX = 256; +}; -pub const mcontext_t = vregs; +pub const rlimit_resource = enum(i32) { + CORE = 0, + CPU = 1, + DATA = 2, + FSIZE = 3, + NOFILE = 4, + STACK = 5, + AS = 6, + NOVMON = 7, + _, +}; -pub const ucontext_t = extern struct { - link: ?*ucontext_t, - sigmask: sigset_t, - stack: stack_t, - mcontext: mcontext_t, +pub const rlim_t = i64; + +pub const RLIM = struct { + /// No limit + pub const INFINITY: rlim_t = (1 << 63) - 1; + + pub const SAVED_MAX = INFINITY; + pub const SAVED_CUR = INFINITY; +}; + +pub const rlimit = extern struct { + /// Soft limit + cur: rlim_t, + /// Hard limit + max: rlim_t, +}; + +pub const SHUT = struct { + pub const RD = 0; + pub const WR = 1; + pub const RDWR = 2; +}; + +// TODO fill out if needed +pub const directory_which = enum(i32) { + B_USER_SETTINGS_DIRECTORY = 0xbbe, + + _, +}; + +pub const MSG_NOSIGNAL = 0x0800; + +// /system/develop/headers/os/kernel/OS.h + +pub const area_id = i32; +pub const port_id = i32; +pub const sem_id = i32; +pub const team_id = i32; +pub const thread_id = i32; + +// /system/develop/headers/os/support/Errors.h + +pub const E = enum(i32) { + pub const B_GENERAL_ERROR_BASE: i32 = std.math.minInt(i32); + pub const B_OS_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x1000; + pub const B_APP_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x2000; + pub const B_INTERFACE_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x3000; + pub const B_MEDIA_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x4000; + pub const B_TRANSLATION_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x4800; + pub const B_MIDI_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x5000; + pub const B_STORAGE_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x6000; + pub const B_POSIX_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x7000; + pub const B_MAIL_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x8000; + pub const B_PRINT_ERROR_BASE = B_GENERAL_ERROR_BASE + 0x9000; + pub const B_DEVICE_ERROR_BASE = B_GENERAL_ERROR_BASE + 0xa000; + + pub const B_ERRORS_END = B_GENERAL_ERROR_BASE + 0xffff; + + pub const B_NO_MEMORY = B_GENERAL_ERROR_BASE + 0; + pub const B_IO_ERROR = B_GENERAL_ERROR_BASE + 1; + pub const B_PERMISSION_DENIED = B_GENERAL_ERROR_BASE + 2; + pub const B_BAD_INDEX = B_GENERAL_ERROR_BASE + 3; + pub const B_BAD_TYPE = B_GENERAL_ERROR_BASE + 4; + pub const B_BAD_VALUE = B_GENERAL_ERROR_BASE + 5; + pub const B_MISMATCHED_VALUES = B_GENERAL_ERROR_BASE + 6; + pub const B_NAME_NOT_FOUND = B_GENERAL_ERROR_BASE + 7; + pub const B_NAME_IN_USE = B_GENERAL_ERROR_BASE + 8; + pub const B_TIMED_OUT = B_GENERAL_ERROR_BASE + 9; + pub const B_INTERRUPTED = B_GENERAL_ERROR_BASE + 10; + pub const B_WOULD_BLOCK = B_GENERAL_ERROR_BASE + 11; + pub const B_CANCELED = B_GENERAL_ERROR_BASE + 12; + pub const B_NO_INIT = B_GENERAL_ERROR_BASE + 13; + pub const B_NOT_INITIALIZED = B_GENERAL_ERROR_BASE + 13; + pub const B_BUSY = B_GENERAL_ERROR_BASE + 14; + pub const B_NOT_ALLOWED = B_GENERAL_ERROR_BASE + 15; + pub const B_BAD_DATA = B_GENERAL_ERROR_BASE + 16; + pub const B_DONT_DO_THAT = B_GENERAL_ERROR_BASE + 17; + + pub const B_BAD_IMAGE_ID = B_OS_ERROR_BASE + 0x300; + pub const B_BAD_ADDRESS = B_OS_ERROR_BASE + 0x301; + pub const B_NOT_AN_EXECUTABLE = B_OS_ERROR_BASE + 0x302; + pub const B_MISSING_LIBRARY = B_OS_ERROR_BASE + 0x303; + pub const B_MISSING_SYMBOL = B_OS_ERROR_BASE + 0x304; + pub const B_UNKNOWN_EXECUTABLE = B_OS_ERROR_BASE + 0x305; + pub const B_LEGACY_EXECUTABLE = B_OS_ERROR_BASE + 0x306; + + pub const B_FILE_ERROR = B_STORAGE_ERROR_BASE + 0; + pub const B_FILE_EXISTS = B_STORAGE_ERROR_BASE + 2; + pub const B_ENTRY_NOT_FOUND = B_STORAGE_ERROR_BASE + 3; + pub const B_NAME_TOO_LONG = B_STORAGE_ERROR_BASE + 4; + pub const B_NOT_A_DIRECTORY = B_STORAGE_ERROR_BASE + 5; + pub const B_DIRECTORY_NOT_EMPTY = B_STORAGE_ERROR_BASE + 6; + pub const B_DEVICE_FULL = B_STORAGE_ERROR_BASE + 7; + pub const B_READ_ONLY_DEVICE = B_STORAGE_ERROR_BASE + 8; + pub const B_IS_A_DIRECTORY = B_STORAGE_ERROR_BASE + 9; + pub const B_NO_MORE_FDS = B_STORAGE_ERROR_BASE + 10; + pub const B_CROSS_DEVICE_LINK = B_STORAGE_ERROR_BASE + 11; + pub const B_LINK_LIMIT = B_STORAGE_ERROR_BASE + 12; + pub const B_BUSTED_PIPE = B_STORAGE_ERROR_BASE + 13; + pub const B_UNSUPPORTED = B_STORAGE_ERROR_BASE + 14; + pub const B_PARTITION_TOO_SMALL = B_STORAGE_ERROR_BASE + 15; + pub const B_PARTIAL_READ = B_STORAGE_ERROR_BASE + 16; + pub const B_PARTIAL_WRITE = B_STORAGE_ERROR_BASE + 17; + + SUCCESS = 0, + + @"2BIG" = B_POSIX_ERROR_BASE + 1, + CHILD = B_POSIX_ERROR_BASE + 2, + DEADLK = B_POSIX_ERROR_BASE + 3, + FBIG = B_POSIX_ERROR_BASE + 4, + MLINK = B_POSIX_ERROR_BASE + 5, + NFILE = B_POSIX_ERROR_BASE + 6, + NODEV = B_POSIX_ERROR_BASE + 7, + NOLCK = B_POSIX_ERROR_BASE + 8, + NOSYS = B_POSIX_ERROR_BASE + 9, + NOTTY = B_POSIX_ERROR_BASE + 10, + NXIO = B_POSIX_ERROR_BASE + 11, + SPIPE = B_POSIX_ERROR_BASE + 12, + SRCH = B_POSIX_ERROR_BASE + 13, + FPOS = B_POSIX_ERROR_BASE + 14, + SIGPARM = B_POSIX_ERROR_BASE + 15, + DOM = B_POSIX_ERROR_BASE + 16, + RANGE = B_POSIX_ERROR_BASE + 17, + PROTOTYPE = B_POSIX_ERROR_BASE + 18, + PROTONOSUPPORT = B_POSIX_ERROR_BASE + 19, + PFNOSUPPORT = B_POSIX_ERROR_BASE + 20, + AFNOSUPPORT = B_POSIX_ERROR_BASE + 21, + ADDRINUSE = B_POSIX_ERROR_BASE + 22, + ADDRNOTAVAIL = B_POSIX_ERROR_BASE + 23, + NETDOWN = B_POSIX_ERROR_BASE + 24, + NETUNREACH = B_POSIX_ERROR_BASE + 25, + NETRESET = B_POSIX_ERROR_BASE + 26, + CONNABORTED = B_POSIX_ERROR_BASE + 27, + CONNRESET = B_POSIX_ERROR_BASE + 28, + ISCONN = B_POSIX_ERROR_BASE + 29, + NOTCONN = B_POSIX_ERROR_BASE + 30, + SHUTDOWN = B_POSIX_ERROR_BASE + 31, + CONNREFUSED = B_POSIX_ERROR_BASE + 32, + HOSTUNREACH = B_POSIX_ERROR_BASE + 33, + NOPROTOOPT = B_POSIX_ERROR_BASE + 34, + NOBUFS = B_POSIX_ERROR_BASE + 35, + INPROGRESS = B_POSIX_ERROR_BASE + 36, + ALREADY = B_POSIX_ERROR_BASE + 37, + ILSEQ = B_POSIX_ERROR_BASE + 38, + NOMSG = B_POSIX_ERROR_BASE + 39, + STALE = B_POSIX_ERROR_BASE + 40, + OVERFLOW = B_POSIX_ERROR_BASE + 41, + MSGSIZE = B_POSIX_ERROR_BASE + 42, + OPNOTSUPP = B_POSIX_ERROR_BASE + 43, + NOTSOCK = B_POSIX_ERROR_BASE + 44, + HOSTDOWN = B_POSIX_ERROR_BASE + 45, + BADMSG = B_POSIX_ERROR_BASE + 46, + CANCELED = B_POSIX_ERROR_BASE + 47, + DESTADDRREQ = B_POSIX_ERROR_BASE + 48, + DQUOT = B_POSIX_ERROR_BASE + 49, + IDRM = B_POSIX_ERROR_BASE + 50, + MULTIHOP = B_POSIX_ERROR_BASE + 51, + NODATA = B_POSIX_ERROR_BASE + 52, + NOLINK = B_POSIX_ERROR_BASE + 53, + NOSR = B_POSIX_ERROR_BASE + 54, + NOSTR = B_POSIX_ERROR_BASE + 55, + NOTSUP = B_POSIX_ERROR_BASE + 56, + PROTO = B_POSIX_ERROR_BASE + 57, + TIME = B_POSIX_ERROR_BASE + 58, + TXTBSY = B_POSIX_ERROR_BASE + 59, + NOATTR = B_POSIX_ERROR_BASE + 60, + NOTRECOVERABLE = B_POSIX_ERROR_BASE + 61, + OWNERDEAD = B_POSIX_ERROR_BASE + 62, + + NOMEM = B_NO_MEMORY, + + ACCES = B_PERMISSION_DENIED, + INTR = B_INTERRUPTED, + IO = B_IO_ERROR, + BUSY = B_BUSY, + FAULT = B_BAD_ADDRESS, + TIMEDOUT = B_TIMED_OUT, + /// Also used for WOULDBLOCK + AGAIN = B_WOULD_BLOCK, + BADF = B_FILE_ERROR, + EXIST = B_FILE_EXISTS, + INVAL = B_BAD_VALUE, + NAMETOOLONG = B_NAME_TOO_LONG, + NOENT = B_ENTRY_NOT_FOUND, + PERM = B_NOT_ALLOWED, + NOTDIR = B_NOT_A_DIRECTORY, + ISDIR = B_IS_A_DIRECTORY, + NOTEMPTY = B_DIRECTORY_NOT_EMPTY, + NOSPC = B_DEVICE_FULL, + ROFS = B_READ_ONLY_DEVICE, + MFILE = B_NO_MORE_FDS, + XDEV = B_CROSS_DEVICE_LINK, + LOOP = B_LINK_LIMIT, + NOEXEC = B_NOT_AN_EXECUTABLE, + PIPE = B_BUSTED_PIPE, + + _, }; +// /system/develop/headers/os/support/SupportDefs.h + +pub const status_t = i32; + // /system/develop/headers/posix/arch/*/signal.h pub const vregs = switch (builtin.cpu.arch) { @@ -822,445 +1116,278 @@ pub const vregs = switch (builtin.cpu.arch) { else => void, }; -// access function -pub const F_OK = 0; // test for existence of file -pub const X_OK = 1; // test for execute or search permission -pub const W_OK = 2; // test for write permission -pub const R_OK = 4; // test for read permission - -pub const F = struct { - pub const DUPFD = 0x0001; - pub const GETFD = 0x0002; - pub const SETFD = 0x0004; - pub const GETFL = 0x0008; - pub const SETFL = 0x0010; - - pub const GETLK = 0x0020; - pub const SETLK = 0x0080; - pub const SETLKW = 0x0100; - pub const DUPFD_CLOEXEC = 0x0200; - - pub const RDLCK = 0x0040; - pub const UNLCK = 0x0200; - pub const WRLCK = 0x0400; -}; - -pub const LOCK = struct { - pub const SH = 0x01; - pub const EX = 0x02; - pub const NB = 0x04; - pub const UN = 0x08; -}; - -pub const FD_CLOEXEC = 1; - -pub const SEEK = struct { - pub const SET = 0; - pub const CUR = 1; - pub const END = 2; +// /system/develop/headers/posix/dirent.h + +pub const DirEnt = extern struct { + /// device + dev: dev_t, + /// parent device (only for queries) + pdev: dev_t, + /// inode number + ino: ino_t, + /// parent inode (only for queries) + pino: ino_t, + /// length of this record, not the name + reclen: u16, + /// name of the entry (null byte terminated) + name: [0]u8, + pub fn getName(dirent: *const DirEnt) [*:0]const u8 { + return @ptrCast(&dirent.name); + } }; -pub const SOCK = struct { - pub const STREAM = 1; - pub const DGRAM = 2; - pub const RAW = 3; - pub const SEQPACKET = 5; +// /system/develop/headers/posix/errno.h - /// WARNING: this flag is not supported by windows socket functions directly, - /// it is only supported by std.os.socket. Be sure that this value does - /// not share any bits with any of the `SOCK` values. - pub const CLOEXEC = 0x10000; - /// WARNING: this flag is not supported by windows socket functions directly, - /// it is only supported by std.os.socket. Be sure that this value does - /// not share any bits with any of the `SOCK` values. - pub const NONBLOCK = 0x20000; -}; +extern "root" fn _errnop() *i32; +pub const _errno = _errnop; -pub const SO = struct { - pub const ACCEPTCONN = 0x00000001; - pub const BROADCAST = 0x00000002; - pub const DEBUG = 0x00000004; - pub const DONTROUTE = 0x00000008; - pub const KEEPALIVE = 0x00000010; - pub const OOBINLINE = 0x00000020; - pub const REUSEADDR = 0x00000040; - pub const REUSEPORT = 0x00000080; - pub const USELOOPBACK = 0x00000100; - pub const LINGER = 0x00000200; +// /system/develop/headers/posix/poll.h - pub const SNDBUF = 0x40000001; - pub const SNDLOWAT = 0x40000002; - pub const SNDTIMEO = 0x40000003; - pub const RCVBUF = 0x40000004; - pub const RCVLOWAT = 0x40000005; - pub const RCVTIMEO = 0x40000006; - pub const ERROR = 0x40000007; - pub const TYPE = 0x40000008; - pub const NONBLOCK = 0x40000009; - pub const BINDTODEVICE = 0x4000000a; - pub const PEERCRED = 0x4000000b; -}; +pub const nfds_t = usize; -pub const SOL = struct { - pub const SOCKET = -1; +pub const pollfd = extern struct { + fd: i32, + events: i16, + revents: i16, }; -pub const PF = struct { - pub const UNSPEC = AF.UNSPEC; - pub const INET = AF.INET; - pub const ROUTE = AF.ROUTE; - pub const LINK = AF.LINK; - pub const INET6 = AF.INET6; - pub const LOCAL = AF.LOCAL; - pub const UNIX = AF.UNIX; - pub const BLUETOOTH = AF.BLUETOOTH; -}; +pub const POLL = struct { + /// any readable data available + pub const IN = 0x0001; + /// file descriptor is writeable + pub const OUT = 0x0002; + pub const RDNORM = IN; + pub const WRNORM = OUT; + /// priority readable data + pub const RDBAND = 0x0008; + /// priority data can be written + pub const WRBAND = 0x0010; + /// high priority readable data + pub const PRI = 0x0020; -pub const AF = struct { - pub const UNSPEC = 0; - pub const INET = 1; - pub const APPLETALK = 2; - pub const ROUTE = 3; - pub const LINK = 4; - pub const INET6 = 5; - pub const DLI = 6; - pub const IPX = 7; - pub const NOTIFY = 8; - pub const LOCAL = 9; - pub const UNIX = LOCAL; - pub const BLUETOOTH = 10; - pub const MAX = 11; + /// errors pending + pub const ERR = 0x0004; + /// disconnected + pub const HUP = 0x0080; + /// invalid file descriptor + pub const NVAL = 0x1000; }; -pub const DT = struct {}; - -/// add event to kq (implies enable) -pub const EV_ADD = 0x0001; - -/// delete event from kq -pub const EV_DELETE = 0x0002; - -/// enable event -pub const EV_ENABLE = 0x0004; - -/// disable event (not reported) -pub const EV_DISABLE = 0x0008; - -/// only report one occurrence -pub const EV_ONESHOT = 0x0010; - -/// clear event state after reporting -pub const EV_CLEAR = 0x0020; - -/// force immediate event output -/// ... with or without EV_ERROR -/// ... use KEVENT_FLAG_ERROR_EVENTS -/// on syscalls supporting flags -pub const EV_RECEIPT = 0x0040; - -/// disable event after reporting -pub const EV_DISPATCH = 0x0080; - -pub const EVFILT_READ = -1; -pub const EVFILT_WRITE = -2; - -/// attached to aio requests -pub const EVFILT_AIO = -3; +// /system/develop/headers/posix/signal.h -/// attached to vnodes -pub const EVFILT_VNODE = -4; +pub const sigset_t = u64; +pub const empty_sigset: sigset_t = 0; +pub const filled_sigset = ~@as(sigset_t, 0); -/// attached to struct proc -pub const EVFILT_PROC = -5; +pub const SIG = struct { + pub const DFL: ?Sigaction.handler_fn = @ptrFromInt(0); + pub const IGN: ?Sigaction.handler_fn = @ptrFromInt(1); + pub const ERR: ?Sigaction.handler_fn = @ptrFromInt(maxInt(usize)); -/// attached to struct proc -pub const EVFILT_SIGNAL = -6; + pub const HOLD: ?Sigaction.handler_fn = @ptrFromInt(3); -/// timers -pub const EVFILT_TIMER = -7; + pub const HUP = 1; + pub const INT = 2; + pub const QUIT = 3; + pub const ILL = 4; + pub const CHLD = 5; + pub const ABRT = 6; + pub const IOT = ABRT; + pub const PIPE = 7; + pub const FPE = 8; + pub const KILL = 9; + pub const STOP = 10; + pub const SEGV = 11; + pub const CONT = 12; + pub const TSTP = 13; + pub const ALRM = 14; + pub const TERM = 15; + pub const TTIN = 16; + pub const TTOU = 17; + pub const USR1 = 18; + pub const USR2 = 19; + pub const WINCH = 20; + pub const KILLTHR = 21; + pub const TRAP = 22; + pub const POLL = 23; + pub const PROF = 24; + pub const SYS = 25; + pub const URG = 26; + pub const VTALRM = 27; + pub const XCPU = 28; + pub const XFSZ = 29; + pub const BUS = 30; + pub const RESERVED1 = 31; + pub const RESERVED2 = 32; -/// Process descriptors -pub const EVFILT_PROCDESC = -8; + pub const BLOCK = 1; + pub const UNBLOCK = 2; + pub const SETMASK = 3; +}; -/// Filesystem events -pub const EVFILT_FS = -9; +pub const siginfo_t = extern struct { + signo: i32, + code: i32, + errno: i32, -pub const EVFILT_LIO = -10; + pid: pid_t, + uid: uid_t, + addr: *allowzero anyopaque, +}; -/// User events -pub const EVFILT_USER = -11; +/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall. +pub const Sigaction = extern struct { + pub const handler_fn = *align(1) const fn (i32) callconv(.C) void; + pub const sigaction_fn = *const fn (i32, *const siginfo_t, ?*anyopaque) callconv(.C) void; -/// Sendfile events -pub const EVFILT_SENDFILE = -12; + /// signal handler + handler: extern union { + handler: handler_fn, + sigaction: sigaction_fn, + }, -pub const EVFILT_EMPTY = -13; + /// signal mask to apply + mask: sigset_t, -pub const T = struct { - pub const CGETA = 0x8000; - pub const CSETA = 0x8001; - pub const CSETAF = 0x8002; - pub const CSETAW = 0x8003; - pub const CWAITEVENT = 0x8004; - pub const CSBRK = 0x8005; - pub const CFLSH = 0x8006; - pub const CXONC = 0x8007; - pub const CQUERYCONNECTED = 0x8008; - pub const CGETBITS = 0x8009; - pub const CSETDTR = 0x8010; - pub const CSETRTS = 0x8011; - pub const IOCGWINSZ = 0x8012; - pub const IOCSWINSZ = 0x8013; - pub const CVTIME = 0x8014; - pub const IOCGPGRP = 0x8015; - pub const IOCSPGRP = 0x8016; - pub const IOCSCTTY = 0x8017; - pub const IOCMGET = 0x8018; - pub const IOCMSET = 0x8019; - pub const IOCSBRK = 0x8020; - pub const IOCCBRK = 0x8021; - pub const IOCMBIS = 0x8022; - pub const IOCMBIC = 0x8023; - pub const IOCGSID = 0x8024; + /// see signal options + flags: i32, - pub const FIONREAD = 0xbe000001; - pub const FIONBIO = 0xbe000000; + /// will be passed to the signal handler, BeOS extension + userdata: *allowzero anyopaque = undefined, }; -pub const winsize = extern struct { - ws_row: u16, - ws_col: u16, - ws_xpixel: u16, - ws_ypixel: u16, +pub const SA = struct { + pub const NOCLDSTOP = 0x01; + pub const NOCLDWAIT = 0x02; + pub const RESETHAND = 0x04; + pub const NODEFER = 0x08; + pub const RESTART = 0x10; + pub const ONSTACK = 0x20; + pub const SIGINFO = 0x40; + pub const NOMASK = NODEFER; + pub const STACK = ONSTACK; + pub const ONESHOT = RESETHAND; }; -const B_POSIX_ERROR_BASE = -2147454976; +pub const SS = struct { + pub const ONSTACK = 0x1; + pub const DISABLE = 0x2; +}; -pub const E = enum(i32) { - @"2BIG" = B_POSIX_ERROR_BASE + 1, - CHILD = B_POSIX_ERROR_BASE + 2, - DEADLK = B_POSIX_ERROR_BASE + 3, - FBIG = B_POSIX_ERROR_BASE + 4, - MLINK = B_POSIX_ERROR_BASE + 5, - NFILE = B_POSIX_ERROR_BASE + 6, - NODEV = B_POSIX_ERROR_BASE + 7, - NOLCK = B_POSIX_ERROR_BASE + 8, - NOSYS = B_POSIX_ERROR_BASE + 9, - NOTTY = B_POSIX_ERROR_BASE + 10, - NXIO = B_POSIX_ERROR_BASE + 11, - SPIPE = B_POSIX_ERROR_BASE + 12, - SRCH = B_POSIX_ERROR_BASE + 13, - FPOS = B_POSIX_ERROR_BASE + 14, - SIGPARM = B_POSIX_ERROR_BASE + 15, - DOM = B_POSIX_ERROR_BASE + 16, - RANGE = B_POSIX_ERROR_BASE + 17, - PROTOTYPE = B_POSIX_ERROR_BASE + 18, - PROTONOSUPPORT = B_POSIX_ERROR_BASE + 19, - PFNOSUPPORT = B_POSIX_ERROR_BASE + 20, - AFNOSUPPORT = B_POSIX_ERROR_BASE + 21, - ADDRINUSE = B_POSIX_ERROR_BASE + 22, - ADDRNOTAVAIL = B_POSIX_ERROR_BASE + 23, - NETDOWN = B_POSIX_ERROR_BASE + 24, - NETUNREACH = B_POSIX_ERROR_BASE + 25, - NETRESET = B_POSIX_ERROR_BASE + 26, - CONNABORTED = B_POSIX_ERROR_BASE + 27, - CONNRESET = B_POSIX_ERROR_BASE + 28, - ISCONN = B_POSIX_ERROR_BASE + 29, - NOTCONN = B_POSIX_ERROR_BASE + 30, - SHUTDOWN = B_POSIX_ERROR_BASE + 31, - CONNREFUSED = B_POSIX_ERROR_BASE + 32, - HOSTUNREACH = B_POSIX_ERROR_BASE + 33, - NOPROTOOPT = B_POSIX_ERROR_BASE + 34, - NOBUFS = B_POSIX_ERROR_BASE + 35, - INPROGRESS = B_POSIX_ERROR_BASE + 36, - ALREADY = B_POSIX_ERROR_BASE + 37, - ILSEQ = B_POSIX_ERROR_BASE + 38, - NOMSG = B_POSIX_ERROR_BASE + 39, - STALE = B_POSIX_ERROR_BASE + 40, - OVERFLOW = B_POSIX_ERROR_BASE + 41, - MSGSIZE = B_POSIX_ERROR_BASE + 42, - OPNOTSUPP = B_POSIX_ERROR_BASE + 43, - NOTSOCK = B_POSIX_ERROR_BASE + 44, - HOSTDOWN = B_POSIX_ERROR_BASE + 45, - BADMSG = B_POSIX_ERROR_BASE + 46, - CANCELED = B_POSIX_ERROR_BASE + 47, - DESTADDRREQ = B_POSIX_ERROR_BASE + 48, - DQUOT = B_POSIX_ERROR_BASE + 49, - IDRM = B_POSIX_ERROR_BASE + 50, - MULTIHOP = B_POSIX_ERROR_BASE + 51, - NODATA = B_POSIX_ERROR_BASE + 52, - NOLINK = B_POSIX_ERROR_BASE + 53, - NOSR = B_POSIX_ERROR_BASE + 54, - NOSTR = B_POSIX_ERROR_BASE + 55, - NOTSUP = B_POSIX_ERROR_BASE + 56, - PROTO = B_POSIX_ERROR_BASE + 57, - TIME = B_POSIX_ERROR_BASE + 58, - TXTBSY = B_POSIX_ERROR_BASE + 59, - NOATTR = B_POSIX_ERROR_BASE + 60, - NOTRECOVERABLE = B_POSIX_ERROR_BASE + 61, - OWNERDEAD = B_POSIX_ERROR_BASE + 62, +pub const MINSIGSTKSZ = 8192; +pub const SIGSTKSZ = 16384; - ACCES = -0x7ffffffe, // Permission denied - INTR = -0x7ffffff6, // Interrupted system call - IO = -0x7fffffff, // Input/output error - BUSY = -0x7ffffff2, // Device busy - FAULT = -0x7fffecff, // Bad address - TIMEDOUT = -2147483639, // Operation timed out - AGAIN = -0x7ffffff5, - BADF = -0x7fffa000, // Bad file descriptor - EXIST = -0x7fff9ffe, // File exists - INVAL = -0x7ffffffb, // Invalid argument - NAMETOOLONG = -2147459068, // File name too long - NOENT = -0x7fff9ffd, // No such file or directory - PERM = -0x7ffffff1, // Operation not permitted - NOTDIR = -0x7fff9ffb, // Not a directory - ISDIR = -0x7fff9ff7, // Is a directory - NOTEMPTY = -2147459066, // Directory not empty - NOSPC = -0x7fff9ff9, // No space left on device - ROFS = -0x7fff9ff8, // Read-only filesystem - MFILE = -0x7fff9ff6, // Too many open files - XDEV = -0x7fff9ff5, // Cross-device link - NOEXEC = -0x7fffecfe, // Exec format error - PIPE = -0x7fff9ff3, // Broken pipe - NOMEM = -0x80000000, // Cannot allocate memory - LOOP = -2147459060, // Too many levels of symbolic links - SUCCESS = 0, - _, +pub const stack_t = extern struct { + sp: [*]u8, + size: isize, + flags: i32, }; -pub const S = struct { - pub const IFMT = 0o170000; - pub const IFSOCK = 0o140000; - pub const IFLNK = 0o120000; - pub const IFREG = 0o100000; - pub const IFBLK = 0o060000; - pub const IFDIR = 0o040000; - pub const IFCHR = 0o020000; - pub const IFIFO = 0o010000; - pub const INDEX_DIR = 0o4000000000; - - pub const IUMSK = 0o7777; - pub const ISUID = 0o4000; - pub const ISGID = 0o2000; - pub const ISVTX = 0o1000; - pub const IRWXU = 0o700; - pub const IRUSR = 0o400; - pub const IWUSR = 0o200; - pub const IXUSR = 0o100; - pub const IRWXG = 0o070; - pub const IRGRP = 0o040; - pub const IWGRP = 0o020; - pub const IXGRP = 0o010; - pub const IRWXO = 0o007; - pub const IROTH = 0o004; - pub const IWOTH = 0o002; - pub const IXOTH = 0o001; +pub const NSIG = 65; - pub fn ISREG(m: u32) bool { - return m & IFMT == IFREG; - } +pub const mcontext_t = vregs; - pub fn ISLNK(m: u32) bool { - return m & IFMT == IFLNK; - } +pub const ucontext_t = extern struct { + link: ?*ucontext_t, + sigmask: sigset_t, + stack: stack_t, + mcontext: mcontext_t, +}; - pub fn ISBLK(m: u32) bool { - return m & IFMT == IFBLK; - } +// /system/develop/headers/posix/sys/stat.h - pub fn ISDIR(m: u32) bool { - return m & IFMT == IFDIR; - } +pub const Stat = extern struct { + dev: dev_t, + ino: ino_t, + mode: mode_t, + nlink: nlink_t, + uid: uid_t, + gid: gid_t, + size: off_t, + rdev: dev_t, + blksize: blksize_t, + atim: timespec, + mtim: timespec, + ctim: timespec, + crtim: timespec, + type: u32, + blocks: blkcnt_t, - pub fn ISCHR(m: u32) bool { - return m & IFMT == IFCHR; + pub fn atime(self: @This()) timespec { + return self.atim; } - - pub fn ISFIFO(m: u32) bool { - return m & IFMT == IFIFO; + pub fn mtime(self: @This()) timespec { + return self.mtim; } - - pub fn ISSOCK(m: u32) bool { - return m & IFMT == IFSOCK; + pub fn ctime(self: @This()) timespec { + return self.ctim; } - - pub fn ISINDEX(m: u32) bool { - return m & INDEX_DIR == INDEX_DIR; + pub fn birthtime(self: @This()) timespec { + return self.crtim; } }; -pub const HOST_NAME_MAX = 255; +// /system/develop/headers/posix/sys/types.h -pub const addrinfo = extern struct { - flags: i32, - family: i32, - socktype: i32, - protocol: i32, - addrlen: socklen_t, - canonname: ?[*:0]u8, - addr: ?*sockaddr, - next: ?*addrinfo, -}; +pub const blkcnt_t = i64; +pub const blksize_t = i32; +pub const fsblkcnt_t = i64; +pub const fsfilcnt_t = i64; +pub const off_t = i64; +pub const ino_t = i64; +pub const cnt_t = i32; +pub const dev_t = i32; +pub const pid_t = i32; +pub const id_t = i32; -pub const IPPROTO = struct { - pub const IP = 0; - pub const HOPOPTS = 0; - pub const ICMP = 1; - pub const IGMP = 2; - pub const TCP = 6; - pub const UDP = 17; - pub const IPV6 = 41; - pub const ROUTING = 43; - pub const FRAGMENT = 44; - pub const ESP = 50; - pub const AH = 51; - pub const ICMPV6 = 58; - pub const NONE = 59; - pub const DSTOPTS = 60; - pub const ETHERIP = 97; - pub const RAW = 255; - pub const MAX = 256; -}; +pub const uid_t = u32; +pub const gid_t = u32; +pub const mode_t = u32; +pub const umode_t = u32; +pub const nlink_t = i32; -pub const rlimit_resource = enum(c_int) { - CORE = 0, - CPU = 1, - DATA = 2, - FSIZE = 3, - NOFILE = 4, - STACK = 5, - AS = 6, - NOVMON = 7, - _, -}; +pub const clockid_t = i32; +pub const timer_t = *opaque {}; -pub const rlim_t = i64; +// /system/develop/headers/posix/time.h -pub const RLIM = struct { - /// No limit - pub const INFINITY: rlim_t = (1 << 63) - 1; +pub const clock_t = i32; +pub const suseconds_t = i32; +pub const useconds_t = u32; - pub const SAVED_MAX = INFINITY; - pub const SAVED_CUR = INFINITY; -}; +pub const time_t = isize; -pub const rlimit = extern struct { - /// Soft limit - cur: rlim_t, - /// Hard limit - max: rlim_t, -}; +pub const CLOCKS_PER_SEC = 1_000_000; +pub const CLK_TCK = CLOCKS_PER_SEC; +pub const TIME_UTC = 1; -pub const SHUT = struct { - pub const RD = 0; - pub const WR = 1; - pub const RDWR = 2; +pub const CLOCK = struct { + /// system-wide monotonic clock (aka system time) + pub const MONOTONIC: clockid_t = 0; + /// system-wide real time clock + pub const REALTIME: clockid_t = -1; + /// clock measuring the used CPU time of the current process + pub const PROCESS_CPUTIME_ID: clockid_t = -2; + /// clock measuring the used CPU time of the current thread + pub const THREAD_CPUTIME_ID: clockid_t = -3; }; -// TODO fill out if needed -pub const directory_which = enum(c_int) { - B_USER_SETTINGS_DIRECTORY = 0xbbe, +pub const timespec = extern struct { + /// seconds + tv_sec: time_t, + /// and nanoseconds + tv_nsec: isize, +}; - _, +pub const itimerspec = extern struct { + interval: timespec, + value: timespec, }; -pub const MSG_NOSIGNAL = 0x0800; +// /system/develop/headers/private/system/syscalls.h + +pub extern "root" fn _kern_get_current_team() team_id; +pub extern "root" fn _kern_open_dir(fd: fd_t, path: [*:0]const u8) fd_t; +pub extern "root" fn _kern_read_dir(fd: fd_t, buffer: [*]u8, bufferSize: usize, maxCount: u32) isize; +pub extern "root" fn _kern_rewind_dir(fd: fd_t) status_t; +pub extern "root" fn _kern_read_stat(fd: fd_t, path: [*:0]const u8, traverseLink: bool, stat: *Stat, statSize: usize) status_t; diff --git a/lib/std/crypto/Certificate.zig b/lib/std/crypto/Certificate.zig index c3ac3e22aa..b1cc4fc095 100644 --- a/lib/std/crypto/Certificate.zig +++ b/lib/std/crypto/Certificate.zig @@ -772,7 +772,7 @@ fn verifyRsa( Hash.hash(message, &msg_hashed, .{}); switch (modulus.len) { - inline 128, 256, 512 => |modulus_len| { + inline 128, 256, 384, 512 => |modulus_len| { const ps_len = modulus_len - (hash_der.len + msg_hashed.len) - 3; const em: [modulus_len]u8 = [2]u8{ 0, 1 } ++ diff --git a/lib/std/crypto/benchmark.zig b/lib/std/crypto/benchmark.zig index 7e118afb26..3e979175d8 100644 --- a/lib/std/crypto/benchmark.zig +++ b/lib/std/crypto/benchmark.zig @@ -131,7 +131,12 @@ pub fn benchmarkKeyExchange(comptime DhKeyExchange: anytype, comptime exchange_c return throughput; } -const signatures = [_]Crypto{Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }}; +const signatures = [_]Crypto{ + Crypto{ .ty = crypto.sign.Ed25519, .name = "ed25519" }, + Crypto{ .ty = crypto.sign.ecdsa.EcdsaP256Sha256, .name = "ecdsa-p256" }, + Crypto{ .ty = crypto.sign.ecdsa.EcdsaP384Sha384, .name = "ecdsa-p384" }, + Crypto{ .ty = crypto.sign.ecdsa.EcdsaSecp256k1Sha256, .name = "ecdsa-secp256k1" }, +}; pub fn benchmarkSignature(comptime Signature: anytype, comptime signatures_count: comptime_int) !u64 { const msg = [_]u8{0} ** 64; diff --git a/lib/std/crypto/ecdsa.zig b/lib/std/crypto/ecdsa.zig index 70362470c3..b2751ce20a 100644 --- a/lib/std/crypto/ecdsa.zig +++ b/lib/std/crypto/ecdsa.zig @@ -4,6 +4,7 @@ const crypto = std.crypto; const fmt = std.fmt; const io = std.io; const mem = std.mem; +const sha3 = crypto.hash.sha3; const testing = std.testing; const EncodingError = crypto.errors.EncodingError; @@ -26,7 +27,11 @@ pub const EcdsaSecp256k1Sha256oSha256 = Ecdsa(crypto.ecc.Secp256k1, crypto.hash. /// Elliptic Curve Digital Signature Algorithm (ECDSA). pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { - const Hmac = crypto.auth.hmac.Hmac(Hash); + const Prf = switch (Hash) { + sha3.Shake128 => sha3.KMac128, + sha3.Shake256 => sha3.KMac256, + else => crypto.auth.hmac.Hmac(Hash), + }; return struct { /// Length (in bytes) of optional random bytes, for non-deterministic signatures. @@ -350,22 +355,22 @@ pub fn Ecdsa(comptime Curve: type, comptime Hash: type) type { if (noise) |n| @memcpy(m_z, &n); @memcpy(m_x, &secret_key); @memcpy(m_h, &h); - Hmac.create(&k, &m, &k); - Hmac.create(m_v, m_v, &k); + Prf.create(&k, &m, &k); + Prf.create(m_v, m_v, &k); m_i.* = 0x01; - Hmac.create(&k, &m, &k); - Hmac.create(m_v, m_v, &k); + Prf.create(&k, &m, &k); + Prf.create(m_v, m_v, &k); while (true) { var t_off: usize = 0; while (t_off < t.len) : (t_off += m_v.len) { const t_end = @min(t_off + m_v.len, t.len); - Hmac.create(m_v, m_v, &k); + Prf.create(m_v, m_v, &k); @memcpy(t[t_off..t_end], m_v[0 .. t_end - t_off]); } if (Curve.scalar.Scalar.fromBytes(t, .big)) |s| return s else |_| {} m_i.* = 0x00; - Hmac.create(&k, m[0 .. m_v.len + 1], &k); - Hmac.create(m_v, m_v, &k); + Prf.create(&k, m[0 .. m_v.len + 1], &k); + Prf.create(m_v, m_v, &k); } } }; diff --git a/lib/std/crypto/keccak_p.zig b/lib/std/crypto/keccak_p.zig index d04373269e..352172a1f6 100644 --- a/lib/std/crypto/keccak_p.zig +++ b/lib/std/crypto/keccak_p.zig @@ -195,7 +195,7 @@ pub fn KeccakF(comptime f: u11) type { } /// A generic Keccak-P state. -pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, comptime rounds: u5) type { +pub fn State(comptime f: u11, comptime capacity: u11, comptime rounds: u5) type { comptime assert(f > 200 and f <= 1600 and f % 200 == 0); // invalid state size comptime assert(capacity < f and capacity % 8 == 0); // invalid capacity size @@ -207,6 +207,9 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti /// Keccak does not have any options. pub const Options = struct {}; + /// The input delimiter. + delim: u8, + offset: usize = 0, buf: [rate]u8 = undefined, @@ -238,10 +241,28 @@ pub fn State(comptime f: u11, comptime capacity: u11, comptime delim: u8, compti } } + /// Initialize the state from a slice of bytes. + pub fn init(bytes: [f / 8]u8) Self { + return .{ .st = KeccakF(f).init(bytes) }; + } + + /// Permute the state + pub fn permute(self: *Self) void { + self.st.permuteR(rounds); + self.offset = 0; + } + + /// Align the input to the rate boundary. + pub fn fillBlock(self: *Self) void { + self.st.addBytes(self.buf[0..self.offset]); + self.st.permuteR(rounds); + self.offset = 0; + } + /// Mark the end of the input. pub fn pad(self: *Self) void { self.st.addBytes(self.buf[0..self.offset]); - self.st.addByte(delim, self.offset); + self.st.addByte(self.delim, self.offset); self.st.addByte(0x80, rate - 1); self.st.permuteR(rounds); self.offset = 0; diff --git a/lib/std/crypto/pcurves/p256/p256_64.zig b/lib/std/crypto/pcurves/p256/p256_64.zig index e8dbaead33..f3d38ca3e6 100644 --- a/lib/std/crypto/pcurves/p256/p256_64.zig +++ b/lib/std/crypto/pcurves/p256/p256_64.zig @@ -73,12 +73,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -95,12 +92,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/p256/p256_scalar_64.zig b/lib/std/crypto/pcurves/p256/p256_scalar_64.zig index 152c2b8787..736a3ea8b7 100644 --- a/lib/std/crypto/pcurves/p256/p256_scalar_64.zig +++ b/lib/std/crypto/pcurves/p256/p256_scalar_64.zig @@ -73,12 +73,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -95,12 +92,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/p384/p384_64.zig b/lib/std/crypto/pcurves/p384/p384_64.zig index f25a7d65b5..e1419e7c81 100644 --- a/lib/std/crypto/pcurves/p384/p384_64.zig +++ b/lib/std/crypto/pcurves/p384/p384_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [6]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/p384/p384_scalar_64.zig b/lib/std/crypto/pcurves/p384/p384_scalar_64.zig index fc787ba7b9..68a0a0ca2f 100644 --- a/lib/std/crypto/pcurves/p384/p384_scalar_64.zig +++ b/lib/std/crypto/pcurves/p384/p384_scalar_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [6]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig b/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig index ae3e97c619..1c69b90eea 100644 --- a/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig +++ b/lib/std/crypto/pcurves/secp256k1/secp256k1_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig b/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig index 12c833bb33..97bf5f0a45 100644 --- a/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig +++ b/lib/std/crypto/pcurves/secp256k1/secp256k1_scalar_64.zig @@ -42,12 +42,9 @@ pub const NonMontgomeryDomainFieldElement = [4]u64; /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @addWithOverflow(arg2, arg3); - const ov2 = @addWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) +% arg3 +% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function subborrowxU64 is a subtraction with borrow. @@ -64,12 +61,9 @@ inline fn addcarryxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) vo /// out1: [0x0 ~> 0xffffffffffffffff] /// out2: [0x0 ~> 0x1] inline fn subborrowxU64(out1: *u64, out2: *u1, arg1: u1, arg2: u64, arg3: u64) void { - @setRuntimeSafety(mode == .Debug); - - const ov1 = @subWithOverflow(arg2, arg3); - const ov2 = @subWithOverflow(ov1[0], arg1); - out1.* = ov2[0]; - out2.* = ov1[1] | ov2[1]; + const x = @as(u128, arg2) -% arg3 -% arg1; + out1.* = @truncate(x); + out2.* = @truncate(x >> 64); } /// The function mulxU64 is a multiplication, returning the full double-width result. diff --git a/lib/std/crypto/sha3.zig b/lib/std/crypto/sha3.zig index 8fa76a48bf..e75dcb7b79 100644 --- a/lib/std/crypto/sha3.zig +++ b/lib/std/crypto/sha3.zig @@ -18,11 +18,20 @@ pub const Keccak_512 = @compileError("Deprecated: use `Keccak512` instead"); pub const Shake128 = Shake(128); pub const Shake256 = Shake(256); +pub const CShake128 = CShake(128, null); +pub const CShake256 = CShake(256, null); + +pub const KMac128 = KMac(128); +pub const KMac256 = KMac(256); + +pub const TupleHash128 = TupleHash(128); +pub const TupleHash256 = TupleHash(256); + /// TurboSHAKE128 is a XOF (a secure hash function with a variable output length), with a 128 bit security level. /// It is based on the same permutation as SHA3 and SHAKE128, but which much higher performance. /// The delimiter is 0x1f by default, but can be changed for context-separation. /// For a protocol that uses both KangarooTwelve and TurboSHAKE128, it is recommended to avoid using 0x06, 0x07 or 0x0b for the delimiter. -pub fn TurboShake128(comptime delim: ?u7) type { +pub fn TurboShake128(delim: ?u7) type { return TurboShake(128, delim); } @@ -34,27 +43,26 @@ pub fn TurboShake256(comptime delim: ?u7) type { } /// A generic Keccak hash function. -pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime delim: u8, comptime rounds: u5) type { +pub fn Keccak(comptime f: u11, comptime output_bits: u11, comptime default_delim: u8, comptime rounds: u5) type { comptime assert(output_bits > 0 and output_bits * 2 < f and output_bits % 8 == 0); // invalid output length - const State = KeccakState(f, output_bits * 2, delim, rounds); + const State = KeccakState(f, output_bits * 2, rounds); return struct { const Self = @This(); - st: State = .{}, + st: State, /// The output length, in bytes. pub const digest_length = output_bits / 8; /// The block length, or rate, in bytes. pub const block_length = State.rate; - /// Keccak does not have any options. - pub const Options = struct {}; + /// The delimiter can be overwritten in the options. + pub const Options = struct { delim: u8 = default_delim }; /// Initialize a Keccak hash function. pub fn init(options: Options) Self { - _ = options; - return Self{}; + return Self{ .st = .{ .delim = options.delim } }; } /// Hash a slice of bytes. @@ -105,29 +113,28 @@ pub fn TurboShake(comptime security_level: u11, comptime delim: ?u7) type { return ShakeLike(security_level, d, 12); } -fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: u5) type { +fn ShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { const f = 1600; - const State = KeccakState(f, security_level * 2, delim, rounds); + const State = KeccakState(f, security_level * 2, rounds); return struct { const Self = @This(); - st: State = .{}, + st: State, buf: [State.rate]u8 = undefined, offset: usize = 0, padded: bool = false, /// The recommended output length, in bytes. - pub const digest_length = security_level / 2; + pub const digest_length = security_level / 8 * 2; /// The block length, or rate, in bytes. pub const block_length = State.rate; - /// Keccak does not have any options. - pub const Options = struct {}; + /// The delimiter can be overwritten in the options. + pub const Options = struct { delim: u8 = default_delim }; /// Initialize a SHAKE extensible hash function. pub fn init(options: Options) Self { - _ = options; - return Self{}; + return Self{ .st = .{ .delim = options.delim } }; } /// Hash a slice of bytes. @@ -182,6 +189,11 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: self.st.st.clear(0, State.rate); } + /// Align the input to a block boundary. + pub fn fillBlock(self: *Self) void { + self.st.fillBlock(); + } + pub const Error = error{}; pub const Writer = std.io.Writer(*Self, Error, write); @@ -196,6 +208,338 @@ fn ShakeLike(comptime security_level: u11, comptime delim: u8, comptime rounds: }; } +/// The cSHAKE extendable output hash function. +/// cSHAKE is similar to SHAKE, but in addition to the input message, it also takes an optional context (aka customization string). +pub fn CShake(comptime security_level: u11, comptime fname: ?[]const u8) type { + return CShakeLike(security_level, 0x04, 24, fname); +} + +fn CShakeLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5, comptime fname: ?[]const u8) type { + return struct { + const Shaker = ShakeLike(security_level, default_delim, rounds); + shaker: Shaker, + + /// The recommended output length, in bytes. + pub const digest_length = Shaker.digest_length; + /// The block length, or rate, in bytes. + pub const block_length = Shaker.block_length; + + /// cSHAKE options can include a context string. + pub const Options = struct { context: ?[]const u8 = null }; + + const Self = @This(); + + /// Initialize a SHAKE extensible hash function. + pub fn init(options: Options) Self { + if (fname == null and options.context == null) { + return Self{ .shaker = Shaker.init(.{ .delim = 0x1f }) }; + } + var shaker = Shaker.init(.{}); + comptime assert(Shaker.block_length % 8 == 0); + const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); + shaker.update(encoded_rate_len.slice()); + const encoded_zero = comptime NistLengthEncoding.encode(.left, 0); + if (fname) |name| { + const encoded_fname_len = comptime NistLengthEncoding.encode(.left, name.len); + const encoded_fname = comptime encoded_fname_len.slice() ++ name; + shaker.update(encoded_fname); + } else { + shaker.update(encoded_zero.slice()); + } + if (options.context) |context| { + const encoded_context_len = NistLengthEncoding.encode(.left, context.len); + shaker.update(encoded_context_len.slice()); + shaker.update(context); + } else { + shaker.update(encoded_zero.slice()); + } + shaker.st.fillBlock(); + return Self{ .shaker = shaker }; + } + + /// Hash a slice of bytes. + /// `out` can be any length. + pub fn hash(bytes: []const u8, out: []u8, options: Options) void { + var st = Self.init(options); + st.update(bytes); + st.squeeze(out); + } + + /// Absorb a slice of bytes into the state. + pub fn update(self: *Self, bytes: []const u8) void { + self.shaker.update(bytes); + } + + /// Squeeze a slice of bytes from the state. + /// `out` can be any length, and the function can be called multiple times. + pub fn squeeze(self: *Self, out: []u8) void { + self.shaker.squeeze(out); + } + + /// Return the hash of the absorbed bytes. + /// `out` can be of any length, but the function must not be called multiple times (use `squeeze` for that purpose instead). + pub fn final(self: *Self, out: []u8) void { + self.shaker.final(out); + } + + /// Align the input to a block boundary. + pub fn fillBlock(self: *Self) void { + self.shaker.fillBlock(); + } + + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; +} + +/// The KMAC extendable output authentication function. +/// KMAC is a keyed version of the cSHAKE function, with an optional context. +/// It can be used as an SHA-3 based alternative to HMAC, as well as a generic keyed XoF (extendable output function). +pub fn KMac(comptime security_level: u11) type { + return KMacLike(security_level, 0x04, 24); +} + +fn KMacLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { + const CShaker = CShakeLike(security_level, default_delim, rounds, "KMAC"); + + return struct { + const Self = @This(); + + /// The recommended output length, in bytes. + pub const mac_length = CShaker.digest_length; + /// The minimum output length, in bytes. + pub const mac_length_min = 4; + /// The recommended key length, in bytes. + pub const key_length = security_level / 8; + /// The minimum key length, in bytes. + pub const key_length_min = 0; + /// The block length, or rate, in bytes. + pub const block_length = CShaker.block_length; + + cshaker: CShaker, + xof_mode: bool = false, + + /// KMAC options can include a context string. + pub const Options = struct { + context: ?[]const u8 = null, + }; + + /// Initialize a state for the KMAC function, with an optional context and an arbitrary-long key. + /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. + /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. + pub fn initWithOptions(key: []const u8, options: Options) Self { + var cshaker = CShaker.init(.{ .context = options.context }); + const encoded_rate_len = NistLengthEncoding.encode(.left, block_length / 8); + cshaker.update(encoded_rate_len.slice()); + const encoded_key_len = NistLengthEncoding.encode(.left, key.len); + cshaker.update(encoded_key_len.slice()); + cshaker.update(key); + cshaker.fillBlock(); + return Self{ + .cshaker = cshaker, + }; + } + + /// Initialize a state for the KMAC function. + /// If the context and key are going to be reused, the structure can be initialized once, and cloned for each message. + /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. + pub fn init(key: []const u8) Self { + return initWithOptions(key, .{}); + } + + /// Add data to the state. + pub fn update(self: *Self, b: []const u8) void { + self.cshaker.update(b); + } + + /// Return an authentication tag for the current state. + pub fn final(self: *Self, out: []u8) void { + const encoded_out_len = NistLengthEncoding.encode(.right, out.len); + self.update(encoded_out_len.slice()); + self.cshaker.final(out); + } + + /// Squeeze a slice of bytes from the state. + /// `out` can be any length, and the function can be called multiple times. + pub fn squeeze(self: *Self, out: []u8) void { + if (!self.xof_mode) { + const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); + self.update(encoded_out_len.slice()); + self.xof_mode = true; + } + self.cshaker.squeeze(out); + } + + /// Return an authentication tag for a message and a key, with an optional context. + pub fn createWithOptions(out: []u8, msg: []const u8, key: []const u8, options: Options) void { + var ctx = Self.initWithOptions(key, options); + ctx.update(msg); + ctx.final(out); + } + + /// Return an authentication tag for a message and a key. + pub fn create(out: []u8, msg: []const u8, key: []const u8) void { + var ctx = Self.init(key); + ctx.update(msg); + ctx.final(out); + } + + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; +} + +/// The TupleHash extendable output hash function, with domain-separated inputs. +/// TupleHash is a secure hash function with a variable output length, based on the cSHAKE function. +/// It is designed for unambiguously hashing tuples of data. +/// +/// With most hash functions, calling `update("A")` followed by `update("B")`is identical to `update("AB")`. +/// With TupleHash, this is not the case: `update("A"); update("B")` is different from `update("AB")`. +/// +/// Any number of inputs can be hashed, and the output depends on individual inputs and their order. +pub fn TupleHash(comptime security_level: u11) type { + return TupleHashLike(security_level, 0x04, 24); +} + +fn TupleHashLike(comptime security_level: u11, comptime default_delim: u8, comptime rounds: u5) type { + const CShaker = CShakeLike(security_level, default_delim, rounds, "TupleHash"); + + return struct { + const Self = @This(); + + /// The output length, in bytes. + pub const digest_length = CShaker.digest_length; + /// The block length, or rate, in bytes. + pub const block_length = CShaker.block_length; + + cshaker: CShaker, + xof_mode: bool = false, + + /// TupleHash options can include a context string. + pub const Options = struct { + context: ?[]const u8 = null, + }; + + /// Initialize a state for the TupleHash function, with an optional context. + /// If the context is going to be reused, the structure can be initialized once, and cloned for each message. + /// This is more efficient than reinitializing the state for each message at the cost of a small amount of memory. + /// + /// A key can be optionally added to the context to create a keyed TupleHash function, similar to KMAC. + pub fn initWithOptions(options: Options) Self { + const cshaker = CShaker.init(.{ .context = options.context }); + return Self{ + .cshaker = cshaker, + }; + } + + /// Initialize a state for the MAC function. + pub fn init() Self { + return initWithOptions(.{}); + } + + /// Add data to the state, separated from previous updates. + pub fn update(self: *Self, b: []const u8) void { + const encoded_b_len = NistLengthEncoding.encode(.left, b.len); + self.cshaker.update(encoded_b_len.slice()); + self.cshaker.update(b); + } + + /// Return an authentication tag for the current state. + pub fn final(self: *Self, out: []u8) void { + const encoded_out_len = NistLengthEncoding.encode(.right, out.len); + self.cshaker.update(encoded_out_len.slice()); + self.cshaker.final(out); + } + + /// Align the input to a block boundary. + pub fn fillBlock(self: *Self) void { + self.cshaker.fillBlock(); + } + + /// Squeeze a slice of bytes from the state. + /// `out` can be any length, and the function can be called multiple times. + pub fn squeeze(self: *Self, out: []u8) void { + if (!self.xof_mode) { + const encoded_out_len = comptime NistLengthEncoding.encode(.right, 0); + self.update(encoded_out_len.slice()); + self.xof_mode = true; + } + self.cshaker.squeeze(out); + } + + pub const Error = error{}; + pub const Writer = std.io.Writer(*Self, Error, write); + + fn write(self: *Self, bytes: []const u8) Error!usize { + self.update(bytes); + return bytes.len; + } + + pub fn writer(self: *Self) Writer { + return .{ .context = self }; + } + }; +} + +/// The NIST SP 800-185 encoded length format. +pub const NistLengthEncoding = enum { + left, + right, + + /// A length encoded according to NIST SP 800-185. + pub const Length = struct { + /// The size of the encoded value, in bytes. + len: usize = 0, + /// A buffer to store the encoded length. + buf: [@sizeOf(usize) + 1]u8 = undefined, + + /// Return the encoded length as a slice. + pub fn slice(self: *const Length) []const u8 { + return self.buf[0..self.len]; + } + }; + + /// Encode a length according to NIST SP 800-185. + pub fn encode(comptime encoding: NistLengthEncoding, len: usize) Length { + const len_bits = @bitSizeOf(@TypeOf(len)) - @clz(len) + 3; + const len_bytes = std.math.divCeil(usize, len_bits, 8) catch unreachable; + + var res = Length{ .len = len_bytes + 1 }; + if (encoding == .right) { + res.buf[len_bytes] = @intCast(len_bytes); + } + const end = if (encoding == .right) len_bytes - 1 else len_bytes; + res.buf[end] = @truncate(len << 3); + var len_ = len >> 5; + for (1..len_bytes) |i| { + res.buf[end - i] = @truncate(len_); + len_ >>= 8; + } + if (encoding == .left) { + res.buf[0] = @intCast(len_bytes); + } + return res; + } +}; + const htest = @import("test.zig"); test "sha3-224 single" { @@ -397,3 +741,88 @@ test "SHA-3 with streaming" { h.final(&out); try htest.assertEqual("5780048dfa381a1d01c747906e4a08711dd34fd712ecd7c6801dd2b38fd81a89", &out); } + +test "cSHAKE-128 with no context nor function name" { + var out: [32]u8 = undefined; + CShake128.hash("hello123", &out, .{}); + try htest.assertEqual("1b85861510bc4d8e467d6f8a92270533cbaa7ba5e06c2d2a502854bac468b8b9", &out); +} + +test "cSHAKE-128 with context" { + var out: [32]u8 = undefined; + CShake128.hash("hello123", &out, .{ .context = "custom" }); + try htest.assertEqual("7509fa13a6bd3e38ad5c6fac042142c233996e40ebffc86c276f108b3b19cc6a", &out); +} + +test "cSHAKE-128 with context and function" { + var out: [32]u8 = undefined; + CShake(128, "function").hash("hello123", &out, .{ .context = "custom" }); + try htest.assertEqual("ad7f4d7db2d96587fcd5047c65d37c368f5366e3afac60bb9b66b0bb95dfb675", &out); +} + +test "cSHAKE-256" { + var out: [32]u8 = undefined; + CShake256.hash("hello123", &out, .{ .context = "custom" }); + try htest.assertEqual("dabe027eb1a6cbe3a0542d0560eb4e6b39146dd72ae1bf89c970a61bd93b1813", &out); +} + +test "KMAC-128 with empty key and message" { + var out: [KMac128.mac_length]u8 = undefined; + const key = ""; + KMac128.create(&out, "", key); + try htest.assertEqual("5c135c615152fb4d9784dd1155f9b6034e013fd77165c327dfa4d36701983ef7", &out); +} + +test "KMAC-128" { + var out: [KMac128.mac_length]u8 = undefined; + const key = "A KMAC secret key"; + KMac128.create(&out, "hello123", key); + try htest.assertEqual("1fa1c0d761129a83f9a4299ca137674de8373a3cc437799ae4c129e651627f8e", &out); +} + +test "KMAC-128 with a customization string" { + var out: [KMac128.mac_length]u8 = undefined; + const key = "A KMAC secret key"; + KMac128.createWithOptions(&out, "hello123", key, .{ .context = "custom" }); + try htest.assertEqual("c58c6d42dc00a27dfa8e7e08f8c9307cecb5d662ddb11b6c36057fc2e0e068ba", &out); +} + +test "KMACXOF-128" { + const key = "A KMAC secret key"; + var xof = KMac128.init(key); + xof.update("hello123"); + var out: [50]u8 = undefined; + xof.squeeze(&out); + try htest.assertEqual("628c2fb870d294b3673ac82d9f0d651aae6a5bb8084ea8cd8343cb888d075b9053173200a71f301141069c3c0322527981f7", &out); + xof.squeeze(&out); + try htest.assertEqual("7b638e178cfdac5727a4ea7694efaa967a65a1d0034501855acff506b4158d187d5a18d668e67b43f2abf61144b20ed4c09f", &out); +} + +test "KMACXOF-256" { + const key = "A KMAC secret key"; + var xof = KMac256.init(key); + xof.update("hello123"); + var out: [50]u8 = undefined; + xof.squeeze(&out); + try htest.assertEqual("23fc644bc2655ba6fde7b7c11f2804f22e8d8c6bd7db856268bf3370ce2362703f6c7e91916a1b8c116e60edfbcb25613054", &out); + xof.squeeze(&out); + try htest.assertEqual("ff97251020ff255ee65a1c1f5f78ebe904f61211c39f973f82fbce2b196b9f51c2cb12afe51549a0f1eaf7954e657ba11af3", &out); +} + +test "TupleHash-128" { + var st = TupleHash128.init(); + st.update("hello"); + st.update("123"); + var out: [32]u8 = undefined; + st.final(&out); + try htest.assertEqual("3938d49ade8ec0f0c305ac63497b2d2e8b2f650714f9667cc41816b1c11ffd20", &out); +} + +test "TupleHash-256" { + var st = TupleHash256.init(); + st.update("hello"); + st.update("123"); + var out: [64]u8 = undefined; + st.final(&out); + try htest.assertEqual("2dca563c2882f2ba4f46a441a4c5e13fb97150d1436fe99c7e4e43a2d20d0f1cd3d38483bde4a966930606dfa6c61c4ca6400aeedfb474d1bf0d7f6a70968289", &out); +} diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d1d6201b80..eaa5ca9ff7 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -406,7 +406,7 @@ pub fn assert(ok: bool) void { pub fn panic(comptime format: []const u8, args: anytype) noreturn { @setCold(true); - panicExtra(null, null, format, args); + panicExtra(@errorReturnTrace(), @returnAddress(), format, args); } /// `panicExtra` is useful when you want to print out an `@errorReturnTrace` @@ -661,7 +661,7 @@ pub const StackIterator = struct { fn isValidMemory(address: usize) bool { // We are unable to determine validity of memory for freestanding targets - if (native_os == .freestanding) return true; + if (native_os == .freestanding or native_os == .uefi) return true; const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1))); if (aligned_address == 0) return false; diff --git a/lib/std/fs/Dir.zig b/lib/std/fs/Dir.zig index 74d91239f8..b6222f942f 100644 --- a/lib/std/fs/Dir.zig +++ b/lib/std/fs/Dir.zig @@ -220,71 +220,83 @@ pub const Iterator = switch (native_os) { }, .haiku => struct { dir: Dir, - buf: [1024]u8, // TODO align(@alignOf(posix.dirent64)), + buf: [@sizeOf(DirEnt) + posix.PATH_MAX]u8 align(@alignOf(DirEnt)), + offset: usize, index: usize, end_index: usize, first_iter: bool, const Self = @This(); + const DirEnt = posix.system.DirEnt; pub const Error = IteratorError; /// Memory such as file names referenced in this returned entry becomes invalid /// with subsequent calls to `next`, as well as when this `Dir` is deinitialized. pub fn next(self: *Self) Error!?Entry { - start_over: while (true) { - // TODO: find a better max - const HAIKU_MAX_COUNT = 10000; + while (true) { if (self.index >= self.end_index) { if (self.first_iter) { - posix.lseek_SET(self.dir.fd, 0) catch unreachable; // EBADF here likely means that the Dir was not opened with iteration permissions + switch (@as(posix.E, @enumFromInt(posix.system._kern_rewind_dir(self.dir.fd)))) { + .SUCCESS => {}, + .BADF => unreachable, // Dir is invalid + .FAULT => unreachable, + .NOTDIR => unreachable, + .INVAL => unreachable, + .ACCES => return error.AccessDenied, + .PERM => return error.AccessDenied, + else => |err| return posix.unexpectedErrno(err), + } self.first_iter = false; } const rc = posix.system._kern_read_dir( self.dir.fd, &self.buf, self.buf.len, - HAIKU_MAX_COUNT, + self.buf.len / @sizeOf(DirEnt), ); if (rc == 0) return null; if (rc < 0) { - switch (posix.errno(rc)) { - .BADF => unreachable, // Dir is invalid or was opened without iteration ability + switch (@as(posix.E, @enumFromInt(rc))) { + .BADF => unreachable, // Dir is invalid .FAULT => unreachable, .NOTDIR => unreachable, .INVAL => unreachable, + .OVERFLOW => unreachable, + .ACCES => return error.AccessDenied, + .PERM => return error.AccessDenied, else => |err| return posix.unexpectedErrno(err), } } + self.offset = 0; self.index = 0; - self.end_index = @as(usize, @intCast(rc)); - } - const haiku_entry = @as(*align(1) posix.system.dirent, @ptrCast(&self.buf[self.index])); - const next_index = self.index + haiku_entry.reclen; - self.index = next_index; - const name = mem.sliceTo(@as([*:0]u8, @ptrCast(&haiku_entry.name)), 0); - - if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or (haiku_entry.ino == 0)) { - continue :start_over; + self.end_index = @intCast(rc); } + const dirent: *DirEnt = @ptrCast(@alignCast(&self.buf[self.offset])); + self.offset += dirent.reclen; + self.index += 1; + const name = mem.span(dirent.getName()); + if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..") or dirent.ino == 0) continue; var stat_info: posix.Stat = undefined; - const rc = posix.system._kern_read_stat( + switch (@as(posix.E, @enumFromInt(posix.system._kern_read_stat( self.dir.fd, - &haiku_entry.name, + name, false, &stat_info, 0, - ); - if (rc != 0) { - switch (posix.errno(rc)) { - .SUCCESS => {}, - .BADF => unreachable, // Dir is invalid or was opened without iteration ability - .FAULT => unreachable, - .NOTDIR => unreachable, - .INVAL => unreachable, - else => |err| return posix.unexpectedErrno(err), - } + )))) { + .SUCCESS => {}, + .INVAL => unreachable, + .BADF => unreachable, // Dir is invalid + .NOMEM => return error.SystemResources, + .ACCES => return error.AccessDenied, + .PERM => return error.AccessDenied, + .FAULT => unreachable, + .NAMETOOLONG => unreachable, + .LOOP => unreachable, + .NOENT => continue, + else => |err| return posix.unexpectedErrno(err), } const statmode = stat_info.mode & posix.S.IFMT; @@ -315,7 +327,7 @@ pub const Iterator = switch (native_os) { dir: Dir, // The if guard is solely there to prevent compile errors from missing `linux.dirent64` // definition when compiling for other OSes. It doesn't do anything when compiling for Linux. - buf: [1024]u8 align(if (native_os != .linux) 1 else @alignOf(linux.dirent64)), + buf: [1024]u8 align(@alignOf(linux.dirent64)), index: usize, end_index: usize, first_iter: bool, @@ -599,8 +611,16 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { .buf = undefined, .first_iter = first_iter_start_value, }, - .linux, .haiku => return Iterator{ + .linux => return Iterator{ + .dir = self, + .index = 0, + .end_index = 0, + .buf = undefined, + .first_iter = first_iter_start_value, + }, + .haiku => return Iterator{ .dir = self, + .offset = 0, .index = 0, .end_index = 0, .buf = undefined, @@ -626,16 +646,17 @@ fn iterateImpl(self: Dir, first_iter_start_value: bool) Iterator { } pub const Walker = struct { - stack: std.ArrayList(StackItem), - name_buffer: std.ArrayList(u8), + stack: std.ArrayListUnmanaged(StackItem), + name_buffer: std.ArrayListUnmanaged(u8), + allocator: Allocator, - pub const WalkerEntry = struct { + pub const Entry = struct { /// The containing directory. This can be used to operate directly on `basename` /// rather than `path`, avoiding `error.NameTooLong` for deeply nested paths. /// The directory remains open until `next` or `deinit` is called. dir: Dir, - basename: []const u8, - path: []const u8, + basename: [:0]const u8, + path: [:0]const u8, kind: Dir.Entry.Kind, }; @@ -647,7 +668,8 @@ pub const Walker = struct { /// After each call to this function, and on deinit(), the memory returned /// from this function becomes invalid. A copy must be made in order to keep /// a reference to the path. - pub fn next(self: *Walker) !?WalkerEntry { + pub fn next(self: *Walker) !?Walker.Entry { + const gpa = self.allocator; while (self.stack.items.len != 0) { // `top` and `containing` become invalid after appending to `self.stack` var top = &self.stack.items[self.stack.items.len - 1]; @@ -666,10 +688,12 @@ pub const Walker = struct { }) |base| { self.name_buffer.shrinkRetainingCapacity(dirname_len); if (self.name_buffer.items.len != 0) { - try self.name_buffer.append(fs.path.sep); + try self.name_buffer.append(gpa, fs.path.sep); dirname_len += 1; } - try self.name_buffer.appendSlice(base.name); + try self.name_buffer.ensureUnusedCapacity(gpa, base.name.len + 1); + self.name_buffer.appendSliceAssumeCapacity(base.name); + self.name_buffer.appendAssumeCapacity(0); if (base.kind == .directory) { var new_dir = top.iter.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) { error.NameTooLong => unreachable, // no path sep in base.name @@ -677,18 +701,18 @@ pub const Walker = struct { }; { errdefer new_dir.close(); - try self.stack.append(StackItem{ + try self.stack.append(gpa, .{ .iter = new_dir.iterateAssumeFirstIteration(), - .dirname_len = self.name_buffer.items.len, + .dirname_len = self.name_buffer.items.len - 1, }); top = &self.stack.items[self.stack.items.len - 1]; containing = &self.stack.items[self.stack.items.len - 2]; } } - return WalkerEntry{ + return .{ .dir = containing.iter.dir, - .basename = self.name_buffer.items[dirname_len..], - .path = self.name_buffer.items, + .basename = self.name_buffer.items[dirname_len .. self.name_buffer.items.len - 1 :0], + .path = self.name_buffer.items[0 .. self.name_buffer.items.len - 1 :0], .kind = base.kind, }; } else { @@ -702,37 +726,39 @@ pub const Walker = struct { } pub fn deinit(self: *Walker) void { + const gpa = self.allocator; // Close any remaining directories except the initial one (which is always at index 0) if (self.stack.items.len > 1) { for (self.stack.items[1..]) |*item| { item.iter.dir.close(); } } - self.stack.deinit(); - self.name_buffer.deinit(); + self.stack.deinit(gpa); + self.name_buffer.deinit(gpa); } }; /// Recursively iterates over a directory. +/// /// `self` must have been opened with `OpenDirOptions{.iterate = true}`. -/// Must call `Walker.deinit` when done. +/// +/// `Walker.deinit` releases allocated memory and directory handles. +/// /// The order of returned file system entries is undefined. +/// /// `self` will not be closed after walking it. -pub fn walk(self: Dir, allocator: Allocator) !Walker { - var name_buffer = std.ArrayList(u8).init(allocator); - errdefer name_buffer.deinit(); - - var stack = std.ArrayList(Walker.StackItem).init(allocator); - errdefer stack.deinit(); +pub fn walk(self: Dir, allocator: Allocator) Allocator.Error!Walker { + var stack: std.ArrayListUnmanaged(Walker.StackItem) = .{}; - try stack.append(Walker.StackItem{ + try stack.append(allocator, .{ .iter = self.iterate(), .dirname_len = 0, }); - return Walker{ + return .{ .stack = stack, - .name_buffer = name_buffer, + .name_buffer = .{}, + .allocator = allocator, }; } @@ -1429,6 +1455,27 @@ pub fn openDirZ(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) Open .wasi => { return openDir(self, mem.sliceTo(sub_path_c, 0), args); }, + .haiku => { + const rc = posix.system._kern_open_dir(self.fd, sub_path_c); + if (rc >= 0) return .{ .fd = rc }; + switch (@as(posix.E, @enumFromInt(rc))) { + .FAULT => unreachable, + .INVAL => unreachable, + .BADF => unreachable, + .ACCES => return error.AccessDenied, + .LOOP => return error.SymLinkLoop, + .MFILE => return error.ProcessFdQuotaExceeded, + .NAMETOOLONG => return error.NameTooLong, + .NFILE => return error.SystemFdQuotaExceeded, + .NODEV => return error.NoDevice, + .NOENT => return error.FileNotFound, + .NOMEM => return error.SystemResources, + .NOTDIR => return error.NotDir, + .PERM => return error.AccessDenied, + .BUSY => return error.DeviceBusy, + else => |err| return posix.unexpectedErrno(err), + } + }, else => { var symlink_flags: posix.O = .{ .ACCMODE = .RDONLY, diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index 0ac4de761e..732c03e721 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -1,285 +1,911 @@ -// There are two implementations of CRC32 implemented with the following key characteristics: -// -// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method. -// -// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is -// still moderately fast just slow relative to the slicing approach. - -const std = @import("std"); -const builtin = @import("builtin"); -const debug = std.debug; -const testing = std.testing; - -pub usingnamespace @import("crc/catalog.zig"); - -pub fn Algorithm(comptime W: type) type { - return struct { - polynomial: W, - initial: W, - reflect_input: bool, - reflect_output: bool, - xor_output: W, - }; -} +//! This file is auto-generated by tools/update_crc_catalog.zig. -pub fn Crc(comptime W: type, comptime algorithm: Algorithm(W)) type { - return struct { - const Self = @This(); - const I = if (@bitSizeOf(W) < 8) u8 else W; - const lookup_table = blk: { - @setEvalBranchQuota(2500); - - const poly = if (algorithm.reflect_input) - @bitReverse(@as(I, algorithm.polynomial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) - else - @as(I, algorithm.polynomial) << (@bitSizeOf(I) - @bitSizeOf(W)); - - var table: [256]I = undefined; - for (&table, 0..) |*e, i| { - var crc: I = i; - if (algorithm.reflect_input) { - var j: usize = 0; - while (j < 8) : (j += 1) { - crc = (crc >> 1) ^ ((crc & 1) * poly); - } - } else { - crc <<= @bitSizeOf(I) - 8; - var j: usize = 0; - while (j < 8) : (j += 1) { - crc = (crc << 1) ^ (((crc >> (@bitSizeOf(I) - 1)) & 1) * poly); - } - } - e.* = crc; - } - break :blk table; - }; - - crc: I, - - pub fn init() Self { - const initial = if (algorithm.reflect_input) - @bitReverse(@as(I, algorithm.initial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) - else - @as(I, algorithm.initial) << (@bitSizeOf(I) - @bitSizeOf(W)); - return Self{ .crc = initial }; - } - - inline fn tableEntry(index: I) I { - return lookup_table[@as(u8, @intCast(index & 0xFF))]; - } - - pub fn update(self: *Self, bytes: []const u8) void { - var i: usize = 0; - if (@bitSizeOf(I) <= 8) { - while (i < bytes.len) : (i += 1) { - self.crc = tableEntry(self.crc ^ bytes[i]); - } - } else if (algorithm.reflect_input) { - while (i < bytes.len) : (i += 1) { - const table_index = self.crc ^ bytes[i]; - self.crc = tableEntry(table_index) ^ (self.crc >> 8); - } - } else { - while (i < bytes.len) : (i += 1) { - const table_index = (self.crc >> (@bitSizeOf(I) - 8)) ^ bytes[i]; - self.crc = tableEntry(table_index) ^ (self.crc << 8); - } - } - } - - pub fn final(self: Self) W { - var c = self.crc; - if (algorithm.reflect_input != algorithm.reflect_output) { - c = @bitReverse(c); - } - if (!algorithm.reflect_output) { - c >>= @bitSizeOf(I) - @bitSizeOf(W); - } - return @as(W, @intCast(c ^ algorithm.xor_output)); - } - - pub fn hash(bytes: []const u8) W { - var c = Self.init(); - c.update(bytes); - return c.final(); - } - }; -} +const impl = @import("crc/impl.zig"); -pub const Polynomial = enum(u32) { - IEEE = 0xedb88320, - Castagnoli = 0x82f63b78, - Koopman = 0xeb31d82e, - _, -}; +pub const Crc = impl.Crc; +pub const Polynomial = impl.Polynomial; +pub const Crc32WithPoly = impl.Crc32WithPoly; +pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly; // IEEE is by far the most common CRC and so is aliased by default. pub const Crc32 = Crc32WithPoly(.IEEE); -// slicing-by-8 crc32 implementation. -pub fn Crc32WithPoly(comptime poly: Polynomial) type { - return struct { - const Self = @This(); - const lookup_tables = block: { - @setEvalBranchQuota(20000); - var tables: [8][256]u32 = undefined; - - for (&tables[0], 0..) |*e, i| { - var crc = @as(u32, @intCast(i)); - var j: usize = 0; - while (j < 8) : (j += 1) { - if (crc & 1 == 1) { - crc = (crc >> 1) ^ @intFromEnum(poly); - } else { - crc = (crc >> 1); - } - } - e.* = crc; - } - - var i: usize = 0; - while (i < 256) : (i += 1) { - var crc = tables[0][i]; - var j: usize = 1; - while (j < 8) : (j += 1) { - const index: u8 = @truncate(crc); - crc = tables[0][index] ^ (crc >> 8); - tables[j][i] = crc; - } - } - - break :block tables; - }; - - crc: u32, - - pub fn init() Self { - return Self{ .crc = 0xffffffff }; - } - - pub fn update(self: *Self, input: []const u8) void { - var i: usize = 0; - while (i + 8 <= input.len) : (i += 8) { - const p = input[i..][0..8]; - - // Unrolling this way gives ~50Mb/s increase - self.crc ^= std.mem.readInt(u32, p[0..4], .little); - - self.crc = - lookup_tables[0][p[7]] ^ - lookup_tables[1][p[6]] ^ - lookup_tables[2][p[5]] ^ - lookup_tables[3][p[4]] ^ - lookup_tables[4][@as(u8, @truncate(self.crc >> 24))] ^ - lookup_tables[5][@as(u8, @truncate(self.crc >> 16))] ^ - lookup_tables[6][@as(u8, @truncate(self.crc >> 8))] ^ - lookup_tables[7][@as(u8, @truncate(self.crc >> 0))]; - } - - while (i < input.len) : (i += 1) { - const index = @as(u8, @truncate(self.crc)) ^ input[i]; - self.crc = (self.crc >> 8) ^ lookup_tables[0][index]; - } - } - - pub fn final(self: *Self) u32 { - return ~self.crc; - } - - pub fn hash(input: []const u8) u32 { - var c = Self.init(); - c.update(input); - return c.final(); - } - }; -} - -const verify = @import("verify.zig"); - -test "crc32 ieee" { - const Crc32Ieee = Crc32WithPoly(.IEEE); - - try testing.expect(Crc32Ieee.hash("") == 0x00000000); - try testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); - try testing.expect(Crc32Ieee.hash("abc") == 0x352441c2); -} - -test "crc32 castagnoli" { - const Crc32Castagnoli = Crc32WithPoly(.Castagnoli); - - try testing.expect(Crc32Castagnoli.hash("") == 0x00000000); - try testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); - try testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7); -} - -test "crc32 iterative" { - try verify.iterativeApi(Crc32WithPoly(.IEEE)); -} - -// half-byte lookup table implementation. -pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { - return struct { - const Self = @This(); - const lookup_table = block: { - var table: [16]u32 = undefined; - - for (&table, 0..) |*e, i| { - var crc = @as(u32, @intCast(i * 16)); - var j: usize = 0; - while (j < 8) : (j += 1) { - if (crc & 1 == 1) { - crc = (crc >> 1) ^ @intFromEnum(poly); - } else { - crc = (crc >> 1); - } - } - e.* = crc; - } - - break :block table; - }; - - crc: u32, - - pub fn init() Self { - return Self{ .crc = 0xffffffff }; - } - - pub fn update(self: *Self, input: []const u8) void { - for (input) |b| { - self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 0)))] ^ (self.crc >> 4); - self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 4)))] ^ (self.crc >> 4); - } - } - - pub fn final(self: *Self) u32 { - return ~self.crc; - } - - pub fn hash(input: []const u8) u32 { - var c = Self.init(); - c.update(input); - return c.final(); - } - }; -} - -test "small crc32 iterative" { - try verify.iterativeApi(Crc32SmallWithPoly(.IEEE)); +test { + _ = @import("crc/test.zig"); } -test "small crc32 ieee" { - const Crc32Ieee = Crc32SmallWithPoly(.IEEE); - - try testing.expect(Crc32Ieee.hash("") == 0x00000000); - try testing.expect(Crc32Ieee.hash("a") == 0xe8b7be43); - try testing.expect(Crc32Ieee.hash("abc") == 0x352441c2); -} - -test "small crc32 castagnoli" { - const Crc32Castagnoli = Crc32SmallWithPoly(.Castagnoli); - - try testing.expect(Crc32Castagnoli.hash("") == 0x00000000); - try testing.expect(Crc32Castagnoli.hash("a") == 0xc1d04330); - try testing.expect(Crc32Castagnoli.hash("abc") == 0x364b3fb7); -} +pub const Crc3Gsm = Crc(u3, .{ + .polynomial = 0x3, + .initial = 0x0, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x7, +}); + +pub const Crc3Rohc = Crc(u3, .{ + .polynomial = 0x3, + .initial = 0x7, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0, +}); + +pub const Crc4G704 = Crc(u4, .{ + .polynomial = 0x3, + .initial = 0x0, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0, +}); + +pub const Crc4Interlaken = Crc(u4, .{ + .polynomial = 0x3, + .initial = 0xf, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xf, +}); + +pub const Crc5EpcC1g2 = Crc(u5, .{ + .polynomial = 0x09, + .initial = 0x09, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc5G704 = Crc(u5, .{ + .polynomial = 0x15, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc5Usb = Crc(u5, .{ + .polynomial = 0x05, + .initial = 0x1f, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x1f, +}); + +pub const Crc6Cdma2000A = Crc(u6, .{ + .polynomial = 0x27, + .initial = 0x3f, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc6Cdma2000B = Crc(u6, .{ + .polynomial = 0x07, + .initial = 0x3f, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc6Darc = Crc(u6, .{ + .polynomial = 0x19, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc6G704 = Crc(u6, .{ + .polynomial = 0x03, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc6Gsm = Crc(u6, .{ + .polynomial = 0x2f, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3f, +}); + +pub const Crc7Mmc = Crc(u7, .{ + .polynomial = 0x09, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc7Rohc = Crc(u7, .{ + .polynomial = 0x4f, + .initial = 0x7f, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc7Umts = Crc(u7, .{ + .polynomial = 0x45, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Autosar = Crc(u8, .{ + .polynomial = 0x2f, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xff, +}); + +pub const Crc8Bluetooth = Crc(u8, .{ + .polynomial = 0xa7, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8Cdma2000 = Crc(u8, .{ + .polynomial = 0x9b, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Darc = Crc(u8, .{ + .polynomial = 0x39, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8DvbS2 = Crc(u8, .{ + .polynomial = 0xd5, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8GsmA = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8GsmB = Crc(u8, .{ + .polynomial = 0x49, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xff, +}); + +pub const Crc8Hitag = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8I4321 = Crc(u8, .{ + .polynomial = 0x07, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x55, +}); + +pub const Crc8ICode = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xfd, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Lte = Crc(u8, .{ + .polynomial = 0x9b, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8MaximDow = Crc(u8, .{ + .polynomial = 0x31, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8MifareMad = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xc7, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Nrsc5 = Crc(u8, .{ + .polynomial = 0x31, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Opensafety = Crc(u8, .{ + .polynomial = 0x2f, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Rohc = Crc(u8, .{ + .polynomial = 0x07, + .initial = 0xff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8SaeJ1850 = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xff, +}); + +pub const Crc8Smbus = Crc(u8, .{ + .polynomial = 0x07, + .initial = 0x00, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00, +}); + +pub const Crc8Tech3250 = Crc(u8, .{ + .polynomial = 0x1d, + .initial = 0xff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc8Wcdma = Crc(u8, .{ + .polynomial = 0x9b, + .initial = 0x00, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00, +}); + +pub const Crc10Atm = Crc(u10, .{ + .polynomial = 0x233, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc10Cdma2000 = Crc(u10, .{ + .polynomial = 0x3d9, + .initial = 0x3ff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc10Gsm = Crc(u10, .{ + .polynomial = 0x175, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3ff, +}); + +pub const Crc11Flexray = Crc(u11, .{ + .polynomial = 0x385, + .initial = 0x01a, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc11Umts = Crc(u11, .{ + .polynomial = 0x307, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc12Cdma2000 = Crc(u12, .{ + .polynomial = 0xf13, + .initial = 0xfff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc12Dect = Crc(u12, .{ + .polynomial = 0x80f, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000, +}); + +pub const Crc12Gsm = Crc(u12, .{ + .polynomial = 0xd31, + .initial = 0x000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xfff, +}); + +pub const Crc12Umts = Crc(u12, .{ + .polynomial = 0x80f, + .initial = 0x000, + .reflect_input = false, + .reflect_output = true, + .xor_output = 0x000, +}); + +pub const Crc13Bbc = Crc(u13, .{ + .polynomial = 0x1cf5, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc14Darc = Crc(u14, .{ + .polynomial = 0x0805, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc14Gsm = Crc(u14, .{ + .polynomial = 0x202d, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3fff, +}); + +pub const Crc15Can = Crc(u15, .{ + .polynomial = 0x4599, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc15Mpt1327 = Crc(u15, .{ + .polynomial = 0x6815, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0001, +}); + +pub const Crc16Arc = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Cdma2000 = Crc(u16, .{ + .polynomial = 0xc867, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Cms = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Dds110 = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x800d, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16DectR = Crc(u16, .{ + .polynomial = 0x0589, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0001, +}); + +pub const Crc16DectX = Crc(u16, .{ + .polynomial = 0x0589, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Dnp = Crc(u16, .{ + .polynomial = 0x3d65, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16En13757 = Crc(u16, .{ + .polynomial = 0x3d65, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Genibus = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Gsm = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Ibm3740 = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16IbmSdlc = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16IsoIec144433A = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xc6c6, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Kermit = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Lj1200 = Crc(u16, .{ + .polynomial = 0x6f63, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16M17 = Crc(u16, .{ + .polynomial = 0x5935, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16MaximDow = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x0000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16Mcrf4xx = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Modbus = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Nrsc5 = Crc(u16, .{ + .polynomial = 0x080b, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16OpensafetyA = Crc(u16, .{ + .polynomial = 0x5935, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16OpensafetyB = Crc(u16, .{ + .polynomial = 0x755b, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Profibus = Crc(u16, .{ + .polynomial = 0x1dcf, + .initial = 0xffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffff, +}); + +pub const Crc16Riello = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0xb2aa, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16SpiFujitsu = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x1d0f, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16T10Dif = Crc(u16, .{ + .polynomial = 0x8bb7, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Teledisk = Crc(u16, .{ + .polynomial = 0xa097, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Tms37157 = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x89ec, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000, +}); + +pub const Crc16Umts = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc16Usb = Crc(u16, .{ + .polynomial = 0x8005, + .initial = 0xffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffff, +}); + +pub const Crc16Xmodem = Crc(u16, .{ + .polynomial = 0x1021, + .initial = 0x0000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000, +}); + +pub const Crc17CanFd = Crc(u17, .{ + .polynomial = 0x1685b, + .initial = 0x00000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000, +}); + +pub const Crc21CanFd = Crc(u21, .{ + .polynomial = 0x102899, + .initial = 0x000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Ble = Crc(u24, .{ + .polynomial = 0x00065b, + .initial = 0x555555, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x000000, +}); + +pub const Crc24FlexrayA = Crc(u24, .{ + .polynomial = 0x5d6dcb, + .initial = 0xfedcba, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24FlexrayB = Crc(u24, .{ + .polynomial = 0x5d6dcb, + .initial = 0xabcdef, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Interlaken = Crc(u24, .{ + .polynomial = 0x328b63, + .initial = 0xffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffff, +}); + +pub const Crc24LteA = Crc(u24, .{ + .polynomial = 0x864cfb, + .initial = 0x000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24LteB = Crc(u24, .{ + .polynomial = 0x800063, + .initial = 0x000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Openpgp = Crc(u24, .{ + .polynomial = 0x864cfb, + .initial = 0xb704ce, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x000000, +}); + +pub const Crc24Os9 = Crc(u24, .{ + .polynomial = 0x800063, + .initial = 0xffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffff, +}); + +pub const Crc30Cdma = Crc(u30, .{ + .polynomial = 0x2030b9c7, + .initial = 0x3fffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x3fffffff, +}); + +pub const Crc31Philips = Crc(u31, .{ + .polynomial = 0x04c11db7, + .initial = 0x7fffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x7fffffff, +}); + +pub const Crc32Aixm = Crc(u32, .{ + .polynomial = 0x814141ab, + .initial = 0x00000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000000, +}); + +pub const Crc32Autosar = Crc(u32, .{ + .polynomial = 0xf4acfb13, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32Base91D = Crc(u32, .{ + .polynomial = 0xa833982b, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32Bzip2 = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffff, +}); + +pub const Crc32CdRomEdc = Crc(u32, .{ + .polynomial = 0x8001801b, + .initial = 0x00000000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00000000, +}); + +pub const Crc32Cksum = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0x00000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffff, +}); + +pub const Crc32Iscsi = Crc(u32, .{ + .polynomial = 0x1edc6f41, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32IsoHdlc = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffff, +}); + +pub const Crc32Jamcrc = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00000000, +}); + +pub const Crc32Mef = Crc(u32, .{ + .polynomial = 0x741b8cd7, + .initial = 0xffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x00000000, +}); + +pub const Crc32Mpeg2 = Crc(u32, .{ + .polynomial = 0x04c11db7, + .initial = 0xffffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000000, +}); + +pub const Crc32Xfer = Crc(u32, .{ + .polynomial = 0x000000af, + .initial = 0x00000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x00000000, +}); + +pub const Crc40Gsm = Crc(u40, .{ + .polynomial = 0x0004820009, + .initial = 0x0000000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffffff, +}); + +pub const Crc64Ecma182 = Crc(u64, .{ + .polynomial = 0x42f0e1eba9ea3693, + .initial = 0x0000000000000000, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0x0000000000000000, +}); + +pub const Crc64GoIso = Crc(u64, .{ + .polynomial = 0x000000000000001b, + .initial = 0xffffffffffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffffffffffff, +}); + +pub const Crc64Ms = Crc(u64, .{ + .polynomial = 0x259c84cba6426349, + .initial = 0xffffffffffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000000000000000, +}); + +pub const Crc64Redis = Crc(u64, .{ + .polynomial = 0xad93d23594c935a9, + .initial = 0x0000000000000000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x0000000000000000, +}); + +pub const Crc64We = Crc(u64, .{ + .polynomial = 0x42f0e1eba9ea3693, + .initial = 0xffffffffffffffff, + .reflect_input = false, + .reflect_output = false, + .xor_output = 0xffffffffffffffff, +}); + +pub const Crc64Xz = Crc(u64, .{ + .polynomial = 0x42f0e1eba9ea3693, + .initial = 0xffffffffffffffff, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0xffffffffffffffff, +}); + +pub const Crc82Darc = Crc(u82, .{ + .polynomial = 0x0308c0111011401440411, + .initial = 0x000000000000000000000, + .reflect_input = true, + .reflect_output = true, + .xor_output = 0x000000000000000000000, +}); diff --git a/lib/std/hash/crc/catalog.zig b/lib/std/hash/crc/catalog.zig deleted file mode 100644 index ed08accce6..0000000000 --- a/lib/std/hash/crc/catalog.zig +++ /dev/null @@ -1,903 +0,0 @@ -//! This file is auto-generated by tools/update_crc_catalog.zig. - -const Crc = @import("../crc.zig").Crc; - -test { - _ = @import("catalog_test.zig"); -} - -pub const Crc3Gsm = Crc(u3, .{ - .polynomial = 0x3, - .initial = 0x0, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x7, -}); - -pub const Crc3Rohc = Crc(u3, .{ - .polynomial = 0x3, - .initial = 0x7, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0, -}); - -pub const Crc4G704 = Crc(u4, .{ - .polynomial = 0x3, - .initial = 0x0, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0, -}); - -pub const Crc4Interlaken = Crc(u4, .{ - .polynomial = 0x3, - .initial = 0xf, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xf, -}); - -pub const Crc5EpcC1g2 = Crc(u5, .{ - .polynomial = 0x09, - .initial = 0x09, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc5G704 = Crc(u5, .{ - .polynomial = 0x15, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc5Usb = Crc(u5, .{ - .polynomial = 0x05, - .initial = 0x1f, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x1f, -}); - -pub const Crc6Cdma2000A = Crc(u6, .{ - .polynomial = 0x27, - .initial = 0x3f, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc6Cdma2000B = Crc(u6, .{ - .polynomial = 0x07, - .initial = 0x3f, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc6Darc = Crc(u6, .{ - .polynomial = 0x19, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc6G704 = Crc(u6, .{ - .polynomial = 0x03, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc6Gsm = Crc(u6, .{ - .polynomial = 0x2f, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3f, -}); - -pub const Crc7Mmc = Crc(u7, .{ - .polynomial = 0x09, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc7Rohc = Crc(u7, .{ - .polynomial = 0x4f, - .initial = 0x7f, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc7Umts = Crc(u7, .{ - .polynomial = 0x45, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Autosar = Crc(u8, .{ - .polynomial = 0x2f, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xff, -}); - -pub const Crc8Bluetooth = Crc(u8, .{ - .polynomial = 0xa7, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8Cdma2000 = Crc(u8, .{ - .polynomial = 0x9b, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Darc = Crc(u8, .{ - .polynomial = 0x39, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8DvbS2 = Crc(u8, .{ - .polynomial = 0xd5, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8GsmA = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8GsmB = Crc(u8, .{ - .polynomial = 0x49, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xff, -}); - -pub const Crc8Hitag = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8I4321 = Crc(u8, .{ - .polynomial = 0x07, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x55, -}); - -pub const Crc8ICode = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xfd, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Lte = Crc(u8, .{ - .polynomial = 0x9b, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8MaximDow = Crc(u8, .{ - .polynomial = 0x31, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8MifareMad = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xc7, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Nrsc5 = Crc(u8, .{ - .polynomial = 0x31, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Opensafety = Crc(u8, .{ - .polynomial = 0x2f, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Rohc = Crc(u8, .{ - .polynomial = 0x07, - .initial = 0xff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8SaeJ1850 = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xff, -}); - -pub const Crc8Smbus = Crc(u8, .{ - .polynomial = 0x07, - .initial = 0x00, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00, -}); - -pub const Crc8Tech3250 = Crc(u8, .{ - .polynomial = 0x1d, - .initial = 0xff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc8Wcdma = Crc(u8, .{ - .polynomial = 0x9b, - .initial = 0x00, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00, -}); - -pub const Crc10Atm = Crc(u10, .{ - .polynomial = 0x233, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc10Cdma2000 = Crc(u10, .{ - .polynomial = 0x3d9, - .initial = 0x3ff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc10Gsm = Crc(u10, .{ - .polynomial = 0x175, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3ff, -}); - -pub const Crc11Flexray = Crc(u11, .{ - .polynomial = 0x385, - .initial = 0x01a, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc11Umts = Crc(u11, .{ - .polynomial = 0x307, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc12Cdma2000 = Crc(u12, .{ - .polynomial = 0xf13, - .initial = 0xfff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc12Dect = Crc(u12, .{ - .polynomial = 0x80f, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000, -}); - -pub const Crc12Gsm = Crc(u12, .{ - .polynomial = 0xd31, - .initial = 0x000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xfff, -}); - -pub const Crc12Umts = Crc(u12, .{ - .polynomial = 0x80f, - .initial = 0x000, - .reflect_input = false, - .reflect_output = true, - .xor_output = 0x000, -}); - -pub const Crc13Bbc = Crc(u13, .{ - .polynomial = 0x1cf5, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc14Darc = Crc(u14, .{ - .polynomial = 0x0805, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc14Gsm = Crc(u14, .{ - .polynomial = 0x202d, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3fff, -}); - -pub const Crc15Can = Crc(u15, .{ - .polynomial = 0x4599, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc15Mpt1327 = Crc(u15, .{ - .polynomial = 0x6815, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0001, -}); - -pub const Crc16Arc = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Cdma2000 = Crc(u16, .{ - .polynomial = 0xc867, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Cms = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Dds110 = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x800d, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16DectR = Crc(u16, .{ - .polynomial = 0x0589, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0001, -}); - -pub const Crc16DectX = Crc(u16, .{ - .polynomial = 0x0589, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Dnp = Crc(u16, .{ - .polynomial = 0x3d65, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16En13757 = Crc(u16, .{ - .polynomial = 0x3d65, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Genibus = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Gsm = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Ibm3740 = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16IbmSdlc = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16IsoIec144433A = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xc6c6, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Kermit = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Lj1200 = Crc(u16, .{ - .polynomial = 0x6f63, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16M17 = Crc(u16, .{ - .polynomial = 0x5935, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16MaximDow = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x0000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16Mcrf4xx = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Modbus = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Nrsc5 = Crc(u16, .{ - .polynomial = 0x080b, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16OpensafetyA = Crc(u16, .{ - .polynomial = 0x5935, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16OpensafetyB = Crc(u16, .{ - .polynomial = 0x755b, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Profibus = Crc(u16, .{ - .polynomial = 0x1dcf, - .initial = 0xffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffff, -}); - -pub const Crc16Riello = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0xb2aa, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16SpiFujitsu = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x1d0f, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16T10Dif = Crc(u16, .{ - .polynomial = 0x8bb7, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Teledisk = Crc(u16, .{ - .polynomial = 0xa097, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Tms37157 = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x89ec, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000, -}); - -pub const Crc16Umts = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc16Usb = Crc(u16, .{ - .polynomial = 0x8005, - .initial = 0xffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffff, -}); - -pub const Crc16Xmodem = Crc(u16, .{ - .polynomial = 0x1021, - .initial = 0x0000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000, -}); - -pub const Crc17CanFd = Crc(u17, .{ - .polynomial = 0x1685b, - .initial = 0x00000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000, -}); - -pub const Crc21CanFd = Crc(u21, .{ - .polynomial = 0x102899, - .initial = 0x000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Ble = Crc(u24, .{ - .polynomial = 0x00065b, - .initial = 0x555555, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x000000, -}); - -pub const Crc24FlexrayA = Crc(u24, .{ - .polynomial = 0x5d6dcb, - .initial = 0xfedcba, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24FlexrayB = Crc(u24, .{ - .polynomial = 0x5d6dcb, - .initial = 0xabcdef, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Interlaken = Crc(u24, .{ - .polynomial = 0x328b63, - .initial = 0xffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffff, -}); - -pub const Crc24LteA = Crc(u24, .{ - .polynomial = 0x864cfb, - .initial = 0x000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24LteB = Crc(u24, .{ - .polynomial = 0x800063, - .initial = 0x000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Openpgp = Crc(u24, .{ - .polynomial = 0x864cfb, - .initial = 0xb704ce, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x000000, -}); - -pub const Crc24Os9 = Crc(u24, .{ - .polynomial = 0x800063, - .initial = 0xffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffff, -}); - -pub const Crc30Cdma = Crc(u30, .{ - .polynomial = 0x2030b9c7, - .initial = 0x3fffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x3fffffff, -}); - -pub const Crc31Philips = Crc(u31, .{ - .polynomial = 0x04c11db7, - .initial = 0x7fffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x7fffffff, -}); - -pub const Crc32Aixm = Crc(u32, .{ - .polynomial = 0x814141ab, - .initial = 0x00000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000000, -}); - -pub const Crc32Autosar = Crc(u32, .{ - .polynomial = 0xf4acfb13, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32Base91D = Crc(u32, .{ - .polynomial = 0xa833982b, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32Bzip2 = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffff, -}); - -pub const Crc32CdRomEdc = Crc(u32, .{ - .polynomial = 0x8001801b, - .initial = 0x00000000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00000000, -}); - -pub const Crc32Cksum = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0x00000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffff, -}); - -pub const Crc32Iscsi = Crc(u32, .{ - .polynomial = 0x1edc6f41, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32IsoHdlc = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffff, -}); - -pub const Crc32Jamcrc = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00000000, -}); - -pub const Crc32Mef = Crc(u32, .{ - .polynomial = 0x741b8cd7, - .initial = 0xffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x00000000, -}); - -pub const Crc32Mpeg2 = Crc(u32, .{ - .polynomial = 0x04c11db7, - .initial = 0xffffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000000, -}); - -pub const Crc32Xfer = Crc(u32, .{ - .polynomial = 0x000000af, - .initial = 0x00000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x00000000, -}); - -pub const Crc40Gsm = Crc(u40, .{ - .polynomial = 0x0004820009, - .initial = 0x0000000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffffff, -}); - -pub const Crc64Ecma182 = Crc(u64, .{ - .polynomial = 0x42f0e1eba9ea3693, - .initial = 0x0000000000000000, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0x0000000000000000, -}); - -pub const Crc64GoIso = Crc(u64, .{ - .polynomial = 0x000000000000001b, - .initial = 0xffffffffffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffffffffffff, -}); - -pub const Crc64Ms = Crc(u64, .{ - .polynomial = 0x259c84cba6426349, - .initial = 0xffffffffffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000000000000000, -}); - -pub const Crc64Redis = Crc(u64, .{ - .polynomial = 0xad93d23594c935a9, - .initial = 0x0000000000000000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x0000000000000000, -}); - -pub const Crc64We = Crc(u64, .{ - .polynomial = 0x42f0e1eba9ea3693, - .initial = 0xffffffffffffffff, - .reflect_input = false, - .reflect_output = false, - .xor_output = 0xffffffffffffffff, -}); - -pub const Crc64Xz = Crc(u64, .{ - .polynomial = 0x42f0e1eba9ea3693, - .initial = 0xffffffffffffffff, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0xffffffffffffffff, -}); - -pub const Crc82Darc = Crc(u82, .{ - .polynomial = 0x0308c0111011401440411, - .initial = 0x000000000000000000000, - .reflect_input = true, - .reflect_output = true, - .xor_output = 0x000000000000000000000, -}); diff --git a/lib/std/hash/crc/impl.zig b/lib/std/hash/crc/impl.zig new file mode 100644 index 0000000000..f0fd5ac14e --- /dev/null +++ b/lib/std/hash/crc/impl.zig @@ -0,0 +1,241 @@ +// There is a generic CRC implementation "Crc()" which can be paramterized via +// the Algorithm struct for a plethora of uses, along with two implementations +// of CRC32 implemented with the following key characteristics: +// +// - Crc32WithPoly uses 8Kb of tables but is ~10x faster than the small method. +// +// - Crc32SmallWithPoly uses only 64 bytes of memory but is slower. Be aware that this is +// still moderately fast just slow relative to the slicing approach. +// +// The primary interface for all of the standard CRC algorithms is the +// generated file "crc.zig", which uses the implementation code here to define +// many standard CRCs. + +const std = @import("std"); + +pub fn Algorithm(comptime W: type) type { + return struct { + polynomial: W, + initial: W, + reflect_input: bool, + reflect_output: bool, + xor_output: W, + }; +} + +pub fn Crc(comptime W: type, comptime algorithm: Algorithm(W)) type { + return struct { + const Self = @This(); + const I = if (@bitSizeOf(W) < 8) u8 else W; + const lookup_table = blk: { + @setEvalBranchQuota(2500); + + const poly = if (algorithm.reflect_input) + @bitReverse(@as(I, algorithm.polynomial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) + else + @as(I, algorithm.polynomial) << (@bitSizeOf(I) - @bitSizeOf(W)); + + var table: [256]I = undefined; + for (&table, 0..) |*e, i| { + var crc: I = i; + if (algorithm.reflect_input) { + var j: usize = 0; + while (j < 8) : (j += 1) { + crc = (crc >> 1) ^ ((crc & 1) * poly); + } + } else { + crc <<= @bitSizeOf(I) - 8; + var j: usize = 0; + while (j < 8) : (j += 1) { + crc = (crc << 1) ^ (((crc >> (@bitSizeOf(I) - 1)) & 1) * poly); + } + } + e.* = crc; + } + break :blk table; + }; + + crc: I, + + pub fn init() Self { + const initial = if (algorithm.reflect_input) + @bitReverse(@as(I, algorithm.initial)) >> (@bitSizeOf(I) - @bitSizeOf(W)) + else + @as(I, algorithm.initial) << (@bitSizeOf(I) - @bitSizeOf(W)); + return Self{ .crc = initial }; + } + + inline fn tableEntry(index: I) I { + return lookup_table[@as(u8, @intCast(index & 0xFF))]; + } + + pub fn update(self: *Self, bytes: []const u8) void { + var i: usize = 0; + if (@bitSizeOf(I) <= 8) { + while (i < bytes.len) : (i += 1) { + self.crc = tableEntry(self.crc ^ bytes[i]); + } + } else if (algorithm.reflect_input) { + while (i < bytes.len) : (i += 1) { + const table_index = self.crc ^ bytes[i]; + self.crc = tableEntry(table_index) ^ (self.crc >> 8); + } + } else { + while (i < bytes.len) : (i += 1) { + const table_index = (self.crc >> (@bitSizeOf(I) - 8)) ^ bytes[i]; + self.crc = tableEntry(table_index) ^ (self.crc << 8); + } + } + } + + pub fn final(self: Self) W { + var c = self.crc; + if (algorithm.reflect_input != algorithm.reflect_output) { + c = @bitReverse(c); + } + if (!algorithm.reflect_output) { + c >>= @bitSizeOf(I) - @bitSizeOf(W); + } + return @as(W, @intCast(c ^ algorithm.xor_output)); + } + + pub fn hash(bytes: []const u8) W { + var c = Self.init(); + c.update(bytes); + return c.final(); + } + }; +} + +pub const Polynomial = enum(u32) { + IEEE = 0xedb88320, + Castagnoli = 0x82f63b78, + Koopman = 0xeb31d82e, + _, +}; + +// slicing-by-8 crc32 implementation. +pub fn Crc32WithPoly(comptime poly: Polynomial) type { + return struct { + const Self = @This(); + const lookup_tables = block: { + @setEvalBranchQuota(20000); + var tables: [8][256]u32 = undefined; + + for (&tables[0], 0..) |*e, i| { + var crc = @as(u32, @intCast(i)); + var j: usize = 0; + while (j < 8) : (j += 1) { + if (crc & 1 == 1) { + crc = (crc >> 1) ^ @intFromEnum(poly); + } else { + crc = (crc >> 1); + } + } + e.* = crc; + } + + var i: usize = 0; + while (i < 256) : (i += 1) { + var crc = tables[0][i]; + var j: usize = 1; + while (j < 8) : (j += 1) { + const index: u8 = @truncate(crc); + crc = tables[0][index] ^ (crc >> 8); + tables[j][i] = crc; + } + } + + break :block tables; + }; + + crc: u32, + + pub fn init() Self { + return Self{ .crc = 0xffffffff }; + } + + pub fn update(self: *Self, input: []const u8) void { + var i: usize = 0; + while (i + 8 <= input.len) : (i += 8) { + const p = input[i..][0..8]; + + // Unrolling this way gives ~50Mb/s increase + self.crc ^= std.mem.readInt(u32, p[0..4], .little); + + self.crc = + lookup_tables[0][p[7]] ^ + lookup_tables[1][p[6]] ^ + lookup_tables[2][p[5]] ^ + lookup_tables[3][p[4]] ^ + lookup_tables[4][@as(u8, @truncate(self.crc >> 24))] ^ + lookup_tables[5][@as(u8, @truncate(self.crc >> 16))] ^ + lookup_tables[6][@as(u8, @truncate(self.crc >> 8))] ^ + lookup_tables[7][@as(u8, @truncate(self.crc >> 0))]; + } + + while (i < input.len) : (i += 1) { + const index = @as(u8, @truncate(self.crc)) ^ input[i]; + self.crc = (self.crc >> 8) ^ lookup_tables[0][index]; + } + } + + pub fn final(self: *Self) u32 { + return ~self.crc; + } + + pub fn hash(input: []const u8) u32 { + var c = Self.init(); + c.update(input); + return c.final(); + } + }; +} + +// half-byte lookup table implementation. +pub fn Crc32SmallWithPoly(comptime poly: Polynomial) type { + return struct { + const Self = @This(); + const lookup_table = block: { + var table: [16]u32 = undefined; + + for (&table, 0..) |*e, i| { + var crc = @as(u32, @intCast(i * 16)); + var j: usize = 0; + while (j < 8) : (j += 1) { + if (crc & 1 == 1) { + crc = (crc >> 1) ^ @intFromEnum(poly); + } else { + crc = (crc >> 1); + } + } + e.* = crc; + } + + break :block table; + }; + + crc: u32, + + pub fn init() Self { + return Self{ .crc = 0xffffffff }; + } + + pub fn update(self: *Self, input: []const u8) void { + for (input) |b| { + self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 0)))] ^ (self.crc >> 4); + self.crc = lookup_table[@as(u4, @truncate(self.crc ^ (b >> 4)))] ^ (self.crc >> 4); + } + } + + pub fn final(self: *Self) u32 { + return ~self.crc; + } + + pub fn hash(input: []const u8) u32 { + var c = Self.init(); + c.update(input); + return c.final(); + } + }; +} diff --git a/lib/std/hash/crc/catalog_test.zig b/lib/std/hash/crc/test.zig index ab89745ca8..c9cabf6463 100644 --- a/lib/std/hash/crc/catalog_test.zig +++ b/lib/std/hash/crc/test.zig @@ -2,10 +2,29 @@ const std = @import("std"); const testing = std.testing; -const catalog = @import("catalog.zig"); +const verify = @import("../verify.zig"); +const crc = @import("../crc.zig"); + +test "crc32 ieee" { + inline for ([2]type{ crc.Crc32WithPoly(.IEEE), crc.Crc32SmallWithPoly(.IEEE) }) |ieee| { + try testing.expect(ieee.hash("") == 0x00000000); + try testing.expect(ieee.hash("a") == 0xe8b7be43); + try testing.expect(ieee.hash("abc") == 0x352441c2); + try verify.iterativeApi(ieee); + } +} + +test "crc32 castagnoli" { + inline for ([2]type{ crc.Crc32WithPoly(.Castagnoli), crc.Crc32SmallWithPoly(.Castagnoli) }) |casta| { + try testing.expect(casta.hash("") == 0x00000000); + try testing.expect(casta.hash("a") == 0xc1d04330); + try testing.expect(casta.hash("abc") == 0x364b3fb7); + try verify.iterativeApi(casta); + } +} test "CRC-3/GSM" { - const Crc3Gsm = catalog.Crc3Gsm; + const Crc3Gsm = crc.Crc3Gsm; try testing.expectEqual(@as(u3, 0x4), Crc3Gsm.hash("123456789")); @@ -16,7 +35,7 @@ test "CRC-3/GSM" { } test "CRC-3/ROHC" { - const Crc3Rohc = catalog.Crc3Rohc; + const Crc3Rohc = crc.Crc3Rohc; try testing.expectEqual(@as(u3, 0x6), Crc3Rohc.hash("123456789")); @@ -27,7 +46,7 @@ test "CRC-3/ROHC" { } test "CRC-4/G-704" { - const Crc4G704 = catalog.Crc4G704; + const Crc4G704 = crc.Crc4G704; try testing.expectEqual(@as(u4, 0x7), Crc4G704.hash("123456789")); @@ -38,7 +57,7 @@ test "CRC-4/G-704" { } test "CRC-4/INTERLAKEN" { - const Crc4Interlaken = catalog.Crc4Interlaken; + const Crc4Interlaken = crc.Crc4Interlaken; try testing.expectEqual(@as(u4, 0xb), Crc4Interlaken.hash("123456789")); @@ -49,7 +68,7 @@ test "CRC-4/INTERLAKEN" { } test "CRC-5/EPC-C1G2" { - const Crc5EpcC1g2 = catalog.Crc5EpcC1g2; + const Crc5EpcC1g2 = crc.Crc5EpcC1g2; try testing.expectEqual(@as(u5, 0x00), Crc5EpcC1g2.hash("123456789")); @@ -60,7 +79,7 @@ test "CRC-5/EPC-C1G2" { } test "CRC-5/G-704" { - const Crc5G704 = catalog.Crc5G704; + const Crc5G704 = crc.Crc5G704; try testing.expectEqual(@as(u5, 0x07), Crc5G704.hash("123456789")); @@ -71,7 +90,7 @@ test "CRC-5/G-704" { } test "CRC-5/USB" { - const Crc5Usb = catalog.Crc5Usb; + const Crc5Usb = crc.Crc5Usb; try testing.expectEqual(@as(u5, 0x19), Crc5Usb.hash("123456789")); @@ -82,7 +101,7 @@ test "CRC-5/USB" { } test "CRC-6/CDMA2000-A" { - const Crc6Cdma2000A = catalog.Crc6Cdma2000A; + const Crc6Cdma2000A = crc.Crc6Cdma2000A; try testing.expectEqual(@as(u6, 0x0d), Crc6Cdma2000A.hash("123456789")); @@ -93,7 +112,7 @@ test "CRC-6/CDMA2000-A" { } test "CRC-6/CDMA2000-B" { - const Crc6Cdma2000B = catalog.Crc6Cdma2000B; + const Crc6Cdma2000B = crc.Crc6Cdma2000B; try testing.expectEqual(@as(u6, 0x3b), Crc6Cdma2000B.hash("123456789")); @@ -104,7 +123,7 @@ test "CRC-6/CDMA2000-B" { } test "CRC-6/DARC" { - const Crc6Darc = catalog.Crc6Darc; + const Crc6Darc = crc.Crc6Darc; try testing.expectEqual(@as(u6, 0x26), Crc6Darc.hash("123456789")); @@ -115,7 +134,7 @@ test "CRC-6/DARC" { } test "CRC-6/G-704" { - const Crc6G704 = catalog.Crc6G704; + const Crc6G704 = crc.Crc6G704; try testing.expectEqual(@as(u6, 0x06), Crc6G704.hash("123456789")); @@ -126,7 +145,7 @@ test "CRC-6/G-704" { } test "CRC-6/GSM" { - const Crc6Gsm = catalog.Crc6Gsm; + const Crc6Gsm = crc.Crc6Gsm; try testing.expectEqual(@as(u6, 0x13), Crc6Gsm.hash("123456789")); @@ -137,7 +156,7 @@ test "CRC-6/GSM" { } test "CRC-7/MMC" { - const Crc7Mmc = catalog.Crc7Mmc; + const Crc7Mmc = crc.Crc7Mmc; try testing.expectEqual(@as(u7, 0x75), Crc7Mmc.hash("123456789")); @@ -148,7 +167,7 @@ test "CRC-7/MMC" { } test "CRC-7/ROHC" { - const Crc7Rohc = catalog.Crc7Rohc; + const Crc7Rohc = crc.Crc7Rohc; try testing.expectEqual(@as(u7, 0x53), Crc7Rohc.hash("123456789")); @@ -159,7 +178,7 @@ test "CRC-7/ROHC" { } test "CRC-7/UMTS" { - const Crc7Umts = catalog.Crc7Umts; + const Crc7Umts = crc.Crc7Umts; try testing.expectEqual(@as(u7, 0x61), Crc7Umts.hash("123456789")); @@ -170,7 +189,7 @@ test "CRC-7/UMTS" { } test "CRC-8/AUTOSAR" { - const Crc8Autosar = catalog.Crc8Autosar; + const Crc8Autosar = crc.Crc8Autosar; try testing.expectEqual(@as(u8, 0xdf), Crc8Autosar.hash("123456789")); @@ -181,7 +200,7 @@ test "CRC-8/AUTOSAR" { } test "CRC-8/BLUETOOTH" { - const Crc8Bluetooth = catalog.Crc8Bluetooth; + const Crc8Bluetooth = crc.Crc8Bluetooth; try testing.expectEqual(@as(u8, 0x26), Crc8Bluetooth.hash("123456789")); @@ -192,7 +211,7 @@ test "CRC-8/BLUETOOTH" { } test "CRC-8/CDMA2000" { - const Crc8Cdma2000 = catalog.Crc8Cdma2000; + const Crc8Cdma2000 = crc.Crc8Cdma2000; try testing.expectEqual(@as(u8, 0xda), Crc8Cdma2000.hash("123456789")); @@ -203,7 +222,7 @@ test "CRC-8/CDMA2000" { } test "CRC-8/DARC" { - const Crc8Darc = catalog.Crc8Darc; + const Crc8Darc = crc.Crc8Darc; try testing.expectEqual(@as(u8, 0x15), Crc8Darc.hash("123456789")); @@ -214,7 +233,7 @@ test "CRC-8/DARC" { } test "CRC-8/DVB-S2" { - const Crc8DvbS2 = catalog.Crc8DvbS2; + const Crc8DvbS2 = crc.Crc8DvbS2; try testing.expectEqual(@as(u8, 0xbc), Crc8DvbS2.hash("123456789")); @@ -225,7 +244,7 @@ test "CRC-8/DVB-S2" { } test "CRC-8/GSM-A" { - const Crc8GsmA = catalog.Crc8GsmA; + const Crc8GsmA = crc.Crc8GsmA; try testing.expectEqual(@as(u8, 0x37), Crc8GsmA.hash("123456789")); @@ -236,7 +255,7 @@ test "CRC-8/GSM-A" { } test "CRC-8/GSM-B" { - const Crc8GsmB = catalog.Crc8GsmB; + const Crc8GsmB = crc.Crc8GsmB; try testing.expectEqual(@as(u8, 0x94), Crc8GsmB.hash("123456789")); @@ -247,7 +266,7 @@ test "CRC-8/GSM-B" { } test "CRC-8/HITAG" { - const Crc8Hitag = catalog.Crc8Hitag; + const Crc8Hitag = crc.Crc8Hitag; try testing.expectEqual(@as(u8, 0xb4), Crc8Hitag.hash("123456789")); @@ -258,7 +277,7 @@ test "CRC-8/HITAG" { } test "CRC-8/I-432-1" { - const Crc8I4321 = catalog.Crc8I4321; + const Crc8I4321 = crc.Crc8I4321; try testing.expectEqual(@as(u8, 0xa1), Crc8I4321.hash("123456789")); @@ -269,7 +288,7 @@ test "CRC-8/I-432-1" { } test "CRC-8/I-CODE" { - const Crc8ICode = catalog.Crc8ICode; + const Crc8ICode = crc.Crc8ICode; try testing.expectEqual(@as(u8, 0x7e), Crc8ICode.hash("123456789")); @@ -280,7 +299,7 @@ test "CRC-8/I-CODE" { } test "CRC-8/LTE" { - const Crc8Lte = catalog.Crc8Lte; + const Crc8Lte = crc.Crc8Lte; try testing.expectEqual(@as(u8, 0xea), Crc8Lte.hash("123456789")); @@ -291,7 +310,7 @@ test "CRC-8/LTE" { } test "CRC-8/MAXIM-DOW" { - const Crc8MaximDow = catalog.Crc8MaximDow; + const Crc8MaximDow = crc.Crc8MaximDow; try testing.expectEqual(@as(u8, 0xa1), Crc8MaximDow.hash("123456789")); @@ -302,7 +321,7 @@ test "CRC-8/MAXIM-DOW" { } test "CRC-8/MIFARE-MAD" { - const Crc8MifareMad = catalog.Crc8MifareMad; + const Crc8MifareMad = crc.Crc8MifareMad; try testing.expectEqual(@as(u8, 0x99), Crc8MifareMad.hash("123456789")); @@ -313,7 +332,7 @@ test "CRC-8/MIFARE-MAD" { } test "CRC-8/NRSC-5" { - const Crc8Nrsc5 = catalog.Crc8Nrsc5; + const Crc8Nrsc5 = crc.Crc8Nrsc5; try testing.expectEqual(@as(u8, 0xf7), Crc8Nrsc5.hash("123456789")); @@ -324,7 +343,7 @@ test "CRC-8/NRSC-5" { } test "CRC-8/OPENSAFETY" { - const Crc8Opensafety = catalog.Crc8Opensafety; + const Crc8Opensafety = crc.Crc8Opensafety; try testing.expectEqual(@as(u8, 0x3e), Crc8Opensafety.hash("123456789")); @@ -335,7 +354,7 @@ test "CRC-8/OPENSAFETY" { } test "CRC-8/ROHC" { - const Crc8Rohc = catalog.Crc8Rohc; + const Crc8Rohc = crc.Crc8Rohc; try testing.expectEqual(@as(u8, 0xd0), Crc8Rohc.hash("123456789")); @@ -346,7 +365,7 @@ test "CRC-8/ROHC" { } test "CRC-8/SAE-J1850" { - const Crc8SaeJ1850 = catalog.Crc8SaeJ1850; + const Crc8SaeJ1850 = crc.Crc8SaeJ1850; try testing.expectEqual(@as(u8, 0x4b), Crc8SaeJ1850.hash("123456789")); @@ -357,7 +376,7 @@ test "CRC-8/SAE-J1850" { } test "CRC-8/SMBUS" { - const Crc8Smbus = catalog.Crc8Smbus; + const Crc8Smbus = crc.Crc8Smbus; try testing.expectEqual(@as(u8, 0xf4), Crc8Smbus.hash("123456789")); @@ -368,7 +387,7 @@ test "CRC-8/SMBUS" { } test "CRC-8/TECH-3250" { - const Crc8Tech3250 = catalog.Crc8Tech3250; + const Crc8Tech3250 = crc.Crc8Tech3250; try testing.expectEqual(@as(u8, 0x97), Crc8Tech3250.hash("123456789")); @@ -379,7 +398,7 @@ test "CRC-8/TECH-3250" { } test "CRC-8/WCDMA" { - const Crc8Wcdma = catalog.Crc8Wcdma; + const Crc8Wcdma = crc.Crc8Wcdma; try testing.expectEqual(@as(u8, 0x25), Crc8Wcdma.hash("123456789")); @@ -390,7 +409,7 @@ test "CRC-8/WCDMA" { } test "CRC-10/ATM" { - const Crc10Atm = catalog.Crc10Atm; + const Crc10Atm = crc.Crc10Atm; try testing.expectEqual(@as(u10, 0x199), Crc10Atm.hash("123456789")); @@ -401,7 +420,7 @@ test "CRC-10/ATM" { } test "CRC-10/CDMA2000" { - const Crc10Cdma2000 = catalog.Crc10Cdma2000; + const Crc10Cdma2000 = crc.Crc10Cdma2000; try testing.expectEqual(@as(u10, 0x233), Crc10Cdma2000.hash("123456789")); @@ -412,7 +431,7 @@ test "CRC-10/CDMA2000" { } test "CRC-10/GSM" { - const Crc10Gsm = catalog.Crc10Gsm; + const Crc10Gsm = crc.Crc10Gsm; try testing.expectEqual(@as(u10, 0x12a), Crc10Gsm.hash("123456789")); @@ -423,7 +442,7 @@ test "CRC-10/GSM" { } test "CRC-11/FLEXRAY" { - const Crc11Flexray = catalog.Crc11Flexray; + const Crc11Flexray = crc.Crc11Flexray; try testing.expectEqual(@as(u11, 0x5a3), Crc11Flexray.hash("123456789")); @@ -434,7 +453,7 @@ test "CRC-11/FLEXRAY" { } test "CRC-11/UMTS" { - const Crc11Umts = catalog.Crc11Umts; + const Crc11Umts = crc.Crc11Umts; try testing.expectEqual(@as(u11, 0x061), Crc11Umts.hash("123456789")); @@ -445,7 +464,7 @@ test "CRC-11/UMTS" { } test "CRC-12/CDMA2000" { - const Crc12Cdma2000 = catalog.Crc12Cdma2000; + const Crc12Cdma2000 = crc.Crc12Cdma2000; try testing.expectEqual(@as(u12, 0xd4d), Crc12Cdma2000.hash("123456789")); @@ -456,7 +475,7 @@ test "CRC-12/CDMA2000" { } test "CRC-12/DECT" { - const Crc12Dect = catalog.Crc12Dect; + const Crc12Dect = crc.Crc12Dect; try testing.expectEqual(@as(u12, 0xf5b), Crc12Dect.hash("123456789")); @@ -467,7 +486,7 @@ test "CRC-12/DECT" { } test "CRC-12/GSM" { - const Crc12Gsm = catalog.Crc12Gsm; + const Crc12Gsm = crc.Crc12Gsm; try testing.expectEqual(@as(u12, 0xb34), Crc12Gsm.hash("123456789")); @@ -478,7 +497,7 @@ test "CRC-12/GSM" { } test "CRC-12/UMTS" { - const Crc12Umts = catalog.Crc12Umts; + const Crc12Umts = crc.Crc12Umts; try testing.expectEqual(@as(u12, 0xdaf), Crc12Umts.hash("123456789")); @@ -489,7 +508,7 @@ test "CRC-12/UMTS" { } test "CRC-13/BBC" { - const Crc13Bbc = catalog.Crc13Bbc; + const Crc13Bbc = crc.Crc13Bbc; try testing.expectEqual(@as(u13, 0x04fa), Crc13Bbc.hash("123456789")); @@ -500,7 +519,7 @@ test "CRC-13/BBC" { } test "CRC-14/DARC" { - const Crc14Darc = catalog.Crc14Darc; + const Crc14Darc = crc.Crc14Darc; try testing.expectEqual(@as(u14, 0x082d), Crc14Darc.hash("123456789")); @@ -511,7 +530,7 @@ test "CRC-14/DARC" { } test "CRC-14/GSM" { - const Crc14Gsm = catalog.Crc14Gsm; + const Crc14Gsm = crc.Crc14Gsm; try testing.expectEqual(@as(u14, 0x30ae), Crc14Gsm.hash("123456789")); @@ -522,7 +541,7 @@ test "CRC-14/GSM" { } test "CRC-15/CAN" { - const Crc15Can = catalog.Crc15Can; + const Crc15Can = crc.Crc15Can; try testing.expectEqual(@as(u15, 0x059e), Crc15Can.hash("123456789")); @@ -533,7 +552,7 @@ test "CRC-15/CAN" { } test "CRC-15/MPT1327" { - const Crc15Mpt1327 = catalog.Crc15Mpt1327; + const Crc15Mpt1327 = crc.Crc15Mpt1327; try testing.expectEqual(@as(u15, 0x2566), Crc15Mpt1327.hash("123456789")); @@ -544,7 +563,7 @@ test "CRC-15/MPT1327" { } test "CRC-16/ARC" { - const Crc16Arc = catalog.Crc16Arc; + const Crc16Arc = crc.Crc16Arc; try testing.expectEqual(@as(u16, 0xbb3d), Crc16Arc.hash("123456789")); @@ -555,7 +574,7 @@ test "CRC-16/ARC" { } test "CRC-16/CDMA2000" { - const Crc16Cdma2000 = catalog.Crc16Cdma2000; + const Crc16Cdma2000 = crc.Crc16Cdma2000; try testing.expectEqual(@as(u16, 0x4c06), Crc16Cdma2000.hash("123456789")); @@ -566,7 +585,7 @@ test "CRC-16/CDMA2000" { } test "CRC-16/CMS" { - const Crc16Cms = catalog.Crc16Cms; + const Crc16Cms = crc.Crc16Cms; try testing.expectEqual(@as(u16, 0xaee7), Crc16Cms.hash("123456789")); @@ -577,7 +596,7 @@ test "CRC-16/CMS" { } test "CRC-16/DDS-110" { - const Crc16Dds110 = catalog.Crc16Dds110; + const Crc16Dds110 = crc.Crc16Dds110; try testing.expectEqual(@as(u16, 0x9ecf), Crc16Dds110.hash("123456789")); @@ -588,7 +607,7 @@ test "CRC-16/DDS-110" { } test "CRC-16/DECT-R" { - const Crc16DectR = catalog.Crc16DectR; + const Crc16DectR = crc.Crc16DectR; try testing.expectEqual(@as(u16, 0x007e), Crc16DectR.hash("123456789")); @@ -599,7 +618,7 @@ test "CRC-16/DECT-R" { } test "CRC-16/DECT-X" { - const Crc16DectX = catalog.Crc16DectX; + const Crc16DectX = crc.Crc16DectX; try testing.expectEqual(@as(u16, 0x007f), Crc16DectX.hash("123456789")); @@ -610,7 +629,7 @@ test "CRC-16/DECT-X" { } test "CRC-16/DNP" { - const Crc16Dnp = catalog.Crc16Dnp; + const Crc16Dnp = crc.Crc16Dnp; try testing.expectEqual(@as(u16, 0xea82), Crc16Dnp.hash("123456789")); @@ -621,7 +640,7 @@ test "CRC-16/DNP" { } test "CRC-16/EN-13757" { - const Crc16En13757 = catalog.Crc16En13757; + const Crc16En13757 = crc.Crc16En13757; try testing.expectEqual(@as(u16, 0xc2b7), Crc16En13757.hash("123456789")); @@ -632,7 +651,7 @@ test "CRC-16/EN-13757" { } test "CRC-16/GENIBUS" { - const Crc16Genibus = catalog.Crc16Genibus; + const Crc16Genibus = crc.Crc16Genibus; try testing.expectEqual(@as(u16, 0xd64e), Crc16Genibus.hash("123456789")); @@ -643,7 +662,7 @@ test "CRC-16/GENIBUS" { } test "CRC-16/GSM" { - const Crc16Gsm = catalog.Crc16Gsm; + const Crc16Gsm = crc.Crc16Gsm; try testing.expectEqual(@as(u16, 0xce3c), Crc16Gsm.hash("123456789")); @@ -654,7 +673,7 @@ test "CRC-16/GSM" { } test "CRC-16/IBM-3740" { - const Crc16Ibm3740 = catalog.Crc16Ibm3740; + const Crc16Ibm3740 = crc.Crc16Ibm3740; try testing.expectEqual(@as(u16, 0x29b1), Crc16Ibm3740.hash("123456789")); @@ -665,7 +684,7 @@ test "CRC-16/IBM-3740" { } test "CRC-16/IBM-SDLC" { - const Crc16IbmSdlc = catalog.Crc16IbmSdlc; + const Crc16IbmSdlc = crc.Crc16IbmSdlc; try testing.expectEqual(@as(u16, 0x906e), Crc16IbmSdlc.hash("123456789")); @@ -676,7 +695,7 @@ test "CRC-16/IBM-SDLC" { } test "CRC-16/ISO-IEC-14443-3-A" { - const Crc16IsoIec144433A = catalog.Crc16IsoIec144433A; + const Crc16IsoIec144433A = crc.Crc16IsoIec144433A; try testing.expectEqual(@as(u16, 0xbf05), Crc16IsoIec144433A.hash("123456789")); @@ -687,7 +706,7 @@ test "CRC-16/ISO-IEC-14443-3-A" { } test "CRC-16/KERMIT" { - const Crc16Kermit = catalog.Crc16Kermit; + const Crc16Kermit = crc.Crc16Kermit; try testing.expectEqual(@as(u16, 0x2189), Crc16Kermit.hash("123456789")); @@ -698,7 +717,7 @@ test "CRC-16/KERMIT" { } test "CRC-16/LJ1200" { - const Crc16Lj1200 = catalog.Crc16Lj1200; + const Crc16Lj1200 = crc.Crc16Lj1200; try testing.expectEqual(@as(u16, 0xbdf4), Crc16Lj1200.hash("123456789")); @@ -709,7 +728,7 @@ test "CRC-16/LJ1200" { } test "CRC-16/M17" { - const Crc16M17 = catalog.Crc16M17; + const Crc16M17 = crc.Crc16M17; try testing.expectEqual(@as(u16, 0x772b), Crc16M17.hash("123456789")); @@ -720,7 +739,7 @@ test "CRC-16/M17" { } test "CRC-16/MAXIM-DOW" { - const Crc16MaximDow = catalog.Crc16MaximDow; + const Crc16MaximDow = crc.Crc16MaximDow; try testing.expectEqual(@as(u16, 0x44c2), Crc16MaximDow.hash("123456789")); @@ -731,7 +750,7 @@ test "CRC-16/MAXIM-DOW" { } test "CRC-16/MCRF4XX" { - const Crc16Mcrf4xx = catalog.Crc16Mcrf4xx; + const Crc16Mcrf4xx = crc.Crc16Mcrf4xx; try testing.expectEqual(@as(u16, 0x6f91), Crc16Mcrf4xx.hash("123456789")); @@ -742,7 +761,7 @@ test "CRC-16/MCRF4XX" { } test "CRC-16/MODBUS" { - const Crc16Modbus = catalog.Crc16Modbus; + const Crc16Modbus = crc.Crc16Modbus; try testing.expectEqual(@as(u16, 0x4b37), Crc16Modbus.hash("123456789")); @@ -753,7 +772,7 @@ test "CRC-16/MODBUS" { } test "CRC-16/NRSC-5" { - const Crc16Nrsc5 = catalog.Crc16Nrsc5; + const Crc16Nrsc5 = crc.Crc16Nrsc5; try testing.expectEqual(@as(u16, 0xa066), Crc16Nrsc5.hash("123456789")); @@ -764,7 +783,7 @@ test "CRC-16/NRSC-5" { } test "CRC-16/OPENSAFETY-A" { - const Crc16OpensafetyA = catalog.Crc16OpensafetyA; + const Crc16OpensafetyA = crc.Crc16OpensafetyA; try testing.expectEqual(@as(u16, 0x5d38), Crc16OpensafetyA.hash("123456789")); @@ -775,7 +794,7 @@ test "CRC-16/OPENSAFETY-A" { } test "CRC-16/OPENSAFETY-B" { - const Crc16OpensafetyB = catalog.Crc16OpensafetyB; + const Crc16OpensafetyB = crc.Crc16OpensafetyB; try testing.expectEqual(@as(u16, 0x20fe), Crc16OpensafetyB.hash("123456789")); @@ -786,7 +805,7 @@ test "CRC-16/OPENSAFETY-B" { } test "CRC-16/PROFIBUS" { - const Crc16Profibus = catalog.Crc16Profibus; + const Crc16Profibus = crc.Crc16Profibus; try testing.expectEqual(@as(u16, 0xa819), Crc16Profibus.hash("123456789")); @@ -797,7 +816,7 @@ test "CRC-16/PROFIBUS" { } test "CRC-16/RIELLO" { - const Crc16Riello = catalog.Crc16Riello; + const Crc16Riello = crc.Crc16Riello; try testing.expectEqual(@as(u16, 0x63d0), Crc16Riello.hash("123456789")); @@ -808,7 +827,7 @@ test "CRC-16/RIELLO" { } test "CRC-16/SPI-FUJITSU" { - const Crc16SpiFujitsu = catalog.Crc16SpiFujitsu; + const Crc16SpiFujitsu = crc.Crc16SpiFujitsu; try testing.expectEqual(@as(u16, 0xe5cc), Crc16SpiFujitsu.hash("123456789")); @@ -819,7 +838,7 @@ test "CRC-16/SPI-FUJITSU" { } test "CRC-16/T10-DIF" { - const Crc16T10Dif = catalog.Crc16T10Dif; + const Crc16T10Dif = crc.Crc16T10Dif; try testing.expectEqual(@as(u16, 0xd0db), Crc16T10Dif.hash("123456789")); @@ -830,7 +849,7 @@ test "CRC-16/T10-DIF" { } test "CRC-16/TELEDISK" { - const Crc16Teledisk = catalog.Crc16Teledisk; + const Crc16Teledisk = crc.Crc16Teledisk; try testing.expectEqual(@as(u16, 0x0fb3), Crc16Teledisk.hash("123456789")); @@ -841,7 +860,7 @@ test "CRC-16/TELEDISK" { } test "CRC-16/TMS37157" { - const Crc16Tms37157 = catalog.Crc16Tms37157; + const Crc16Tms37157 = crc.Crc16Tms37157; try testing.expectEqual(@as(u16, 0x26b1), Crc16Tms37157.hash("123456789")); @@ -852,7 +871,7 @@ test "CRC-16/TMS37157" { } test "CRC-16/UMTS" { - const Crc16Umts = catalog.Crc16Umts; + const Crc16Umts = crc.Crc16Umts; try testing.expectEqual(@as(u16, 0xfee8), Crc16Umts.hash("123456789")); @@ -863,7 +882,7 @@ test "CRC-16/UMTS" { } test "CRC-16/USB" { - const Crc16Usb = catalog.Crc16Usb; + const Crc16Usb = crc.Crc16Usb; try testing.expectEqual(@as(u16, 0xb4c8), Crc16Usb.hash("123456789")); @@ -874,7 +893,7 @@ test "CRC-16/USB" { } test "CRC-16/XMODEM" { - const Crc16Xmodem = catalog.Crc16Xmodem; + const Crc16Xmodem = crc.Crc16Xmodem; try testing.expectEqual(@as(u16, 0x31c3), Crc16Xmodem.hash("123456789")); @@ -885,7 +904,7 @@ test "CRC-16/XMODEM" { } test "CRC-17/CAN-FD" { - const Crc17CanFd = catalog.Crc17CanFd; + const Crc17CanFd = crc.Crc17CanFd; try testing.expectEqual(@as(u17, 0x04f03), Crc17CanFd.hash("123456789")); @@ -896,7 +915,7 @@ test "CRC-17/CAN-FD" { } test "CRC-21/CAN-FD" { - const Crc21CanFd = catalog.Crc21CanFd; + const Crc21CanFd = crc.Crc21CanFd; try testing.expectEqual(@as(u21, 0x0ed841), Crc21CanFd.hash("123456789")); @@ -907,7 +926,7 @@ test "CRC-21/CAN-FD" { } test "CRC-24/BLE" { - const Crc24Ble = catalog.Crc24Ble; + const Crc24Ble = crc.Crc24Ble; try testing.expectEqual(@as(u24, 0xc25a56), Crc24Ble.hash("123456789")); @@ -918,7 +937,7 @@ test "CRC-24/BLE" { } test "CRC-24/FLEXRAY-A" { - const Crc24FlexrayA = catalog.Crc24FlexrayA; + const Crc24FlexrayA = crc.Crc24FlexrayA; try testing.expectEqual(@as(u24, 0x7979bd), Crc24FlexrayA.hash("123456789")); @@ -929,7 +948,7 @@ test "CRC-24/FLEXRAY-A" { } test "CRC-24/FLEXRAY-B" { - const Crc24FlexrayB = catalog.Crc24FlexrayB; + const Crc24FlexrayB = crc.Crc24FlexrayB; try testing.expectEqual(@as(u24, 0x1f23b8), Crc24FlexrayB.hash("123456789")); @@ -940,7 +959,7 @@ test "CRC-24/FLEXRAY-B" { } test "CRC-24/INTERLAKEN" { - const Crc24Interlaken = catalog.Crc24Interlaken; + const Crc24Interlaken = crc.Crc24Interlaken; try testing.expectEqual(@as(u24, 0xb4f3e6), Crc24Interlaken.hash("123456789")); @@ -951,7 +970,7 @@ test "CRC-24/INTERLAKEN" { } test "CRC-24/LTE-A" { - const Crc24LteA = catalog.Crc24LteA; + const Crc24LteA = crc.Crc24LteA; try testing.expectEqual(@as(u24, 0xcde703), Crc24LteA.hash("123456789")); @@ -962,7 +981,7 @@ test "CRC-24/LTE-A" { } test "CRC-24/LTE-B" { - const Crc24LteB = catalog.Crc24LteB; + const Crc24LteB = crc.Crc24LteB; try testing.expectEqual(@as(u24, 0x23ef52), Crc24LteB.hash("123456789")); @@ -973,7 +992,7 @@ test "CRC-24/LTE-B" { } test "CRC-24/OPENPGP" { - const Crc24Openpgp = catalog.Crc24Openpgp; + const Crc24Openpgp = crc.Crc24Openpgp; try testing.expectEqual(@as(u24, 0x21cf02), Crc24Openpgp.hash("123456789")); @@ -984,7 +1003,7 @@ test "CRC-24/OPENPGP" { } test "CRC-24/OS-9" { - const Crc24Os9 = catalog.Crc24Os9; + const Crc24Os9 = crc.Crc24Os9; try testing.expectEqual(@as(u24, 0x200fa5), Crc24Os9.hash("123456789")); @@ -995,7 +1014,7 @@ test "CRC-24/OS-9" { } test "CRC-30/CDMA" { - const Crc30Cdma = catalog.Crc30Cdma; + const Crc30Cdma = crc.Crc30Cdma; try testing.expectEqual(@as(u30, 0x04c34abf), Crc30Cdma.hash("123456789")); @@ -1006,7 +1025,7 @@ test "CRC-30/CDMA" { } test "CRC-31/PHILIPS" { - const Crc31Philips = catalog.Crc31Philips; + const Crc31Philips = crc.Crc31Philips; try testing.expectEqual(@as(u31, 0x0ce9e46c), Crc31Philips.hash("123456789")); @@ -1017,7 +1036,7 @@ test "CRC-31/PHILIPS" { } test "CRC-32/AIXM" { - const Crc32Aixm = catalog.Crc32Aixm; + const Crc32Aixm = crc.Crc32Aixm; try testing.expectEqual(@as(u32, 0x3010bf7f), Crc32Aixm.hash("123456789")); @@ -1028,7 +1047,7 @@ test "CRC-32/AIXM" { } test "CRC-32/AUTOSAR" { - const Crc32Autosar = catalog.Crc32Autosar; + const Crc32Autosar = crc.Crc32Autosar; try testing.expectEqual(@as(u32, 0x1697d06a), Crc32Autosar.hash("123456789")); @@ -1039,7 +1058,7 @@ test "CRC-32/AUTOSAR" { } test "CRC-32/BASE91-D" { - const Crc32Base91D = catalog.Crc32Base91D; + const Crc32Base91D = crc.Crc32Base91D; try testing.expectEqual(@as(u32, 0x87315576), Crc32Base91D.hash("123456789")); @@ -1050,7 +1069,7 @@ test "CRC-32/BASE91-D" { } test "CRC-32/BZIP2" { - const Crc32Bzip2 = catalog.Crc32Bzip2; + const Crc32Bzip2 = crc.Crc32Bzip2; try testing.expectEqual(@as(u32, 0xfc891918), Crc32Bzip2.hash("123456789")); @@ -1061,7 +1080,7 @@ test "CRC-32/BZIP2" { } test "CRC-32/CD-ROM-EDC" { - const Crc32CdRomEdc = catalog.Crc32CdRomEdc; + const Crc32CdRomEdc = crc.Crc32CdRomEdc; try testing.expectEqual(@as(u32, 0x6ec2edc4), Crc32CdRomEdc.hash("123456789")); @@ -1072,7 +1091,7 @@ test "CRC-32/CD-ROM-EDC" { } test "CRC-32/CKSUM" { - const Crc32Cksum = catalog.Crc32Cksum; + const Crc32Cksum = crc.Crc32Cksum; try testing.expectEqual(@as(u32, 0x765e7680), Crc32Cksum.hash("123456789")); @@ -1083,7 +1102,7 @@ test "CRC-32/CKSUM" { } test "CRC-32/ISCSI" { - const Crc32Iscsi = catalog.Crc32Iscsi; + const Crc32Iscsi = crc.Crc32Iscsi; try testing.expectEqual(@as(u32, 0xe3069283), Crc32Iscsi.hash("123456789")); @@ -1094,7 +1113,7 @@ test "CRC-32/ISCSI" { } test "CRC-32/ISO-HDLC" { - const Crc32IsoHdlc = catalog.Crc32IsoHdlc; + const Crc32IsoHdlc = crc.Crc32IsoHdlc; try testing.expectEqual(@as(u32, 0xcbf43926), Crc32IsoHdlc.hash("123456789")); @@ -1105,7 +1124,7 @@ test "CRC-32/ISO-HDLC" { } test "CRC-32/JAMCRC" { - const Crc32Jamcrc = catalog.Crc32Jamcrc; + const Crc32Jamcrc = crc.Crc32Jamcrc; try testing.expectEqual(@as(u32, 0x340bc6d9), Crc32Jamcrc.hash("123456789")); @@ -1116,7 +1135,7 @@ test "CRC-32/JAMCRC" { } test "CRC-32/MEF" { - const Crc32Mef = catalog.Crc32Mef; + const Crc32Mef = crc.Crc32Mef; try testing.expectEqual(@as(u32, 0xd2c22f51), Crc32Mef.hash("123456789")); @@ -1127,7 +1146,7 @@ test "CRC-32/MEF" { } test "CRC-32/MPEG-2" { - const Crc32Mpeg2 = catalog.Crc32Mpeg2; + const Crc32Mpeg2 = crc.Crc32Mpeg2; try testing.expectEqual(@as(u32, 0x0376e6e7), Crc32Mpeg2.hash("123456789")); @@ -1138,7 +1157,7 @@ test "CRC-32/MPEG-2" { } test "CRC-32/XFER" { - const Crc32Xfer = catalog.Crc32Xfer; + const Crc32Xfer = crc.Crc32Xfer; try testing.expectEqual(@as(u32, 0xbd0be338), Crc32Xfer.hash("123456789")); @@ -1149,7 +1168,7 @@ test "CRC-32/XFER" { } test "CRC-40/GSM" { - const Crc40Gsm = catalog.Crc40Gsm; + const Crc40Gsm = crc.Crc40Gsm; try testing.expectEqual(@as(u40, 0xd4164fc646), Crc40Gsm.hash("123456789")); @@ -1160,7 +1179,7 @@ test "CRC-40/GSM" { } test "CRC-64/ECMA-182" { - const Crc64Ecma182 = catalog.Crc64Ecma182; + const Crc64Ecma182 = crc.Crc64Ecma182; try testing.expectEqual(@as(u64, 0x6c40df5f0b497347), Crc64Ecma182.hash("123456789")); @@ -1171,7 +1190,7 @@ test "CRC-64/ECMA-182" { } test "CRC-64/GO-ISO" { - const Crc64GoIso = catalog.Crc64GoIso; + const Crc64GoIso = crc.Crc64GoIso; try testing.expectEqual(@as(u64, 0xb90956c775a41001), Crc64GoIso.hash("123456789")); @@ -1182,7 +1201,7 @@ test "CRC-64/GO-ISO" { } test "CRC-64/MS" { - const Crc64Ms = catalog.Crc64Ms; + const Crc64Ms = crc.Crc64Ms; try testing.expectEqual(@as(u64, 0x75d4b74f024eceea), Crc64Ms.hash("123456789")); @@ -1193,7 +1212,7 @@ test "CRC-64/MS" { } test "CRC-64/REDIS" { - const Crc64Redis = catalog.Crc64Redis; + const Crc64Redis = crc.Crc64Redis; try testing.expectEqual(@as(u64, 0xe9c6d914c4b8d9ca), Crc64Redis.hash("123456789")); @@ -1204,7 +1223,7 @@ test "CRC-64/REDIS" { } test "CRC-64/WE" { - const Crc64We = catalog.Crc64We; + const Crc64We = crc.Crc64We; try testing.expectEqual(@as(u64, 0x62ec59e3f1a4f00a), Crc64We.hash("123456789")); @@ -1215,7 +1234,7 @@ test "CRC-64/WE" { } test "CRC-64/XZ" { - const Crc64Xz = catalog.Crc64Xz; + const Crc64Xz = crc.Crc64Xz; try testing.expectEqual(@as(u64, 0x995dc9bbdf1939fa), Crc64Xz.hash("123456789")); @@ -1226,7 +1245,7 @@ test "CRC-64/XZ" { } test "CRC-82/DARC" { - const Crc82Darc = catalog.Crc82Darc; + const Crc82Darc = crc.Crc82Darc; try testing.expectEqual(@as(u82, 0x09ea83f625023801fd612), Crc82Darc.hash("123456789")); diff --git a/lib/std/http/Client.zig b/lib/std/http/Client.zig index 2cec73f281..837bdc63c7 100644 --- a/lib/std/http/Client.zig +++ b/lib/std/http/Client.zig @@ -771,17 +771,41 @@ pub const Request = struct { req.client.connection_pool.release(req.client.allocator, req.connection.?); req.connection = null; - const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme; + var server_header = std.heap.FixedBufferAllocator.init(req.response.parser.header_bytes_buffer); + defer req.response.parser.header_bytes_buffer = server_header.buffer[server_header.end_index..]; + const protocol, const valid_uri = try validateUri(uri, server_header.allocator()); + + const new_host = valid_uri.host.?.raw; + const prev_host = req.uri.host.?.raw; + const keep_privileged_headers = + std.ascii.eqlIgnoreCase(valid_uri.scheme, req.uri.scheme) and + std.ascii.endsWithIgnoreCase(new_host, prev_host) and + (new_host.len == prev_host.len or new_host[new_host.len - prev_host.len - 1] == '.'); + if (!keep_privileged_headers) { + // When redirecting to a different domain, strip privileged headers. + req.privileged_headers = &.{}; + } - const port: u16 = uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }; + if (switch (req.response.status) { + .see_other => true, + .moved_permanently, .found => req.method == .POST, + else => false, + }) { + // A redirect to a GET must change the method and remove the body. + req.method = .GET; + req.transfer_encoding = .none; + req.headers.content_type = .omit; + } - const host = uri.host orelse return error.UriMissingHost; + if (req.transfer_encoding != .none) { + // The request body has already been sent. The request is + // still in a valid state, but the redirect must be handled + // manually. + return error.RedirectRequiresResend; + } - req.uri = uri; - req.connection = try req.client.connect(host, port, protocol); + req.uri = valid_uri; + req.connection = try req.client.connect(new_host, uriPort(valid_uri, protocol), protocol); req.redirect_behavior.subtractOne(); req.response.parser.reset(); @@ -796,13 +820,8 @@ pub const Request = struct { pub const SendError = Connection.WriteError || error{ InvalidContentLength, UnsupportedTransferEncoding }; - pub const SendOptions = struct { - /// Specifies that the uri is already escaped. - raw_uri: bool = false, - }; - /// Send the HTTP request headers to the server. - pub fn send(req: *Request, options: SendOptions) SendError!void { + pub fn send(req: *Request) SendError!void { if (!req.method.requestHasBody() and req.transfer_encoding != .none) return error.UnsupportedTransferEncoding; @@ -821,7 +840,6 @@ pub const Request = struct { .authority = connection.proxied, .path = true, .query = true, - .raw = options.raw_uri, }, w); } try w.writeByte(' '); @@ -1038,55 +1056,19 @@ pub const Request = struct { const location = req.response.location orelse return error.HttpRedirectLocationMissing; - // This mutates the beginning of header_buffer and uses that - // for the backing memory of the returned new_uri. - const header_buffer = req.response.parser.header_bytes_buffer; - const new_uri = req.uri.resolve_inplace(location, header_buffer) catch - return error.HttpRedirectLocationInvalid; - - // The new URI references the beginning of header_bytes_buffer memory. - // That memory will be kept, but everything after it will be - // reused by the subsequent request. In other words, - // header_bytes_buffer must be large enough to store all - // redirect locations as well as the final request header. - const path_end = new_uri.path.ptr + new_uri.path.len; - // https://github.com/ziglang/zig/issues/1738 - const path_offset = @intFromPtr(path_end) - @intFromPtr(header_buffer.ptr); - const end_offset = @max(path_offset, location.len); - req.response.parser.header_bytes_buffer = header_buffer[end_offset..]; - - const is_same_domain_or_subdomain = - std.ascii.endsWithIgnoreCase(new_uri.host.?, req.uri.host.?) and - (new_uri.host.?.len == req.uri.host.?.len or - new_uri.host.?[new_uri.host.?.len - req.uri.host.?.len - 1] == '.'); - - if (new_uri.host == null or !is_same_domain_or_subdomain or - !std.ascii.eqlIgnoreCase(new_uri.scheme, req.uri.scheme)) - { - // When redirecting to a different domain, strip privileged headers. - req.privileged_headers = &.{}; - } - - if (switch (req.response.status) { - .see_other => true, - .moved_permanently, .found => req.method == .POST, - else => false, - }) { - // A redirect to a GET must change the method and remove the body. - req.method = .GET; - req.transfer_encoding = .none; - req.headers.content_type = .omit; - } - - if (req.transfer_encoding != .none) { - // The request body has already been sent. The request is - // still in a valid state, but the redirect must be handled - // manually. - return error.RedirectRequiresResend; - } - - try req.redirect(new_uri); - try req.send(.{}); + // This mutates the beginning of header_bytes_buffer and uses that + // for the backing memory of the returned Uri. + try req.redirect(req.uri.resolve_inplace( + location, + &req.response.parser.header_bytes_buffer, + ) catch |err| switch (err) { + error.UnexpectedCharacter, + error.InvalidFormat, + error.InvalidPort, + => return error.HttpRedirectLocationInvalid, + error.NoSpaceLeft => return error.HttpHeadersOversize, + }); + try req.send(); } else { req.response.skip = false; if (!req.response.parser.done) { @@ -1264,30 +1246,25 @@ fn createProxyFromEnvVar(arena: Allocator, env_var_names: []const []const u8) !? }; } else return null; - const uri = Uri.parse(content) catch try Uri.parseWithoutScheme(content); - - const protocol = if (uri.scheme.len == 0) - .plain // No scheme, assume http:// - else - protocol_map.get(uri.scheme) orelse return null; // Unknown scheme, ignore - - const host = uri.host orelse return error.HttpProxyMissingHost; + const uri = Uri.parse(content) catch try Uri.parseAfterScheme("http", content); + const protocol, const valid_uri = validateUri(uri, arena) catch |err| switch (err) { + error.UnsupportedUriScheme => return null, + error.UriMissingHost => return error.HttpProxyMissingHost, + error.OutOfMemory => |e| return e, + }; - const authorization: ?[]const u8 = if (uri.user != null or uri.password != null) a: { - const authorization = try arena.alloc(u8, basic_authorization.valueLengthFromUri(uri)); - assert(basic_authorization.value(uri, authorization).len == authorization.len); + const authorization: ?[]const u8 = if (valid_uri.user != null or valid_uri.password != null) a: { + const authorization = try arena.alloc(u8, basic_authorization.valueLengthFromUri(valid_uri)); + assert(basic_authorization.value(valid_uri, authorization).len == authorization.len); break :a authorization; } else null; const proxy = try arena.create(Proxy); proxy.* = .{ .protocol = protocol, - .host = host, + .host = valid_uri.host.?.raw, .authorization = authorization, - .port = uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }, + .port = uriPort(valid_uri, protocol), .supports_connect = true, }; return proxy; @@ -1305,24 +1282,26 @@ pub const basic_authorization = struct { } pub fn valueLengthFromUri(uri: Uri) usize { - return valueLength( - if (uri.user) |user| user.len else 0, - if (uri.password) |password| password.len else 0, - ); + var stream = std.io.countingWriter(std.io.null_writer); + try stream.writer().print("{user}", .{uri.user orelse Uri.Component.empty}); + const user_len = stream.bytes_written; + stream.bytes_written = 0; + try stream.writer().print("{password}", .{uri.password orelse Uri.Component.empty}); + const password_len = stream.bytes_written; + return valueLength(@intCast(user_len), @intCast(password_len)); } pub fn value(uri: Uri, out: []u8) []u8 { - assert(uri.user == null or uri.user.?.len <= max_user_len); - assert(uri.password == null or uri.password.?.len <= max_password_len); - - @memcpy(out[0..prefix.len], prefix); - var buf: [max_user_len + ":".len + max_password_len]u8 = undefined; - const unencoded = std.fmt.bufPrint(&buf, "{s}:{s}", .{ - uri.user orelse "", uri.password orelse "", - }) catch unreachable; - const base64 = std.base64.standard.Encoder.encode(out[prefix.len..], unencoded); + var stream = std.io.fixedBufferStream(&buf); + stream.writer().print("{user}", .{uri.user orelse Uri.Component.empty}) catch + unreachable; + assert(stream.pos <= max_user_len); + stream.writer().print(":{password}", .{uri.password orelse Uri.Component.empty}) catch + unreachable; + @memcpy(out[0..prefix.len], prefix); + const base64 = std.base64.standard.Encoder.encode(out[prefix.len..], stream.getWritten()); return out[0 .. prefix.len + base64.len]; } }; @@ -1337,8 +1316,7 @@ pub fn connectTcp(client: *Client, host: []const u8, port: u16, protocol: Connec .host = host, .port = port, .protocol = protocol, - })) |node| - return node; + })) |node| return node; if (disable_tls and protocol == .tls) return error.TlsInitializationFailed; @@ -1449,19 +1427,12 @@ pub fn connectTunnel( client.connection_pool.release(client.allocator, conn); } - const uri: Uri = .{ + var buffer: [8096]u8 = undefined; + var req = client.open(.CONNECT, .{ .scheme = "http", - .user = null, - .password = null, - .host = tunnel_host, + .host = .{ .raw = tunnel_host }, .port = tunnel_port, - .path = "", - .query = null, - .fragment = null, - }; - - var buffer: [8096]u8 = undefined; - var req = client.open(.CONNECT, uri, .{ + }, .{ .redirect_behavior = .unhandled, .connection = conn, .server_header_buffer = &buffer, @@ -1471,7 +1442,7 @@ pub fn connectTunnel( }; defer req.deinit(); - req.send(.{ .raw_uri = true }) catch |err| break :tunnel err; + req.send() catch |err| break :tunnel err; req.wait() catch |err| break :tunnel err; if (req.response.status.class() == .server_error) { @@ -1500,7 +1471,7 @@ pub fn connectTunnel( } // Prevents a dependency loop in open() -const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUrlScheme, ConnectionRefused }; +const ConnectErrorPartial = ConnectTcpError || error{ UnsupportedUriScheme, ConnectionRefused }; pub const ConnectError = ConnectErrorPartial || RequestError; /// Connect to `host:port` using the specified protocol. This will reuse a @@ -1548,7 +1519,7 @@ pub fn connect( pub const RequestError = ConnectTcpError || ConnectErrorPartial || Request.SendError || std.fmt.ParseIntError || Connection.WriteError || error{ // TODO: file a zig fmt issue for this bad indentation - UnsupportedUrlScheme, + UnsupportedUriScheme, UriMissingHost, CertificateBundleLoadFailure, @@ -1598,12 +1569,28 @@ pub const RequestOptions = struct { privileged_headers: []const http.Header = &.{}, }; -pub const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ - .{ "http", .plain }, - .{ "ws", .plain }, - .{ "https", .tls }, - .{ "wss", .tls }, -}); +fn validateUri(uri: Uri, arena: Allocator) !struct { Connection.Protocol, Uri } { + const protocol_map = std.ComptimeStringMap(Connection.Protocol, .{ + .{ "http", .plain }, + .{ "ws", .plain }, + .{ "https", .tls }, + .{ "wss", .tls }, + }); + const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUriScheme; + var valid_uri = uri; + // The host is always going to be needed as a raw string for hostname resolution anyway. + valid_uri.host = .{ + .raw = try (uri.host orelse return error.UriMissingHost).toRawMaybeAlloc(arena), + }; + return .{ protocol, valid_uri }; +} + +fn uriPort(uri: Uri, protocol: Connection.Protocol) u16 { + return uri.port orelse switch (protocol) { + .plain => 80, + .tls => 443, + }; +} /// Open a connection to the host specified by `uri` and prepare to send a HTTP request. /// @@ -1633,14 +1620,8 @@ pub fn open( } } - const protocol = protocol_map.get(uri.scheme) orelse return error.UnsupportedUrlScheme; - - const port: u16 = uri.port orelse switch (protocol) { - .plain => 80, - .tls => 443, - }; - - const host = uri.host orelse return error.UriMissingHost; + var server_header = std.heap.FixedBufferAllocator.init(options.server_header_buffer); + const protocol, const valid_uri = try validateUri(uri, server_header.allocator()); if (protocol == .tls and @atomicLoad(bool, &client.next_https_rescan_certs, .acquire)) { if (disable_tls) unreachable; @@ -1649,15 +1630,17 @@ pub fn open( defer client.ca_bundle_mutex.unlock(); if (client.next_https_rescan_certs) { - client.ca_bundle.rescan(client.allocator) catch return error.CertificateBundleLoadFailure; + client.ca_bundle.rescan(client.allocator) catch + return error.CertificateBundleLoadFailure; @atomicStore(bool, &client.next_https_rescan_certs, false, .release); } } - const conn = options.connection orelse try client.connect(host, port, protocol); + const conn = options.connection orelse + try client.connect(valid_uri.host.?.raw, uriPort(valid_uri, protocol), protocol); var req: Request = .{ - .uri = uri, + .uri = valid_uri, .client = client, .connection = conn, .keep_alive = options.keep_alive, @@ -1671,7 +1654,7 @@ pub fn open( .status = undefined, .reason = undefined, .keep_alive = undefined, - .parser = proto.HeadersParser.init(options.server_header_buffer), + .parser = proto.HeadersParser.init(server_header.buffer[server_header.end_index..]), }, .headers = options.headers, .extra_headers = options.extra_headers, @@ -1751,7 +1734,7 @@ pub fn fetch(client: *Client, options: FetchOptions) !FetchResult { if (options.payload) |payload| req.transfer_encoding = .{ .content_length = payload.len }; - try req.send(.{ .raw_uri = options.raw_uri }); + try req.send(); if (options.payload) |payload| try req.writeAll(payload); diff --git a/lib/std/http/test.zig b/lib/std/http/test.zig index e2aa810d58..caeed0e1ea 100644 --- a/lib/std/http/test.zig +++ b/lib/std/http/test.zig @@ -64,7 +64,7 @@ test "trailers" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -474,6 +474,15 @@ test "general client/server API coverage" { .{ .name = "location", .value = "/redirect/3" }, }, }); + } else if (mem.eql(u8, request.head.target, "/redirect/5")) { + try request.respond("Hello, Redirected!\n", .{ + .status = .found, + .extra_headers = &.{ + .{ .name = "location", .value = "/%2525" }, + }, + }); + } else if (mem.eql(u8, request.head.target, "/%2525")) { + try request.respond("Encoded redirect successful!\n", .{}); } else if (mem.eql(u8, request.head.target, "/redirect/invalid")) { const invalid_port = try getUnusedTcpPort(); const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}", .{invalid_port}); @@ -529,7 +538,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -554,7 +563,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192 * 1024); @@ -578,7 +587,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -604,7 +613,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -629,7 +638,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -656,7 +665,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -684,7 +693,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); try std.testing.expectEqual(.ok, req.response.status); @@ -725,7 +734,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -749,7 +758,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -773,7 +782,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); @@ -797,13 +806,34 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); req.wait() catch |err| switch (err) { error.TooManyHttpRedirects => {}, else => return err, }; } + { // redirect to encoded url + const location = try std.fmt.allocPrint(gpa, "http://127.0.0.1:{d}/redirect/5", .{port}); + defer gpa.free(location); + const uri = try std.Uri.parse(location); + + log.info("{s}", .{location}); + var server_header_buffer: [1024]u8 = undefined; + var req = try client.open(.GET, uri, .{ + .server_header_buffer = &server_header_buffer, + }); + defer req.deinit(); + + try req.send(); + try req.wait(); + + const body = try req.reader().readAllAlloc(gpa, 8192); + defer gpa.free(body); + + try expectEqualStrings("Encoded redirect successful!\n", body); + } + // connection has been kept alive try expect(client.http_proxy != null or client.connection_pool.free_len == 1); @@ -819,7 +849,7 @@ test "general client/server API coverage" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); const result = req.wait(); // a proxy without an upstream is likely to return a 5xx status. @@ -913,16 +943,16 @@ test "Server streams both reading and writing" { var server_header_buffer: [555]u8 = undefined; var req = try client.open(.POST, .{ .scheme = "http", - .host = "127.0.0.1", + .host = .{ .raw = "127.0.0.1" }, .port = test_server.port(), - .path = "/", + .path = .{ .percent_encoded = "/" }, }, .{ .server_header_buffer = &server_header_buffer, }); defer req.deinit(); req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.wait(); try req.writeAll("one "); @@ -956,7 +986,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .{ .content_length = 14 }; - try req.send(.{}); + try req.send(); try req.writeAll("Hello, "); try req.writeAll("World!\n"); try req.finish(); @@ -990,7 +1020,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.writeAll("Hello, "); try req.writeAll("World!\n"); try req.finish(); @@ -1044,7 +1074,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.writeAll("Hello, "); try req.writeAll("World!\n"); try req.finish(); @@ -1075,7 +1105,7 @@ fn echoTests(client: *http.Client, port: u16) !void { req.transfer_encoding = .chunked; - try req.send(.{}); + try req.send(); try req.wait(); try expectEqual(.expectation_failed, req.response.status); } @@ -1180,7 +1210,7 @@ test "redirect to different connection" { }); defer req.deinit(); - try req.send(.{}); + try req.send(); try req.wait(); const body = try req.reader().readAllAlloc(gpa, 8192); diff --git a/lib/std/io.zig b/lib/std/io.zig index 9f0f444a83..ab89114000 100644 --- a/lib/std/io.zig +++ b/lib/std/io.zig @@ -413,7 +413,7 @@ pub const StreamSource = @import("io/stream_source.zig").StreamSource; pub const tty = @import("io/tty.zig"); /// A Writer that doesn't write to anything. -pub const null_writer = @as(NullWriter, .{ .context = {} }); +pub const null_writer: NullWriter = .{ .context = {} }; const NullWriter = Writer(void, error{}, dummyWrite); fn dummyWrite(context: void, data: []const u8) error{}!usize { diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b564d9a99b..93ad1ccbe2 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -3776,19 +3776,19 @@ fn llshr(r: []Limb, a: []const Limb, shift: usize) void { const limb_shift = shift / limb_bits; const interior_limb_shift = @as(Log2Limb, @truncate(shift)); - var carry: Limb = 0; var i: usize = 0; while (i < a.len - limb_shift) : (i += 1) { - const src_i = a.len - i - 1; - const dst_i = src_i - limb_shift; + const dst_i = i; + const src_i = dst_i + limb_shift; const src_digit = a[src_i]; - r[dst_i] = carry | (src_digit >> interior_limb_shift); - carry = @call(.always_inline, math.shl, .{ + const src_digit_next = if (src_i + 1 < a.len) a[src_i + 1] else 0; + const carry = @call(.always_inline, math.shl, .{ Limb, - src_digit, + src_digit_next, limb_bits - @as(Limb, @intCast(interior_limb_shift)), }); + r[dst_i] = carry | (src_digit >> interior_limb_shift); } } diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index f9d7543c2b..624bdc0b83 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2019,6 +2019,19 @@ test "shift-right multi" { try a.shiftRight(&a, 63); try a.shiftRight(&a, 2); try testing.expect(a.eqlZero()); + + try a.set(0xffff0000eeee1111dddd2222cccc3333000000000000000000000); + try a.shiftRight(&a, 84); + const string = try a.toString( + testing.allocator, + 16, + .lower, + ); + defer testing.allocator.free(string); + try std.testing.expectEqualStrings( + string, + "ffff0000eeee1111dddd2222cccc3333", + ); } test "shift-left single" { diff --git a/lib/std/once.zig b/lib/std/once.zig index 2f6ee709e2..ee3a8b7a35 100644 --- a/lib/std/once.zig +++ b/lib/std/once.zig @@ -7,6 +7,7 @@ pub fn once(comptime f: fn () void) Once(f) { } /// An object that executes the function `f` just once. +/// It is undefined behavior if `f` re-enters the same Once instance. pub fn Once(comptime f: fn () void) type { return struct { done: bool = false, @@ -51,15 +52,18 @@ test "Once executes its function just once" { global_once.call(); } else { var threads: [10]std.Thread = undefined; - defer for (threads) |handle| handle.join(); + var thread_count: usize = 0; + defer for (threads[0..thread_count]) |handle| handle.join(); for (&threads) |*handle| { handle.* = try std.Thread.spawn(.{}, struct { fn thread_fn(x: u8) void { _ = x; global_once.call(); + if (global_number != 1) @panic("memory ordering bug"); } }.thread_fn, .{0}); + thread_count += 1; } } diff --git a/lib/std/posix/test.zig b/lib/std/posix/test.zig index 1020bef4b7..1847ceb8a1 100644 --- a/lib/std/posix/test.zig +++ b/lib/std/posix/test.zig @@ -839,7 +839,7 @@ test "sigaction" { const S = struct { var handler_called_count: u32 = 0; - fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) void { + fn handler(sig: i32, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.C) void { _ = ctx_ptr; // Check that we received the correct signal. switch (native_os) { diff --git a/lib/std/tar.zig b/lib/std/tar.zig index 2977bc16cc..9dc5bb4a53 100644 --- a/lib/std/tar.zig +++ b/lib/std/tar.zig @@ -30,7 +30,7 @@ pub const Diagnostics = struct { errors: std.ArrayListUnmanaged(Error) = .{}, root_entries: usize = 0, - root_dir: ?[]const u8 = null, + root_dir: []const u8 = "", pub const Error = union(enum) { unable_to_create_sym_link: struct { @@ -55,10 +55,8 @@ pub const Diagnostics = struct { d.root_dir = try d.allocator.dupe(u8, root_dir); return; } - if (d.root_dir) |r| { - d.allocator.free(r); - d.root_dir = null; - } + d.allocator.free(d.root_dir); + d.root_dir = ""; } } @@ -103,10 +101,7 @@ pub const Diagnostics = struct { } } d.errors.deinit(d.allocator); - if (d.root_dir) |r| { - d.allocator.free(r); - d.root_dir = null; - } + d.allocator.free(d.root_dir); d.* = undefined; } }; @@ -1060,7 +1055,7 @@ test "pipeToFileSystem root_dir" { }; // there is no root_dir - try testing.expect(diagnostics.root_dir == null); + try testing.expectEqual(0, diagnostics.root_dir.len); try testing.expectEqual(3, diagnostics.root_entries); } @@ -1082,7 +1077,7 @@ test "pipeToFileSystem root_dir" { }; // root_dir found - try testing.expectEqualStrings("example", diagnostics.root_dir.?); + try testing.expectEqualStrings("example", diagnostics.root_dir); try testing.expectEqual(1, diagnostics.root_entries); } } diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index db082b7f8e..64e8a1c805 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -106,12 +106,8 @@ pub const NullTerminatedString = enum(u32) { /// Given an index into `string_bytes` returns the null-terminated string found there. pub fn nullTerminatedString(code: Zir, index: NullTerminatedString) [:0]const u8 { - const start = @intFromEnum(index); - var end: u32 = start; - while (code.string_bytes[end] != 0) { - end += 1; - } - return code.string_bytes[start..end :0]; + const slice = code.string_bytes[@intFromEnum(index)..]; + return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0]; } pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref { diff --git a/lib/std/zig/system.zig b/lib/std/zig/system.zig index c9fe14a1c6..af0986e0b8 100644 --- a/lib/std/zig/system.zig +++ b/lib/std/zig/system.zig @@ -988,9 +988,13 @@ fn detectAbiAndDynamicLinker( // if it finds one, then instead of using /usr/bin/env as the ELF file to examine, it uses the file it references instead, // doing the same logic recursively in case it finds another shebang line. - // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a - // reasonably reliable path to start with. - var file_name: []const u8 = "/usr/bin/env"; + var file_name: []const u8 = switch (os.tag) { + // Since /usr/bin/env is hard-coded into the shebang line of many portable scripts, it's a + // reasonably reliable path to start with. + else => "/usr/bin/env", + // Haiku does not have a /usr root directory. + .haiku => "/bin/env", + }; // #! (2) + 255 (max length of shebang line since Linux 5.1) + \n (1) var buffer: [258]u8 = undefined; while (true) { diff --git a/lib/std/zig/system/NativePaths.zig b/lib/std/zig/system/NativePaths.zig index 9d9ab22812..2a50e27b0c 100644 --- a/lib/std/zig/system/NativePaths.zig +++ b/lib/std/zig/system/NativePaths.zig @@ -105,6 +105,13 @@ pub fn detect(arena: Allocator, native_target: std.Target) !NativePaths { return self; } + if (builtin.os.tag == .haiku) { + try self.addLibDir("/system/non-packaged/lib"); + try self.addLibDir("/system/develop/lib"); + try self.addLibDir("/system/lib"); + return self; + } + if (builtin.os.tag != .windows and builtin.os.tag != .wasi) { const triple = try native_target.linuxTriple(arena); diff --git a/src/Compilation.zig b/src/Compilation.zig index c533f2fae7..7af3d7bfd1 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3159,7 +3159,7 @@ pub fn addModuleErrorMsg(mod: *Module, eb: *ErrorBundle.Wip, module_err_msg: Mod const rt_file_path = try module_reference.src_loc.file_scope.fullPath(gpa); defer gpa.free(rt_file_path); ref_traces.appendAssumeCapacity(.{ - .decl_name = try eb.addString(ip.stringToSlice(module_reference.decl)), + .decl_name = try eb.addString(module_reference.decl.toSlice(ip)), .src_loc = try eb.addSourceLocation(.{ .src_path = try eb.addString(rt_file_path), .span_start = span.start, @@ -3731,24 +3731,24 @@ fn docsCopyFallible(comp: *Compilation) anyerror!void { }; defer tar_file.close(); - var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, void) = .{}; + var seen_table: std.AutoArrayHashMapUnmanaged(*Package.Module, []const u8) = .{}; defer seen_table.deinit(comp.gpa); - try seen_table.put(comp.gpa, zcu.main_mod, {}); - try seen_table.put(comp.gpa, zcu.std_mod, {}); + try seen_table.put(comp.gpa, zcu.main_mod, comp.root_name); + try seen_table.put(comp.gpa, zcu.std_mod, zcu.std_mod.fully_qualified_name); var i: usize = 0; while (i < seen_table.count()) : (i += 1) { const mod = seen_table.keys()[i]; - try comp.docsCopyModule(mod, tar_file); + try comp.docsCopyModule(mod, seen_table.values()[i], tar_file); const deps = mod.deps.values(); try seen_table.ensureUnusedCapacity(comp.gpa, deps.len); - for (deps) |dep| seen_table.putAssumeCapacity(dep, {}); + for (deps) |dep| seen_table.putAssumeCapacity(dep, dep.fully_qualified_name); } } -fn docsCopyModule(comp: *Compilation, module: *Package.Module, tar_file: std.fs.File) !void { +fn docsCopyModule(comp: *Compilation, module: *Package.Module, name: []const u8, tar_file: std.fs.File) !void { const root = module.root; const sub_path = if (root.sub_path.len == 0) "." else root.sub_path; var mod_dir = root.root_dir.handle.openDir(sub_path, .{ .iterate = true }) catch |err| { @@ -3788,7 +3788,7 @@ fn docsCopyModule(comp: *Compilation, module: *Package.Module, tar_file: std.fs. var file_header = std.tar.output.Header.init(); file_header.typeflag = .regular; - try file_header.setPath(module.fully_qualified_name, entry.path); + try file_header.setPath(name, entry.path); try file_header.setSize(stat.size); try file_header.updateChecksum(); @@ -4074,8 +4074,7 @@ fn workerCheckEmbedFile( fn detectEmbedFileUpdate(comp: *Compilation, embed_file: *Module.EmbedFile) !void { const mod = comp.module.?; const ip = &mod.intern_pool; - const sub_file_path = ip.stringToSlice(embed_file.sub_file_path); - var file = try embed_file.owner.root.openFile(sub_file_path, .{}); + var file = try embed_file.owner.root.openFile(embed_file.sub_file_path.toSlice(ip), .{}); defer file.close(); const stat = try file.stat(); @@ -4444,7 +4443,7 @@ fn reportRetryableEmbedFileError( const ip = &mod.intern_pool; const err_msg = try Module.ErrorMsg.create(gpa, src_loc, "unable to load '{}{s}': {s}", .{ embed_file.owner.root, - ip.stringToSlice(embed_file.sub_file_path), + embed_file.sub_file_path.toSlice(ip), @errorName(err), }); diff --git a/src/InternPool.zig b/src/InternPool.zig index 1155d8c5ad..15dba62e07 100644 --- a/src/InternPool.zig +++ b/src/InternPool.zig @@ -351,7 +351,7 @@ const KeyAdapter = struct { pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { _ = b_void; if (ctx.intern_pool.items.items(.tag)[b_map_index] == .removed) return false; - return ctx.intern_pool.indexToKey(@as(Index, @enumFromInt(b_map_index))).eql(a, ctx.intern_pool); + return ctx.intern_pool.indexToKey(@enumFromInt(b_map_index)).eql(a, ctx.intern_pool); } pub fn hash(ctx: @This(), a: Key) u32 { @@ -385,7 +385,7 @@ pub const RuntimeIndex = enum(u32) { _, pub fn increment(ri: *RuntimeIndex) void { - ri.* = @as(RuntimeIndex, @enumFromInt(@intFromEnum(ri.*) + 1)); + ri.* = @enumFromInt(@intFromEnum(ri.*) + 1); } }; @@ -418,12 +418,44 @@ pub const OptionalNamespaceIndex = enum(u32) { /// An index into `string_bytes`. pub const String = enum(u32) { + /// An empty string. + empty = 0, + _, + + pub fn toSlice(string: String, len: u64, ip: *const InternPool) []const u8 { + return ip.string_bytes.items[@intFromEnum(string)..][0..@intCast(len)]; + } + + pub fn at(string: String, index: u64, ip: *const InternPool) u8 { + return ip.string_bytes.items[@intCast(@intFromEnum(string) + index)]; + } + + pub fn toNullTerminatedString(string: String, len: u64, ip: *const InternPool) NullTerminatedString { + assert(std.mem.indexOfScalar(u8, string.toSlice(len, ip), 0) == null); + assert(string.at(len, ip) == 0); + return @enumFromInt(@intFromEnum(string)); + } +}; + +/// An index into `string_bytes` which might be `none`. +pub const OptionalString = enum(u32) { + /// This is distinct from `none` - it is a valid index that represents empty string. + empty = 0, + none = std.math.maxInt(u32), _, + + pub fn unwrap(string: OptionalString) ?String { + return if (string != .none) @enumFromInt(@intFromEnum(string)) else null; + } + + pub fn toSlice(string: OptionalString, len: u64, ip: *const InternPool) ?[]const u8 { + return (string.unwrap() orelse return null).toSlice(len, ip); + } }; /// An index into `string_bytes`. pub const NullTerminatedString = enum(u32) { - /// This is distinct from `none` - it is a valid index that represents empty string. + /// An empty string. empty = 0, _, @@ -447,6 +479,19 @@ pub const NullTerminatedString = enum(u32) { return @enumFromInt(@intFromEnum(self)); } + pub fn toSlice(string: NullTerminatedString, ip: *const InternPool) [:0]const u8 { + const slice = ip.string_bytes.items[@intFromEnum(string)..]; + return slice[0..std.mem.indexOfScalar(u8, slice, 0).? :0]; + } + + pub fn length(string: NullTerminatedString, ip: *const InternPool) u32 { + return @intCast(string.toSlice(ip).len); + } + + pub fn eqlSlice(string: NullTerminatedString, slice: []const u8, ip: *const InternPool) bool { + return std.mem.eql(u8, string.toSlice(ip), slice); + } + const Adapter = struct { strings: []const NullTerminatedString, @@ -467,11 +512,11 @@ pub const NullTerminatedString = enum(u32) { return @intFromEnum(a) < @intFromEnum(b); } - pub fn toUnsigned(self: NullTerminatedString, ip: *const InternPool) ?u32 { - const s = ip.stringToSlice(self); - if (s.len > 1 and s[0] == '0') return null; - if (std.mem.indexOfScalar(u8, s, '_')) |_| return null; - return std.fmt.parseUnsigned(u32, s, 10) catch null; + pub fn toUnsigned(string: NullTerminatedString, ip: *const InternPool) ?u32 { + const slice = string.toSlice(ip); + if (slice.len > 1 and slice[0] == '0') return null; + if (std.mem.indexOfScalar(u8, slice, '_')) |_| return null; + return std.fmt.parseUnsigned(u32, slice, 10) catch null; } const FormatData = struct { @@ -484,11 +529,11 @@ pub const NullTerminatedString = enum(u32) { _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - const s = data.ip.stringToSlice(data.string); + const slice = data.string.toSlice(data.ip); if (comptime std.mem.eql(u8, specifier, "")) { - try writer.writeAll(s); + try writer.writeAll(slice); } else if (comptime std.mem.eql(u8, specifier, "i")) { - try writer.print("{p}", .{std.zig.fmtId(s)}); + try writer.print("{p}", .{std.zig.fmtId(slice)}); } else @compileError("invalid format string '" ++ specifier ++ "' for '" ++ @typeName(NullTerminatedString) ++ "'"); } @@ -504,9 +549,12 @@ pub const OptionalNullTerminatedString = enum(u32) { none = std.math.maxInt(u32), _, - pub fn unwrap(oi: OptionalNullTerminatedString) ?NullTerminatedString { - if (oi == .none) return null; - return @enumFromInt(@intFromEnum(oi)); + pub fn unwrap(string: OptionalNullTerminatedString) ?NullTerminatedString { + return if (string != .none) @enumFromInt(@intFromEnum(string)) else null; + } + + pub fn toSlice(string: OptionalNullTerminatedString, ip: *const InternPool) ?[:0]const u8 { + return (string.unwrap() orelse return null).toSlice(ip); } }; @@ -690,6 +738,10 @@ pub const Key = union(enum) { len: u64, child: Index, sentinel: Index = .none, + + pub fn lenIncludingSentinel(array_type: ArrayType) u64 { + return array_type.len + @intFromBool(array_type.sentinel != .none); + } }; /// Extern so that hashing can be done via memory reinterpreting. @@ -1043,7 +1095,7 @@ pub const Key = union(enum) { storage: Storage, pub const Storage = union(enum) { - bytes: []const u8, + bytes: String, elems: []const Index, repeated_elem: Index, @@ -1203,7 +1255,7 @@ pub const Key = union(enum) { if (child == .u8_type) { switch (aggregate.storage) { - .bytes => |bytes| for (bytes[0..@intCast(len)]) |byte| { + .bytes => |bytes| for (bytes.toSlice(len, ip)) |byte| { std.hash.autoHash(&hasher, KeyTag.int); std.hash.autoHash(&hasher, byte); }, @@ -1240,7 +1292,7 @@ pub const Key = union(enum) { switch (aggregate.storage) { .bytes => unreachable, - .elems => |elems| for (elems[0..@as(usize, @intCast(len))]) |elem| + .elems => |elems| for (elems[0..@intCast(len)]) |elem| std.hash.autoHash(&hasher, elem), .repeated_elem => |elem| { var remaining = len; @@ -1505,11 +1557,11 @@ pub const Key = union(enum) { if (a_info.ty == .c_longdouble_type and a_info.storage != .f80) { // These are strange: we'll sometimes represent them as f128, even if the // underlying type is smaller. f80 is an exception: see float_c_longdouble_f80. - const a_val = switch (a_info.storage) { - inline else => |val| @as(u128, @bitCast(@as(f128, @floatCast(val)))), + const a_val: u128 = switch (a_info.storage) { + inline else => |val| @bitCast(@as(f128, @floatCast(val))), }; - const b_val = switch (b_info.storage) { - inline else => |val| @as(u128, @bitCast(@as(f128, @floatCast(val)))), + const b_val: u128 = switch (b_info.storage) { + inline else => |val| @bitCast(@as(f128, @floatCast(val))), }; return a_val == b_val; } @@ -1560,11 +1612,11 @@ pub const Key = union(enum) { const len = ip.aggregateTypeLen(a_info.ty); const StorageTag = @typeInfo(Key.Aggregate.Storage).Union.tag_type.?; if (@as(StorageTag, a_info.storage) != @as(StorageTag, b_info.storage)) { - for (0..@as(usize, @intCast(len))) |elem_index| { + for (0..@intCast(len)) |elem_index| { const a_elem = switch (a_info.storage) { .bytes => |bytes| ip.getIfExists(.{ .int = .{ .ty = .u8_type, - .storage = .{ .u64 = bytes[elem_index] }, + .storage = .{ .u64 = bytes.at(elem_index, ip) }, } }) orelse return false, .elems => |elems| elems[elem_index], .repeated_elem => |elem| elem, @@ -1572,7 +1624,7 @@ pub const Key = union(enum) { const b_elem = switch (b_info.storage) { .bytes => |bytes| ip.getIfExists(.{ .int = .{ .ty = .u8_type, - .storage = .{ .u64 = bytes[elem_index] }, + .storage = .{ .u64 = bytes.at(elem_index, ip) }, } }) orelse return false, .elems => |elems| elems[elem_index], .repeated_elem => |elem| elem, @@ -1585,18 +1637,15 @@ pub const Key = union(enum) { switch (a_info.storage) { .bytes => |a_bytes| { const b_bytes = b_info.storage.bytes; - return std.mem.eql( - u8, - a_bytes[0..@as(usize, @intCast(len))], - b_bytes[0..@as(usize, @intCast(len))], - ); + return a_bytes == b_bytes or + std.mem.eql(u8, a_bytes.toSlice(len, ip), b_bytes.toSlice(len, ip)); }, .elems => |a_elems| { const b_elems = b_info.storage.elems; return std.mem.eql( Index, - a_elems[0..@as(usize, @intCast(len))], - b_elems[0..@as(usize, @intCast(len))], + a_elems[0..@intCast(len)], + b_elems[0..@intCast(len)], ); }, .repeated_elem => |a_elem| { @@ -4175,10 +4224,10 @@ pub const Float64 = struct { } fn pack(val: f64) Float64 { - const bits = @as(u64, @bitCast(val)); + const bits: u64 = @bitCast(val); return .{ - .piece0 = @as(u32, @truncate(bits)), - .piece1 = @as(u32, @truncate(bits >> 32)), + .piece0 = @truncate(bits), + .piece1 = @truncate(bits >> 32), }; } }; @@ -4197,11 +4246,11 @@ pub const Float80 = struct { } fn pack(val: f80) Float80 { - const bits = @as(u80, @bitCast(val)); + const bits: u80 = @bitCast(val); return .{ - .piece0 = @as(u32, @truncate(bits)), - .piece1 = @as(u32, @truncate(bits >> 32)), - .piece2 = @as(u16, @truncate(bits >> 64)), + .piece0 = @truncate(bits), + .piece1 = @truncate(bits >> 32), + .piece2 = @truncate(bits >> 64), }; } }; @@ -4222,12 +4271,12 @@ pub const Float128 = struct { } fn pack(val: f128) Float128 { - const bits = @as(u128, @bitCast(val)); + const bits: u128 = @bitCast(val); return .{ - .piece0 = @as(u32, @truncate(bits)), - .piece1 = @as(u32, @truncate(bits >> 32)), - .piece2 = @as(u32, @truncate(bits >> 64)), - .piece3 = @as(u32, @truncate(bits >> 96)), + .piece0 = @truncate(bits), + .piece1 = @truncate(bits >> 32), + .piece2 = @truncate(bits >> 64), + .piece3 = @truncate(bits >> 96), }; } }; @@ -4244,7 +4293,7 @@ pub fn init(ip: *InternPool, gpa: Allocator) !void { assert(ip.items.len == 0); // Reserve string index 0 for an empty string. - assert((try ip.getOrPutString(gpa, "")) == .empty); + assert((try ip.getOrPutString(gpa, "", .no_embedded_nulls)) == .empty); // So that we can use `catch unreachable` below. try ip.items.ensureUnusedCapacity(gpa, static_keys.len); @@ -4329,13 +4378,13 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .type_int_signed => .{ .int_type = .{ .signedness = .signed, - .bits = @as(u16, @intCast(data)), + .bits = @intCast(data), }, }, .type_int_unsigned => .{ .int_type = .{ .signedness = .unsigned, - .bits = @as(u16, @intCast(data)), + .bits = @intCast(data), }, }, .type_array_big => { @@ -4354,8 +4403,8 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .sentinel = .none, } }; }, - .simple_type => .{ .simple_type = @as(SimpleType, @enumFromInt(data)) }, - .simple_value => .{ .simple_value = @as(SimpleValue, @enumFromInt(data)) }, + .simple_type => .{ .simple_type = @enumFromInt(data) }, + .simple_value => .{ .simple_value = @enumFromInt(data) }, .type_vector => { const vector_info = ip.extraData(Vector, data); @@ -4506,9 +4555,9 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { } }, .type_function => .{ .func_type = ip.extraFuncType(data) }, - .undef => .{ .undef = @as(Index, @enumFromInt(data)) }, + .undef => .{ .undef = @enumFromInt(data) }, .opt_null => .{ .opt = .{ - .ty = @as(Index, @enumFromInt(data)), + .ty = @enumFromInt(data), .val = .none, } }, .opt_payload => { @@ -4670,11 +4719,11 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { }, .float_f16 => .{ .float = .{ .ty = .f16_type, - .storage = .{ .f16 = @as(f16, @bitCast(@as(u16, @intCast(data)))) }, + .storage = .{ .f16 = @bitCast(@as(u16, @intCast(data))) }, } }, .float_f32 => .{ .float = .{ .ty = .f32_type, - .storage = .{ .f32 = @as(f32, @bitCast(data)) }, + .storage = .{ .f32 = @bitCast(data) }, } }, .float_f64 => .{ .float = .{ .ty = .f64_type, @@ -4771,10 +4820,9 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { }, .bytes => { const extra = ip.extraData(Bytes, data); - const len: u32 = @intCast(ip.aggregateTypeLenIncludingSentinel(extra.ty)); return .{ .aggregate = .{ .ty = extra.ty, - .storage = .{ .bytes = ip.string_bytes.items[@intFromEnum(extra.bytes)..][0..len] }, + .storage = .{ .bytes = extra.bytes }, } }; }, .aggregate => { @@ -4809,14 +4857,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key { .val = .{ .payload = extra.val }, } }; }, - .enum_literal => .{ .enum_literal = @as(NullTerminatedString, @enumFromInt(data)) }, + .enum_literal => .{ .enum_literal = @enumFromInt(data) }, .enum_tag => .{ .enum_tag = ip.extraData(Tag.EnumTag, data) }, .memoized_call => { const extra = ip.extraDataTrail(MemoizedCall, data); return .{ .memoized_call = .{ .func = extra.data.func, - .arg_values = @as([]const Index, @ptrCast(ip.extra.items[extra.end..][0..extra.data.args_len])), + .arg_values = @ptrCast(ip.extra.items[extra.end..][0..extra.data.args_len]), .result = extra.data.result, } }; }, @@ -5596,9 +5644,8 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { switch (aggregate.storage) { .bytes => |bytes| { assert(child == .u8_type); - if (bytes.len != len) { - assert(bytes.len == len_including_sentinel); - assert(bytes[@intCast(len)] == ip.indexToKey(sentinel).int.storage.u64); + if (sentinel != .none) { + assert(bytes.at(@intCast(len), ip) == ip.indexToKey(sentinel).int.storage.u64); } }, .elems => |elems| { @@ -5641,11 +5688,16 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { switch (ty_key) { .anon_struct_type => |anon_struct_type| opv: { switch (aggregate.storage) { - .bytes => |bytes| for (anon_struct_type.values.get(ip), bytes) |value, byte| { - if (value != ip.getIfExists(.{ .int = .{ - .ty = .u8_type, - .storage = .{ .u64 = byte }, - } })) break :opv; + .bytes => |bytes| for (anon_struct_type.values.get(ip), bytes.at(0, ip)..) |value, byte| { + if (value == .none) break :opv; + switch (ip.indexToKey(value)) { + .undef => break :opv, + .int => |int| switch (int.storage) { + .u64 => |x| if (x != byte) break :opv, + else => break :opv, + }, + else => unreachable, + } }, .elems => |elems| if (!std.mem.eql( Index, @@ -5670,9 +5722,9 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { repeated: { switch (aggregate.storage) { - .bytes => |bytes| for (bytes[1..@as(usize, @intCast(len))]) |byte| - if (byte != bytes[0]) break :repeated, - .elems => |elems| for (elems[1..@as(usize, @intCast(len))]) |elem| + .bytes => |bytes| for (bytes.toSlice(len, ip)[1..]) |byte| + if (byte != bytes.at(0, ip)) break :repeated, + .elems => |elems| for (elems[1..@intCast(len)]) |elem| if (elem != elems[0]) break :repeated, .repeated_elem => {}, } @@ -5681,7 +5733,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { _ = ip.map.pop(); const elem = try ip.get(gpa, .{ .int = .{ .ty = .u8_type, - .storage = .{ .u64 = bytes[0] }, + .storage = .{ .u64 = bytes.at(0, ip) }, } }); assert(!(try ip.map.getOrPutAdapted(gpa, key, adapter)).found_existing); try ip.items.ensureUnusedCapacity(gpa, 1); @@ -5710,7 +5762,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { try ip.string_bytes.ensureUnusedCapacity(gpa, @intCast(len_including_sentinel + 1)); try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Bytes).Struct.fields.len); switch (aggregate.storage) { - .bytes => |bytes| ip.string_bytes.appendSliceAssumeCapacity(bytes[0..@intCast(len)]), + .bytes => |bytes| ip.string_bytes.appendSliceAssumeCapacity(bytes.toSlice(len, ip)), .elems => |elems| for (elems[0..@intCast(len)]) |elem| switch (ip.indexToKey(elem)) { .undef => { ip.string_bytes.shrinkRetainingCapacity(string_bytes_index); @@ -5730,15 +5782,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { else => unreachable, }, } - const has_internal_null = - std.mem.indexOfScalar(u8, ip.string_bytes.items[string_bytes_index..], 0) != null; if (sentinel != .none) ip.string_bytes.appendAssumeCapacity( @intCast(ip.indexToKey(sentinel).int.storage.u64), ); - const string: String = if (has_internal_null) - @enumFromInt(string_bytes_index) - else - (try ip.getOrPutTrailingString(gpa, @intCast(len_including_sentinel))).toString(); + const string = try ip.getOrPutTrailingString( + gpa, + @intCast(len_including_sentinel), + .maybe_embedded_nulls, + ); ip.items.appendAssumeCapacity(.{ .tag = .bytes, .data = ip.addExtraAssumeCapacity(Bytes{ @@ -5780,7 +5831,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index { .tag = .memoized_call, .data = ip.addExtraAssumeCapacity(MemoizedCall{ .func = memoized_call.func, - .args_len = @as(u32, @intCast(memoized_call.arg_values.len)), + .args_len = @intCast(memoized_call.arg_values.len), .result = memoized_call.result, }), }); @@ -6753,7 +6804,7 @@ fn finishFuncInstance( const decl = ip.declPtr(decl_index); decl.name = try ip.getOrPutStringFmt(gpa, "{}__anon_{d}", .{ fn_owner_decl.name.fmt(ip), @intFromEnum(decl_index), - }); + }, .no_embedded_nulls); return func_index; } @@ -7216,7 +7267,7 @@ pub fn remove(ip: *InternPool, index: Index) void { } fn addInt(ip: *InternPool, gpa: Allocator, ty: Index, tag: Tag, limbs: []const Limb) !void { - const limbs_len = @as(u32, @intCast(limbs.len)); + const limbs_len: u32 = @intCast(limbs.len); try ip.reserveLimbs(gpa, @typeInfo(Int).Struct.fields.len + limbs_len); ip.items.appendAssumeCapacity(.{ .tag = tag, @@ -7235,7 +7286,7 @@ fn addExtra(ip: *InternPool, gpa: Allocator, extra: anytype) Allocator.Error!u32 } fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 { - const result = @as(u32, @intCast(ip.extra.items.len)); + const result: u32 = @intCast(ip.extra.items.len); inline for (@typeInfo(@TypeOf(extra)).Struct.fields) |field| { ip.extra.appendAssumeCapacity(switch (field.type) { Index, @@ -7286,7 +7337,7 @@ fn addLimbsExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 { @sizeOf(u64) => {}, else => @compileError("unsupported host"), } - const result = @as(u32, @intCast(ip.limbs.items.len)); + const result: u32 = @intCast(ip.limbs.items.len); inline for (@typeInfo(@TypeOf(extra)).Struct.fields, 0..) |field, i| { const new: u32 = switch (field.type) { u32 => @field(extra, field.name), @@ -7374,7 +7425,7 @@ fn limbData(ip: *const InternPool, comptime T: type, index: usize) T { @field(result, field.name) = switch (field.type) { u32 => int32, - Index => @as(Index, @enumFromInt(int32)), + Index => @enumFromInt(int32), else => @compileError("bad field type: " ++ @typeName(field.type)), }; } @@ -7410,8 +7461,8 @@ fn limbsSliceToIndex(ip: *const InternPool, limbs: []const Limb) LimbsAsIndexes }; // TODO: https://github.com/ziglang/zig/issues/1738 return .{ - .start = @as(u32, @intCast(@divExact(@intFromPtr(limbs.ptr) - @intFromPtr(host_slice.ptr), @sizeOf(Limb)))), - .len = @as(u32, @intCast(limbs.len)), + .start = @intCast(@divExact(@intFromPtr(limbs.ptr) - @intFromPtr(host_slice.ptr), @sizeOf(Limb))), + .len = @intCast(limbs.len), }; } @@ -7683,7 +7734,7 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al .val = error_union.val, } }), .aggregate => |aggregate| { - const new_len = @as(usize, @intCast(ip.aggregateTypeLen(new_ty))); + const new_len: usize = @intCast(ip.aggregateTypeLen(new_ty)); direct: { const old_ty_child = switch (ip.indexToKey(old_ty)) { inline .array_type, .vector_type => |seq_type| seq_type.child, @@ -7696,16 +7747,11 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al else => unreachable, }; if (old_ty_child != new_ty_child) break :direct; - // TODO: write something like getCoercedInts to avoid needing to dupe here switch (aggregate.storage) { - .bytes => |bytes| { - const bytes_copy = try gpa.dupe(u8, bytes[0..new_len]); - defer gpa.free(bytes_copy); - return ip.get(gpa, .{ .aggregate = .{ - .ty = new_ty, - .storage = .{ .bytes = bytes_copy }, - } }); - }, + .bytes => |bytes| return ip.get(gpa, .{ .aggregate = .{ + .ty = new_ty, + .storage = .{ .bytes = bytes }, + } }), .elems => |elems| { const elems_copy = try gpa.dupe(Index, elems[0..new_len]); defer gpa.free(elems_copy); @@ -7729,14 +7775,13 @@ pub fn getCoerced(ip: *InternPool, gpa: Allocator, val: Index, new_ty: Index) Al // lifetime issues, since it'll allow us to avoid referencing `aggregate` after we // begin interning elems. switch (aggregate.storage) { - .bytes => { + .bytes => |bytes| { // We have to intern each value here, so unfortunately we can't easily avoid // the repeated indexToKey calls. - for (agg_elems, 0..) |*elem, i| { - const x = ip.indexToKey(val).aggregate.storage.bytes[i]; + for (agg_elems, 0..) |*elem, index| { elem.* = try ip.get(gpa, .{ .int = .{ .ty = .u8_type, - .storage = .{ .u64 = x }, + .storage = .{ .u64 = bytes.at(index, ip) }, } }); } }, @@ -8169,9 +8214,8 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void { .bytes => b: { const info = ip.extraData(Bytes, data); - const len = @as(u32, @intCast(ip.aggregateTypeLenIncludingSentinel(info.ty))); - break :b @sizeOf(Bytes) + len + - @intFromBool(ip.string_bytes.items[@intFromEnum(info.bytes) + len - 1] != 0); + const len: usize = @intCast(ip.aggregateTypeLenIncludingSentinel(info.ty)); + break :b @sizeOf(Bytes) + len + @intFromBool(info.bytes.at(len - 1, ip) != 0); }, .aggregate => b: { const info = ip.extraData(Tag.Aggregate, data); @@ -8434,15 +8478,35 @@ pub fn destroyNamespace(ip: *InternPool, gpa: Allocator, index: NamespaceIndex) }; } +const EmbeddedNulls = enum { + no_embedded_nulls, + maybe_embedded_nulls, + + fn StringType(comptime embedded_nulls: EmbeddedNulls) type { + return switch (embedded_nulls) { + .no_embedded_nulls => NullTerminatedString, + .maybe_embedded_nulls => String, + }; + } + + fn OptionalStringType(comptime embedded_nulls: EmbeddedNulls) type { + return switch (embedded_nulls) { + .no_embedded_nulls => OptionalNullTerminatedString, + .maybe_embedded_nulls => OptionalString, + }; + } +}; + pub fn getOrPutString( ip: *InternPool, gpa: Allocator, - s: []const u8, -) Allocator.Error!NullTerminatedString { - try ip.string_bytes.ensureUnusedCapacity(gpa, s.len + 1); - ip.string_bytes.appendSliceAssumeCapacity(s); + slice: []const u8, + comptime embedded_nulls: EmbeddedNulls, +) Allocator.Error!embedded_nulls.StringType() { + try ip.string_bytes.ensureUnusedCapacity(gpa, slice.len + 1); + ip.string_bytes.appendSliceAssumeCapacity(slice); ip.string_bytes.appendAssumeCapacity(0); - return ip.getOrPutTrailingString(gpa, s.len + 1); + return ip.getOrPutTrailingString(gpa, slice.len + 1, embedded_nulls); } pub fn getOrPutStringFmt( @@ -8450,23 +8514,24 @@ pub fn getOrPutStringFmt( gpa: Allocator, comptime format: []const u8, args: anytype, -) Allocator.Error!NullTerminatedString { + comptime embedded_nulls: EmbeddedNulls, +) Allocator.Error!embedded_nulls.StringType() { // ensure that references to string_bytes in args do not get invalidated const len: usize = @intCast(std.fmt.count(format, args) + 1); try ip.string_bytes.ensureUnusedCapacity(gpa, len); ip.string_bytes.writer(undefined).print(format, args) catch unreachable; ip.string_bytes.appendAssumeCapacity(0); - return ip.getOrPutTrailingString(gpa, len); + return ip.getOrPutTrailingString(gpa, len, embedded_nulls); } pub fn getOrPutStringOpt( ip: *InternPool, gpa: Allocator, - optional_string: ?[]const u8, -) Allocator.Error!OptionalNullTerminatedString { - const s = optional_string orelse return .none; - const interned = try getOrPutString(ip, gpa, s); - return interned.toOptional(); + slice: ?[]const u8, + comptime embedded_nulls: EmbeddedNulls, +) Allocator.Error!embedded_nulls.OptionalStringType() { + const string = try getOrPutString(ip, gpa, slice orelse return .none, embedded_nulls); + return string.toOptional(); } /// Uses the last len bytes of ip.string_bytes as the key. @@ -8474,7 +8539,8 @@ pub fn getOrPutTrailingString( ip: *InternPool, gpa: Allocator, len: usize, -) Allocator.Error!NullTerminatedString { + comptime embedded_nulls: EmbeddedNulls, +) Allocator.Error!embedded_nulls.StringType() { const string_bytes = &ip.string_bytes; const str_index: u32 = @intCast(string_bytes.items.len - len); if (len > 0 and string_bytes.getLast() == 0) { @@ -8483,6 +8549,14 @@ pub fn getOrPutTrailingString( try string_bytes.ensureUnusedCapacity(gpa, 1); } const key: []const u8 = string_bytes.items[str_index..]; + const has_embedded_null = std.mem.indexOfScalar(u8, key, 0) != null; + switch (embedded_nulls) { + .no_embedded_nulls => assert(!has_embedded_null), + .maybe_embedded_nulls => if (has_embedded_null) { + string_bytes.appendAssumeCapacity(0); + return @enumFromInt(str_index); + }, + } const gop = try ip.string_table.getOrPutContextAdapted(gpa, key, std.hash_map.StringIndexAdapter{ .bytes = string_bytes, }, std.hash_map.StringIndexContext{ @@ -8498,58 +8572,10 @@ pub fn getOrPutTrailingString( } } -/// Uses the last len bytes of ip.string_bytes as the key. -pub fn getTrailingAggregate( - ip: *InternPool, - gpa: Allocator, - ty: Index, - len: usize, -) Allocator.Error!Index { - try ip.items.ensureUnusedCapacity(gpa, 1); - try ip.extra.ensureUnusedCapacity(gpa, @typeInfo(Bytes).Struct.fields.len); - - const str: String = @enumFromInt(ip.string_bytes.items.len - len); - const adapter: KeyAdapter = .{ .intern_pool = ip }; - const gop = try ip.map.getOrPutAdapted(gpa, Key{ .aggregate = .{ - .ty = ty, - .storage = .{ .bytes = ip.string_bytes.items[@intFromEnum(str)..] }, - } }, adapter); - if (gop.found_existing) return @enumFromInt(gop.index); - - ip.items.appendAssumeCapacity(.{ - .tag = .bytes, - .data = ip.addExtraAssumeCapacity(Bytes{ - .ty = ty, - .bytes = str, - }), - }); - return @enumFromInt(ip.items.len - 1); -} - pub fn getString(ip: *InternPool, s: []const u8) OptionalNullTerminatedString { - if (ip.string_table.getKeyAdapted(s, std.hash_map.StringIndexAdapter{ + return if (ip.string_table.getKeyAdapted(s, std.hash_map.StringIndexAdapter{ .bytes = &ip.string_bytes, - })) |index| { - return @as(NullTerminatedString, @enumFromInt(index)).toOptional(); - } else { - return .none; - } -} - -pub fn stringToSlice(ip: *const InternPool, s: NullTerminatedString) [:0]const u8 { - const string_bytes = ip.string_bytes.items; - const start = @intFromEnum(s); - var end: usize = start; - while (string_bytes[end] != 0) end += 1; - return string_bytes[start..end :0]; -} - -pub fn stringToSliceUnwrap(ip: *const InternPool, s: OptionalNullTerminatedString) ?[:0]const u8 { - return ip.stringToSlice(s.unwrap() orelse return null); -} - -pub fn stringEqlSlice(ip: *const InternPool, a: NullTerminatedString, b: []const u8) bool { - return std.mem.eql(u8, stringToSlice(ip, a), b); + })) |index| @enumFromInt(index) else .none; } pub fn typeOf(ip: *const InternPool, index: Index) Index { @@ -8767,7 +8793,7 @@ pub fn aggregateTypeLenIncludingSentinel(ip: *const InternPool, ty: Index) u64 { return switch (ip.indexToKey(ty)) { .struct_type => ip.loadStructType(ty).field_types.len, .anon_struct_type => |anon_struct_type| anon_struct_type.types.len, - .array_type => |array_type| array_type.len + @intFromBool(array_type.sentinel != .none), + .array_type => |array_type| array_type.lenIncludingSentinel(), .vector_type => |vector_type| vector_type.len, else => unreachable, }; diff --git a/src/Module.zig b/src/Module.zig index 0399a2f85b..c4d7f43fe4 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -763,11 +763,11 @@ pub const Namespace = struct { ) !InternPool.NullTerminatedString { const ip = &zcu.intern_pool; const count = count: { - var count: usize = ip.stringToSlice(name).len + 1; + var count: usize = name.length(ip) + 1; var cur_ns = &ns; while (true) { const decl = zcu.declPtr(cur_ns.decl_index); - count += ip.stringToSlice(decl.name).len + 1; + count += decl.name.length(ip) + 1; cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse { count += ns.file_scope.sub_file_path.len; break :count count; @@ -793,7 +793,7 @@ pub const Namespace = struct { }; } - return ip.getOrPutTrailingString(gpa, ip.string_bytes.items.len - start); + return ip.getOrPutTrailingString(gpa, ip.string_bytes.items.len - start, .no_embedded_nulls); } pub fn getType(ns: Namespace, zcu: *Zcu) Type { @@ -980,17 +980,13 @@ pub const File = struct { const ip = &mod.intern_pool; const start = ip.string_bytes.items.len; try file.renderFullyQualifiedName(ip.string_bytes.writer(mod.gpa)); - return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start); + return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start, .no_embedded_nulls); } pub fn fullPath(file: File, ally: Allocator) ![]u8 { return file.mod.root.joinString(ally, file.sub_file_path); } - pub fn fullPathZ(file: File, ally: Allocator) ![:0]u8 { - return file.mod.root.joinStringZ(ally, file.sub_file_path); - } - pub fn dumpSrc(file: *File, src: LazySrcLoc) void { const loc = std.zig.findLineColumn(file.source.bytes, src); std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 }); @@ -2534,6 +2530,7 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { const name_ip = try zcu.intern_pool.getOrPutString( zcu.gpa, old_zir.nullTerminatedString(name_zir), + .no_embedded_nulls, ); try old_names.put(zcu.gpa, name_ip, {}); } @@ -2551,6 +2548,7 @@ fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { const name_ip = try zcu.intern_pool.getOrPutString( zcu.gpa, old_zir.nullTerminatedString(name_zir), + .no_embedded_nulls, ); if (!old_names.swapRemove(name_ip)) continue; // Name added @@ -3555,37 +3553,46 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { const gpa = mod.gpa; const zir = decl.getFileScope(mod).zir; - const builtin_type_target_index: InternPool.Index = blk: { + const builtin_type_target_index: InternPool.Index = ip_index: { const std_mod = mod.std_mod; - if (decl.getFileScope(mod).mod != std_mod) break :blk .none; + if (decl.getFileScope(mod).mod != std_mod) break :ip_index .none; // We're in the std module. const std_file = (try mod.importPkg(std_mod)).file; const std_decl = mod.declPtr(std_file.root_decl.unwrap().?); const std_namespace = std_decl.getInnerNamespace(mod).?; - const builtin_str = try ip.getOrPutString(gpa, "builtin"); - const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .zcu = mod }) orelse break :blk .none); - const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :blk .none; - if (decl.src_namespace != builtin_namespace) break :blk .none; + const builtin_str = try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls); + const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .zcu = mod }) orelse break :ip_index .none); + const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :ip_index .none; + if (decl.src_namespace != builtin_namespace) break :ip_index .none; // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index. - for ([_]struct { []const u8, InternPool.Index }{ - .{ "AtomicOrder", .atomic_order_type }, - .{ "AtomicRmwOp", .atomic_rmw_op_type }, - .{ "CallingConvention", .calling_convention_type }, - .{ "AddressSpace", .address_space_type }, - .{ "FloatMode", .float_mode_type }, - .{ "ReduceOp", .reduce_op_type }, - .{ "CallModifier", .call_modifier_type }, - .{ "PrefetchOptions", .prefetch_options_type }, - .{ "ExportOptions", .export_options_type }, - .{ "ExternOptions", .extern_options_type }, - .{ "Type", .type_info_type }, - }) |pair| { - const decl_name = ip.stringToSlice(decl.name); - if (std.mem.eql(u8, decl_name, pair[0])) { - break :blk pair[1]; - } + for ([_][]const u8{ + "AtomicOrder", + "AtomicRmwOp", + "CallingConvention", + "AddressSpace", + "FloatMode", + "ReduceOp", + "CallModifier", + "PrefetchOptions", + "ExportOptions", + "ExternOptions", + "Type", + }, [_]InternPool.Index{ + .atomic_order_type, + .atomic_rmw_op_type, + .calling_convention_type, + .address_space_type, + .float_mode_type, + .reduce_op_type, + .call_modifier_type, + .prefetch_options_type, + .export_options_type, + .extern_options_type, + .type_info_type, + }) |type_name, type_ip| { + if (decl.name.eqlSlice(type_name, ip)) break :ip_index type_ip; } - break :blk .none; + break :ip_index .none; }; mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .decl = decl_index })); @@ -3725,8 +3732,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { } else if (bytes.len == 0) { return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{}); } - const section = try ip.getOrPutString(gpa, bytes); - break :blk section.toOptional(); + break :blk try ip.getOrPutStringOpt(gpa, bytes, .no_embedded_nulls); }; decl.@"addrspace" = blk: { const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_val.toIntern())) { @@ -4101,7 +4107,10 @@ fn newEmbedFile( .sentinel = .zero_u8, .child = .u8_type, } }); - const array_val = try ip.getTrailingAggregate(gpa, array_ty, bytes.len); + const array_val = try ip.get(gpa, .{ .aggregate = .{ + .ty = array_ty, + .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, bytes.len, .maybe_embedded_nulls) }, + } }); const ptr_ty = (try mod.ptrType(.{ .child = array_ty, @@ -4111,7 +4120,6 @@ fn newEmbedFile( .address_space = .generic, }, })).toIntern(); - const ptr_val = try ip.get(gpa, .{ .ptr = .{ .ty = ptr_ty, .addr = .{ .anon_decl = .{ @@ -4122,7 +4130,7 @@ fn newEmbedFile( result.* = new_file; new_file.* = .{ - .sub_file_path = try ip.getOrPutString(gpa, sub_file_path), + .sub_file_path = try ip.getOrPutString(gpa, sub_file_path, .no_embedded_nulls), .owner = pkg, .stat = stat, .val = ptr_val, @@ -4214,11 +4222,11 @@ const ScanDeclIter = struct { const zcu = iter.zcu; const gpa = zcu.gpa; const ip = &zcu.intern_pool; - var name = try ip.getOrPutStringFmt(gpa, fmt, args); + var name = try ip.getOrPutStringFmt(gpa, fmt, args, .no_embedded_nulls); var gop = try iter.seen_decls.getOrPut(gpa, name); var next_suffix: u32 = 0; while (gop.found_existing) { - name = try ip.getOrPutStringFmt(gpa, fmt ++ "_{d}", args ++ .{next_suffix}); + name = try ip.getOrPutStringFmt(gpa, "{}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls); gop = try iter.seen_decls.getOrPut(gpa, name); next_suffix += 1; } @@ -4300,7 +4308,11 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void }; } else info: { if (iter.pass != .named) return; - const name = try ip.getOrPutString(gpa, zir.nullTerminatedString(declaration.name.toString(zir).?)); + const name = try ip.getOrPutString( + gpa, + zir.nullTerminatedString(declaration.name.toString(zir).?), + .no_embedded_nulls, + ); try iter.seen_decls.putNoClobber(gpa, name, {}); break :info .{ name, @@ -4362,9 +4374,10 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void if (!comp.config.is_test) break :a false; if (decl_mod != zcu.main_mod) break :a false; if (is_named_test and comp.test_filters.len > 0) { - const decl_fqn = ip.stringToSlice(try namespace.fullyQualifiedName(zcu, decl_name)); + const decl_fqn = try namespace.fullyQualifiedName(zcu, decl_name); + const decl_fqn_slice = decl_fqn.toSlice(ip); for (comp.test_filters) |test_filter| { - if (mem.indexOf(u8, decl_fqn, test_filter)) |_| break; + if (mem.indexOf(u8, decl_fqn_slice, test_filter)) |_| break; } else break :a false; } zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update @@ -4377,8 +4390,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void // `is_export` is unchanged. In this case, the incremental update mechanism will handle // re-analysis for us if necessary. if (prev_exported != declaration.flags.is_export or decl.analysis == .unreferenced) { - log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{s}' decl_index={d}", .{ - namespace.file_scope.sub_file_path, ip.stringToSlice(decl_name), decl_index, + log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{}' decl_index={d}", .{ + namespace.file_scope.sub_file_path, decl_name.fmt(ip), decl_index, }); comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = decl_index }); } @@ -5300,7 +5313,7 @@ pub fn populateTestFunctions( const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file; const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?); const builtin_namespace = mod.namespacePtr(root_decl.src_namespace); - const test_functions_str = try ip.getOrPutString(gpa, "test_functions"); + const test_functions_str = try ip.getOrPutString(gpa, "test_functions", .no_embedded_nulls); const decl_index = builtin_namespace.decls.getKeyAdapted( test_functions_str, DeclAdapter{ .zcu = mod }, @@ -5327,16 +5340,16 @@ pub fn populateTestFunctions( for (test_fn_vals, mod.test_functions.keys()) |*test_fn_val, test_decl_index| { const test_decl = mod.declPtr(test_decl_index); - const test_decl_name = try gpa.dupe(u8, ip.stringToSlice(try test_decl.fullyQualifiedName(mod))); - defer gpa.free(test_decl_name); + const test_decl_name = try test_decl.fullyQualifiedName(mod); + const test_decl_name_len = test_decl_name.length(ip); const test_name_anon_decl: InternPool.Key.Ptr.Addr.AnonDecl = n: { const test_name_ty = try mod.arrayType(.{ - .len = test_decl_name.len, + .len = test_decl_name_len, .child = .u8_type, }); const test_name_val = try mod.intern(.{ .aggregate = .{ .ty = test_name_ty.toIntern(), - .storage = .{ .bytes = test_decl_name }, + .storage = .{ .bytes = test_decl_name.toString() }, } }); break :n .{ .orig_ty = (try mod.singleConstPtrType(test_name_ty)).toIntern(), @@ -5354,7 +5367,7 @@ pub fn populateTestFunctions( } }), .len = try mod.intern(.{ .int = .{ .ty = .usize_type, - .storage = .{ .u64 = test_decl_name.len }, + .storage = .{ .u64 = test_decl_name_len }, } }), } }), // func diff --git a/src/Package.zig b/src/Package.zig index e173665e11..61f90727f3 100644 --- a/src/Package.zig +++ b/src/Package.zig @@ -2,3 +2,7 @@ pub const Module = @import("Package/Module.zig"); pub const Fetch = @import("Package/Fetch.zig"); pub const build_zig_basename = "build.zig"; pub const Manifest = @import("Package/Manifest.zig"); + +test { + _ = Fetch; +} diff --git a/src/Package/Fetch.zig b/src/Package/Fetch.zig index 9aeaaffea3..506075e921 100644 --- a/src/Package/Fetch.zig +++ b/src/Package/Fetch.zig @@ -339,12 +339,12 @@ pub fn run(f: *Fetch) RunError!void { .path_or_url => |path_or_url| { if (fs.cwd().openDir(path_or_url, .{ .iterate = true })) |dir| { var resource: Resource = .{ .dir = dir }; - return runResource(f, path_or_url, &resource, null); + return f.runResource(path_or_url, &resource, null); } else |dir_err| { const file_err = if (dir_err == error.NotDir) e: { if (fs.cwd().openFile(path_or_url, .{})) |file| { var resource: Resource = .{ .file = file }; - return runResource(f, path_or_url, &resource, null); + return f.runResource(path_or_url, &resource, null); } else |err| break :e err; } else dir_err; @@ -356,7 +356,7 @@ pub fn run(f: *Fetch) RunError!void { }; var server_header_buffer: [header_buffer_size]u8 = undefined; var resource = try f.initResource(uri, &server_header_buffer); - return runResource(f, uri.path, &resource, null); + return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, null); } }, }; @@ -418,7 +418,7 @@ pub fn run(f: *Fetch) RunError!void { ); var server_header_buffer: [header_buffer_size]u8 = undefined; var resource = try f.initResource(uri, &server_header_buffer); - return runResource(f, uri.path, &resource, remote.hash); + return f.runResource(try uri.path.toRawMaybeAlloc(arena), &resource, remote.hash); } pub fn deinit(f: *Fetch) void { @@ -461,14 +461,10 @@ fn runResource( }; defer tmp_directory.handle.close(); - // Unpack resource into tmp_directory. A non-null return value means - // that the package contents are inside a `pkg_dir` sub-directory. - const pkg_dir = try unpackResource(f, resource, uri_path, tmp_directory); + // Fetch and unpack a resource into a temporary directory. + var unpack_result = try unpackResource(f, resource, uri_path, tmp_directory); - var pkg_path: Cache.Path = .{ - .root_dir = tmp_directory, - .sub_path = if (pkg_dir) |pkg_dir_name| pkg_dir_name else "", - }; + var pkg_path: Cache.Path = .{ .root_dir = tmp_directory, .sub_path = unpack_result.root_dir }; // Apply btrfs workaround if needed. Reopen tmp_directory. if (native_os == .linux and f.job_queue.work_around_btrfs_bug) { @@ -488,10 +484,9 @@ fn runResource( .include_paths = if (f.manifest) |m| m.paths else .{}, }; - // TODO: - // If any error occurred for files that were ultimately excluded, those - // errors should be ignored, such as failure to create symlinks that - // weren't supposed to be included anyway. + // Ignore errors that were excluded by manifest, such as failure to + // create symlinks that weren't supposed to be included anyway. + try unpack_result.validate(f, filter); // Apply the manifest's inclusion rules to the temporary directory by // deleting excluded files. @@ -500,8 +495,8 @@ fn runResource( // directory. f.actual_hash = try computeHash(f, pkg_path, filter); - break :blk if (pkg_dir) |pkg_dir_name| - try fs.path.join(arena, &.{ tmp_dir_sub_path, pkg_dir_name }) + break :blk if (unpack_result.root_dir.len > 0) + try fs.path.join(arena, &.{ tmp_dir_sub_path, unpack_result.root_dir }) else tmp_dir_sub_path; }; @@ -902,13 +897,14 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re const arena = f.arena.allocator(); const eb = &f.error_bundle; - if (ascii.eqlIgnoreCase(uri.scheme, "file")) return .{ - .file = f.parent_package_root.openFile(uri.path, .{}) catch |err| { + if (ascii.eqlIgnoreCase(uri.scheme, "file")) { + const path = try uri.path.toRawMaybeAlloc(arena); + return .{ .file = f.parent_package_root.openFile(path, .{}) catch |err| { return f.fail(f.location_tok, try eb.printString("unable to open '{}{s}': {s}", .{ - f.parent_package_root, uri.path, @errorName(err), + f.parent_package_root, path, @errorName(err), })); - }, - }; + } }; + } const http_client = f.job_queue.http_client; @@ -925,7 +921,7 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re }; errdefer req.deinit(); // releases more than memory - req.send(.{}) catch |err| { + req.send() catch |err| { return f.fail(f.location_tok, try eb.printString( "HTTP request failed: {s}", .{@errorName(err)}, @@ -972,7 +968,8 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re }; const want_oid = want_oid: { - const want_ref = uri.fragment orelse "HEAD"; + const want_ref = + if (uri.fragment) |fragment| try fragment.toRawMaybeAlloc(arena) else "HEAD"; if (git.parseOid(want_ref)) |oid| break :want_oid oid else |_| {} const want_ref_head = try std.fmt.allocPrint(arena, "refs/heads/{s}", .{want_ref}); @@ -1044,16 +1041,12 @@ fn initResource(f: *Fetch, uri: std.Uri, server_header_buffer: []u8) RunError!Re )); } -/// A `null` return value indicates the `tmp_directory` is populated directly -/// with the package contents. -/// A non-null return value means that the package contents are inside a -/// sub-directory indicated by the named path. fn unpackResource( f: *Fetch, resource: *Resource, uri_path: []const u8, tmp_directory: Cache.Directory, -) RunError!?[]const u8 { +) RunError!UnpackResult { const eb = &f.error_bundle; const file_type = switch (resource.*) { .file => FileType.fromPath(uri_path) orelse @@ -1121,7 +1114,7 @@ fn unpackResource( .{ uri_path, @errorName(err) }, )); }; - return null; + return .{}; }, }; @@ -1156,27 +1149,22 @@ fn unpackResource( }); return try unpackTarball(f, tmp_directory.handle, dcp.reader()); }, - .git_pack => { - unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) { - error.FetchFailed => return error.FetchFailed, - error.OutOfMemory => return error.OutOfMemory, - else => |e| return f.fail(f.location_tok, try eb.printString( - "unable to unpack git files: {s}", - .{@errorName(e)}, - )), - }; - return null; + .git_pack => return unpackGitPack(f, tmp_directory.handle, resource) catch |err| switch (err) { + error.FetchFailed => return error.FetchFailed, + error.OutOfMemory => return error.OutOfMemory, + else => |e| return f.fail(f.location_tok, try eb.printString( + "unable to unpack git files: {s}", + .{@errorName(e)}, + )), }, } } -fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!?[]const u8 { +fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!UnpackResult { const eb = &f.error_bundle; const arena = f.arena.allocator(); - const gpa = f.arena.child_allocator; - var diagnostics: std.tar.Diagnostics = .{ .allocator = gpa }; - defer diagnostics.deinit(); + var diagnostics: std.tar.Diagnostics = .{ .allocator = arena }; std.tar.pipeToFileSystem(out_dir, reader, .{ .diagnostics = &diagnostics, @@ -1188,53 +1176,27 @@ fn unpackTarball(f: *Fetch, out_dir: fs.Dir, reader: anytype) RunError!?[]const .{@errorName(err)}, )); + var res: UnpackResult = .{ .root_dir = diagnostics.root_dir }; if (diagnostics.errors.items.len > 0) { - const notes_len: u32 = @intCast(diagnostics.errors.items.len); - try eb.addRootErrorMessage(.{ - .msg = try eb.addString("unable to unpack tarball"), - .src_loc = try f.srcLoc(f.location_tok), - .notes_len = notes_len, - }); - const notes_start = try eb.reserveNotes(notes_len); - for (diagnostics.errors.items, notes_start..) |item, note_i| { + try res.allocErrors(arena, diagnostics.errors.items.len, "unable to unpack tarball"); + for (diagnostics.errors.items) |item| { switch (item) { - .unable_to_create_sym_link => |info| { - eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("unable to create symlink from '{s}' to '{s}': {s}", .{ - info.file_name, info.link_name, @errorName(info.code), - }), - })); - }, - .unable_to_create_file => |info| { - eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("unable to create file '{s}': {s}", .{ - info.file_name, @errorName(info.code), - }), - })); - }, - .unsupported_file_type => |info| { - eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("file '{s}' has unsupported type '{c}'", .{ - info.file_name, @intFromEnum(info.file_type), - }), - })); - }, + .unable_to_create_file => |i| res.unableToCreateFile(stripRoot(i.file_name, res.root_dir), i.code), + .unable_to_create_sym_link => |i| res.unableToCreateSymLink(stripRoot(i.file_name, res.root_dir), i.link_name, i.code), + .unsupported_file_type => |i| res.unsupportedFileType(stripRoot(i.file_name, res.root_dir), @intFromEnum(i.file_type)), } } - return error.FetchFailed; } - - return if (diagnostics.root_dir) |root_dir| - return try arena.dupe(u8, root_dir) - else - null; + return res; } -fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void { - const eb = &f.error_bundle; +fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!UnpackResult { + const arena = f.arena.allocator(); const gpa = f.arena.child_allocator; const want_oid = resource.git.want_oid; const reader = resource.git.fetch_stream.reader(); + + var res: UnpackResult = .{}; // The .git directory is used to store the packfile and associated index, but // we do not attempt to replicate the exact structure of a real .git // directory, since that isn't relevant for fetching a package. @@ -1265,35 +1227,23 @@ fn unpackGitPack(f: *Fetch, out_dir: fs.Dir, resource: *Resource) anyerror!void checkout_prog_node.activate(); var repository = try git.Repository.init(gpa, pack_file, index_file); defer repository.deinit(); - var diagnostics: git.Diagnostics = .{ .allocator = gpa }; - defer diagnostics.deinit(); + var diagnostics: git.Diagnostics = .{ .allocator = arena }; try repository.checkout(out_dir, want_oid, &diagnostics); if (diagnostics.errors.items.len > 0) { - const notes_len: u32 = @intCast(diagnostics.errors.items.len); - try eb.addRootErrorMessage(.{ - .msg = try eb.addString("unable to unpack packfile"), - .src_loc = try f.srcLoc(f.location_tok), - .notes_len = notes_len, - }); - const notes_start = try eb.reserveNotes(notes_len); - for (diagnostics.errors.items, notes_start..) |item, note_i| { + try res.allocErrors(arena, diagnostics.errors.items.len, "unable to unpack packfile"); + for (diagnostics.errors.items) |item| { switch (item) { - .unable_to_create_sym_link => |info| { - eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ - .msg = try eb.printString("unable to create symlink from '{s}' to '{s}': {s}", .{ - info.file_name, info.link_name, @errorName(info.code), - }), - })); - }, + .unable_to_create_file => |i| res.unableToCreateFile(i.file_name, i.code), + .unable_to_create_sym_link => |i| res.unableToCreateSymLink(i.file_name, i.link_name, i.code), } } - return error.InvalidGitPack; } } } try out_dir.deleteTree(".git"); + return res; } fn recursiveDirectoryCopy(f: *Fetch, dir: fs.Dir, tmp_dir: fs.Dir) anyerror!void { @@ -1743,6 +1693,7 @@ const native_os = builtin.os.tag; test { _ = Filter; _ = FileType; + _ = UnpackResult; } // Detects executable header: ELF magic header or shebang line. @@ -1778,3 +1729,469 @@ test FileHeader { h.update(FileHeader.elf_magic[2..4]); try std.testing.expect(h.isExecutable()); } + +// Result of the `unpackResource` operation. Enables collecting errors from +// tar/git diagnostic, filtering that errors by manifest inclusion rules and +// emitting remaining errors to an `ErrorBundle`. +const UnpackResult = struct { + errors: []Error = undefined, + errors_count: usize = 0, + root_error_message: []const u8 = "", + + // A non empty value means that the package contents are inside a + // sub-directory indicated by the named path. + root_dir: []const u8 = "", + + const Error = union(enum) { + unable_to_create_sym_link: struct { + code: anyerror, + file_name: []const u8, + link_name: []const u8, + }, + unable_to_create_file: struct { + code: anyerror, + file_name: []const u8, + }, + unsupported_file_type: struct { + file_name: []const u8, + file_type: u8, + }, + + fn excluded(self: Error, filter: Filter) bool { + const file_name = switch (self) { + .unable_to_create_file => |info| info.file_name, + .unable_to_create_sym_link => |info| info.file_name, + .unsupported_file_type => |info| info.file_name, + }; + return !filter.includePath(file_name); + } + }; + + fn allocErrors(self: *UnpackResult, arena: std.mem.Allocator, n: usize, root_error_message: []const u8) !void { + self.root_error_message = try arena.dupe(u8, root_error_message); + self.errors = try arena.alloc(UnpackResult.Error, n); + } + + fn hasErrors(self: *UnpackResult) bool { + return self.errors_count > 0; + } + + fn unableToCreateFile(self: *UnpackResult, file_name: []const u8, err: anyerror) void { + self.errors[self.errors_count] = .{ .unable_to_create_file = .{ + .code = err, + .file_name = file_name, + } }; + self.errors_count += 1; + } + + fn unableToCreateSymLink(self: *UnpackResult, file_name: []const u8, link_name: []const u8, err: anyerror) void { + self.errors[self.errors_count] = .{ .unable_to_create_sym_link = .{ + .code = err, + .file_name = file_name, + .link_name = link_name, + } }; + self.errors_count += 1; + } + + fn unsupportedFileType(self: *UnpackResult, file_name: []const u8, file_type: u8) void { + self.errors[self.errors_count] = .{ .unsupported_file_type = .{ + .file_name = file_name, + .file_type = file_type, + } }; + self.errors_count += 1; + } + + fn validate(self: *UnpackResult, f: *Fetch, filter: Filter) !void { + if (self.errors_count == 0) return; + + var unfiltered_errors: u32 = 0; + for (self.errors) |item| { + if (item.excluded(filter)) continue; + unfiltered_errors += 1; + } + if (unfiltered_errors == 0) return; + + // Emmit errors to an `ErrorBundle`. + const eb = &f.error_bundle; + try eb.addRootErrorMessage(.{ + .msg = try eb.addString(self.root_error_message), + .src_loc = try f.srcLoc(f.location_tok), + .notes_len = unfiltered_errors, + }); + var note_i: u32 = try eb.reserveNotes(unfiltered_errors); + for (self.errors) |item| { + if (item.excluded(filter)) continue; + switch (item) { + .unable_to_create_sym_link => |info| { + eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ + .msg = try eb.printString("unable to create symlink from '{s}' to '{s}': {s}", .{ + info.file_name, info.link_name, @errorName(info.code), + }), + })); + }, + .unable_to_create_file => |info| { + eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ + .msg = try eb.printString("unable to create file '{s}': {s}", .{ + info.file_name, @errorName(info.code), + }), + })); + }, + .unsupported_file_type => |info| { + eb.extra.items[note_i] = @intFromEnum(try eb.addErrorMessage(.{ + .msg = try eb.printString("file '{s}' has unsupported type '{c}'", .{ + info.file_name, info.file_type, + }), + })); + }, + } + note_i += 1; + } + + return error.FetchFailed; + } + + test validate { + const gpa = std.testing.allocator; + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + // fill UnpackResult with errors + var res: UnpackResult = .{}; + try res.allocErrors(arena, 4, "unable to unpack"); + try std.testing.expectEqual(0, res.errors_count); + res.unableToCreateFile("dir1/file1", error.File1); + res.unableToCreateSymLink("dir2/file2", "filename", error.SymlinkError); + res.unableToCreateFile("dir1/file3", error.File3); + res.unsupportedFileType("dir2/file4", 'x'); + try std.testing.expectEqual(4, res.errors_count); + + // create filter, includes dir2, excludes dir1 + var filter: Filter = .{}; + try filter.include_paths.put(arena, "dir2", {}); + + // init Fetch + var fetch: Fetch = undefined; + fetch.parent_manifest_ast = null; + fetch.location_tok = 0; + try fetch.error_bundle.init(gpa); + defer fetch.error_bundle.deinit(); + + // validate errors with filter + try std.testing.expectError(error.FetchFailed, res.validate(&fetch, filter)); + + // output errors to string + var errors = try fetch.error_bundle.toOwnedBundle(""); + defer errors.deinit(gpa); + var out = std.ArrayList(u8).init(gpa); + defer out.deinit(); + try errors.renderToWriter(.{ .ttyconf = .no_color }, out.writer()); + try std.testing.expectEqualStrings( + \\error: unable to unpack + \\ note: unable to create symlink from 'dir2/file2' to 'filename': SymlinkError + \\ note: file 'dir2/file4' has unsupported type 'x' + \\ + , out.items); + } +}; + +test "tarball with duplicate paths" { + // This tarball has duplicate path 'dir1/file1' to simulate case sensitve + // file system on any file sytstem. + // + // duplicate_paths/ + // duplicate_paths/dir1/ + // duplicate_paths/dir1/file1 + // duplicate_paths/dir1/file1 + // duplicate_paths/build.zig.zon + // duplicate_paths/src/ + // duplicate_paths/src/main.zig + // duplicate_paths/src/root.zig + // duplicate_paths/build.zig + // + + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + const tarball_name = "duplicate_paths.tar.gz"; + try saveEmbedFile(tarball_name, tmp.dir); + const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); + defer gpa.free(tarball_path); + + // Run tarball fetch, expect to fail + var fb: TestFetchBuilder = undefined; + var fetch = try fb.build(gpa, tmp.dir, tarball_path); + defer fb.deinit(); + try std.testing.expectError(error.FetchFailed, fetch.run()); + + try fb.expectFetchErrors(1, + \\error: unable to unpack tarball + \\ note: unable to create file 'dir1/file1': PathAlreadyExists + \\ + ); +} + +test "tarball with excluded duplicate paths" { + // Same as previous tarball but has build.zig.zon wich excludes 'dir1'. + // + // .paths = .{ + // "build.zig", + // "build.zig.zon", + // "src", + // } + // + + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + const tarball_name = "duplicate_paths_excluded.tar.gz"; + try saveEmbedFile(tarball_name, tmp.dir); + const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); + defer gpa.free(tarball_path); + + // Run tarball fetch, should succeed + var fb: TestFetchBuilder = undefined; + var fetch = try fb.build(gpa, tmp.dir, tarball_path); + defer fb.deinit(); + try fetch.run(); + + const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash); + try std.testing.expectEqualStrings( + "12200bafe035cbb453dd717741b66e9f9d1e6c674069d06121dafa1b2e62eb6b22da", + &hex_digest, + ); + + const expected_files: []const []const u8 = &.{ + "build.zig", + "build.zig.zon", + "src/main.zig", + "src/root.zig", + }; + try fb.expectPackageFiles(expected_files); +} + +test "tarball without root folder" { + // Tarball with root folder. Manifest excludes dir1 and dir2. + // + // build.zig + // build.zig.zon + // dir1/ + // dir1/file2 + // dir1/file1 + // dir2/ + // dir2/file2 + // src/ + // src/main.zig + // + + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + const tarball_name = "no_root.tar.gz"; + try saveEmbedFile(tarball_name, tmp.dir); + const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); + defer gpa.free(tarball_path); + + // Run tarball fetch, should succeed + var fb: TestFetchBuilder = undefined; + var fetch = try fb.build(gpa, tmp.dir, tarball_path); + defer fb.deinit(); + try fetch.run(); + + const hex_digest = Package.Manifest.hexDigest(fetch.actual_hash); + try std.testing.expectEqualStrings( + "12209f939bfdcb8b501a61bb4a43124dfa1b2848adc60eec1e4624c560357562b793", + &hex_digest, + ); + + const expected_files: []const []const u8 = &.{ + "build.zig", + "build.zig.zon", + "src/main.zig", + }; + try fb.expectPackageFiles(expected_files); +} + +test "set executable bit based on file content" { + if (!std.fs.has_executable_bit) return error.SkipZigTest; + const gpa = std.testing.allocator; + var tmp = std.testing.tmpDir(.{}); + defer tmp.cleanup(); + + const tarball_name = "executables.tar.gz"; + try saveEmbedFile(tarball_name, tmp.dir); + const tarball_path = try std.fmt.allocPrint(gpa, "zig-cache/tmp/{s}/{s}", .{ tmp.sub_path, tarball_name }); + defer gpa.free(tarball_path); + + // $ tar -tvf executables.tar.gz + // drwxrwxr-x 0 executables/ + // -rwxrwxr-x 170 executables/hello + // lrwxrwxrwx 0 executables/hello_ln -> hello + // -rw-rw-r-- 0 executables/file1 + // -rw-rw-r-- 17 executables/script_with_shebang_without_exec_bit + // -rwxrwxr-x 7 executables/script_without_shebang + // -rwxrwxr-x 17 executables/script + + var fb: TestFetchBuilder = undefined; + var fetch = try fb.build(gpa, tmp.dir, tarball_path); + defer fb.deinit(); + + try fetch.run(); + try std.testing.expectEqualStrings( + "1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3", + &Manifest.hexDigest(fetch.actual_hash), + ); + + var out = try fb.packageDir(); + defer out.close(); + const S = std.posix.S; + // expect executable bit not set + try std.testing.expect((try out.statFile("file1")).mode & S.IXUSR == 0); + try std.testing.expect((try out.statFile("script_without_shebang")).mode & S.IXUSR == 0); + // expect executable bit set + try std.testing.expect((try out.statFile("hello")).mode & S.IXUSR != 0); + try std.testing.expect((try out.statFile("script")).mode & S.IXUSR != 0); + try std.testing.expect((try out.statFile("script_with_shebang_without_exec_bit")).mode & S.IXUSR != 0); + try std.testing.expect((try out.statFile("hello_ln")).mode & S.IXUSR != 0); + + // + // $ ls -al zig-cache/tmp/OCz9ovUcstDjTC_U/zig-global-cache/p/1220fecb4c06a9da8673c87fe8810e15785f1699212f01728eadce094d21effeeef3 + // -rw-rw-r-- 1 0 Apr file1 + // -rwxrwxr-x 1 170 Apr hello + // lrwxrwxrwx 1 5 Apr hello_ln -> hello + // -rwxrwxr-x 1 17 Apr script + // -rw-rw-r-- 1 7 Apr script_without_shebang + // -rwxrwxr-x 1 17 Apr script_with_shebang_without_exec_bit +} + +fn saveEmbedFile(comptime tarball_name: []const u8, dir: fs.Dir) !void { + //const tarball_name = "duplicate_paths_excluded.tar.gz"; + const tarball_content = @embedFile("Fetch/testdata/" ++ tarball_name); + var tmp_file = try dir.createFile(tarball_name, .{}); + defer tmp_file.close(); + try tmp_file.writeAll(tarball_content); +} + +// Builds Fetch with required dependencies, clears dependencies on deinit(). +const TestFetchBuilder = struct { + thread_pool: ThreadPool, + http_client: std.http.Client, + global_cache_directory: Cache.Directory, + progress: std.Progress, + job_queue: Fetch.JobQueue, + fetch: Fetch, + + fn build( + self: *TestFetchBuilder, + allocator: std.mem.Allocator, + cache_parent_dir: std.fs.Dir, + path_or_url: []const u8, + ) !*Fetch { + const cache_dir = try cache_parent_dir.makeOpenPath("zig-global-cache", .{}); + + try self.thread_pool.init(.{ .allocator = allocator }); + self.http_client = .{ .allocator = allocator }; + self.global_cache_directory = .{ .handle = cache_dir, .path = null }; + + self.progress = .{ .dont_print_on_dumb = true }; + + self.job_queue = .{ + .http_client = &self.http_client, + .thread_pool = &self.thread_pool, + .global_cache = self.global_cache_directory, + .recursive = false, + .read_only = false, + .debug_hash = false, + .work_around_btrfs_bug = false, + }; + + self.fetch = .{ + .arena = std.heap.ArenaAllocator.init(allocator), + .location = .{ .path_or_url = path_or_url }, + .location_tok = 0, + .hash_tok = 0, + .name_tok = 0, + .lazy_status = .eager, + .parent_package_root = Cache.Path{ .root_dir = Cache.Directory{ .handle = cache_dir, .path = null } }, + .parent_manifest_ast = null, + .prog_node = self.progress.start("Fetch", 0), + .job_queue = &self.job_queue, + .omit_missing_hash_error = true, + .allow_missing_paths_field = false, + + .package_root = undefined, + .error_bundle = undefined, + .manifest = null, + .manifest_ast = undefined, + .actual_hash = undefined, + .has_build_zig = false, + .oom_flag = false, + .module = null, + }; + return &self.fetch; + } + + fn deinit(self: *TestFetchBuilder) void { + self.fetch.deinit(); + self.job_queue.deinit(); + self.fetch.prog_node.end(); + self.global_cache_directory.handle.close(); + self.http_client.deinit(); + self.thread_pool.deinit(); + } + + fn packageDir(self: *TestFetchBuilder) !fs.Dir { + const root = self.fetch.package_root; + return try root.root_dir.handle.openDir(root.sub_path, .{ .iterate = true }); + } + + // Test helper, asserts thet package dir constains expected_files. + // expected_files must be sorted. + fn expectPackageFiles(self: *TestFetchBuilder, expected_files: []const []const u8) !void { + var package_dir = try self.packageDir(); + defer package_dir.close(); + + var actual_files: std.ArrayListUnmanaged([]u8) = .{}; + defer actual_files.deinit(std.testing.allocator); + defer for (actual_files.items) |file| std.testing.allocator.free(file); + var walker = try package_dir.walk(std.testing.allocator); + defer walker.deinit(); + while (try walker.next()) |entry| { + if (entry.kind != .file) continue; + const path = try std.testing.allocator.dupe(u8, entry.path); + errdefer std.testing.allocator.free(path); + std.mem.replaceScalar(u8, path, std.fs.path.sep, '/'); + try actual_files.append(std.testing.allocator, path); + } + std.mem.sortUnstable([]u8, actual_files.items, {}, struct { + fn lessThan(_: void, a: []u8, b: []u8) bool { + return std.mem.lessThan(u8, a, b); + } + }.lessThan); + + try std.testing.expectEqual(expected_files.len, actual_files.items.len); + for (expected_files, 0..) |file_name, i| { + try std.testing.expectEqualStrings(file_name, actual_files.items[i]); + } + try std.testing.expectEqualDeep(expected_files, actual_files.items); + } + + // Test helper, asserts that fetch has failed with `msg` error message. + fn expectFetchErrors(self: *TestFetchBuilder, notes_len: usize, msg: []const u8) !void { + var errors = try self.fetch.error_bundle.toOwnedBundle(""); + defer errors.deinit(std.testing.allocator); + + const em = errors.getErrorMessage(errors.getMessages()[0]); + try std.testing.expectEqual(1, em.count); + if (notes_len > 0) { + try std.testing.expectEqual(notes_len, em.notes_len); + } + var al = std.ArrayList(u8).init(std.testing.allocator); + defer al.deinit(); + try errors.renderToWriter(.{ .ttyconf = .no_color }, al.writer()); + try std.testing.expectEqualStrings(msg, al.items); + } +}; diff --git a/src/Package/Fetch/git.zig b/src/Package/Fetch/git.zig index 36652bd88c..a8c106412e 100644 --- a/src/Package/Fetch/git.zig +++ b/src/Package/Fetch/git.zig @@ -46,6 +46,10 @@ pub const Diagnostics = struct { file_name: []const u8, link_name: []const u8, }, + unable_to_create_file: struct { + code: anyerror, + file_name: []const u8, + }, }; pub fn deinit(d: *Diagnostics) void { @@ -55,6 +59,9 @@ pub const Diagnostics = struct { d.allocator.free(info.file_name); d.allocator.free(info.link_name); }, + .unable_to_create_file => |info| { + d.allocator.free(info.file_name); + }, } } d.errors.deinit(d.allocator); @@ -119,11 +126,19 @@ pub const Repository = struct { try repository.checkoutTree(subdir, entry.oid, sub_path, diagnostics); }, .file => { - var file = try dir.createFile(entry.name, .{}); - defer file.close(); try repository.odb.seekOid(entry.oid); const file_object = try repository.odb.readObject(); if (file_object.type != .blob) return error.InvalidFile; + var file = dir.createFile(entry.name, .{ .exclusive = true }) catch |e| { + const file_name = try std.fs.path.join(diagnostics.allocator, &.{ current_path, entry.name }); + errdefer diagnostics.allocator.free(file_name); + try diagnostics.errors.append(diagnostics.allocator, .{ .unable_to_create_file = .{ + .code = e, + .file_name = file_name, + } }); + continue; + }; + defer file.close(); try file.writeAll(file_object.data); try file.sync(); }, @@ -525,9 +540,13 @@ pub const Session = struct { http_headers_buffer: []u8, ) !CapabilityIterator { var info_refs_uri = session.uri; - info_refs_uri.path = try std.fs.path.resolvePosix(allocator, &.{ "/", session.uri.path, "info/refs" }); - defer allocator.free(info_refs_uri.path); - info_refs_uri.query = "service=git-upload-pack"; + { + const session_uri_path = try std.fmt.allocPrint(allocator, "{path}", .{session.uri.path}); + defer allocator.free(session_uri_path); + info_refs_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(allocator, &.{ "/", session_uri_path, "info/refs" }) }; + } + defer allocator.free(info_refs_uri.path.percent_encoded); + info_refs_uri.query = .{ .percent_encoded = "service=git-upload-pack" }; info_refs_uri.fragment = null; const max_redirects = 3; @@ -539,16 +558,18 @@ pub const Session = struct { }, }); errdefer request.deinit(); - try request.send(.{}); + try request.send(); try request.finish(); try request.wait(); if (request.response.status != .ok) return error.ProtocolError; const any_redirects_occurred = request.redirect_behavior.remaining() < max_redirects; if (any_redirects_occurred) { - if (!mem.endsWith(u8, request.uri.path, "/info/refs")) return error.UnparseableRedirect; + const request_uri_path = try std.fmt.allocPrint(allocator, "{path}", .{request.uri.path}); + defer allocator.free(request_uri_path); + if (!mem.endsWith(u8, request_uri_path, "/info/refs")) return error.UnparseableRedirect; var new_uri = request.uri; - new_uri.path = new_uri.path[0 .. new_uri.path.len - "/info/refs".len]; + new_uri.path = .{ .percent_encoded = request_uri_path[0 .. request_uri_path.len - "/info/refs".len] }; new_uri.query = null; redirect_uri.* = try std.fmt.allocPrint(allocator, "{+/}", .{new_uri}); return error.Redirected; @@ -630,8 +651,12 @@ pub const Session = struct { /// Returns an iterator over refs known to the server. pub fn listRefs(session: Session, allocator: Allocator, options: ListRefsOptions) !RefIterator { var upload_pack_uri = session.uri; - upload_pack_uri.path = try std.fs.path.resolvePosix(allocator, &.{ "/", session.uri.path, "git-upload-pack" }); - defer allocator.free(upload_pack_uri.path); + { + const session_uri_path = try std.fmt.allocPrint(allocator, "{path}", .{session.uri.path}); + defer allocator.free(session_uri_path); + upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; + } + defer allocator.free(upload_pack_uri.path.percent_encoded); upload_pack_uri.query = null; upload_pack_uri.fragment = null; @@ -666,7 +691,7 @@ pub const Session = struct { }); errdefer request.deinit(); request.transfer_encoding = .{ .content_length = body.items.len }; - try request.send(.{}); + try request.send(); try request.writeAll(body.items); try request.finish(); @@ -733,8 +758,12 @@ pub const Session = struct { http_headers_buffer: []u8, ) !FetchStream { var upload_pack_uri = session.uri; - upload_pack_uri.path = try std.fs.path.resolvePosix(allocator, &.{ "/", session.uri.path, "git-upload-pack" }); - defer allocator.free(upload_pack_uri.path); + { + const session_uri_path = try std.fmt.allocPrint(allocator, "{path}", .{session.uri.path}); + defer allocator.free(session_uri_path); + upload_pack_uri.path = .{ .percent_encoded = try std.fs.path.resolvePosix(allocator, &.{ "/", session_uri_path, "git-upload-pack" }) }; + } + defer allocator.free(upload_pack_uri.path.percent_encoded); upload_pack_uri.query = null; upload_pack_uri.fragment = null; @@ -771,7 +800,7 @@ pub const Session = struct { }); errdefer request.deinit(); request.transfer_encoding = .{ .content_length = body.items.len }; - try request.send(.{}); + try request.send(); try request.writeAll(body.items); try request.finish(); diff --git a/src/Package/Fetch/testdata/duplicate_paths.tar.gz b/src/Package/Fetch/testdata/duplicate_paths.tar.gz Binary files differnew file mode 100644 index 0000000000..118a934c1b --- /dev/null +++ b/src/Package/Fetch/testdata/duplicate_paths.tar.gz diff --git a/src/Package/Fetch/testdata/duplicate_paths_excluded.tar.gz b/src/Package/Fetch/testdata/duplicate_paths_excluded.tar.gz Binary files differnew file mode 100644 index 0000000000..760b37cd40 --- /dev/null +++ b/src/Package/Fetch/testdata/duplicate_paths_excluded.tar.gz diff --git a/src/Package/Fetch/testdata/executables.tar.gz b/src/Package/Fetch/testdata/executables.tar.gz Binary files differnew file mode 100644 index 0000000000..abc650801e --- /dev/null +++ b/src/Package/Fetch/testdata/executables.tar.gz diff --git a/src/Package/Fetch/testdata/no_root.tar.gz b/src/Package/Fetch/testdata/no_root.tar.gz Binary files differnew file mode 100644 index 0000000000..a3a4baf40f --- /dev/null +++ b/src/Package/Fetch/testdata/no_root.tar.gz diff --git a/src/Sema.zig b/src/Sema.zig index d64226fef0..d3989f630c 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -2059,12 +2059,12 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) const st_ptr = try err_trace_block.addTy(.alloc, try mod.singleMutPtrType(stack_trace_ty)); // st.instruction_addresses = &addrs; - const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses"); + const instruction_addresses_field_name = try ip.getOrPutString(gpa, "instruction_addresses", .no_embedded_nulls); const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); // st.index = 0; - const index_field_name = try ip.getOrPutString(gpa, "index"); + const index_field_name = try ip.getOrPutString(gpa, "index", .no_embedded_nulls); const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); @@ -2348,13 +2348,13 @@ fn failWithInvalidFieldAccess( fn typeSupportsFieldAccess(mod: *const Module, ty: Type, field_name: InternPool.NullTerminatedString) bool { const ip = &mod.intern_pool; switch (ty.zigTypeTag(mod)) { - .Array => return ip.stringEqlSlice(field_name, "len"), + .Array => return field_name.eqlSlice("len", ip), .Pointer => { const ptr_info = ty.ptrInfo(mod); if (ptr_info.flags.size == .Slice) { - return ip.stringEqlSlice(field_name, "ptr") or ip.stringEqlSlice(field_name, "len"); + return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip); } else if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Array) { - return ip.stringEqlSlice(field_name, "len"); + return field_name.eqlSlice("len", ip); } else return false; }, .Type, .Struct, .Union => return true, @@ -2703,12 +2703,20 @@ fn getCaptures(sema: *Sema, block: *Block, type_src: LazySrcLoc, extra_index: us break :capture .{ .runtime = sema.typeOf(air_ref).toIntern() }; }), .decl_val => |str| capture: { - const decl_name = try ip.getOrPutString(sema.gpa, sema.code.nullTerminatedString(str)); + const decl_name = try ip.getOrPutString( + sema.gpa, + sema.code.nullTerminatedString(str), + .no_embedded_nulls, + ); const decl = try sema.lookupIdentifier(block, .unneeded, decl_name); // TODO: could we need this src loc? break :capture InternPool.CaptureValue.wrap(.{ .decl_val = decl }); }, .decl_ref => |str| capture: { - const decl_name = try ip.getOrPutString(sema.gpa, sema.code.nullTerminatedString(str)); + const decl_name = try ip.getOrPutString( + sema.gpa, + sema.code.nullTerminatedString(str), + .no_embedded_nulls, + ); const decl = try sema.lookupIdentifier(block, .unneeded, decl_name); // TODO: could we need this src loc? break :capture InternPool.CaptureValue.wrap(.{ .decl_ref = decl }); }, @@ -2882,7 +2890,7 @@ fn createAnonymousDeclTypeNamed( const name = mod.intern_pool.getOrPutStringFmt(gpa, "{}__{s}_{d}", .{ src_decl.name.fmt(&mod.intern_pool), anon_prefix, @intFromEnum(new_decl_index), - }) catch unreachable; + }, .no_embedded_nulls) catch unreachable; try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, val, name); return new_decl_index; }, @@ -2923,7 +2931,7 @@ fn createAnonymousDeclTypeNamed( }; try writer.writeByte(')'); - const name = try mod.intern_pool.getOrPutString(gpa, buf.items); + const name = try mod.intern_pool.getOrPutString(gpa, buf.items, .no_embedded_nulls); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, val, name); return new_decl_index; }, @@ -2937,8 +2945,7 @@ fn createAnonymousDeclTypeNamed( const name = try mod.intern_pool.getOrPutStringFmt(gpa, "{}.{s}", .{ src_decl.name.fmt(&mod.intern_pool), zir_data[i].str_op.getStr(sema.code), - }); - + }, .no_embedded_nulls); try mod.initNewAnonDecl(new_decl_index, src_decl.src_line, val, name); return new_decl_index; }, @@ -3157,7 +3164,7 @@ fn zirEnumDecl( const field_name_zir = sema.code.nullTerminatedString(field_name_index); extra_index += 2; // field name, doc comment - const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir); + const field_name = try mod.intern_pool.getOrPutString(gpa, field_name_zir, .no_embedded_nulls); const tag_overflow = if (has_tag_value) overflow: { const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]); @@ -3462,7 +3469,7 @@ fn zirErrorSetDecl( while (extra_index < extra_index_end) : (extra_index += 2) { // +2 to skip over doc_string const name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]); const name = sema.code.nullTerminatedString(name_index); - const name_ip = try mod.intern_pool.getOrPutString(gpa, name); + const name_ip = try mod.intern_pool.getOrPutString(gpa, name, .no_embedded_nulls); _ = try mod.getErrorValue(name_ip); const result = names.getOrPutAssumeCapacity(name_ip); assert(!result.found_existing); // verified in AstGen @@ -3635,7 +3642,7 @@ fn indexablePtrLen( const is_pointer_to = object_ty.isSinglePointer(mod); const indexable_ty = if (is_pointer_to) object_ty.childType(mod) else object_ty; try checkIndexable(sema, block, src, indexable_ty); - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len", .no_embedded_nulls); return sema.fieldVal(block, src, object, field_name, src); } @@ -3649,7 +3656,7 @@ fn indexablePtrLenOrNone( const operand_ty = sema.typeOf(operand); try checkMemOperand(sema, block, src, operand_ty); if (operand_ty.ptrSize(mod) == .Many) return .none; - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len"); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "len", .no_embedded_nulls); return sema.fieldVal(block, src, operand, field_name, src); } @@ -4363,7 +4370,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. } if (!object_ty.indexableHasLen(mod)) continue; - break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len"), arg_src); + break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, "len", .no_embedded_nulls), arg_src); }; const arg_len = try sema.coerce(block, Type.usize, arg_len_uncoerced, arg_src); if (len == .none) { @@ -4747,7 +4754,11 @@ fn validateUnionInit( const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node }; const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; - const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start)); + const field_name = try mod.intern_pool.getOrPutString( + gpa, + sema.code.nullTerminatedString(field_ptr_extra.field_name_start), + .no_embedded_nulls, + ); const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); const air_tags = sema.air_instructions.items(.tag); const air_datas = sema.air_instructions.items(.data); @@ -4890,6 +4901,7 @@ fn validateStructInit( const field_name = try ip.getOrPutString( gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start), + .no_embedded_nulls, ); field_index.* = if (struct_ty.isTuple(mod)) try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) @@ -5672,25 +5684,26 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code); - return sema.addStrLitNoAlias(bytes); + return sema.addStrLit( + try sema.mod.intern_pool.getOrPutString(sema.gpa, bytes, .maybe_embedded_nulls), + bytes.len, + ); } -fn addStrLit(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { - const duped_bytes = try sema.arena.dupe(u8, bytes); - return addStrLitNoAlias(sema, duped_bytes); +fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref { + return sema.addStrLit(string.toString(), string.length(&sema.mod.intern_pool)); } -/// Safe to call when `bytes` does not point into `InternPool`. -fn addStrLitNoAlias(sema: *Sema, bytes: []const u8) CompileError!Air.Inst.Ref { +fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref { const mod = sema.mod; const array_ty = try mod.arrayType(.{ - .len = bytes.len, + .len = len, .sentinel = .zero_u8, .child = .u8_type, }); const val = try mod.intern(.{ .aggregate = .{ .ty = array_ty.toIntern(), - .storage = .{ .bytes = bytes }, + .storage = .{ .bytes = string }, } }); return anonDeclRef(sema, val); } @@ -6370,7 +6383,11 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const src = inst_data.src(); const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; - const decl_name = try mod.intern_pool.getOrPutString(mod.gpa, sema.code.nullTerminatedString(extra.decl_name)); + const decl_name = try mod.intern_pool.getOrPutString( + mod.gpa, + sema.code.nullTerminatedString(extra.decl_name), + .no_embedded_nulls, + ); const decl_index = if (extra.namespace != .none) index_blk: { const container_ty = try sema.resolveType(block, operand_src, extra.namespace); const container_namespace = container_ty.getNamespaceIndex(mod); @@ -6721,7 +6738,11 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; const src = inst_data.src(); - const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + const decl_name = try mod.intern_pool.getOrPutString( + sema.gpa, + inst_data.get(sema.code), + .no_embedded_nulls, + ); const decl_index = try sema.lookupIdentifier(block, src, decl_name); try sema.addReferencedBy(block, src, decl_index); return sema.analyzeDeclRef(decl_index); @@ -6731,7 +6752,11 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; const src = inst_data.src(); - const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + const decl_name = try mod.intern_pool.getOrPutString( + sema.gpa, + inst_data.get(sema.code), + .no_embedded_nulls, + ); const decl = try sema.lookupIdentifier(block, src, decl_name); return sema.analyzeDeclVal(block, src, decl); } @@ -6883,7 +6908,7 @@ pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref error.NeededSourceLocation, error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, else => |e| return e, }; - const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); + const field_name = try mod.intern_pool.getOrPutString(gpa, "index", .no_embedded_nulls); const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, .unneeded) catch |err| switch (err) { error.AnalysisFail, error.NeededSourceLocation => @panic("std.builtin.StackTrace is corrupt"), error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, @@ -6926,7 +6951,7 @@ fn popErrorReturnTrace( try sema.resolveTypeFields(stack_trace_ty); const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); - const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); + const field_name = try mod.intern_pool.getOrPutString(gpa, "index", .no_embedded_nulls); const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty, true); try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); } else if (is_non_error == null) { @@ -6952,7 +6977,7 @@ fn popErrorReturnTrace( try sema.resolveTypeFields(stack_trace_ty); const ptr_stack_trace_ty = try mod.singleMutPtrType(stack_trace_ty); const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); - const field_name = try mod.intern_pool.getOrPutString(gpa, "index"); + const field_name = try mod.intern_pool.getOrPutString(gpa, "index", .no_embedded_nulls); const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty, true); try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); _ = try then_block.addBr(cond_block_inst, .void_value); @@ -7010,7 +7035,11 @@ fn zirCall( .direct => .{ .direct = try sema.resolveInst(extra.data.callee) }, .field => blk: { const object_ptr = try sema.resolveInst(extra.data.obj_ptr); - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.data.field_name_start)); + const field_name = try mod.intern_pool.getOrPutString( + sema.gpa, + sema.code.nullTerminatedString(extra.data.field_name_start), + .no_embedded_nulls, + ); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); }, @@ -7073,7 +7102,7 @@ fn zirCall( if (input_is_error or (pop_error_return_trace and return_ty.isError(mod))) { const stack_trace_ty = try sema.getBuiltinType("StackTrace"); try sema.resolveTypeFields(stack_trace_ty); - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index"); + const field_name = try mod.intern_pool.getOrPutString(sema.gpa, "index", .no_embedded_nulls); const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); // Insert a save instruction before the arg resolution + call instructions we just generated @@ -8648,7 +8677,11 @@ fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! _ = block; const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; - const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + const name = try mod.intern_pool.getOrPutString( + sema.gpa, + inst_data.get(sema.code), + .no_embedded_nulls, + ); _ = try mod.getErrorValue(name); // Create an error set type with only this error value, and return the value. const error_set_type = try mod.singleErrorSetType(name); @@ -8804,7 +8837,7 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; const name = inst_data.get(sema.code); return Air.internedToRef((try mod.intern(.{ - .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name), + .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name, .no_embedded_nulls), }))); } @@ -9761,7 +9794,7 @@ fn funcCommon( const func_index = try ip.getExternFunc(gpa, .{ .ty = func_ty, .decl = sema.owner_decl_index, - .lib_name = try mod.intern_pool.getOrPutStringOpt(gpa, opt_lib_name), + .lib_name = try mod.intern_pool.getOrPutStringOpt(gpa, opt_lib_name, .no_embedded_nulls), }); return finishFunc( sema, @@ -10225,7 +10258,11 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); + const field_name = try mod.intern_pool.getOrPutString( + sema.gpa, + sema.code.nullTerminatedString(extra.field_name_start), + .no_embedded_nulls, + ); const object = try sema.resolveInst(extra.lhs); return sema.fieldVal(block, src, object, field_name, field_name_src); } @@ -10239,7 +10276,11 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); + const field_name = try mod.intern_pool.getOrPutString( + sema.gpa, + sema.code.nullTerminatedString(extra.field_name_start), + .no_embedded_nulls, + ); const object_ptr = try sema.resolveInst(extra.lhs); return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); } @@ -10253,7 +10294,11 @@ fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi const src = inst_data.src(); const field_name_src: LazySrcLoc = .{ .node_offset_field_name_init = inst_data.src_node }; const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; - const field_name = try mod.intern_pool.getOrPutString(sema.gpa, sema.code.nullTerminatedString(extra.field_name_start)); + const field_name = try mod.intern_pool.getOrPutString( + sema.gpa, + sema.code.nullTerminatedString(extra.field_name_start), + .no_embedded_nulls, + ); const object_ptr = try sema.resolveInst(extra.lhs); const struct_ty = sema.typeOf(object_ptr).childType(mod); switch (struct_ty.zigTypeTag(mod)) { @@ -13759,8 +13804,8 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai switch (ip.indexToKey(ty.toIntern())) { .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .Slice => { - if (ip.stringEqlSlice(field_name, "ptr")) break :hf true; - if (ip.stringEqlSlice(field_name, "len")) break :hf true; + if (field_name.eqlSlice("ptr", ip)) break :hf true; + if (field_name.eqlSlice("len", ip)) break :hf true; break :hf false; }, else => {}, @@ -13783,7 +13828,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .enum_type => { break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null; }, - .array_type => break :hf ip.stringEqlSlice(field_name, "len"), + .array_type => break :hf field_name.eqlSlice("len", ip), else => {}, } return sema.fail(block, ty_src, "type '{}' does not support '@hasField'", .{ @@ -13885,7 +13930,11 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; - const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + const name = try mod.intern_pool.getOrPutString( + sema.gpa, + inst_data.get(sema.code), + .no_embedded_nulls, + ); _ = try mod.getErrorValue(name); const error_set_type = try mod.singleErrorSetType(name); return Air.internedToRef((try mod.intern(.{ .err = .{ @@ -17552,11 +17601,9 @@ fn zirBuiltinSrc( const gpa = sema.gpa; const func_name_val = v: { - // This dupe prevents InternPool string pool memory from being reallocated - // while a reference exists. - const bytes = try sema.arena.dupe(u8, ip.stringToSlice(fn_owner_decl.name)); + const func_name_len = fn_owner_decl.name.length(ip); const array_ty = try ip.get(gpa, .{ .array_type = .{ - .len = bytes.len, + .len = func_name_len, .sentinel = .zero_u8, .child = .u8_type, } }); @@ -17568,19 +17615,19 @@ fn zirBuiltinSrc( .orig_ty = .slice_const_u8_sentinel_0_type, .val = try ip.get(gpa, .{ .aggregate = .{ .ty = array_ty, - .storage = .{ .bytes = bytes }, + .storage = .{ .bytes = fn_owner_decl.name.toString() }, } }), } }, } }), - .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), + .len = (try mod.intValue(Type.usize, func_name_len)).toIntern(), } }); }; const file_name_val = v: { // The compiler must not call realpath anywhere. - const bytes = try fn_owner_decl.getFileScope(mod).fullPathZ(sema.arena); + const file_name = try fn_owner_decl.getFileScope(mod).fullPath(sema.arena); const array_ty = try ip.get(gpa, .{ .array_type = .{ - .len = bytes.len, + .len = file_name.len, .sentinel = .zero_u8, .child = .u8_type, } }); @@ -17592,11 +17639,13 @@ fn zirBuiltinSrc( .orig_ty = .slice_const_u8_sentinel_0_type, .val = try ip.get(gpa, .{ .aggregate = .{ .ty = array_ty, - .storage = .{ .bytes = bytes }, + .storage = .{ + .bytes = try ip.getOrPutString(gpa, file_name, .maybe_embedded_nulls), + }, } }), } }, } }), - .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), + .len = (try mod.intValue(Type.usize, file_name.len)).toIntern(), } }); }; @@ -17651,7 +17700,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Fn"), + try ip.getOrPutString(gpa, "Fn", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(fn_info_decl_index); const fn_info_decl = mod.declPtr(fn_info_decl_index); @@ -17661,7 +17710,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, fn_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Param"), + try ip.getOrPutString(gpa, "Param", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(param_info_decl_index); const param_info_decl = mod.declPtr(param_info_decl_index); @@ -17762,7 +17811,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Int"), + try ip.getOrPutString(gpa, "Int", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(int_info_decl_index); const int_info_decl = mod.declPtr(int_info_decl_index); @@ -17790,7 +17839,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Float"), + try ip.getOrPutString(gpa, "Float", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(float_info_decl_index); const float_info_decl = mod.declPtr(float_info_decl_index); @@ -17822,7 +17871,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, (try sema.getBuiltinType("Type")).getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Pointer"), + try ip.getOrPutString(gpa, "Pointer", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(decl_index); const decl = mod.declPtr(decl_index); @@ -17833,7 +17882,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, pointer_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Size"), + try ip.getOrPutString(gpa, "Size", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(decl_index); const decl = mod.declPtr(decl_index); @@ -17876,7 +17925,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Array"), + try ip.getOrPutString(gpa, "Array", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(array_field_ty_decl_index); const array_field_ty_decl = mod.declPtr(array_field_ty_decl_index); @@ -17907,7 +17956,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Vector"), + try ip.getOrPutString(gpa, "Vector", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(vector_field_ty_decl_index); const vector_field_ty_decl = mod.declPtr(vector_field_ty_decl_index); @@ -17936,7 +17985,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Optional"), + try ip.getOrPutString(gpa, "Optional", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(optional_field_ty_decl_index); const optional_field_ty_decl = mod.declPtr(optional_field_ty_decl_index); @@ -17963,7 +18012,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Error"), + try ip.getOrPutString(gpa, "Error", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(set_field_ty_decl_index); const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index); @@ -17980,18 +18029,18 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai else => |err_set_ty_index| blk: { const names = ip.indexToKey(err_set_ty_index).error_set_type.names; const vals = try sema.arena.alloc(InternPool.Index, names.len); - for (vals, 0..) |*field_val, i| { - // TODO: write something like getCoercedInts to avoid needing to dupe - const name = try sema.arena.dupeZ(u8, ip.stringToSlice(names.get(ip)[i])); - const name_val = v: { + for (vals, 0..) |*field_val, error_index| { + const error_name = names.get(ip)[error_index]; + const error_name_len = error_name.length(ip); + const error_name_val = v: { const new_decl_ty = try mod.arrayType(.{ - .len = name.len, + .len = error_name_len, .sentinel = .zero_u8, .child = .u8_type, }); const new_decl_val = try mod.intern(.{ .aggregate = .{ .ty = new_decl_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = error_name.toString() }, } }); break :v try mod.intern(.{ .slice = .{ .ty = .slice_const_u8_sentinel_0_type, @@ -18002,13 +18051,13 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .orig_ty = .slice_const_u8_sentinel_0_type, } }, } }), - .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + .len = (try mod.intValue(Type.usize, error_name_len)).toIntern(), } }); }; const error_field_fields = .{ // name: [:0]const u8, - name_val, + error_name_val, }; field_val.* = try mod.intern(.{ .aggregate = .{ .ty = error_field_ty.toIntern(), @@ -18069,7 +18118,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "ErrorUnion"), + try ip.getOrPutString(gpa, "ErrorUnion", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(error_union_field_ty_decl_index); const error_union_field_ty_decl = mod.declPtr(error_union_field_ty_decl_index); @@ -18099,7 +18148,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "EnumField"), + try ip.getOrPutString(gpa, "EnumField", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(enum_field_ty_decl_index); const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index); @@ -18107,27 +18156,29 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai }; const enum_field_vals = try sema.arena.alloc(InternPool.Index, ip.loadEnumType(ty.toIntern()).names.len); - for (enum_field_vals, 0..) |*field_val, i| { + for (enum_field_vals, 0..) |*field_val, tag_index| { const enum_type = ip.loadEnumType(ty.toIntern()); const value_val = if (enum_type.values.len > 0) try mod.intern_pool.getCoercedInts( mod.gpa, - mod.intern_pool.indexToKey(enum_type.values.get(ip)[i]).int, + mod.intern_pool.indexToKey(enum_type.values.get(ip)[tag_index]).int, .comptime_int_type, ) else - (try mod.intValue(Type.comptime_int, i)).toIntern(); + (try mod.intValue(Type.comptime_int, tag_index)).toIntern(); + // TODO: write something like getCoercedInts to avoid needing to dupe - const name = try sema.arena.dupeZ(u8, ip.stringToSlice(enum_type.names.get(ip)[i])); const name_val = v: { + const tag_name = enum_type.names.get(ip)[tag_index]; + const tag_name_len = tag_name.length(ip); const new_decl_ty = try mod.arrayType(.{ - .len = name.len, + .len = tag_name_len, .sentinel = .zero_u8, .child = .u8_type, }); const new_decl_val = try mod.intern(.{ .aggregate = .{ .ty = new_decl_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = tag_name.toString() }, } }); break :v try mod.intern(.{ .slice = .{ .ty = .slice_const_u8_sentinel_0_type, @@ -18138,7 +18189,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .orig_ty = .slice_const_u8_sentinel_0_type, } }, } }), - .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + .len = (try mod.intValue(Type.usize, tag_name_len)).toIntern(), } }); }; @@ -18191,7 +18242,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Enum"), + try ip.getOrPutString(gpa, "Enum", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(type_enum_ty_decl_index); const type_enum_ty_decl = mod.declPtr(type_enum_ty_decl_index); @@ -18223,7 +18274,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Union"), + try ip.getOrPutString(gpa, "Union", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(type_union_ty_decl_index); const type_union_ty_decl = mod.declPtr(type_union_ty_decl_index); @@ -18235,7 +18286,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "UnionField"), + try ip.getOrPutString(gpa, "UnionField", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(union_field_ty_decl_index); const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index); @@ -18250,18 +18301,18 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const union_field_vals = try gpa.alloc(InternPool.Index, tag_type.names.len); defer gpa.free(union_field_vals); - for (union_field_vals, 0..) |*field_val, i| { - // TODO: write something like getCoercedInts to avoid needing to dupe - const name = try sema.arena.dupeZ(u8, ip.stringToSlice(tag_type.names.get(ip)[i])); + for (union_field_vals, 0..) |*field_val, field_index| { const name_val = v: { + const field_name = tag_type.names.get(ip)[field_index]; + const field_name_len = field_name.length(ip); const new_decl_ty = try mod.arrayType(.{ - .len = name.len, + .len = field_name_len, .sentinel = .zero_u8, .child = .u8_type, }); const new_decl_val = try mod.intern(.{ .aggregate = .{ .ty = new_decl_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = field_name.toString() }, } }); break :v try mod.intern(.{ .slice = .{ .ty = .slice_const_u8_sentinel_0_type, @@ -18272,16 +18323,16 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .orig_ty = .slice_const_u8_sentinel_0_type, } }, } }), - .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + .len = (try mod.intValue(Type.usize, field_name_len)).toIntern(), } }); }; const alignment = switch (layout) { - .auto, .@"extern" => try sema.unionFieldAlignment(union_obj, @intCast(i)), + .auto, .@"extern" => try sema.unionFieldAlignment(union_obj, @intCast(field_index)), .@"packed" => .none, }; - const field_ty = union_obj.field_types.get(ip)[i]; + const field_ty = union_obj.field_types.get(ip)[field_index]; const union_field_fields = .{ // name: [:0]const u8, name_val, @@ -18338,7 +18389,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, (try sema.getBuiltinType("Type")).getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "ContainerLayout"), + try ip.getOrPutString(gpa, "ContainerLayout", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(decl_index); const decl = mod.declPtr(decl_index); @@ -18371,7 +18422,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Struct"), + try ip.getOrPutString(gpa, "Struct", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(type_struct_ty_decl_index); const type_struct_ty_decl = mod.declPtr(type_struct_ty_decl_index); @@ -18383,7 +18434,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "StructField"), + try ip.getOrPutString(gpa, "StructField", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(struct_field_ty_decl_index); const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index); @@ -18396,27 +18447,25 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai defer gpa.free(struct_field_vals); fv: { const struct_type = switch (ip.indexToKey(ty.toIntern())) { - .anon_struct_type => |tuple| { - struct_field_vals = try gpa.alloc(InternPool.Index, tuple.types.len); - for (struct_field_vals, 0..) |*struct_field_val, i| { - const anon_struct_type = ip.indexToKey(ty.toIntern()).anon_struct_type; - const field_ty = anon_struct_type.types.get(ip)[i]; - const field_val = anon_struct_type.values.get(ip)[i]; + .anon_struct_type => |anon_struct_type| { + struct_field_vals = try gpa.alloc(InternPool.Index, anon_struct_type.types.len); + for (struct_field_vals, 0..) |*struct_field_val, field_index| { + const field_ty = anon_struct_type.types.get(ip)[field_index]; + const field_val = anon_struct_type.values.get(ip)[field_index]; const name_val = v: { - // TODO: write something like getCoercedInts to avoid needing to dupe - const bytes = if (tuple.names.len != 0) - // https://github.com/ziglang/zig/issues/15709 - try sema.arena.dupeZ(u8, ip.stringToSlice(ip.indexToKey(ty.toIntern()).anon_struct_type.names.get(ip)[i])) + const field_name = if (anon_struct_type.names.len != 0) + anon_struct_type.names.get(ip)[field_index] else - try std.fmt.allocPrintZ(sema.arena, "{d}", .{i}); + try ip.getOrPutStringFmt(gpa, "{d}", .{field_index}, .no_embedded_nulls); + const field_name_len = field_name.length(ip); const new_decl_ty = try mod.arrayType(.{ - .len = bytes.len, + .len = field_name_len, .sentinel = .zero_u8, .child = .u8_type, }); const new_decl_val = try mod.intern(.{ .aggregate = .{ .ty = new_decl_ty.toIntern(), - .storage = .{ .bytes = bytes }, + .storage = .{ .bytes = field_name.toString() }, } }); break :v try mod.intern(.{ .slice = .{ .ty = .slice_const_u8_sentinel_0_type, @@ -18427,7 +18476,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .orig_ty = .slice_const_u8_sentinel_0_type, } }, } }), - .len = (try mod.intValue(Type.usize, bytes.len)).toIntern(), + .len = (try mod.intValue(Type.usize, field_name_len)).toIntern(), } }); }; @@ -18462,24 +18511,24 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai try sema.resolveStructFieldInits(ty); - for (struct_field_vals, 0..) |*field_val, i| { - // TODO: write something like getCoercedInts to avoid needing to dupe - const name = if (struct_type.fieldName(ip, i).unwrap()) |name_nts| - try sema.arena.dupeZ(u8, ip.stringToSlice(name_nts)) + for (struct_field_vals, 0..) |*field_val, field_index| { + const field_name = if (struct_type.fieldName(ip, field_index).unwrap()) |field_name| + field_name else - try std.fmt.allocPrintZ(sema.arena, "{d}", .{i}); - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); - const field_init = struct_type.fieldInit(ip, i); - const field_is_comptime = struct_type.fieldIsComptime(ip, i); + try ip.getOrPutStringFmt(gpa, "{d}", .{field_index}, .no_embedded_nulls); + const field_name_len = field_name.length(ip); + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + const field_init = struct_type.fieldInit(ip, field_index); + const field_is_comptime = struct_type.fieldIsComptime(ip, field_index); const name_val = v: { const new_decl_ty = try mod.arrayType(.{ - .len = name.len, + .len = field_name_len, .sentinel = .zero_u8, .child = .u8_type, }); const new_decl_val = try mod.intern(.{ .aggregate = .{ .ty = new_decl_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = field_name.toString() }, } }); break :v try mod.intern(.{ .slice = .{ .ty = .slice_const_u8_sentinel_0_type, @@ -18490,7 +18539,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai .orig_ty = .slice_const_u8_sentinel_0_type, } }, } }), - .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + .len = (try mod.intValue(Type.usize, field_name_len)).toIntern(), } }); }; @@ -18499,7 +18548,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const alignment = switch (struct_type.layout) { .@"packed" => .none, else => try sema.structFieldAlignment( - struct_type.fieldAlign(ip, i), + struct_type.fieldAlign(ip, field_index), field_ty, struct_type.layout, ), @@ -18569,7 +18618,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, (try sema.getBuiltinType("Type")).getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "ContainerLayout"), + try ip.getOrPutString(gpa, "ContainerLayout", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(decl_index); const decl = mod.declPtr(decl_index); @@ -18605,7 +18654,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai block, src, type_info_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, "Opaque"), + try ip.getOrPutString(gpa, "Opaque", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(type_opaque_ty_decl_index); const type_opaque_ty_decl = mod.declPtr(type_opaque_ty_decl_index); @@ -18648,7 +18697,7 @@ fn typeInfoDecls( block, src, type_info_ty.getNamespaceIndex(mod), - try mod.intern_pool.getOrPutString(gpa, "Declaration"), + try mod.intern_pool.getOrPutString(gpa, "Declaration", .no_embedded_nulls), )).?; try sema.ensureDeclAnalyzed(declaration_ty_decl_index); const declaration_ty_decl = mod.declPtr(declaration_ty_decl_index); @@ -18722,16 +18771,15 @@ fn typeInfoNamespaceDecls( } if (decl.kind != .named) continue; const name_val = v: { - // TODO: write something like getCoercedInts to avoid needing to dupe - const name = try sema.arena.dupeZ(u8, ip.stringToSlice(decl.name)); + const decl_name_len = decl.name.length(ip); const new_decl_ty = try mod.arrayType(.{ - .len = name.len, + .len = decl_name_len, .sentinel = .zero_u8, .child = .u8_type, }); const new_decl_val = try mod.intern(.{ .aggregate = .{ .ty = new_decl_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = decl.name.toString() }, } }); break :v try mod.intern(.{ .slice = .{ .ty = .slice_const_u8_sentinel_0_type, @@ -18742,7 +18790,7 @@ fn typeInfoNamespaceDecls( .val = new_decl_val, } }, } }), - .len = (try mod.intValue(Type.usize, name.len)).toIntern(), + .len = (try mod.intValue(Type.usize, decl_name_len)).toIntern(), } }); }; @@ -19385,7 +19433,11 @@ fn zirRetErrValue( ) CompileError!void { const mod = sema.mod; const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok; - const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code)); + const err_name = try mod.intern_pool.getOrPutString( + sema.gpa, + inst_data.get(sema.code), + .no_embedded_nulls, + ); _ = try mod.getErrorValue(err_name); const src = inst_data.src(); // Return the error code from the function. @@ -20072,7 +20124,11 @@ fn zirStructInit( const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; - const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); + const field_name = try ip.getOrPutString( + gpa, + sema.code.nullTerminatedString(field_type_extra.name_start), + .no_embedded_nulls, + ); const field_index = if (resolved_ty.isTuple(mod)) try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) else @@ -20109,7 +20165,11 @@ fn zirStructInit( const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node; const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node }; const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; - const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start)); + const field_name = try ip.getOrPutString( + gpa, + sema.code.nullTerminatedString(field_type_extra.name_start), + .no_embedded_nulls, + ); const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); const tag_ty = resolved_ty.unionTagTypeHypothetical(mod); const tag_val = try mod.enumValueFieldIndex(tag_ty, field_index); @@ -20417,8 +20477,7 @@ fn structInitAnon( }, }; - const name_ip = try mod.intern_pool.getOrPutString(gpa, name); - field_name.* = name_ip; + field_name.* = try mod.intern_pool.getOrPutString(gpa, name, .no_embedded_nulls); const init = try sema.resolveInst(item.data.init); field_ty.* = sema.typeOf(init).toIntern(); @@ -20809,7 +20868,7 @@ fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Comp }; const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(mod); const zir_field_name = sema.code.nullTerminatedString(extra.name_start); - const field_name = try ip.getOrPutString(sema.gpa, zir_field_name); + const field_name = try ip.getOrPutString(sema.gpa, zir_field_name, .no_embedded_nulls); return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); } @@ -20975,7 +21034,7 @@ fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { const err_name = sema.mod.intern_pool.indexToKey(val.toIntern()).err.name; - return sema.addStrLit(sema.mod.intern_pool.stringToSlice(err_name)); + return sema.addNullTerminatedStrLit(err_name); } // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass @@ -21093,7 +21152,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air .EnumLiteral => { const val = try sema.resolveConstDefinedValue(block, .unneeded, operand, undefined); const tag_name = ip.indexToKey(val.toIntern()).enum_literal; - return sema.addStrLit(ip.stringToSlice(tag_name)); + return sema.addNullTerminatedStrLit(tag_name); }, .Enum => operand_ty, .Union => operand_ty.unionTagType(mod) orelse @@ -21127,7 +21186,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air }; // TODO: write something like getCoercedInts to avoid needing to dupe const field_name = enum_ty.enumFieldName(field_index, mod); - return sema.addStrLit(ip.stringToSlice(field_name)); + return sema.addNullTerminatedStrLit(field_name); } try sema.requireRuntimeBlock(block, src, operand_src); if (block.wantSafety() and sema.mod.backendSupportsFeature(.is_named_enum_value)) { @@ -21179,11 +21238,11 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const signedness_val = try Value.fromInterned(union_val.val).fieldValue( mod, - struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "signedness")).?, + struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "signedness", .no_embedded_nulls)).?, ); const bits_val = try Value.fromInterned(union_val.val).fieldValue( mod, - struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "bits")).?, + struct_type.nameIndex(ip, try ip.getOrPutString(gpa, "bits", .no_embedded_nulls)).?, ); const signedness = mod.toEnum(std.builtin.Signedness, signedness_val); @@ -21195,11 +21254,11 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "len"), + try ip.getOrPutString(gpa, "len", .no_embedded_nulls), ).?); const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "child"), + try ip.getOrPutString(gpa, "child", .no_embedded_nulls), ).?); const len: u32 = @intCast(try len_val.toUnsignedIntAdvanced(sema)); @@ -21217,7 +21276,7 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const bits_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "bits"), + try ip.getOrPutString(gpa, "bits", .no_embedded_nulls), ).?); const bits: u16 = @intCast(try bits_val.toUnsignedIntAdvanced(sema)); @@ -21235,35 +21294,35 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const size_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "size"), + try ip.getOrPutString(gpa, "size", .no_embedded_nulls), ).?); const is_const_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_const"), + try ip.getOrPutString(gpa, "is_const", .no_embedded_nulls), ).?); const is_volatile_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_volatile"), + try ip.getOrPutString(gpa, "is_volatile", .no_embedded_nulls), ).?); const alignment_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "alignment"), + try ip.getOrPutString(gpa, "alignment", .no_embedded_nulls), ).?); const address_space_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "address_space"), + try ip.getOrPutString(gpa, "address_space", .no_embedded_nulls), ).?); const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "child"), + try ip.getOrPutString(gpa, "child", .no_embedded_nulls), ).?); const is_allowzero_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_allowzero"), + try ip.getOrPutString(gpa, "is_allowzero", .no_embedded_nulls), ).?); const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "sentinel"), + try ip.getOrPutString(gpa, "sentinel", .no_embedded_nulls), ).?); if (!try sema.intFitsInType(alignment_val, Type.u32, null)) { @@ -21341,15 +21400,15 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const len_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "len"), + try ip.getOrPutString(gpa, "len", .no_embedded_nulls), ).?); const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "child"), + try ip.getOrPutString(gpa, "child", .no_embedded_nulls), ).?); const sentinel_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "sentinel"), + try ip.getOrPutString(gpa, "sentinel", .no_embedded_nulls), ).?); const len = try len_val.toUnsignedIntAdvanced(sema); @@ -21370,7 +21429,7 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const child_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "child"), + try ip.getOrPutString(gpa, "child", .no_embedded_nulls), ).?); const child_ty = child_val.toType(); @@ -21382,11 +21441,11 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const error_set_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "error_set"), + try ip.getOrPutString(gpa, "error_set", .no_embedded_nulls), ).?); const payload_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "payload"), + try ip.getOrPutString(gpa, "payload", .no_embedded_nulls), ).?); const error_set_ty = error_set_val.toType(); @@ -21415,7 +21474,7 @@ fn zirReify( const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); const name_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "name"), + try ip.getOrPutString(gpa, "name", .no_embedded_nulls), ).?); const name = try sema.sliceToIpString(block, src, name_val, .{ @@ -21437,23 +21496,23 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "layout"), + try ip.getOrPutString(gpa, "layout", .no_embedded_nulls), ).?); const backing_integer_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "backing_integer"), + try ip.getOrPutString(gpa, "backing_integer", .no_embedded_nulls), ).?); const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "fields"), + try ip.getOrPutString(gpa, "fields", .no_embedded_nulls), ).?); const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "decls"), + try ip.getOrPutString(gpa, "decls", .no_embedded_nulls), ).?); const is_tuple_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_tuple"), + try ip.getOrPutString(gpa, "is_tuple", .no_embedded_nulls), ).?); const layout = mod.toEnum(std.builtin.Type.ContainerLayout, layout_val); @@ -21477,19 +21536,19 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "tag_type"), + try ip.getOrPutString(gpa, "tag_type", .no_embedded_nulls), ).?); const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "fields"), + try ip.getOrPutString(gpa, "fields", .no_embedded_nulls), ).?); const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "decls"), + try ip.getOrPutString(gpa, "decls", .no_embedded_nulls), ).?); const is_exhaustive_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_exhaustive"), + try ip.getOrPutString(gpa, "is_exhaustive", .no_embedded_nulls), ).?); if (try decls_val.sliceLen(sema) > 0) { @@ -21506,7 +21565,7 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "decls"), + try ip.getOrPutString(gpa, "decls", .no_embedded_nulls), ).?); // Decls @@ -21544,19 +21603,19 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const layout_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "layout"), + try ip.getOrPutString(gpa, "layout", .no_embedded_nulls), ).?); const tag_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "tag_type"), + try ip.getOrPutString(gpa, "tag_type", .no_embedded_nulls), ).?); const fields_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "fields"), + try ip.getOrPutString(gpa, "fields", .no_embedded_nulls), ).?); const decls_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "decls"), + try ip.getOrPutString(gpa, "decls", .no_embedded_nulls), ).?); if (try decls_val.sliceLen(sema) > 0) { @@ -21574,23 +21633,23 @@ fn zirReify( const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); const calling_convention_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "calling_convention"), + try ip.getOrPutString(gpa, "calling_convention", .no_embedded_nulls), ).?); const is_generic_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_generic"), + try ip.getOrPutString(gpa, "is_generic", .no_embedded_nulls), ).?); const is_var_args_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_var_args"), + try ip.getOrPutString(gpa, "is_var_args", .no_embedded_nulls), ).?); const return_type_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "return_type"), + try ip.getOrPutString(gpa, "return_type", .no_embedded_nulls), ).?); const params_slice_val = try Value.fromInterned(union_val.val).fieldValue(mod, struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "params"), + try ip.getOrPutString(gpa, "params", .no_embedded_nulls), ).?); const is_generic = is_generic_val.toBool(); @@ -21620,15 +21679,15 @@ fn zirReify( const elem_struct_type = ip.loadStructType(ip.typeOf(elem_val.toIntern())); const param_is_generic_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_generic"), + try ip.getOrPutString(gpa, "is_generic", .no_embedded_nulls), ).?); const param_is_noalias_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "is_noalias"), + try ip.getOrPutString(gpa, "is_noalias", .no_embedded_nulls), ).?); const opt_param_type_val = try elem_val.fieldValue(mod, elem_struct_type.nameIndex( ip, - try ip.getOrPutString(gpa, "type"), + try ip.getOrPutString(gpa, "type", .no_embedded_nulls), ).?); if (param_is_generic_val.toBool()) { @@ -22366,13 +22425,14 @@ fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; + const ip = &mod.intern_pool; + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const ty = try sema.resolveType(block, ty_src, inst_data.operand); - var bytes = std.ArrayList(u8).init(sema.arena); - try ty.print(bytes.writer(), mod); - return addStrLitNoAlias(sema, bytes.items); + const type_name = try ip.getOrPutStringFmt(sema.gpa, "{}", .{ty.fmt(mod)}, .no_embedded_nulls); + return sema.addNullTerminatedStrLit(type_name); } fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -23507,7 +23567,7 @@ fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u6 } const field_index = if (ty.isTuple(mod)) blk: { - if (ip.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", ip)) { return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); } break :blk try sema.tupleFieldIndex(block, ty, field_name, rhs_src); @@ -23977,18 +24037,18 @@ fn resolveExportOptions( const section_src = sema.maybeOptionsSrc(block, src, "section"); const visibility_src = sema.maybeOptionsSrc(block, src, "visibility"); - const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); + const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name", .no_embedded_nulls), name_src); const name = try sema.toConstString(block, name_src, name_operand, .{ .needed_comptime_reason = "name of exported value must be comptime-known", }); - const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); + const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage", .no_embedded_nulls), linkage_src); const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .needed_comptime_reason = "linkage of exported value must be comptime-known", }); const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); - const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section"), section_src); + const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "section", .no_embedded_nulls), section_src); const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .needed_comptime_reason = "linksection of exported value must be comptime-known", }); @@ -23999,7 +24059,7 @@ fn resolveExportOptions( else null; - const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility"), visibility_src); + const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "visibility", .no_embedded_nulls), visibility_src); const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .needed_comptime_reason = "visibility of exported value must be comptime-known", }); @@ -24016,9 +24076,9 @@ fn resolveExportOptions( } return .{ - .name = try ip.getOrPutString(gpa, name), + .name = try ip.getOrPutString(gpa, name, .no_embedded_nulls), .linkage = linkage, - .section = try ip.getOrPutStringOpt(gpa, section), + .section = try ip.getOrPutStringOpt(gpa, section, .no_embedded_nulls), .visibility = visibility, }; } @@ -24896,7 +24956,7 @@ fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Ins const field_index = switch (parent_ty.zigTypeTag(mod)) { .Struct => blk: { if (parent_ty.isTuple(mod)) { - if (ip.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", ip)) { return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); } break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src); @@ -25578,7 +25638,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const runtime_src = rs: { const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; - const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len"), dest_src); + const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, "len", .no_embedded_nulls), dest_src); const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; const len_u64 = (try len_val.getUnsignedIntAdvanced(mod, sema)).?; const len = try sema.usizeCast(block, dest_src, len_u64); @@ -25708,7 +25768,7 @@ fn zirVarExtended( .ty = var_ty.toIntern(), .init = init_val, .decl = sema.owner_decl_index, - .lib_name = try mod.intern_pool.getOrPutStringOpt(sema.gpa, lib_name), + .lib_name = try mod.intern_pool.getOrPutStringOpt(sema.gpa, lib_name, .no_embedded_nulls), .is_extern = small.is_extern, .is_const = small.is_const, .is_threadlocal = small.is_threadlocal, @@ -26076,17 +26136,17 @@ fn resolvePrefetchOptions( const locality_src = sema.maybeOptionsSrc(block, src, "locality"); const cache_src = sema.maybeOptionsSrc(block, src, "cache"); - const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw"), rw_src); + const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "rw", .no_embedded_nulls), rw_src); const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .needed_comptime_reason = "prefetch read/write must be comptime-known", }); - const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality"), locality_src); + const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "locality", .no_embedded_nulls), locality_src); const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .needed_comptime_reason = "prefetch locality must be comptime-known", }); - const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache"), cache_src); + const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "cache", .no_embedded_nulls), cache_src); const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .needed_comptime_reason = "prefetch cache must be comptime-known", }); @@ -26155,23 +26215,23 @@ fn resolveExternOptions( const linkage_src = sema.maybeOptionsSrc(block, src, "linkage"); const thread_local_src = sema.maybeOptionsSrc(block, src, "thread_local"); - const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name"), name_src); + const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "name", .no_embedded_nulls), name_src); const name = try sema.toConstString(block, name_src, name_ref, .{ .needed_comptime_reason = "name of the extern symbol must be comptime-known", }); - const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name"), library_src); + const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "library_name", .no_embedded_nulls), library_src); const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .needed_comptime_reason = "library in which extern symbol is must be comptime-known", }); - const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage"), linkage_src); + const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "linkage", .no_embedded_nulls), linkage_src); const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .needed_comptime_reason = "linkage of the extern symbol must be comptime-known", }); const linkage = mod.toEnum(std.builtin.GlobalLinkage, linkage_val); - const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local"), thread_local_src); + const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, "is_thread_local", .no_embedded_nulls), thread_local_src); const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .needed_comptime_reason = "threadlocality of the extern symbol must be comptime-known", }); @@ -26196,8 +26256,8 @@ fn resolveExternOptions( } return .{ - .name = try ip.getOrPutString(gpa, name), - .library_name = try ip.getOrPutStringOpt(gpa, library_name), + .name = try ip.getOrPutString(gpa, name, .no_embedded_nulls), + .library_name = try ip.getOrPutStringOpt(gpa, library_name, .no_embedded_nulls), .linkage = linkage, .is_thread_local = is_thread_local_val.toBool(), }; @@ -26809,7 +26869,7 @@ fn preparePanicId(sema: *Sema, block: *Block, panic_id: Module.PanicId) !InternP block, .unneeded, panic_messages_ty.getNamespaceIndex(mod), - try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id)), + try mod.intern_pool.getOrPutString(gpa, @tagName(panic_id), .no_embedded_nulls), ) catch |err| switch (err) { error.AnalysisFail, error.NeededSourceLocation => @panic("std.builtin.panic_messages is corrupt"), error.GenericPoison, error.ComptimeReturn, error.ComptimeBreak => unreachable, @@ -27129,9 +27189,9 @@ fn fieldVal( switch (inner_ty.zigTypeTag(mod)) { .Array => { - if (ip.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", ip)) { return Air.internedToRef((try mod.intValue(Type.usize, inner_ty.arrayLen(mod))).toIntern()); - } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) { + } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { const ptr_info = object_ty.ptrInfo(mod); const result_ty = try sema.ptrType(.{ .child = Type.fromInterned(ptr_info.child).childType(mod).toIntern(), @@ -27160,13 +27220,13 @@ fn fieldVal( .Pointer => { const ptr_info = inner_ty.ptrInfo(mod); if (ptr_info.flags.size == .Slice) { - if (ip.stringEqlSlice(field_name, "ptr")) { + if (field_name.eqlSlice("ptr", ip)) { const slice = if (is_pointer_to) try sema.analyzeLoad(block, src, object, object_src) else object; return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); - } else if (ip.stringEqlSlice(field_name, "len")) { + } else if (field_name.eqlSlice("len", ip)) { const slice = if (is_pointer_to) try sema.analyzeLoad(block, src, object, object_src) else @@ -27319,10 +27379,10 @@ fn fieldPtr( switch (inner_ty.zigTypeTag(mod)) { .Array => { - if (ip.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", ip)) { const int_val = try mod.intValue(Type.usize, inner_ty.arrayLen(mod)); return anonDeclRef(sema, int_val.toIntern()); - } else if (ip.stringEqlSlice(field_name, "ptr") and is_pointer_to) { + } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { const ptr_info = object_ty.ptrInfo(mod); const new_ptr_ty = try sema.ptrType(.{ .child = Type.fromInterned(ptr_info.child).childType(mod).toIntern(), @@ -27370,7 +27430,7 @@ fn fieldPtr( const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; - if (ip.stringEqlSlice(field_name, "ptr")) { + if (field_name.eqlSlice("ptr", ip)) { const slice_ptr_ty = inner_ty.slicePtrFieldType(mod); const result_ty = try sema.ptrType(.{ @@ -27396,7 +27456,7 @@ fn fieldPtr( const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); return field_ptr; - } else if (ip.stringEqlSlice(field_name, "len")) { + } else if (field_name.eqlSlice("len", ip)) { const result_ty = try sema.ptrType(.{ .child = .usize_type, .flags = .{ @@ -27584,7 +27644,7 @@ fn fieldCallBind( return sema.finishFieldCallBind(block, src, ptr_ty, field_ty, field_index, object_ptr); } else if (concrete_ty.isTuple(mod)) { - if (ip.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", ip)) { return .{ .direct = try mod.intRef(Type.usize, concrete_ty.structFieldCount(mod)) }; } if (field_name.toUnsigned(ip)) |field_index| { @@ -27808,7 +27868,7 @@ fn structFieldPtr( try sema.resolveStructLayout(struct_ty); if (struct_ty.isTuple(mod)) { - if (ip.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", ip)) { const len_inst = try mod.intRef(Type.usize, struct_ty.structFieldCount(mod)); return sema.analyzeRef(block, src, len_inst); } @@ -28023,7 +28083,7 @@ fn tupleFieldVal( tuple_ty: Type, ) CompileError!Air.Inst.Ref { const mod = sema.mod; - if (mod.intern_pool.stringEqlSlice(field_name, "len")) { + if (field_name.eqlSlice("len", &mod.intern_pool)) { return mod.intRef(Type.usize, tuple_ty.structFieldCount(mod)); } const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); @@ -28039,16 +28099,17 @@ fn tupleFieldIndex( field_name_src: LazySrcLoc, ) CompileError!u32 { const mod = sema.mod; - assert(!mod.intern_pool.stringEqlSlice(field_name, "len")); - if (field_name.toUnsigned(&mod.intern_pool)) |field_index| { + const ip = &mod.intern_pool; + assert(!field_name.eqlSlice("len", ip)); + if (field_name.toUnsigned(ip)) |field_index| { if (field_index < tuple_ty.structFieldCount(mod)) return field_index; return sema.fail(block, field_name_src, "index '{}' out of bounds of tuple '{}'", .{ - field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), + field_name.fmt(ip), tuple_ty.fmt(mod), }); } return sema.fail(block, field_name_src, "no field named '{}' in tuple '{}'", .{ - field_name.fmt(&mod.intern_pool), tuple_ty.fmt(mod), + field_name.fmt(ip), tuple_ty.fmt(mod), }); } @@ -28076,7 +28137,7 @@ fn tupleFieldValByIndex( return switch (mod.intern_pool.indexToKey(tuple_val.toIntern())) { .undef => mod.undefRef(field_ty), .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) { - .bytes => |bytes| try mod.intValue(Type.u8, bytes[0]), + .bytes => |bytes| try mod.intValue(Type.u8, bytes.at(field_index, &mod.intern_pool)), .elems => |elems| Value.fromInterned(elems[field_index]), .repeated_elem => |elem| Value.fromInterned(elem), }.toIntern()), @@ -32266,38 +32327,36 @@ fn coerceTupleToStruct( .struct_type => ip.loadStructType(inst_ty.toIntern()).field_types.len, else => unreachable, }; - for (0..field_count) |field_index_usize| { - const field_i: u32 = @intCast(field_index_usize); + for (0..field_count) |tuple_field_index| { const field_src = inst_src; // TODO better source location - // https://github.com/ziglang/zig/issues/15709 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) - anon_struct_type.names.get(ip)[field_i] + anon_struct_type.names.get(ip)[tuple_field_index] else - try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), - .struct_type => ip.loadStructType(inst_ty.toIntern()).field_names.get(ip)[field_i], + try ip.getOrPutStringFmt(sema.gpa, "{d}", .{tuple_field_index}, .no_embedded_nulls), + .struct_type => ip.loadStructType(inst_ty.toIntern()).field_names.get(ip)[tuple_field_index], else => unreachable, }; - const field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); - const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); - const coerced = try sema.coerce(block, field_ty, elem_ref, field_src); - field_refs[field_index] = coerced; - if (struct_type.fieldIsComptime(ip, field_index)) { + const struct_field_index = try sema.structFieldIndex(block, struct_ty, field_name, field_src); + const struct_field_ty = Type.fromInterned(struct_type.field_types.get(ip)[struct_field_index]); + const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, @intCast(tuple_field_index)); + const coerced = try sema.coerce(block, struct_field_ty, elem_ref, field_src); + field_refs[struct_field_index] = coerced; + if (struct_type.fieldIsComptime(ip, struct_field_index)) { const init_val = (try sema.resolveValue(coerced)) orelse { return sema.failWithNeededComptime(block, field_src, .{ .needed_comptime_reason = "value stored in comptime field must be comptime-known", }); }; - const field_init = Value.fromInterned(struct_type.field_inits.get(ip)[field_index]); - if (!init_val.eql(field_init, field_ty, sema.mod)) { - return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); + const field_init = Value.fromInterned(struct_type.field_inits.get(ip)[struct_field_index]); + if (!init_val.eql(field_init, struct_field_ty, sema.mod)) { + return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, tuple_field_index); } } if (runtime_src == null) { if (try sema.resolveValue(coerced)) |field_val| { - field_vals[field_index] = field_val.toIntern(); + field_vals[struct_field_index] = field_val.toIntern(); } else { runtime_src = field_src; } @@ -32382,24 +32441,23 @@ fn coerceTupleToTuple( for (0..dest_field_count) |field_index_usize| { const field_i: u32 = @intCast(field_index_usize); const field_src = inst_src; // TODO better source location - // https://github.com/ziglang/zig/issues/15709 const field_name: InternPool.NullTerminatedString = switch (ip.indexToKey(inst_ty.toIntern())) { .anon_struct_type => |anon_struct_type| if (anon_struct_type.names.len > 0) anon_struct_type.names.get(ip)[field_i] else - try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}), + try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}, .no_embedded_nulls), .struct_type => s: { const struct_type = ip.loadStructType(inst_ty.toIntern()); if (struct_type.field_names.len > 0) { break :s struct_type.field_names.get(ip)[field_i]; } else { - break :s try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}); + break :s try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_i}, .no_embedded_nulls); } }, else => unreachable, }; - if (ip.stringEqlSlice(field_name, "len")) + if (field_name.eqlSlice("len", ip)) return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{}); const field_ty = switch (ip.indexToKey(tuple_ty.toIntern())) { @@ -34196,7 +34254,7 @@ const PeerResolveResult = union(enum) { /// There was an error when resolving the type of a struct or tuple field. field_error: struct { /// The name of the field which caused the failure. - field_name: []const u8, + field_name: InternPool.NullTerminatedString, /// The type of this field in each peer. field_types: []Type, /// The error from resolving the field type. Guaranteed not to be `success`. @@ -34237,8 +34295,8 @@ const PeerResolveResult = union(enum) { }; }, .field_error => |field_error| { - const fmt = "struct field '{s}' has conflicting types"; - const args = .{field_error.field_name}; + const fmt = "struct field '{}' has conflicting types"; + const args = .{field_error.field_name.fmt(&mod.intern_pool)}; if (opt_msg) |msg| { try sema.errNote(block, src, msg, fmt, args); } else { @@ -35321,7 +35379,7 @@ fn resolvePeerTypesInner( const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); - for (field_types, field_vals, 0..) |*field_ty, *field_val, field_idx| { + for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| { // Fill buffers with types and values of the field for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { const ty = opt_ty orelse { @@ -35329,8 +35387,8 @@ fn resolvePeerTypesInner( peer_field_val.* = null; continue; }; - peer_field_ty.* = ty.structFieldType(field_idx, mod); - peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_idx) else null; + peer_field_ty.* = ty.structFieldType(field_index, mod); + peer_field_val.* = if (opt_val) |val| try val.fieldValue(mod, field_index) else null; } // Resolve field type recursively @@ -35339,9 +35397,10 @@ fn resolvePeerTypesInner( else => |result| { const result_buf = try sema.arena.create(PeerResolveResult); result_buf.* = result; - const field_name = if (is_tuple) name: { - break :name try std.fmt.allocPrint(sema.arena, "{d}", .{field_idx}); - } else try sema.arena.dupe(u8, ip.stringToSlice(field_names[field_idx])); + const field_name = if (is_tuple) + try ip.getOrPutStringFmt(sema.gpa, "{d}", .{field_index}, .no_embedded_nulls) + else + field_names[field_index]; // The error info needs the field types, but we can't reuse sub_peer_tys // since the recursive call may have clobbered it. @@ -35350,7 +35409,7 @@ fn resolvePeerTypesInner( // Already-resolved types won't be referenced by the error so it's fine // to leave them undefined. const ty = opt_ty orelse continue; - peer_field_ty.* = ty.structFieldType(field_idx, mod); + peer_field_ty.* = ty.structFieldType(field_index, mod); } return .{ .field_error = .{ @@ -35369,7 +35428,7 @@ fn resolvePeerTypesInner( const struct_ty = opt_ty orelse continue; try sema.resolveStructFieldInits(struct_ty); - const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_idx) orelse { + const uncoerced_field_val = try struct_ty.structFieldValueComptime(mod, field_index) orelse { comptime_val = null; break; }; @@ -36811,7 +36870,7 @@ fn semaStructFields( // This string needs to outlive the ZIR code. if (opt_field_name_zir) |field_name_zir| { - const field_name = try ip.getOrPutString(gpa, field_name_zir); + const field_name = try ip.getOrPutString(gpa, field_name_zir, .no_embedded_nulls); assert(struct_type.addFieldName(ip, field_name) == null); } @@ -37342,7 +37401,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Loaded } // This string needs to outlive the ZIR code. - const field_name = try ip.getOrPutString(gpa, field_name_zir); + const field_name = try ip.getOrPutString(gpa, field_name_zir, .no_embedded_nulls); if (enum_field_names.len != 0) { enum_field_names[field_i] = field_name; } @@ -37528,7 +37587,12 @@ fn generateUnionTagTypeNumbered( const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node); errdefer mod.destroyDecl(new_decl_index); const fqn = try union_owner_decl.fullyQualifiedName(mod); - const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); + const name = try ip.getOrPutStringFmt( + gpa, + "@typeInfo({}).Union.tag_type.?", + .{fqn.fmt(ip)}, + .no_embedded_nulls, + ); try mod.initNewAnonDecl( new_decl_index, src_decl.src_line, @@ -37574,7 +37638,12 @@ fn generateUnionTagTypeSimple( const src_decl = mod.declPtr(block.src_decl); const new_decl_index = try mod.allocateNewDecl(block.namespace, src_decl.src_node); errdefer mod.destroyDecl(new_decl_index); - const name = try ip.getOrPutStringFmt(gpa, "@typeInfo({}).Union.tag_type.?", .{fqn.fmt(ip)}); + const name = try ip.getOrPutStringFmt( + gpa, + "@typeInfo({}).Union.tag_type.?", + .{fqn.fmt(ip)}, + .no_embedded_nulls, + ); try mod.initNewAnonDecl( new_decl_index, src_decl.src_line, @@ -37638,7 +37707,7 @@ fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Int block, src, mod.declPtr(std_file.root_decl.unwrap().?).src_namespace.toOptional(), - try ip.getOrPutString(gpa, "builtin"), + try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls), )) orelse @panic("lib/std.zig is corrupt and missing 'builtin'"); const builtin_inst = try sema.analyzeLoad(block, src, opt_builtin_inst, src); const builtin_ty = sema.analyzeAsType(block, src, builtin_inst) catch |err| switch (err) { @@ -37649,7 +37718,7 @@ fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Int block, src, builtin_ty.getNamespaceIndex(mod), - try ip.getOrPutString(gpa, name), + try ip.getOrPutString(gpa, name, .no_embedded_nulls), )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name}); return decl_index; } @@ -38820,7 +38889,7 @@ fn intFitsInType( .aggregate => |aggregate| { assert(ty.zigTypeTag(mod) == .Vector); return switch (aggregate.storage) { - .bytes => |bytes| for (bytes, 0..) |byte, i| { + .bytes => |bytes| for (bytes.toSlice(ty.vectorLen(mod), &mod.intern_pool), 0..) |byte, i| { if (byte == 0) continue; const actual_needed_bits = std.math.log2(byte) + 1 + @intFromBool(info.signedness == .signed); if (info.bits >= actual_needed_bits) continue; diff --git a/src/Value.zig b/src/Value.zig index 7a9775e198..0f8dc5f7dc 100644 --- a/src/Value.zig +++ b/src/Value.zig @@ -52,30 +52,31 @@ pub fn toIpString(val: Value, ty: Type, mod: *Module) !InternPool.NullTerminated assert(ty.zigTypeTag(mod) == .Array); assert(ty.childType(mod).toIntern() == .u8_type); const ip = &mod.intern_pool; - return switch (mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage) { - .bytes => |bytes| try ip.getOrPutString(mod.gpa, bytes), - .elems => try arrayToIpString(val, ty.arrayLen(mod), mod), + switch (mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage) { + .bytes => |bytes| return bytes.toNullTerminatedString(ty.arrayLen(mod), ip), + .elems => return arrayToIpString(val, ty.arrayLen(mod), mod), .repeated_elem => |elem| { - const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod))); - const len = @as(usize, @intCast(ty.arrayLen(mod))); + const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(mod)); + const len: usize = @intCast(ty.arrayLen(mod)); try ip.string_bytes.appendNTimes(mod.gpa, byte, len); - return ip.getOrPutTrailingString(mod.gpa, len); + return ip.getOrPutTrailingString(mod.gpa, len, .no_embedded_nulls); }, - }; + } } /// Asserts that the value is representable as an array of bytes. /// Copies the value into a freshly allocated slice of memory, which is owned by the caller. pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module) ![]u8 { - return switch (mod.intern_pool.indexToKey(val.toIntern())) { - .enum_literal => |enum_literal| allocator.dupe(u8, mod.intern_pool.stringToSlice(enum_literal)), + const ip = &mod.intern_pool; + return switch (ip.indexToKey(val.toIntern())) { + .enum_literal => |enum_literal| allocator.dupe(u8, enum_literal.toSlice(ip)), .slice => |slice| try arrayToAllocatedBytes(val, Value.fromInterned(slice.len).toUnsignedInt(mod), allocator, mod), .aggregate => |aggregate| switch (aggregate.storage) { - .bytes => |bytes| try allocator.dupe(u8, bytes), + .bytes => |bytes| try allocator.dupe(u8, bytes.toSlice(ty.arrayLenIncludingSentinel(mod), ip)), .elems => try arrayToAllocatedBytes(val, ty.arrayLen(mod), allocator, mod), .repeated_elem => |elem| { - const byte = @as(u8, @intCast(Value.fromInterned(elem).toUnsignedInt(mod))); - const result = try allocator.alloc(u8, @as(usize, @intCast(ty.arrayLen(mod)))); + const byte: u8 = @intCast(Value.fromInterned(elem).toUnsignedInt(mod)); + const result = try allocator.alloc(u8, @intCast(ty.arrayLen(mod))); @memset(result, byte); return result; }, @@ -85,10 +86,10 @@ pub fn toAllocatedBytes(val: Value, ty: Type, allocator: Allocator, mod: *Module } fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Module) ![]u8 { - const result = try allocator.alloc(u8, @as(usize, @intCast(len))); + const result = try allocator.alloc(u8, @intCast(len)); for (result, 0..) |*elem, i| { const elem_val = try val.elemValue(mod, i); - elem.* = @as(u8, @intCast(elem_val.toUnsignedInt(mod))); + elem.* = @intCast(elem_val.toUnsignedInt(mod)); } return result; } @@ -96,7 +97,7 @@ fn arrayToAllocatedBytes(val: Value, len: u64, allocator: Allocator, mod: *Modul fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTerminatedString { const gpa = mod.gpa; const ip = &mod.intern_pool; - const len = @as(usize, @intCast(len_u64)); + const len: usize = @intCast(len_u64); try ip.string_bytes.ensureUnusedCapacity(gpa, len); for (0..len) |i| { // I don't think elemValue has the possibility to affect ip.string_bytes. Let's @@ -104,10 +105,10 @@ fn arrayToIpString(val: Value, len_u64: u64, mod: *Module) !InternPool.NullTermi const prev = ip.string_bytes.items.len; const elem_val = try val.elemValue(mod, i); assert(ip.string_bytes.items.len == prev); - const byte = @as(u8, @intCast(elem_val.toUnsignedInt(mod))); + const byte: u8 = @intCast(elem_val.toUnsignedInt(mod)); ip.string_bytes.appendAssumeCapacity(byte); } - return ip.getOrPutTrailingString(gpa, len); + return ip.getOrPutTrailingString(gpa, len, .no_embedded_nulls); } pub fn fromInterned(i: InternPool.Index) Value { @@ -256,7 +257,7 @@ pub fn getUnsignedIntAdvanced(val: Value, mod: *Module, opt_sema: ?*Sema) !?u64 const base_addr = (try Value.fromInterned(field.base).getUnsignedIntAdvanced(mod, opt_sema)) orelse return null; const struct_ty = Value.fromInterned(field.base).typeOf(mod).childType(mod); if (opt_sema) |sema| try sema.resolveTypeLayout(struct_ty); - return base_addr + struct_ty.structFieldOffset(@as(usize, @intCast(field.index)), mod); + return base_addr + struct_ty.structFieldOffset(@intCast(field.index), mod); }, else => null, }, @@ -351,17 +352,17 @@ pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{ bigint.writeTwosComplement(buffer[0..byte_count], endian); }, .Float => switch (ty.floatBits(target)) { - 16 => std.mem.writeInt(u16, buffer[0..2], @as(u16, @bitCast(val.toFloat(f16, mod))), endian), - 32 => std.mem.writeInt(u32, buffer[0..4], @as(u32, @bitCast(val.toFloat(f32, mod))), endian), - 64 => std.mem.writeInt(u64, buffer[0..8], @as(u64, @bitCast(val.toFloat(f64, mod))), endian), - 80 => std.mem.writeInt(u80, buffer[0..10], @as(u80, @bitCast(val.toFloat(f80, mod))), endian), - 128 => std.mem.writeInt(u128, buffer[0..16], @as(u128, @bitCast(val.toFloat(f128, mod))), endian), + 16 => std.mem.writeInt(u16, buffer[0..2], @bitCast(val.toFloat(f16, mod)), endian), + 32 => std.mem.writeInt(u32, buffer[0..4], @bitCast(val.toFloat(f32, mod)), endian), + 64 => std.mem.writeInt(u64, buffer[0..8], @bitCast(val.toFloat(f64, mod)), endian), + 80 => std.mem.writeInt(u80, buffer[0..10], @bitCast(val.toFloat(f80, mod)), endian), + 128 => std.mem.writeInt(u128, buffer[0..16], @bitCast(val.toFloat(f128, mod)), endian), else => unreachable, }, .Array => { const len = ty.arrayLen(mod); const elem_ty = ty.childType(mod); - const elem_size = @as(usize, @intCast(elem_ty.abiSize(mod))); + const elem_size: usize = @intCast(elem_ty.abiSize(mod)); var elem_i: usize = 0; var buf_off: usize = 0; while (elem_i < len) : (elem_i += 1) { @@ -380,17 +381,17 @@ pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{ const struct_type = mod.typeToStruct(ty) orelse return error.IllDefinedMemoryLayout; switch (struct_type.layout) { .auto => return error.IllDefinedMemoryLayout, - .@"extern" => for (0..struct_type.field_types.len) |i| { - const off: usize = @intCast(ty.structFieldOffset(i, mod)); + .@"extern" => for (0..struct_type.field_types.len) |field_index| { + const off: usize = @intCast(ty.structFieldOffset(field_index, mod)); const field_val = Value.fromInterned(switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| { - buffer[off] = bytes[i]; + buffer[off] = bytes.at(field_index, ip); continue; }, - .elems => |elems| elems[i], + .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, }); - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); try writeToMemory(field_val, field_ty, mod, buffer[off..]); }, .@"packed" => { @@ -423,7 +424,7 @@ pub fn writeToMemory(val: Value, ty: Type, mod: *Module, buffer: []u8) error{ const field_index = mod.unionTagFieldIndex(union_obj, union_tag).?; const field_type = Type.fromInterned(union_obj.field_types.get(&mod.intern_pool)[field_index]); const field_val = try val.fieldValue(mod, field_index); - const byte_count = @as(usize, @intCast(field_type.abiSize(mod))); + const byte_count: usize = @intCast(field_type.abiSize(mod)); return writeToMemory(field_val, field_type, mod, buffer[0..byte_count]); } else { const backing_ty = try ty.unionBackingType(mod); @@ -471,7 +472,7 @@ pub fn writeToPackedMemory( const target = mod.getTarget(); const endian = target.cpu.arch.endian(); if (val.isUndef(mod)) { - const bit_size = @as(usize, @intCast(ty.bitSize(mod))); + const bit_size: usize = @intCast(ty.bitSize(mod)); std.mem.writeVarPackedInt(buffer, bit_offset, bit_size, @as(u1, 0), endian); return; } @@ -507,17 +508,17 @@ pub fn writeToPackedMemory( } }, .Float => switch (ty.floatBits(target)) { - 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @as(u16, @bitCast(val.toFloat(f16, mod))), endian), - 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @as(u32, @bitCast(val.toFloat(f32, mod))), endian), - 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @as(u64, @bitCast(val.toFloat(f64, mod))), endian), - 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @as(u80, @bitCast(val.toFloat(f80, mod))), endian), - 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @as(u128, @bitCast(val.toFloat(f128, mod))), endian), + 16 => std.mem.writePackedInt(u16, buffer, bit_offset, @bitCast(val.toFloat(f16, mod)), endian), + 32 => std.mem.writePackedInt(u32, buffer, bit_offset, @bitCast(val.toFloat(f32, mod)), endian), + 64 => std.mem.writePackedInt(u64, buffer, bit_offset, @bitCast(val.toFloat(f64, mod)), endian), + 80 => std.mem.writePackedInt(u80, buffer, bit_offset, @bitCast(val.toFloat(f80, mod)), endian), + 128 => std.mem.writePackedInt(u128, buffer, bit_offset, @bitCast(val.toFloat(f128, mod)), endian), else => unreachable, }, .Vector => { const elem_ty = ty.childType(mod); - const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod))); - const len = @as(usize, @intCast(ty.arrayLen(mod))); + const elem_bit_size: u16 = @intCast(elem_ty.bitSize(mod)); + const len: usize = @intCast(ty.arrayLen(mod)); var bits: u16 = 0; var elem_i: usize = 0; @@ -644,22 +645,22 @@ pub fn readFromMemory( .Float => return Value.fromInterned((try mod.intern(.{ .float = .{ .ty = ty.toIntern(), .storage = switch (ty.floatBits(target)) { - 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readInt(u16, buffer[0..2], endian))) }, - 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readInt(u32, buffer[0..4], endian))) }, - 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readInt(u64, buffer[0..8], endian))) }, - 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readInt(u80, buffer[0..10], endian))) }, - 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readInt(u128, buffer[0..16], endian))) }, + 16 => .{ .f16 = @bitCast(std.mem.readInt(u16, buffer[0..2], endian)) }, + 32 => .{ .f32 = @bitCast(std.mem.readInt(u32, buffer[0..4], endian)) }, + 64 => .{ .f64 = @bitCast(std.mem.readInt(u64, buffer[0..8], endian)) }, + 80 => .{ .f80 = @bitCast(std.mem.readInt(u80, buffer[0..10], endian)) }, + 128 => .{ .f128 = @bitCast(std.mem.readInt(u128, buffer[0..16], endian)) }, else => unreachable, }, } }))), .Array => { const elem_ty = ty.childType(mod); const elem_size = elem_ty.abiSize(mod); - const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod)))); + const elems = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(mod))); var offset: usize = 0; for (elems) |*elem| { elem.* = (try readFromMemory(elem_ty, mod, buffer[offset..], arena)).toIntern(); - offset += @as(usize, @intCast(elem_size)); + offset += @intCast(elem_size); } return Value.fromInterned((try mod.intern(.{ .aggregate = .{ .ty = ty.toIntern(), @@ -795,7 +796,7 @@ pub fn readFromPackedMemory( }; // Slow path, we have to construct a big-int - const abi_size = @as(usize, @intCast(ty.abiSize(mod))); + const abi_size: usize = @intCast(ty.abiSize(mod)); const Limb = std.math.big.Limb; const limb_count = (abi_size + @sizeOf(Limb) - 1) / @sizeOf(Limb); const limbs_buffer = try arena.alloc(Limb, limb_count); @@ -812,20 +813,20 @@ pub fn readFromPackedMemory( .Float => return Value.fromInterned((try mod.intern(.{ .float = .{ .ty = ty.toIntern(), .storage = switch (ty.floatBits(target)) { - 16 => .{ .f16 = @as(f16, @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian))) }, - 32 => .{ .f32 = @as(f32, @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian))) }, - 64 => .{ .f64 = @as(f64, @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian))) }, - 80 => .{ .f80 = @as(f80, @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian))) }, - 128 => .{ .f128 = @as(f128, @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian))) }, + 16 => .{ .f16 = @bitCast(std.mem.readPackedInt(u16, buffer, bit_offset, endian)) }, + 32 => .{ .f32 = @bitCast(std.mem.readPackedInt(u32, buffer, bit_offset, endian)) }, + 64 => .{ .f64 = @bitCast(std.mem.readPackedInt(u64, buffer, bit_offset, endian)) }, + 80 => .{ .f80 = @bitCast(std.mem.readPackedInt(u80, buffer, bit_offset, endian)) }, + 128 => .{ .f128 = @bitCast(std.mem.readPackedInt(u128, buffer, bit_offset, endian)) }, else => unreachable, }, } }))), .Vector => { const elem_ty = ty.childType(mod); - const elems = try arena.alloc(InternPool.Index, @as(usize, @intCast(ty.arrayLen(mod)))); + const elems = try arena.alloc(InternPool.Index, @intCast(ty.arrayLen(mod))); var bits: u16 = 0; - const elem_bit_size = @as(u16, @intCast(elem_ty.bitSize(mod))); + const elem_bit_size: u16 = @intCast(elem_ty.bitSize(mod)); for (elems, 0..) |_, i| { // On big-endian systems, LLVM reverses the element order of vectors by default const tgt_elem_i = if (endian == .big) elems.len - i - 1 else i; @@ -909,7 +910,7 @@ fn bigIntToFloat(limbs: []const std.math.big.Limb, positive: bool) f128 { var i: usize = limbs.len; while (i != 0) { i -= 1; - const limb: f128 = @as(f128, @floatFromInt(limbs[i])); + const limb: f128 = @floatFromInt(limbs[i]); result = @mulAdd(f128, base, result, limb); } if (positive) { @@ -934,7 +935,7 @@ pub fn ctz(val: Value, ty: Type, mod: *Module) u64 { pub fn popCount(val: Value, ty: Type, mod: *Module) u64 { var bigint_buf: BigIntSpace = undefined; const bigint = val.toBigInt(&bigint_buf, mod); - return @as(u64, @intCast(bigint.popCount(ty.intInfo(mod).bits))); + return @intCast(bigint.popCount(ty.intInfo(mod).bits)); } pub fn bitReverse(val: Value, ty: Type, mod: *Module, arena: Allocator) !Value { @@ -1191,7 +1192,7 @@ pub fn compareAllWithZeroAdvancedExtra( inline else => |x| if (std.math.isNan(x)) return op == .neq, }, .aggregate => |aggregate| return switch (aggregate.storage) { - .bytes => |bytes| for (bytes) |byte| { + .bytes => |bytes| for (bytes.toSlice(lhs.typeOf(mod).arrayLenIncludingSentinel(mod), &mod.intern_pool)) |byte| { if (!std.math.order(byte, 0).compare(op)) break false; } else true, .elems => |elems| for (elems) |elem| { @@ -1279,7 +1280,7 @@ pub fn elemValue(val: Value, zcu: *Zcu, index: usize) Allocator.Error!Value { if (index < len) return Value.fromInterned(switch (aggregate.storage) { .bytes => |bytes| try zcu.intern(.{ .int = .{ .ty = .u8_type, - .storage = .{ .u64 = bytes[index] }, + .storage = .{ .u64 = bytes.at(index, ip) }, } }), .elems => |elems| elems[index], .repeated_elem => |elem| elem, @@ -1318,28 +1319,37 @@ pub fn sliceArray( start: usize, end: usize, ) error{OutOfMemory}!Value { - // TODO: write something like getCoercedInts to avoid needing to dupe const mod = sema.mod; - const aggregate = mod.intern_pool.indexToKey(val.toIntern()).aggregate; - return Value.fromInterned(try mod.intern(.{ .aggregate = .{ - .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) { - .array_type => |array_type| try mod.arrayType(.{ - .len = @as(u32, @intCast(end - start)), - .child = array_type.child, - .sentinel = if (end == array_type.len) array_type.sentinel else .none, - }), - .vector_type => |vector_type| try mod.vectorType(.{ - .len = @as(u32, @intCast(end - start)), - .child = vector_type.child, - }), - else => unreachable, - }.toIntern(), - .storage = switch (aggregate.storage) { - .bytes => .{ .bytes = try sema.arena.dupe(u8, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.bytes[start..end]) }, - .elems => .{ .elems = try sema.arena.dupe(InternPool.Index, mod.intern_pool.indexToKey(val.toIntern()).aggregate.storage.elems[start..end]) }, - .repeated_elem => |elem| .{ .repeated_elem = elem }, + const ip = &mod.intern_pool; + return Value.fromInterned(try mod.intern(.{ + .aggregate = .{ + .ty = switch (mod.intern_pool.indexToKey(mod.intern_pool.typeOf(val.toIntern()))) { + .array_type => |array_type| try mod.arrayType(.{ + .len = @intCast(end - start), + .child = array_type.child, + .sentinel = if (end == array_type.len) array_type.sentinel else .none, + }), + .vector_type => |vector_type| try mod.vectorType(.{ + .len = @intCast(end - start), + .child = vector_type.child, + }), + else => unreachable, + }.toIntern(), + .storage = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { + .bytes => |bytes| storage: { + try ip.string_bytes.ensureUnusedCapacity(sema.gpa, end - start + 1); + break :storage .{ .bytes = try ip.getOrPutString( + sema.gpa, + bytes.toSlice(end, ip)[start..], + .maybe_embedded_nulls, + ) }; + }, + // TODO: write something like getCoercedInts to avoid needing to dupe + .elems => |elems| .{ .elems = try sema.arena.dupe(InternPool.Index, elems[start..end]) }, + .repeated_elem => |elem| .{ .repeated_elem = elem }, + }, }, - } })); + })); } pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value { @@ -1350,7 +1360,7 @@ pub fn fieldValue(val: Value, mod: *Module, index: usize) !Value { .aggregate => |aggregate| Value.fromInterned(switch (aggregate.storage) { .bytes => |bytes| try mod.intern(.{ .int = .{ .ty = .u8_type, - .storage = .{ .u64 = bytes[index] }, + .storage = .{ .u64 = bytes.at(index, &mod.intern_pool) }, } }), .elems => |elems| elems[index], .repeated_elem => |elem| elem, @@ -1461,7 +1471,7 @@ pub fn getErrorName(val: Value, mod: *const Module) InternPool.OptionalNullTermi pub fn getErrorInt(val: Value, mod: *const Module) Module.ErrorInt { return if (getErrorName(val, mod).unwrap()) |err_name| - @as(Module.ErrorInt, @intCast(mod.global_error_set.getIndex(err_name).?)) + @intCast(mod.global_error_set.getIndex(err_name).?) else 0; } @@ -2413,14 +2423,14 @@ pub fn intTruncBitsAsValue( for (result_data, 0..) |*scalar, i| { const elem_val = try val.elemValue(mod, i); const bits_elem = try bits.elemValue(mod, i); - scalar.* = (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @as(u16, @intCast(bits_elem.toUnsignedInt(mod))), mod)).toIntern(); + scalar.* = (try intTruncScalar(elem_val, scalar_ty, allocator, signedness, @intCast(bits_elem.toUnsignedInt(mod)), mod)).toIntern(); } return Value.fromInterned((try mod.intern(.{ .aggregate = .{ .ty = ty.toIntern(), .storage = .{ .elems = result_data }, } }))); } - return intTruncScalar(val, ty, allocator, signedness, @as(u16, @intCast(bits.toUnsignedInt(mod))), mod); + return intTruncScalar(val, ty, allocator, signedness, @intCast(bits.toUnsignedInt(mod)), mod); } pub fn intTruncScalar( @@ -2468,7 +2478,7 @@ pub fn shlScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *M // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; const lhs_bigint = lhs.toBigInt(&lhs_space, mod); - const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod))); + const shift: usize = @intCast(rhs.toUnsignedInt(mod)); const limbs = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, @@ -2530,7 +2540,7 @@ pub fn shlWithOverflowScalar( const info = ty.intInfo(mod); var lhs_space: Value.BigIntSpace = undefined; const lhs_bigint = lhs.toBigInt(&lhs_space, mod); - const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod))); + const shift: usize = @intCast(rhs.toUnsignedInt(mod)); const limbs = try allocator.alloc( std.math.big.Limb, lhs_bigint.limbs.len + (shift / (@sizeOf(std.math.big.Limb) * 8)) + 1, @@ -2587,7 +2597,7 @@ pub fn shlSatScalar( var lhs_space: Value.BigIntSpace = undefined; const lhs_bigint = lhs.toBigInt(&lhs_space, mod); - const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod))); + const shift: usize = @intCast(rhs.toUnsignedInt(mod)); const limbs = try arena.alloc( std.math.big.Limb, std.math.big.int.calcTwosCompLimbCount(info.bits) + 1, @@ -2659,7 +2669,7 @@ pub fn shrScalar(lhs: Value, rhs: Value, ty: Type, allocator: Allocator, mod: *M // resorting to BigInt first. var lhs_space: Value.BigIntSpace = undefined; const lhs_bigint = lhs.toBigInt(&lhs_space, mod); - const shift = @as(usize, @intCast(rhs.toUnsignedInt(mod))); + const shift: usize = @intCast(rhs.toUnsignedInt(mod)); const result_limbs = lhs_bigint.limbs.len -| (shift / (@sizeOf(std.math.big.Limb) * 8)); if (result_limbs == 0) { diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index b9f8259c05..ddde72345e 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -4345,8 +4345,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier .data = .{ .reg = .x30 }, }); } else if (func_value.getExternFunc(mod)) |extern_func| { - const decl_name = mod.intern_pool.stringToSlice(mod.declPtr(extern_func.decl).name); - const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); + const decl_name = mod.declPtr(extern_func.decl).name.toSlice(&mod.intern_pool); + const lib_name = extern_func.lib_name.toSlice(&mod.intern_pool); if (self.bin_file.cast(link.File.MachO)) |macho_file| { _ = macho_file; @panic("TODO airCall"); diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index 022f2f9bee..83159ec80e 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -2199,9 +2199,9 @@ fn airCall(func: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModif const atom = func.bin_file.getAtomPtr(atom_index); const type_index = try func.bin_file.storeDeclType(extern_func.decl, func_type); try func.bin_file.addOrUpdateImport( - mod.intern_pool.stringToSlice(ext_decl.name), + ext_decl.name.toSlice(&mod.intern_pool), atom.sym_index, - mod.intern_pool.stringToSliceUnwrap(ext_decl.getOwnedExternFunc(mod).?.lib_name), + ext_decl.getOwnedExternFunc(mod).?.lib_name.toSlice(&mod.intern_pool), type_index, ); break :blk extern_func.decl; @@ -7236,8 +7236,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { defer arena_allocator.deinit(); const arena = arena_allocator.allocator(); - const fqn = ip.stringToSlice(try mod.declPtr(enum_decl_index).fullyQualifiedName(mod)); - const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn}); + const fqn = try mod.declPtr(enum_decl_index).fullyQualifiedName(mod); + const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{fqn.fmt(ip)}); // check if we already generated code for this. if (func.bin_file.findGlobalSymbol(func_name)) |loc| { @@ -7268,17 +7268,18 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { // generate an if-else chain for each tag value as well as constant. const tag_names = enum_ty.enumFields(mod); for (0..tag_names.len) |tag_index| { - const tag_name = ip.stringToSlice(tag_names.get(ip)[tag_index]); + const tag_name = tag_names.get(ip)[tag_index]; + const tag_name_len = tag_name.length(ip); // for each tag name, create an unnamed const, // and then get a pointer to its value. const name_ty = try mod.arrayType(.{ - .len = tag_name.len, + .len = tag_name_len, .child = .u8_type, .sentinel = .zero_u8, }); const name_val = try mod.intern(.{ .aggregate = .{ .ty = name_ty.toIntern(), - .storage = .{ .bytes = tag_name }, + .storage = .{ .bytes = tag_name.toString() }, } }); const tag_sym_index = try func.bin_file.lowerUnnamedConst( Value.fromInterned(name_val), @@ -7338,7 +7339,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { // store length try writer.writeByte(std.wasm.opcode(.i32_const)); - try leb.writeULEB128(writer, @as(u32, @intCast(tag_name.len))); + try leb.writeULEB128(writer, @as(u32, @intCast(tag_name_len))); try writer.writeByte(std.wasm.opcode(.i32_store)); try leb.writeULEB128(writer, encoded_alignment); try leb.writeULEB128(writer, @as(u32, 4)); @@ -7359,7 +7360,7 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { // store length try writer.writeByte(std.wasm.opcode(.i64_const)); - try leb.writeULEB128(writer, @as(u64, @intCast(tag_name.len))); + try leb.writeULEB128(writer, @as(u64, @intCast(tag_name_len))); try writer.writeByte(std.wasm.opcode(.i64_store)); try leb.writeULEB128(writer, encoded_alignment); try leb.writeULEB128(writer, @as(u32, 8)); diff --git a/src/arch/x86_64/CodeGen.zig b/src/arch/x86_64/CodeGen.zig index 7a90eacf54..c165baf7e8 100644 --- a/src/arch/x86_64/CodeGen.zig +++ b/src/arch/x86_64/CodeGen.zig @@ -2247,7 +2247,7 @@ fn genLazy(self: *Self, lazy_sym: link.File.LazySymbol) InnerError!void { var data_off: i32 = 0; const tag_names = enum_ty.enumFields(mod); for (exitlude_jump_relocs, 0..) |*exitlude_jump_reloc, tag_index| { - const tag_name_len = ip.stringToSlice(tag_names.get(ip)[tag_index]).len; + const tag_name_len = tag_names.get(ip)[tag_index].length(ip); const tag_val = try mod.enumValueFieldIndex(enum_ty, @intCast(tag_index)); const tag_mcv = try self.genTypedValue(tag_val); try self.genBinOpMir(.{ ._, .cmp }, enum_ty, enum_mcv, tag_mcv); @@ -12314,8 +12314,8 @@ fn genCall(self: *Self, info: union(enum) { }, .extern_func => |extern_func| { const owner_decl = mod.declPtr(extern_func.decl); - const lib_name = mod.intern_pool.stringToSliceUnwrap(extern_func.lib_name); - const decl_name = mod.intern_pool.stringToSlice(owner_decl.name); + const lib_name = extern_func.lib_name.toSlice(&mod.intern_pool); + const decl_name = owner_decl.name.toSlice(&mod.intern_pool); try self.genExternSymbolRef(.call, lib_name, decl_name); }, else => return self.fail("TODO implement calling bitcasted functions", .{}), diff --git a/src/codegen.zig b/src/codegen.zig index 76be8be974..b45777564a 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -97,7 +97,7 @@ fn writeFloat(comptime F: type, f: F, target: Target, endian: std.builtin.Endian _ = target; const bits = @typeInfo(F).Float.bits; const Int = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = bits } }); - const int = @as(Int, @bitCast(f)); + const int: Int = @bitCast(f); mem.writeInt(Int, code[0..@divExact(bits, 8)], int, endian); } @@ -136,24 +136,24 @@ pub fn generateLazySymbol( if (lazy_sym.ty.isAnyError(zcu)) { alignment.* = .@"4"; const err_names = zcu.global_error_set.keys(); - mem.writeInt(u32, try code.addManyAsArray(4), @as(u32, @intCast(err_names.len)), endian); + mem.writeInt(u32, try code.addManyAsArray(4), @intCast(err_names.len), endian); var offset = code.items.len; try code.resize((1 + err_names.len + 1) * 4); for (err_names) |err_name_nts| { - const err_name = zcu.intern_pool.stringToSlice(err_name_nts); - mem.writeInt(u32, code.items[offset..][0..4], @as(u32, @intCast(code.items.len)), endian); + const err_name = err_name_nts.toSlice(ip); + mem.writeInt(u32, code.items[offset..][0..4], @intCast(code.items.len), endian); offset += 4; try code.ensureUnusedCapacity(err_name.len + 1); code.appendSliceAssumeCapacity(err_name); code.appendAssumeCapacity(0); } - mem.writeInt(u32, code.items[offset..][0..4], @as(u32, @intCast(code.items.len)), endian); + mem.writeInt(u32, code.items[offset..][0..4], @intCast(code.items.len), endian); return Result.ok; } else if (lazy_sym.ty.zigTypeTag(zcu) == .Enum) { alignment.* = .@"1"; const tag_names = lazy_sym.ty.enumFields(zcu); for (0..tag_names.len) |tag_index| { - const tag_name = zcu.intern_pool.stringToSlice(tag_names.get(ip)[tag_index]); + const tag_name = tag_names.get(ip)[tag_index].toSlice(ip); try code.ensureUnusedCapacity(tag_name.len + 1); code.appendSliceAssumeCapacity(tag_name); code.appendAssumeCapacity(0); @@ -241,13 +241,13 @@ pub fn generateSymbol( }, .err => |err| { const int = try mod.getErrorValue(err.name); - try code.writer().writeInt(u16, @as(u16, @intCast(int)), endian); + try code.writer().writeInt(u16, @intCast(int), endian); }, .error_union => |error_union| { const payload_ty = ty.errorUnionPayload(mod); - const err_val = switch (error_union.val) { - .err_name => |err_name| @as(u16, @intCast(try mod.getErrorValue(err_name))), - .payload => @as(u16, 0), + const err_val: u16 = switch (error_union.val) { + .err_name => |err_name| @intCast(try mod.getErrorValue(err_name)), + .payload => 0, }; if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { @@ -357,15 +357,13 @@ pub fn generateSymbol( }, .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { .array_type => |array_type| switch (aggregate.storage) { - .bytes => |bytes| try code.appendSlice(bytes), + .bytes => |bytes| try code.appendSlice(bytes.toSlice(array_type.lenIncludingSentinel(), ip)), .elems, .repeated_elem => { var index: u64 = 0; - const len_including_sentinel = - array_type.len + @intFromBool(array_type.sentinel != .none); - while (index < len_including_sentinel) : (index += 1) { + while (index < array_type.lenIncludingSentinel()) : (index += 1) { switch (try generateSymbol(bin_file, src_loc, Value.fromInterned(switch (aggregate.storage) { .bytes => unreachable, - .elems => |elems| elems[@as(usize, @intCast(index))], + .elems => |elems| elems[@intCast(index)], .repeated_elem => |elem| if (index < array_type.len) elem else @@ -399,7 +397,7 @@ pub fn generateSymbol( }) { .bool_true => true, .bool_false => false, - else => |elem| switch (mod.intern_pool.indexToKey(elem)) { + else => |elem| switch (ip.indexToKey(elem)) { .undef => continue, .int => |int| switch (int.storage) { .u64 => |x| switch (x) { @@ -420,7 +418,7 @@ pub fn generateSymbol( } } else { switch (aggregate.storage) { - .bytes => |bytes| try code.appendSlice(bytes), + .bytes => |bytes| try code.appendSlice(bytes.toSlice(vector_type.len, ip)), .elems, .repeated_elem => { var index: u64 = 0; while (index < vector_type.len) : (index += 1) { @@ -457,7 +455,7 @@ pub fn generateSymbol( const field_val = switch (aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty, - .storage = .{ .u64 = bytes[index] }, + .storage = .{ .u64 = bytes.at(index, ip) }, } }), .elems => |elems| elems[index], .repeated_elem => |elem| elem, @@ -493,7 +491,7 @@ pub fn generateSymbol( const field_val = switch (aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty, - .storage = .{ .u64 = bytes[index] }, + .storage = .{ .u64 = bytes.at(index, ip) }, } }), .elems => |elems| elems[index], .repeated_elem => |elem| elem, @@ -513,7 +511,7 @@ pub fn generateSymbol( } else { Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), mod, code.items[current_pos..], bits) catch unreachable; } - bits += @as(u16, @intCast(Type.fromInterned(field_ty).bitSize(mod))); + bits += @intCast(Type.fromInterned(field_ty).bitSize(mod)); } }, .auto, .@"extern" => { @@ -529,7 +527,7 @@ pub fn generateSymbol( const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty, - .storage = .{ .u64 = bytes[field_index] }, + .storage = .{ .u64 = bytes.at(field_index, ip) }, } }), .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, @@ -625,7 +623,8 @@ fn lowerParentPtr( reloc_info: RelocInfo, ) CodeGenError!Result { const mod = bin_file.comp.module.?; - const ptr = mod.intern_pool.indexToKey(parent_ptr).ptr; + const ip = &mod.intern_pool; + const ptr = ip.indexToKey(parent_ptr).ptr; return switch (ptr.addr) { .decl => |decl| try lowerDeclRef(bin_file, src_loc, decl, code, debug_output, reloc_info), .anon_decl => |ad| try lowerAnonDeclRef(bin_file, src_loc, ad, code, debug_output, reloc_info), @@ -636,10 +635,10 @@ fn lowerParentPtr( eu_payload, code, debug_output, - reloc_info.offset(@as(u32, @intCast(errUnionPayloadOffset( - Type.fromInterned(mod.intern_pool.typeOf(eu_payload)), + reloc_info.offset(@intCast(errUnionPayloadOffset( + Type.fromInterned(ip.typeOf(eu_payload)), mod, - )))), + ))), ), .opt_payload => |opt_payload| try lowerParentPtr( bin_file, @@ -655,19 +654,19 @@ fn lowerParentPtr( elem.base, code, debug_output, - reloc_info.offset(@as(u32, @intCast(elem.index * - Type.fromInterned(mod.intern_pool.typeOf(elem.base)).elemType2(mod).abiSize(mod)))), + reloc_info.offset(@intCast(elem.index * + Type.fromInterned(ip.typeOf(elem.base)).elemType2(mod).abiSize(mod))), ), .field => |field| { - const base_ptr_ty = mod.intern_pool.typeOf(field.base); - const base_ty = mod.intern_pool.indexToKey(base_ptr_ty).ptr_type.child; + const base_ptr_ty = ip.typeOf(field.base); + const base_ty = ip.indexToKey(base_ptr_ty).ptr_type.child; return lowerParentPtr( bin_file, src_loc, field.base, code, debug_output, - reloc_info.offset(switch (mod.intern_pool.indexToKey(base_ty)) { + reloc_info.offset(switch (ip.indexToKey(base_ty)) { .ptr_type => |ptr_type| switch (ptr_type.flags.size) { .One, .Many, .C => unreachable, .Slice => switch (field.index) { @@ -723,11 +722,12 @@ fn lowerAnonDeclRef( ) CodeGenError!Result { _ = debug_output; const zcu = lf.comp.module.?; + const ip = &zcu.intern_pool; const target = lf.comp.root_mod.resolved_target.result; const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); const decl_val = anon_decl.val; - const decl_ty = Type.fromInterned(zcu.intern_pool.typeOf(decl_val)); + const decl_ty = Type.fromInterned(ip.typeOf(decl_val)); log.debug("lowerAnonDecl: ty = {}", .{decl_ty.fmt(zcu)}); const is_fn_body = decl_ty.zigTypeTag(zcu) == .Fn; if (!is_fn_body and !decl_ty.hasRuntimeBits(zcu)) { @@ -735,7 +735,7 @@ fn lowerAnonDeclRef( return Result.ok; } - const decl_align = zcu.intern_pool.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; + const decl_align = ip.indexToKey(anon_decl.orig_ty).ptr_type.flags.alignment; const res = try lf.lowerAnonDecl(decl_val, decl_align, src_loc); switch (res) { .ok => {}, @@ -787,8 +787,8 @@ fn lowerDeclRef( }); const endian = target.cpu.arch.endian(); switch (ptr_width) { - 16 => mem.writeInt(u16, try code.addManyAsArray(2), @as(u16, @intCast(vaddr)), endian), - 32 => mem.writeInt(u32, try code.addManyAsArray(4), @as(u32, @intCast(vaddr)), endian), + 16 => mem.writeInt(u16, try code.addManyAsArray(2), @intCast(vaddr), endian), + 32 => mem.writeInt(u32, try code.addManyAsArray(4), @intCast(vaddr), endian), 64 => mem.writeInt(u64, try code.addManyAsArray(8), vaddr, endian), else => unreachable, } @@ -859,6 +859,7 @@ fn genDeclRef( ptr_decl_index: InternPool.DeclIndex, ) CodeGenError!GenResult { const zcu = lf.comp.module.?; + const ip = &zcu.intern_pool; const ty = val.typeOf(zcu); log.debug("genDeclRef: val = {}", .{val.fmtValue(zcu)}); @@ -869,7 +870,7 @@ fn genDeclRef( const ptr_bits = target.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); - const decl_index = switch (zcu.intern_pool.indexToKey(ptr_decl.val.toIntern())) { + const decl_index = switch (ip.indexToKey(ptr_decl.val.toIntern())) { .func => |func| func.owner_decl, .extern_func => |extern_func| extern_func.decl, else => ptr_decl_index, @@ -909,12 +910,9 @@ fn genDeclRef( if (lf.cast(link.File.Elf)) |elf_file| { if (is_extern) { - const name = zcu.intern_pool.stringToSlice(decl.name); + const name = decl.name.toSlice(ip); // TODO audit this - const lib_name = if (decl.getOwnedVariable(zcu)) |ov| - zcu.intern_pool.stringToSliceUnwrap(ov.lib_name) - else - null; + const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null; const sym_index = try elf_file.getGlobalSymbol(name, lib_name); elf_file.symbol(elf_file.zigObjectPtr().?.symbol(sym_index)).flags.needs_got = true; return GenResult.mcv(.{ .load_symbol = sym_index }); @@ -927,11 +925,8 @@ fn genDeclRef( return GenResult.mcv(.{ .load_symbol = sym.esym_index }); } else if (lf.cast(link.File.MachO)) |macho_file| { if (is_extern) { - const name = zcu.intern_pool.stringToSlice(decl.name); - const lib_name = if (decl.getOwnedVariable(zcu)) |ov| - zcu.intern_pool.stringToSliceUnwrap(ov.lib_name) - else - null; + const name = decl.name.toSlice(ip); + const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null; const sym_index = try macho_file.getGlobalSymbol(name, lib_name); macho_file.getSymbol(macho_file.getZigObject().?.symbols.items[sym_index]).flags.needs_got = true; return GenResult.mcv(.{ .load_symbol = sym_index }); @@ -944,12 +939,9 @@ fn genDeclRef( return GenResult.mcv(.{ .load_symbol = sym.nlist_idx }); } else if (lf.cast(link.File.Coff)) |coff_file| { if (is_extern) { - const name = zcu.intern_pool.stringToSlice(decl.name); + const name = decl.name.toSlice(ip); // TODO audit this - const lib_name = if (decl.getOwnedVariable(zcu)) |ov| - zcu.intern_pool.stringToSliceUnwrap(ov.lib_name) - else - null; + const lib_name = if (decl.getOwnedVariable(zcu)) |ov| ov.lib_name.toSlice(ip) else null; const global_index = try coff_file.getGlobalSymbol(name, lib_name); try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT return GenResult.mcv(.{ .load_got = link.File.Coff.global_symbol_bit | global_index }); @@ -1012,6 +1004,7 @@ pub fn genTypedValue( owner_decl_index: InternPool.DeclIndex, ) CodeGenError!GenResult { const zcu = lf.comp.module.?; + const ip = &zcu.intern_pool; const ty = val.typeOf(zcu); log.debug("genTypedValue: val = {}", .{val.fmtValue(zcu)}); @@ -1024,7 +1017,7 @@ pub fn genTypedValue( const target = namespace.file_scope.mod.resolved_target.result; const ptr_bits = target.ptrBitWidth(); - if (!ty.isSlice(zcu)) switch (zcu.intern_pool.indexToKey(val.toIntern())) { + if (!ty.isSlice(zcu)) switch (ip.indexToKey(val.toIntern())) { .ptr => |ptr| switch (ptr.addr) { .decl => |decl| return genDeclRef(lf, src_loc, val, decl), else => {}, @@ -1041,7 +1034,7 @@ pub fn genTypedValue( return GenResult.mcv(.{ .immediate = 0 }); }, .none => {}, - else => switch (zcu.intern_pool.indexToKey(val.toIntern())) { + else => switch (ip.indexToKey(val.toIntern())) { .int => { return GenResult.mcv(.{ .immediate = val.toUnsignedInt(zcu) }); }, @@ -1052,8 +1045,8 @@ pub fn genTypedValue( .Int => { const info = ty.intInfo(zcu); if (info.bits <= ptr_bits) { - const unsigned = switch (info.signedness) { - .signed => @as(u64, @bitCast(val.toSignedInt(zcu))), + const unsigned: u64 = switch (info.signedness) { + .signed => @bitCast(val.toSignedInt(zcu)), .unsigned => val.toUnsignedInt(zcu), }; return GenResult.mcv(.{ .immediate = unsigned }); @@ -1075,7 +1068,7 @@ pub fn genTypedValue( } }, .Enum => { - const enum_tag = zcu.intern_pool.indexToKey(val.toIntern()).enum_tag; + const enum_tag = ip.indexToKey(val.toIntern()).enum_tag; return genTypedValue( lf, src_loc, @@ -1084,7 +1077,7 @@ pub fn genTypedValue( ); }, .ErrorSet => { - const err_name = zcu.intern_pool.indexToKey(val.toIntern()).err.name; + const err_name = ip.indexToKey(val.toIntern()).err.name; const error_index = zcu.global_error_set.getIndex(err_name).?; return GenResult.mcv(.{ .immediate = error_index }); }, @@ -1094,7 +1087,7 @@ pub fn genTypedValue( if (!payload_type.hasRuntimeBitsIgnoreComptime(zcu)) { // We use the error type directly as the type. const err_int_ty = try zcu.errorIntType(); - switch (zcu.intern_pool.indexToKey(val.toIntern()).error_union.val) { + switch (ip.indexToKey(val.toIntern()).error_union.val) { .err_name => |err_name| return genTypedValue( lf, src_loc, diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 95f4ecc4ac..818267a8b8 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -43,10 +43,12 @@ pub const CValue = union(enum) { decl_ref: InternPool.DeclIndex, /// An undefined value (cannot be dereferenced) undef: Type, - /// Render the slice as an identifier (using fmtIdent) + /// Rendered as an identifier (using fmtIdent) identifier: []const u8, - /// Render the slice as an payload.identifier (using fmtIdent) + /// Rendered as "payload." followed by as identifier (using fmtIdent) payload_identifier: []const u8, + /// Rendered with fmtCTypePoolString + ctype_pool_string: CType.Pool.String, }; const BlockData = struct { @@ -62,10 +64,10 @@ pub const LazyFnKey = union(enum) { never_inline: InternPool.DeclIndex, }; pub const LazyFnValue = struct { - fn_name: CType.String, + fn_name: CType.Pool.String, data: Data, - pub const Data = union { + const Data = union { tag_name: Type, never_tail: void, never_inline: void, @@ -80,7 +82,7 @@ const Local = struct { _: u20 = undefined, }, - pub fn getType(local: Local) LocalType { + fn getType(local: Local) LocalType { return .{ .ctype = local.ctype, .alignas = local.flags.alignas }; } }; @@ -96,12 +98,20 @@ const ValueRenderLocation = enum { StaticInitializer, Other, - pub fn isInitializer(self: ValueRenderLocation) bool { - return switch (self) { + fn isInitializer(loc: ValueRenderLocation) bool { + return switch (loc) { .Initializer, .StaticInitializer => true, else => false, }; } + + fn toCTypeKind(loc: ValueRenderLocation) CType.Kind { + return switch (loc) { + .FunctionArgument => .parameter, + .Initializer, .Other => .complete, + .StaticInitializer => .global, + }; + } }; const BuiltinInfo = enum { none, bits }; @@ -234,12 +244,11 @@ fn isReservedIdent(ident: []const u8) bool { fn formatIdent( ident: []const u8, - comptime fmt: []const u8, - options: std.fmt.FormatOptions, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, writer: anytype, -) !void { - _ = options; - const solo = fmt.len != 0 and fmt[0] == ' '; // space means solo; not part of a bigger ident. +) @TypeOf(writer).Error!void { + const solo = fmt_str.len != 0 and fmt_str[0] == ' '; // space means solo; not part of a bigger ident. if (solo and isReservedIdent(ident)) { try writer.writeAll("zig_e_"); } @@ -256,11 +265,32 @@ fn formatIdent( } } } - pub fn fmtIdent(ident: []const u8) std.fmt.Formatter(formatIdent) { return .{ .data = ident }; } +const CTypePoolStringFormatData = struct { + ctype_pool_string: CType.Pool.String, + ctype_pool: *const CType.Pool, +}; +fn formatCTypePoolString( + data: CTypePoolStringFormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, +) @TypeOf(writer).Error!void { + if (data.ctype_pool_string.toSlice(data.ctype_pool)) |slice| + try formatIdent(slice, fmt_str, fmt_opts, writer) + else + try writer.print("{}", .{data.ctype_pool_string.fmt(data.ctype_pool)}); +} +pub fn fmtCTypePoolString( + ctype_pool_string: CType.Pool.String, + ctype_pool: *const CType.Pool, +) std.fmt.Formatter(formatCTypePoolString) { + return .{ .data = .{ .ctype_pool_string = ctype_pool_string, .ctype_pool = ctype_pool } }; +} + // Returns true if `formatIdent` would make any edits to ident. // This must be kept in sync with `formatIdent`. pub fn isMangledIdent(ident: []const u8, solo: bool) bool { @@ -321,7 +351,7 @@ pub const Function = struct { try writer.writeAll(" = "); try f.object.dg.renderValue(writer, val, .StaticInitializer); try writer.writeAll(";\n "); - break :result decl_c_value; + break :result .{ .local = decl_c_value.new_local }; } else .{ .constant = val }; gop.value_ptr.* = result; @@ -377,27 +407,7 @@ pub const Function = struct { switch (c_value) { .none => unreachable, .new_local, .local => |i| try w.print("t{d}", .{i}), - .local_ref => |i| { - const local = &f.locals.items[i]; - if (local.flags.alignas.abiOrder().compare(.lt)) { - const gpa = f.object.dg.gpa; - const mod = f.object.dg.mod; - const ctype_pool = &f.object.dg.ctype_pool; - - try w.writeByte('('); - try f.renderCType(w, try ctype_pool.getPointer(gpa, .{ - .elem_ctype = try ctype_pool.fromIntInfo(gpa, .{ - .signedness = .unsigned, - .bits = @min( - local.flags.alignas.toByteUnits(), - mod.resolved_target.result.maxIntAlignment(), - ) * 8, - }, mod, .forward), - })); - try w.writeByte(')'); - } - try w.print("&t{d}", .{i}); - }, + .local_ref => |i| try w.print("&t{d}", .{i}), .constant => |val| try f.object.dg.renderValue(w, val, location), .arg => |i| try w.print("a{d}", .{i}), .arg_array => |i| try f.writeCValueMember(w, .{ .arg = i }, .{ .identifier = "array" }), @@ -505,7 +515,7 @@ pub const Function = struct { .never_inline, => |owner_decl| try ctype_pool.fmt(gpa, "zig_{s}_{}__{d}", .{ @tagName(key), - fmtIdent(zcu.intern_pool.stringToSlice(zcu.declPtr(owner_decl).name)), + fmtIdent(zcu.declPtr(owner_decl).name.toSlice(&zcu.intern_pool)), @intFromEnum(owner_decl), }), }, @@ -516,7 +526,7 @@ pub const Function = struct { }, }; } - return gop.value_ptr.fn_name.slice(ctype_pool); + return gop.value_ptr.fn_name.toSlice(ctype_pool).?; } pub fn deinit(f: *Function) void { @@ -538,6 +548,43 @@ pub const Function = struct { const zcu = f.object.dg.zcu; return f.air.typeOfIndex(inst, &zcu.intern_pool); } + + fn copyCValue(f: *Function, ctype: CType, dst: CValue, src: CValue) !void { + switch (dst) { + .new_local, .local => |dst_local_index| switch (src) { + .new_local, .local => |src_local_index| if (dst_local_index == src_local_index) return, + else => {}, + }, + else => {}, + } + const writer = f.object.writer(); + const a = try Assignment.start(f, writer, ctype); + try f.writeCValue(writer, dst, .Other); + try a.assign(f, writer); + try f.writeCValue(writer, src, .Initializer); + try a.end(f, writer); + } + + fn moveCValue(f: *Function, inst: Air.Inst.Index, ty: Type, src: CValue) !CValue { + switch (src) { + // Move the freshly allocated local to be owned by this instruction, + // by returning it here instead of freeing it. + .new_local => return src, + else => { + try freeCValue(f, inst, src); + const dst = try f.allocLocal(inst, ty); + try f.copyCValue(try f.ctypeFromType(ty, .complete), dst, src); + return dst; + }, + } + } + + fn freeCValue(f: *Function, inst: ?Air.Inst.Index, val: CValue) !void { + switch (val) { + .new_local => |local_index| try freeLocal(f, inst, local_index, null), + else => {}, + } + } }; /// This data is available when outputting .c code for a `Zcu`. @@ -627,13 +674,14 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const elem_ctype = (try dg.ctypeFromType(ptr_ty, .complete)).info(ctype_pool).pointer.elem_ctype; + const ptr_ctype = try dg.ctypeFromType(ptr_ty, .complete); + const elem_ctype = ptr_ctype.info(ctype_pool).pointer.elem_ctype; const decl_ctype = try dg.ctypeFromType(decl_ty, .complete); const need_cast = !elem_ctype.eql(decl_ctype) and (elem_ctype.info(ctype_pool) != .function or decl_ctype.info(ctype_pool) != .function); if (need_cast) { try writer.writeAll("(("); - try dg.renderType(writer, ptr_ty); + try dg.renderCType(writer, ptr_ctype); try writer.writeByte(')'); } try writer.writeByte('&'); @@ -692,13 +740,14 @@ pub const DeclGen = struct { // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug // somewhere and we should let the C compiler tell us about it. - const elem_ctype = (try dg.ctypeFromType(ty, .complete)).info(ctype_pool).pointer.elem_ctype; + const ctype = try dg.ctypeFromType(ty, .complete); + const elem_ctype = ctype.info(ctype_pool).pointer.elem_ctype; const decl_ctype = try dg.ctypeFromType(decl_ty, .complete); const need_cast = !elem_ctype.eql(decl_ctype) and (elem_ctype.info(ctype_pool) != .function or decl_ctype.info(ctype_pool) != .function); if (need_cast) { try writer.writeAll("(("); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } try writer.writeByte('&'); @@ -822,25 +871,18 @@ pub const DeclGen = struct { try dg.fmtIntLiteral(try zcu.intValue(Type.usize, byte_offset), .Other), }); }, - .end => { - const ptr_base_ctype = try dg.ctypeFromType(ptr_base_ty, .complete); - if (!ptr_ctype.eql(ptr_base_ctype)) { - try writer.writeByte('('); - try dg.renderCType(writer, ptr_ctype); - try writer.writeByte(')'); - } - try writer.writeAll("(("); - try dg.renderParentPtr(writer, field.base, location); - try writer.print(") + {})", .{ - try dg.fmtIntLiteral(try zcu.intValue(Type.usize, 1), .Other), - }); - }, } }, .comptime_field, .comptime_alloc => unreachable, } } + fn renderErrorName(dg: *DeclGen, writer: anytype, err_name: InternPool.NullTerminatedString) !void { + const zcu = dg.zcu; + const ip = &zcu.intern_pool; + try writer.print("zig_error_{}", .{fmtIdent(err_name.toSlice(ip))}); + } + fn renderValue( dg: *DeclGen, writer: anytype, @@ -850,6 +892,7 @@ pub const DeclGen = struct { const zcu = dg.zcu; const ip = &zcu.intern_pool; const target = &dg.mod.resolved_target.result; + const ctype_pool = &dg.ctype_pool; const initializer_type: ValueRenderLocation = switch (location) { .StaticInitializer => .StaticInitializer, @@ -858,6 +901,7 @@ pub const DeclGen = struct { const ty = val.typeOf(zcu); if (val.isUndefDeep(zcu)) return dg.renderUndefValue(writer, ty, location); + const ctype = try dg.ctypeFromType(ty, location.toCTypeKind()); switch (ip.indexToKey(val.toIntern())) { // types, not values .int_type, @@ -903,76 +947,53 @@ pub const DeclGen = struct { .u64, .i64, .big_int => try writer.print("{}", .{try dg.fmtIntLiteral(val, location)}), .lazy_align, .lazy_size => { try writer.writeAll("(("); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.print("){x})", .{try dg.fmtIntLiteral( try zcu.intValue(Type.usize, val.toUnsignedInt(zcu)), .Other, )}); }, }, - .err => |err| try writer.print("zig_error_{}", .{ - fmtIdent(ip.stringToSlice(err.name)), - }), - .error_union => |error_union| { - const payload_ty = ty.errorUnionPayload(zcu); - const error_ty = ty.errorUnionSet(zcu); - const err_int_ty = try zcu.errorIntType(); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - switch (error_union.val) { - .err_name => |err_name| return dg.renderValue( - writer, - Value.fromInterned((try zcu.intern(.{ .err = .{ - .ty = error_ty.toIntern(), - .name = err_name, - } }))), - location, - ), - .payload => return dg.renderValue( - writer, - try zcu.intValue(err_int_ty, 0), - location, - ), + .err => |err| try dg.renderErrorName(writer, err.name), + .error_union => |error_union| switch (ctype.info(ctype_pool)) { + .basic => switch (error_union.val) { + .err_name => |err_name| try dg.renderErrorName(writer, err_name), + .payload => try writer.writeAll("0"), + }, + .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderCType(writer, ctype); + try writer.writeByte(')'); } - } - - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } - - try writer.writeAll("{ .payload = "); - try dg.renderValue( - writer, - Value.fromInterned(switch (error_union.val) { - .err_name => (try zcu.undefValue(payload_ty)).toIntern(), - .payload => |payload| payload, - }), - initializer_type, - ); - try writer.writeAll(", .error = "); - switch (error_union.val) { - .err_name => |err_name| try dg.renderValue( - writer, - Value.fromInterned((try zcu.intern(.{ .err = .{ - .ty = error_ty.toIntern(), - .name = err_name, - } }))), - location, - ), - .payload => try dg.renderValue( - writer, - try zcu.intValue(err_int_ty, 0), - location, - ), - } - try writer.writeAll(" }"); + try writer.writeByte('{'); + for (0..aggregate.fields.len) |field_index| { + if (field_index > 0) try writer.writeByte(','); + switch (aggregate.fields.at(field_index, ctype_pool).name.index) { + .@"error" => switch (error_union.val) { + .err_name => |err_name| try dg.renderErrorName(writer, err_name), + .payload => try writer.writeByte('0'), + }, + .payload => switch (error_union.val) { + .err_name => try dg.renderUndefValue( + writer, + ty.errorUnionPayload(zcu), + initializer_type, + ), + .payload => |payload| try dg.renderValue( + writer, + Value.fromInterned(payload), + initializer_type, + ), + }, + else => unreachable, + } + } + try writer.writeByte('}'); + }, }, - .enum_tag => |enum_tag| try dg.renderValue( - writer, - Value.fromInterned(enum_tag.int), - location, - ), + .enum_tag => |enum_tag| try dg.renderValue(writer, Value.fromInterned(enum_tag.int), location), .float => { const bits = ty.floatBits(target.*); const f128_val = val.toFloat(f128, zcu); @@ -1063,15 +1084,23 @@ pub const DeclGen = struct { if (!empty) try writer.writeByte(')'); }, .slice => |slice| { + const aggregate = ctype.info(ctype_pool).aggregate; if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } try writer.writeByte('{'); - try dg.renderValue(writer, Value.fromInterned(slice.ptr), initializer_type); - try writer.writeAll(", "); - try dg.renderValue(writer, Value.fromInterned(slice.len), initializer_type); + for (0..aggregate.fields.len) |field_index| { + if (field_index > 0) try writer.writeByte(','); + try dg.renderValue(writer, Value.fromInterned( + switch (aggregate.fields.at(field_index, ctype_pool).name.index) { + .ptr => slice.ptr, + .len => slice.len, + else => unreachable, + }, + ), initializer_type); + } try writer.writeByte('}'); }, .ptr => |ptr| switch (ptr.addr) { @@ -1079,7 +1108,7 @@ pub const DeclGen = struct { .anon_decl => |decl_val| try dg.renderAnonDeclValue(writer, val, decl_val, location), .int => |int| { try writer.writeAll("(("); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.print("){x})", .{try dg.fmtIntLiteral(Value.fromInterned(int), location)}); }, .eu_payload, @@ -1089,54 +1118,80 @@ pub const DeclGen = struct { => try dg.renderParentPtr(writer, val.toIntern(), location), .comptime_field, .comptime_alloc => unreachable, }, - .opt => |opt| { - const payload_ty = ty.optionalChild(zcu); - - const is_null_val = Value.makeBool(opt.val == .none); - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) - return dg.renderValue(writer, is_null_val, location); - - if (ty.optionalReprIsPayload(zcu)) return dg.renderValue( - writer, + .opt => |opt| switch (ctype.info(ctype_pool)) { + .basic => if (ctype.isBool()) try writer.writeAll(switch (opt.val) { + .none => "true", + else => "false", + }) else switch (opt.val) { + .none => try writer.writeAll("0"), + else => |payload| switch (ip.indexToKey(payload)) { + .undef => |err_ty| try dg.renderUndefValue( + writer, + Type.fromInterned(err_ty), + location, + ), + .err => |err| try dg.renderErrorName(writer, err.name), + else => unreachable, + }, + }, + .pointer => switch (opt.val) { + .none => try writer.writeAll("NULL"), + else => |payload| try dg.renderValue(writer, Value.fromInterned(payload), location), + }, + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| { switch (opt.val) { - .none => switch (payload_ty.zigTypeTag(zcu)) { - .ErrorSet => try zcu.intValue(try zcu.errorIntType(), 0), - .Pointer => try zcu.getCoerced(val, payload_ty), + .none => {}, + else => |payload| switch (aggregate.fields.at(0, ctype_pool).name.index) { + .is_null, .payload => {}, + .ptr, .len => return dg.renderValue( + writer, + Value.fromInterned(payload), + location, + ), else => unreachable, }, - else => |payload| Value.fromInterned(payload), - }, - location, - ); - - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } - - try writer.writeAll("{ .payload = "); - switch (opt.val) { - .none => try dg.renderUndefValue(writer, payload_ty, initializer_type), - else => |payload| try dg.renderValue( - writer, - Value.fromInterned(payload), - initializer_type, - ), - } - try writer.writeAll(", .is_null = "); - try dg.renderValue(writer, is_null_val, initializer_type); - try writer.writeAll(" }"); + } + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderCType(writer, ctype); + try writer.writeByte(')'); + } + try writer.writeByte('{'); + for (0..aggregate.fields.len) |field_index| { + if (field_index > 0) try writer.writeByte(','); + switch (aggregate.fields.at(field_index, ctype_pool).name.index) { + .is_null => try writer.writeAll(switch (opt.val) { + .none => "true", + else => "false", + }), + .payload => switch (opt.val) { + .none => try dg.renderUndefValue( + writer, + ty.optionalChild(zcu), + initializer_type, + ), + else => |payload| try dg.renderValue( + writer, + Value.fromInterned(payload), + initializer_type, + ), + }, + .ptr => try writer.writeAll("NULL"), + .len => try dg.renderUndefValue(writer, Type.usize, initializer_type), + else => unreachable, + } + } + try writer.writeByte('}'); + }, }, .aggregate => switch (ip.indexToKey(ty.toIntern())) { .array_type, .vector_type => { if (location == .FunctionArgument) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } - // Fall back to generic implementation. - const ai = ty.arrayInfo(zcu); if (ai.elem_type.eql(Type.u8, zcu)) { var literal = stringLiteral(writer, ty.arrayLenIncludingSentinel(zcu)); @@ -1173,7 +1228,7 @@ pub const DeclGen = struct { .anon_struct_type => |tuple| { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } @@ -1191,7 +1246,7 @@ pub const DeclGen = struct { switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try ip.get(zcu.gpa, .{ .int = .{ .ty = field_ty.toIntern(), - .storage = .{ .u64 = bytes[field_index] }, + .storage = .{ .u64 = bytes.at(field_index, ip) }, } }), .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, @@ -1209,7 +1264,7 @@ pub const DeclGen = struct { .auto, .@"extern" => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } @@ -1225,7 +1280,7 @@ pub const DeclGen = struct { const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try ip.get(zcu.gpa, .{ .int = .{ .ty = field_ty.toIntern(), - .storage = .{ .u64 = bytes[field_index] }, + .storage = .{ .u64 = bytes.at(field_index, ip) }, } }), .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, @@ -1251,7 +1306,7 @@ pub const DeclGen = struct { if (eff_num_fields == 0) { try writer.writeByte('('); - try dg.renderUndefValue(writer, ty, initializer_type); + try dg.renderUndefValue(writer, ty, location); try writer.writeByte(')'); } else if (ty.bitSize(zcu) > 64) { // zig_or_u128(zig_or_u128(zig_shl_u128(a, a_off), zig_shl_u128(b, b_off)), zig_shl_u128(c, c_off)) @@ -1271,7 +1326,7 @@ pub const DeclGen = struct { const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try ip.get(zcu.gpa, .{ .int = .{ .ty = field_ty.toIntern(), - .storage = .{ .u64 = bytes[field_index] }, + .storage = .{ .u64 = bytes.at(field_index, ip) }, } }), .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, @@ -1306,13 +1361,13 @@ pub const DeclGen = struct { if (!empty) try writer.writeAll(" | "); try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { .bytes => |bytes| try ip.get(zcu.gpa, .{ .int = .{ .ty = field_ty.toIntern(), - .storage = .{ .u64 = bytes[field_index] }, + .storage = .{ .u64 = bytes.at(field_index, ip) }, } }), .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, @@ -1347,7 +1402,7 @@ pub const DeclGen = struct { try dg.renderType(writer, backing_ty); try writer.writeByte(')'); } - try dg.renderValue(writer, Value.fromInterned(un.val), initializer_type); + try dg.renderValue(writer, Value.fromInterned(un.val), location); }, .@"extern" => { if (location == .StaticInitializer) { @@ -1360,7 +1415,7 @@ pub const DeclGen = struct { try writer.writeAll(")("); try dg.renderType(writer, backing_ty); try writer.writeAll("){"); - try dg.renderValue(writer, Value.fromInterned(un.val), initializer_type); + try dg.renderValue(writer, Value.fromInterned(un.val), location); try writer.writeAll("})"); }, else => unreachable, @@ -1368,7 +1423,7 @@ pub const DeclGen = struct { } else { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } @@ -1379,43 +1434,56 @@ pub const DeclGen = struct { if (field_ty.hasRuntimeBits(zcu)) { if (field_ty.isPtrAtRuntime(zcu)) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } else if (field_ty.zigTypeTag(zcu) == .Float) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } - try dg.renderValue(writer, Value.fromInterned(un.val), initializer_type); - } else { - try writer.writeAll("0"); - } + try dg.renderValue(writer, Value.fromInterned(un.val), location); + } else try writer.writeAll("0"); return; } - try writer.writeByte('{'); - if (ty.unionTagTypeSafety(zcu)) |_| { - const layout = zcu.getUnionLayout(loaded_union); - if (layout.tag_size != 0) { - try writer.writeAll(" .tag = "); - try dg.renderValue(writer, Value.fromInterned(un.tag), initializer_type); + const has_tag = loaded_union.hasTag(ip); + if (has_tag) try writer.writeByte('{'); + const aggregate = ctype.info(ctype_pool).aggregate; + for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| { + if (outer_field_index > 0) try writer.writeByte(','); + switch (if (has_tag) + aggregate.fields.at(outer_field_index, ctype_pool).name.index + else + .payload) { + .tag => try dg.renderValue( + writer, + Value.fromInterned(un.tag), + initializer_type, + ), + .payload => { + try writer.writeByte('{'); + if (field_ty.hasRuntimeBits(zcu)) { + try writer.print(" .{ } = ", .{fmtIdent(field_name.toSlice(ip))}); + try dg.renderValue( + writer, + Value.fromInterned(un.val), + initializer_type, + ); + try writer.writeByte(' '); + } else for (0..loaded_union.field_types.len) |inner_field_index| { + const inner_field_ty = Type.fromInterned( + loaded_union.field_types.get(ip)[inner_field_index], + ); + if (!inner_field_ty.hasRuntimeBits(zcu)) continue; + try dg.renderUndefValue(writer, inner_field_ty, initializer_type); + break; + } + try writer.writeByte('}'); + }, + else => unreachable, } - if (ty.unionHasAllZeroBitFieldTypes(zcu)) return try writer.writeByte('}'); - if (layout.tag_size != 0) try writer.writeByte(','); - try writer.writeAll(" .payload = {"); - } - if (field_ty.hasRuntimeBits(zcu)) { - try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))}); - try dg.renderValue(writer, Value.fromInterned(un.val), initializer_type); - try writer.writeByte(' '); - } else for (0..loaded_union.field_types.len) |this_field_index| { - const this_field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[this_field_index]); - if (!this_field_ty.hasRuntimeBits(zcu)) continue; - try dg.renderUndefValue(writer, this_field_ty, initializer_type); - break; } - if (ty.unionTagTypeSafety(zcu)) |_| try writer.writeByte('}'); - try writer.writeByte('}'); + if (has_tag) try writer.writeByte('}'); } }, } @@ -1430,6 +1498,7 @@ pub const DeclGen = struct { const zcu = dg.zcu; const ip = &zcu.intern_pool; const target = &dg.mod.resolved_target.result; + const ctype_pool = &dg.ctype_pool; const initializer_type: ValueRenderLocation = switch (location) { .StaticInitializer => .StaticInitializer, @@ -1441,6 +1510,7 @@ pub const DeclGen = struct { .ReleaseFast, .ReleaseSmall => false, }; + const ctype = try dg.ctypeFromType(ty, location.toCTypeKind()); switch (ty.toIntern()) { .c_longdouble_type, .f16_type, @@ -1478,48 +1548,64 @@ pub const DeclGen = struct { => return writer.print("{x}", .{ try dg.fmtIntLiteral(try zcu.undefValue(ty), location), }), - .ptr_type => if (ty.isSlice(zcu)) { - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } + .ptr_type => |ptr_type| switch (ptr_type.flags.size) { + .One, .Many, .C => { + try writer.writeAll("(("); + try dg.renderCType(writer, ctype); + return writer.print("){x})", .{ + try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other), + }); + }, + .Slice => { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderCType(writer, ctype); + try writer.writeByte(')'); + } - try writer.writeAll("{("); - const ptr_ty = ty.slicePtrFieldType(zcu); - try dg.renderType(writer, ptr_ty); - return writer.print("){x}, {0x}}}", .{ - try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other), - }); - } else { - try writer.writeAll("(("); - try dg.renderType(writer, ty); - return writer.print("){x})", .{ - try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other), - }); + try writer.writeAll("{("); + const ptr_ty = ty.slicePtrFieldType(zcu); + try dg.renderType(writer, ptr_ty); + return writer.print("){x}, {0x}}}", .{ + try dg.fmtIntLiteral(try zcu.undefValue(Type.usize), .Other), + }); + }, }, - .opt_type => { - const payload_ty = ty.optionalChild(zcu); - - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return dg.renderUndefValue(writer, Type.bool, location); - } - - if (ty.optionalReprIsPayload(zcu)) { - return dg.renderUndefValue(writer, payload_ty, location); - } - - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } - - try writer.writeAll("{ .payload = "); - try dg.renderUndefValue(writer, payload_ty, initializer_type); - try writer.writeAll(", .is_null = "); - try dg.renderUndefValue(writer, Type.bool, initializer_type); - return writer.writeAll(" }"); + .opt_type => |child_type| switch (ctype.info(ctype_pool)) { + .basic, .pointer => try dg.renderUndefValue( + writer, + Type.fromInterned(if (ctype.isBool()) .bool_type else child_type), + location, + ), + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| { + switch (aggregate.fields.at(0, ctype_pool).name.index) { + .is_null, .payload => {}, + .ptr, .len => return dg.renderUndefValue( + writer, + Type.fromInterned(child_type), + location, + ), + else => unreachable, + } + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderCType(writer, ctype); + try writer.writeByte(')'); + } + try writer.writeByte('{'); + for (0..aggregate.fields.len) |field_index| { + if (field_index > 0) try writer.writeByte(','); + try dg.renderUndefValue(writer, Type.fromInterned( + switch (aggregate.fields.at(field_index, ctype_pool).name.index) { + .is_null => .bool_type, + .payload => child_type, + else => unreachable, + }, + ), initializer_type); + } + try writer.writeByte('}'); + }, }, .struct_type => { const loaded_struct = ip.loadStructType(ty.toIntern()); @@ -1527,7 +1613,7 @@ pub const DeclGen = struct { .auto, .@"extern" => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } @@ -1552,7 +1638,7 @@ pub const DeclGen = struct { .anon_struct_type => |anon_struct_info| { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } @@ -1575,54 +1661,80 @@ pub const DeclGen = struct { .auto, .@"extern" => { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } - try writer.writeByte('{'); - if (ty.unionTagTypeSafety(zcu)) |tag_ty| { - const layout = ty.unionGetLayout(zcu); - if (layout.tag_size != 0) { - try writer.writeAll(" .tag = "); - try dg.renderUndefValue(writer, tag_ty, initializer_type); + const has_tag = loaded_union.hasTag(ip); + if (has_tag) try writer.writeByte('{'); + const aggregate = ctype.info(ctype_pool).aggregate; + for (0..if (has_tag) aggregate.fields.len else 1) |outer_field_index| { + if (outer_field_index > 0) try writer.writeByte(','); + switch (if (has_tag) + aggregate.fields.at(outer_field_index, ctype_pool).name.index + else + .payload) { + .tag => try dg.renderUndefValue( + writer, + Type.fromInterned(loaded_union.enum_tag_ty), + initializer_type, + ), + .payload => { + try writer.writeByte('{'); + for (0..loaded_union.field_types.len) |inner_field_index| { + const inner_field_ty = Type.fromInterned( + loaded_union.field_types.get(ip)[inner_field_index], + ); + if (!inner_field_ty.hasRuntimeBits(zcu)) continue; + try dg.renderUndefValue( + writer, + inner_field_ty, + initializer_type, + ); + break; + } + try writer.writeByte('}'); + }, + else => unreachable, } - if (ty.unionHasAllZeroBitFieldTypes(zcu)) return try writer.writeByte('}'); - if (layout.tag_size != 0) try writer.writeByte(','); - try writer.writeAll(" .payload = {"); } - for (0..loaded_union.field_types.len) |field_index| { - const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]); - if (!field_ty.hasRuntimeBits(zcu)) continue; - try dg.renderUndefValue(writer, field_ty, initializer_type); - break; - } - if (ty.unionTagTypeSafety(zcu)) |_| try writer.writeByte('}'); - return writer.writeByte('}'); + if (has_tag) try writer.writeByte('}'); }, .@"packed" => return writer.print("{x}", .{ try dg.fmtIntLiteral(try zcu.undefValue(ty), .Other), }), } }, - .error_union_type => { - const payload_ty = ty.errorUnionPayload(zcu); - const error_ty = ty.errorUnionSet(zcu); - - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return dg.renderUndefValue(writer, error_ty, location); - } - - if (!location.isInitializer()) { - try writer.writeByte('('); - try dg.renderType(writer, ty); - try writer.writeByte(')'); - } - - try writer.writeAll("{ .payload = "); - try dg.renderUndefValue(writer, payload_ty, initializer_type); - try writer.writeAll(", .error = "); - try dg.renderUndefValue(writer, error_ty, initializer_type); - return writer.writeAll(" }"); + .error_union_type => |error_union_type| switch (ctype.info(ctype_pool)) { + .basic => try dg.renderUndefValue( + writer, + Type.fromInterned(error_union_type.error_set_type), + location, + ), + .pointer, .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| { + if (!location.isInitializer()) { + try writer.writeByte('('); + try dg.renderCType(writer, ctype); + try writer.writeByte(')'); + } + try writer.writeByte('{'); + for (0..aggregate.fields.len) |field_index| { + if (field_index > 0) try writer.writeByte(','); + try dg.renderUndefValue( + writer, + Type.fromInterned( + switch (aggregate.fields.at(field_index, ctype_pool).name.index) { + .@"error" => error_union_type.error_set_type, + .payload => error_union_type.payload_type, + else => unreachable, + }, + ), + initializer_type, + ); + } + try writer.writeByte('}'); + }, }, .array_type, .vector_type => { const ai = ty.arrayInfo(zcu); @@ -1637,7 +1749,7 @@ pub const DeclGen = struct { } else { if (!location.isInitializer()) { try writer.writeByte('('); - try dg.renderType(writer, ty); + try dg.renderCType(writer, ctype); try writer.writeByte(')'); } @@ -1687,6 +1799,7 @@ pub const DeclGen = struct { name: union(enum) { export_index: u32, ident: []const u8, + fmt_ctype_pool_string: std.fmt.Formatter(formatCTypePoolString), }, ) !void { const zcu = dg.zcu; @@ -1730,6 +1843,7 @@ pub const DeclGen = struct { try dg.renderDeclName(w, fn_decl_index, export_index); }, .ident => |ident| try w.print("{}{ }", .{ trailing, fmtIdent(ident) }), + .fmt_ctype_pool_string => |fmt| try w.print("{}{ }", .{ trailing, fmt }), } try renderTypeSuffix( @@ -1754,14 +1868,12 @@ pub const DeclGen = struct { switch (name) { .export_index => |export_index| mangled: { const maybe_exports = zcu.decl_exports.get(fn_decl_index); - const external_name = ip.stringToSlice( - if (maybe_exports) |exports| - exports.items[export_index].opts.name - else if (fn_decl.isExtern(zcu)) - fn_decl.name - else - break :mangled, - ); + const external_name = (if (maybe_exports) |exports| + exports.items[export_index].opts.name + else if (fn_decl.isExtern(zcu)) + fn_decl.name + else + break :mangled).toSlice(ip); const is_mangled = isMangledIdent(external_name, true); const is_export = export_index > 0; if (is_mangled and is_export) { @@ -1769,7 +1881,7 @@ pub const DeclGen = struct { fmtIdent(external_name), fmtStringLiteral(external_name, null), fmtStringLiteral( - ip.stringToSlice(maybe_exports.?.items[0].opts.name), + maybe_exports.?.items[0].opts.name.toSlice(ip), null, ), }); @@ -1780,14 +1892,14 @@ pub const DeclGen = struct { } else if (is_export) { try w.print(" zig_export({s}, {s})", .{ fmtStringLiteral( - ip.stringToSlice(maybe_exports.?.items[0].opts.name), + maybe_exports.?.items[0].opts.name.toSlice(ip), null, ), fmtStringLiteral(external_name, null), }); } }, - .ident => {}, + .ident, .fmt_ctype_pool_string => {}, } }, .complete => {}, @@ -1815,11 +1927,11 @@ pub const DeclGen = struct { /// | `renderTypeAndName` | "uint8_t *name" | "uint8_t *name[10]" | /// | `renderType` | "uint8_t *" | "uint8_t *[10]" | /// - fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void { + fn renderType(dg: *DeclGen, w: anytype, t: Type) error{OutOfMemory}!void { try dg.renderCType(w, try dg.ctypeFromType(t, .complete)); } - fn renderCType(dg: *DeclGen, w: anytype, ctype: CType) error{ OutOfMemory, AnalysisFail }!void { + fn renderCType(dg: *DeclGen, w: anytype, ctype: CType) error{OutOfMemory}!void { _ = try renderTypePrefix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, .{}); try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, .{}); } @@ -1844,7 +1956,26 @@ pub const DeclGen = struct { } } }; + fn intCastIsNoop(dg: *DeclGen, dest_ty: Type, src_ty: Type) bool { + const zcu = dg.zcu; + const dest_bits = dest_ty.bitSize(zcu); + const dest_int_info = dest_ty.intInfo(zcu); + + const src_is_ptr = src_ty.isPtrAtRuntime(zcu); + const src_eff_ty: Type = if (src_is_ptr) switch (dest_int_info.signedness) { + .unsigned => Type.usize, + .signed => Type.isize, + } else src_ty; + const src_bits = src_eff_ty.bitSize(zcu); + const src_int_info = if (src_eff_ty.isAbiInt(zcu)) src_eff_ty.intInfo(zcu) else null; + if (dest_bits <= 64 and src_bits <= 64) { + const needs_cast = src_int_info == null or + (toCIntBits(dest_int_info.bits) != toCIntBits(src_int_info.?.bits) or + dest_int_info.signedness != src_int_info.?.signedness); + return !needs_cast and !src_is_ptr; + } else return false; + } /// Renders a cast to an int type, from either an int or a pointer. /// /// Some platforms don't have 128 bit integers, so we need to use @@ -1858,7 +1989,14 @@ pub const DeclGen = struct { /// | > 64 bit integer | pointer | zig_make_<dest_ty>(0, (zig_<u|i>size)src) /// | > 64 bit integer | < 64 bit integer | zig_make_<dest_ty>(0, src) /// | > 64 bit integer | > 64 bit integer | zig_make_<dest_ty>(zig_hi_<src_ty>(src), zig_lo_<src_ty>(src)) - fn renderIntCast(dg: *DeclGen, w: anytype, dest_ty: Type, context: IntCastContext, src_ty: Type, location: ValueRenderLocation) !void { + fn renderIntCast( + dg: *DeclGen, + w: anytype, + dest_ty: Type, + context: IntCastContext, + src_ty: Type, + location: ValueRenderLocation, + ) !void { const zcu = dg.zcu; const dest_bits = dest_ty.bitSize(zcu); const dest_int_info = dest_ty.intInfo(zcu); @@ -2013,12 +2151,23 @@ pub const DeclGen = struct { fmtIdent("payload"), fmtIdent(ident), }), + .ctype_pool_string => |string| try w.print("{ }", .{ + fmtCTypePoolString(string, &dg.ctype_pool), + }), } } fn writeCValueDeref(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { - .none, .new_local, .local, .local_ref, .constant, .arg, .arg_array => unreachable, + .none, + .new_local, + .local, + .local_ref, + .constant, + .arg, + .arg_array, + .ctype_pool_string, + => unreachable, .field => |i| try w.print("f{d}", .{i}), .decl => |decl| { try w.writeAll("(*"); @@ -2048,7 +2197,17 @@ pub const DeclGen = struct { fn writeCValueDerefMember(dg: *DeclGen, writer: anytype, c_value: CValue, member: CValue) !void { switch (c_value) { - .none, .new_local, .local, .local_ref, .constant, .field, .undef, .arg, .arg_array => unreachable, + .none, + .new_local, + .local, + .local_ref, + .constant, + .field, + .undef, + .arg, + .arg_array, + .ctype_pool_string, + => unreachable, .decl, .identifier, .payload_identifier => { try dg.writeCValue(writer, c_value); try writer.writeAll("->"); @@ -2088,12 +2247,12 @@ pub const DeclGen = struct { .complete, ); mangled: { - const external_name = zcu.intern_pool.stringToSlice(if (maybe_exports) |exports| + const external_name = (if (maybe_exports) |exports| exports.items[0].opts.name else if (variable.is_extern) decl.name else - break :mangled); + break :mangled).toSlice(&zcu.intern_pool); if (isMangledIdent(external_name, true)) { try fwd.print(" zig_mangled_{s}({ }, {s})", .{ @tagName(fwd_kind), @@ -2107,15 +2266,16 @@ pub const DeclGen = struct { fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: InternPool.DeclIndex, export_index: u32) !void { const zcu = dg.zcu; + const ip = &zcu.intern_pool; const decl = zcu.declPtr(decl_index); if (zcu.decl_exports.get(decl_index)) |exports| { try writer.print("{ }", .{ - fmtIdent(zcu.intern_pool.stringToSlice(exports.items[export_index].opts.name)), + fmtIdent(exports.items[export_index].opts.name.toSlice(ip)), }); } else if (decl.getExternDecl(zcu).unwrap()) |extern_decl_index| { try writer.print("{ }", .{ - fmtIdent(zcu.intern_pool.stringToSlice(zcu.declPtr(extern_decl_index).name)), + fmtIdent(zcu.declPtr(extern_decl_index).name.toSlice(ip)), }); } else { // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), @@ -2186,11 +2346,7 @@ pub const DeclGen = struct { loc: ValueRenderLocation, ) !std.fmt.Formatter(formatIntLiteral) { const zcu = dg.zcu; - const kind: CType.Kind = switch (loc) { - .FunctionArgument => .parameter, - .Initializer, .Other => .complete, - .StaticInitializer => .global, - }; + const kind = loc.toCTypeKind(); const ty = val.typeOf(zcu); return std.fmt.Formatter(formatIntLiteral){ .data = .{ .dg = dg, @@ -2239,7 +2395,7 @@ fn renderFwdDeclTypeName( switch (fwd_decl.name) { .anon => try w.print("anon__lazy_{d}", .{@intFromEnum(ctype.index)}), .owner_decl => |owner_decl| try w.print("{}__{d}", .{ - fmtIdent(zcu.intern_pool.stringToSlice(zcu.declPtr(owner_decl).name)), + fmtIdent(zcu.declPtr(owner_decl).name.toSlice(&zcu.intern_pool)), @intFromEnum(owner_decl), }), } @@ -2453,7 +2609,7 @@ fn renderFields( .suffix, .{}, ); - try writer.print("{}{ }", .{ trailing, fmtIdent(field_info.name.slice(ctype_pool)) }); + try writer.print("{}{ }", .{ trailing, fmtCTypePoolString(field_info.name, ctype_pool) }); try renderTypeSuffix(.flush, ctype_pool, zcu, writer, field_info.ctype, .suffix, .{}); try writer.writeAll(";\n"); } @@ -2561,7 +2717,7 @@ pub fn genErrDecls(o: *Object) !void { try writer.writeAll("enum {\n"); o.indent_writer.pushIndent(); for (zcu.global_error_set.keys()[1..], 1..) |name_nts, value| { - const name = ip.stringToSlice(name_nts); + const name = name_nts.toSlice(ip); max_name_len = @max(name.len, max_name_len); const err_val = try zcu.intern(.{ .err = .{ .ty = .anyerror_type, @@ -2579,19 +2735,19 @@ pub fn genErrDecls(o: *Object) !void { defer o.dg.gpa.free(name_buf); @memcpy(name_buf[0..name_prefix.len], name_prefix); - for (zcu.global_error_set.keys()) |name_ip| { - const name = ip.stringToSlice(name_ip); - @memcpy(name_buf[name_prefix.len..][0..name.len], name); - const identifier = name_buf[0 .. name_prefix.len + name.len]; + for (zcu.global_error_set.keys()) |name| { + const name_slice = name.toSlice(ip); + @memcpy(name_buf[name_prefix.len..][0..name_slice.len], name_slice); + const identifier = name_buf[0 .. name_prefix.len + name_slice.len]; const name_ty = try zcu.arrayType(.{ - .len = name.len, + .len = name_slice.len, .child = .u8_type, .sentinel = .zero_u8, }); const name_val = try zcu.intern(.{ .aggregate = .{ .ty = name_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = name.toString() }, } }); try writer.writeAll("static "); @@ -2624,7 +2780,7 @@ pub fn genErrDecls(o: *Object) !void { ); try writer.writeAll(" = {"); for (zcu.global_error_set.keys(), 0..) |name_nts, value| { - const name = ip.stringToSlice(name_nts); + const name = name_nts.toSlice(ip); if (value != 0) try writer.writeByte(','); try writer.print("{{" ++ name_prefix ++ "{}, {}}}", .{ fmtIdent(name), @@ -2672,7 +2828,7 @@ fn genExports(o: *Object) !void { for (exports.items[1..]) |@"export"| { try fwd.writeAll("zig_extern "); if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); - const export_name = ip.stringToSlice(@"export".opts.name); + const export_name = @"export".opts.name.toSlice(ip); try o.dg.renderTypeAndName( fwd, decl.typeOf(zcu), @@ -2685,11 +2841,11 @@ fn genExports(o: *Object) !void { try fwd.print(" zig_mangled_export({ }, {s}, {s})", .{ fmtIdent(export_name), fmtStringLiteral(export_name, null), - fmtStringLiteral(ip.stringToSlice(exports.items[0].opts.name), null), + fmtStringLiteral(exports.items[0].opts.name.toSlice(ip), null), }); } else { try fwd.print(" zig_export({s}, {s})", .{ - fmtStringLiteral(ip.stringToSlice(exports.items[0].opts.name), null), + fmtStringLiteral(exports.items[0].opts.name.toSlice(ip), null), fmtStringLiteral(export_name, null), }); } @@ -2712,24 +2868,23 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn try w.writeAll("static "); try o.dg.renderType(w, name_slice_ty); - try w.writeByte(' '); - try w.writeAll(val.fn_name.slice(lazy_ctype_pool)); - try w.writeByte('('); + try w.print(" {}(", .{val.fn_name.fmt(lazy_ctype_pool)}); try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete); try w.writeAll(") {\n switch (tag) {\n"); const tag_names = enum_ty.enumFields(zcu); for (0..tag_names.len) |tag_index| { - const tag_name = ip.stringToSlice(tag_names.get(ip)[tag_index]); + const tag_name = tag_names.get(ip)[tag_index]; + const tag_name_len = tag_name.length(ip); const tag_val = try zcu.enumValueFieldIndex(enum_ty, @intCast(tag_index)); const name_ty = try zcu.arrayType(.{ - .len = tag_name.len, + .len = tag_name_len, .child = .u8_type, .sentinel = .zero_u8, }); const name_val = try zcu.intern(.{ .aggregate = .{ .ty = name_ty.toIntern(), - .storage = .{ .bytes = tag_name }, + .storage = .{ .bytes = tag_name.toString() }, } }); try w.print(" case {}: {{\n static ", .{ @@ -2742,7 +2897,7 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn try o.dg.renderType(w, name_slice_ty); try w.print("){{{}, {}}};\n", .{ fmtIdent("name"), - try o.dg.fmtIntLiteral(try zcu.intValue(Type.usize, tag_name.len), .Other), + try o.dg.fmtIntLiteral(try zcu.intValue(Type.usize, tag_name_len), .Other), }); try w.writeAll(" }\n"); @@ -2757,21 +2912,18 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn const fn_decl = zcu.declPtr(fn_decl_index); const fn_ctype = try o.dg.ctypeFromType(fn_decl.typeOf(zcu), .complete); const fn_info = fn_ctype.info(ctype_pool).function; - const fn_name = val.fn_name.slice(lazy_ctype_pool); + const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool); const fwd_decl_writer = o.dg.fwdDeclWriter(); try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature( - fwd_decl_writer, - fn_decl_index, - .forward, - .{ .ident = fn_name }, - ); + try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ + .fmt_ctype_pool_string = fn_name, + }); try fwd_decl_writer.writeAll(";\n"); try w.print("static zig_{s} ", .{@tagName(key)}); try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ - .ident = fn_name, + .fmt_ctype_pool_string = fn_name, }); try w.writeAll(" {\n return "); try o.dg.renderDeclName(w, fn_decl_index, 0); @@ -2810,7 +2962,7 @@ pub fn genFunc(f: *Function) !void { try o.indent_writer.insertNewline(); if (!is_global) try o.writer().writeAll("static "); - if (zcu.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |s| + if (decl.@"linksection".toSlice(&zcu.intern_pool)) |s| try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderFunctionSignature(o.writer(), decl_index, .complete, .{ .export_index = 0 }); try o.writer().writeByte(' '); @@ -2900,7 +3052,7 @@ pub fn genDecl(o: *Object) !void { if (!is_global) try w.writeAll("static "); if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage "); if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); - if (zcu.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |s| + if (decl.@"linksection".toSlice(&zcu.intern_pool)) |s| try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); const decl_c_value = .{ .decl = decl_index }; try o.dg.renderTypeAndName(w, decl_ty, decl_c_value, .{}, decl.alignment, .complete); @@ -2933,7 +3085,7 @@ pub fn genDeclValue( switch (o.dg.pass) { .decl => |decl_index| { if (zcu.decl_exports.get(decl_index)) |exports| { - const export_name = zcu.intern_pool.stringToSlice(exports.items[0].opts.name); + const export_name = exports.items[0].opts.name.toSlice(&zcu.intern_pool); if (isMangledIdent(export_name, true)) { try fwd_decl_writer.print(" zig_mangled_final({ }, {s})", .{ fmtIdent(export_name), fmtStringLiteral(export_name, null), @@ -2949,7 +3101,7 @@ pub fn genDeclValue( const w = o.writer(); if (!is_global) try w.writeAll("static "); - if (zcu.intern_pool.stringToSliceUnwrap(@"linksection")) |s| + if (@"linksection".toSlice(&zcu.intern_pool)) |s| try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete); try w.writeAll(" = "); @@ -3156,8 +3308,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .shl_exact => try airBinOp(f, inst, "<<", "shl", .none), .not => try airNot (f, inst), - .optional_payload => try airOptionalPayload(f, inst), - .optional_payload_ptr => try airOptionalPayloadPtr(f, inst), + .optional_payload => try airOptionalPayload(f, inst, false), + .optional_payload_ptr => try airOptionalPayload(f, inst, true), .optional_payload_ptr_set => try airOptionalPayloadPtrSet(f, inst), .wrap_optional => try airWrapOptional(f, inst), @@ -3166,10 +3318,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .is_err_ptr => try airIsErr(f, inst, true, "!="), .is_non_err_ptr => try airIsErr(f, inst, true, "=="), - .is_null => try airIsNull(f, inst, "==", false), - .is_non_null => try airIsNull(f, inst, "!=", false), - .is_null_ptr => try airIsNull(f, inst, "==", true), - .is_non_null_ptr => try airIsNull(f, inst, "!=", true), + .is_null => try airIsNull(f, inst, .eq, false), + .is_non_null => try airIsNull(f, inst, .neq, false), + .is_null_ptr => try airIsNull(f, inst, .eq, true), + .is_non_null_ptr => try airIsNull(f, inst, .neq, true), .alloc => try airAlloc(f, inst), .ret_ptr => try airRetPtr(f, inst), @@ -3252,8 +3404,8 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .slice_ptr => try airSliceField(f, inst, false, "ptr"), .slice_len => try airSliceField(f, inst, false, "len"), - .ptr_slice_len_ptr => try airSliceField(f, inst, true, "len"), .ptr_slice_ptr_ptr => try airSliceField(f, inst, true, "ptr"), + .ptr_slice_len_ptr => try airSliceField(f, inst, true, "len"), .ptr_elem_val => try airPtrElemVal(f, inst), .ptr_elem_ptr => try airPtrElemPtr(f, inst), @@ -3321,7 +3473,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, } try f.value_map.putNoClobber(inst.toRef(), switch (result_value) { .none => continue, - .new_local => |i| .{ .local = i }, + .new_local => |local_index| .{ .local = local_index }, else => result_value, }); } @@ -3336,7 +3488,7 @@ fn airSliceField(f: *Function, inst: Air.Inst.Index, is_ptr: bool, field_name: [ const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); if (is_ptr) { @@ -3362,7 +3514,7 @@ fn airPtrElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValue(writer, ptr, .Other); @@ -3388,7 +3540,7 @@ fn airPtrElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try writer.writeByte('('); @@ -3423,7 +3575,7 @@ fn airSliceElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValueMember(writer, slice, .{ .identifier = "ptr" }); @@ -3450,7 +3602,7 @@ fn airSliceElemPtr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); if (elem_has_bits) try writer.writeByte('&'); @@ -3479,7 +3631,7 @@ fn airArrayElemVal(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValue(writer, array, .Other); @@ -3704,17 +3856,18 @@ fn airIntCast(f: *Function, inst: Air.Inst.Index) !CValue { const operand_ty = f.typeOf(ty_op.operand); const scalar_ty = operand_ty.scalarType(zcu); + if (f.object.dg.intCastIsNoop(inst_scalar_ty, scalar_ty)) return f.moveCValue(inst, inst_ty, operand); + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, writer, operand_ty); - const a = try Assignment.start(f, writer, scalar_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(scalar_ty, .complete)); try f.writeCValue(writer, local, .Other); try v.elem(f, writer); try a.assign(f, writer); try f.renderIntCast(writer, inst_scalar_ty, operand, v, scalar_ty, .Other); try a.end(f, writer); try v.end(f, inst, writer); - return local; } @@ -3724,38 +3877,40 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); + const inst_ty = f.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(zcu); const dest_int_info = inst_scalar_ty.intInfo(zcu); const dest_bits = dest_int_info.bits; - const dest_c_bits = toCIntBits(dest_int_info.bits) orelse + const dest_c_bits = toCIntBits(dest_bits) orelse return f.fail("TODO: C backend: implement integer types larger than 128 bits", .{}); const operand_ty = f.typeOf(ty_op.operand); const scalar_ty = operand_ty.scalarType(zcu); const scalar_int_info = scalar_ty.intInfo(zcu); + const need_cast = dest_c_bits < 64; + const need_lo = scalar_int_info.bits > 64 and dest_bits <= 64; + const need_mask = dest_bits < 8 or !std.math.isPowerOfTwo(dest_bits); + if (!need_cast and !need_lo and !need_mask) return f.moveCValue(inst, inst_ty, operand); + const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, writer, operand_ty); - + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_scalar_ty, .complete)); try f.writeCValue(writer, local, .Other); try v.elem(f, writer); - try writer.writeAll(" = "); - - if (dest_c_bits < 64) { + try a.assign(f, writer); + if (need_cast) { try writer.writeByte('('); try f.renderType(writer, inst_scalar_ty); try writer.writeByte(')'); } - - const needs_lo = scalar_int_info.bits > 64 and dest_bits <= 64; - if (needs_lo) { + if (need_lo) { try writer.writeAll("zig_lo_"); try f.object.dg.renderTypeForBuiltinFnName(writer, scalar_ty); try writer.writeByte('('); } - - if (dest_bits >= 8 and std.math.isPowerOfTwo(dest_bits)) { + if (!need_mask) { try f.writeCValue(writer, operand, .Other); try v.elem(f, writer); } else switch (dest_int_info.signedness) { @@ -3795,11 +3950,9 @@ fn airTrunc(f: *Function, inst: Air.Inst.Index) !CValue { try writer.print(", {})", .{try f.fmtIntLiteral(shift_val)}); }, } - - if (needs_lo) try writer.writeByte(')'); - try writer.writeAll(";\n"); + if (need_lo) try writer.writeByte(')'); + try a.end(f, writer); try v.end(f, inst, writer); - return local; } @@ -3810,7 +3963,7 @@ fn airIntFromBool(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const inst_ty = f.typeOfIndex(inst); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValue(writer, operand, .Other); @@ -3855,9 +4008,8 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const src_val = try f.resolveInst(bin_op.rhs); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + const src_scalar_ctype = try f.ctypeFromType(src_ty.scalarType(zcu), .complete); const writer = f.object.writer(); - const v = try Vectorize.start(f, inst, writer, ptr_ty); - if (need_memcpy) { // For this memcpy to safely work we need the rhs to have the same // underlying type as the lhs (i.e. they must both be arrays of the same underlying type). @@ -3876,6 +4028,7 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { break :blk new_local; } else src_val; + const v = try Vectorize.start(f, inst, writer, ptr_ty); try writer.writeAll("memcpy((char *)"); try f.writeCValue(writer, ptr_val, .FunctionArgument); try v.elem(f, writer); @@ -3886,9 +4039,9 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { try writer.writeAll(", sizeof("); try f.renderType(writer, src_ty); try writer.writeAll("))"); - if (src_val == .constant) { - try freeLocal(f, inst, array_src.new_local, null); - } + try f.freeCValue(inst, array_src); + try writer.writeAll(";\n"); + try v.end(f, inst, writer); } else if (ptr_info.packed_offset.host_size > 0 and ptr_info.flags.vector_index == .none) { const host_bits = ptr_info.packed_offset.host_size * 8; const host_ty = try zcu.intType(.unsigned, host_bits); @@ -3911,9 +4064,12 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { const mask_val = try zcu.intValue_big(host_ty, mask.toConst()); + const v = try Vectorize.start(f, inst, writer, ptr_ty); + const a = try Assignment.start(f, writer, src_scalar_ctype); try f.writeCValueDeref(writer, ptr_val); try v.elem(f, writer); - try writer.writeAll(" = zig_or_"); + try a.assign(f, writer); + try writer.writeAll("zig_or_"); try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); try writer.writeAll("(zig_and_"); try f.object.dg.renderTypeForBuiltinFnName(writer, host_ty); @@ -3944,16 +4100,27 @@ fn airStore(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { try v.elem(f, writer); if (cant_cast) try writer.writeByte(')'); try writer.print(", {}))", .{try f.fmtIntLiteral(bit_offset_val)}); + try a.end(f, writer); + try v.end(f, inst, writer); } else { + switch (ptr_val) { + .local_ref => |ptr_local_index| switch (src_val) { + .new_local, .local => |src_local_index| if (ptr_local_index == src_local_index) + return .none, + else => {}, + }, + else => {}, + } + const v = try Vectorize.start(f, inst, writer, ptr_ty); + const a = try Assignment.start(f, writer, src_scalar_ctype); try f.writeCValueDeref(writer, ptr_val); try v.elem(f, writer); - try writer.writeAll(" = "); + try a.assign(f, writer); try f.writeCValue(writer, src_val, .Other); try v.elem(f, writer); + try a.end(f, writer); + try v.end(f, inst, writer); } - try writer.writeAll(";\n"); - try v.end(f, inst, writer); - return .none; } @@ -4116,6 +4283,7 @@ fn airEquality( operator: std.math.CompareOperator, ) !CValue { const zcu = f.object.dg.zcu; + const ctype_pool = &f.object.dg.ctype_pool; const bin_op = f.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const operand_ty = f.typeOf(bin_op.lhs); @@ -4137,28 +4305,47 @@ fn airEquality( try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const writer = f.object.writer(); - const inst_ty = f.typeOfIndex(inst); - const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const local = try f.allocLocal(inst, Type.bool); + const a = try Assignment.start(f, writer, CType.bool); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); - if (operand_ty.zigTypeTag(zcu) == .Optional and !operand_ty.optionalReprIsPayload(zcu)) { - try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" }); - try writer.writeAll(" || "); - try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" }); - try writer.writeAll(" ? "); - try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" }); - try writer.writeAll(compareOperatorC(operator)); - try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" }); - try writer.writeAll(" : "); - try f.writeCValueMember(writer, lhs, .{ .identifier = "payload" }); - try writer.writeAll(compareOperatorC(operator)); - try f.writeCValueMember(writer, rhs, .{ .identifier = "payload" }); - } else { - try f.writeCValue(writer, lhs, .Other); - try writer.writeAll(compareOperatorC(operator)); - try f.writeCValue(writer, rhs, .Other); + const operand_ctype = try f.ctypeFromType(operand_ty, .complete); + switch (operand_ctype.info(ctype_pool)) { + .basic, .pointer => { + try f.writeCValue(writer, lhs, .Other); + try writer.writeAll(compareOperatorC(operator)); + try f.writeCValue(writer, rhs, .Other); + }, + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| if (aggregate.fields.len == 2 and + (aggregate.fields.at(0, ctype_pool).name.index == .is_null or + aggregate.fields.at(1, ctype_pool).name.index == .is_null)) + { + try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" }); + try writer.writeAll(" || "); + try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" }); + try writer.writeAll(" ? "); + try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" }); + try writer.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" }); + try writer.writeAll(" : "); + try f.writeCValueMember(writer, lhs, .{ .identifier = "payload" }); + try writer.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(writer, rhs, .{ .identifier = "payload" }); + } else for (0..aggregate.fields.len) |field_index| { + if (field_index > 0) try writer.writeAll(switch (operator) { + .lt, .lte, .gte, .gt => unreachable, + .eq => " && ", + .neq => " || ", + }); + const field_name: CValue = .{ + .ctype_pool_string = aggregate.fields.at(field_index, ctype_pool).name, + }; + try f.writeCValueMember(writer, lhs, field_name); + try writer.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(writer, rhs, field_name); + }, } try a.end(f, writer); @@ -4168,12 +4355,11 @@ fn airEquality( fn airCmpLtErrorsLen(f: *Function, inst: Air.Inst.Index) !CValue { const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; - const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(un_op); try reap(f, inst, &.{un_op}); const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); + const local = try f.allocLocal(inst, Type.bool); try f.writeCValue(writer, local, .Other); try writer.writeAll(" = "); try f.writeCValue(writer, operand, .Other); @@ -4193,39 +4379,34 @@ fn airPtrAddSub(f: *Function, inst: Air.Inst.Index, operator: u8) !CValue { const inst_ty = f.typeOfIndex(inst); const inst_scalar_ty = inst_ty.scalarType(zcu); const elem_ty = inst_scalar_ty.elemType2(zcu); + if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) return f.moveCValue(inst, inst_ty, lhs); + const inst_scalar_ctype = try f.ctypeFromType(inst_scalar_ty, .complete); const local = try f.allocLocal(inst, inst_ty); const writer = f.object.writer(); const v = try Vectorize.start(f, inst, writer, inst_ty); + const a = try Assignment.start(f, writer, inst_scalar_ctype); try f.writeCValue(writer, local, .Other); try v.elem(f, writer); - try writer.writeAll(" = "); - - if (elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - // We must convert to and from integer types to prevent UB if the operation - // results in a NULL pointer, or if LHS is NULL. The operation is only UB - // if the result is NULL and then dereferenced. - try writer.writeByte('('); - try f.renderType(writer, inst_scalar_ty); - try writer.writeAll(")(((uintptr_t)"); - try f.writeCValue(writer, lhs, .Other); - try v.elem(f, writer); - try writer.writeAll(") "); - try writer.writeByte(operator); - try writer.writeAll(" ("); - try f.writeCValue(writer, rhs, .Other); - try v.elem(f, writer); - try writer.writeAll("*sizeof("); - try f.renderType(writer, elem_ty); - try writer.writeAll(")))"); - } else { - try f.writeCValue(writer, lhs, .Other); - try v.elem(f, writer); - } - - try writer.writeAll(";\n"); + try a.assign(f, writer); + // We must convert to and from integer types to prevent UB if the operation + // results in a NULL pointer, or if LHS is NULL. The operation is only UB + // if the result is NULL and then dereferenced. + try writer.writeByte('('); + try f.renderCType(writer, inst_scalar_ctype); + try writer.writeAll(")(((uintptr_t)"); + try f.writeCValue(writer, lhs, .Other); + try v.elem(f, writer); + try writer.writeAll(") "); + try writer.writeByte(operator); + try writer.writeAll(" ("); + try f.writeCValue(writer, rhs, .Other); + try v.elem(f, writer); + try writer.writeAll("*sizeof("); + try f.renderType(writer, elem_ty); + try writer.writeAll(")))"); + try a.end(f, writer); try v.end(f, inst, writer); - return local; } @@ -4286,14 +4467,14 @@ fn airSlice(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); { - const a = try Assignment.start(f, writer, ptr_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(ptr_ty, .complete)); try f.writeCValueMember(writer, local, .{ .identifier = "ptr" }); try a.assign(f, writer); try f.writeCValue(writer, ptr, .Other); try a.end(f, writer); } { - const a = try Assignment.start(f, writer, Type.usize); + const a = try Assignment.start(f, writer, CType.usize); try f.writeCValueMember(writer, local, .{ .identifier = "len" }); try a.assign(f, writer); try f.writeCValue(writer, len, .Initializer); @@ -4360,7 +4541,7 @@ fn airCall( }).?; const ret_ty = Type.fromInterned(fn_info.return_type); const ret_ctype: CType = if (ret_ty.isNoReturn(zcu)) - .{ .index = .void } + CType.void else try f.ctypeFromType(ret_ty, .parameter); @@ -4372,7 +4553,7 @@ fn airCall( break :result .none; } else if (f.liveness.isUnused(inst)) { try writer.writeByte('('); - try f.renderCType(writer, .{ .index = .void }); + try f.renderCType(writer, CType.void); try writer.writeByte(')'); break :result .none; } else { @@ -4427,10 +4608,7 @@ fn airCall( if (need_comma) try writer.writeAll(", "); need_comma = true; try f.writeCValue(writer, resolved_arg, .FunctionArgument); - switch (resolved_arg) { - .new_local => |local| try freeLocal(f, inst, local, null), - else => {}, - } + try f.freeCValue(inst, resolved_arg); } try writer.writeAll(");\n"); @@ -4614,7 +4792,7 @@ fn lowerTry( } const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); if (is_ptr) { @@ -4637,7 +4815,7 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(branch.operand); try reap(f, inst, &.{branch.operand}); - const a = try Assignment.start(f, writer, operand_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(operand_ty, .complete)); try f.writeCValue(writer, result, .Other); try a.assign(f, writer); try f.writeCValue(writer, operand, .Other); @@ -4650,53 +4828,17 @@ fn airBr(f: *Function, inst: Air.Inst.Index) !CValue { fn airBitcast(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const dest_ty = f.typeOfIndex(inst); + const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); const operand_ty = f.typeOf(ty_op.operand); - const bitcasted = try bitcast(f, dest_ty, operand, operand_ty); + const bitcasted = try bitcast(f, inst_ty, operand, operand_ty); try reap(f, inst, &.{ty_op.operand}); - return bitcasted.move(f, inst, dest_ty); + return f.moveCValue(inst, inst_ty, bitcasted); } -const LocalResult = struct { - c_value: CValue, - need_free: bool, - - fn move(lr: LocalResult, f: *Function, inst: Air.Inst.Index, dest_ty: Type) !CValue { - const zcu = f.object.dg.zcu; - - if (lr.need_free) { - // Move the freshly allocated local to be owned by this instruction, - // by returning it here instead of freeing it. - return lr.c_value; - } - - const local = try f.allocLocal(inst, dest_ty); - try lr.free(f); - const writer = f.object.writer(); - try f.writeCValue(writer, local, .Other); - if (dest_ty.isAbiInt(zcu)) { - try writer.writeAll(" = "); - } else { - try writer.writeAll(" = ("); - try f.renderType(writer, dest_ty); - try writer.writeByte(')'); - } - try f.writeCValue(writer, lr.c_value, .Initializer); - try writer.writeAll(";\n"); - return local; - } - - fn free(lr: LocalResult, f: *Function) !void { - if (lr.need_free) { - try freeLocal(f, null, lr.c_value.new_local, null); - } - } -}; - -fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !LocalResult { +fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !CValue { const zcu = f.object.dg.zcu; const target = &f.object.dg.mod.resolved_target.result; const ctype_pool = &f.object.dg.ctype_pool; @@ -4706,13 +4848,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !Loca const src_info = dest_ty.intInfo(zcu); const dest_info = operand_ty.intInfo(zcu); if (src_info.signedness == dest_info.signedness and - src_info.bits == dest_info.bits) - { - return .{ - .c_value = operand, - .need_free = false, - }; - } + src_info.bits == dest_info.bits) return operand; } if (dest_ty.isPtrAtRuntime(zcu) and operand_ty.isPtrAtRuntime(zcu)) { @@ -4723,10 +4859,7 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !Loca try writer.writeByte(')'); try f.writeCValue(writer, operand, .Other); try writer.writeAll(";\n"); - return .{ - .c_value = local, - .need_free = true, - }; + return local; } const operand_lval = if (operand == .constant) blk: { @@ -4813,14 +4946,8 @@ fn bitcast(f: *Function, dest_ty: Type, operand: CValue, operand_ty: Type) !Loca try writer.writeAll(");\n"); } - if (operand == .constant) { - try freeLocal(f, null, operand_lval.new_local, null); - } - - return .{ - .c_value = local, - .need_free = true, - }; + try f.freeCValue(null, operand_lval); + return local; } fn airTrap(f: *Function, writer: anytype) !CValue { @@ -4931,13 +5058,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); try writer.writeAll("switch ("); - if (condition_ty.zigTypeTag(zcu) == .Bool) { - try writer.writeByte('('); - try f.renderType(writer, Type.u1); - try writer.writeByte(')'); - } else if (condition_ty.isPtrAtRuntime(zcu)) { + + const lowered_condition_ty = if (condition_ty.toIntern() == .bool_type) + Type.u1 + else if (condition_ty.isPtrAtRuntime(zcu)) + Type.usize + else + condition_ty; + if (condition_ty.toIntern() != lowered_condition_ty.toIntern()) { try writer.writeByte('('); - try f.renderType(writer, Type.usize); + try f.renderType(writer, lowered_condition_ty); try writer.writeByte(')'); } try f.writeCValue(writer, condition, .Other); @@ -4956,18 +5086,24 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { for (0..switch_br.data.cases_len) |case_i| { const case = f.air.extraData(Air.SwitchBr.Case, extra_index); const items = @as([]const Air.Inst.Ref, @ptrCast(f.air.extra[case.end..][0..case.data.items_len])); - const case_body: []const Air.Inst.Index = @ptrCast(f.air.extra[case.end + items.len ..][0..case.data.body_len]); + const case_body: []const Air.Inst.Index = + @ptrCast(f.air.extra[case.end + items.len ..][0..case.data.body_len]); extra_index = case.end + case.data.items_len + case_body.len; for (items) |item| { try f.object.indent_writer.insertNewline(); try writer.writeAll("case "); - if (condition_ty.isPtrAtRuntime(zcu)) { - try writer.writeByte('('); - try f.renderType(writer, Type.usize); - try writer.writeByte(')'); + const item_value = try f.air.value(item, zcu); + if (item_value.?.getUnsignedInt(zcu)) |item_int| try writer.print("{}\n", .{ + try f.fmtIntLiteral(try zcu.intValue(lowered_condition_ty, item_int)), + }) else { + if (condition_ty.isPtrAtRuntime(zcu)) { + try writer.writeByte('('); + try f.renderType(writer, Type.usize); + try writer.writeByte(')'); + } + try f.object.dg.renderValue(writer, (try f.air.value(item, zcu)).?, .Other); } - try f.object.dg.renderValue(writer, (try f.air.value(item, zcu)).?, .Other); try writer.writeByte(':'); } try writer.writeByte(' '); @@ -5289,10 +5425,11 @@ fn airAsm(f: *Function, inst: Air.Inst.Index) !CValue { fn airIsNull( f: *Function, inst: Air.Inst.Index, - operator: []const u8, + operator: std.math.CompareOperator, is_ptr: bool, ) !CValue { const zcu = f.object.dg.zcu; + const ctype_pool = &f.object.dg.ctype_pool; const un_op = f.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const writer = f.object.writer(); @@ -5300,106 +5437,84 @@ fn airIsNull( try reap(f, inst, &.{un_op}); const local = try f.allocLocal(inst, Type.bool); + const a = try Assignment.start(f, writer, CType.bool); try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - if (is_ptr) { - try f.writeCValueDeref(writer, operand); - } else { - try f.writeCValue(writer, operand, .Other); - } + try a.assign(f, writer); const operand_ty = f.typeOf(un_op); const optional_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; - const payload_ty = optional_ty.optionalChild(zcu); - const err_int_ty = try zcu.errorIntType(); - - const rhs = if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) - Value.true - else if (optional_ty.isPtrLikeOptional(zcu)) - // operand is a regular pointer, test `operand !=/== NULL` - try zcu.getCoerced(Value.null, optional_ty) - else if (payload_ty.zigTypeTag(zcu) == .ErrorSet) - try zcu.intValue(err_int_ty, 0) - else if (payload_ty.isSlice(zcu) and optional_ty.optionalReprIsPayload(zcu)) rhs: { - try writer.writeAll(".ptr"); - const slice_ptr_ty = payload_ty.slicePtrFieldType(zcu); - const opt_slice_ptr_ty = try zcu.optionalType(slice_ptr_ty.toIntern()); - break :rhs try zcu.nullValue(opt_slice_ptr_ty); - } else rhs: { - try writer.writeAll(".is_null"); - break :rhs Value.true; + const opt_ctype = try f.ctypeFromType(optional_ty, .complete); + const rhs = switch (opt_ctype.info(ctype_pool)) { + .basic, .pointer => rhs: { + if (is_ptr) + try f.writeCValueDeref(writer, operand) + else + try f.writeCValue(writer, operand, .Other); + break :rhs if (opt_ctype.isBool()) + "true" + else if (opt_ctype.isInteger()) + "0" + else + "NULL"; + }, + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { + .is_null, .payload => rhs: { + if (is_ptr) + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" }) + else + try f.writeCValueMember(writer, operand, .{ .identifier = "is_null" }); + break :rhs "true"; + }, + .ptr, .len => rhs: { + if (is_ptr) + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "ptr" }) + else + try f.writeCValueMember(writer, operand, .{ .identifier = "ptr" }); + break :rhs "NULL"; + }, + else => unreachable, + }, }; - try writer.writeByte(' '); - try writer.writeAll(operator); - try writer.writeByte(' '); - try f.object.dg.renderValue(writer, rhs, .Other); - try writer.writeAll(";\n"); - return local; -} - -fn airOptionalPayload(f: *Function, inst: Air.Inst.Index) !CValue { - const zcu = f.object.dg.zcu; - const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - - const operand = try f.resolveInst(ty_op.operand); - try reap(f, inst, &.{ty_op.operand}); - const opt_ty = f.typeOf(ty_op.operand); - - const payload_ty = opt_ty.optionalChild(zcu); - - if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { - return .none; - } - - const inst_ty = f.typeOfIndex(inst); - const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); - - if (opt_ty.optionalReprIsPayload(zcu)) { - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - try f.writeCValue(writer, operand, .Other); - try writer.writeAll(";\n"); - return local; - } - - const a = try Assignment.start(f, writer, inst_ty); - try f.writeCValue(writer, local, .Other); - try a.assign(f, writer); - try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); + try writer.writeAll(compareOperatorC(operator)); + try writer.writeAll(rhs); try a.end(f, writer); return local; } -fn airOptionalPayloadPtr(f: *Function, inst: Air.Inst.Index) !CValue { +fn airOptionalPayload(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValue { const zcu = f.object.dg.zcu; + const ctype_pool = &f.object.dg.ctype_pool; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; - const writer = f.object.writer(); - const operand = try f.resolveInst(ty_op.operand); - try reap(f, inst, &.{ty_op.operand}); - const ptr_ty = f.typeOf(ty_op.operand); - const opt_ty = ptr_ty.childType(zcu); const inst_ty = f.typeOfIndex(inst); + const operand_ty = f.typeOf(ty_op.operand); + const opt_ty = if (is_ptr) operand_ty.childType(zcu) else operand_ty; + const opt_ctype = try f.ctypeFromType(opt_ty, .complete); + if (opt_ctype.isBool()) return if (is_ptr) .{ .undef = inst_ty } else .none; - if (!inst_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) { - return .{ .undef = inst_ty }; - } - - const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - - if (opt_ty.optionalReprIsPayload(zcu)) { - // the operand is just a regular pointer, no need to do anything special. - // *?*T -> **T and ?*T -> *T are **T -> **T and *T -> *T in C - try writer.writeAll(" = "); - try f.writeCValue(writer, operand, .Other); - } else { - try writer.writeAll(" = &"); - try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); + const operand = try f.resolveInst(ty_op.operand); + switch (opt_ctype.info(ctype_pool)) { + .basic, .pointer => return f.moveCValue(inst, inst_ty, operand), + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { + .is_null, .payload => { + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); + try f.writeCValue(writer, local, .Other); + try a.assign(f, writer); + if (is_ptr) { + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); + } else try f.writeCValueMember(writer, operand, .{ .identifier = "payload" }); + try a.end(f, writer); + return local; + }, + .ptr, .len => return f.moveCValue(inst, inst_ty, operand), + else => unreachable, + }, } - try writer.writeAll(";\n"); - return local; } fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { @@ -5410,38 +5525,46 @@ fn airOptionalPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.typeOf(ty_op.operand); - const opt_ty = operand_ty.childType(zcu); - const inst_ty = f.typeOfIndex(inst); - - if (opt_ty.optionalReprIsPayload(zcu)) { - if (f.liveness.isUnused(inst)) { - return .none; - } - const local = try f.allocLocal(inst, inst_ty); - // The payload and the optional are the same value. - // Setting to non-null will be done when the payload is set. - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - try f.writeCValue(writer, operand, .Other); - try writer.writeAll(";\n"); - return local; - } else { - try f.writeCValueDeref(writer, operand); - try writer.writeAll(".is_null = "); - try f.object.dg.renderValue(writer, Value.false, .Initializer); - try writer.writeAll(";\n"); - - if (f.liveness.isUnused(inst)) { + const opt_ctype = try f.ctypeFromType(operand_ty.childType(zcu), .complete); + switch (opt_ctype.info(&f.object.dg.ctype_pool)) { + .basic => { + const a = try Assignment.start(f, writer, opt_ctype); + try f.writeCValueDeref(writer, operand); + try a.assign(f, writer); + try f.object.dg.renderValue(writer, Value.false, .Initializer); + try a.end(f, writer); return .none; - } - - const local = try f.allocLocal(inst, inst_ty); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = &"); - try f.writeCValueDeref(writer, operand); - try writer.writeAll(".payload;\n"); - return local; + }, + .pointer => { + if (f.liveness.isUnused(inst)) return .none; + const local = try f.allocLocal(inst, inst_ty); + const a = try Assignment.start(f, writer, opt_ctype); + try f.writeCValue(writer, local, .Other); + try a.assign(f, writer); + try f.writeCValue(writer, operand, .Other); + try a.end(f, writer); + return local; + }, + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => { + { + const a = try Assignment.start(f, writer, opt_ctype); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "is_null" }); + try a.assign(f, writer); + try f.object.dg.renderValue(writer, Value.false, .Initializer); + try a.end(f, writer); + } + if (f.liveness.isUnused(inst)) return .none; + const local = try f.allocLocal(inst, inst_ty); + const a = try Assignment.start(f, writer, opt_ctype); + try f.writeCValue(writer, local, .Other); + try a.assign(f, writer); + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); + try a.end(f, writer); + return local; + }, } } @@ -5453,49 +5576,39 @@ fn fieldLocation( ) union(enum) { begin: void, field: CValue, - byte_offset: u32, - end: void, + byte_offset: u64, } { const ip = &zcu.intern_pool; const container_ty = Type.fromInterned(ip.indexToKey(container_ptr_ty.toIntern()).ptr_type.child); switch (ip.indexToKey(container_ty.toIntern())) { .struct_type => { const loaded_struct = ip.loadStructType(container_ty.toIntern()); - switch (loaded_struct.layout) { - .auto, .@"extern" => { - var field_it = loaded_struct.iterateRuntimeOrder(ip); - var before = true; - while (field_it.next()) |next_field_index| { - if (next_field_index == field_index) before = false; - if (before) continue; - const field_type = Type.fromInterned(loaded_struct.field_types.get(ip)[next_field_index]); - if (!field_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; - return .{ .field = if (loaded_struct.fieldName(ip, next_field_index).unwrap()) |field_name| - .{ .identifier = ip.stringToSlice(field_name) } - else - .{ .field = next_field_index } }; - } - return if (container_ty.hasRuntimeBitsIgnoreComptime(zcu)) .end else .begin; - }, - .@"packed" => return if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0) + return switch (loaded_struct.layout) { + .auto, .@"extern" => if (!container_ty.hasRuntimeBitsIgnoreComptime(zcu)) + .begin + else if (!field_ptr_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) + .{ .byte_offset = loaded_struct.offsets.get(ip)[field_index] } + else + .{ .field = if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| + .{ .identifier = field_name.toSlice(ip) } + else + .{ .field = field_index } }, + .@"packed" => if (field_ptr_ty.ptrInfo(zcu).packed_offset.host_size == 0) .{ .byte_offset = @divExact(zcu.structPackedFieldBitOffset(loaded_struct, field_index) + container_ptr_ty.ptrInfo(zcu).packed_offset.bit_offset, 8) } else .begin, - } - }, - .anon_struct_type => |anon_struct_info| { - for (field_index..anon_struct_info.types.len) |next_field_index| { - if (anon_struct_info.values.get(ip)[next_field_index] != .none) continue; - const field_type = Type.fromInterned(anon_struct_info.types.get(ip)[next_field_index]); - if (!field_type.hasRuntimeBitsIgnoreComptime(zcu)) continue; - return .{ .field = if (anon_struct_info.fieldName(ip, next_field_index).unwrap()) |field_name| - .{ .identifier = ip.stringToSlice(field_name) } - else - .{ .field = next_field_index } }; - } - return if (container_ty.hasRuntimeBitsIgnoreComptime(zcu)) .end else .begin; + }; }, + .anon_struct_type => |anon_struct_info| return if (!container_ty.hasRuntimeBitsIgnoreComptime(zcu)) + .begin + else if (!field_ptr_ty.childType(zcu).hasRuntimeBitsIgnoreComptime(zcu)) + .{ .byte_offset = container_ty.structFieldOffset(field_index, zcu) } + else + .{ .field = if (anon_struct_info.fieldName(ip, field_index).unwrap()) |field_name| + .{ .identifier = field_name.toSlice(ip) } + else + .{ .field = field_index } }, .union_type => { const loaded_union = ip.loadUnionType(container_ty.toIntern()); switch (loaded_union.getLayout(ip)) { @@ -5508,9 +5621,9 @@ fn fieldLocation( .begin; const field_name = loaded_union.loadTagType(ip).names.get(ip)[field_index]; return .{ .field = if (loaded_union.hasTag(ip)) - .{ .payload_identifier = ip.stringToSlice(field_name) } + .{ .payload_identifier = field_name.toSlice(ip) } else - .{ .identifier = ip.stringToSlice(field_name) } }; + .{ .identifier = field_name.toSlice(ip) } }; }, .@"packed" => return .begin, } @@ -5591,10 +5704,6 @@ fn airFieldParentPtr(f: *Function, inst: Air.Inst.Index) !CValue { try f.fmtIntLiteral(try zcu.intValue(Type.usize, byte_offset)), }); }, - .end => { - try f.writeCValue(writer, field_ptr_val, .Other); - try writer.print(" - {}", .{try f.fmtIntLiteral(try zcu.intValue(Type.usize, 1))}); - }, } try writer.writeAll(";\n"); @@ -5639,11 +5748,6 @@ fn fieldPtr( try f.fmtIntLiteral(try zcu.intValue(Type.usize, byte_offset)), }); }, - .end => { - try writer.writeByte('('); - try f.writeCValue(writer, container_ptr_val, .Other); - try writer.print(" + {})", .{try f.fmtIntLiteral(try zcu.intValue(Type.usize, 1))}); - }, } try writer.writeAll(";\n"); @@ -5675,7 +5779,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { const loaded_struct = ip.loadStructType(struct_ty.toIntern()); switch (loaded_struct.layout) { .auto, .@"extern" => break :field_name if (loaded_struct.fieldName(ip, extra.field_index).unwrap()) |field_name| - .{ .identifier = ip.stringToSlice(field_name) } + .{ .identifier = field_name.toSlice(ip) } else .{ .field = extra.field_index }, .@"packed" => { @@ -5720,20 +5824,22 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { if (inst_ty.eql(field_int_ty, f.object.dg.zcu)) return temp_local; const local = try f.allocLocal(inst, inst_ty); - try writer.writeAll("memcpy("); - try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); - try writer.writeAll(", "); - try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); - try writer.writeAll(", sizeof("); - try f.renderType(writer, inst_ty); - try writer.writeAll("));\n"); + if (local.new_local != temp_local.new_local) { + try writer.writeAll("memcpy("); + try f.writeCValue(writer, .{ .local_ref = local.new_local }, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValue(writer, .{ .local_ref = temp_local.new_local }, .FunctionArgument); + try writer.writeAll(", sizeof("); + try f.renderType(writer, inst_ty); + try writer.writeAll("));\n"); + } try freeLocal(f, inst, temp_local.new_local, null); return local; }, } }, .anon_struct_type => |anon_struct_info| if (anon_struct_info.fieldName(ip, extra.field_index).unwrap()) |field_name| - .{ .identifier = ip.stringToSlice(field_name) } + .{ .identifier = field_name.toSlice(ip) } else .{ .field = extra.field_index }, .union_type => field_name: { @@ -5742,9 +5848,9 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { .auto, .@"extern" => { const name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index]; break :field_name if (loaded_union.hasTag(ip)) - .{ .payload_identifier = ip.stringToSlice(name) } + .{ .payload_identifier = name.toSlice(ip) } else - .{ .identifier = ip.stringToSlice(name) }; + .{ .identifier = name.toSlice(ip) }; }, .@"packed" => { const operand_lval = if (struct_byval == .constant) blk: { @@ -5755,20 +5861,23 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(";\n"); break :blk operand_local; } else struct_byval; - const local = try f.allocLocal(inst, inst_ty); - try writer.writeAll("memcpy(&"); - try f.writeCValue(writer, local, .Other); - try writer.writeAll(", &"); - try f.writeCValue(writer, operand_lval, .Other); - try writer.writeAll(", sizeof("); - try f.renderType(writer, inst_ty); - try writer.writeAll("));\n"); - - if (struct_byval == .constant) { - try freeLocal(f, inst, operand_lval.new_local, null); + if (switch (local) { + .new_local, .local => |local_index| switch (operand_lval) { + .new_local, .local => |operand_local_index| local_index != operand_local_index, + else => true, + }, + else => true, + }) { + try writer.writeAll("memcpy(&"); + try f.writeCValue(writer, local, .Other); + try writer.writeAll(", &"); + try f.writeCValue(writer, operand_lval, .Other); + try writer.writeAll(", sizeof("); + try f.renderType(writer, inst_ty); + try writer.writeAll("));\n"); } - + try f.freeCValue(inst, operand_lval); return local; }, } @@ -5777,7 +5886,7 @@ fn airStructFieldVal(f: *Function, inst: Air.Inst.Index) !CValue { }; const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValueMember(writer, struct_byval, field_name); @@ -5850,7 +5959,7 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu } const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); if (is_ptr) { @@ -5862,35 +5971,42 @@ fn airUnwrapErrUnionPay(f: *Function, inst: Air.Inst.Index, is_ptr: bool) !CValu } fn airWrapOptional(f: *Function, inst: Air.Inst.Index) !CValue { - const zcu = f.object.dg.zcu; + const ctype_pool = &f.object.dg.ctype_pool; const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); - const repr_is_payload = inst_ty.optionalReprIsPayload(zcu); - const payload_ty = f.typeOf(ty_op.operand); - const payload = try f.resolveInst(ty_op.operand); - try reap(f, inst, &.{ty_op.operand}); + const inst_ctype = try f.ctypeFromType(inst_ty, .complete); + if (inst_ctype.isBool()) return .{ .constant = Value.true }; - const writer = f.object.writer(); - const local = try f.allocLocal(inst, inst_ty); - { - const a = try Assignment.start(f, writer, payload_ty); - if (repr_is_payload) - try f.writeCValue(writer, local, .Other) - else - try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); - try a.assign(f, writer); - try f.writeCValue(writer, payload, .Other); - try a.end(f, writer); - } - if (!repr_is_payload) { - const a = try Assignment.start(f, writer, Type.bool); - try f.writeCValueMember(writer, local, .{ .identifier = "is_null" }); - try a.assign(f, writer); - try f.object.dg.renderValue(writer, Value.false, .Other); - try a.end(f, writer); + const operand = try f.resolveInst(ty_op.operand); + switch (inst_ctype.info(ctype_pool)) { + .basic, .pointer => return f.moveCValue(inst, inst_ty, operand), + .aligned, .array, .vector, .fwd_decl, .function => unreachable, + .aggregate => |aggregate| switch (aggregate.fields.at(0, ctype_pool).name.index) { + .is_null, .payload => { + const operand_ctype = try f.ctypeFromType(f.typeOf(ty_op.operand), .complete); + const writer = f.object.writer(); + const local = try f.allocLocal(inst, inst_ty); + { + const a = try Assignment.start(f, writer, CType.bool); + try f.writeCValueMember(writer, local, .{ .identifier = "is_null" }); + try a.assign(f, writer); + try writer.writeAll("false"); + try a.end(f, writer); + } + { + const a = try Assignment.start(f, writer, operand_ctype); + try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); + try a.assign(f, writer); + try f.writeCValue(writer, operand, .Initializer); + try a.end(f, writer); + } + return local; + }, + .ptr, .len => return f.moveCValue(inst, inst_ty, operand), + else => unreachable, + }, } - return local; } fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { @@ -5913,14 +6029,14 @@ fn airWrapErrUnionErr(f: *Function, inst: Air.Inst.Index) !CValue { } if (!repr_is_err) { - const a = try Assignment.start(f, writer, payload_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(payload_ty, .complete)); try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); try a.assign(f, writer); try f.object.dg.renderUndefValue(writer, payload_ty, .Other); try a.end(f, writer); } { - const a = try Assignment.start(f, writer, err_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(err_ty, .complete)); if (repr_is_err) try f.writeCValue(writer, local, .Other) else @@ -5936,31 +6052,43 @@ fn airErrUnionPayloadPtrSet(f: *Function, inst: Air.Inst.Index) !CValue { const zcu = f.object.dg.zcu; const writer = f.object.writer(); const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; + const inst_ty = f.typeOfIndex(inst); const operand = try f.resolveInst(ty_op.operand); - const error_union_ty = f.typeOf(ty_op.operand).childType(zcu); + const operand_ty = f.typeOf(ty_op.operand); + const error_union_ty = operand_ty.childType(zcu); const payload_ty = error_union_ty.errorUnionPayload(zcu); const err_int_ty = try zcu.errorIntType(); const no_err = try zcu.intValue(err_int_ty, 0); + try reap(f, inst, &.{ty_op.operand}); // First, set the non-error value. if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { + const a = try Assignment.start(f, writer, try f.ctypeFromType(operand_ty, .complete)); try f.writeCValueDeref(writer, operand); - try writer.print(" = {};\n", .{try f.fmtIntLiteral(no_err)}); - return operand; + try a.assign(f, writer); + try writer.print("{}", .{try f.fmtIntLiteral(no_err)}); + try a.end(f, writer); + return .none; + } + { + const a = try Assignment.start(f, writer, try f.ctypeFromType(err_int_ty, .complete)); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "error" }); + try a.assign(f, writer); + try writer.print("{}", .{try f.fmtIntLiteral(no_err)}); + try a.end(f, writer); } - try reap(f, inst, &.{ty_op.operand}); - try f.writeCValueDeref(writer, operand); - try writer.print(".error = {};\n", .{try f.fmtIntLiteral(no_err)}); // Then return the payload pointer (only if it is used) if (f.liveness.isUnused(inst)) return .none; - const local = try f.allocLocal(inst, f.typeOfIndex(inst)); + const local = try f.allocLocal(inst, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = &("); - try f.writeCValueDeref(writer, operand); - try writer.writeAll(").payload;\n"); + try a.assign(f, writer); + try writer.writeByte('&'); + try f.writeCValueDerefMember(writer, operand, .{ .identifier = "payload" }); + try a.end(f, writer); return local; } @@ -5993,14 +6121,14 @@ fn airWrapErrUnionPay(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); if (!repr_is_err) { - const a = try Assignment.start(f, writer, payload_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(payload_ty, .complete)); try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); try a.assign(f, writer); try f.writeCValue(writer, payload, .Other); try a.end(f, writer); } { - const a = try Assignment.start(f, writer, err_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(err_ty, .complete)); if (repr_is_err) try f.writeCValue(writer, local, .Other) else @@ -6025,7 +6153,7 @@ fn airIsErr(f: *Function, inst: Air.Inst.Index, is_ptr: bool, operator: []const const payload_ty = err_union_ty.errorUnionPayload(zcu); const error_ty = err_union_ty.errorUnionSet(zcu); - const a = try Assignment.start(f, writer, Type.bool); + const a = try Assignment.start(f, writer, CType.bool); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); const err_int_ty = try zcu.errorIntType(); @@ -6062,7 +6190,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { const array_ty = operand_ty.childType(zcu); { - const a = try Assignment.start(f, writer, ptr_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(ptr_ty, .complete)); try f.writeCValueMember(writer, local, .{ .identifier = "ptr" }); try a.assign(f, writer); if (operand == .undef) { @@ -6088,7 +6216,7 @@ fn airArrayToSlice(f: *Function, inst: Air.Inst.Index) !CValue { try a.end(f, writer); } { - const a = try Assignment.start(f, writer, Type.usize); + const a = try Assignment.start(f, writer, CType.usize); try f.writeCValueMember(writer, local, .{ .identifier = "len" }); try a.assign(f, writer); try writer.print("{}", .{ @@ -6123,7 +6251,7 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, writer, operand_ty); - const a = try Assignment.start(f, writer, scalar_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(scalar_ty, .complete)); try f.writeCValue(writer, local, .Other); try v.elem(f, writer); try a.assign(f, writer); @@ -6165,11 +6293,10 @@ fn airIntFromPtr(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll(" = ("); try f.renderType(writer, inst_ty); try writer.writeByte(')'); - if (operand_ty.isSlice(zcu)) { - try f.writeCValueMember(writer, operand, .{ .identifier = "ptr" }); - } else { + if (operand_ty.isSlice(zcu)) + try f.writeCValueMember(writer, operand, .{ .identifier = "ptr" }) + else try f.writeCValue(writer, operand, .Other); - } try writer.writeAll(";\n"); return local; } @@ -6338,9 +6465,10 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const new_value = try f.resolveInst(extra.new_value); const ptr_ty = f.typeOf(extra.ptr); const ty = ptr_ty.childType(zcu); + const ctype = try f.ctypeFromType(ty, .complete); const writer = f.object.writer(); - const new_value_mat = try Materialize.start(f, inst, writer, ty, new_value); + const new_value_mat = try Materialize.start(f, inst, ty, new_value); try reap(f, inst, &.{ extra.ptr, extra.expected_value, extra.new_value }); const repr_ty = if (ty.isRuntimeFloat()) @@ -6351,7 +6479,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue const local = try f.allocLocal(inst, inst_ty); if (inst_ty.isPtrLikeOptional(zcu)) { { - const a = try Assignment.start(f, writer, ty); + const a = try Assignment.start(f, writer, ctype); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValue(writer, expected_value, .Other); @@ -6381,7 +6509,7 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll(") {\n"); f.object.indent_writer.pushIndent(); { - const a = try Assignment.start(f, writer, ty); + const a = try Assignment.start(f, writer, ctype); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try writer.writeAll("NULL"); @@ -6391,14 +6519,14 @@ fn airCmpxchg(f: *Function, inst: Air.Inst.Index, flavor: [*:0]const u8) !CValue try writer.writeAll("}\n"); } else { { - const a = try Assignment.start(f, writer, ty); + const a = try Assignment.start(f, writer, ctype); try f.writeCValueMember(writer, local, .{ .identifier = "payload" }); try a.assign(f, writer); try f.writeCValue(writer, expected_value, .Other); try a.end(f, writer); } { - const a = try Assignment.start(f, writer, Type.bool); + const a = try Assignment.start(f, writer, CType.bool); try f.writeCValueMember(writer, local, .{ .identifier = "is_null" }); try a.assign(f, writer); try writer.print("zig_cmpxchg_{s}((zig_atomic(", .{flavor}); @@ -6444,7 +6572,7 @@ fn airAtomicRmw(f: *Function, inst: Air.Inst.Index) !CValue { const operand = try f.resolveInst(extra.operand); const writer = f.object.writer(); - const operand_mat = try Materialize.start(f, inst, writer, ty, operand); + const operand_mat = try Materialize.start(f, inst, ty, operand); try reap(f, inst, &.{ pl_op.operand, extra.operand }); const repr_bits = @as(u16, @intCast(ty.abiSize(zcu) * 8)); @@ -6533,7 +6661,7 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa const element = try f.resolveInst(bin_op.rhs); const writer = f.object.writer(); - const element_mat = try Materialize.start(f, inst, writer, ty, element); + const element_mat = try Materialize.start(f, inst, ty, element); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); const repr_ty = if (ty.isRuntimeFloat()) @@ -6644,7 +6772,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { try f.writeCValue(writer, index, .Other); try writer.writeAll(") "); - const a = try Assignment.start(f, writer, elem_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(elem_ty, .complete)); try writer.writeAll("(("); try f.renderType(writer, elem_ptr_ty); try writer.writeByte(')'); @@ -6669,7 +6797,7 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { .Slice => { try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); try writer.writeAll(", "); - try f.writeCValue(writer, bitcasted.c_value, .FunctionArgument); + try f.writeCValue(writer, bitcasted, .FunctionArgument); try writer.writeAll(", "); try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); try writer.writeAll(");\n"); @@ -6680,12 +6808,12 @@ fn airMemset(f: *Function, inst: Air.Inst.Index, safety: bool) !CValue { try f.writeCValue(writer, dest_slice, .FunctionArgument); try writer.writeAll(", "); - try f.writeCValue(writer, bitcasted.c_value, .FunctionArgument); + try f.writeCValue(writer, bitcasted, .FunctionArgument); try writer.print(", {d});\n", .{len}); }, .Many, .C => unreachable, } - try bitcasted.free(f); + try f.freeCValue(inst, bitcasted); try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } @@ -6732,7 +6860,7 @@ fn airSetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const tag_ty = union_ty.unionTagTypeSafety(zcu).?; const writer = f.object.writer(); - const a = try Assignment.start(f, writer, tag_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(tag_ty, .complete)); try f.writeCValueDerefMember(writer, union_ptr, .{ .identifier = "tag" }); try a.assign(f, writer); try f.writeCValue(writer, new_tag, .Other); @@ -6754,7 +6882,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue { const inst_ty = f.typeOfIndex(inst); const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); - const a = try Assignment.start(f, writer, inst_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_ty, .complete)); try f.writeCValue(writer, local, .Other); try a.assign(f, writer); try f.writeCValueMember(writer, operand, .{ .identifier = "tag" }); @@ -6812,7 +6940,7 @@ fn airSplat(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); const v = try Vectorize.start(f, inst, writer, inst_ty); - const a = try Assignment.init(f, inst_scalar_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(inst_scalar_ty, .complete)); try f.writeCValue(writer, local, .Other); try v.elem(f, writer); try a.assign(f, writer); @@ -7065,7 +7193,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const local = try f.allocLocal(inst, inst_ty); switch (ip.indexToKey(inst_ty.toIntern())) { inline .array_type, .vector_type => |info, tag| { - const a = try Assignment.init(f, Type.fromInterned(info.child)); + const a: Assignment = .{ + .ctype = try f.ctypeFromType(Type.fromInterned(info.child), .complete), + }; for (resolved_elements, 0..) |element, i| { try a.restart(f, writer); try f.writeCValue(writer, local, .Other); @@ -7092,9 +7222,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const field_ty = Type.fromInterned(loaded_struct.field_types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - const a = try Assignment.start(f, writer, field_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(field_ty, .complete)); try f.writeCValueMember(writer, local, if (loaded_struct.fieldName(ip, field_index).unwrap()) |field_name| - .{ .identifier = ip.stringToSlice(field_name) } + .{ .identifier = field_name.toSlice(ip) } else .{ .field = field_index }); try a.assign(f, writer); @@ -7172,9 +7302,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { const field_ty = Type.fromInterned(anon_struct_info.types.get(ip)[field_index]); if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) continue; - const a = try Assignment.start(f, writer, field_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(field_ty, .complete)); try f.writeCValueMember(writer, local, if (anon_struct_info.fieldName(ip, field_index).unwrap()) |field_name| - .{ .identifier = ip.stringToSlice(field_name) } + .{ .identifier = field_name.toSlice(ip) } else .{ .field = field_index }); try a.assign(f, writer); @@ -7202,13 +7332,7 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const writer = f.object.writer(); const local = try f.allocLocal(inst, union_ty); - if (loaded_union.getLayout(ip) == .@"packed") { - try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); - try f.writeCValue(writer, payload, .Initializer); - try writer.writeAll(";\n"); - return local; - } + if (loaded_union.getLayout(ip) == .@"packed") return f.moveCValue(inst, union_ty, payload); const field: CValue = if (union_ty.unionTagTypeSafety(zcu)) |tag_ty| field: { const layout = union_ty.unionGetLayout(zcu); @@ -7216,16 +7340,16 @@ fn airUnionInit(f: *Function, inst: Air.Inst.Index) !CValue { const field_index = tag_ty.enumFieldIndex(field_name, zcu).?; const tag_val = try zcu.enumValueFieldIndex(tag_ty, field_index); - const a = try Assignment.start(f, writer, tag_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(tag_ty, .complete)); try f.writeCValueMember(writer, local, .{ .identifier = "tag" }); try a.assign(f, writer); try writer.print("{}", .{try f.fmtIntLiteral(try tag_val.intFromEnum(tag_ty, zcu))}); try a.end(f, writer); } - break :field .{ .payload_identifier = ip.stringToSlice(field_name) }; - } else .{ .identifier = ip.stringToSlice(field_name) }; + break :field .{ .payload_identifier = field_name.toSlice(ip) }; + } else .{ .identifier = field_name.toSlice(ip) }; - const a = try Assignment.start(f, writer, payload_ty); + const a = try Assignment.start(f, writer, try f.ctypeFromType(payload_ty, .complete)); try f.writeCValueMember(writer, local, field); try a.assign(f, writer); try f.writeCValue(writer, payload, .Other); @@ -7680,11 +7804,13 @@ fn StringLiteral(comptime WriterType: type) type { // MSVC throws C2078 if an array of size 65536 or greater is initialized with a string literal, // regardless of the length of the string literal initializing it. Array initializer syntax is // used instead. - const max_string_initializer_len = 65535; + // C99 only requires 4095. + const max_string_initializer_len = @min(65535, 4095); // MSVC has a length limit of 16380 per string literal (before concatenation) + // C99 only requires 4095. const max_char_len = 4; - const max_literal_len = 16380 - max_char_len; + const max_literal_len = @min(16380 - max_char_len, 4095); return struct { len: u64, @@ -7856,13 +7982,13 @@ fn formatIntLiteral( } = switch (data.ctype.info(ctype_pool)) { .basic => |basic_info| switch (basic_info) { else => .{ - .ctype = .{ .index = .void }, + .ctype = CType.void, .count = 1, .endian = .little, .homogeneous = true, }, .zig_u128, .zig_i128 => .{ - .ctype = .{ .index = .uint64_t }, + .ctype = CType.u64, .count = 2, .endian = .big, .homogeneous = false, @@ -7978,28 +8104,12 @@ fn formatIntLiteral( const Materialize = struct { local: CValue, - pub fn start( - f: *Function, - inst: Air.Inst.Index, - writer: anytype, - ty: Type, - value: CValue, - ) !Materialize { - switch (value) { - .local_ref, .constant, .decl_ref, .undef => { - const local = try f.allocLocal(inst, ty); - - const a = try Assignment.start(f, writer, ty); - try f.writeCValue(writer, local, .Other); - try a.assign(f, writer); - try f.writeCValue(writer, value, .Other); - try a.end(f, writer); - - return .{ .local = local }; - }, - .new_local => |local| return .{ .local = .{ .local = local } }, - else => return .{ .local = value }, - } + pub fn start(f: *Function, inst: Air.Inst.Index, ty: Type, value: CValue) !Materialize { + return .{ .local = switch (value) { + .local_ref, .constant, .decl_ref, .undef => try f.moveCValue(inst, ty, value), + .new_local => |local| .{ .local = local }, + else => value, + } }; } pub fn mat(self: Materialize, f: *Function, writer: anytype) !void { @@ -8007,22 +8117,15 @@ const Materialize = struct { } pub fn end(self: Materialize, f: *Function, inst: Air.Inst.Index) !void { - switch (self.local) { - .new_local => |local| try freeLocal(f, inst, local, null), - else => {}, - } + try f.freeCValue(inst, self.local); } }; const Assignment = struct { ctype: CType, - pub fn init(f: *Function, ty: Type) !Assignment { - return .{ .ctype = try f.ctypeFromType(ty, .complete) }; - } - - pub fn start(f: *Function, writer: anytype, ty: Type) !Assignment { - const self = try init(f, ty); + pub fn start(f: *Function, writer: anytype, ctype: CType) !Assignment { + const self: Assignment = .{ .ctype = ctype }; try self.restart(f, writer); return self; } diff --git a/src/codegen/c/Type.zig b/src/codegen/c/Type.zig index 1c460acc6b..357677522c 100644 --- a/src/codegen/c/Type.zig +++ b/src/codegen/c/Type.zig @@ -1,13 +1,33 @@ index: CType.Index, +pub const @"void": CType = .{ .index = .void }; +pub const @"bool": CType = .{ .index = .bool }; +pub const @"i8": CType = .{ .index = .int8_t }; +pub const @"u8": CType = .{ .index = .uint8_t }; +pub const @"i16": CType = .{ .index = .int16_t }; +pub const @"u16": CType = .{ .index = .uint16_t }; +pub const @"i32": CType = .{ .index = .int32_t }; +pub const @"u32": CType = .{ .index = .uint32_t }; +pub const @"i64": CType = .{ .index = .int64_t }; +pub const @"u64": CType = .{ .index = .uint64_t }; +pub const @"i128": CType = .{ .index = .zig_i128 }; +pub const @"u128": CType = .{ .index = .zig_u128 }; +pub const @"isize": CType = .{ .index = .intptr_t }; +pub const @"usize": CType = .{ .index = .uintptr_t }; +pub const @"f16": CType = .{ .index = .zig_f16 }; +pub const @"f32": CType = .{ .index = .zig_f32 }; +pub const @"f64": CType = .{ .index = .zig_f64 }; +pub const @"f80": CType = .{ .index = .zig_f80 }; +pub const @"f128": CType = .{ .index = .zig_f128 }; + pub fn fromPoolIndex(pool_index: usize) CType { return .{ .index = @enumFromInt(CType.Index.first_pool_index + pool_index) }; } pub fn toPoolIndex(ctype: CType) ?u32 { - const pool_index, const is_basic = + const pool_index, const is_null = @subWithOverflow(@intFromEnum(ctype.index), CType.Index.first_pool_index); - return switch (is_basic) { + return switch (is_null) { 0 => pool_index, 1 => null, }; @@ -710,20 +730,6 @@ pub const Kind = enum { } }; -pub const String = struct { - index: String.Index, - - const Index = enum(u32) { - _, - }; - - pub fn slice(string: String, pool: *const Pool) []const u8 { - const start = pool.string_indices.items[@intFromEnum(string.index)]; - const end = pool.string_indices.items[@intFromEnum(string.index) + 1]; - return pool.string_bytes.items[start..end]; - } -}; - pub const Info = union(enum) { basic: CType.Index, pointer: Pointer, @@ -766,7 +772,7 @@ pub const Info = union(enum) { pub const AggregateTag = enum { @"enum", @"struct", @"union" }; pub const Field = struct { - name: String, + name: Pool.String, ctype: CType, alignas: AlignAs, @@ -812,12 +818,15 @@ pub const Info = union(enum) { rhs_pool: *const Pool, pool_adapter: anytype, ) bool { - return std.meta.eql(lhs_field.alignas, rhs_field.alignas) and - pool_adapter.eql(lhs_field.ctype, rhs_field.ctype) and std.mem.eql( - u8, - lhs_field.name.slice(lhs_pool), - rhs_field.name.slice(rhs_pool), - ); + if (!std.meta.eql(lhs_field.alignas, rhs_field.alignas)) return false; + if (!pool_adapter.eql(lhs_field.ctype, rhs_field.ctype)) return false; + return if (lhs_field.name.toPoolSlice(lhs_pool)) |lhs_name| + if (rhs_field.name.toPoolSlice(rhs_pool)) |rhs_name| + std.mem.eql(u8, lhs_name, rhs_name) + else + false + else + lhs_field.name.index == rhs_field.name.index; } }; @@ -918,6 +927,86 @@ pub const Pool = struct { const Map = std.AutoArrayHashMapUnmanaged(void, void); + pub const String = struct { + index: String.Index, + + const FormatData = struct { string: String, pool: *const Pool }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (fmt_str.len > 0) @compileError("invalid format string '" ++ fmt_str ++ "'"); + if (data.string.toSlice(data.pool)) |slice| + try writer.writeAll(slice) + else + try writer.print("f{d}", .{@intFromEnum(data.string.index)}); + } + pub fn fmt(str: String, pool: *const Pool) std.fmt.Formatter(format) { + return .{ .data = .{ .string = str, .pool = pool } }; + } + + fn fromUnnamed(index: u31) String { + return .{ .index = @enumFromInt(index) }; + } + + fn isNamed(str: String) bool { + return @intFromEnum(str.index) >= String.Index.first_named_index; + } + + pub fn toSlice(str: String, pool: *const Pool) ?[]const u8 { + return str.toPoolSlice(pool) orelse if (str.isNamed()) @tagName(str.index) else null; + } + + fn toPoolSlice(str: String, pool: *const Pool) ?[]const u8 { + if (str.toPoolIndex()) |pool_index| { + const start = pool.string_indices.items[pool_index + 0]; + const end = pool.string_indices.items[pool_index + 1]; + return pool.string_bytes.items[start..end]; + } else return null; + } + + fn fromPoolIndex(pool_index: usize) String { + return .{ .index = @enumFromInt(String.Index.first_pool_index + pool_index) }; + } + + fn toPoolIndex(str: String) ?u32 { + const pool_index, const is_null = + @subWithOverflow(@intFromEnum(str.index), String.Index.first_pool_index); + return switch (is_null) { + 0 => pool_index, + 1 => null, + }; + } + + const Index = enum(u32) { + array = first_named_index, + @"error", + is_null, + len, + payload, + ptr, + tag, + _, + + const first_named_index: u32 = 1 << 31; + const first_pool_index: u32 = first_named_index + @typeInfo(String.Index).Enum.fields.len; + }; + + const Adapter = struct { + pool: *const Pool, + pub fn hash(_: @This(), slice: []const u8) Map.Hash { + return @truncate(Hasher.Impl.hash(1, slice)); + } + pub fn eql(string_adapter: @This(), lhs_slice: []const u8, _: void, rhs_index: usize) bool { + const rhs_string = String.fromPoolIndex(rhs_index); + const rhs_slice = rhs_string.toPoolSlice(string_adapter.pool).?; + return std.mem.eql(u8, lhs_slice, rhs_slice); + } + }; + }; + pub const empty: Pool = .{ .map = .{}, .items = .{}, @@ -1200,26 +1289,26 @@ pub const Pool = struct { kind: Kind, ) !CType { switch (int_info.bits) { - 0 => return .{ .index = .void }, + 0 => return CType.void, 1...8 => switch (int_info.signedness) { - .unsigned => return .{ .index = .uint8_t }, - .signed => return .{ .index = .int8_t }, + .signed => return CType.i8, + .unsigned => return CType.u8, }, 9...16 => switch (int_info.signedness) { - .unsigned => return .{ .index = .uint16_t }, - .signed => return .{ .index = .int16_t }, + .signed => return CType.i16, + .unsigned => return CType.u16, }, 17...32 => switch (int_info.signedness) { - .unsigned => return .{ .index = .uint32_t }, - .signed => return .{ .index = .int32_t }, + .signed => return CType.i32, + .unsigned => return CType.u32, }, 33...64 => switch (int_info.signedness) { - .unsigned => return .{ .index = .uint64_t }, - .signed => return .{ .index = .int64_t }, + .signed => return CType.i64, + .unsigned => return CType.u64, }, 65...128 => switch (int_info.signedness) { - .unsigned => return .{ .index = .zig_u128 }, - .signed => return .{ .index = .zig_i128 }, + .signed => return CType.i128, + .unsigned => return CType.u128, }, else => { const target = &mod.resolved_target.result; @@ -1235,7 +1324,7 @@ pub const Pool = struct { if (!kind.isParameter()) return array_ctype; var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "array"), + .name = .{ .index = .array }, .ctype = array_ctype, .alignas = AlignAs.fromAbiAlignment(abi_align), }, @@ -1267,19 +1356,19 @@ pub const Pool = struct { .null_type, .undefined_type, .enum_literal_type, - => return .{ .index = .void }, - .u1_type, .u8_type => return .{ .index = .uint8_t }, - .i8_type => return .{ .index = .int8_t }, - .u16_type => return .{ .index = .uint16_t }, - .i16_type => return .{ .index = .int16_t }, - .u29_type, .u32_type => return .{ .index = .uint32_t }, - .i32_type => return .{ .index = .int32_t }, - .u64_type => return .{ .index = .uint64_t }, - .i64_type => return .{ .index = .int64_t }, - .u80_type, .u128_type => return .{ .index = .zig_u128 }, - .i128_type => return .{ .index = .zig_i128 }, - .usize_type => return .{ .index = .uintptr_t }, - .isize_type => return .{ .index = .intptr_t }, + => return CType.void, + .u1_type, .u8_type => return CType.u8, + .i8_type => return CType.i8, + .u16_type => return CType.u16, + .i16_type => return CType.i16, + .u29_type, .u32_type => return CType.u32, + .i32_type => return CType.i32, + .u64_type => return CType.u64, + .i64_type => return CType.i64, + .u80_type, .u128_type => return CType.u128, + .i128_type => return CType.i128, + .usize_type => return CType.usize, + .isize_type => return CType.isize, .c_char_type => return .{ .index = .char }, .c_short_type => return .{ .index = .short }, .c_ushort_type => return .{ .index = .@"unsigned short" }, @@ -1290,12 +1379,12 @@ pub const Pool = struct { .c_longlong_type => return .{ .index = .@"long long" }, .c_ulonglong_type => return .{ .index = .@"unsigned long long" }, .c_longdouble_type => return .{ .index = .@"long double" }, - .f16_type => return .{ .index = .zig_f16 }, - .f32_type => return .{ .index = .zig_f32 }, - .f64_type => return .{ .index = .zig_f64 }, - .f80_type => return .{ .index = .zig_f80 }, - .f128_type => return .{ .index = .zig_f128 }, - .bool_type, .optional_noreturn_type => return .{ .index = .bool }, + .f16_type => return CType.f16, + .f32_type => return CType.f32, + .f64_type => return CType.f64, + .f80_type => return CType.f80, + .f128_type => return CType.f128, + .bool_type, .optional_noreturn_type => return CType.bool, .noreturn_type, .anyframe_type, .generic_poison_type, @@ -1324,17 +1413,17 @@ pub const Pool = struct { }, mod, kind), .manyptr_u8_type, => return pool.getPointer(allocator, .{ - .elem_ctype = .{ .index = .uint8_t }, + .elem_ctype = CType.u8, }), .manyptr_const_u8_type, .manyptr_const_u8_sentinel_0_type, => return pool.getPointer(allocator, .{ - .elem_ctype = .{ .index = .uint8_t }, + .elem_ctype = CType.u8, .@"const" = true, }), .single_const_pointer_to_comptime_int_type, => return pool.getPointer(allocator, .{ - .elem_ctype = .{ .index = .void }, + .elem_ctype = CType.void, .@"const" = true, }), .slice_const_u8_type, @@ -1343,16 +1432,16 @@ pub const Pool = struct { const target = &mod.resolved_target.result; var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "ptr"), + .name = .{ .index = .ptr }, .ctype = try pool.getPointer(allocator, .{ - .elem_ctype = .{ .index = .uint8_t }, + .elem_ctype = CType.u8, .@"const" = true, }), .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target.*)), }, .{ - .name = try pool.string(allocator, "len"), - .ctype = .{ .index = .uintptr_t }, + .name = .{ .index = .len }, + .ctype = CType.usize, .alignas = AlignAs.fromAbiAlignment( Type.intAbiAlignment(target.ptrBitWidth(), target.*), ), @@ -1442,7 +1531,7 @@ pub const Pool = struct { const target = &mod.resolved_target.result; var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "ptr"), + .name = .{ .index = .ptr }, .ctype = try pool.fromType( allocator, scratch, @@ -1454,8 +1543,8 @@ pub const Pool = struct { .alignas = AlignAs.fromAbiAlignment(Type.ptrAbiAlignment(target.*)), }, .{ - .name = try pool.string(allocator, "len"), - .ctype = .{ .index = .uintptr_t }, + .name = .{ .index = .len }, + .ctype = CType.usize, .alignas = AlignAs.fromAbiAlignment( Type.intAbiAlignment(target.ptrBitWidth(), target.*), ), @@ -1465,8 +1554,8 @@ pub const Pool = struct { }, }, .array_type => |array_info| { - const len = array_info.len + @intFromBool(array_info.sentinel != .none); - if (len == 0) return .{ .index = .void }; + const len = array_info.lenIncludingSentinel(); + if (len == 0) return CType.void; const elem_type = Type.fromInterned(array_info.child); const elem_ctype = try pool.fromType( allocator, @@ -1476,15 +1565,15 @@ pub const Pool = struct { mod, kind.noParameter(), ); - if (elem_ctype.index == .void) return .{ .index = .void }; + if (elem_ctype.index == .void) return CType.void; const array_ctype = try pool.getArray(allocator, .{ .elem_ctype = elem_ctype, - .len = array_info.len + @intFromBool(array_info.sentinel != .none), + .len = len, }); if (!kind.isParameter()) return array_ctype; var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "array"), + .name = .{ .index = .array }, .ctype = array_ctype, .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)), }, @@ -1492,7 +1581,7 @@ pub const Pool = struct { return pool.fromFields(allocator, .@"struct", &fields, kind); }, .vector_type => |vector_info| { - if (vector_info.len == 0) return .{ .index = .void }; + if (vector_info.len == 0) return CType.void; const elem_type = Type.fromInterned(vector_info.child); const elem_ctype = try pool.fromType( allocator, @@ -1502,7 +1591,7 @@ pub const Pool = struct { mod, kind.noParameter(), ); - if (elem_ctype.index == .void) return .{ .index = .void }; + if (elem_ctype.index == .void) return CType.void; const vector_ctype = try pool.getVector(allocator, .{ .elem_ctype = elem_ctype, .len = vector_info.len, @@ -1510,7 +1599,7 @@ pub const Pool = struct { if (!kind.isParameter()) return vector_ctype; var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "array"), + .name = .{ .index = .array }, .ctype = vector_ctype, .alignas = AlignAs.fromAbiAlignment(elem_type.abiAlignment(zcu)), }, @@ -1518,7 +1607,7 @@ pub const Pool = struct { return pool.fromFields(allocator, .@"struct", &fields, kind); }, .opt_type => |payload_type| { - if (ip.isNoReturn(payload_type)) return .{ .index = .void }; + if (ip.isNoReturn(payload_type)) return CType.void; const payload_ctype = try pool.fromType( allocator, scratch, @@ -1527,7 +1616,7 @@ pub const Pool = struct { mod, kind.noParameter(), ); - if (payload_ctype.index == .void) return .{ .index = .bool }; + if (payload_ctype.index == .void) return CType.bool; switch (payload_type) { .anyerror_type => return payload_ctype, else => switch (ip.indexToKey(payload_type)) { @@ -1539,12 +1628,12 @@ pub const Pool = struct { } var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "is_null"), - .ctype = .{ .index = .bool }, + .name = .{ .index = .is_null }, + .ctype = CType.bool, .alignas = AlignAs.fromAbiAlignment(.@"1"), }, .{ - .name = try pool.string(allocator, "payload"), + .name = .{ .index = .payload }, .ctype = payload_ctype, .alignas = AlignAs.fromAbiAlignment( Type.fromInterned(payload_type).abiAlignment(zcu), @@ -1574,14 +1663,14 @@ pub const Pool = struct { const target = &mod.resolved_target.result; var fields = [_]Info.Field{ .{ - .name = try pool.string(allocator, "error"), + .name = .{ .index = .@"error" }, .ctype = error_set_ctype, .alignas = AlignAs.fromAbiAlignment( Type.intAbiAlignment(error_set_bits, target.*), ), }, .{ - .name = try pool.string(allocator, "payload"), + .name = .{ .index = .payload }, .ctype = payload_ctype, .alignas = AlignAs.fromAbiAlignment(payload_type.abiAlignment(zcu)), }, @@ -1600,7 +1689,7 @@ pub const Pool = struct { if (kind.isForward()) return if (ty.hasRuntimeBitsIgnoreComptime(zcu)) fwd_decl else - .{ .index = .void }; + CType.void; const scratch_top = scratch.items.len; defer scratch.shrinkRetainingCapacity(scratch_top); try scratch.ensureUnusedCapacity( @@ -1625,9 +1714,9 @@ pub const Pool = struct { if (field_ctype.index == .void) continue; const field_name = if (loaded_struct.fieldName(ip, field_index) .unwrap()) |field_name| - try pool.string(allocator, ip.stringToSlice(field_name)) + try pool.string(allocator, field_name.toSlice(ip)) else - try pool.fmt(allocator, "f{d}", .{field_index}); + String.fromUnnamed(@intCast(field_index)); const field_alignas = AlignAs.fromAlignment(.{ .@"align" = loaded_struct.fieldAlign(ip, field_index), .abi = field_type.abiAlignment(zcu), @@ -1644,7 +1733,7 @@ pub const Pool = struct { scratch.items.len - scratch_top, @typeInfo(Field).Struct.fields.len, )); - if (fields_len == 0) return .{ .index = .void }; + if (fields_len == 0) return CType.void; try pool.ensureUnusedCapacity(allocator, 1); const extra_index = try pool.addHashedExtra(allocator, &hasher, Aggregate, .{ .fwd_decl = fwd_decl.index, @@ -1685,7 +1774,7 @@ pub const Pool = struct { if (field_ctype.index == .void) continue; const field_name = if (anon_struct_info.fieldName(ip, @intCast(field_index)) .unwrap()) |field_name| - try pool.string(allocator, ip.stringToSlice(field_name)) + try pool.string(allocator, field_name.toSlice(ip)) else try pool.fmt(allocator, "f{d}", .{field_index}); pool.addHashedExtraAssumeCapacityTo(scratch, &hasher, Field, .{ @@ -1700,7 +1789,7 @@ pub const Pool = struct { scratch.items.len - scratch_top, @typeInfo(Field).Struct.fields.len, )); - if (fields_len == 0) return .{ .index = .void }; + if (fields_len == 0) return CType.void; if (kind.isForward()) { try pool.ensureUnusedCapacity(allocator, 1); const extra_index = try pool.addHashedExtra( @@ -1739,7 +1828,7 @@ pub const Pool = struct { if (kind.isForward()) return if (ty.hasRuntimeBitsIgnoreComptime(zcu)) fwd_decl else - .{ .index = .void }; + CType.void; const loaded_tag = loaded_union.loadTagType(ip); const scratch_top = scratch.items.len; defer scratch.shrinkRetainingCapacity(scratch_top); @@ -1766,7 +1855,7 @@ pub const Pool = struct { if (field_ctype.index == .void) continue; const field_name = try pool.string( allocator, - ip.stringToSlice(loaded_tag.names.get(ip)[field_index]), + loaded_tag.names.get(ip)[field_index].toSlice(ip), ); const field_alignas = AlignAs.fromAlignment(.{ .@"align" = loaded_union.fieldAlign(ip, @intCast(field_index)), @@ -1786,7 +1875,7 @@ pub const Pool = struct { @typeInfo(Field).Struct.fields.len, )); if (!has_tag) { - if (fields_len == 0) return .{ .index = .void }; + if (fields_len == 0) return CType.void; try pool.ensureUnusedCapacity(allocator, 1); const extra_index = try pool.addHashedExtra( allocator, @@ -1813,7 +1902,7 @@ pub const Pool = struct { ); if (tag_ctype.index != .void) { struct_fields[struct_fields_len] = .{ - .name = try pool.string(allocator, "tag"), + .name = .{ .index = .tag }, .ctype = tag_ctype, .alignas = AlignAs.fromAbiAlignment(tag_type.abiAlignment(zcu)), }; @@ -1846,14 +1935,14 @@ pub const Pool = struct { }; if (payload_ctype.index != .void) { struct_fields[struct_fields_len] = .{ - .name = try pool.string(allocator, "payload"), + .name = .{ .index = .payload }, .ctype = payload_ctype, .alignas = AlignAs.fromAbiAlignment(payload_align), }; struct_fields_len += 1; } } - if (struct_fields_len == 0) return .{ .index = .void }; + if (struct_fields_len == 0) return CType.void; sortFields(struct_fields[0..struct_fields_len]); return pool.getAggregate(allocator, .{ .tag = .@"struct", @@ -1867,7 +1956,7 @@ pub const Pool = struct { }, mod, kind), } }, - .opaque_type => return .{ .index = .void }, + .opaque_type => return CType.void, .enum_type => return pool.fromType( allocator, scratch, @@ -1876,7 +1965,7 @@ pub const Pool = struct { mod, kind, ), - .func_type => |func_info| if (func_info.is_generic) return .{ .index = .void } else { + .func_type => |func_info| if (func_info.is_generic) return CType.void else { const scratch_top = scratch.items.len; defer scratch.shrinkRetainingCapacity(scratch_top); try scratch.ensureUnusedCapacity(allocator, func_info.param_types.len); @@ -1890,7 +1979,7 @@ pub const Pool = struct { zcu, mod, kind.asParameter(), - ) else .{ .index = .void }; + ) else CType.void; for (0..func_info.param_types.len) |param_index| { const param_type = Type.fromInterned( func_info.param_types.get(ip)[param_index], @@ -2024,7 +2113,10 @@ pub const Pool = struct { }); for (0..fields.len) |field_index| { const field = fields.at(field_index, source_pool); - const field_name = try pool.string(allocator, field.name.slice(source_pool)); + const field_name = if (field.name.toPoolSlice(source_pool)) |slice| + try pool.string(allocator, slice) + else + field.name; pool.addExtraAssumeCapacity(Field, .{ .name = field_name.index, .ctype = pool_adapter.copy(field.ctype).index, @@ -2054,7 +2146,10 @@ pub const Pool = struct { }); for (0..aggregate_info.fields.len) |field_index| { const field = aggregate_info.fields.at(field_index, source_pool); - const field_name = try pool.string(allocator, field.name.slice(source_pool)); + const field_name = if (field.name.toPoolSlice(source_pool)) |slice| + try pool.string(allocator, slice) + else + field.name; pool.addExtraAssumeCapacity(Field, .{ .name = field_name.index, .ctype = pool_adapter.copy(field.ctype).index, @@ -2082,8 +2177,8 @@ pub const Pool = struct { return .{ ctype, gop.found_existing }; } - pub fn string(pool: *Pool, allocator: std.mem.Allocator, str: []const u8) !String { - try pool.string_bytes.appendSlice(allocator, str); + pub fn string(pool: *Pool, allocator: std.mem.Allocator, slice: []const u8) !String { + try pool.string_bytes.appendSlice(allocator, slice); return pool.trailingString(allocator); } @@ -2111,12 +2206,15 @@ pub const Pool = struct { fn updateExtra(hasher: *Hasher, comptime Extra: type, extra: Extra, pool: *const Pool) void { inline for (@typeInfo(Extra).Struct.fields) |field| { const value = @field(extra, field.name); - hasher.update(switch (field.type) { + switch (field.type) { Pool.Tag, String, CType => unreachable, - CType.Index => (CType{ .index = value }).hash(pool), - String.Index => (String{ .index = value }).slice(pool), - else => value, - }); + CType.Index => hasher.update((CType{ .index = value }).hash(pool)), + String.Index => if ((String{ .index = value }).toPoolSlice(pool)) |slice| + hasher.update(slice) + else + hasher.update(@intFromEnum(value)), + else => hasher.update(value), + } } } fn update(hasher: *Hasher, data: anytype) void { @@ -2231,30 +2329,30 @@ pub const Pool = struct { } fn trailingString(pool: *Pool, allocator: std.mem.Allocator) !String { - const StringAdapter = struct { - pool: *const Pool, - pub fn hash(_: @This(), slice: []const u8) Map.Hash { - return @truncate(Hasher.Impl.hash(1, slice)); - } - pub fn eql(string_adapter: @This(), lhs_slice: []const u8, _: void, rhs_index: usize) bool { - const rhs_string: String = .{ .index = @enumFromInt(rhs_index) }; - const rhs_slice = rhs_string.slice(string_adapter.pool); - return std.mem.eql(u8, lhs_slice, rhs_slice); - } - }; + const start = pool.string_indices.getLast(); + const slice: []const u8 = pool.string_bytes.items[start..]; + if (slice.len >= 2 and slice[0] == 'f' and switch (slice[1]) { + '0' => slice.len == 2, + '1'...'9' => true, + else => false, + }) if (std.fmt.parseInt(u31, slice[1..], 10)) |unnamed| { + pool.string_bytes.shrinkRetainingCapacity(start); + return String.fromUnnamed(unnamed); + } else |_| {}; + if (std.meta.stringToEnum(String.Index, slice)) |index| { + pool.string_bytes.shrinkRetainingCapacity(start); + return .{ .index = index }; + } + try pool.string_map.ensureUnusedCapacity(allocator, 1); try pool.string_indices.ensureUnusedCapacity(allocator, 1); - const start = pool.string_indices.getLast(); - const gop = pool.string_map.getOrPutAssumeCapacityAdapted( - @as([]const u8, pool.string_bytes.items[start..]), - StringAdapter{ .pool = pool }, - ); + const gop = pool.string_map.getOrPutAssumeCapacityAdapted(slice, String.Adapter{ .pool = pool }); if (gop.found_existing) pool.string_bytes.shrinkRetainingCapacity(start) else pool.string_indices.appendAssumeCapacity(@intCast(pool.string_bytes.items.len)); - return .{ .index = @enumFromInt(gop.index) }; + return String.fromPoolIndex(gop.index); } const Item = struct { diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 7419e778a1..db0eaa3ce5 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1011,7 +1011,7 @@ pub const Object = struct { llvm_errors[0] = try o.builder.undefConst(llvm_slice_ty); for (llvm_errors[1..], error_name_list[1..]) |*llvm_error, name| { - const name_string = try o.builder.stringNull(mod.intern_pool.stringToSlice(name)); + const name_string = try o.builder.stringNull(name.toSlice(&mod.intern_pool)); const name_init = try o.builder.stringConst(name_string); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); @@ -1086,7 +1086,7 @@ pub const Object = struct { for (object.extern_collisions.keys()) |decl_index| { const global = object.decl_map.get(decl_index) orelse continue; // Same logic as below but for externs instead of exports. - const decl_name = object.builder.strtabStringIfExists(mod.intern_pool.stringToSlice(mod.declPtr(decl_index).name)) orelse continue; + const decl_name = object.builder.strtabStringIfExists(mod.declPtr(decl_index).name.toSlice(&mod.intern_pool)) orelse continue; const other_global = object.builder.getGlobal(decl_name) orelse continue; if (other_global.toConst().getBase(&object.builder) == global.toConst().getBase(&object.builder)) continue; @@ -1116,7 +1116,7 @@ pub const Object = struct { for (export_list) |exp| { // Detect if the LLVM global has already been created as an extern. In such // case, we need to replace all uses of it with this exported global. - const exp_name = object.builder.strtabStringIfExists(mod.intern_pool.stringToSlice(exp.opts.name)) orelse continue; + const exp_name = object.builder.strtabStringIfExists(exp.opts.name.toSlice(&mod.intern_pool)) orelse continue; const other_global = object.builder.getGlobal(exp_name) orelse continue; if (other_global.toConst().getBase(&object.builder) == global_base) continue; @@ -1442,7 +1442,7 @@ pub const Object = struct { } }, &o.builder); } - if (ip.stringToSliceUnwrap(decl.@"linksection")) |section| + if (decl.@"linksection".toSlice(ip)) |section| function_index.setSection(try o.builder.string(section), &o.builder); var deinit_wip = true; @@ -1662,7 +1662,7 @@ pub const Object = struct { const subprogram = try o.builder.debugSubprogram( file, - try o.builder.metadataString(ip.stringToSlice(decl.name)), + try o.builder.metadataString(decl.name.toSlice(ip)), try o.builder.metadataStringFromStrtabString(function_index.name(&o.builder)), line_number, line_number + func.lbrace_line, @@ -1752,6 +1752,7 @@ pub const Object = struct { .value => |val| return updateExportedValue(self, mod, val, exports), }; const gpa = mod.gpa; + const ip = &mod.intern_pool; // If the module does not already have the function, we ignore this function call // because we call `updateExports` at the end of `updateFunc` and `updateDecl`. const global_index = self.decl_map.get(decl_index) orelse return; @@ -1759,17 +1760,14 @@ pub const Object = struct { const comp = mod.comp; if (decl.isExtern(mod)) { const decl_name = decl_name: { - const decl_name = mod.intern_pool.stringToSlice(decl.name); - if (mod.getTarget().isWasm() and decl.val.typeOf(mod).zigTypeTag(mod) == .Fn) { - if (mod.intern_pool.stringToSliceUnwrap(decl.getOwnedExternFunc(mod).?.lib_name)) |lib_name| { + if (decl.getOwnedExternFunc(mod).?.lib_name.toSlice(ip)) |lib_name| { if (!std.mem.eql(u8, lib_name, "c")) { - break :decl_name try self.builder.strtabStringFmt("{s}|{s}", .{ decl_name, lib_name }); + break :decl_name try self.builder.strtabStringFmt("{}|{s}", .{ decl.name.fmt(ip), lib_name }); } } } - - break :decl_name try self.builder.strtabString(decl_name); + break :decl_name try self.builder.strtabString(decl.name.toSlice(ip)); }; if (self.builder.getGlobal(decl_name)) |other_global| { @@ -1792,9 +1790,7 @@ pub const Object = struct { if (decl_var.is_weak_linkage) global_index.setLinkage(.extern_weak, &self.builder); } } else if (exports.len != 0) { - const main_exp_name = try self.builder.strtabString( - mod.intern_pool.stringToSlice(exports[0].opts.name), - ); + const main_exp_name = try self.builder.strtabString(exports[0].opts.name.toSlice(ip)); try global_index.rename(main_exp_name, &self.builder); if (decl.val.getVariable(mod)) |decl_var| if (decl_var.is_threadlocal) @@ -1803,9 +1799,7 @@ pub const Object = struct { return updateExportedGlobal(self, mod, global_index, exports); } else { - const fqn = try self.builder.strtabString( - mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)), - ); + const fqn = try self.builder.strtabString((try decl.fullyQualifiedName(mod)).toSlice(ip)); try global_index.rename(fqn, &self.builder); global_index.setLinkage(.internal, &self.builder); if (comp.config.dll_export_fns) @@ -1832,9 +1826,8 @@ pub const Object = struct { exports: []const *Module.Export, ) link.File.UpdateExportsError!void { const gpa = mod.gpa; - const main_exp_name = try o.builder.strtabString( - mod.intern_pool.stringToSlice(exports[0].opts.name), - ); + const ip = &mod.intern_pool; + const main_exp_name = try o.builder.strtabString(exports[0].opts.name.toSlice(ip)); const global_index = i: { const gop = try o.anon_decl_map.getOrPut(gpa, exported_value); if (gop.found_existing) { @@ -1845,7 +1838,7 @@ pub const Object = struct { const llvm_addr_space = toLlvmAddressSpace(.generic, o.target); const variable_index = try o.builder.addVariable( main_exp_name, - try o.lowerType(Type.fromInterned(mod.intern_pool.typeOf(exported_value))), + try o.lowerType(Type.fromInterned(ip.typeOf(exported_value))), llvm_addr_space, ); const global_index = variable_index.ptrConst(&o.builder).global; @@ -1867,8 +1860,9 @@ pub const Object = struct { global_index: Builder.Global.Index, exports: []const *Module.Export, ) link.File.UpdateExportsError!void { - global_index.setUnnamedAddr(.default, &o.builder); const comp = mod.comp; + const ip = &mod.intern_pool; + global_index.setUnnamedAddr(.default, &o.builder); if (comp.config.dll_export_fns) global_index.setDllStorageClass(.dllexport, &o.builder); global_index.setLinkage(switch (exports[0].opts.linkage) { @@ -1882,7 +1876,7 @@ pub const Object = struct { .hidden => .hidden, .protected => .protected, }, &o.builder); - if (mod.intern_pool.stringToSliceUnwrap(exports[0].opts.section)) |section| + if (exports[0].opts.section.toSlice(ip)) |section| switch (global_index.ptrConst(&o.builder).kind) { .variable => |impl_index| impl_index.setSection( try o.builder.string(section), @@ -1900,7 +1894,7 @@ pub const Object = struct { // Until then we iterate over existing aliases and make them point // to the correct decl, or otherwise add a new alias. Old aliases are leaked. for (exports[1..]) |exp| { - const exp_name = try o.builder.strtabString(mod.intern_pool.stringToSlice(exp.opts.name)); + const exp_name = try o.builder.strtabString(exp.opts.name.toSlice(ip)); if (o.builder.getGlobal(exp_name)) |global| { switch (global.ptrConst(&o.builder).kind) { .alias => |alias| { @@ -2013,7 +2007,7 @@ pub const Object = struct { std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst(); enumerators[i] = try o.builder.debugEnumerator( - try o.builder.metadataString(ip.stringToSlice(field_name_ip)), + try o.builder.metadataString(field_name_ip.toSlice(ip)), int_info.signedness == .unsigned, int_info.bits, bigint, @@ -2473,7 +2467,7 @@ pub const Object = struct { offset = field_offset + field_size; const field_name = if (tuple.names.len != 0) - ip.stringToSlice(tuple.names.get(ip)[i]) + tuple.names.get(ip)[i].toSlice(ip) else try std.fmt.allocPrintZ(gpa, "{d}", .{i}); defer if (tuple.names.len == 0) gpa.free(field_name); @@ -2557,10 +2551,10 @@ pub const Object = struct { const field_offset = ty.structFieldOffset(field_index, mod); const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse - try ip.getOrPutStringFmt(gpa, "{d}", .{field_index}); + try ip.getOrPutStringFmt(gpa, "{d}", .{field_index}, .no_embedded_nulls); fields.appendAssumeCapacity(try o.builder.debugMemberType( - try o.builder.metadataString(ip.stringToSlice(field_name)), + try o.builder.metadataString(field_name.toSlice(ip)), .none, // File debug_fwd_ref, 0, // Line @@ -2655,7 +2649,7 @@ pub const Object = struct { const field_name = tag_type.names.get(ip)[field_index]; fields.appendAssumeCapacity(try o.builder.debugMemberType( - try o.builder.metadataString(ip.stringToSlice(field_name)), + try o.builder.metadataString(field_name.toSlice(ip)), .none, // File debug_union_fwd_ref, 0, // Line @@ -2827,7 +2821,7 @@ pub const Object = struct { const mod = o.module; const decl = mod.declPtr(decl_index); return o.builder.debugStructType( - try o.builder.metadataString(mod.intern_pool.stringToSlice(decl.name)), // TODO use fully qualified name + try o.builder.metadataString(decl.name.toSlice(&mod.intern_pool)), // TODO use fully qualified name try o.getDebugFile(mod.namespacePtr(decl.src_namespace).file_scope), try o.namespaceToDebugScope(decl.src_namespace), decl.src_line + 1, @@ -2844,11 +2838,11 @@ pub const Object = struct { const std_mod = mod.std_mod; const std_file = (mod.importPkg(std_mod) catch unreachable).file; - const builtin_str = try mod.intern_pool.getOrPutString(mod.gpa, "builtin"); + const builtin_str = try mod.intern_pool.getOrPutString(mod.gpa, "builtin", .no_embedded_nulls); const std_namespace = mod.namespacePtr(mod.declPtr(std_file.root_decl.unwrap().?).src_namespace); const builtin_decl = std_namespace.decls.getKeyAdapted(builtin_str, Module.DeclAdapter{ .zcu = mod }).?; - const stack_trace_str = try mod.intern_pool.getOrPutString(mod.gpa, "StackTrace"); + const stack_trace_str = try mod.intern_pool.getOrPutString(mod.gpa, "StackTrace", .no_embedded_nulls); // buffer is only used for int_type, `builtin` is a struct. const builtin_ty = mod.declPtr(builtin_decl).val.toType(); const builtin_namespace = mod.namespacePtrUnwrap(builtin_ty.getNamespaceIndex(mod)).?; @@ -2892,10 +2886,10 @@ pub const Object = struct { const is_extern = decl.isExtern(zcu); const function_index = try o.builder.addFunction( try o.lowerType(zig_fn_type), - try o.builder.strtabString(ip.stringToSlice(if (is_extern) + try o.builder.strtabString((if (is_extern) decl.name else - try decl.fullyQualifiedName(zcu))), + try decl.fullyQualifiedName(zcu)).toSlice(ip)), toLlvmAddressSpace(decl.@"addrspace", target), ); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; @@ -2910,9 +2904,9 @@ pub const Object = struct { if (target.isWasm()) { try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("wasm-import-name"), - .value = try o.builder.string(ip.stringToSlice(decl.name)), + .value = try o.builder.string(decl.name.toSlice(ip)), } }, &o.builder); - if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(zcu).?.lib_name)) |lib_name| { + if (decl.getOwnedExternFunc(zcu).?.lib_name.toSlice(ip)) |lib_name| { if (!std.mem.eql(u8, lib_name, "c")) try attributes.addFnAttr(.{ .string = .{ .kind = try o.builder.string("wasm-import-module"), .value = try o.builder.string(lib_name), @@ -3108,9 +3102,10 @@ pub const Object = struct { const is_extern = decl.isExtern(mod); const variable_index = try o.builder.addVariable( - try o.builder.strtabString(mod.intern_pool.stringToSlice( - if (is_extern) decl.name else try decl.fullyQualifiedName(mod), - )), + try o.builder.strtabString((if (is_extern) + decl.name + else + try decl.fullyQualifiedName(mod)).toSlice(&mod.intern_pool)), try o.lowerType(decl.typeOf(mod)), toLlvmGlobalAddressSpace(decl.@"addrspace", mod.getTarget()), ); @@ -3258,7 +3253,7 @@ pub const Object = struct { }; }, .array_type => |array_type| o.builder.arrayType( - array_type.len + @intFromBool(array_type.sentinel != .none), + array_type.lenIncludingSentinel(), try o.lowerType(Type.fromInterned(array_type.child)), ), .vector_type => |vector_type| o.builder.vectorType( @@ -3335,9 +3330,7 @@ pub const Object = struct { return int_ty; } - const name = try o.builder.string(ip.stringToSlice( - try mod.declPtr(struct_type.decl.unwrap().?).fullyQualifiedName(mod), - )); + const fqn = try mod.declPtr(struct_type.decl.unwrap().?).fullyQualifiedName(mod); var llvm_field_types = std.ArrayListUnmanaged(Builder.Type){}; defer llvm_field_types.deinit(o.gpa); @@ -3402,7 +3395,7 @@ pub const Object = struct { ); } - const ty = try o.builder.opaqueType(name); + const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3491,9 +3484,7 @@ pub const Object = struct { return enum_tag_ty; } - const name = try o.builder.string(ip.stringToSlice( - try mod.declPtr(union_obj.decl).fullyQualifiedName(mod), - )); + const fqn = try mod.declPtr(union_obj.decl).fullyQualifiedName(mod); const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]); const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty); @@ -3513,7 +3504,7 @@ pub const Object = struct { }; if (layout.tag_size == 0) { - const ty = try o.builder.opaqueType(name); + const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3541,7 +3532,7 @@ pub const Object = struct { llvm_fields_len += 1; } - const ty = try o.builder.opaqueType(name); + const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); try o.type_map.put(o.gpa, t.toIntern(), ty); o.builder.namedTypeSetBody( @@ -3554,8 +3545,8 @@ pub const Object = struct { const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); if (!gop.found_existing) { const decl = mod.declPtr(ip.loadOpaqueType(t.toIntern()).decl); - const name = try o.builder.string(ip.stringToSlice(try decl.fullyQualifiedName(mod))); - gop.value_ptr.* = try o.builder.opaqueType(name); + const fqn = try decl.fullyQualifiedName(mod); + gop.value_ptr.* = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip))); } return gop.value_ptr.*; }, @@ -3859,7 +3850,9 @@ pub const Object = struct { }, .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { .array_type => |array_type| switch (aggregate.storage) { - .bytes => |bytes| try o.builder.stringConst(try o.builder.string(bytes)), + .bytes => |bytes| try o.builder.stringConst(try o.builder.string( + bytes.toSlice(array_type.lenIncludingSentinel(), ip), + )), .elems => |elems| { const array_ty = try o.lowerType(ty); const elem_ty = array_ty.childType(&o.builder); @@ -3892,8 +3885,7 @@ pub const Object = struct { }, .repeated_elem => |elem| { const len: usize = @intCast(array_type.len); - const len_including_sentinel: usize = - @intCast(len + @intFromBool(array_type.sentinel != .none)); + const len_including_sentinel: usize = @intCast(array_type.lenIncludingSentinel()); const array_ty = try o.lowerType(ty); const elem_ty = array_ty.childType(&o.builder); @@ -3942,7 +3934,7 @@ pub const Object = struct { defer allocator.free(vals); switch (aggregate.storage) { - .bytes => |bytes| for (vals, bytes) |*result_val, byte| { + .bytes => |bytes| for (vals, bytes.toSlice(vector_type.len, ip)) |*result_val, byte| { result_val.* = try o.builder.intConst(.i8, byte); }, .elems => |elems| for (vals, elems) |*result_val, elem| { @@ -4633,7 +4625,7 @@ pub const Object = struct { defer wip_switch.finish(&wip); for (0..enum_type.names.len) |field_index| { - const name = try o.builder.stringNull(ip.stringToSlice(enum_type.names.get(ip)[field_index])); + const name = try o.builder.stringNull(enum_type.names.get(ip)[field_index].toSlice(ip)); const name_init = try o.builder.stringConst(name); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); @@ -4693,6 +4685,7 @@ pub const DeclGen = struct { fn genDecl(dg: *DeclGen) !void { const o = dg.object; const zcu = o.module; + const ip = &zcu.intern_pool; const decl = dg.decl; const decl_index = dg.decl_index; assert(decl.has_tv); @@ -4705,7 +4698,7 @@ pub const DeclGen = struct { decl.getAlignment(zcu).toLlvm(), &o.builder, ); - if (zcu.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |section| + if (decl.@"linksection".toSlice(ip)) |section| variable_index.setSection(try o.builder.string(section), &o.builder); assert(decl.has_tv); const init_val = if (decl.val.getVariable(zcu)) |decl_var| decl_var.init else init_val: { @@ -4728,7 +4721,7 @@ pub const DeclGen = struct { const debug_file = try o.getDebugFile(namespace.file_scope); const debug_global_var = try o.builder.debugGlobalVar( - try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)), // Name + try o.builder.metadataString(decl.name.toSlice(ip)), // Name try o.builder.metadataStringFromStrtabString(variable_index.name(&o.builder)), // Linkage name debug_file, // File debug_file, // Scope @@ -5156,8 +5149,8 @@ pub const FuncGen = struct { self.scope = try o.builder.debugSubprogram( self.file, - try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)), - try o.builder.metadataString(zcu.intern_pool.stringToSlice(fqn)), + try o.builder.metadataString(decl.name.toSlice(&zcu.intern_pool)), + try o.builder.metadataString(fqn.toSlice(&zcu.intern_pool)), line_number, line_number + func.lbrace_line, try o.lowerDebugType(fn_ty), diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 2570343763..53ec59d531 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -1028,39 +1028,30 @@ const DeclGen = struct { inline .array_type, .vector_type => |array_type, tag| { const elem_ty = Type.fromInterned(array_type.child); - const constituents = try self.gpa.alloc(IdRef, @as(u32, @intCast(ty.arrayLenIncludingSentinel(mod)))); + const constituents = try self.gpa.alloc(IdRef, @intCast(ty.arrayLenIncludingSentinel(mod))); defer self.gpa.free(constituents); switch (aggregate.storage) { .bytes => |bytes| { // TODO: This is really space inefficient, perhaps there is a better // way to do it? - for (bytes, 0..) |byte, i| { - constituents[i] = try self.constInt(elem_ty, byte, .indirect); + for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| { + constituent.* = try self.constInt(elem_ty, byte, .indirect); } }, .elems => |elems| { - for (0..@as(usize, @intCast(array_type.len))) |i| { - constituents[i] = try self.constant(elem_ty, Value.fromInterned(elems[i]), .indirect); + for (constituents, elems) |*constituent, elem| { + constituent.* = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); } }, .repeated_elem => |elem| { - const val_id = try self.constant(elem_ty, Value.fromInterned(elem), .indirect); - for (0..@as(usize, @intCast(array_type.len))) |i| { - constituents[i] = val_id; - } + @memset(constituents, try self.constant(elem_ty, Value.fromInterned(elem), .indirect)); }, } switch (tag) { - inline .array_type => { - if (array_type.sentinel != .none) { - const sentinel = Value.fromInterned(array_type.sentinel); - constituents[constituents.len - 1] = try self.constant(elem_ty, sentinel, .indirect); - } - return self.constructArray(ty, constituents); - }, - inline .vector_type => return self.constructVector(ty, constituents), + .array_type => return self.constructArray(ty, constituents), + .vector_type => return self.constructVector(ty, constituents), else => unreachable, } }, @@ -1683,9 +1674,9 @@ const DeclGen = struct { } const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse - try ip.getOrPutStringFmt(mod.gpa, "{d}", .{field_index}); + try ip.getOrPutStringFmt(mod.gpa, "{d}", .{field_index}, .no_embedded_nulls); try member_types.append(try self.resolveType(field_ty, .indirect)); - try member_names.append(ip.stringToSlice(field_name)); + try member_names.append(field_name.toSlice(ip)); } const result_id = try self.spv.structType(member_types.items, member_names.items); @@ -2123,12 +2114,12 @@ const DeclGen = struct { // Append the actual code into the functions section. try self.spv.addFunction(spv_decl_index, self.func); - const fqn = ip.stringToSlice(try decl.fullyQualifiedName(self.module)); - try self.spv.debugName(result_id, fqn); + const fqn = try decl.fullyQualifiedName(self.module); + try self.spv.debugName(result_id, fqn.toSlice(ip)); // Temporarily generate a test kernel declaration if this is a test function. if (self.module.test_functions.contains(self.decl_index)) { - try self.generateTestEntryPoint(fqn, spv_decl_index); + try self.generateTestEntryPoint(fqn.toSlice(ip), spv_decl_index); } }, .global => { @@ -2152,8 +2143,8 @@ const DeclGen = struct { .storage_class = final_storage_class, }); - const fqn = ip.stringToSlice(try decl.fullyQualifiedName(self.module)); - try self.spv.debugName(result_id, fqn); + const fqn = try decl.fullyQualifiedName(self.module); + try self.spv.debugName(result_id, fqn.toSlice(ip)); try self.spv.declareDeclDeps(spv_decl_index, &.{}); }, .invocation_global => { @@ -2197,8 +2188,8 @@ const DeclGen = struct { try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {}); try self.spv.addFunction(spv_decl_index, self.func); - const fqn = ip.stringToSlice(try decl.fullyQualifiedName(self.module)); - try self.spv.debugNameFmt(initializer_id, "initializer of {s}", .{fqn}); + const fqn = try decl.fullyQualifiedName(self.module); + try self.spv.debugNameFmt(initializer_id, "initializer of {}", .{fqn.fmt(ip)}); try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{ .id_result_type = ptr_ty_id, diff --git a/src/link/Coff.zig b/src/link/Coff.zig index aaf840e02c..413294489a 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1176,9 +1176,9 @@ pub fn lowerUnnamedConst(self: *Coff, val: Value, decl_index: InternPool.DeclInd gop.value_ptr.* = .{}; } const unnamed_consts = gop.value_ptr; - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); const index = unnamed_consts.items.len; - const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); + const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); defer gpa.free(sym_name); const ty = val.typeOf(mod); const atom_index = switch (try self.lowerConst(sym_name, val, ty.abiAlignment(mod), self.rdata_section_index.?, decl.srcLoc(mod))) { @@ -1257,8 +1257,8 @@ pub fn updateDecl( if (decl.isExtern(mod)) { // TODO make this part of getGlobalSymbol const variable = decl.getOwnedVariable(mod).?; - const name = mod.intern_pool.stringToSlice(decl.name); - const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); + const name = decl.name.toSlice(&mod.intern_pool); + const lib_name = variable.lib_name.toSlice(&mod.intern_pool); const global_index = try self.getGlobalSymbol(name, lib_name); try self.need_got_table.put(gpa, global_index, {}); return; @@ -1425,9 +1425,9 @@ fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, com const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); + log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); const required_alignment: u32 = @intCast(decl.getAlignment(mod).toByteUnits() orelse 0); const decl_metadata = self.decls.get(decl_index).?; @@ -1439,7 +1439,7 @@ fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, com if (atom.size != 0) { const sym = atom.getSymbolPtr(self); - try self.setSymbolName(sym, decl_name); + try self.setSymbolName(sym, decl_name.toSlice(&mod.intern_pool)); sym.section_number = @as(coff.SectionNumber, @enumFromInt(sect_index + 1)); sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; @@ -1447,7 +1447,7 @@ fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, com const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment); if (need_realloc) { const vaddr = try self.growAtom(atom_index, code_len, required_alignment); - log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, sym.value, vaddr }); + log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), sym.value, vaddr }); log.debug(" (required alignment 0x{x}", .{required_alignment}); if (vaddr != sym.value) { @@ -1463,13 +1463,13 @@ fn updateDeclCode(self: *Coff, decl_index: InternPool.DeclIndex, code: []u8, com self.getAtomPtr(atom_index).size = code_len; } else { const sym = atom.getSymbolPtr(self); - try self.setSymbolName(sym, decl_name); + try self.setSymbolName(sym, decl_name.toSlice(&mod.intern_pool)); sym.section_number = @as(coff.SectionNumber, @enumFromInt(sect_index + 1)); sym.type = .{ .complex_type = complex_type, .base_type = .NULL }; const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment); errdefer self.freeAtom(atom_index); - log.debug("allocated atom for {s} at 0x{x}", .{ decl_name, vaddr }); + log.debug("allocated atom for {} at 0x{x}", .{ decl_name.fmt(&mod.intern_pool), vaddr }); self.getAtomPtr(atom_index).size = code_len; sym.value = vaddr; @@ -1534,20 +1534,18 @@ pub fn updateExports( else => std.builtin.CallingConvention.C, }; const decl_cc = exported_decl.typeOf(mod).fnCallingConvention(mod); - if (decl_cc == .C and ip.stringEqlSlice(exp.opts.name, "main") and - comp.config.link_libc) - { + if (decl_cc == .C and exp.opts.name.eqlSlice("main", ip) and comp.config.link_libc) { mod.stage1_flags.have_c_main = true; } else if (decl_cc == winapi_cc and target.os.tag == .windows) { - if (ip.stringEqlSlice(exp.opts.name, "WinMain")) { + if (exp.opts.name.eqlSlice("WinMain", ip)) { mod.stage1_flags.have_winmain = true; - } else if (ip.stringEqlSlice(exp.opts.name, "wWinMain")) { + } else if (exp.opts.name.eqlSlice("wWinMain", ip)) { mod.stage1_flags.have_wwinmain = true; - } else if (ip.stringEqlSlice(exp.opts.name, "WinMainCRTStartup")) { + } else if (exp.opts.name.eqlSlice("WinMainCRTStartup", ip)) { mod.stage1_flags.have_winmain_crt_startup = true; - } else if (ip.stringEqlSlice(exp.opts.name, "wWinMainCRTStartup")) { + } else if (exp.opts.name.eqlSlice("wWinMainCRTStartup", ip)) { mod.stage1_flags.have_wwinmain_crt_startup = true; - } else if (ip.stringEqlSlice(exp.opts.name, "DllMainCRTStartup")) { + } else if (exp.opts.name.eqlSlice("DllMainCRTStartup", ip)) { mod.stage1_flags.have_dllmain_crt_startup = true; } } @@ -1585,7 +1583,7 @@ pub fn updateExports( for (exports) |exp| { log.debug("adding new export '{}'", .{exp.opts.name.fmt(&mod.intern_pool)}); - if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section_name| { + if (exp.opts.section.toSlice(&mod.intern_pool)) |section_name| { if (!mem.eql(u8, section_name, ".text")) { try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( gpa, @@ -1607,7 +1605,7 @@ pub fn updateExports( continue; } - const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); + const exp_name = exp.opts.name.toSlice(&mod.intern_pool); const sym_index = metadata.getExport(self, exp_name) orelse blk: { const sym_index = if (self.getGlobalIndex(exp_name)) |global_index| ind: { const global = self.globals.items[global_index]; @@ -1646,18 +1644,18 @@ pub fn updateExports( pub fn deleteDeclExport( self: *Coff, decl_index: InternPool.DeclIndex, - name_ip: InternPool.NullTerminatedString, + name: InternPool.NullTerminatedString, ) void { if (self.llvm_object) |_| return; const metadata = self.decls.getPtr(decl_index) orelse return; const mod = self.base.comp.module.?; - const name = mod.intern_pool.stringToSlice(name_ip); - const sym_index = metadata.getExportPtr(self, name) orelse return; + const name_slice = name.toSlice(&mod.intern_pool); + const sym_index = metadata.getExportPtr(self, name_slice) orelse return; const gpa = self.base.comp.gpa; const sym_loc = SymbolWithLoc{ .sym_index = sym_index.*, .file = null }; const sym = self.getSymbolPtr(sym_loc); - log.debug("deleting export '{s}'", .{name}); + log.debug("deleting export '{}'", .{name.fmt(&mod.intern_pool)}); assert(sym.storage_class == .EXTERNAL and sym.section_number != .UNDEFINED); sym.* = .{ .name = [_]u8{0} ** 8, @@ -1669,7 +1667,7 @@ pub fn deleteDeclExport( }; self.locals_free_list.append(gpa, sym_index.*) catch {}; - if (self.resolver.fetchRemove(name)) |entry| { + if (self.resolver.fetchRemove(name_slice)) |entry| { defer gpa.free(entry.key); self.globals_free_list.append(gpa, entry.value) catch {}; self.globals.items[entry.value] = .{ diff --git a/src/link/Dwarf.zig b/src/link/Dwarf.zig index 6a41bb3793..5f82c924c7 100644 --- a/src/link/Dwarf.zig +++ b/src/link/Dwarf.zig @@ -339,15 +339,14 @@ pub const DeclState = struct { struct_type.field_names.get(ip), struct_type.field_types.get(ip), struct_type.offsets.get(ip), - ) |field_name_ip, field_ty, field_off| { + ) |field_name, field_ty, field_off| { if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; - const field_name = ip.stringToSlice(field_name_ip); + const field_name_slice = field_name.toSlice(ip); // DW.AT.member - try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2); + try dbg_info_buffer.ensureUnusedCapacity(field_name_slice.len + 2); dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.struct_member)); // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(field_name); - dbg_info_buffer.appendAssumeCapacity(0); + dbg_info_buffer.appendSliceAssumeCapacity(field_name_slice[0 .. field_name_slice.len + 1]); // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.appendNTimes(0, 4); @@ -374,14 +373,13 @@ pub const DeclState = struct { try dbg_info_buffer.append(0); const enum_type = ip.loadEnumType(ty.ip_index); - for (enum_type.names.get(ip), 0..) |field_name_index, field_i| { - const field_name = ip.stringToSlice(field_name_index); + for (enum_type.names.get(ip), 0..) |field_name, field_i| { + const field_name_slice = field_name.toSlice(ip); // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(field_name.len + 2 + @sizeOf(u64)); + try dbg_info_buffer.ensureUnusedCapacity(field_name_slice.len + 2 + @sizeOf(u64)); dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant)); // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(field_name); - dbg_info_buffer.appendAssumeCapacity(0); + dbg_info_buffer.appendSliceAssumeCapacity(field_name_slice[0 .. field_name_slice.len + 1]); // DW.AT.const_value, DW.FORM.data8 const value: u64 = value: { if (enum_type.values.len == 0) break :value field_i; // auto-numbered @@ -443,11 +441,11 @@ pub const DeclState = struct { for (union_obj.field_types.get(ip), union_obj.loadTagType(ip).names.get(ip)) |field_ty, field_name| { if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; + const field_name_slice = field_name.toSlice(ip); // DW.AT.member try dbg_info_buffer.append(@intFromEnum(AbbrevCode.struct_member)); // DW.AT.name, DW.FORM.string - try dbg_info_buffer.appendSlice(ip.stringToSlice(field_name)); - try dbg_info_buffer.append(0); + try dbg_info_buffer.appendSlice(field_name_slice[0 .. field_name_slice.len + 1]); // DW.AT.type, DW.FORM.ref4 const index = dbg_info_buffer.items.len; try dbg_info_buffer.appendNTimes(0, 4); @@ -1155,8 +1153,8 @@ pub fn initDeclState(self: *Dwarf, mod: *Module, decl_index: InternPool.DeclInde dbg_line_buffer.appendAssumeCapacity(DW.LNS.copy); // .debug_info subprogram - const decl_name_slice = mod.intern_pool.stringToSlice(decl.name); - const decl_linkage_name_slice = mod.intern_pool.stringToSlice(decl_linkage_name); + const decl_name_slice = decl.name.toSlice(&mod.intern_pool); + const decl_linkage_name_slice = decl_linkage_name.toSlice(&mod.intern_pool); try dbg_info_buffer.ensureUnusedCapacity(1 + ptr_width_bytes + 4 + 4 + (decl_name_slice.len + 1) + (decl_linkage_name_slice.len + 1)); @@ -2866,15 +2864,14 @@ fn addDbgInfoErrorSetNames( // DW.AT.const_value, DW.FORM.data8 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), 0, target_endian); - for (error_names) |error_name_ip| { - const int = try mod.getErrorValue(error_name_ip); - const error_name = mod.intern_pool.stringToSlice(error_name_ip); + for (error_names) |error_name| { + const int = try mod.getErrorValue(error_name); + const error_name_slice = error_name.toSlice(&mod.intern_pool); // DW.AT.enumerator - try dbg_info_buffer.ensureUnusedCapacity(error_name.len + 2 + @sizeOf(u64)); + try dbg_info_buffer.ensureUnusedCapacity(error_name_slice.len + 2 + @sizeOf(u64)); dbg_info_buffer.appendAssumeCapacity(@intFromEnum(AbbrevCode.enum_variant)); // DW.AT.name, DW.FORM.string - dbg_info_buffer.appendSliceAssumeCapacity(error_name); - dbg_info_buffer.appendAssumeCapacity(0); + dbg_info_buffer.appendSliceAssumeCapacity(error_name_slice[0 .. error_name_slice.len + 1]); // DW.AT.const_value, DW.FORM.data8 mem.writeInt(u64, dbg_info_buffer.addManyAsArrayAssumeCapacity(8), int, target_endian); } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index c20a4b6afa..03451d140e 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1517,7 +1517,7 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void { if (self.base.isStatic()) { try argv.append("-static"); - } else if (self.base.isDynLib()) { + } else if (self.isEffectivelyDynLib()) { try argv.append("-shared"); } @@ -1997,7 +1997,7 @@ fn markImportsExports(self: *Elf) void { } if (file_ptr.index() == file_index) { global.flags.@"export" = true; - if (elf_file.base.isDynLib() and vis != .PROTECTED) { + if (elf_file.isEffectivelyDynLib() and vis != .PROTECTED) { global.flags.import = true; } } @@ -2005,7 +2005,7 @@ fn markImportsExports(self: *Elf) void { } }.mark; - if (!self.base.isDynLib()) { + if (!self.isEffectivelyDynLib()) { for (self.shared_objects.items) |index| { for (self.file(index).?.globals()) |global_index| { const global = self.symbol(global_index); @@ -2469,7 +2469,10 @@ fn linkWithLLD(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node) !voi } else { try argv.append("-static"); } - } else if (is_dyn_lib) { + } else if (switch (target.os.tag) { + else => is_dyn_lib, + .haiku => is_exe_or_dyn_lib, + }) { try argv.append("-shared"); } @@ -2925,7 +2928,7 @@ pub fn writeElfHeader(self: *Elf) !void { const output_mode = comp.config.output_mode; const link_mode = comp.config.link_mode; const elf_type: elf.ET = switch (output_mode) { - .Exe => if (comp.config.pie) .DYN else .EXEC, + .Exe => if (comp.config.pie or target.os.tag == .haiku) .DYN else .EXEC, .Obj => .REL, .Lib => switch (link_mode) { .static => @as(elf.ET, .REL), @@ -3114,7 +3117,7 @@ fn addLinkerDefinedSymbols(self: *Elf) !void { } } - if (self.getTarget().cpu.arch == .riscv64 and self.base.isDynLib()) { + if (self.getTarget().cpu.arch == .riscv64 and self.isEffectivelyDynLib()) { self.global_pointer_index = try linker_defined.addGlobal("__global_pointer$", self); } @@ -3420,7 +3423,7 @@ fn initSyntheticSections(self: *Elf) !void { }); } - if (self.base.isDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { + if (self.isEffectivelyDynLib() or self.shared_objects.items.len > 0 or comp.config.pie) { self.dynstrtab_section_index = try self.addSection(.{ .name = ".dynstr", .flags = elf.SHF_ALLOC, @@ -3657,7 +3660,7 @@ fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void { try self.dynamic.addNeeded(shared_object, self); } - if (self.base.isDynLib()) { + if (self.isEffectivelyDynLib()) { if (self.soname) |soname| { try self.dynamic.setSoname(soname, self); } @@ -5246,6 +5249,16 @@ const CsuObjects = struct { } }; +/// If a target compiles other output modes as dynamic libraries, +/// this function returns true for those too. +pub fn isEffectivelyDynLib(self: Elf) bool { + if (self.base.isDynLib()) return true; + return switch (self.getTarget().os.tag) { + .haiku => self.base.isExe(), + else => false, + }; +} + pub fn isZigSection(self: Elf, shndx: u32) bool { inline for (&[_]?u32{ self.zig_text_section_index, diff --git a/src/link/Elf/Atom.zig b/src/link/Elf/Atom.zig index 3db1182696..b5ceeb24b8 100644 --- a/src/link/Elf/Atom.zig +++ b/src/link/Elf/Atom.zig @@ -1054,7 +1054,7 @@ const x86_64 = struct { it: *RelocsIterator, ) !void { const is_static = elf_file.base.isStatic(); - const is_dyn_lib = elf_file.base.isDynLib(); + const is_dyn_lib = elf_file.isEffectivelyDynLib(); const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; @@ -1599,7 +1599,7 @@ const aarch64 = struct { _ = it; const r_type: elf.R_AARCH64 = @enumFromInt(rel.r_type()); - const is_dyn_lib = elf_file.base.isDynLib(); + const is_dyn_lib = elf_file.isEffectivelyDynLib(); switch (r_type) { .ABS64 => { diff --git a/src/link/Elf/Object.zig b/src/link/Elf/Object.zig index cc135f2f97..d483540aa6 100644 --- a/src/link/Elf/Object.zig +++ b/src/link/Elf/Object.zig @@ -568,7 +568,7 @@ pub fn claimUnresolved(self: *Object, elf_file: *Elf) void { } const is_import = blk: { - if (!elf_file.base.isDynLib()) break :blk false; + if (!elf_file.isEffectivelyDynLib()) break :blk false; const vis = @as(elf.STV, @enumFromInt(esym.st_other)); if (vis == .HIDDEN) break :blk false; break :blk true; diff --git a/src/link/Elf/Symbol.zig b/src/link/Elf/Symbol.zig index 0ddf19cd05..9db17f2f4f 100644 --- a/src/link/Elf/Symbol.zig +++ b/src/link/Elf/Symbol.zig @@ -105,6 +105,29 @@ pub fn address(symbol: Symbol, opts: struct { plt: bool = true }, elf_file: *Elf return symbol.pltAddress(elf_file); } if (symbol.atom(elf_file)) |atom_ptr| { + if (!atom_ptr.flags.alive) { + if (mem.eql(u8, atom_ptr.name(elf_file), ".eh_frame")) { + const sym_name = symbol.name(elf_file); + if (mem.startsWith(u8, sym_name, "__EH_FRAME_BEGIN__") or + mem.startsWith(u8, sym_name, "__EH_FRAME_LIST__") or + mem.startsWith(u8, sym_name, ".eh_frame_seg") or + symbol.elfSym(elf_file).st_type() == elf.STT_SECTION) + { + return elf_file.shdrs.items[elf_file.eh_frame_section_index.?].sh_addr; + } + + if (mem.startsWith(u8, sym_name, "__FRAME_END__") or + mem.startsWith(u8, sym_name, "__EH_FRAME_LIST_END__")) + { + const shdr = elf_file.shdrs.items[elf_file.eh_frame_section_index.?]; + return shdr.sh_addr + shdr.sh_size; + } + + // TODO I think we potentially should error here + } + + return 0; + } return atom_ptr.address(elf_file) + symbol.value; } return symbol.value; @@ -432,6 +455,7 @@ pub const Index = u32; const assert = std.debug.assert; const elf = std.elf; +const mem = std.mem; const std = @import("std"); const synthetic_sections = @import("synthetic_sections.zig"); diff --git a/src/link/Elf/ZigObject.zig b/src/link/Elf/ZigObject.zig index 6aede441c8..f65ef43eac 100644 --- a/src/link/Elf/ZigObject.zig +++ b/src/link/Elf/ZigObject.zig @@ -367,7 +367,7 @@ pub fn claimUnresolved(self: ZigObject, elf_file: *Elf) void { } const is_import = blk: { - if (!elf_file.base.isDynLib()) break :blk false; + if (!elf_file.isEffectivelyDynLib()) break :blk false; const vis = @as(elf.STV, @enumFromInt(esym.st_other)); if (vis == .HIDDEN) break :blk false; break :blk true; @@ -902,9 +902,9 @@ fn updateDeclCode( const gpa = elf_file.base.comp.gpa; const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); + log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); const required_alignment = decl.getAlignment(mod); @@ -915,7 +915,7 @@ fn updateDeclCode( sym.output_section_index = shdr_index; atom_ptr.output_section_index = shdr_index; - sym.name_offset = try self.strtab.insert(gpa, decl_name); + sym.name_offset = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool)); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_name = sym.name_offset; @@ -932,7 +932,7 @@ fn updateDeclCode( const need_realloc = code.len > capacity or !required_alignment.check(atom_ptr.value); if (need_realloc) { try atom_ptr.grow(elf_file); - log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom_ptr.value }); + log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom_ptr.value }); if (old_vaddr != atom_ptr.value) { sym.value = 0; esym.st_value = 0; @@ -1000,9 +1000,9 @@ fn updateTlv( const gpa = elf_file.base.comp.gpa; const mod = elf_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateTlv {s} ({*})", .{ decl_name, decl }); + log.debug("updateTlv {} ({*})", .{ decl_name.fmt(&mod.intern_pool), decl }); const required_alignment = decl.getAlignment(mod); @@ -1014,7 +1014,7 @@ fn updateTlv( sym.output_section_index = shndx; atom_ptr.output_section_index = shndx; - sym.name_offset = try self.strtab.insert(gpa, decl_name); + sym.name_offset = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool)); atom_ptr.flags.alive = true; atom_ptr.name_offset = sym.name_offset; esym.st_value = 0; @@ -1136,8 +1136,8 @@ pub fn updateDecl( if (decl.isExtern(mod)) { // Extern variable gets a .got entry only. const variable = decl.getOwnedVariable(mod).?; - const name = mod.intern_pool.stringToSlice(decl.name); - const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); + const name = decl.name.toSlice(&mod.intern_pool); + const lib_name = variable.lib_name.toSlice(&mod.intern_pool); const esym_index = try self.getGlobalSymbol(elf_file, name, lib_name); elf_file.symbol(self.symbol(esym_index)).flags.needs_got = true; return; @@ -1293,9 +1293,9 @@ pub fn lowerUnnamedConst( } const unnamed_consts = gop.value_ptr; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); const index = unnamed_consts.items.len; - const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); defer gpa.free(name); const ty = val.typeOf(mod); const sym_index = switch (try self.lowerConst( @@ -1418,7 +1418,7 @@ pub fn updateExports( for (exports) |exp| { if (exp.opts.section.unwrap()) |section_name| { - if (!mod.intern_pool.stringEqlSlice(section_name, ".text")) { + if (!section_name.eqlSlice(".text", &mod.intern_pool)) { try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create( gpa, @@ -1445,7 +1445,7 @@ pub fn updateExports( }, }; const stt_bits: u8 = @as(u4, @truncate(esym.st_info)); - const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); + const exp_name = exp.opts.name.toSlice(&mod.intern_pool); const name_off = try self.strtab.insert(gpa, exp_name); const global_esym_index = if (metadata.@"export"(self, exp_name)) |exp_index| exp_index.* @@ -1476,9 +1476,9 @@ pub fn updateDeclLineNumber( defer tracy.end(); const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); + log.debug("updateDeclLineNumber {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); if (self.dwarf) |*dw| { try dw.updateDeclLineNumber(mod, decl_index); @@ -1493,7 +1493,7 @@ pub fn deleteDeclExport( ) void { const metadata = self.decls.getPtr(decl_index) orelse return; const mod = elf_file.base.comp.module.?; - const exp_name = mod.intern_pool.stringToSlice(name); + const exp_name = name.toSlice(&mod.intern_pool); const esym_index = metadata.@"export"(self, exp_name) orelse return; log.debug("deleting export '{s}'", .{exp_name}); const esym = &self.global_esyms.items(.elf_sym)[esym_index.*]; diff --git a/src/link/Elf/synthetic_sections.zig b/src/link/Elf/synthetic_sections.zig index 0e7cb90545..2ef7d49540 100644 --- a/src/link/Elf/synthetic_sections.zig +++ b/src/link/Elf/synthetic_sections.zig @@ -89,7 +89,7 @@ pub const DynamicSection = struct { if (elf_file.verneed_section_index != null) nentries += 2; // VERNEED if (dt.getFlags(elf_file) != null) nentries += 1; // FLAGS if (dt.getFlags1(elf_file) != null) nentries += 1; // FLAGS_1 - if (!elf_file.base.isDynLib()) nentries += 1; // DEBUG + if (!elf_file.isEffectivelyDynLib()) nentries += 1; // DEBUG nentries += 1; // NULL return nentries * @sizeOf(elf.Elf64_Dyn); } @@ -216,7 +216,7 @@ pub const DynamicSection = struct { } // DEBUG - if (!elf_file.base.isDynLib()) try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_DEBUG, .d_val = 0 }); + if (!elf_file.isEffectivelyDynLib()) try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_DEBUG, .d_val = 0 }); // NULL try writer.writeStruct(elf.Elf64_Dyn{ .d_tag = elf.DT_NULL, .d_val = 0 }); @@ -256,7 +256,7 @@ pub const ZigGotSection = struct { entry.* = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_zig_got = true; - if (elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) { + if (elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) { zig_got.flags.needs_rela = true; } if (symbol.extra(elf_file)) |extra| { @@ -495,7 +495,7 @@ pub const GotSection = struct { const symbol = elf_file.symbol(sym_index); symbol.flags.has_got = true; if (symbol.flags.import or symbol.isIFunc(elf_file) or - ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file))) + ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.isAbs(elf_file))) { got.flags.needs_rela = true; } @@ -528,7 +528,7 @@ pub const GotSection = struct { entry.symbol_index = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_tlsgd = true; - if (symbol.flags.import or elf_file.base.isDynLib()) got.flags.needs_rela = true; + if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true; if (symbol.extra(elf_file)) |extra| { var new_extra = extra; new_extra.tlsgd = index; @@ -545,7 +545,7 @@ pub const GotSection = struct { entry.symbol_index = sym_index; const symbol = elf_file.symbol(sym_index); symbol.flags.has_gottp = true; - if (symbol.flags.import or elf_file.base.isDynLib()) got.flags.needs_rela = true; + if (symbol.flags.import or elf_file.isEffectivelyDynLib()) got.flags.needs_rela = true; if (symbol.extra(elf_file)) |extra| { var new_extra = extra; new_extra.gottp = index; @@ -580,7 +580,7 @@ pub const GotSection = struct { pub fn write(got: GotSection, elf_file: *Elf, writer: anytype) !void { const comp = elf_file.base.comp; - const is_dyn_lib = elf_file.base.isDynLib(); + const is_dyn_lib = elf_file.isEffectivelyDynLib(); const apply_relocs = true; // TODO add user option for this for (got.entries.items) |entry| { @@ -595,7 +595,7 @@ pub const GotSection = struct { if (symbol.?.flags.import) break :blk 0; if (symbol.?.isIFunc(elf_file)) break :blk if (apply_relocs) value else 0; - if ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and + if ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file)) { break :blk if (apply_relocs) value else 0; @@ -653,7 +653,7 @@ pub const GotSection = struct { pub fn addRela(got: GotSection, elf_file: *Elf) !void { const comp = elf_file.base.comp; const gpa = comp.gpa; - const is_dyn_lib = elf_file.base.isDynLib(); + const is_dyn_lib = elf_file.isEffectivelyDynLib(); const cpu_arch = elf_file.getTarget().cpu.arch; try elf_file.rela_dyn.ensureUnusedCapacity(gpa, got.numRela(elf_file)); @@ -683,7 +683,7 @@ pub const GotSection = struct { }); continue; } - if ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and + if ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file)) { elf_file.addRelaDynAssumeCapacity(.{ @@ -758,7 +758,7 @@ pub const GotSection = struct { pub fn numRela(got: GotSection, elf_file: *Elf) usize { const comp = elf_file.base.comp; - const is_dyn_lib = elf_file.base.isDynLib(); + const is_dyn_lib = elf_file.isEffectivelyDynLib(); var num: usize = 0; for (got.entries.items) |entry| { const symbol = switch (entry.tag) { @@ -767,7 +767,7 @@ pub const GotSection = struct { }; switch (entry.tag) { .got => if (symbol.?.flags.import or symbol.?.isIFunc(elf_file) or - ((elf_file.base.isDynLib() or (elf_file.base.isExe() and comp.config.pie)) and + ((elf_file.isEffectivelyDynLib() or (elf_file.base.isExe() and comp.config.pie)) and !symbol.?.isAbs(elf_file))) { num += 1; diff --git a/src/link/MachO/ZigObject.zig b/src/link/MachO/ZigObject.zig index fb27c96525..bb788fcacc 100644 --- a/src/link/MachO/ZigObject.zig +++ b/src/link/MachO/ZigObject.zig @@ -716,8 +716,8 @@ pub fn updateDecl( if (decl.isExtern(mod)) { // Extern variable gets a __got entry only const variable = decl.getOwnedVariable(mod).?; - const name = mod.intern_pool.stringToSlice(decl.name); - const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); + const name = decl.name.toSlice(&mod.intern_pool); + const lib_name = variable.lib_name.toSlice(&mod.intern_pool); const index = try self.getGlobalSymbol(macho_file, name, lib_name); const actual_index = self.symbols.items[index]; macho_file.getSymbol(actual_index).flags.needs_got = true; @@ -786,9 +786,9 @@ fn updateDeclCode( const gpa = macho_file.base.comp.gpa; const mod = macho_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateDeclCode {s}{*}", .{ decl_name, decl }); + log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); const required_alignment = decl.getAlignment(mod); @@ -800,7 +800,7 @@ fn updateDeclCode( sym.out_n_sect = sect_index; atom.out_n_sect = sect_index; - sym.name = try self.strtab.insert(gpa, decl_name); + sym.name = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool)); atom.flags.alive = true; atom.name = sym.name; nlist.n_strx = sym.name; @@ -819,7 +819,7 @@ fn updateDeclCode( if (need_realloc) { try atom.grow(macho_file); - log.debug("growing {s} from 0x{x} to 0x{x}", .{ decl_name, old_vaddr, atom.value }); + log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom.value }); if (old_vaddr != atom.value) { sym.value = 0; nlist.n_value = 0; @@ -870,23 +870,24 @@ fn updateTlv( ) !void { const mod = macho_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateTlv {s} ({*})", .{ decl_name, decl }); + log.debug("updateTlv {} ({*})", .{ decl_name.fmt(&mod.intern_pool), decl }); + const decl_name_slice = decl_name.toSlice(&mod.intern_pool); const required_alignment = decl.getAlignment(mod); // 1. Lower TLV initializer const init_sym_index = try self.createTlvInitializer( macho_file, - decl_name, + decl_name_slice, required_alignment, sect_index, code, ); // 2. Create TLV descriptor - try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl_name); + try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl_name_slice); } fn createTlvInitializer( @@ -1073,9 +1074,9 @@ pub fn lowerUnnamedConst( } const unnamed_consts = gop.value_ptr; const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); const index = unnamed_consts.items.len; - const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); defer gpa.free(name); const sym_index = switch (try self.lowerConst( macho_file, @@ -1199,7 +1200,7 @@ pub fn updateExports( for (exports) |exp| { if (exp.opts.section.unwrap()) |section_name| { - if (!mod.intern_pool.stringEqlSlice(section_name, "__text")) { + if (!section_name.eqlSlice("__text", &mod.intern_pool)) { try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1); mod.failed_exports.putAssumeCapacityNoClobber(exp, try Module.ErrorMsg.create( gpa, @@ -1220,7 +1221,7 @@ pub fn updateExports( continue; } - const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); + const exp_name = exp.opts.name.toSlice(&mod.intern_pool); const global_nlist_index = if (metadata.@"export"(self, exp_name)) |exp_index| exp_index.* else blk: { @@ -1349,13 +1350,12 @@ pub fn deleteDeclExport( decl_index: InternPool.DeclIndex, name: InternPool.NullTerminatedString, ) void { - const metadata = self.decls.getPtr(decl_index) orelse return; - const mod = macho_file.base.comp.module.?; - const exp_name = mod.intern_pool.stringToSlice(name); - const nlist_index = metadata.@"export"(self, exp_name) orelse return; - log.debug("deleting export '{s}'", .{exp_name}); + const metadata = self.decls.getPtr(decl_index) orelse return; + const nlist_index = metadata.@"export"(self, name.toSlice(&mod.intern_pool)) orelse return; + + log.debug("deleting export '{}'", .{name.fmt(&mod.intern_pool)}); const nlist = &self.symtab.items(.nlist)[nlist_index.*]; self.symtab.items(.size)[nlist_index.*] = 0; diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index d5900d2b16..323cc8c4a9 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -477,11 +477,11 @@ pub fn lowerUnnamedConst(self: *Plan9, val: Value, decl_index: InternPool.DeclIn } const unnamed_consts = gop.value_ptr; - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); const index = unnamed_consts.items.len; // name is freed when the unnamed const is freed - const name = try std.fmt.allocPrint(gpa, "__unnamed_{s}_{d}", .{ decl_name, index }); + const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index }); const sym_index = try self.allocateSymbolIndex(); const new_atom_idx = try self.createAtom(); @@ -529,7 +529,7 @@ pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: InternPool.DeclIndex) const decl = mod.declPtr(decl_index); if (decl.isExtern(mod)) { - log.debug("found extern decl: {s}", .{mod.intern_pool.stringToSlice(decl.name)}); + log.debug("found extern decl: {}", .{decl.name.fmt(&mod.intern_pool)}); return; } const atom_idx = try self.seeDecl(decl_index); @@ -573,7 +573,7 @@ fn updateFinish(self: *Plan9, decl_index: InternPool.DeclIndex) !void { const sym: aout.Sym = .{ .value = undefined, // the value of stuff gets filled in in flushModule .type = atom.type, - .name = try gpa.dupe(u8, mod.intern_pool.stringToSlice(decl.name)), + .name = try gpa.dupe(u8, decl.name.toSlice(&mod.intern_pool)), }; if (atom.sym_index) |s| { @@ -1013,10 +1013,12 @@ fn addDeclExports( const atom = self.getAtom(metadata.index); for (exports) |exp| { - const exp_name = mod.intern_pool.stringToSlice(exp.opts.name); + const exp_name = exp.opts.name.toSlice(&mod.intern_pool); // plan9 does not support custom sections if (exp.opts.section.unwrap()) |section_name| { - if (!mod.intern_pool.stringEqlSlice(section_name, ".text") and !mod.intern_pool.stringEqlSlice(section_name, ".data")) { + if (!section_name.eqlSlice(".text", &mod.intern_pool) and + !section_name.eqlSlice(".data", &mod.intern_pool)) + { try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create( gpa, mod.declPtr(decl_index).srcLoc(mod), @@ -1129,19 +1131,21 @@ pub fn seeDecl(self: *Plan9, decl_index: InternPool.DeclIndex) !Atom.Index { // handle externs here because they might not get updateDecl called on them const mod = self.base.comp.module.?; const decl = mod.declPtr(decl_index); - const name = mod.intern_pool.stringToSlice(decl.name); if (decl.isExtern(mod)) { // this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs - if (std.mem.eql(u8, name, "etext")) { + if (decl.name.eqlSlice("etext", &mod.intern_pool)) { self.etext_edata_end_atom_indices[0] = atom_idx; - } else if (std.mem.eql(u8, name, "edata")) { + } else if (decl.name.eqlSlice("edata", &mod.intern_pool)) { self.etext_edata_end_atom_indices[1] = atom_idx; - } else if (std.mem.eql(u8, name, "end")) { + } else if (decl.name.eqlSlice("end", &mod.intern_pool)) { self.etext_edata_end_atom_indices[2] = atom_idx; } try self.updateFinish(decl_index); - log.debug("seeDecl(extern) for {s} (got_addr=0x{x})", .{ name, self.getAtom(atom_idx).getOffsetTableAddress(self) }); - } else log.debug("seeDecl for {s}", .{name}); + log.debug("seeDecl(extern) for {} (got_addr=0x{x})", .{ + decl.name.fmt(&mod.intern_pool), + self.getAtom(atom_idx).getOffsetTableAddress(self), + }); + } else log.debug("seeDecl for {}", .{decl.name.fmt(&mod.intern_pool)}); return atom_idx; } @@ -1393,7 +1397,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const sym = self.syms.items[atom.sym_index.?]; try self.writeSym(writer, sym); if (self.base.comp.module.?.decl_exports.get(decl_index)) |exports| { - for (exports.items) |e| if (decl_metadata.getExport(self, ip.stringToSlice(e.opts.name))) |exp_i| { + for (exports.items) |e| if (decl_metadata.getExport(self, e.opts.name.toSlice(ip))) |exp_i| { try self.writeSym(writer, self.syms.items[exp_i]); }; } @@ -1440,7 +1444,7 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void { const sym = self.syms.items[atom.sym_index.?]; try self.writeSym(writer, sym); if (self.base.comp.module.?.decl_exports.get(decl_index)) |exports| { - for (exports.items) |e| if (decl_metadata.getExport(self, ip.stringToSlice(e.opts.name))) |exp_i| { + for (exports.items) |e| if (decl_metadata.getExport(self, e.opts.name.toSlice(ip))) |exp_i| { const s = self.syms.items[exp_i]; if (mem.eql(u8, s.name, "_start")) self.entry_val = s.value; @@ -1483,25 +1487,25 @@ pub fn getDeclVAddr( reloc_info: link.File.RelocInfo, ) !u64 { const mod = self.base.comp.module.?; + const ip = &mod.intern_pool; const decl = mod.declPtr(decl_index); - log.debug("getDeclVAddr for {s}", .{mod.intern_pool.stringToSlice(decl.name)}); + log.debug("getDeclVAddr for {}", .{decl.name.fmt(ip)}); if (decl.isExtern(mod)) { - const extern_name = mod.intern_pool.stringToSlice(decl.name); - if (std.mem.eql(u8, extern_name, "etext")) { + if (decl.name.eqlSlice("etext", ip)) { try self.addReloc(reloc_info.parent_atom_index, .{ .target = undefined, .offset = reloc_info.offset, .addend = reloc_info.addend, .type = .special_etext, }); - } else if (std.mem.eql(u8, extern_name, "edata")) { + } else if (decl.name.eqlSlice("edata", ip)) { try self.addReloc(reloc_info.parent_atom_index, .{ .target = undefined, .offset = reloc_info.offset, .addend = reloc_info.addend, .type = .special_edata, }); - } else if (std.mem.eql(u8, extern_name, "end")) { + } else if (decl.name.eqlSlice("end", ip)) { try self.addReloc(reloc_info.parent_atom_index, .{ .target = undefined, .offset = reloc_info.offset, diff --git a/src/link/SpirV.zig b/src/link/SpirV.zig index 950e0375f0..27c905cc61 100644 --- a/src/link/SpirV.zig +++ b/src/link/SpirV.zig @@ -130,7 +130,7 @@ pub fn updateFunc(self: *SpirV, module: *Module, func_index: InternPool.Index, a const func = module.funcInfo(func_index); const decl = module.declPtr(func.owner_decl); - log.debug("lowering function {s}", .{module.intern_pool.stringToSlice(decl.name)}); + log.debug("lowering function {}", .{decl.name.fmt(&module.intern_pool)}); try self.object.updateFunc(module, func_index, air, liveness); } @@ -141,7 +141,7 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl_index: InternPool.DeclInde } const decl = module.declPtr(decl_index); - log.debug("lowering declaration {s}", .{module.intern_pool.stringToSlice(decl.name)}); + log.debug("lowering declaration {}", .{decl.name.fmt(&module.intern_pool)}); try self.object.updateDecl(module, decl_index); } @@ -178,7 +178,7 @@ pub fn updateExports( for (exports) |exp| { try self.object.spv.declareEntryPoint( spv_decl_index, - mod.intern_pool.stringToSlice(exp.opts.name), + exp.opts.name.toSlice(&mod.intern_pool), execution_model, ); } @@ -227,16 +227,24 @@ pub fn flushModule(self: *SpirV, arena: Allocator, prog_node: *std.Progress.Node try error_info.appendSlice("zig_errors"); const mod = self.base.comp.module.?; - for (mod.global_error_set.keys()) |name_nts| { - const name = mod.intern_pool.stringToSlice(name_nts); + for (mod.global_error_set.keys()) |name| { // Errors can contain pretty much any character - to encode them in a string we must escape // them somehow. Easiest here is to use some established scheme, one which also preseves the // name if it contains no strange characters is nice for debugging. URI encoding fits the bill. // We're using : as separator, which is a reserved character. - const escaped_name = try std.Uri.escapeString(gpa, name); - defer gpa.free(escaped_name); - try error_info.writer().print(":{s}", .{escaped_name}); + try std.Uri.Component.percentEncode( + error_info.writer(), + name.toSlice(&mod.intern_pool), + struct { + fn isValidChar(c: u8) bool { + return switch (c) { + 0, '%', ':' => false, + else => true, + }; + } + }.isValidChar, + ); } try spv.sections.debug_strings.emit(gpa, .OpSourceExtension, .{ .extension = error_info.items, diff --git a/src/link/Wasm/ZigObject.zig b/src/link/Wasm/ZigObject.zig index 9bf7718d2b..bcd98c3d3c 100644 --- a/src/link/Wasm/ZigObject.zig +++ b/src/link/Wasm/ZigObject.zig @@ -258,8 +258,8 @@ pub fn updateDecl( if (decl.isExtern(mod)) { const variable = decl.getOwnedVariable(mod).?; - const name = mod.intern_pool.stringToSlice(decl.name); - const lib_name = mod.intern_pool.stringToSliceUnwrap(variable.lib_name); + const name = decl.name.toSlice(&mod.intern_pool); + const lib_name = variable.lib_name.toSlice(&mod.intern_pool); return zig_object.addOrUpdateImport(wasm_file, name, atom.sym_index, lib_name, null); } const val = if (decl.val.getVariable(mod)) |variable| Value.fromInterned(variable.init) else decl.val; @@ -341,8 +341,8 @@ fn finishUpdateDecl( const atom_index = decl_info.atom; const atom = wasm_file.getAtomPtr(atom_index); const sym = zig_object.symbol(atom.sym_index); - const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); - sym.name = try zig_object.string_table.insert(gpa, full_name); + const full_name = try decl.fullyQualifiedName(mod); + sym.name = try zig_object.string_table.insert(gpa, full_name.toSlice(&mod.intern_pool)); try atom.code.appendSlice(gpa, code); atom.size = @intCast(code.len); @@ -382,7 +382,7 @@ fn finishUpdateDecl( // Will be freed upon freeing of decl or after cleanup of Wasm binary. const full_segment_name = try std.mem.concat(gpa, u8, &.{ segment_name, - full_name, + full_name.toSlice(&mod.intern_pool), }); errdefer gpa.free(full_segment_name); sym.tag = .data; @@ -427,9 +427,9 @@ pub fn getOrCreateAtomForDecl(zig_object: *ZigObject, wasm_file: *Wasm, decl_ind gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) }; const mod = wasm_file.base.comp.module.?; const decl = mod.declPtr(decl_index); - const full_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const full_name = try decl.fullyQualifiedName(mod); const sym = zig_object.symbol(sym_index); - sym.name = try zig_object.string_table.insert(gpa, full_name); + sym.name = try zig_object.string_table.insert(gpa, full_name.toSlice(&mod.intern_pool)); } return gop.value_ptr.atom; } @@ -478,9 +478,9 @@ pub fn lowerUnnamedConst(zig_object: *ZigObject, wasm_file: *Wasm, val: Value, d const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, decl_index); const parent_atom = wasm_file.getAtom(parent_atom_index); const local_index = parent_atom.locals.items.len; - const fqn = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); - const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{s}_{d}", .{ - fqn, local_index, + const fqn = try decl.fullyQualifiedName(mod); + const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{}_{d}", .{ + fqn.fmt(&mod.intern_pool), local_index, }); defer gpa.free(name); @@ -623,11 +623,11 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void { // Addend for each relocation to the table var addend: u32 = 0; const mod = wasm_file.base.comp.module.?; - for (mod.global_error_set.keys()) |error_name_nts| { + for (mod.global_error_set.keys()) |error_name| { const atom = wasm_file.getAtomPtr(atom_index); - const error_name = mod.intern_pool.stringToSlice(error_name_nts); - const len: u32 = @intCast(error_name.len + 1); // names are 0-terminated + const error_name_slice = error_name.toSlice(&mod.intern_pool); + const len: u32 = @intCast(error_name_slice.len + 1); // names are 0-terminated const slice_ty = Type.slice_const_u8_sentinel_0; const offset = @as(u32, @intCast(atom.code.items.len)); @@ -646,10 +646,9 @@ fn populateErrorNameTable(zig_object: *ZigObject, wasm_file: *Wasm) !void { // as we updated the error name table, we now store the actual name within the names atom try names_atom.code.ensureUnusedCapacity(gpa, len); - names_atom.code.appendSliceAssumeCapacity(error_name); - names_atom.code.appendAssumeCapacity(0); + names_atom.code.appendSliceAssumeCapacity(error_name_slice[0..len]); - log.debug("Populated error name: '{s}'", .{error_name}); + log.debug("Populated error name: '{}'", .{error_name.fmt(&mod.intern_pool)}); } names_atom.size = addend; zig_object.error_names_atom = names_atom_index; @@ -833,8 +832,7 @@ pub fn deleteDeclExport( ) void { const mod = wasm_file.base.comp.module.?; const decl_info = zig_object.decls_map.getPtr(decl_index) orelse return; - const export_name = mod.intern_pool.stringToSlice(name); - if (decl_info.@"export"(zig_object, export_name)) |sym_index| { + if (decl_info.@"export"(zig_object, name.toSlice(&mod.intern_pool))) |sym_index| { const sym = zig_object.symbol(sym_index); decl_info.deleteExport(sym_index); std.debug.assert(zig_object.global_syms.remove(sym.name)); @@ -864,10 +862,10 @@ pub fn updateExports( const atom = wasm_file.getAtom(atom_index); const atom_sym = atom.symbolLoc().getSymbol(wasm_file).*; const gpa = mod.gpa; - log.debug("Updating exports for decl '{s}'", .{mod.intern_pool.stringToSlice(decl.name)}); + log.debug("Updating exports for decl '{}'", .{decl.name.fmt(&mod.intern_pool)}); for (exports) |exp| { - if (mod.intern_pool.stringToSliceUnwrap(exp.opts.section)) |section| { + if (exp.opts.section.toSlice(&mod.intern_pool)) |section| { try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create( gpa, decl.srcLoc(mod), @@ -877,10 +875,8 @@ pub fn updateExports( continue; } - const export_string = mod.intern_pool.stringToSlice(exp.opts.name); - const sym_index = if (decl_info.@"export"(zig_object, export_string)) |idx| - idx - else index: { + const export_string = exp.opts.name.toSlice(&mod.intern_pool); + const sym_index = if (decl_info.@"export"(zig_object, export_string)) |idx| idx else index: { const sym_index = try zig_object.allocateSymbol(gpa); try decl_info.appendExport(gpa, sym_index); break :index sym_index; @@ -1089,9 +1085,9 @@ pub fn createDebugSectionForIndex(zig_object: *ZigObject, wasm_file: *Wasm, inde pub fn updateDeclLineNumber(zig_object: *ZigObject, mod: *Module, decl_index: InternPool.DeclIndex) !void { if (zig_object.dwarf) |*dw| { const decl = mod.declPtr(decl_index); - const decl_name = mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)); + const decl_name = try decl.fullyQualifiedName(mod); - log.debug("updateDeclLineNumber {s}{*}", .{ decl_name, decl }); + log.debug("updateDeclLineNumber {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl }); try dw.updateDeclLineNumber(mod, decl_index); } } diff --git a/src/mutable_value.zig b/src/mutable_value.zig index 327c354108..f16a8fd3f9 100644 --- a/src/mutable_value.zig +++ b/src/mutable_value.zig @@ -73,7 +73,7 @@ pub const MutableValue = union(enum) { } }), .bytes => |b| try ip.get(gpa, .{ .aggregate = .{ .ty = b.ty, - .storage = .{ .bytes = b.data }, + .storage = .{ .bytes = try ip.getOrPutString(gpa, b.data, .maybe_embedded_nulls) }, } }), .aggregate => |a| { const elems = try arena.alloc(InternPool.Index, a.elems.len); @@ -158,18 +158,18 @@ pub const MutableValue = union(enum) { }, .aggregate => |agg| switch (agg.storage) { .bytes => |bytes| { - assert(bytes.len == ip.aggregateTypeLenIncludingSentinel(agg.ty)); + const len: usize = @intCast(ip.aggregateTypeLenIncludingSentinel(agg.ty)); assert(ip.childType(agg.ty) == .u8_type); if (allow_bytes) { - const arena_bytes = try arena.alloc(u8, bytes.len); - @memcpy(arena_bytes, bytes); + const arena_bytes = try arena.alloc(u8, len); + @memcpy(arena_bytes, bytes.toSlice(len, ip)); mv.* = .{ .bytes = .{ .ty = agg.ty, .data = arena_bytes, } }; } else { - const mut_elems = try arena.alloc(MutableValue, bytes.len); - for (bytes, mut_elems) |b, *mut_elem| { + const mut_elems = try arena.alloc(MutableValue, len); + for (bytes.toSlice(len, ip), mut_elems) |b, *mut_elem| { mut_elem.* = .{ .interned = try ip.get(gpa, .{ .int = .{ .ty = .u8_type, .storage = .{ .u64 = b }, diff --git a/src/print_value.zig b/src/print_value.zig index 21a322cd63..c021b011c1 100644 --- a/src/print_value.zig +++ b/src/print_value.zig @@ -204,26 +204,35 @@ fn printAggregate( try writer.writeAll(" }"); return; }, - .Array => if (aggregate.storage == .bytes and aggregate.storage.bytes.len > 0) { - const skip_terminator = aggregate.storage.bytes[aggregate.storage.bytes.len - 1] == 0; - const bytes = if (skip_terminator) b: { - break :b aggregate.storage.bytes[0 .. aggregate.storage.bytes.len - 1]; - } else aggregate.storage.bytes; - try writer.print("\"{}\"", .{std.zig.fmtEscapes(bytes)}); - if (!is_ref) try writer.writeAll(".*"); - return; - } else if (ty.arrayLen(zcu) == 0) { - if (is_ref) try writer.writeByte('&'); - return writer.writeAll(".{}"); - } else if (ty.arrayLen(zcu) == 1) one_byte_str: { - // The repr isn't `bytes`, but we might still be able to print this as a string - if (ty.childType(zcu).toIntern() != .u8_type) break :one_byte_str; - const elem_val = Value.fromInterned(aggregate.storage.values()[0]); - if (elem_val.isUndef(zcu)) break :one_byte_str; - const byte = elem_val.toUnsignedInt(zcu); - try writer.print("\"{}\"", .{std.zig.fmtEscapes(&.{@intCast(byte)})}); - if (!is_ref) try writer.writeAll(".*"); - return; + .Array => { + switch (aggregate.storage) { + .bytes => |bytes| string: { + const len = ty.arrayLenIncludingSentinel(zcu); + if (len == 0) break :string; + const slice = bytes.toSlice(if (bytes.at(len - 1, ip) == 0) len - 1 else len, ip); + try writer.print("\"{}\"", .{std.zig.fmtEscapes(slice)}); + if (!is_ref) try writer.writeAll(".*"); + return; + }, + .elems, .repeated_elem => {}, + } + switch (ty.arrayLen(zcu)) { + 0 => { + if (is_ref) try writer.writeByte('&'); + return writer.writeAll(".{}"); + }, + 1 => one_byte_str: { + // The repr isn't `bytes`, but we might still be able to print this as a string + if (ty.childType(zcu).toIntern() != .u8_type) break :one_byte_str; + const elem_val = Value.fromInterned(aggregate.storage.values()[0]); + if (elem_val.isUndef(zcu)) break :one_byte_str; + const byte = elem_val.toUnsignedInt(zcu); + try writer.print("\"{}\"", .{std.zig.fmtEscapes(&.{@intCast(byte)})}); + if (!is_ref) try writer.writeAll(".*"); + return; + }, + else => {}, + } }, .Vector => if (ty.arrayLen(zcu) == 0) { if (is_ref) try writer.writeByte('&'); diff --git a/src/target.zig b/src/target.zig index fa782075c7..3ad36deab2 100644 --- a/src/target.zig +++ b/src/target.zig @@ -59,10 +59,15 @@ pub fn alwaysSingleThreaded(target: std.Target) bool { } pub fn defaultSingleThreaded(target: std.Target) bool { - return switch (target.cpu.arch) { - .wasm32, .wasm64 => true, - else => false, - }; + switch (target.cpu.arch) { + .wasm32, .wasm64 => return true, + else => {}, + } + switch (target.os.tag) { + .haiku => return true, + else => {}, + } + return false; } /// Valgrind supports more, but Zig does not support them yet. diff --git a/src/translate_c.zig b/src/translate_c.zig index 16c2060163..bf6b8ec1d8 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -2086,6 +2086,11 @@ fn finishBoolExpr( } }, .Pointer => { + if (node.tag() == .string_literal) { + // @intFromPtr(node) != 0 + const int_from_ptr = try Tag.int_from_ptr.create(c.arena, node); + return Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() }); + } // node != null return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() }); }, @@ -5793,7 +5798,12 @@ fn macroIntToBool(c: *Context, node: Node) !Node { if (isBoolRes(node)) { return node; } - + if (node.tag() == .string_literal) { + // @intFromPtr(node) != 0 + const int_from_ptr = try Tag.int_from_ptr.create(c.arena, node); + return Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() }); + } + // node != 0 return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() }); } diff --git a/src/type.zig b/src/type.zig index 8352552463..264125c6d0 100644 --- a/src/type.zig +++ b/src/type.zig @@ -490,18 +490,10 @@ pub const Type = struct { }; }, .anyframe_type => true, - .array_type => |array_type| { - if (array_type.sentinel != .none) { - return Type.fromInterned(array_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat); - } else { - return array_type.len > 0 and - try Type.fromInterned(array_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat); - } - }, - .vector_type => |vector_type| { - return vector_type.len > 0 and - try Type.fromInterned(vector_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat); - }, + .array_type => |array_type| return array_type.lenIncludingSentinel() > 0 and + try Type.fromInterned(array_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), + .vector_type => |vector_type| return vector_type.len > 0 and + try Type.fromInterned(vector_type.child).hasRuntimeBitsAdvanced(mod, ignore_comptime_only, strat), .opt_type => |child| { const child_ty = Type.fromInterned(child); if (child_ty.isNoReturn(mod)) { @@ -1240,7 +1232,7 @@ pub const Type = struct { .anyframe_type => return AbiSizeAdvanced{ .scalar = @divExact(target.ptrBitWidth(), 8) }, .array_type => |array_type| { - const len = array_type.len + @intFromBool(array_type.sentinel != .none); + const len = array_type.lenIncludingSentinel(); if (len == 0) return .{ .scalar = 0 }; switch (try Type.fromInterned(array_type.child).abiSizeAdvanced(mod, strat)) { .scalar => |elem_size| return .{ .scalar = len * elem_size }, @@ -1577,7 +1569,7 @@ pub const Type = struct { .anyframe_type => return target.ptrBitWidth(), .array_type => |array_type| { - const len = array_type.len + @intFromBool(array_type.sentinel != .none); + const len = array_type.lenIncludingSentinel(); if (len == 0) return 0; const elem_ty = Type.fromInterned(array_type.child); const elem_size = @max( @@ -1731,7 +1723,7 @@ pub const Type = struct { .struct_type => ip.loadStructType(ty.toIntern()).haveLayout(ip), .union_type => ip.loadUnionType(ty.toIntern()).haveLayout(ip), .array_type => |array_type| { - if ((array_type.len + @intFromBool(array_type.sentinel != .none)) == 0) return true; + if (array_type.lenIncludingSentinel() == 0) return true; return Type.fromInterned(array_type.child).layoutIsResolved(mod); }, .opt_type => |child| Type.fromInterned(child).layoutIsResolved(mod), diff --git a/stage1/zig1.wasm b/stage1/zig1.wasm Binary files differindex 5005c8c79a..c2cfd2fde4 100644 --- a/stage1/zig1.wasm +++ b/stage1/zig1.wasm diff --git a/test/behavior/abs.zig b/test/behavior/abs.zig index 2f81dbb9fb..7fc3fbbb4a 100644 --- a/test/behavior/abs.zig +++ b/test/behavior/abs.zig @@ -214,7 +214,6 @@ fn testAbsIntVectors(comptime len: comptime_int) !void { } test "@abs unsigned int vectors" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO @@ -274,7 +273,6 @@ fn testAbsUnsignedIntVectors(comptime len: comptime_int) !void { } test "@abs float vectors" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO diff --git a/test/behavior/call.zig b/test/behavior/call.zig index 68239705da..c1ae3b66c6 100644 --- a/test/behavior/call.zig +++ b/test/behavior/call.zig @@ -267,7 +267,6 @@ test "arguments to comptime parameters generated in comptime blocks" { test "forced tail call" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -280,6 +279,8 @@ test "forced tail call" { } } + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls + const S = struct { fn fibonacciTailInternal(n: u16, a: u16, b: u16) u16 { if (n == 0) return a; @@ -301,7 +302,6 @@ test "forced tail call" { test "inline call preserves tail call" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -314,6 +314,8 @@ test "inline call preserves tail call" { } } + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls + const max = std.math.maxInt(u16); const S = struct { var a: u16 = 0; @@ -432,7 +434,6 @@ test "method call as parameter type" { test "non-anytype generic parameters provide result type" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -463,7 +464,6 @@ test "non-anytype generic parameters provide result type" { test "argument to generic function has correct result type" { if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/call_tail.zig b/test/behavior/call_tail.zig index ccd0d2ad1e..24aab2a01e 100644 --- a/test/behavior/call_tail.zig +++ b/test/behavior/call_tail.zig @@ -45,9 +45,10 @@ test "arguments pointed to on stack into tailcall" { else => {}, } if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // MSVC doesn't support always tail calls + var data = [_]u64{ 1, 6, 2, 7, 1, 9, 3 }; base = @intFromPtr(&data); insertionSort(data[0..]); diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index e88a53629e..72e48de104 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -1119,7 +1119,6 @@ fn foobar(func: PFN_void) !void { } test "cast function with an opaque parameter" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_c) { @@ -1461,6 +1460,7 @@ test "pointer to empty struct literal to mutable slice" { test "coerce between pointers of compatible differently-named floats" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows and !builtin.link_libc) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; @@ -2558,7 +2558,6 @@ test "@intCast vector of signed integer" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO var x: @Vector(4, i32) = .{ 1, 2, 3, 4 }; diff --git a/test/behavior/export_builtin.zig b/test/behavior/export_builtin.zig index e93dc6107a..25b6e2527e 100644 --- a/test/behavior/export_builtin.zig +++ b/test/behavior/export_builtin.zig @@ -5,7 +5,6 @@ const expect = std.testing.expect; test "exporting enum type and value" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { const E = enum(c_int) { one, two }; @@ -20,7 +19,6 @@ test "exporting enum type and value" { test "exporting with internal linkage" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { fn foo() callconv(.C) void {} @@ -34,7 +32,6 @@ test "exporting with internal linkage" { test "exporting using field access" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { const Inner = struct { diff --git a/test/behavior/extern.zig b/test/behavior/extern.zig index 747d62986e..a85f300b10 100644 --- a/test/behavior/extern.zig +++ b/test/behavior/extern.zig @@ -16,7 +16,6 @@ test "anyopaque extern symbol" { export var a_mystery_symbol: i32 = 1234; test "function extern symbol" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; @@ -30,7 +29,6 @@ export fn a_mystery_function() i32 { } test "function extern symbol matches extern decl" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_x86_64 and builtin.target.ofmt != .elf and builtin.target.ofmt != .macho) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; diff --git a/test/behavior/fn.zig b/test/behavior/fn.zig index 373a29f844..ab7aca6ed6 100644 --- a/test/behavior/fn.zig +++ b/test/behavior/fn.zig @@ -582,7 +582,6 @@ test "pass and return comptime-only types" { test "pointer to alias behaves same as pointer to function" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; const S = struct { diff --git a/test/behavior/globals.zig b/test/behavior/globals.zig index 96987c95df..89dc20c5c7 100644 --- a/test/behavior/globals.zig +++ b/test/behavior/globals.zig @@ -7,7 +7,6 @@ test "store to global array" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try expect(pos[1] == 0.0); pos = [2]f32{ 0.0, 1.0 }; @@ -19,7 +18,6 @@ test "store to global vector" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; try expect(vpos[1] == 0.0); vpos = @Vector(2, f32){ 0.0, 1.0 }; @@ -49,7 +47,6 @@ test "global loads can affect liveness" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; const S = struct { const ByRef = struct { diff --git a/test/behavior/optional.zig b/test/behavior/optional.zig index e808710f98..944ec85f85 100644 --- a/test/behavior/optional.zig +++ b/test/behavior/optional.zig @@ -55,17 +55,57 @@ fn testNullPtrsEql() !void { try expect(&number == x); } -test "optional with void type" { - if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; - if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO +test "optional with zero-bit type" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; - const Foo = struct { - x: ?void, + const S = struct { + fn doTheTest(comptime ZeroBit: type, comptime zero_bit: ZeroBit) !void { + const WithRuntime = struct { + zero_bit: ZeroBit, + runtime: u1, + }; + var with_runtime: WithRuntime = undefined; + with_runtime = .{ .zero_bit = zero_bit, .runtime = 0 }; + + const Opt = struct { opt: ?ZeroBit }; + var opt: Opt = .{ .opt = null }; + try expect(opt.opt == null); + try expect(opt.opt != zero_bit); + try expect(opt.opt != with_runtime.zero_bit); + opt.opt = zero_bit; + try expect(opt.opt != null); + try expect(opt.opt == zero_bit); + try expect(opt.opt == with_runtime.zero_bit); + opt = .{ .opt = zero_bit }; + try expect(opt.opt != null); + try expect(opt.opt == zero_bit); + try expect(opt.opt == with_runtime.zero_bit); + opt.opt = with_runtime.zero_bit; + try expect(opt.opt != null); + try expect(opt.opt == zero_bit); + try expect(opt.opt == with_runtime.zero_bit); + opt = .{ .opt = with_runtime.zero_bit }; + try expect(opt.opt != null); + try expect(opt.opt == zero_bit); + try expect(opt.opt == with_runtime.zero_bit); + + var two: ?struct { ZeroBit, ZeroBit } = undefined; + two = .{ with_runtime.zero_bit, with_runtime.zero_bit }; + if (!@inComptime()) { + try expect(two != null); + try expect(two.?[0] == zero_bit); + try expect(two.?[0] == with_runtime.zero_bit); + try expect(two.?[1] == zero_bit); + try expect(two.?[1] == with_runtime.zero_bit); + } + } }; - var x = Foo{ .x = null }; - _ = &x; - try expect(x.x == null); + + try S.doTheTest(void, {}); + try comptime S.doTheTest(void, {}); + try S.doTheTest(enum { only }, .only); + try comptime S.doTheTest(enum { only }, .only); } test "address of unwrap optional" { diff --git a/test/behavior/packed-struct.zig b/test/behavior/packed-struct.zig index 423d7f78e8..3556c06f9c 100644 --- a/test/behavior/packed-struct.zig +++ b/test/behavior/packed-struct.zig @@ -1125,12 +1125,13 @@ test "pointer loaded correctly from packed struct" { } } }; - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_c and builtin.os.tag == .windows) return error.SkipZigTest; // crashes MSVC + var ram = try RAM.new(); var cpu = try CPU.new(&ram); try cpu.tick(); diff --git a/test/behavior/undefined.zig b/test/behavior/undefined.zig index bb8dfda9df..adda49cfe0 100644 --- a/test/behavior/undefined.zig +++ b/test/behavior/undefined.zig @@ -101,7 +101,6 @@ test "reslice of undefined global var slice" { test "returned undef is 0xaa bytes when runtime safety is enabled" { if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 43131ae2d4..7cc272fd77 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1644,7 +1644,6 @@ test "undefined-layout union field pointer has correct alignment" { } test "packed union field pointer has correct alignment" { - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO diff --git a/test/behavior/vector.zig b/test/behavior/vector.zig index eaf09db628..8a23954c76 100644 --- a/test/behavior/vector.zig +++ b/test/behavior/vector.zig @@ -1392,7 +1392,6 @@ test "store vector with memset" { if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_llvm) { switch (builtin.target.cpu.arch) { diff --git a/test/cases/translate_c/assert_with_strlit.c b/test/cases/translate_c/assert_with_strlit.c new file mode 100644 index 0000000000..445428bcf1 --- /dev/null +++ b/test/cases/translate_c/assert_with_strlit.c @@ -0,0 +1,8 @@ + +void assert(int x) {} +#define FOO assert(0 && "error message") + +// translate-c +// c_frontend=clang +// +// pub const FOO = assert((@as(c_int, 0) != 0) and (@intFromPtr("error message") != 0)); diff --git a/test/cases/translate_c/strlit_as_bool.c b/test/cases/translate_c/strlit_as_bool.c new file mode 100644 index 0000000000..4ba0cfe2a4 --- /dev/null +++ b/test/cases/translate_c/strlit_as_bool.c @@ -0,0 +1,8 @@ +void foo() { if(0 && "error message") {} } + +// translate-c +// c_frontend=clang +// +// pub export fn foo() void { +// if (false and (@intFromPtr("error message") != 0)) {} +// } diff --git a/test/link/bss/build.zig b/test/link/bss/build.zig index 865af0a488..cbe913781c 100644 --- a/test/link/bss/build.zig +++ b/test/link/bss/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "bss", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = b.host, .optimize = .Debug, }); diff --git a/test/link/common_symbols/build.zig b/test/link/common_symbols/build.zig index 9b5c9da662..dd093a891a 100644 --- a/test/link/common_symbols/build.zig +++ b/test/link/common_symbols/build.zig @@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, }); test_exe.linkLibrary(lib_a); diff --git a/test/link/common_symbols_alignment/build.zig b/test/link/common_symbols_alignment/build.zig index 63aff339a9..7e732762ef 100644 --- a/test/link/common_symbols_alignment/build.zig +++ b/test/link/common_symbols_alignment/build.zig @@ -22,7 +22,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, }); test_exe.linkLibrary(lib_a); diff --git a/test/link/glibc_compat/build.zig b/test/link/glibc_compat/build.zig index e0cc1a70d8..67a58200c3 100644 --- a/test/link/glibc_compat/build.zig +++ b/test/link/glibc_compat/build.zig @@ -21,7 +21,7 @@ pub fn build(b: *std.Build) void { .{ .arch_os_abi = t }, ) catch unreachable), }); - exe.addCSourceFile(.{ .file = .{ .path = "main.c" } }); + exe.addCSourceFile(.{ .file = b.path("main.c") }); exe.linkLibC(); // TODO: actually test the output _ = exe.getEmittedBin(); @@ -45,7 +45,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = t, - .root_source_file = .{ .path = "glibc_runtime_check.zig" }, + .root_source_file = b.path("glibc_runtime_check.zig"), .target = target, }); exe.linkLibC(); diff --git a/test/link/interdependent_static_c_libs/build.zig b/test/link/interdependent_static_c_libs/build.zig index 01ed218cce..7b84235b0c 100644 --- a/test/link/interdependent_static_c_libs/build.zig +++ b/test/link/interdependent_static_c_libs/build.zig @@ -16,24 +16,24 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize .optimize = optimize, .target = b.host, }); - lib_a.addCSourceFile(.{ .file = .{ .path = "a.c" }, .flags = &[_][]const u8{} }); - lib_a.addIncludePath(.{ .path = "." }); + lib_a.addCSourceFile(.{ .file = b.path("a.c"), .flags = &[_][]const u8{} }); + lib_a.addIncludePath(b.path(".")); const lib_b = b.addStaticLibrary(.{ .name = "b", .optimize = optimize, .target = b.host, }); - lib_b.addCSourceFile(.{ .file = .{ .path = "b.c" }, .flags = &[_][]const u8{} }); - lib_b.addIncludePath(.{ .path = "." }); + lib_b.addCSourceFile(.{ .file = b.path("b.c"), .flags = &[_][]const u8{} }); + lib_b.addIncludePath(b.path(".")); const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, }); test_exe.linkLibrary(lib_a); test_exe.linkLibrary(lib_b); - test_exe.addIncludePath(.{ .path = "." }); + test_exe.addIncludePath(b.path(".")); test_step.dependOn(&b.addRunArtifact(test_exe).step); } diff --git a/test/link/macho.zig b/test/link/macho.zig index 9e91e14da3..4fc6f1b5f1 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -836,9 +836,9 @@ fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step { , .cpp_source_flags = &.{ "-nostdlib++", "-nostdinc++" }, }); - exe.root_module.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); - exe.root_module.addIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }) }); - exe.root_module.addObjectFile(.{ .path = b.pathJoin(&.{ sdk, "/usr/lib/libc++.tbd" }) }); + exe.root_module.addSystemIncludePath(b.path(b.pathJoin(&.{ sdk, "/usr/include" }))); + exe.root_module.addIncludePath(b.path(b.pathJoin(&.{ sdk, "/usr/include/c++/v1" }))); + exe.root_module.addObjectFile(b.path(b.pathJoin(&.{ sdk, "/usr/lib/libc++.tbd" }))); const check = exe.checkObject(); check.checkInSymtab(); diff --git a/test/link/static_libs_from_object_files/build.zig b/test/link/static_libs_from_object_files/build.zig index d0ea78bbd6..dd679e7ef8 100644 --- a/test/link/static_libs_from_object_files/build.zig +++ b/test/link/static_libs_from_object_files/build.zig @@ -57,7 +57,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built { const exe = b.addExecutable(.{ .name = "test1", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = b.host, }); @@ -93,7 +93,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built const exe = b.addExecutable(.{ .name = "test2", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = b.host, .optimize = optimize, }); @@ -134,7 +134,7 @@ fn add(b: *Build, test_step: *Step, files: []const LazyPath, optimize: std.built const exe = b.addExecutable(.{ .name = "test3", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = b.host, .optimize = optimize, }); diff --git a/test/link/wasm/archive/build.zig b/test/link/wasm/archive/build.zig index cd91feae65..34c5818ad8 100644 --- a/test/link/wasm/archive/build.zig +++ b/test/link/wasm/archive/build.zig @@ -17,7 +17,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize // and therefore link with its archive file. const lib = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .strip = false, diff --git a/test/link/wasm/basic-features/build.zig b/test/link/wasm/basic-features/build.zig index 6f9e82d09d..87355a5c12 100644 --- a/test/link/wasm/basic-features/build.zig +++ b/test/link/wasm/basic-features/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { // Library with explicitly set cpu features const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = .Debug, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, diff --git a/test/link/wasm/bss/build.zig b/test/link/wasm/bss/build.zig index 3a1592f394..dc7d1bae4b 100644 --- a/test/link/wasm/bss/build.zig +++ b/test/link/wasm/bss/build.zig @@ -16,7 +16,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize_mode, .strip = false, @@ -64,7 +64,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.Opt { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib2.zig" }, + .root_source_file = b.path("lib2.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize_mode, .strip = false, diff --git a/test/link/wasm/export-data/build.zig b/test/link/wasm/export-data/build.zig index 05b2ca161b..7e3c5e5841 100644 --- a/test/link/wasm/export-data/build.zig +++ b/test/link/wasm/export-data/build.zig @@ -11,7 +11,7 @@ pub fn build(b: *std.Build) void { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .optimize = .ReleaseSafe, // to make the output deterministic in address positions .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); diff --git a/test/link/wasm/export/build.zig b/test/link/wasm/export/build.zig index ab2893ce3c..368826843a 100644 --- a/test/link/wasm/export/build.zig +++ b/test/link/wasm/export/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const no_export = b.addExecutable(.{ .name = "no-export", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); @@ -25,7 +25,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const dynamic_export = b.addExecutable(.{ .name = "dynamic", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); @@ -36,7 +36,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const force_export = b.addExecutable(.{ .name = "force", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), }); diff --git a/test/link/wasm/extern-mangle/build.zig b/test/link/wasm/extern-mangle/build.zig index 41a00eefc9..5a4cbc1cda 100644 --- a/test/link/wasm/extern-mangle/build.zig +++ b/test/link/wasm/extern-mangle/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); diff --git a/test/link/wasm/extern/build.zig b/test/link/wasm/extern/build.zig index baa2b6d61e..77dcbc47c5 100644 --- a/test/link/wasm/extern/build.zig +++ b/test/link/wasm/extern/build.zig @@ -15,11 +15,11 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "extern", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .wasi }), }); - exe.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &.{} }); + exe.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} }); exe.use_llvm = false; exe.use_lld = false; diff --git a/test/link/wasm/function-table/build.zig b/test/link/wasm/function-table/build.zig index aca66e4f71..3042ddf5d5 100644 --- a/test/link/wasm/function-table/build.zig +++ b/test/link/wasm/function-table/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const import_table = b.addExecutable(.{ .name = "import_table", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); @@ -27,7 +27,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const export_table = b.addExecutable(.{ .name = "export_table", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); @@ -39,7 +39,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize const regular_table = b.addExecutable(.{ .name = "regular_table", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, }); diff --git a/test/link/wasm/infer-features/build.zig b/test/link/wasm/infer-features/build.zig index e3fe860f54..e72e3934f7 100644 --- a/test/link/wasm/infer-features/build.zig +++ b/test/link/wasm/infer-features/build.zig @@ -13,13 +13,13 @@ pub fn build(b: *std.Build) void { .os_tag = .freestanding, }), }); - c_obj.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &.{} }); + c_obj.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &.{} }); // Wasm library that doesn't have any features specified. This will // infer its featureset from other linked object files. const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = .Debug, .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, diff --git a/test/link/wasm/producers/build.zig b/test/link/wasm/producers/build.zig index 4fb777e123..2dae6e3f9b 100644 --- a/test/link/wasm/producers/build.zig +++ b/test/link/wasm/producers/build.zig @@ -16,7 +16,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, .strip = false, diff --git a/test/link/wasm/segments/build.zig b/test/link/wasm/segments/build.zig index 86073ff977..3c8bac3f07 100644 --- a/test/link/wasm/segments/build.zig +++ b/test/link/wasm/segments/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, .strip = false, diff --git a/test/link/wasm/shared-memory/build.zig b/test/link/wasm/shared-memory/build.zig index 0cad2560cb..7807a95a4f 100644 --- a/test/link/wasm/shared-memory/build.zig +++ b/test/link/wasm/shared-memory/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize_mode: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .cpu_model = .{ .explicit = &std.Target.wasm.cpu.mvp }, diff --git a/test/link/wasm/stack_pointer/build.zig b/test/link/wasm/stack_pointer/build.zig index e95c27827e..e42e362880 100644 --- a/test/link/wasm/stack_pointer/build.zig +++ b/test/link/wasm/stack_pointer/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const lib = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, .strip = false, diff --git a/test/link/wasm/type/build.zig b/test/link/wasm/type/build.zig index b62886c74e..46b80dbfe5 100644 --- a/test/link/wasm/type/build.zig +++ b/test/link/wasm/type/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "lib", - .root_source_file = .{ .path = "lib.zig" }, + .root_source_file = b.path("lib.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .wasm32, .os_tag = .freestanding }), .optimize = optimize, .strip = false, diff --git a/test/standalone/build.zig b/test/standalone/build.zig index 673a52cedd..b2575bc83e 100644 --- a/test/standalone/build.zig +++ b/test/standalone/build.zig @@ -44,7 +44,7 @@ pub fn build(b: *std.Build) void { }) |tool_src_path| { const tool = b.addTest(.{ .name = std.fs.path.stem(tool_src_path), - .root_source_file = .{ .path = tool_src_path }, + .root_source_file = b.path(tool_src_path), .optimize = .Debug, .target = tools_target, }); diff --git a/test/standalone/c_compiler/build.zig b/test/standalone/c_compiler/build.zig index 0550ad8cee..a3c842d8d5 100644 --- a/test/standalone/c_compiler/build.zig +++ b/test/standalone/c_compiler/build.zig @@ -30,7 +30,7 @@ fn add( .optimize = optimize, .target = target, }); - exe_c.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &[0][]const u8{} }); + exe_c.addCSourceFile(.{ .file = b.path("test.c"), .flags = &[0][]const u8{} }); exe_c.linkLibC(); const exe_cpp = b.addExecutable(.{ @@ -39,7 +39,7 @@ fn add( .target = target, }); b.default_step.dependOn(&exe_cpp.step); - exe_cpp.addCSourceFile(.{ .file = .{ .path = "test.cpp" }, .flags = &[0][]const u8{} }); + exe_cpp.addCSourceFile(.{ .file = b.path("test.cpp"), .flags = &[0][]const u8{} }); exe_cpp.linkLibCpp(); switch (target.result.os.tag) { diff --git a/test/standalone/child_process/build.zig b/test/standalone/child_process/build.zig index 89558c00e6..35317602b5 100644 --- a/test/standalone/child_process/build.zig +++ b/test/standalone/child_process/build.zig @@ -12,14 +12,14 @@ pub fn build(b: *std.Build) void { const child = b.addExecutable(.{ .name = "child", - .root_source_file = .{ .path = "child.zig" }, + .root_source_file = b.path("child.zig"), .optimize = optimize, .target = target, }); const main = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = target, }); diff --git a/test/standalone/cmakedefine/build.zig b/test/standalone/cmakedefine/build.zig index 967aa7ecbd..d90441360f 100644 --- a/test/standalone/cmakedefine/build.zig +++ b/test/standalone/cmakedefine/build.zig @@ -4,7 +4,7 @@ const ConfigHeader = std.Build.Step.ConfigHeader; pub fn build(b: *std.Build) void { const config_header = b.addConfigHeader( .{ - .style = .{ .cmake = .{ .path = "config.h.in" } }, + .style = .{ .cmake = b.path("config.h.in") }, .include_path = "config.h", }, .{ @@ -28,7 +28,7 @@ pub fn build(b: *std.Build) void { const pwd_sh = b.addConfigHeader( .{ - .style = .{ .cmake = .{ .path = "pwd.sh.in" } }, + .style = .{ .cmake = b.path("pwd.sh.in") }, .include_path = "pwd.sh", }, .{ .DIR = "${PWD}" }, @@ -36,7 +36,7 @@ pub fn build(b: *std.Build) void { const sigil_header = b.addConfigHeader( .{ - .style = .{ .cmake = .{ .path = "sigil.h.in" } }, + .style = .{ .cmake = b.path("sigil.h.in") }, .include_path = "sigil.h", }, .{}, @@ -44,7 +44,7 @@ pub fn build(b: *std.Build) void { const stack_header = b.addConfigHeader( .{ - .style = .{ .cmake = .{ .path = "stack.h.in" } }, + .style = .{ .cmake = b.path("stack.h.in") }, .include_path = "stack.h", }, .{ @@ -57,7 +57,7 @@ pub fn build(b: *std.Build) void { const wrapper_header = b.addConfigHeader( .{ - .style = .{ .cmake = .{ .path = "wrapper.h.in" } }, + .style = .{ .cmake = b.path("wrapper.h.in") }, .include_path = "wrapper.h", }, .{ diff --git a/test/standalone/compiler_rt_panic/build.zig b/test/standalone/compiler_rt_panic/build.zig index 8ad7732a93..93331a0282 100644 --- a/test/standalone/compiler_rt_panic/build.zig +++ b/test/standalone/compiler_rt_panic/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { }); exe.linkLibC(); exe.addCSourceFile(.{ - .file = .{ .path = "main.c" }, + .file = b.path("main.c"), .flags = &.{}, }); exe.link_gc_sections = false; diff --git a/test/standalone/dep_diamond/build.zig b/test/standalone/dep_diamond/build.zig index 9190b29594..b14503e349 100644 --- a/test/standalone/dep_diamond/build.zig +++ b/test/standalone/dep_diamond/build.zig @@ -7,21 +7,21 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const shared = b.createModule(.{ - .root_source_file = .{ .path = "shared.zig" }, + .root_source_file = b.path("shared.zig"), }); const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .target = b.host, .optimize = optimize, }); exe.root_module.addAnonymousImport("foo", .{ - .root_source_file = .{ .path = "foo.zig" }, + .root_source_file = b.path("foo.zig"), .imports = &.{.{ .name = "shared", .module = shared }}, }); exe.root_module.addAnonymousImport("bar", .{ - .root_source_file = .{ .path = "bar.zig" }, + .root_source_file = b.path("bar.zig"), .imports = &.{.{ .name = "shared", .module = shared }}, }); diff --git a/test/standalone/dep_mutually_recursive/build.zig b/test/standalone/dep_mutually_recursive/build.zig index 04589d9b5b..804abbce18 100644 --- a/test/standalone/dep_mutually_recursive/build.zig +++ b/test/standalone/dep_mutually_recursive/build.zig @@ -7,17 +7,17 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.createModule(.{ - .root_source_file = .{ .path = "foo.zig" }, + .root_source_file = b.path("foo.zig"), }); const bar = b.createModule(.{ - .root_source_file = .{ .path = "bar.zig" }, + .root_source_file = b.path("bar.zig"), }); foo.addImport("bar", bar); bar.addImport("foo", foo); const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .target = b.host, .optimize = optimize, }); diff --git a/test/standalone/dep_recursive/build.zig b/test/standalone/dep_recursive/build.zig index a6334e6a97..0ab732db4b 100644 --- a/test/standalone/dep_recursive/build.zig +++ b/test/standalone/dep_recursive/build.zig @@ -7,13 +7,13 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const foo = b.createModule(.{ - .root_source_file = .{ .path = "foo.zig" }, + .root_source_file = b.path("foo.zig"), }); foo.addImport("foo", foo); const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .target = b.host, .optimize = optimize, }); diff --git a/test/standalone/dep_shared_builtin/build.zig b/test/standalone/dep_shared_builtin/build.zig index 33d53ad166..de84e2848d 100644 --- a/test/standalone/dep_shared_builtin/build.zig +++ b/test/standalone/dep_shared_builtin/build.zig @@ -8,12 +8,12 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .target = b.host, .optimize = optimize, }); exe.root_module.addAnonymousImport("foo", .{ - .root_source_file = .{ .path = "foo.zig" }, + .root_source_file = b.path("foo.zig"), }); const run = b.addRunArtifact(exe); diff --git a/test/standalone/dep_triangle/build.zig b/test/standalone/dep_triangle/build.zig index b155554997..f9f29099d5 100644 --- a/test/standalone/dep_triangle/build.zig +++ b/test/standalone/dep_triangle/build.zig @@ -7,17 +7,17 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const shared = b.createModule(.{ - .root_source_file = .{ .path = "shared.zig" }, + .root_source_file = b.path("shared.zig"), }); const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .target = b.host, .optimize = optimize, }); exe.root_module.addAnonymousImport("foo", .{ - .root_source_file = .{ .path = "foo.zig" }, + .root_source_file = b.path("foo.zig"), .imports = &.{.{ .name = "shared", .module = shared }}, }); exe.root_module.addImport("shared", shared); diff --git a/test/standalone/depend_on_main_mod/build.zig b/test/standalone/depend_on_main_mod/build.zig index bbef64693e..42e96e0aa0 100644 --- a/test/standalone/depend_on_main_mod/build.zig +++ b/test/standalone/depend_on_main_mod/build.zig @@ -9,13 +9,13 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "depend_on_main_mod", - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = target, .optimize = optimize, }); const foo_module = b.addModule("foo", .{ - .root_source_file = .{ .path = "src/foo.zig" }, + .root_source_file = b.path("src/foo.zig"), }); foo_module.addImport("root2", &exe.root_module); diff --git a/test/standalone/dirname/build.zig b/test/standalone/dirname/build.zig index 272ed54b38..279f66093a 100644 --- a/test/standalone/dirname/build.zig +++ b/test/standalone/dirname/build.zig @@ -6,9 +6,7 @@ pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test it"); b.default_step = test_step; - const touch_src = std.Build.LazyPath{ - .path = "touch.zig", - }; + const touch_src = b.path("touch.zig"); const touch = b.addExecutable(.{ .name = "touch", @@ -20,14 +18,14 @@ pub fn build(b: *std.Build) void { const exists_in = b.addExecutable(.{ .name = "exists_in", - .root_source_file = .{ .path = "exists_in.zig" }, + .root_source_file = b.path("exists_in.zig"), .optimize = .Debug, .target = target, }); const has_basename = b.addExecutable(.{ .name = "has_basename", - .root_source_file = .{ .path = "has_basename.zig" }, + .root_source_file = b.path("has_basename.zig"), .optimize = .Debug, .target = target, }); diff --git a/test/standalone/embed_generated_file/build.zig b/test/standalone/embed_generated_file/build.zig index 3f67cdea54..f7430b7716 100644 --- a/test/standalone/embed_generated_file/build.zig +++ b/test/standalone/embed_generated_file/build.zig @@ -6,7 +6,7 @@ pub fn build(b: *std.Build) void { const bootloader = b.addExecutable(.{ .name = "bootloader", - .root_source_file = .{ .path = "bootloader.zig" }, + .root_source_file = b.path("bootloader.zig"), .target = b.resolveTargetQuery(.{ .cpu_arch = .x86, .os_tag = .freestanding, @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { }); const exe = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = .Debug, }); exe.root_module.addAnonymousImport("bootloader.elf", .{ diff --git a/test/standalone/empty_env/build.zig b/test/standalone/empty_env/build.zig index a2fe483128..b8e488f830 100644 --- a/test/standalone/empty_env/build.zig +++ b/test/standalone/empty_env/build.zig @@ -14,7 +14,7 @@ pub fn build(b: *std.Build) void { const main = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = b.host, .optimize = optimize, }); diff --git a/test/standalone/extern/build.zig b/test/standalone/extern/build.zig index 401deb3dc4..878b92ab58 100644 --- a/test/standalone/extern/build.zig +++ b/test/standalone/extern/build.zig @@ -5,12 +5,12 @@ pub fn build(b: *std.Build) void { const obj = b.addObject(.{ .name = "exports", - .root_source_file = .{ .path = "exports.zig" }, + .root_source_file = b.path("exports.zig"), .target = b.host, .optimize = optimize, }); const main = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, }); main.addObject(obj); diff --git a/test/standalone/global_linkage/build.zig b/test/standalone/global_linkage/build.zig index 13b7fcfa0e..9edbc96129 100644 --- a/test/standalone/global_linkage/build.zig +++ b/test/standalone/global_linkage/build.zig @@ -9,20 +9,20 @@ pub fn build(b: *std.Build) void { const obj1 = b.addStaticLibrary(.{ .name = "obj1", - .root_source_file = .{ .path = "obj1.zig" }, + .root_source_file = b.path("obj1.zig"), .optimize = optimize, .target = target, }); const obj2 = b.addStaticLibrary(.{ .name = "obj2", - .root_source_file = .{ .path = "obj2.zig" }, + .root_source_file = b.path("obj2.zig"), .optimize = optimize, .target = target, }); const main = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, }); main.linkLibrary(obj1); diff --git a/test/standalone/install_headers/build.zig b/test/standalone/install_headers/build.zig index 4c9bbb501a..889ee717d6 100644 --- a/test/standalone/install_headers/build.zig +++ b/test/standalone/install_headers/build.zig @@ -32,7 +32,7 @@ pub fn build(b: *std.Build) void { \\} ) }); - libfoo.installHeadersDirectory(.{ .path = "include" }, "foo", .{ .exclude_extensions = &.{".ignore_me.h"} }); + libfoo.installHeadersDirectory(b.path("include"), "foo", .{ .exclude_extensions = &.{".ignore_me.h"} }); libfoo.installHeader(b.addWriteFiles().add("d.h", \\#define FOO_D "D" \\ @@ -78,7 +78,7 @@ pub fn build(b: *std.Build) void { }); const check_exists = b.addExecutable(.{ .name = "check_exists", - .root_source_file = .{ .path = "check_exists.zig" }, + .root_source_file = b.path("check_exists.zig"), .target = b.resolveTargetQuery(.{}), .optimize = .Debug, }); diff --git a/test/standalone/install_raw_hex/build.zig b/test/standalone/install_raw_hex/build.zig index d1ec55ab53..515528534e 100644 --- a/test/standalone/install_raw_hex/build.zig +++ b/test/standalone/install_raw_hex/build.zig @@ -16,7 +16,7 @@ pub fn build(b: *std.Build) void { const elf = b.addExecutable(.{ .name = "zig-nrf52-blink.elf", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = target, .optimize = optimize, }); diff --git a/test/standalone/ios/build.zig b/test/standalone/ios/build.zig index 356f12a3d9..daabe8990d 100644 --- a/test/standalone/ios/build.zig +++ b/test/standalone/ios/build.zig @@ -21,10 +21,10 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .target = target, }); - exe.addCSourceFile(.{ .file = .{ .path = "main.m" }, .flags = &.{} }); - exe.addSystemIncludePath(.{ .path = b.pathJoin(&.{ sdk, "/usr/include" }) }); - exe.addSystemFrameworkPath(.{ .path = b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }) }); - exe.addLibraryPath(.{ .path = b.pathJoin(&.{ sdk, "/usr/lib" }) }); + exe.addCSourceFile(.{ .file = b.path("main.m"), .flags = &.{} }); + exe.addSystemIncludePath(b.path(b.pathJoin(&.{ sdk, "/usr/include" }))); + exe.addSystemFrameworkPath(b.path(b.pathJoin(&.{ sdk, "/System/Library/Frameworks" }))); + exe.addLibraryPath(b.path(b.pathJoin(&.{ sdk, "/usr/lib" }))); exe.linkFramework("Foundation"); exe.linkFramework("UIKit"); exe.linkLibC(); diff --git a/test/standalone/issue_11595/build.zig b/test/standalone/issue_11595/build.zig index c591b3058b..b6e17f1eef 100644 --- a/test/standalone/issue_11595/build.zig +++ b/test/standalone/issue_11595/build.zig @@ -15,7 +15,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "zigtest", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = target, .optimize = optimize, }); diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig index 9a80dae256..528eba89d3 100644 --- a/test/standalone/issue_12706/build.zig +++ b/test/standalone/issue_12706/build.zig @@ -10,7 +10,7 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = target, }); diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index 5dc686a779..6327c5ed97 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void { const obj = b.addObject(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .target = target, .optimize = optimize, }); diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig index eb05aa9b4f..d42ff1f304 100644 --- a/test/standalone/issue_794/build.zig +++ b/test/standalone/issue_794/build.zig @@ -5,9 +5,9 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const test_artifact = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), }); - test_artifact.addIncludePath(.{ .path = "a_directory" }); + test_artifact.addIncludePath(b.path("a_directory")); // TODO: actually check the output _ = test_artifact.getEmittedBin(); diff --git a/test/standalone/issue_8550/build.zig b/test/standalone/issue_8550/build.zig index 557019cfdc..a0dea518d6 100644 --- a/test/standalone/issue_8550/build.zig +++ b/test/standalone/issue_8550/build.zig @@ -15,12 +15,12 @@ pub fn build(b: *std.Build) !void { const kernel = b.addExecutable(.{ .name = "kernel", - .root_source_file = .{ .path = "./main.zig" }, + .root_source_file = b.path("./main.zig"), .optimize = optimize, .target = target, }); - kernel.addObjectFile(.{ .path = "./boot.S" }); - kernel.setLinkerScript(.{ .path = "./linker.ld" }); + kernel.addObjectFile(b.path("./boot.S")); + kernel.setLinkerScript(b.path("./linker.ld")); b.installArtifact(kernel); test_step.dependOn(&kernel.step); diff --git a/test/standalone/load_dynamic_library/build.zig b/test/standalone/load_dynamic_library/build.zig index 140f276ebe..27f8f6de7c 100644 --- a/test/standalone/load_dynamic_library/build.zig +++ b/test/standalone/load_dynamic_library/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { const lib = b.addSharedLibrary(.{ .name = "add", - .root_source_file = .{ .path = "add.zig" }, + .root_source_file = b.path("add.zig"), .version = .{ .major = 1, .minor = 0, .patch = 0 }, .optimize = optimize, .target = target, @@ -25,7 +25,7 @@ pub fn build(b: *std.Build) void { const main = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = target, }); diff --git a/test/standalone/mix_c_files/build.zig b/test/standalone/mix_c_files/build.zig index e91f87e132..779d4d030e 100644 --- a/test/standalone/mix_c_files/build.zig +++ b/test/standalone/mix_c_files/build.zig @@ -18,11 +18,11 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = b.host, .optimize = optimize, }); - exe.addCSourceFile(.{ .file = .{ .path = "test.c" }, .flags = &[_][]const u8{"-std=c11"} }); + exe.addCSourceFile(.{ .file = b.path("test.c"), .flags = &[_][]const u8{"-std=c11"} }); exe.linkLibC(); const run_cmd = b.addRunArtifact(exe); diff --git a/test/standalone/mix_o_files/build.zig b/test/standalone/mix_o_files/build.zig index aa648145a8..dd8ef9a4c4 100644 --- a/test/standalone/mix_o_files/build.zig +++ b/test/standalone/mix_o_files/build.zig @@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void { const obj = b.addObject(.{ .name = "base64", - .root_source_file = .{ .path = "base64.zig" }, + .root_source_file = b.path("base64.zig"), .optimize = optimize, .target = target, }); @@ -20,7 +20,7 @@ pub fn build(b: *std.Build) void { .target = target, }); exe.addCSourceFile(.{ - .file = .{ .path = "test.c" }, + .file = b.path("test.c"), .flags = &[_][]const u8{"-std=c99"}, }); exe.addObject(obj); diff --git a/test/standalone/pie/build.zig b/test/standalone/pie/build.zig index 2e9f99ff4b..25ed330abc 100644 --- a/test/standalone/pie/build.zig +++ b/test/standalone/pie/build.zig @@ -11,7 +11,7 @@ pub fn build(b: *std.Build) void { }); const main = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = target, }); diff --git a/test/standalone/pkg_import/build.zig b/test/standalone/pkg_import/build.zig index 207f924f9b..4bef1d756f 100644 --- a/test/standalone/pkg_import/build.zig +++ b/test/standalone/pkg_import/build.zig @@ -8,11 +8,11 @@ pub fn build(b: *std.Build) void { const exe = b.addExecutable(.{ .name = "test", - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), .optimize = optimize, .target = b.host, }); - exe.root_module.addAnonymousImport("my_pkg", .{ .root_source_file = .{ .path = "pkg.zig" } }); + exe.root_module.addAnonymousImport("my_pkg", .{ .root_source_file = b.path("pkg.zig") }); const run = b.addRunArtifact(exe); test_step.dependOn(&run.step); diff --git a/test/standalone/self_exe_symlink/build.zig b/test/standalone/self_exe_symlink/build.zig index d61d502574..77799cfa00 100644 --- a/test/standalone/self_exe_symlink/build.zig +++ b/test/standalone/self_exe_symlink/build.zig @@ -15,14 +15,14 @@ pub fn build(b: *std.Build) void { const main = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = target, }); const create_symlink_exe = b.addExecutable(.{ .name = "create-symlink", - .root_source_file = .{ .path = "create-symlink.zig" }, + .root_source_file = b.path("create-symlink.zig"), .optimize = optimize, .target = target, }); diff --git a/test/standalone/shared_library/build.zig b/test/standalone/shared_library/build.zig index 4d409295e8..2765a2ec07 100644 --- a/test/standalone/shared_library/build.zig +++ b/test/standalone/shared_library/build.zig @@ -13,7 +13,7 @@ pub fn build(b: *std.Build) void { const target = b.host; const lib = b.addSharedLibrary(.{ .name = "mathtest", - .root_source_file = .{ .path = "mathtest.zig" }, + .root_source_file = b.path("mathtest.zig"), .version = .{ .major = 1, .minor = 0, .patch = 0 }, .target = target, .optimize = optimize, @@ -25,7 +25,7 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); exe.addCSourceFile(.{ - .file = .{ .path = "test.c" }, + .file = b.path("test.c"), .flags = &[_][]const u8{"-std=c99"}, }); exe.linkLibrary(lib); diff --git a/test/standalone/simple/build.zig b/test/standalone/simple/build.zig index 8bcfbe0fca..c623780fa5 100644 --- a/test/standalone/simple/build.zig +++ b/test/standalone/simple/build.zig @@ -42,7 +42,7 @@ pub fn build(b: *std.Build) void { if (case.is_exe) { const exe = b.addExecutable(.{ .name = std.fs.path.stem(case.src_path), - .root_source_file = .{ .path = case.src_path }, + .root_source_file = b.path(case.src_path), .optimize = optimize, .target = resolved_target, }); @@ -56,7 +56,7 @@ pub fn build(b: *std.Build) void { if (case.is_test) { const exe = b.addTest(.{ .name = std.fs.path.stem(case.src_path), - .root_source_file = .{ .path = case.src_path }, + .root_source_file = b.path(case.src_path), .optimize = optimize, .target = resolved_target, }); diff --git a/test/standalone/stack_iterator/build.zig b/test/standalone/stack_iterator/build.zig index 463a533ac4..7041aaa0b8 100644 --- a/test/standalone/stack_iterator/build.zig +++ b/test/standalone/stack_iterator/build.zig @@ -19,7 +19,7 @@ pub fn build(b: *std.Build) void { { const exe = b.addExecutable(.{ .name = "unwind_fp", - .root_source_file = .{ .path = "unwind.zig" }, + .root_source_file = b.path("unwind.zig"), .target = target, .optimize = optimize, .unwind_tables = if (target.result.isDarwin()) true else null, @@ -42,7 +42,7 @@ pub fn build(b: *std.Build) void { { const exe = b.addExecutable(.{ .name = "unwind_nofp", - .root_source_file = .{ .path = "unwind.zig" }, + .root_source_file = b.path("unwind.zig"), .target = target, .optimize = optimize, .unwind_tables = true, @@ -74,14 +74,14 @@ pub fn build(b: *std.Build) void { c_shared_lib.defineCMacro("LIB_API", "__declspec(dllexport)"); c_shared_lib.addCSourceFile(.{ - .file = .{ .path = "shared_lib.c" }, + .file = b.path("shared_lib.c"), .flags = &.{"-fomit-frame-pointer"}, }); c_shared_lib.linkLibC(); const exe = b.addExecutable(.{ .name = "shared_lib_unwind", - .root_source_file = .{ .path = "shared_lib_unwind.zig" }, + .root_source_file = b.path("shared_lib_unwind.zig"), .target = target, .optimize = optimize, .unwind_tables = if (target.result.isDarwin()) true else null, diff --git a/test/standalone/static_c_lib/build.zig b/test/standalone/static_c_lib/build.zig index 244107b0f1..c025aa9b39 100644 --- a/test/standalone/static_c_lib/build.zig +++ b/test/standalone/static_c_lib/build.zig @@ -11,15 +11,15 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .target = b.host, }); - foo.addCSourceFile(.{ .file = .{ .path = "foo.c" }, .flags = &[_][]const u8{} }); - foo.addIncludePath(.{ .path = "." }); + foo.addCSourceFile(.{ .file = b.path("foo.c"), .flags = &[_][]const u8{} }); + foo.addIncludePath(b.path(".")); const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "foo.zig" }, + .root_source_file = b.path("foo.zig"), .optimize = optimize, }); test_exe.linkLibrary(foo); - test_exe.addIncludePath(.{ .path = "." }); + test_exe.addIncludePath(b.path(".")); test_step.dependOn(&b.addRunArtifact(test_exe).step); } diff --git a/test/standalone/strip_empty_loop/build.zig b/test/standalone/strip_empty_loop/build.zig index 4875bd9128..aadfbd2fc0 100644 --- a/test/standalone/strip_empty_loop/build.zig +++ b/test/standalone/strip_empty_loop/build.zig @@ -9,7 +9,7 @@ pub fn build(b: *std.Build) void { const main = b.addExecutable(.{ .name = "main", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .target = target, .strip = true, diff --git a/test/standalone/strip_struct_init/build.zig b/test/standalone/strip_struct_init/build.zig index 2d903f973a..ef7960d130 100644 --- a/test/standalone/strip_struct_init/build.zig +++ b/test/standalone/strip_struct_init/build.zig @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const main = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, .strip = true, }); diff --git a/test/standalone/test_runner_module_imports/build.zig b/test/standalone/test_runner_module_imports/build.zig index af8e68d717..cad3b05e08 100644 --- a/test/standalone/test_runner_module_imports/build.zig +++ b/test/standalone/test_runner_module_imports/build.zig @@ -2,13 +2,13 @@ const std = @import("std"); pub fn build(b: *std.Build) void { const t = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, - .test_runner = "test_runner/main.zig", + .root_source_file = b.path("src/main.zig"), + .test_runner = b.path("test_runner/main.zig"), }); - const module1 = b.createModule(.{ .root_source_file = .{ .path = "module1/main.zig" } }); + const module1 = b.createModule(.{ .root_source_file = b.path("module1/main.zig") }); const module2 = b.createModule(.{ - .root_source_file = .{ .path = "module2/main.zig" }, + .root_source_file = b.path("module2/main.zig"), .imports = &.{.{ .name = "module1", .module = module1 }}, }); diff --git a/test/standalone/test_runner_path/build.zig b/test/standalone/test_runner_path/build.zig index 352a18efe0..48ca3c19db 100644 --- a/test/standalone/test_runner_path/build.zig +++ b/test/standalone/test_runner_path/build.zig @@ -7,9 +7,9 @@ pub fn build(b: *std.Build) void { b.default_step = test_step; const test_exe = b.addTest(.{ - .root_source_file = .{ .path = "test.zig" }, + .root_source_file = b.path("test.zig"), }); - test_exe.test_runner = "test_runner.zig"; + test_exe.test_runner = b.path("test_runner.zig"); const test_run = b.addRunArtifact(test_exe); test_step.dependOn(&test_run.step); diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index 0511cd3935..7ee501713c 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -7,10 +7,10 @@ pub fn build(b: *std.Build) void { const optimize: std.builtin.OptimizeMode = .Debug; const main = b.addTest(.{ - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .optimize = optimize, }); - main.addIncludePath(.{ .path = "." }); + main.addIncludePath(b.path(".")); test_step.dependOn(&b.addRunArtifact(main).step); } diff --git a/test/standalone/windows_entry_points/build.zig b/test/standalone/windows_entry_points/build.zig index 25c4839147..c3d9c49940 100644 --- a/test/standalone/windows_entry_points/build.zig +++ b/test/standalone/windows_entry_points/build.zig @@ -17,7 +17,7 @@ pub fn build(b: *std.Build) void { .optimize = .Debug, .link_libc = true, }); - exe.addCSourceFile(.{ .file = .{ .path = "main.c" } }); + exe.addCSourceFile(.{ .file = b.path("main.c") }); _ = exe.getEmittedBin(); test_step.dependOn(&exe.step); @@ -31,7 +31,7 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); exe.mingw_unicode_entry_point = true; - exe.addCSourceFile(.{ .file = .{ .path = "wmain.c" } }); + exe.addCSourceFile(.{ .file = b.path("wmain.c") }); _ = exe.getEmittedBin(); test_step.dependOn(&exe.step); @@ -45,7 +45,7 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); // Note: `exe.subsystem = .Windows;` is not necessary - exe.addCSourceFile(.{ .file = .{ .path = "winmain.c" } }); + exe.addCSourceFile(.{ .file = b.path("winmain.c") }); _ = exe.getEmittedBin(); test_step.dependOn(&exe.step); @@ -60,7 +60,7 @@ pub fn build(b: *std.Build) void { }); exe.mingw_unicode_entry_point = true; // Note: `exe.subsystem = .Windows;` is not necessary - exe.addCSourceFile(.{ .file = .{ .path = "wwinmain.c" } }); + exe.addCSourceFile(.{ .file = b.path("wwinmain.c") }); _ = exe.getEmittedBin(); test_step.dependOn(&exe.step); diff --git a/test/standalone/windows_resources/build.zig b/test/standalone/windows_resources/build.zig index 5111751571..6a72dee2a6 100644 --- a/test/standalone/windows_resources/build.zig +++ b/test/standalone/windows_resources/build.zig @@ -25,12 +25,12 @@ fn add( ) void { const exe = b.addExecutable(.{ .name = "zig_resource_test", - .root_source_file = .{ .path = "main.zig" }, + .root_source_file = b.path("main.zig"), .target = target, .optimize = .Debug, }); exe.addWin32ResourceFile(.{ - .file = .{ .path = "res/zig.rc" }, + .file = b.path("res/zig.rc"), .flags = &.{"/c65001"}, // UTF-8 code page }); exe.rc_includes = switch (rc_includes) { diff --git a/test/standalone/zerolength_check/build.zig b/test/standalone/zerolength_check/build.zig index dacfc841c1..8118c6e172 100644 --- a/test/standalone/zerolength_check/build.zig +++ b/test/standalone/zerolength_check/build.zig @@ -12,7 +12,7 @@ pub fn build(b: *std.Build) void { fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.OptimizeMode) void { const unit_tests = b.addTest(.{ - .root_source_file = .{ .path = "src/main.zig" }, + .root_source_file = b.path("src/main.zig"), .target = b.resolveTargetQuery(.{ .os_tag = .wasi, .cpu_arch = .wasm32, diff --git a/test/tests.zig b/test/tests.zig index 286ac7e91c..d18958cfc6 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -641,7 +641,7 @@ pub fn addStackTraceTests( ) *Step { const check_exe = b.addExecutable(.{ .name = "check-stack-trace", - .root_source_file = .{ .path = "test/src/check-stack-trace.zig" }, + .root_source_file = b.path("test/src/check-stack-trace.zig"), .target = b.host, .optimize = .Debug, }); @@ -879,7 +879,7 @@ pub fn addCliTests(b: *std.Build) *Step { run6.step.dependOn(&write6.step); // TODO change this to an exact match - const check6 = b.addCheckFile(.{ .path = fmt6_path }, .{ + const check6 = b.addCheckFile(.{ .cwd_relative = fmt6_path }, .{ .expected_matches = &.{ "// no reason", }, @@ -1037,7 +1037,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { options.max_rss; const these_tests = b.addTest(.{ - .root_source_file = .{ .path = options.root_src }, + .root_source_file = b.path(options.root_src), .optimize = test_target.optimize_mode, .target = resolved_target, .max_rss = max_rss, @@ -1046,7 +1046,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { .single_threaded = test_target.single_threaded, .use_llvm = test_target.use_llvm, .use_lld = test_target.use_lld, - .zig_lib_dir = .{ .path = "lib" }, + .zig_lib_dir = b.path("lib"), .pic = test_target.pic, .strip = test_target.strip, }); @@ -1062,7 +1062,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { const use_lld = if (test_target.use_lld == false) "-no-lld" else ""; const use_pic = if (test_target.pic == true) "-pic" else ""; - for (options.include_paths) |include_path| these_tests.addIncludePath(.{ .path = include_path }); + for (options.include_paths) |include_path| these_tests.addIncludePath(b.path(include_path)); const qualified_name = b.fmt("{s}-{s}-{s}-{s}{s}{s}{s}{s}{s}", .{ options.name, @@ -1084,7 +1084,7 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { .name = qualified_name, .link_libc = test_target.link_libc, .target = b.resolveTargetQuery(altered_query), - .zig_lib_dir = .{ .path = "lib" }, + .zig_lib_dir = b.path("lib"), }); compile_c.addCSourceFile(.{ .file = these_tests.getEmittedBin(), @@ -1092,9 +1092,17 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { // Tracking issue for making the C backend generate C89 compatible code: // https://github.com/ziglang/zig/issues/19468 "-std=c99", - "-pedantic", "-Werror", + "-Wall", + "-Wembedded-directive", + "-Wempty-translation-unit", + "-Wextra", + "-Wgnu", + "-Winvalid-utf8", + "-Wkeyword-macro", + "-Woverlength-strings", + // Tracking issue for making the C backend generate code // that does not trigger warnings: // https://github.com/ziglang/zig/issues/19467 @@ -1103,17 +1111,17 @@ pub fn addModuleTests(b: *std.Build, options: ModuleTestOptions) *Step { "-Wno-builtin-requires-header", // spotted on linux - "-Wno-gnu-folding-constant", - "-Wno-incompatible-function-pointer-types", - "-Wno-incompatible-pointer-types", - "-Wno-overlength-strings", + "-Wno-braced-scalar-init", + "-Wno-excess-initializers", + "-Wno-incompatible-pointer-types-discards-qualifiers", + "-Wno-unused", + "-Wno-unused-parameter", // spotted on darwin - "-Wno-dollar-in-identifier-extension", - "-Wno-absolute-value", + "-Wno-incompatible-pointer-types", }, }); - compile_c.addIncludePath(.{ .path = "lib" }); // for zig.h + compile_c.addIncludePath(b.path("lib")); // for zig.h if (target.os.tag == .windows) { if (true) { // Unfortunately this requires about 8G of RAM for clang to compile @@ -1189,7 +1197,7 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S if (c_abi_target.use_lld == false) "-no-lld" else "", if (c_abi_target.pic == true) "-pic" else "", }), - .root_source_file = .{ .path = "test/c_abi/main.zig" }, + .root_source_file = b.path("test/c_abi/main.zig"), .target = resolved_target, .optimize = optimize_mode, .link_libc = true, @@ -1199,7 +1207,7 @@ pub fn addCAbiTests(b: *std.Build, skip_non_native: bool, skip_release: bool) *S .strip = c_abi_target.strip, }); test_step.addCSourceFile(.{ - .file = .{ .path = "test/c_abi/cfuncs.c" }, + .file = b.path("test/c_abi/cfuncs.c"), .flags = &.{"-std=c99"}, }); for (c_abi_target.c_defines) |define| test_step.defineCMacro(define, null); diff --git a/tools/update_crc_catalog.zig b/tools/update_crc_catalog.zig index 0e8d9b3ef8..31d0b55e1b 100644 --- a/tools/update_crc_catalog.zig +++ b/tools/update_crc_catalog.zig @@ -23,11 +23,15 @@ pub fn main() anyerror!void { var zig_src_dir = try fs.cwd().openDir(zig_src_root, .{}); defer zig_src_dir.close(); - const target_sub_path = try fs.path.join(arena, &.{ "lib", "std", "hash", "crc" }); - var target_dir = try zig_src_dir.makeOpenPath(target_sub_path, .{}); - defer target_dir.close(); + const hash_sub_path = try fs.path.join(arena, &.{ "lib", "std", "hash" }); + var hash_target_dir = try zig_src_dir.makeOpenPath(hash_sub_path, .{}); + defer hash_target_dir.close(); - var zig_code_file = try target_dir.createFile("catalog.zig", .{}); + const crc_sub_path = try fs.path.join(arena, &.{ "lib", "std", "hash", "crc" }); + var crc_target_dir = try zig_src_dir.makeOpenPath(crc_sub_path, .{}); + defer crc_target_dir.close(); + + var zig_code_file = try hash_target_dir.createFile("crc.zig", .{}); defer zig_code_file.close(); var cbw = std.io.bufferedWriter(zig_code_file.writer()); @@ -37,15 +41,23 @@ pub fn main() anyerror!void { try code_writer.writeAll( \\//! This file is auto-generated by tools/update_crc_catalog.zig. \\ - \\const Crc = @import("../crc.zig").Crc; + \\const impl = @import("crc/impl.zig"); + \\ + \\pub const Crc = impl.Crc; + \\pub const Polynomial = impl.Polynomial; + \\pub const Crc32WithPoly = impl.Crc32WithPoly; + \\pub const Crc32SmallWithPoly = impl.Crc32SmallWithPoly; + \\ + \\// IEEE is by far the most common CRC and so is aliased by default. + \\pub const Crc32 = Crc32WithPoly(.IEEE); \\ \\test { - \\ _ = @import("catalog_test.zig"); + \\ _ = @import("crc/test.zig"); \\} \\ ); - var zig_test_file = try target_dir.createFile("catalog_test.zig", .{}); + var zig_test_file = try crc_target_dir.createFile("test.zig", .{}); defer zig_test_file.close(); var tbw = std.io.bufferedWriter(zig_test_file.writer()); @@ -57,7 +69,26 @@ pub fn main() anyerror!void { \\ \\const std = @import("std"); \\const testing = std.testing; - \\const catalog = @import("catalog.zig"); + \\const verify = @import("../verify.zig"); + \\const crc = @import("../crc.zig"); + \\ + \\test "crc32 ieee" { + \\ inline for ([2]type{ crc.Crc32WithPoly(.IEEE), crc.Crc32SmallWithPoly(.IEEE) }) |ieee| { + \\ try testing.expect(ieee.hash("") == 0x00000000); + \\ try testing.expect(ieee.hash("a") == 0xe8b7be43); + \\ try testing.expect(ieee.hash("abc") == 0x352441c2); + \\ try verify.iterativeApi(ieee); + \\ } + \\} + \\ + \\test "crc32 castagnoli" { + \\ inline for ([2]type{ crc.Crc32WithPoly(.Castagnoli), crc.Crc32SmallWithPoly(.Castagnoli) }) |casta| { + \\ try testing.expect(casta.hash("") == 0x00000000); + \\ try testing.expect(casta.hash("a") == 0xc1d04330); + \\ try testing.expect(casta.hash("abc") == 0x364b3fb7); + \\ try verify.iterativeApi(casta); + \\ } + \\} \\ ); @@ -146,7 +177,7 @@ pub fn main() anyerror!void { try test_writer.writeAll(try std.fmt.allocPrint(arena, \\ \\test "{0s}" {{ - \\ const {1s} = catalog.{1s}; + \\ const {1s} = crc.{1s}; \\ \\ try testing.expectEqual(@as(u{2s}, {3s}), {1s}.hash("123456789")); \\ |
