Merge remote-tracking branch 'origin/master' into llvm7

This commit is contained in:
Andrew Kelley 2018-06-14 18:27:59 -04:00
commit 32dd98b19f
93 changed files with 5388 additions and 2155 deletions

View File

@ -464,6 +464,7 @@ set(ZIG_STD_FILES
"math/atan.zig" "math/atan.zig"
"math/atan2.zig" "math/atan2.zig"
"math/atanh.zig" "math/atanh.zig"
"math/big/int.zig"
"math/cbrt.zig" "math/cbrt.zig"
"math/ceil.zig" "math/ceil.zig"
"math/complex/abs.zig" "math/complex/abs.zig"
@ -555,6 +556,7 @@ set(ZIG_STD_FILES
"special/compiler_rt/aulldiv.zig" "special/compiler_rt/aulldiv.zig"
"special/compiler_rt/aullrem.zig" "special/compiler_rt/aullrem.zig"
"special/compiler_rt/comparetf2.zig" "special/compiler_rt/comparetf2.zig"
"special/compiler_rt/divti3.zig"
"special/compiler_rt/fixuint.zig" "special/compiler_rt/fixuint.zig"
"special/compiler_rt/fixunsdfdi.zig" "special/compiler_rt/fixunsdfdi.zig"
"special/compiler_rt/fixunsdfsi.zig" "special/compiler_rt/fixunsdfsi.zig"
@ -565,6 +567,7 @@ set(ZIG_STD_FILES
"special/compiler_rt/fixunstfdi.zig" "special/compiler_rt/fixunstfdi.zig"
"special/compiler_rt/fixunstfsi.zig" "special/compiler_rt/fixunstfsi.zig"
"special/compiler_rt/fixunstfti.zig" "special/compiler_rt/fixunstfti.zig"
"special/compiler_rt/muloti4.zig"
"special/compiler_rt/index.zig" "special/compiler_rt/index.zig"
"special/compiler_rt/udivmod.zig" "special/compiler_rt/udivmod.zig"
"special/compiler_rt/udivmoddi4.zig" "special/compiler_rt/udivmoddi4.zig"

View File

@ -55,18 +55,18 @@ that counts as "freestanding" for the purposes of this table.
|i386 | OK | planned | OK | planned | planned | |i386 | OK | planned | OK | planned | planned |
|x86_64 | OK | OK | OK | OK | planned | |x86_64 | OK | OK | OK | OK | planned |
|arm | OK | planned | planned | N/A | planned | |arm | OK | planned | planned | N/A | planned |
|aarch64 | OK | planned | planned | planned | planned | |aarch64 | OK | planned | N/A | planned | planned |
|bpf | OK | planned | planned | N/A | planned | |bpf | OK | planned | N/A | N/A | planned |
|hexagon | OK | planned | planned | N/A | planned | |hexagon | OK | planned | N/A | N/A | planned |
|mips | OK | planned | planned | N/A | planned | |mips | OK | planned | N/A | N/A | planned |
|powerpc | OK | planned | planned | N/A | planned | |powerpc | OK | planned | N/A | N/A | planned |
|r600 | OK | planned | planned | N/A | planned | |r600 | OK | planned | N/A | N/A | planned |
|amdgcn | OK | planned | planned | N/A | planned | |amdgcn | OK | planned | N/A | N/A | planned |
|sparc | OK | planned | planned | N/A | planned | |sparc | OK | planned | N/A | N/A | planned |
|s390x | OK | planned | planned | N/A | planned | |s390x | OK | planned | N/A | N/A | planned |
|thumb | OK | planned | planned | N/A | planned | |thumb | OK | planned | N/A | N/A | planned |
|spir | OK | planned | planned | N/A | planned | |spir | OK | planned | N/A | N/A | planned |
|lanai | OK | planned | planned | N/A | planned | |lanai | OK | planned | N/A | N/A | planned |
## Community ## Community

View File

@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void {
exe.addObjectFile(lib); exe.addObjectFile(lib);
} }
} else { } else {
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm");
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf");
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff");
addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib");
@ -74,7 +75,7 @@ pub fn build(b: *Builder) !void {
cxx_compiler, cxx_compiler,
"-print-file-name=libstdc++.a", "-print-file-name=libstdc++.a",
}); });
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next(); const libstdcxx_path = mem.split(libstdcxx_path_padded, "\r\n").next().?;
if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) { if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
warn( warn(
\\Unable to determine path to libstdc++.a \\Unable to determine path to libstdc++.a
@ -101,11 +102,11 @@ pub fn build(b: *Builder) !void {
b.default_step.dependOn(&exe.step); b.default_step.dependOn(&exe.step);
const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") ?? false; const skip_self_hosted = b.option(bool, "skip-self-hosted", "Main test suite skips building self hosted compiler") orelse false;
if (!skip_self_hosted) { if (!skip_self_hosted) {
test_step.dependOn(&exe.step); test_step.dependOn(&exe.step);
} }
const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") ?? false; const verbose_link_exe = b.option(bool, "verbose-link", "Print link command for self hosted compiler") orelse false;
exe.setVerboseLink(verbose_link_exe); exe.setVerboseLink(verbose_link_exe);
b.installArtifact(exe); b.installArtifact(exe);
@ -113,7 +114,7 @@ pub fn build(b: *Builder) !void {
installCHeaders(b, c_header_files); installCHeaders(b, c_header_files);
const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") ?? false; const with_lldb = b.option(bool, "with-lldb", "Run tests in LLDB to get a backtrace if one fails") orelse false;
test_step.dependOn(docs_step); test_step.dependOn(docs_step);

View File

@ -6,7 +6,7 @@ Every type has a "handle". If a type is a simple primitive type such as i32 or
f64, the handle is "by value", meaning that we pass around the value itself when f64, the handle is "by value", meaning that we pass around the value itself when
we refer to a value of that type. we refer to a value of that type.
If a type is a container, error union, maybe type, slice, or array, then its If a type is a container, error union, optional type, slice, or array, then its
handle is a pointer, and everywhere we refer to a value of this type we refer to handle is a pointer, and everywhere we refer to a value of this type we refer to
a pointer. a pointer.
@ -19,7 +19,7 @@ Error union types are represented as:
payload: T, payload: T,
} }
Maybe types are represented as: Optional types are represented as:
struct { struct {
payload: T, payload: T,
@ -28,6 +28,6 @@ Maybe types are represented as:
## Data Optimizations ## Data Optimizations
Maybe pointer types are special: the 0x0 pointer value is used to represent a Optional pointer types are special: the 0x0 pointer value is used to represent a
null pointer. Thus, instead of the struct above, maybe pointer types are null pointer. Thus, instead of the struct above, optional pointer types are
represented as a `usize` in codegen and the handle is by value. represented as a `usize` in codegen and the handle is by value.

View File

@ -25,13 +25,13 @@ pub fn main() !void {
if (!args_it.skip()) @panic("expected self arg"); if (!args_it.skip()) @panic("expected self arg");
const zig_exe = try (args_it.next(allocator) ?? @panic("expected zig exe arg")); const zig_exe = try (args_it.next(allocator) orelse @panic("expected zig exe arg"));
defer allocator.free(zig_exe); defer allocator.free(zig_exe);
const in_file_name = try (args_it.next(allocator) ?? @panic("expected input arg")); const in_file_name = try (args_it.next(allocator) orelse @panic("expected input arg"));
defer allocator.free(in_file_name); defer allocator.free(in_file_name);
const out_file_name = try (args_it.next(allocator) ?? @panic("expected output arg")); const out_file_name = try (args_it.next(allocator) orelse @panic("expected output arg"));
defer allocator.free(out_file_name); defer allocator.free(out_file_name);
var in_file = try os.File.openRead(allocator, in_file_name); var in_file = try os.File.openRead(allocator, in_file_name);
@ -51,14 +51,8 @@ pub fn main() !void {
var toc = try genToc(allocator, &tokenizer); var toc = try genToc(allocator, &tokenizer);
try os.makePath(allocator, tmp_dir_name); try os.makePath(allocator, tmp_dir_name);
defer { defer os.deleteTree(allocator, tmp_dir_name) catch {};
// TODO issue #709
// disabled to pass CI tests, but obviously we want to implement this
// and then remove this workaround
if (builtin.os != builtin.Os.windows) {
os.deleteTree(allocator, tmp_dir_name) catch {};
}
}
try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe); try genHtml(allocator, &tokenizer, &toc, &buffered_out_stream.stream, zig_exe);
try buffered_out_stream.flush(); try buffered_out_stream.flush();
} }
@ -300,6 +294,7 @@ const Link = struct {
const Node = union(enum) { const Node = union(enum) {
Content: []const u8, Content: []const u8,
Nav, Nav,
Builtin,
HeaderOpen: HeaderOpen, HeaderOpen: HeaderOpen,
SeeAlso: []const SeeAlsoItem, SeeAlso: []const SeeAlsoItem,
Code: Code, Code: Code,
@ -356,6 +351,9 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc {
_ = try eatToken(tokenizer, Token.Id.BracketClose); _ = try eatToken(tokenizer, Token.Id.BracketClose);
try nodes.append(Node.Nav); try nodes.append(Node.Nav);
} else if (mem.eql(u8, tag_name, "builtin")) {
_ = try eatToken(tokenizer, Token.Id.BracketClose);
try nodes.append(Node.Builtin);
} else if (mem.eql(u8, tag_name, "header_open")) { } else if (mem.eql(u8, tag_name, "header_open")) {
_ = try eatToken(tokenizer, Token.Id.Separator); _ = try eatToken(tokenizer, Token.Id.Separator);
const content_token = try eatToken(tokenizer, Token.Id.TagContent); const content_token = try eatToken(tokenizer, Token.Id.TagContent);
@ -690,6 +688,9 @@ fn termColor(allocator: *mem.Allocator, input: []const u8) ![]u8 {
fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void { fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var, zig_exe: []const u8) !void {
var code_progress_index: usize = 0; var code_progress_index: usize = 0;
const builtin_code = try escapeHtml(allocator, try getBuiltinCode(allocator, zig_exe));
for (toc.nodes) |node| { for (toc.nodes) |node| {
switch (node) { switch (node) {
Node.Content => |data| { Node.Content => |data| {
@ -704,6 +705,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
Node.Nav => { Node.Nav => {
try out.write(toc.toc); try out.write(toc.toc);
}, },
Node.Builtin => {
try out.print("<pre><code class=\"zig\">{}</code></pre>", builtin_code);
},
Node.HeaderOpen => |info| { Node.HeaderOpen => |info| {
try out.print("<h{} id=\"{}\">{}</h{}>\n", info.n, info.url, info.name, info.n); try out.print("<h{} id=\"{}\">{}</h{}>\n", info.n, info.url, info.name, info.n);
}, },
@ -954,6 +958,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
var build_args = std.ArrayList([]const u8).init(allocator); var build_args = std.ArrayList([]const u8).init(allocator);
defer build_args.deinit(); defer build_args.deinit();
const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name);
const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext);
try build_args.appendSlice([][]const u8{ try build_args.appendSlice([][]const u8{
zig_exe, zig_exe,
"build-obj", "build-obj",
@ -962,6 +969,8 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
"on", "on",
"--output", "--output",
tmp_obj_file_name, tmp_obj_file_name,
"--output-h",
output_h_file_name,
}); });
if (!code.is_inline) { if (!code.is_inline) {
@ -1060,3 +1069,11 @@ fn exec(allocator: *mem.Allocator, args: []const []const u8) !os.ChildProcess.Ex
} }
return result; return result;
} }
fn getBuiltinCode(allocator: *mem.Allocator, zig_exe: []const u8) ![]const u8 {
const result = try exec(allocator, []const []const u8{
zig_exe,
"builtin",
});
return result.stdout;
}

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ const allocator = std.debug.global_allocator;
pub fn main() !void { pub fn main() !void {
var args_it = os.args(); var args_it = os.args();
const exe = try unwrapArg(??args_it.next(allocator)); const exe = try unwrapArg(args_it.next(allocator).?);
var catted_anything = false; var catted_anything = false;
var stdout_file = try io.getStdOut(); var stdout_file = try io.getStdOut();

View File

@ -99,7 +99,7 @@ pub const Args = struct {
error.ArgumentNotInAllowedSet => { error.ArgumentNotInAllowedSet => {
std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg); std.debug.warn("argument '{}' is invalid for flag '{}'\n", args[i], arg);
std.debug.warn("allowed options are "); std.debug.warn("allowed options are ");
for (??flag.allowed_set) |possible| { for (flag.allowed_set.?) |possible| {
std.debug.warn("'{}' ", possible); std.debug.warn("'{}' ", possible);
} }
std.debug.warn("\n"); std.debug.warn("\n");
@ -276,14 +276,14 @@ test "parse arguments" {
debug.assert(!args.present("help2")); debug.assert(!args.present("help2"));
debug.assert(!args.present("init")); debug.assert(!args.present("init"));
debug.assert(mem.eql(u8, ??args.single("build-file"), "build.zig")); debug.assert(mem.eql(u8, args.single("build-file").?, "build.zig"));
debug.assert(mem.eql(u8, ??args.single("color"), "on")); debug.assert(mem.eql(u8, args.single("color").?, "on"));
const objects = ??args.many("object"); const objects = args.many("object").?;
debug.assert(mem.eql(u8, objects[0], "obj1")); debug.assert(mem.eql(u8, objects[0], "obj1"));
debug.assert(mem.eql(u8, objects[1], "obj2")); debug.assert(mem.eql(u8, objects[1], "obj2"));
debug.assert(mem.eql(u8, ??args.single("library"), "lib2")); debug.assert(mem.eql(u8, args.single("library").?, "lib2"));
const pos = args.positionals.toSliceConst(); const pos = args.positionals.toSliceConst();
debug.assert(mem.eql(u8, pos[0], "build")); debug.assert(mem.eql(u8, pos[0], "build"));

View File

@ -27,7 +27,7 @@ pub fn findZigLibDir(allocator: *mem.Allocator) ![]u8 {
var cur_path: []const u8 = self_exe_path; var cur_path: []const u8 = self_exe_path;
while (true) { while (true) {
const test_dir = os.path.dirname(cur_path); const test_dir = os.path.dirname(cur_path) orelse ".";
if (mem.eql(u8, test_dir, cur_path)) { if (mem.eql(u8, test_dir, cur_path)) {
break; break;

View File

@ -8,6 +8,6 @@ pub const ContextRef = removeNullability(c.LLVMContextRef);
pub const BuilderRef = removeNullability(c.LLVMBuilderRef); pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
fn removeNullability(comptime T: type) type { fn removeNullability(comptime T: type) type {
comptime assert(@typeId(T) == builtin.TypeId.Nullable); comptime assert(@typeId(T) == builtin.TypeId.Optional);
return T.Child; return T.Child;
} }

View File

@ -212,7 +212,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig"); const build_runner_path = try os.path.join(allocator, special_dir, "build_runner.zig");
defer allocator.free(build_runner_path); defer allocator.free(build_runner_path);
const build_file = flags.single("build-file") ?? "build.zig"; const build_file = flags.single("build-file") orelse "build.zig";
const build_file_abs = try os.path.resolve(allocator, ".", build_file); const build_file_abs = try os.path.resolve(allocator, ".", build_file);
defer allocator.free(build_file_abs); defer allocator.free(build_file_abs);
@ -249,7 +249,7 @@ fn cmdBuild(allocator: *Allocator, args: []const []const u8) !void {
defer build_args.deinit(); defer build_args.deinit();
const build_file_basename = os.path.basename(build_file_abs); const build_file_basename = os.path.basename(build_file_abs);
const build_file_dirname = os.path.dirname(build_file_abs); const build_file_dirname = os.path.dirname(build_file_abs) orelse ".";
var full_cache_dir: []u8 = undefined; var full_cache_dir: []u8 = undefined;
if (flags.single("cache-dir")) |cache_dir| { if (flags.single("cache-dir")) |cache_dir| {
@ -490,7 +490,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
try stderr.print("encountered --pkg-end with no matching --pkg-begin\n"); try stderr.print("encountered --pkg-end with no matching --pkg-begin\n");
os.exit(1); os.exit(1);
} }
cur_pkg = ??cur_pkg.parent; cur_pkg = cur_pkg.parent.?;
} }
} }
@ -514,28 +514,28 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
}, },
} }
const basename = os.path.basename(??in_file); const basename = os.path.basename(in_file.?);
var it = mem.split(basename, "."); var it = mem.split(basename, ".");
const root_name = it.next() ?? { const root_name = it.next() orelse {
try stderr.write("file name cannot be empty\n"); try stderr.write("file name cannot be empty\n");
os.exit(1); os.exit(1);
}; };
const asm_a = flags.many("assembly"); const asm_a = flags.many("assembly");
const obj_a = flags.many("object"); const obj_a = flags.many("object");
if (in_file == null and (obj_a == null or (??obj_a).len == 0) and (asm_a == null or (??asm_a).len == 0)) { if (in_file == null and (obj_a == null or obj_a.?.len == 0) and (asm_a == null or asm_a.?.len == 0)) {
try stderr.write("Expected source file argument or at least one --object or --assembly argument\n"); try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
os.exit(1); os.exit(1);
} }
if (out_type == Module.Kind.Obj and (obj_a != null and (??obj_a).len != 0)) { if (out_type == Module.Kind.Obj and (obj_a != null and obj_a.?.len != 0)) {
try stderr.write("When building an object file, --object arguments are invalid\n"); try stderr.write("When building an object file, --object arguments are invalid\n");
os.exit(1); os.exit(1);
} }
const zig_root_source_file = in_file; const zig_root_source_file = in_file;
const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") ?? "zig-cache"[0..]) catch { const full_cache_dir = os.path.resolve(allocator, ".", flags.single("cache-dir") orelse "zig-cache"[0..]) catch {
os.exit(1); os.exit(1);
}; };
defer allocator.free(full_cache_dir); defer allocator.free(full_cache_dir);
@ -555,9 +555,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
); );
defer module.destroy(); defer module.destroy();
module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") ?? "0", 10); module.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") ?? "0", 10); module.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") ?? "0", 10); module.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
module.is_test = false; module.is_test = false;
@ -652,7 +652,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Mo
} }
try module.build(); try module.build();
try module.link(flags.single("out-file") ?? null); try module.link(flags.single("out-file") orelse null);
if (flags.present("print-timing-info")) { if (flags.present("print-timing-info")) {
// codegen_print_timing_info(g, stderr); // codegen_print_timing_info(g, stderr);
@ -734,7 +734,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
defer file.close(); defer file.close();
const source_code = io.readFileAlloc(allocator, file_path) catch |err| { const source_code = io.readFileAlloc(allocator, file_path) catch |err| {
try stderr.print("unable to open '{}': {}", file_path, err); try stderr.print("unable to open '{}': {}\n", file_path, err);
fmt_errors = true; fmt_errors = true;
continue; continue;
}; };

View File

@ -130,13 +130,13 @@ pub const Module = struct {
var name_buffer = try Buffer.init(allocator, name); var name_buffer = try Buffer.init(allocator, name);
errdefer name_buffer.deinit(); errdefer name_buffer.deinit();
const context = c.LLVMContextCreate() ?? return error.OutOfMemory; const context = c.LLVMContextCreate() orelse return error.OutOfMemory;
errdefer c.LLVMContextDispose(context); errdefer c.LLVMContextDispose(context);
const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory; const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) orelse return error.OutOfMemory;
errdefer c.LLVMDisposeModule(module); errdefer c.LLVMDisposeModule(module);
const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory; const builder = c.LLVMCreateBuilderInContext(context) orelse return error.OutOfMemory;
errdefer c.LLVMDisposeBuilder(builder); errdefer c.LLVMDisposeBuilder(builder);
const module_ptr = try allocator.create(Module); const module_ptr = try allocator.create(Module);
@ -223,7 +223,7 @@ pub const Module = struct {
c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr);
} }
const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path"); const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| { const root_src_real_path = os.path.real(self.allocator, root_src_path) catch |err| {
try printError("unable to get real path '{}': {}", root_src_path, err); try printError("unable to get real path '{}': {}", root_src_path, err);
return err; return err;

View File

@ -144,6 +144,9 @@ enum ConstPtrSpecial {
// understand the value of pointee at compile time. However, we will still // understand the value of pointee at compile time. However, we will still
// emit a binary with a compile time known address. // emit a binary with a compile time known address.
// In this case index is the numeric address value. // In this case index is the numeric address value.
// We also use this for null pointer. We need the data layout for ConstCastOnly == true
// types to be the same, so all optionals of pointer types use x_ptr
// instead of x_optional
ConstPtrSpecialHardCodedAddr, ConstPtrSpecialHardCodedAddr,
// This means that the pointer represents memory of assigning to _. // This means that the pointer represents memory of assigning to _.
// That is, storing discards the data, and loading is invalid. // That is, storing discards the data, and loading is invalid.
@ -219,10 +222,10 @@ enum RuntimeHintErrorUnion {
RuntimeHintErrorUnionNonError, RuntimeHintErrorUnionNonError,
}; };
enum RuntimeHintMaybe { enum RuntimeHintOptional {
RuntimeHintMaybeUnknown, RuntimeHintOptionalUnknown,
RuntimeHintMaybeNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known. RuntimeHintOptionalNull, // TODO is this value even possible? if this is the case it might mean the const value is compile time known.
RuntimeHintMaybeNonNull, RuntimeHintOptionalNonNull,
}; };
enum RuntimeHintPtr { enum RuntimeHintPtr {
@ -251,7 +254,7 @@ struct ConstExprValue {
bool x_bool; bool x_bool;
ConstBoundFnValue x_bound_fn; ConstBoundFnValue x_bound_fn;
TypeTableEntry *x_type; TypeTableEntry *x_type;
ConstExprValue *x_maybe; ConstExprValue *x_optional;
ConstErrValue x_err_union; ConstErrValue x_err_union;
ErrorTableEntry *x_err_set; ErrorTableEntry *x_err_set;
BigInt x_enum_tag; BigInt x_enum_tag;
@ -265,7 +268,7 @@ struct ConstExprValue {
// populated if special == ConstValSpecialRuntime // populated if special == ConstValSpecialRuntime
RuntimeHintErrorUnion rh_error_union; RuntimeHintErrorUnion rh_error_union;
RuntimeHintMaybe rh_maybe; RuntimeHintOptional rh_maybe;
RuntimeHintPtr rh_ptr; RuntimeHintPtr rh_ptr;
} data; } data;
}; };
@ -384,6 +387,7 @@ enum NodeType {
NodeTypeSliceExpr, NodeTypeSliceExpr,
NodeTypeFieldAccessExpr, NodeTypeFieldAccessExpr,
NodeTypePtrDeref, NodeTypePtrDeref,
NodeTypeUnwrapOptional,
NodeTypeUse, NodeTypeUse,
NodeTypeBoolLiteral, NodeTypeBoolLiteral,
NodeTypeNullLiteral, NodeTypeNullLiteral,
@ -553,7 +557,7 @@ enum BinOpType {
BinOpTypeMultWrap, BinOpTypeMultWrap,
BinOpTypeDiv, BinOpTypeDiv,
BinOpTypeMod, BinOpTypeMod,
BinOpTypeUnwrapMaybe, BinOpTypeUnwrapOptional,
BinOpTypeArrayCat, BinOpTypeArrayCat,
BinOpTypeArrayMult, BinOpTypeArrayMult,
BinOpTypeErrorUnion, BinOpTypeErrorUnion,
@ -572,6 +576,10 @@ struct AstNodeCatchExpr {
AstNode *op2; AstNode *op2;
}; };
struct AstNodeUnwrapOptional {
AstNode *expr;
};
enum CastOp { enum CastOp {
CastOpNoCast, // signifies the function call expression is not a cast CastOpNoCast, // signifies the function call expression is not a cast
CastOpNoop, // fn call expr is a cast, but does nothing CastOpNoop, // fn call expr is a cast, but does nothing
@ -583,6 +591,7 @@ enum CastOp {
CastOpNumLitToConcrete, CastOpNumLitToConcrete,
CastOpErrSet, CastOpErrSet,
CastOpBitCast, CastOpBitCast,
CastOpPtrOfArrayToSlice,
}; };
struct AstNodeFnCallExpr { struct AstNodeFnCallExpr {
@ -619,8 +628,7 @@ enum PrefixOp {
PrefixOpBinNot, PrefixOpBinNot,
PrefixOpNegation, PrefixOpNegation,
PrefixOpNegationWrap, PrefixOpNegationWrap,
PrefixOpMaybe, PrefixOpOptional,
PrefixOpUnwrapMaybe,
PrefixOpAddrOf, PrefixOpAddrOf,
}; };
@ -905,6 +913,7 @@ struct AstNode {
AstNodeTestDecl test_decl; AstNodeTestDecl test_decl;
AstNodeBinOpExpr bin_op_expr; AstNodeBinOpExpr bin_op_expr;
AstNodeCatchExpr unwrap_err_expr; AstNodeCatchExpr unwrap_err_expr;
AstNodeUnwrapOptional unwrap_optional;
AstNodePrefixOpExpr prefix_op_expr; AstNodePrefixOpExpr prefix_op_expr;
AstNodePointerType pointer_type; AstNodePointerType pointer_type;
AstNodeFnCallExpr fn_call_expr; AstNodeFnCallExpr fn_call_expr;
@ -1037,6 +1046,10 @@ struct TypeTableEntryStruct {
// whether we've finished resolving it // whether we've finished resolving it
bool complete; bool complete;
// whether any of the fields require comptime
// the value is not valid until zero_bits_known == true
bool requires_comptime;
bool zero_bits_loop_flag; bool zero_bits_loop_flag;
bool zero_bits_known; bool zero_bits_known;
uint32_t abi_alignment; // also figured out with zero_bits pass uint32_t abi_alignment; // also figured out with zero_bits pass
@ -1044,7 +1057,7 @@ struct TypeTableEntryStruct {
HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name; HashMap<Buf *, TypeStructField *, buf_hash, buf_eql_buf> fields_by_name;
}; };
struct TypeTableEntryMaybe { struct TypeTableEntryOptional {
TypeTableEntry *child_type; TypeTableEntry *child_type;
}; };
@ -1078,8 +1091,7 @@ struct TypeTableEntryEnum {
bool zero_bits_loop_flag; bool zero_bits_loop_flag;
bool zero_bits_known; bool zero_bits_known;
bool generate_name_table; LLVMValueRef name_function;
LLVMValueRef name_table;
HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name; HashMap<Buf *, TypeEnumField *, buf_hash, buf_eql_buf> fields_by_name;
}; };
@ -1105,6 +1117,10 @@ struct TypeTableEntryUnion {
// whether we've finished resolving it // whether we've finished resolving it
bool complete; bool complete;
// whether any of the fields require comptime
// the value is not valid until zero_bits_known == true
bool requires_comptime;
bool zero_bits_loop_flag; bool zero_bits_loop_flag;
bool zero_bits_known; bool zero_bits_known;
uint32_t abi_alignment; // also figured out with zero_bits pass uint32_t abi_alignment; // also figured out with zero_bits pass
@ -1163,7 +1179,7 @@ enum TypeTableEntryId {
TypeTableEntryIdComptimeInt, TypeTableEntryIdComptimeInt,
TypeTableEntryIdUndefined, TypeTableEntryIdUndefined,
TypeTableEntryIdNull, TypeTableEntryIdNull,
TypeTableEntryIdMaybe, TypeTableEntryIdOptional,
TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorUnion,
TypeTableEntryIdErrorSet, TypeTableEntryIdErrorSet,
TypeTableEntryIdEnum, TypeTableEntryIdEnum,
@ -1194,7 +1210,7 @@ struct TypeTableEntry {
TypeTableEntryFloat floating; TypeTableEntryFloat floating;
TypeTableEntryArray array; TypeTableEntryArray array;
TypeTableEntryStruct structure; TypeTableEntryStruct structure;
TypeTableEntryMaybe maybe; TypeTableEntryOptional maybe;
TypeTableEntryErrorUnion error_union; TypeTableEntryErrorUnion error_union;
TypeTableEntryErrorSet error_set; TypeTableEntryErrorSet error_set;
TypeTableEntryEnum enumeration; TypeTableEntryEnum enumeration;
@ -1346,7 +1362,6 @@ enum BuiltinFnId {
BuiltinFnIdSetRuntimeSafety, BuiltinFnIdSetRuntimeSafety,
BuiltinFnIdSetFloatMode, BuiltinFnIdSetFloatMode,
BuiltinFnIdTypeName, BuiltinFnIdTypeName,
BuiltinFnIdCanImplicitCast,
BuiltinFnIdPanic, BuiltinFnIdPanic,
BuiltinFnIdPtrCast, BuiltinFnIdPtrCast,
BuiltinFnIdBitCast, BuiltinFnIdBitCast,
@ -1391,10 +1406,11 @@ enum PanicMsgId {
PanicMsgIdRemainderDivisionByZero, PanicMsgIdRemainderDivisionByZero,
PanicMsgIdExactDivisionRemainder, PanicMsgIdExactDivisionRemainder,
PanicMsgIdSliceWidenRemainder, PanicMsgIdSliceWidenRemainder,
PanicMsgIdUnwrapMaybeFail, PanicMsgIdUnwrapOptionalFail,
PanicMsgIdInvalidErrorCode, PanicMsgIdInvalidErrorCode,
PanicMsgIdIncorrectAlignment, PanicMsgIdIncorrectAlignment,
PanicMsgIdBadUnionField, PanicMsgIdBadUnionField,
PanicMsgIdBadEnumValue,
PanicMsgIdCount, PanicMsgIdCount,
}; };
@ -1712,8 +1728,6 @@ struct CodeGen {
ZigList<Buf *> link_objects; ZigList<Buf *> link_objects;
ZigList<Buf *> assembly_files; ZigList<Buf *> assembly_files;
ZigList<TypeTableEntry *> name_table_enums;
Buf *test_filter; Buf *test_filter;
Buf *test_name_prefix; Buf *test_name_prefix;
@ -2003,8 +2017,8 @@ enum IrInstructionId {
IrInstructionIdAsm, IrInstructionIdAsm,
IrInstructionIdSizeOf, IrInstructionIdSizeOf,
IrInstructionIdTestNonNull, IrInstructionIdTestNonNull,
IrInstructionIdUnwrapMaybe, IrInstructionIdUnwrapOptional,
IrInstructionIdMaybeWrap, IrInstructionIdOptionalWrap,
IrInstructionIdUnionTag, IrInstructionIdUnionTag,
IrInstructionIdClz, IrInstructionIdClz,
IrInstructionIdCtz, IrInstructionIdCtz,
@ -2055,7 +2069,6 @@ enum IrInstructionId {
IrInstructionIdCheckSwitchProngs, IrInstructionIdCheckSwitchProngs,
IrInstructionIdCheckStatementIsVoid, IrInstructionIdCheckStatementIsVoid,
IrInstructionIdTypeName, IrInstructionIdTypeName,
IrInstructionIdCanImplicitCast,
IrInstructionIdDeclRef, IrInstructionIdDeclRef,
IrInstructionIdPanic, IrInstructionIdPanic,
IrInstructionIdTagName, IrInstructionIdTagName,
@ -2172,7 +2185,7 @@ enum IrUnOp {
IrUnOpNegation, IrUnOpNegation,
IrUnOpNegationWrap, IrUnOpNegationWrap,
IrUnOpDereference, IrUnOpDereference,
IrUnOpMaybe, IrUnOpOptional,
}; };
struct IrInstructionUnOp { struct IrInstructionUnOp {
@ -2475,7 +2488,7 @@ struct IrInstructionTestNonNull {
IrInstruction *value; IrInstruction *value;
}; };
struct IrInstructionUnwrapMaybe { struct IrInstructionUnwrapOptional {
IrInstruction base; IrInstruction base;
IrInstruction *value; IrInstruction *value;
@ -2733,7 +2746,7 @@ struct IrInstructionUnwrapErrPayload {
bool safety_check_on; bool safety_check_on;
}; };
struct IrInstructionMaybeWrap { struct IrInstructionOptionalWrap {
IrInstruction base; IrInstruction base;
IrInstruction *value; IrInstruction *value;
@ -2848,13 +2861,6 @@ struct IrInstructionTypeName {
IrInstruction *type_value; IrInstruction *type_value;
}; };
struct IrInstructionCanImplicitCast {
IrInstruction base;
IrInstruction *type_value;
IrInstruction *target_value;
};
struct IrInstructionDeclRef { struct IrInstructionDeclRef {
IrInstruction base; IrInstruction base;
@ -2949,10 +2955,10 @@ struct IrInstructionExport {
struct IrInstructionErrorReturnTrace { struct IrInstructionErrorReturnTrace {
IrInstruction base; IrInstruction base;
enum Nullable { enum Optional {
Null, Null,
NonNull, NonNull,
} nullable; } optional;
}; };
struct IrInstructionErrorUnion { struct IrInstructionErrorUnion {

View File

@ -236,7 +236,7 @@ bool type_is_complete(TypeTableEntry *type_entry) {
case TypeTableEntryIdComptimeInt: case TypeTableEntryIdComptimeInt:
case TypeTableEntryIdUndefined: case TypeTableEntryIdUndefined:
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
@ -272,7 +272,7 @@ bool type_has_zero_bits_known(TypeTableEntry *type_entry) {
case TypeTableEntryIdComptimeInt: case TypeTableEntryIdComptimeInt:
case TypeTableEntryIdUndefined: case TypeTableEntryIdUndefined:
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
@ -384,6 +384,7 @@ TypeTableEntry *get_pointer_to_type_extra(CodeGen *g, TypeTableEntry *child_type
bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count) bool is_volatile, PtrLen ptr_len, uint32_t byte_alignment, uint32_t bit_offset, uint32_t unaligned_bit_count)
{ {
assert(!type_is_invalid(child_type)); assert(!type_is_invalid(child_type));
assert(ptr_len == PtrLenSingle || child_type->id != TypeTableEntryIdOpaque);
TypeId type_id = {}; TypeId type_id = {};
TypeTableEntry **parent_pointer = nullptr; TypeTableEntry **parent_pointer = nullptr;
@ -519,9 +520,8 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
} else { } else {
ensure_complete_type(g, child_type); ensure_complete_type(g, child_type);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe); TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdOptional);
assert(child_type->type_ref || child_type->zero_bits); assert(child_type->type_ref || child_type->zero_bits);
assert(child_type->di_type);
entry->is_copyable = type_is_copyable(g, child_type); entry->is_copyable = type_is_copyable(g, child_type);
buf_resize(&entry->name, 0); buf_resize(&entry->name, 0);
@ -531,12 +531,14 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
entry->type_ref = LLVMInt1Type(); entry->type_ref = LLVMInt1Type();
entry->di_type = g->builtin_types.entry_bool->di_type; entry->di_type = g->builtin_types.entry_bool->di_type;
} else if (type_is_codegen_pointer(child_type)) { } else if (type_is_codegen_pointer(child_type)) {
assert(child_type->di_type);
// this is an optimization but also is necessary for calling C // this is an optimization but also is necessary for calling C
// functions where all pointers are maybe pointers // functions where all pointers are maybe pointers
// function types are technically pointers // function types are technically pointers
entry->type_ref = child_type->type_ref; entry->type_ref = child_type->type_ref;
entry->di_type = child_type->di_type; entry->di_type = child_type->di_type;
} else { } else {
assert(child_type->di_type);
// create a struct with a boolean whether this is the null value // create a struct with a boolean whether this is the null value
LLVMTypeRef elem_types[] = { LLVMTypeRef elem_types[] = {
child_type->type_ref, child_type->type_ref,
@ -1360,7 +1362,7 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) {
return type_entry->data.structure.layout == ContainerLayoutPacked; return type_entry->data.structure.layout == ContainerLayoutPacked;
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
return type_entry->data.unionation.layout == ContainerLayoutPacked; return type_entry->data.unionation.layout == ContainerLayoutPacked;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
{ {
TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *child_type = type_entry->data.maybe.child_type;
return type_is_codegen_pointer(child_type); return type_is_codegen_pointer(child_type);
@ -1414,7 +1416,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) {
return type_allowed_in_extern(g, type_entry->data.pointer.child_type); return type_allowed_in_extern(g, type_entry->data.pointer.child_type);
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked; return type_entry->data.structure.layout == ContainerLayoutExtern || type_entry->data.structure.layout == ContainerLayoutPacked;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
{ {
TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *child_type = type_entry->data.maybe.child_type;
return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn; return child_type->id == TypeTableEntryIdPointer || child_type->id == TypeTableEntryIdFn;
@ -1537,7 +1539,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
case TypeTableEntryIdArray: case TypeTableEntryIdArray:
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
@ -1631,7 +1633,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
case TypeTableEntryIdArray: case TypeTableEntryIdArray:
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
@ -2532,6 +2534,10 @@ static void resolve_struct_zero_bits(CodeGen *g, TypeTableEntry *struct_type) {
continue; continue;
} }
if (type_requires_comptime(field_type)) {
struct_type->data.structure.requires_comptime = true;
}
if (!type_has_bits(field_type)) if (!type_has_bits(field_type))
continue; continue;
@ -2723,6 +2729,11 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
} }
union_field->type_entry = field_type; union_field->type_entry = field_type;
if (type_requires_comptime(field_type)) {
union_type->data.unionation.requires_comptime = true;
}
if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) { if (field_node->data.struct_field.value != nullptr && !decl_node->data.container_decl.auto_enum) {
ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value, ErrorMsg *msg = add_node_error(g, field_node->data.struct_field.value,
buf_sprintf("non-enum union field assignment")); buf_sprintf("non-enum union field assignment"));
@ -2975,8 +2986,8 @@ static void typecheck_panic_fn(CodeGen *g, FnTableEntry *panic_fn) {
return wrong_panic_prototype(g, proto_node, fn_type); return wrong_panic_prototype(g, proto_node, fn_type);
} }
TypeTableEntry *nullable_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g)); TypeTableEntry *optional_ptr_to_stack_trace_type = get_maybe_type(g, get_ptr_to_stack_trace_type(g));
if (fn_type_id->param_info[1].type != nullable_ptr_to_stack_trace_type) { if (fn_type_id->param_info[1].type != optional_ptr_to_stack_trace_type) {
return wrong_panic_prototype(g, proto_node, fn_type); return wrong_panic_prototype(g, proto_node, fn_type);
} }
@ -3298,6 +3309,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
case NodeTypeAsmExpr: case NodeTypeAsmExpr:
case NodeTypeFieldAccessExpr: case NodeTypeFieldAccessExpr:
case NodeTypePtrDeref: case NodeTypePtrDeref:
case NodeTypeUnwrapOptional:
case NodeTypeStructField: case NodeTypeStructField:
case NodeTypeContainerInitExpr: case NodeTypeContainerInitExpr:
case NodeTypeStructValueField: case NodeTypeStructValueField:
@ -3358,7 +3370,7 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
case TypeTableEntryIdArray: case TypeTableEntryIdArray:
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
@ -3736,7 +3748,7 @@ static bool is_container(TypeTableEntry *type_entry) {
case TypeTableEntryIdComptimeInt: case TypeTableEntryIdComptimeInt:
case TypeTableEntryIdUndefined: case TypeTableEntryIdUndefined:
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
@ -3751,14 +3763,24 @@ static bool is_container(TypeTableEntry *type_entry) {
zig_unreachable(); zig_unreachable();
} }
bool is_ref(TypeTableEntry *type_entry) {
return type_entry->id == TypeTableEntryIdPointer && type_entry->data.pointer.ptr_len == PtrLenSingle;
}
bool is_array_ref(TypeTableEntry *type_entry) {
TypeTableEntry *array = is_ref(type_entry) ?
type_entry->data.pointer.child_type : type_entry;
return array->id == TypeTableEntryIdArray;
}
bool is_container_ref(TypeTableEntry *type_entry) { bool is_container_ref(TypeTableEntry *type_entry) {
return (type_entry->id == TypeTableEntryIdPointer) ? return is_ref(type_entry) ?
is_container(type_entry->data.pointer.child_type) : is_container(type_entry); is_container(type_entry->data.pointer.child_type) : is_container(type_entry);
} }
TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) { TypeTableEntry *container_ref_type(TypeTableEntry *type_entry) {
assert(is_container_ref(type_entry)); assert(is_container_ref(type_entry));
return (type_entry->id == TypeTableEntryIdPointer) ? return is_ref(type_entry) ?
type_entry->data.pointer.child_type : type_entry; type_entry->data.pointer.child_type : type_entry;
} }
@ -3785,7 +3807,7 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) {
case TypeTableEntryIdComptimeInt: case TypeTableEntryIdComptimeInt:
case TypeTableEntryIdUndefined: case TypeTableEntryIdUndefined:
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
@ -3804,7 +3826,7 @@ TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type) {
if (type->id == TypeTableEntryIdPointer) return type; if (type->id == TypeTableEntryIdPointer) return type;
if (type->id == TypeTableEntryIdFn) return type; if (type->id == TypeTableEntryIdFn) return type;
if (type->id == TypeTableEntryIdPromise) return type; if (type->id == TypeTableEntryIdPromise) return type;
if (type->id == TypeTableEntryIdMaybe) { if (type->id == TypeTableEntryIdOptional) {
if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdPointer) return type->data.maybe.child_type;
if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdFn) return type->data.maybe.child_type;
if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type; if (type->data.maybe.child_type->id == TypeTableEntryIdPromise) return type->data.maybe.child_type;
@ -4311,7 +4333,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return type_has_bits(type_entry); return type_has_bits(type_entry);
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
return type_has_bits(type_entry->data.error_union.payload_type); return type_has_bits(type_entry->data.error_union.payload_type);
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
return type_has_bits(type_entry->data.maybe.child_type) && return type_has_bits(type_entry->data.maybe.child_type) &&
!type_is_codegen_pointer(type_entry->data.maybe.child_type); !type_is_codegen_pointer(type_entry->data.maybe.child_type);
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
@ -4558,6 +4580,52 @@ bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) {
return true; return true;
} }
static uint32_t hash_const_val_ptr(ConstExprValue *const_val) {
uint32_t hash_val = 0;
switch (const_val->data.x_ptr.mut) {
case ConstPtrMutRuntimeVar:
hash_val += (uint32_t)3500721036;
break;
case ConstPtrMutComptimeConst:
hash_val += (uint32_t)4214318515;
break;
case ConstPtrMutComptimeVar:
hash_val += (uint32_t)1103195694;
break;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
hash_val += (uint32_t)2478261866;
hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
return hash_val;
case ConstPtrSpecialBaseArray:
hash_val += (uint32_t)1764906839;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
return hash_val;
case ConstPtrSpecialBaseStruct:
hash_val += (uint32_t)3518317043;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
return hash_val;
case ConstPtrSpecialHardCodedAddr:
hash_val += (uint32_t)4048518294;
hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
return hash_val;
case ConstPtrSpecialDiscard:
hash_val += 2010123162;
return hash_val;
case ConstPtrSpecialFunction:
hash_val += (uint32_t)2590901619;
hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
return hash_val;
}
zig_unreachable();
}
static uint32_t hash_const_val(ConstExprValue *const_val) { static uint32_t hash_const_val(ConstExprValue *const_val) {
assert(const_val->special == ConstValSpecialStatic); assert(const_val->special == ConstValSpecialStatic);
switch (const_val->type->id) { switch (const_val->type->id) {
@ -4626,51 +4694,7 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction); assert(const_val->data.x_ptr.special == ConstPtrSpecialFunction);
return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry); return 3677364617 ^ hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
{ return hash_const_val_ptr(const_val);
uint32_t hash_val = 0;
switch (const_val->data.x_ptr.mut) {
case ConstPtrMutRuntimeVar:
hash_val += (uint32_t)3500721036;
break;
case ConstPtrMutComptimeConst:
hash_val += (uint32_t)4214318515;
break;
case ConstPtrMutComptimeVar:
hash_val += (uint32_t)1103195694;
break;
}
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
hash_val += (uint32_t)2478261866;
hash_val += hash_ptr(const_val->data.x_ptr.data.ref.pointee);
return hash_val;
case ConstPtrSpecialBaseArray:
hash_val += (uint32_t)1764906839;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
hash_val += const_val->data.x_ptr.data.base_array.is_cstr ? 1297263887 : 200363492;
return hash_val;
case ConstPtrSpecialBaseStruct:
hash_val += (uint32_t)3518317043;
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
hash_val += hash_size(const_val->data.x_ptr.data.base_struct.field_index);
return hash_val;
case ConstPtrSpecialHardCodedAddr:
hash_val += (uint32_t)4048518294;
hash_val += hash_size(const_val->data.x_ptr.data.hard_coded_addr.addr);
return hash_val;
case ConstPtrSpecialDiscard:
hash_val += 2010123162;
return hash_val;
case ConstPtrSpecialFunction:
hash_val += (uint32_t)2590901619;
hash_val += hash_ptr(const_val->data.x_ptr.data.fn.fn_entry);
return hash_val;
}
zig_unreachable();
}
case TypeTableEntryIdPromise: case TypeTableEntryIdPromise:
// TODO better hashing algorithm // TODO better hashing algorithm
return 223048345; return 223048345;
@ -4687,11 +4711,15 @@ static uint32_t hash_const_val(ConstExprValue *const_val) {
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
// TODO better hashing algorithm // TODO better hashing algorithm
return 2709806591; return 2709806591;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
if (const_val->data.x_maybe) { if (get_codegen_ptr_type(const_val->type) != nullptr) {
return hash_const_val(const_val->data.x_maybe) * 1992916303; return hash_const_val(const_val) * 1992916303;
} else { } else {
return 4016830364; if (const_val->data.x_optional) {
return hash_const_val(const_val->data.x_optional) * 1992916303;
} else {
return 4016830364;
}
} }
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
// TODO better hashing algorithm // TODO better hashing algorithm
@ -4791,10 +4819,12 @@ static bool can_mutate_comptime_var_state(ConstExprValue *value) {
} }
return false; return false;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
if (value->data.x_maybe == nullptr) if (get_codegen_ptr_type(value->type) != nullptr)
return value->data.x_ptr.mut == ConstPtrMutComptimeVar;
if (value->data.x_optional == nullptr)
return false; return false;
return can_mutate_comptime_var_state(value->data.x_maybe); return can_mutate_comptime_var_state(value->data.x_optional);
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
if (value->data.x_err_union.err != nullptr) if (value->data.x_err_union.err != nullptr)
@ -4841,7 +4871,7 @@ static bool return_type_is_cacheable(TypeTableEntry *return_type) {
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
return false; return false;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
return return_type_is_cacheable(return_type->data.maybe.child_type); return return_type_is_cacheable(return_type->data.maybe.child_type);
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
@ -4943,17 +4973,29 @@ bool type_requires_comptime(TypeTableEntry *type_entry) {
case TypeTableEntryIdArgTuple: case TypeTableEntryIdArgTuple:
return true; return true;
case TypeTableEntryIdArray: case TypeTableEntryIdArray:
return type_requires_comptime(type_entry->data.array.child_type);
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
assert(type_has_zero_bits_known(type_entry));
return type_entry->data.structure.requires_comptime;
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
case TypeTableEntryIdMaybe: assert(type_has_zero_bits_known(type_entry));
return type_entry->data.unionation.requires_comptime;
case TypeTableEntryIdOptional:
return type_requires_comptime(type_entry->data.maybe.child_type);
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
return type_requires_comptime(type_entry->data.error_union.payload_type);
case TypeTableEntryIdPointer:
if (type_entry->data.pointer.child_type->id == TypeTableEntryIdOpaque) {
return false;
} else {
return type_requires_comptime(type_entry->data.pointer.child_type);
}
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
case TypeTableEntryIdBool: case TypeTableEntryIdBool:
case TypeTableEntryIdInt: case TypeTableEntryIdInt:
case TypeTableEntryIdFloat: case TypeTableEntryIdFloat:
case TypeTableEntryIdPointer:
case TypeTableEntryIdVoid: case TypeTableEntryIdVoid:
case TypeTableEntryIdUnreachable: case TypeTableEntryIdUnreachable:
case TypeTableEntryIdPromise: case TypeTableEntryIdPromise:
@ -5308,6 +5350,52 @@ bool ir_get_var_is_comptime(VariableTableEntry *var) {
return var->is_comptime->value.data.x_bool; return var->is_comptime->value.data.x_bool;
} }
bool const_values_equal_ptr(ConstExprValue *a, ConstExprValue *b) {
if (a->data.x_ptr.special != b->data.x_ptr.special)
return false;
if (a->data.x_ptr.mut != b->data.x_ptr.mut)
return false;
switch (a->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
return false;
return true;
case ConstPtrSpecialBaseArray:
if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
a->data.x_ptr.data.base_array.array_val->global_refs !=
b->data.x_ptr.data.base_array.array_val->global_refs)
{
return false;
}
if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
return false;
if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
return false;
return true;
case ConstPtrSpecialBaseStruct:
if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
a->data.x_ptr.data.base_struct.struct_val->global_refs !=
b->data.x_ptr.data.base_struct.struct_val->global_refs)
{
return false;
}
if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
return false;
return true;
case ConstPtrSpecialHardCodedAddr:
if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
return false;
return true;
case ConstPtrSpecialDiscard:
return true;
case ConstPtrSpecialFunction:
return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
}
zig_unreachable();
}
bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
assert(a->type->id == b->type->id); assert(a->type->id == b->type->id);
assert(a->special == ConstValSpecialStatic); assert(a->special == ConstValSpecialStatic);
@ -5359,49 +5447,7 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ; return bigint_cmp(&a->data.x_bigint, &b->data.x_bigint) == CmpEQ;
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
if (a->data.x_ptr.special != b->data.x_ptr.special) return const_values_equal_ptr(a, b);
return false;
if (a->data.x_ptr.mut != b->data.x_ptr.mut)
return false;
switch (a->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
if (a->data.x_ptr.data.ref.pointee != b->data.x_ptr.data.ref.pointee)
return false;
return true;
case ConstPtrSpecialBaseArray:
if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val &&
a->data.x_ptr.data.base_array.array_val->global_refs !=
b->data.x_ptr.data.base_array.array_val->global_refs)
{
return false;
}
if (a->data.x_ptr.data.base_array.elem_index != b->data.x_ptr.data.base_array.elem_index)
return false;
if (a->data.x_ptr.data.base_array.is_cstr != b->data.x_ptr.data.base_array.is_cstr)
return false;
return true;
case ConstPtrSpecialBaseStruct:
if (a->data.x_ptr.data.base_struct.struct_val != b->data.x_ptr.data.base_struct.struct_val &&
a->data.x_ptr.data.base_struct.struct_val->global_refs !=
b->data.x_ptr.data.base_struct.struct_val->global_refs)
{
return false;
}
if (a->data.x_ptr.data.base_struct.field_index != b->data.x_ptr.data.base_struct.field_index)
return false;
return true;
case ConstPtrSpecialHardCodedAddr:
if (a->data.x_ptr.data.hard_coded_addr.addr != b->data.x_ptr.data.hard_coded_addr.addr)
return false;
return true;
case ConstPtrSpecialDiscard:
return true;
case ConstPtrSpecialFunction:
return a->data.x_ptr.data.fn.fn_entry == b->data.x_ptr.data.fn.fn_entry;
}
zig_unreachable();
case TypeTableEntryIdArray: case TypeTableEntryIdArray:
zig_panic("TODO"); zig_panic("TODO");
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
@ -5416,11 +5462,13 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) {
zig_panic("TODO"); zig_panic("TODO");
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
zig_panic("TODO"); zig_panic("TODO");
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
if (a->data.x_maybe == nullptr || b->data.x_maybe == nullptr) { if (get_codegen_ptr_type(a->type) != nullptr)
return (a->data.x_maybe == nullptr && b->data.x_maybe == nullptr); return const_values_equal_ptr(a, b);
if (a->data.x_optional == nullptr || b->data.x_optional == nullptr) {
return (a->data.x_optional == nullptr && b->data.x_optional == nullptr);
} else { } else {
return const_values_equal(a->data.x_maybe, b->data.x_maybe); return const_values_equal(a->data.x_optional, b->data.x_optional);
} }
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
zig_panic("TODO"); zig_panic("TODO");
@ -5493,6 +5541,41 @@ void eval_min_max_value(CodeGen *g, TypeTableEntry *type_entry, ConstExprValue *
} }
} }
void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeTableEntry *type_entry) {
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
case ConstPtrSpecialBaseStruct:
buf_appendf(buf, "*");
render_const_value(g, buf, const_ptr_pointee(g, const_val));
return;
case ConstPtrSpecialBaseArray:
if (const_val->data.x_ptr.data.base_array.is_cstr) {
buf_appendf(buf, "*(c str lit)");
return;
} else {
buf_appendf(buf, "*");
render_const_value(g, buf, const_ptr_pointee(g, const_val));
return;
}
case ConstPtrSpecialHardCodedAddr:
buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
const_val->data.x_ptr.data.hard_coded_addr.addr);
return;
case ConstPtrSpecialDiscard:
buf_append_str(buf, "*_");
return;
case ConstPtrSpecialFunction:
{
FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
return;
}
}
zig_unreachable();
}
void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
switch (const_val->special) { switch (const_val->special) {
case ConstValSpecialRuntime: case ConstValSpecialRuntime:
@ -5569,38 +5652,7 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
return; return;
} }
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
switch (const_val->data.x_ptr.special) { return render_const_val_ptr(g, buf, const_val, type_entry);
case ConstPtrSpecialInvalid:
zig_unreachable();
case ConstPtrSpecialRef:
case ConstPtrSpecialBaseStruct:
buf_appendf(buf, "&");
render_const_value(g, buf, const_ptr_pointee(g, const_val));
return;
case ConstPtrSpecialBaseArray:
if (const_val->data.x_ptr.data.base_array.is_cstr) {
buf_appendf(buf, "&(c str lit)");
return;
} else {
buf_appendf(buf, "&");
render_const_value(g, buf, const_ptr_pointee(g, const_val));
return;
}
case ConstPtrSpecialHardCodedAddr:
buf_appendf(buf, "(&%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
const_val->data.x_ptr.data.hard_coded_addr.addr);
return;
case ConstPtrSpecialDiscard:
buf_append_str(buf, "&_");
return;
case ConstPtrSpecialFunction:
{
FnTableEntry *fn_entry = const_val->data.x_ptr.data.fn.fn_entry;
buf_appendf(buf, "@ptrCast(%s, %s)", buf_ptr(&const_val->type->name), buf_ptr(&fn_entry->symbol_name));
return;
}
}
zig_unreachable();
case TypeTableEntryIdBlock: case TypeTableEntryIdBlock:
{ {
AstNode *node = const_val->data.x_block->source_node; AstNode *node = const_val->data.x_block->source_node;
@ -5658,10 +5710,12 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
buf_appendf(buf, "undefined"); buf_appendf(buf, "undefined");
return; return;
} }
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
{ {
if (const_val->data.x_maybe) { if (get_codegen_ptr_type(const_val->type) != nullptr)
render_const_value(g, buf, const_val->data.x_maybe); return render_const_val_ptr(g, buf, const_val, type_entry->data.maybe.child_type);
if (const_val->data.x_optional) {
render_const_value(g, buf, const_val->data.x_optional);
} else { } else {
buf_appendf(buf, "null"); buf_appendf(buf, "null");
} }
@ -5767,7 +5821,7 @@ uint32_t type_id_hash(TypeId x) {
case TypeTableEntryIdComptimeInt: case TypeTableEntryIdComptimeInt:
case TypeTableEntryIdUndefined: case TypeTableEntryIdUndefined:
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
case TypeTableEntryIdUnion: case TypeTableEntryIdUnion:
@ -5813,7 +5867,7 @@ bool type_id_eql(TypeId a, TypeId b) {
case TypeTableEntryIdComptimeInt: case TypeTableEntryIdComptimeInt:
case TypeTableEntryIdUndefined: case TypeTableEntryIdUndefined:
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdPromise: case TypeTableEntryIdPromise:
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:
case TypeTableEntryIdEnum: case TypeTableEntryIdEnum:
@ -5935,7 +5989,7 @@ static const TypeTableEntryId all_type_ids[] = {
TypeTableEntryIdComptimeInt, TypeTableEntryIdComptimeInt,
TypeTableEntryIdUndefined, TypeTableEntryIdUndefined,
TypeTableEntryIdNull, TypeTableEntryIdNull,
TypeTableEntryIdMaybe, TypeTableEntryIdOptional,
TypeTableEntryIdErrorUnion, TypeTableEntryIdErrorUnion,
TypeTableEntryIdErrorSet, TypeTableEntryIdErrorSet,
TypeTableEntryIdEnum, TypeTableEntryIdEnum,
@ -5980,7 +6034,7 @@ size_t type_id_index(TypeTableEntry *entry) {
return 7; return 7;
case TypeTableEntryIdStruct: case TypeTableEntryIdStruct:
if (entry->data.structure.is_slice) if (entry->data.structure.is_slice)
return 25; return 6;
return 8; return 8;
case TypeTableEntryIdComptimeFloat: case TypeTableEntryIdComptimeFloat:
return 9; return 9;
@ -5990,7 +6044,7 @@ size_t type_id_index(TypeTableEntry *entry) {
return 11; return 11;
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
return 12; return 12;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
return 13; return 13;
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
return 14; return 14;
@ -6048,8 +6102,8 @@ const char *type_id_name(TypeTableEntryId id) {
return "Undefined"; return "Undefined";
case TypeTableEntryIdNull: case TypeTableEntryIdNull:
return "Null"; return "Null";
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
return "Nullable"; return "Optional";
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
return "ErrorUnion"; return "ErrorUnion";
case TypeTableEntryIdErrorSet: case TypeTableEntryIdErrorSet:

View File

@ -70,6 +70,8 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name);
TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag); TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *tag);
TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag); TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag);
bool is_ref(TypeTableEntry *type_entry);
bool is_array_ref(TypeTableEntry *type_entry);
bool is_container_ref(TypeTableEntry *type_entry); bool is_container_ref(TypeTableEntry *type_entry);
void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node);
void scan_import(CodeGen *g, ImportTableEntry *import); void scan_import(CodeGen *g, ImportTableEntry *import);

View File

@ -50,7 +50,7 @@ static const char *bin_op_str(BinOpType bin_op) {
case BinOpTypeAssignBitXor: return "^="; case BinOpTypeAssignBitXor: return "^=";
case BinOpTypeAssignBitOr: return "|="; case BinOpTypeAssignBitOr: return "|=";
case BinOpTypeAssignMergeErrorSets: return "||="; case BinOpTypeAssignMergeErrorSets: return "||=";
case BinOpTypeUnwrapMaybe: return "??"; case BinOpTypeUnwrapOptional: return "orelse";
case BinOpTypeArrayCat: return "++"; case BinOpTypeArrayCat: return "++";
case BinOpTypeArrayMult: return "**"; case BinOpTypeArrayMult: return "**";
case BinOpTypeErrorUnion: return "!"; case BinOpTypeErrorUnion: return "!";
@ -66,8 +66,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) {
case PrefixOpNegationWrap: return "-%"; case PrefixOpNegationWrap: return "-%";
case PrefixOpBoolNot: return "!"; case PrefixOpBoolNot: return "!";
case PrefixOpBinNot: return "~"; case PrefixOpBinNot: return "~";
case PrefixOpMaybe: return "?"; case PrefixOpOptional: return "?";
case PrefixOpUnwrapMaybe: return "??";
case PrefixOpAddrOf: return "&"; case PrefixOpAddrOf: return "&";
} }
zig_unreachable(); zig_unreachable();
@ -222,6 +221,8 @@ static const char *node_type_str(NodeType node_type) {
return "FieldAccessExpr"; return "FieldAccessExpr";
case NodeTypePtrDeref: case NodeTypePtrDeref:
return "PtrDerefExpr"; return "PtrDerefExpr";
case NodeTypeUnwrapOptional:
return "UnwrapOptional";
case NodeTypeContainerDecl: case NodeTypeContainerDecl:
return "ContainerDecl"; return "ContainerDecl";
case NodeTypeStructField: case NodeTypeStructField:
@ -711,6 +712,13 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, ".*"); fprintf(ar->f, ".*");
break; break;
} }
case NodeTypeUnwrapOptional:
{
AstNode *lhs = node->data.unwrap_optional.expr;
render_node_ungrouped(ar, lhs);
fprintf(ar->f, ".?");
break;
}
case NodeTypeUndefinedLiteral: case NodeTypeUndefinedLiteral:
fprintf(ar->f, "undefined"); fprintf(ar->f, "undefined");
break; break;

View File

@ -869,7 +869,7 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("exact division produced remainder"); return buf_create_from_str("exact division produced remainder");
case PanicMsgIdSliceWidenRemainder: case PanicMsgIdSliceWidenRemainder:
return buf_create_from_str("slice widening size mismatch"); return buf_create_from_str("slice widening size mismatch");
case PanicMsgIdUnwrapMaybeFail: case PanicMsgIdUnwrapOptionalFail:
return buf_create_from_str("attempt to unwrap null"); return buf_create_from_str("attempt to unwrap null");
case PanicMsgIdUnreachable: case PanicMsgIdUnreachable:
return buf_create_from_str("reached unreachable code"); return buf_create_from_str("reached unreachable code");
@ -879,6 +879,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
return buf_create_from_str("incorrect alignment"); return buf_create_from_str("incorrect alignment");
case PanicMsgIdBadUnionField: case PanicMsgIdBadUnionField:
return buf_create_from_str("access of inactive union field"); return buf_create_from_str("access of inactive union field");
case PanicMsgIdBadEnumValue:
return buf_create_from_str("invalid enum value");
} }
zig_unreachable(); zig_unreachable();
} }
@ -2497,7 +2499,7 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
assert(wanted_type->data.structure.is_slice); assert(wanted_type->data.structure.is_slice);
assert(actual_type->id == TypeTableEntryIdArray); assert(actual_type->id == TypeTableEntryIdArray);
TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[0].type_entry; TypeTableEntry *wanted_pointer_type = wanted_type->data.structure.fields[slice_ptr_index].type_entry;
TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type; TypeTableEntry *wanted_child_type = wanted_pointer_type->data.pointer.child_type;
@ -2543,6 +2545,29 @@ static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutable *executable,
return expr_val; return expr_val;
case CastOpBitCast: case CastOpBitCast:
return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, ""); return LLVMBuildBitCast(g->builder, expr_val, wanted_type->type_ref, "");
case CastOpPtrOfArrayToSlice: {
assert(cast_instruction->tmp_ptr);
assert(actual_type->id == TypeTableEntryIdPointer);
TypeTableEntry *array_type = actual_type->data.pointer.child_type;
assert(array_type->id == TypeTableEntryIdArray);
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
slice_ptr_index, "");
LLVMValueRef indices[] = {
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
LLVMConstInt(g->builtin_types.entry_usize->type_ref, 0, false),
};
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, "");
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, cast_instruction->tmp_ptr,
slice_len_index, "");
LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->type_ref,
array_type->data.array.len, false);
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
return cast_instruction->tmp_ptr;
}
} }
zig_unreachable(); zig_unreachable();
} }
@ -2678,7 +2703,7 @@ static LLVMValueRef ir_render_un_op(CodeGen *g, IrExecutable *executable, IrInst
switch (op_id) { switch (op_id) {
case IrUnOpInvalid: case IrUnOpInvalid:
case IrUnOpMaybe: case IrUnOpOptional:
case IrUnOpDereference: case IrUnOpDereference:
zig_unreachable(); zig_unreachable();
case IrUnOpNegation: case IrUnOpNegation:
@ -3249,7 +3274,7 @@ static LLVMValueRef ir_render_asm(CodeGen *g, IrExecutable *executable, IrInstru
} }
static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) { static LLVMValueRef gen_non_null_bit(CodeGen *g, TypeTableEntry *maybe_type, LLVMValueRef maybe_handle) {
assert(maybe_type->id == TypeTableEntryIdMaybe); assert(maybe_type->id == TypeTableEntryIdOptional);
TypeTableEntry *child_type = maybe_type->data.maybe.child_type; TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
if (child_type->zero_bits) { if (child_type->zero_bits) {
return maybe_handle; return maybe_handle;
@ -3271,23 +3296,23 @@ static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutable *executable
} }
static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_unwrap_maybe(CodeGen *g, IrExecutable *executable,
IrInstructionUnwrapMaybe *instruction) IrInstructionUnwrapOptional *instruction)
{ {
TypeTableEntry *ptr_type = instruction->value->value.type; TypeTableEntry *ptr_type = instruction->value->value.type;
assert(ptr_type->id == TypeTableEntryIdPointer); assert(ptr_type->id == TypeTableEntryIdPointer);
TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type; TypeTableEntry *maybe_type = ptr_type->data.pointer.child_type;
assert(maybe_type->id == TypeTableEntryIdMaybe); assert(maybe_type->id == TypeTableEntryIdOptional);
TypeTableEntry *child_type = maybe_type->data.maybe.child_type; TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value); LLVMValueRef maybe_ptr = ir_llvm_value(g, instruction->value);
LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type); LLVMValueRef maybe_handle = get_handle_value(g, maybe_ptr, maybe_type, ptr_type);
if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) { if (ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on) {
LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle);
LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeOk"); LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk");
LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapMaybeFail"); LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail");
LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block);
LLVMPositionBuilderAtEnd(g->builder, fail_block); LLVMPositionBuilderAtEnd(g->builder, fail_block);
gen_safety_crash(g, PanicMsgIdUnwrapMaybeFail); gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail);
LLVMPositionBuilderAtEnd(g->builder, ok_block); LLVMPositionBuilderAtEnd(g->builder, ok_block);
} }
@ -3432,34 +3457,112 @@ static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutable *executable, IrI
return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, ""); return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, "");
} }
static LLVMValueRef get_enum_tag_name_function(CodeGen *g, TypeTableEntry *enum_type) {
assert(enum_type->id == TypeTableEntryIdEnum);
if (enum_type->data.enumeration.name_function)
return enum_type->data.enumeration.name_function;
TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
TypeTableEntry *u8_slice_type = get_slice_type(g, u8_ptr_type);
TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type;
LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(u8_slice_type->type_ref, 0),
&tag_int_type->type_ref, 1, false);
Buf *fn_name = get_mangled_name(g, buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)), false);
LLVMValueRef fn_val = LLVMAddFunction(g->module, buf_ptr(fn_name), fn_type_ref);
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
add_uwtable_attr(g, fn_val);
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim-non-leaf", nullptr);
}
LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder);
LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder);
FnTableEntry *prev_cur_fn = g->cur_fn;
LLVMValueRef prev_cur_fn_val = g->cur_fn_val;
LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry");
LLVMPositionBuilderAtEnd(g->builder, entry_block);
ZigLLVMClearCurrentDebugLocation(g->builder);
g->cur_fn = nullptr;
g->cur_fn_val = fn_val;
size_t field_count = enum_type->data.enumeration.src_field_count;
LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue");
LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0);
LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count);
TypeTableEntry *usize = g->builtin_types.entry_usize;
LLVMValueRef array_ptr_indices[] = {
LLVMConstNull(usize->type_ref),
LLVMConstNull(usize->type_ref),
};
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
Buf *name = enum_type->data.enumeration.fields[field_i].name;
LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
LLVMSetInitializer(str_global, str_init);
LLVMSetLinkage(str_global, LLVMPrivateLinkage);
LLVMSetGlobalConstant(str_global, true);
LLVMSetUnnamedAddr(str_global, true);
LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));
LLVMValueRef fields[] = {
LLVMConstGEP(str_global, array_ptr_indices, 2),
LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
};
LLVMValueRef slice_init_value = LLVMConstNamedStruct(u8_slice_type->type_ref, fields, 2);
LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), "");
LLVMSetInitializer(slice_global, slice_init_value);
LLVMSetLinkage(slice_global, LLVMPrivateLinkage);
LLVMSetGlobalConstant(slice_global, true);
LLVMSetUnnamedAddr(slice_global, true);
LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value)));
LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name");
LLVMValueRef this_tag_int_value = bigint_to_llvm_const(tag_int_type->type_ref,
&enum_type->data.enumeration.fields[field_i].value);
LLVMAddCase(switch_instr, this_tag_int_value, return_block);
LLVMPositionBuilderAtEnd(g->builder, return_block);
LLVMBuildRet(g->builder, slice_global);
}
LLVMPositionBuilderAtEnd(g->builder, bad_value_block);
if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) {
gen_safety_crash(g, PanicMsgIdBadEnumValue);
} else {
LLVMBuildUnreachable(g->builder);
}
g->cur_fn = prev_cur_fn;
g->cur_fn_val = prev_cur_fn_val;
LLVMPositionBuilderAtEnd(g->builder, prev_block);
LLVMSetCurrentDebugLocation(g->builder, prev_debug_location);
enum_type->data.enumeration.name_function = fn_val;
return fn_val;
}
static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutable *executable,
IrInstructionTagName *instruction) IrInstructionTagName *instruction)
{ {
TypeTableEntry *enum_type = instruction->target->value.type; TypeTableEntry *enum_type = instruction->target->value.type;
assert(enum_type->id == TypeTableEntryIdEnum); assert(enum_type->id == TypeTableEntryIdEnum);
assert(enum_type->data.enumeration.generate_name_table);
TypeTableEntry *tag_int_type = enum_type->data.enumeration.tag_int_type; LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type);
LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target);
if (ir_want_runtime_safety(g, &instruction->base)) { return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1,
size_t field_count = enum_type->data.enumeration.src_field_count; get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
// if the field_count can't fit in the bits of the enum_type, then it can't possibly
// be the wrong value
BigInt field_bi;
bigint_init_unsigned(&field_bi, field_count);
if (bigint_fits_in_bits(&field_bi, tag_int_type->data.integral.bit_count, false)) {
LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(enum_tag_value), field_count, false);
add_bounds_check(g, enum_tag_value, LLVMIntEQ, nullptr, LLVMIntULT, end_val);
}
}
LLVMValueRef indices[] = {
LLVMConstNull(g->builtin_types.entry_usize->type_ref),
gen_widen_or_shorten(g, false, tag_int_type,
g->builtin_types.entry_usize, enum_tag_value),
};
return LLVMBuildInBoundsGEP(g->builder, enum_type->data.enumeration.name_table, indices, 2, "");
} }
static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable, static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutable *executable,
@ -3509,17 +3612,17 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
} else if (target_type->id == TypeTableEntryIdFn) { } else if (target_type->id == TypeTableEntryIdFn) {
align_bytes = target_type->data.fn.fn_type_id.alignment; align_bytes = target_type->data.fn.fn_type_id.alignment;
ptr_val = target_val; ptr_val = target_val;
} else if (target_type->id == TypeTableEntryIdMaybe && } else if (target_type->id == TypeTableEntryIdOptional &&
target_type->data.maybe.child_type->id == TypeTableEntryIdPointer) target_type->data.maybe.child_type->id == TypeTableEntryIdPointer)
{ {
align_bytes = target_type->data.maybe.child_type->data.pointer.alignment; align_bytes = target_type->data.maybe.child_type->data.pointer.alignment;
ptr_val = target_val; ptr_val = target_val;
} else if (target_type->id == TypeTableEntryIdMaybe && } else if (target_type->id == TypeTableEntryIdOptional &&
target_type->data.maybe.child_type->id == TypeTableEntryIdFn) target_type->data.maybe.child_type->id == TypeTableEntryIdFn)
{ {
align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment;
ptr_val = target_val; ptr_val = target_val;
} else if (target_type->id == TypeTableEntryIdMaybe && } else if (target_type->id == TypeTableEntryIdOptional &&
target_type->data.maybe.child_type->id == TypeTableEntryIdPromise) target_type->data.maybe.child_type->id == TypeTableEntryIdPromise)
{ {
zig_panic("TODO audit this function"); zig_panic("TODO audit this function");
@ -3621,7 +3724,7 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn
success_order, failure_order, instruction->is_weak); success_order, failure_order, instruction->is_weak);
TypeTableEntry *maybe_type = instruction->base.value.type; TypeTableEntry *maybe_type = instruction->base.value.type;
assert(maybe_type->id == TypeTableEntryIdMaybe); assert(maybe_type->id == TypeTableEntryIdOptional);
TypeTableEntry *child_type = maybe_type->data.maybe.child_type; TypeTableEntry *child_type = maybe_type->data.maybe.child_type;
if (type_is_codegen_pointer(child_type)) { if (type_is_codegen_pointer(child_type)) {
@ -3730,7 +3833,6 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
} else { } else {
end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false); end_val = LLVMConstInt(g->builtin_types.entry_usize->type_ref, array_type->data.array.len, false);
} }
if (want_runtime_safety) { if (want_runtime_safety) {
add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
if (instruction->end) { if (instruction->end) {
@ -4008,10 +4110,10 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
} }
} }
static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionMaybeWrap *instruction) { static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, IrInstructionOptionalWrap *instruction) {
TypeTableEntry *wanted_type = instruction->base.value.type; TypeTableEntry *wanted_type = instruction->base.value.type;
assert(wanted_type->id == TypeTableEntryIdMaybe); assert(wanted_type->id == TypeTableEntryIdOptional);
TypeTableEntry *child_type = wanted_type->data.maybe.child_type; TypeTableEntry *child_type = wanted_type->data.maybe.child_type;
@ -4540,7 +4642,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdCheckSwitchProngs: case IrInstructionIdCheckSwitchProngs:
case IrInstructionIdCheckStatementIsVoid: case IrInstructionIdCheckStatementIsVoid:
case IrInstructionIdTypeName: case IrInstructionIdTypeName:
case IrInstructionIdCanImplicitCast:
case IrInstructionIdDeclRef: case IrInstructionIdDeclRef:
case IrInstructionIdSwitchVar: case IrInstructionIdSwitchVar:
case IrInstructionIdOffsetOf: case IrInstructionIdOffsetOf:
@ -4593,8 +4694,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_asm(g, executable, (IrInstructionAsm *)instruction); return ir_render_asm(g, executable, (IrInstructionAsm *)instruction);
case IrInstructionIdTestNonNull: case IrInstructionIdTestNonNull:
return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction); return ir_render_test_non_null(g, executable, (IrInstructionTestNonNull *)instruction);
case IrInstructionIdUnwrapMaybe: case IrInstructionIdUnwrapOptional:
return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapMaybe *)instruction); return ir_render_unwrap_maybe(g, executable, (IrInstructionUnwrapOptional *)instruction);
case IrInstructionIdClz: case IrInstructionIdClz:
return ir_render_clz(g, executable, (IrInstructionClz *)instruction); return ir_render_clz(g, executable, (IrInstructionClz *)instruction);
case IrInstructionIdCtz: case IrInstructionIdCtz:
@ -4635,8 +4736,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction); return ir_render_unwrap_err_code(g, executable, (IrInstructionUnwrapErrCode *)instruction);
case IrInstructionIdUnwrapErrPayload: case IrInstructionIdUnwrapErrPayload:
return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction); return ir_render_unwrap_err_payload(g, executable, (IrInstructionUnwrapErrPayload *)instruction);
case IrInstructionIdMaybeWrap: case IrInstructionIdOptionalWrap:
return ir_render_maybe_wrap(g, executable, (IrInstructionMaybeWrap *)instruction); return ir_render_maybe_wrap(g, executable, (IrInstructionOptionalWrap *)instruction);
case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapCode:
return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction); return ir_render_err_wrap_code(g, executable, (IrInstructionErrWrapCode *)instruction);
case IrInstructionIdErrWrapPayload: case IrInstructionIdErrWrapPayload:
@ -4866,7 +4967,7 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
} }
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdPromise: case TypeTableEntryIdPromise:
{ {
LLVMValueRef ptr_val = gen_const_val(g, const_val, ""); LLVMValueRef ptr_val = gen_const_val(g, const_val, "");
@ -4914,6 +5015,79 @@ static bool is_llvm_value_unnamed_type(TypeTableEntry *type_entry, LLVMValueRef
return LLVMTypeOf(val) != type_entry->type_ref; return LLVMTypeOf(val) != type_entry->type_ref;
} }
static LLVMValueRef gen_const_val_ptr(CodeGen *g, ConstExprValue *const_val, const char *name) {
render_const_val_global(g, const_val, name);
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
{
ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
render_const_val(g, pointee, "");
render_const_val_global(g, pointee, "");
ConstExprValue *other_val = pointee;
const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
case ConstPtrSpecialBaseArray:
{
ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
assert(array_const_val->type->id == TypeTableEntryIdArray);
if (array_const_val->type->zero_bits) {
// make this a null pointer
TypeTableEntry *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
elem_index);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialBaseStruct:
{
ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
assert(struct_const_val->type->id == TypeTableEntryIdStruct);
if (struct_const_val->type->zero_bits) {
// make this a null pointer
TypeTableEntry *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
size_t gen_field_index =
struct_const_val->type->data.structure.fields[src_field_index].gen_index;
LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
gen_field_index);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialHardCodedAddr:
{
uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
TypeTableEntry *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
case ConstPtrSpecialFunction:
return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
}
zig_unreachable();
}
static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) { static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const char *name) {
TypeTableEntry *type_entry = const_val->type; TypeTableEntry *type_entry = const_val->type;
assert(!type_entry->zero_bits); assert(!type_entry->zero_bits);
@ -4958,23 +5132,19 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
} else { } else {
return LLVMConstNull(LLVMInt1Type()); return LLVMConstNull(LLVMInt1Type());
} }
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
{ {
TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *child_type = type_entry->data.maybe.child_type;
if (child_type->zero_bits) { if (child_type->zero_bits) {
return LLVMConstInt(LLVMInt1Type(), const_val->data.x_maybe ? 1 : 0, false); return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false);
} else if (type_is_codegen_pointer(child_type)) { } else if (type_is_codegen_pointer(child_type)) {
if (const_val->data.x_maybe) { return gen_const_val_ptr(g, const_val, name);
return gen_const_val(g, const_val->data.x_maybe, "");
} else {
return LLVMConstNull(child_type->type_ref);
}
} else { } else {
LLVMValueRef child_val; LLVMValueRef child_val;
LLVMValueRef maybe_val; LLVMValueRef maybe_val;
bool make_unnamed_struct; bool make_unnamed_struct;
if (const_val->data.x_maybe) { if (const_val->data.x_optional) {
child_val = gen_const_val(g, const_val->data.x_maybe, ""); child_val = gen_const_val(g, const_val->data.x_optional, "");
maybe_val = LLVMConstAllOnes(LLVMInt1Type()); maybe_val = LLVMConstAllOnes(LLVMInt1Type());
make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val); make_unnamed_struct = is_llvm_value_unnamed_type(const_val->type, child_val);
@ -5164,78 +5334,7 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst); assert(const_val->data.x_ptr.mut == ConstPtrMutComptimeConst);
return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry); return fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry);
case TypeTableEntryIdPointer: case TypeTableEntryIdPointer:
{ return gen_const_val_ptr(g, const_val, name);
render_const_val_global(g, const_val, name);
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
case ConstPtrSpecialDiscard:
zig_unreachable();
case ConstPtrSpecialRef:
{
ConstExprValue *pointee = const_val->data.x_ptr.data.ref.pointee;
render_const_val(g, pointee, "");
render_const_val_global(g, pointee, "");
ConstExprValue *other_val = pointee;
const_val->global_refs->llvm_value = LLVMConstBitCast(other_val->global_refs->llvm_global, const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
case ConstPtrSpecialBaseArray:
{
ConstExprValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
assert(array_const_val->type->id == TypeTableEntryIdArray);
if (array_const_val->type->zero_bits) {
// make this a null pointer
TypeTableEntry *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val,
elem_index);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialBaseStruct:
{
ConstExprValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val;
assert(struct_const_val->type->id == TypeTableEntryIdStruct);
if (struct_const_val->type->zero_bits) {
// make this a null pointer
TypeTableEntry *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->type_ref),
const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index;
size_t gen_field_index =
struct_const_val->type->data.structure.fields[src_field_index].gen_index;
LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val,
gen_field_index);
LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, const_val->type->type_ref);
const_val->global_refs->llvm_value = ptr_val;
render_const_val_global(g, const_val, "");
return ptr_val;
}
case ConstPtrSpecialHardCodedAddr:
{
uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr;
TypeTableEntry *usize = g->builtin_types.entry_usize;
const_val->global_refs->llvm_value = LLVMConstIntToPtr(LLVMConstInt(usize->type_ref, addr_value, false),
const_val->type->type_ref);
render_const_val_global(g, const_val, "");
return const_val->global_refs->llvm_value;
}
case ConstPtrSpecialFunction:
return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), const_val->type->type_ref);
}
}
zig_unreachable();
case TypeTableEntryIdErrorUnion: case TypeTableEntryIdErrorUnion:
{ {
TypeTableEntry *payload_type = type_entry->data.error_union.payload_type; TypeTableEntry *payload_type = type_entry->data.error_union.payload_type;
@ -5367,55 +5466,6 @@ static void generate_error_name_table(CodeGen *g) {
LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init))); LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init)));
} }
static void generate_enum_name_tables(CodeGen *g) {
TypeTableEntry *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false,
PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0);
TypeTableEntry *str_type = get_slice_type(g, u8_ptr_type);
TypeTableEntry *usize = g->builtin_types.entry_usize;
LLVMValueRef array_ptr_indices[] = {
LLVMConstNull(usize->type_ref),
LLVMConstNull(usize->type_ref),
};
for (size_t enum_i = 0; enum_i < g->name_table_enums.length; enum_i += 1) {
TypeTableEntry *enum_type = g->name_table_enums.at(enum_i);
assert(enum_type->id == TypeTableEntryIdEnum);
size_t field_count = enum_type->data.enumeration.src_field_count;
LLVMValueRef *values = allocate<LLVMValueRef>(field_count);
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
Buf *name = enum_type->data.enumeration.fields[field_i].name;
LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true);
LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), "");
LLVMSetInitializer(str_global, str_init);
LLVMSetLinkage(str_global, LLVMPrivateLinkage);
LLVMSetGlobalConstant(str_global, true);
LLVMSetUnnamedAddr(str_global, true);
LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init)));
LLVMValueRef fields[] = {
LLVMConstGEP(str_global, array_ptr_indices, 2),
LLVMConstInt(g->builtin_types.entry_usize->type_ref, buf_len(name), false),
};
values[field_i] = LLVMConstNamedStruct(str_type->type_ref, fields, 2);
}
LLVMValueRef name_table_init = LLVMConstArray(str_type->type_ref, values, (unsigned)field_count);
Buf *table_name = get_mangled_name(g, buf_sprintf("%s_name_table", buf_ptr(&enum_type->name)), false);
LLVMValueRef name_table = LLVMAddGlobal(g->module, LLVMTypeOf(name_table_init), buf_ptr(table_name));
LLVMSetInitializer(name_table, name_table_init);
LLVMSetLinkage(name_table, LLVMPrivateLinkage);
LLVMSetGlobalConstant(name_table, true);
LLVMSetUnnamedAddr(name_table, true);
LLVMSetAlignment(name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(name_table_init)));
enum_type->data.enumeration.name_table = name_table;
}
}
static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) { static void build_all_basic_blocks(CodeGen *g, FnTableEntry *fn) {
IrExecutable *executable = &fn->analyzed_executable; IrExecutable *executable = &fn->analyzed_executable;
assert(executable->basic_block_list.length > 0); assert(executable->basic_block_list.length > 0);
@ -5512,7 +5562,6 @@ static void do_code_gen(CodeGen *g) {
} }
generate_error_name_table(g); generate_error_name_table(g);
generate_enum_name_tables(g);
// Generate module level variables // Generate module level variables
for (size_t i = 0; i < g->global_vars.length; i += 1) { for (size_t i = 0; i < g->global_vars.length; i += 1) {
@ -5651,8 +5700,8 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdSlice) { } else if (instruction->id == IrInstructionIdSlice) {
IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
slot = &slice_instruction->tmp_ptr; slot = &slice_instruction->tmp_ptr;
} else if (instruction->id == IrInstructionIdMaybeWrap) { } else if (instruction->id == IrInstructionIdOptionalWrap) {
IrInstructionMaybeWrap *maybe_wrap_instruction = (IrInstructionMaybeWrap *)instruction; IrInstructionOptionalWrap *maybe_wrap_instruction = (IrInstructionOptionalWrap *)instruction;
slot = &maybe_wrap_instruction->tmp_ptr; slot = &maybe_wrap_instruction->tmp_ptr;
} else if (instruction->id == IrInstructionIdErrWrapPayload) { } else if (instruction->id == IrInstructionIdErrWrapPayload) {
IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction; IrInstructionErrWrapPayload *err_wrap_payload_instruction = (IrInstructionErrWrapPayload *)instruction;
@ -6192,7 +6241,6 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1);
create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1);
create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1);
create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2);
create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1);
create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6);
create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6);
@ -6250,13 +6298,7 @@ static const char *build_mode_to_str(BuildMode build_mode) {
zig_unreachable(); zig_unreachable();
} }
static void define_builtin_compile_vars(CodeGen *g) { Buf *codegen_generate_builtin_source(CodeGen *g) {
if (g->std_package == nullptr)
return;
const char *builtin_zig_basename = "builtin.zig";
Buf *builtin_zig_path = buf_alloc();
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
Buf *contents = buf_alloc(); Buf *contents = buf_alloc();
// Modifications to this struct must be coordinated with code that does anything with // Modifications to this struct must be coordinated with code that does anything with
@ -6396,7 +6438,6 @@ static void define_builtin_compile_vars(CodeGen *g) {
const TypeTableEntryId id = type_id_at_index(i); const TypeTableEntryId id = type_id_at_index(i);
buf_appendf(contents, " %s,\n", type_id_name(id)); buf_appendf(contents, " %s,\n", type_id_name(id));
} }
buf_appendf(contents, " Slice,\n");
buf_appendf(contents, "};\n\n"); buf_appendf(contents, "};\n\n");
} }
{ {
@ -6409,14 +6450,13 @@ static void define_builtin_compile_vars(CodeGen *g) {
" Int: Int,\n" " Int: Int,\n"
" Float: Float,\n" " Float: Float,\n"
" Pointer: Pointer,\n" " Pointer: Pointer,\n"
" Slice: Slice,\n"
" Array: Array,\n" " Array: Array,\n"
" Struct: Struct,\n" " Struct: Struct,\n"
" ComptimeFloat: void,\n" " ComptimeFloat: void,\n"
" ComptimeInt: void,\n" " ComptimeInt: void,\n"
" Undefined: void,\n" " Undefined: void,\n"
" Null: void,\n" " Null: void,\n"
" Nullable: Nullable,\n" " Optional: Optional,\n"
" ErrorUnion: ErrorUnion,\n" " ErrorUnion: ErrorUnion,\n"
" ErrorSet: ErrorSet,\n" " ErrorSet: ErrorSet,\n"
" Enum: Enum,\n" " Enum: Enum,\n"
@ -6439,13 +6479,18 @@ static void define_builtin_compile_vars(CodeGen *g) {
" };\n" " };\n"
"\n" "\n"
" pub const Pointer = struct {\n" " pub const Pointer = struct {\n"
" size: Size,\n"
" is_const: bool,\n" " is_const: bool,\n"
" is_volatile: bool,\n" " is_volatile: bool,\n"
" alignment: u32,\n" " alignment: u32,\n"
" child: type,\n" " child: type,\n"
" };\n"
"\n" "\n"
" pub const Slice = Pointer;\n" " pub const Size = enum {\n"
" One,\n"
" Many,\n"
" Slice,\n"
" };\n"
" };\n"
"\n" "\n"
" pub const Array = struct {\n" " pub const Array = struct {\n"
" len: usize,\n" " len: usize,\n"
@ -6470,7 +6515,7 @@ static void define_builtin_compile_vars(CodeGen *g) {
" defs: []Definition,\n" " defs: []Definition,\n"
" };\n" " };\n"
"\n" "\n"
" pub const Nullable = struct {\n" " pub const Optional = struct {\n"
" child: type,\n" " child: type,\n"
" };\n" " };\n"
"\n" "\n"
@ -6619,6 +6664,19 @@ static void define_builtin_compile_vars(CodeGen *g) {
buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n"); buf_appendf(contents, "pub const __zig_test_fn_slice = {}; // overwritten later\n");
return contents;
}
static void define_builtin_compile_vars(CodeGen *g) {
if (g->std_package == nullptr)
return;
const char *builtin_zig_basename = "builtin.zig";
Buf *builtin_zig_path = buf_alloc();
os_path_join(g->cache_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path);
Buf *contents = codegen_generate_builtin_source(g);
ensure_cache_dir(g); ensure_cache_dir(g);
os_write_file(builtin_zig_path, contents); os_write_file(builtin_zig_path, contents);
@ -7032,7 +7090,7 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry
case TypeTableEntryIdArray: case TypeTableEntryIdArray:
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type);
return; return;
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type);
return; return;
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
@ -7121,7 +7179,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf)); buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf));
break; break;
} }
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
{ {
TypeTableEntry *child_type = type_entry->data.maybe.child_type; TypeTableEntry *child_type = type_entry->data.maybe.child_type;
if (child_type->zero_bits) { if (child_type->zero_bits) {
@ -7335,7 +7393,7 @@ static void gen_h_file(CodeGen *g) {
case TypeTableEntryIdBlock: case TypeTableEntryIdBlock:
case TypeTableEntryIdBoundFn: case TypeTableEntryIdBoundFn:
case TypeTableEntryIdArgTuple: case TypeTableEntryIdArgTuple:
case TypeTableEntryIdMaybe: case TypeTableEntryIdOptional:
case TypeTableEntryIdFn: case TypeTableEntryIdFn:
case TypeTableEntryIdPromise: case TypeTableEntryIdPromise:
zig_unreachable(); zig_unreachable();

View File

@ -59,5 +59,7 @@ void codegen_add_object(CodeGen *g, Buf *object_path);
void codegen_translate_c(CodeGen *g, Buf *path); void codegen_translate_c(CodeGen *g, Buf *path);
Buf *codegen_generate_builtin_source(CodeGen *g);
#endif #endif

1060
src/ir.cpp

File diff suppressed because it is too large Load Diff

View File

@ -148,7 +148,7 @@ static const char *ir_un_op_id_str(IrUnOp op_id) {
return "-%"; return "-%";
case IrUnOpDereference: case IrUnOpDereference:
return "*"; return "*";
case IrUnOpMaybe: case IrUnOpOptional:
return "?"; return "?";
} }
zig_unreachable(); zig_unreachable();
@ -481,7 +481,7 @@ static void ir_print_test_null(IrPrint *irp, IrInstructionTestNonNull *instructi
fprintf(irp->f, " != null"); fprintf(irp->f, " != null");
} }
static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapMaybe *instruction) { static void ir_print_unwrap_maybe(IrPrint *irp, IrInstructionUnwrapOptional *instruction) {
fprintf(irp->f, "&??*"); fprintf(irp->f, "&??*");
ir_print_other_instruction(irp, instruction->value); ir_print_other_instruction(irp, instruction->value);
if (!instruction->safety_check_on) { if (!instruction->safety_check_on) {
@ -777,7 +777,7 @@ static void ir_print_unwrap_err_payload(IrPrint *irp, IrInstructionUnwrapErrPayl
} }
} }
static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionMaybeWrap *instruction) { static void ir_print_maybe_wrap(IrPrint *irp, IrInstructionOptionalWrap *instruction) {
fprintf(irp->f, "@maybeWrap("); fprintf(irp->f, "@maybeWrap(");
ir_print_other_instruction(irp, instruction->value); ir_print_other_instruction(irp, instruction->value);
fprintf(irp->f, ")"); fprintf(irp->f, ")");
@ -913,14 +913,6 @@ static void ir_print_tag_name(IrPrint *irp, IrInstructionTagName *instruction) {
ir_print_other_instruction(irp, instruction->target); ir_print_other_instruction(irp, instruction->target);
} }
static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCast *instruction) {
fprintf(irp->f, "@canImplicitCast(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->target_value);
fprintf(irp->f, ")");
}
static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) { static void ir_print_ptr_type(IrPrint *irp, IrInstructionPtrType *instruction) {
fprintf(irp->f, "&"); fprintf(irp->f, "&");
if (instruction->align_value != nullptr) { if (instruction->align_value != nullptr) {
@ -1040,7 +1032,7 @@ static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) { static void ir_print_error_return_trace(IrPrint *irp, IrInstructionErrorReturnTrace *instruction) {
fprintf(irp->f, "@errorReturnTrace("); fprintf(irp->f, "@errorReturnTrace(");
switch (instruction->nullable) { switch (instruction->optional) {
case IrInstructionErrorReturnTrace::Null: case IrInstructionErrorReturnTrace::Null:
fprintf(irp->f, "Null"); fprintf(irp->f, "Null");
break; break;
@ -1356,8 +1348,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTestNonNull: case IrInstructionIdTestNonNull:
ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction); ir_print_test_null(irp, (IrInstructionTestNonNull *)instruction);
break; break;
case IrInstructionIdUnwrapMaybe: case IrInstructionIdUnwrapOptional:
ir_print_unwrap_maybe(irp, (IrInstructionUnwrapMaybe *)instruction); ir_print_unwrap_maybe(irp, (IrInstructionUnwrapOptional *)instruction);
break; break;
case IrInstructionIdCtz: case IrInstructionIdCtz:
ir_print_ctz(irp, (IrInstructionCtz *)instruction); ir_print_ctz(irp, (IrInstructionCtz *)instruction);
@ -1473,8 +1465,8 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdUnwrapErrPayload: case IrInstructionIdUnwrapErrPayload:
ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction); ir_print_unwrap_err_payload(irp, (IrInstructionUnwrapErrPayload *)instruction);
break; break;
case IrInstructionIdMaybeWrap: case IrInstructionIdOptionalWrap:
ir_print_maybe_wrap(irp, (IrInstructionMaybeWrap *)instruction); ir_print_maybe_wrap(irp, (IrInstructionOptionalWrap *)instruction);
break; break;
case IrInstructionIdErrWrapCode: case IrInstructionIdErrWrapCode:
ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction); ir_print_err_wrap_code(irp, (IrInstructionErrWrapCode *)instruction);
@ -1524,9 +1516,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTagName: case IrInstructionIdTagName:
ir_print_tag_name(irp, (IrInstructionTagName *)instruction); ir_print_tag_name(irp, (IrInstructionTagName *)instruction);
break; break;
case IrInstructionIdCanImplicitCast:
ir_print_can_implicit_cast(irp, (IrInstructionCanImplicitCast *)instruction);
break;
case IrInstructionIdPtrType: case IrInstructionIdPtrType:
ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction); ir_print_ptr_type(irp, (IrInstructionPtrType *)instruction);
break; break;

View File

@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) {
} }
} }
static void construct_linker_job_wasm(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("--relocatable"); // So lld doesn't look for _start.
lj->args.append("-o");
lj->args.append(buf_ptr(&lj->out_file));
// .o files
for (size_t i = 0; i < g->link_objects.length; i += 1) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
}
//static bool is_target_cyg_mingw(const ZigTarget *target) { //static bool is_target_cyg_mingw(const ZigTarget *target) {
// return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || // return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) ||
// (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); // (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU);
@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) {
case ZigLLVM_MachO: case ZigLLVM_MachO:
return construct_linker_job_macho(lj); return construct_linker_job_macho(lj);
case ZigLLVM_Wasm: case ZigLLVM_Wasm:
zig_panic("TODO link wasm"); return construct_linker_job_wasm(lj);
} }
} }

View File

@ -23,6 +23,7 @@ static int usage(const char *arg0) {
" build-exe [source] create executable from source or object files\n" " build-exe [source] create executable from source or object files\n"
" build-lib [source] create library from source or object files\n" " build-lib [source] create library from source or object files\n"
" build-obj [source] create object from source or assembly\n" " build-obj [source] create object from source or assembly\n"
" builtin show the source code of that @import(\"builtin\")\n"
" run [source] create executable and run immediately\n" " run [source] create executable and run immediately\n"
" translate-c [source] convert c code to zig code\n" " translate-c [source] convert c code to zig code\n"
" targets list available compilation targets\n" " targets list available compilation targets\n"
@ -214,6 +215,7 @@ static Buf *resolve_zig_lib_dir(void) {
enum Cmd { enum Cmd {
CmdInvalid, CmdInvalid,
CmdBuild, CmdBuild,
CmdBuiltin,
CmdRun, CmdRun,
CmdTest, CmdTest,
CmdVersion, CmdVersion,
@ -664,6 +666,8 @@ int main(int argc, char **argv) {
out_type = OutTypeExe; out_type = OutTypeExe;
} else if (strcmp(arg, "targets") == 0) { } else if (strcmp(arg, "targets") == 0) {
cmd = CmdTargets; cmd = CmdTargets;
} else if (strcmp(arg, "builtin") == 0) {
cmd = CmdBuiltin;
} else { } else {
fprintf(stderr, "Unrecognized command: %s\n", arg); fprintf(stderr, "Unrecognized command: %s\n", arg);
return usage(arg0); return usage(arg0);
@ -681,6 +685,7 @@ int main(int argc, char **argv) {
return usage(arg0); return usage(arg0);
} }
break; break;
case CmdBuiltin:
case CmdVersion: case CmdVersion:
case CmdZen: case CmdZen:
case CmdTargets: case CmdTargets:
@ -727,6 +732,16 @@ int main(int argc, char **argv) {
} }
switch (cmd) { switch (cmd) {
case CmdBuiltin: {
Buf *zig_lib_dir_buf = resolve_zig_lib_dir();
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, zig_lib_dir_buf);
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout)));
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
case CmdRun: case CmdRun:
case CmdBuild: case CmdBuild:
case CmdTranslateC: case CmdTranslateC:

View File

@ -1046,12 +1046,11 @@ static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index
} }
/* /*
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | PtrDerefExpression | SliceExpression) SuffixOpExpression = ("async" option("&lt;" SuffixOpExpression "&gt;") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression | ".*" | ".?")
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen) FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
ArrayAccessExpression : token(LBracket) Expression token(RBracket) ArrayAccessExpression : token(LBracket) Expression token(RBracket)
SliceExpression = "[" Expression ".." option(Expression) "]" SliceExpression = "[" Expression ".." option(Expression) "]"
FieldAccessExpression : token(Dot) token(Symbol) FieldAccessExpression : token(Dot) token(Symbol)
PtrDerefExpression = ".*"
StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression StructLiteralField : token(Dot) token(Symbol) token(Eq) Expression
*/ */
static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) { static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@ -1148,6 +1147,13 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token); AstNode *node = ast_create_node(pc, NodeTypePtrDeref, first_token);
node->data.ptr_deref_expr.target = primary_expr; node->data.ptr_deref_expr.target = primary_expr;
primary_expr = node;
} else if (token->id == TokenIdQuestion) {
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeUnwrapOptional, first_token);
node->data.unwrap_optional.expr = primary_expr;
primary_expr = node; primary_expr = node;
} else { } else {
ast_invalid_token_error(pc, token); ast_invalid_token_error(pc, token);
@ -1165,8 +1171,7 @@ static PrefixOp tok_to_prefix_op(Token *token) {
case TokenIdDash: return PrefixOpNegation; case TokenIdDash: return PrefixOpNegation;
case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdMinusPercent: return PrefixOpNegationWrap;
case TokenIdTilde: return PrefixOpBinNot; case TokenIdTilde: return PrefixOpBinNot;
case TokenIdMaybe: return PrefixOpMaybe; case TokenIdQuestion: return PrefixOpOptional;
case TokenIdDoubleQuestion: return PrefixOpUnwrapMaybe;
case TokenIdAmpersand: return PrefixOpAddrOf; case TokenIdAmpersand: return PrefixOpAddrOf;
default: return PrefixOpInvalid; default: return PrefixOpInvalid;
} }
@ -2304,8 +2309,8 @@ static BinOpType ast_parse_ass_op(ParseContext *pc, size_t *token_index, bool ma
} }
/* /*
UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression UnwrapExpression : BoolOrExpression (UnwrapOptional | UnwrapError) | BoolOrExpression
UnwrapMaybe : "??" BoolOrExpression UnwrapOptional = "orelse" Expression
UnwrapError = "catch" option("|" Symbol "|") Expression UnwrapError = "catch" option("|" Symbol "|") Expression
*/ */
static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) { static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
@ -2315,14 +2320,14 @@ static AstNode *ast_parse_unwrap_expr(ParseContext *pc, size_t *token_index, boo
Token *token = &pc->tokens->at(*token_index); Token *token = &pc->tokens->at(*token_index);
if (token->id == TokenIdDoubleQuestion) { if (token->id == TokenIdKeywordOrElse) {
*token_index += 1; *token_index += 1;
AstNode *rhs = ast_parse_expression(pc, token_index, true); AstNode *rhs = ast_parse_expression(pc, token_index, true);
AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token); AstNode *node = ast_create_node(pc, NodeTypeBinOpExpr, token);
node->data.bin_op_expr.op1 = lhs; node->data.bin_op_expr.op1 = lhs;
node->data.bin_op_expr.bin_op = BinOpTypeUnwrapMaybe; node->data.bin_op_expr.bin_op = BinOpTypeUnwrapOptional;
node->data.bin_op_expr.op2 = rhs; node->data.bin_op_expr.op2 = rhs;
return node; return node;
@ -3028,6 +3033,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
case NodeTypePtrDeref: case NodeTypePtrDeref:
visit_field(&node->data.ptr_deref_expr.target, visit, context); visit_field(&node->data.ptr_deref_expr.target, visit, context);
break; break;
case NodeTypeUnwrapOptional:
visit_field(&node->data.unwrap_optional.expr, visit, context);
break;
case NodeTypeUse: case NodeTypeUse:
visit_field(&node->data.use.expr, visit, context); visit_field(&node->data.use.expr, visit, context);
break; break;

View File

@ -596,12 +596,15 @@ void resolve_target_object_format(ZigTarget *target) {
case ZigLLVM_tce: case ZigLLVM_tce:
case ZigLLVM_tcele: case ZigLLVM_tcele:
case ZigLLVM_thumbeb: case ZigLLVM_thumbeb:
case ZigLLVM_wasm32:
case ZigLLVM_wasm64:
case ZigLLVM_xcore: case ZigLLVM_xcore:
target->oformat= ZigLLVM_ELF; target->oformat= ZigLLVM_ELF;
return; return;
case ZigLLVM_wasm32:
case ZigLLVM_wasm64:
target->oformat = ZigLLVM_Wasm;
return;
case ZigLLVM_ppc: case ZigLLVM_ppc:
case ZigLLVM_ppc64: case ZigLLVM_ppc64:
if (is_os_darwin(target)) { if (is_os_darwin(target)) {

View File

@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"noalias", TokenIdKeywordNoAlias}, {"noalias", TokenIdKeywordNoAlias},
{"null", TokenIdKeywordNull}, {"null", TokenIdKeywordNull},
{"or", TokenIdKeywordOr}, {"or", TokenIdKeywordOr},
{"orelse", TokenIdKeywordOrElse},
{"packed", TokenIdKeywordPacked}, {"packed", TokenIdKeywordPacked},
{"promise", TokenIdKeywordPromise}, {"promise", TokenIdKeywordPromise},
{"pub", TokenIdKeywordPub}, {"pub", TokenIdKeywordPub},
@ -215,7 +216,6 @@ enum TokenizeState {
TokenizeStateSawGreaterThanGreaterThan, TokenizeStateSawGreaterThanGreaterThan,
TokenizeStateSawDot, TokenizeStateSawDot,
TokenizeStateSawDotDot, TokenizeStateSawDotDot,
TokenizeStateSawQuestionMark,
TokenizeStateSawAtSign, TokenizeStateSawAtSign,
TokenizeStateCharCode, TokenizeStateCharCode,
TokenizeStateError, TokenizeStateError,
@ -532,6 +532,10 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdComma); begin_token(&t, TokenIdComma);
end_token(&t); end_token(&t);
break; break;
case '?':
begin_token(&t, TokenIdQuestion);
end_token(&t);
break;
case '{': case '{':
begin_token(&t, TokenIdLBrace); begin_token(&t, TokenIdLBrace);
end_token(&t); end_token(&t);
@ -624,33 +628,10 @@ void tokenize(Buf *buf, Tokenization *out) {
begin_token(&t, TokenIdDot); begin_token(&t, TokenIdDot);
t.state = TokenizeStateSawDot; t.state = TokenizeStateSawDot;
break; break;
case '?':
begin_token(&t, TokenIdMaybe);
t.state = TokenizeStateSawQuestionMark;
break;
default: default:
invalid_char_error(&t, c); invalid_char_error(&t, c);
} }
break; break;
case TokenizeStateSawQuestionMark:
switch (c) {
case '?':
set_token_id(&t, t.cur_tok, TokenIdDoubleQuestion);
end_token(&t);
t.state = TokenizeStateStart;
break;
case '=':
set_token_id(&t, t.cur_tok, TokenIdMaybeAssign);
end_token(&t);
t.state = TokenizeStateStart;
break;
default:
t.pos -= 1;
end_token(&t);
t.state = TokenizeStateStart;
continue;
}
break;
case TokenizeStateSawDot: case TokenizeStateSawDot:
switch (c) { switch (c) {
case '.': case '.':
@ -1485,7 +1466,6 @@ void tokenize(Buf *buf, Tokenization *out) {
case TokenizeStateSawGreaterThan: case TokenizeStateSawGreaterThan:
case TokenizeStateSawGreaterThanGreaterThan: case TokenizeStateSawGreaterThanGreaterThan:
case TokenizeStateSawDot: case TokenizeStateSawDot:
case TokenizeStateSawQuestionMark:
case TokenizeStateSawAtSign: case TokenizeStateSawAtSign:
case TokenizeStateSawStarPercent: case TokenizeStateSawStarPercent:
case TokenizeStateSawPlusPercent: case TokenizeStateSawPlusPercent:
@ -1550,7 +1530,6 @@ const char * token_name(TokenId id) {
case TokenIdDash: return "-"; case TokenIdDash: return "-";
case TokenIdDivEq: return "/="; case TokenIdDivEq: return "/=";
case TokenIdDot: return "."; case TokenIdDot: return ".";
case TokenIdDoubleQuestion: return "??";
case TokenIdEllipsis2: return ".."; case TokenIdEllipsis2: return "..";
case TokenIdEllipsis3: return "..."; case TokenIdEllipsis3: return "...";
case TokenIdEof: return "EOF"; case TokenIdEof: return "EOF";
@ -1587,6 +1566,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordNoAlias: return "noalias"; case TokenIdKeywordNoAlias: return "noalias";
case TokenIdKeywordNull: return "null"; case TokenIdKeywordNull: return "null";
case TokenIdKeywordOr: return "or"; case TokenIdKeywordOr: return "or";
case TokenIdKeywordOrElse: return "orelse";
case TokenIdKeywordPacked: return "packed"; case TokenIdKeywordPacked: return "packed";
case TokenIdKeywordPromise: return "promise"; case TokenIdKeywordPromise: return "promise";
case TokenIdKeywordPub: return "pub"; case TokenIdKeywordPub: return "pub";
@ -1609,8 +1589,7 @@ const char * token_name(TokenId id) {
case TokenIdLBrace: return "{"; case TokenIdLBrace: return "{";
case TokenIdLBracket: return "["; case TokenIdLBracket: return "[";
case TokenIdLParen: return "("; case TokenIdLParen: return "(";
case TokenIdMaybe: return "?"; case TokenIdQuestion: return "?";
case TokenIdMaybeAssign: return "?=";
case TokenIdMinusEq: return "-="; case TokenIdMinusEq: return "-=";
case TokenIdMinusPercent: return "-%"; case TokenIdMinusPercent: return "-%";
case TokenIdMinusPercentEq: return "-%="; case TokenIdMinusPercentEq: return "-%=";

View File

@ -41,7 +41,6 @@ enum TokenId {
TokenIdDash, TokenIdDash,
TokenIdDivEq, TokenIdDivEq,
TokenIdDot, TokenIdDot,
TokenIdDoubleQuestion,
TokenIdEllipsis2, TokenIdEllipsis2,
TokenIdEllipsis3, TokenIdEllipsis3,
TokenIdEof, TokenIdEof,
@ -76,6 +75,7 @@ enum TokenId {
TokenIdKeywordNoAlias, TokenIdKeywordNoAlias,
TokenIdKeywordNull, TokenIdKeywordNull,
TokenIdKeywordOr, TokenIdKeywordOr,
TokenIdKeywordOrElse,
TokenIdKeywordPacked, TokenIdKeywordPacked,
TokenIdKeywordPromise, TokenIdKeywordPromise,
TokenIdKeywordPub, TokenIdKeywordPub,
@ -100,8 +100,7 @@ enum TokenId {
TokenIdLBrace, TokenIdLBrace,
TokenIdLBracket, TokenIdLBracket,
TokenIdLParen, TokenIdLParen,
TokenIdMaybe, TokenIdQuestion,
TokenIdMaybeAssign,
TokenIdMinusEq, TokenIdMinusEq,
TokenIdMinusPercent, TokenIdMinusPercent,
TokenIdMinusPercentEq, TokenIdMinusPercentEq,
@ -170,6 +169,8 @@ struct Token {
TokenCharLit char_lit; TokenCharLit char_lit;
} data; } data;
}; };
// work around conflicting name Token which is also found in libclang
typedef Token ZigToken;
struct Tokenization { struct Tokenization {
ZigList<Token> *tokens; ZigList<Token> *tokens;

View File

@ -260,6 +260,12 @@ static AstNode *trans_create_node_prefix_op(Context *c, PrefixOp op, AstNode *ch
return node; return node;
} }
static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child_node) {
AstNode *node = trans_create_node(c, NodeTypeUnwrapOptional);
node->data.unwrap_optional.expr = child_node;
return node;
}
static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) { static AstNode *trans_create_node_bin_op(Context *c, AstNode *lhs_node, BinOpType op, AstNode *rhs_node) {
AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); AstNode *node = trans_create_node(c, NodeTypeBinOpExpr);
node->data.bin_op_expr.op1 = lhs_node; node->data.bin_op_expr.op1 = lhs_node;
@ -276,8 +282,11 @@ static AstNode *maybe_suppress_result(Context *c, ResultUsed result_used, AstNod
node); node);
} }
static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node) { static AstNode *trans_create_node_ptr_type(Context *c, bool is_const, bool is_volatile, AstNode *child_node, PtrLen ptr_len) {
AstNode *node = trans_create_node(c, NodeTypePointerType); AstNode *node = trans_create_node(c, NodeTypePointerType);
node->data.pointer_type.star_token = allocate<ZigToken>(1);
node->data.pointer_type.star_token->id = (ptr_len == PtrLenSingle) ? TokenIdStar: TokenIdBracketStarBracket;
node->data.pointer_type.is_const = is_const;
node->data.pointer_type.is_const = is_const; node->data.pointer_type.is_const = is_const;
node->data.pointer_type.is_volatile = is_volatile; node->data.pointer_type.is_volatile = is_volatile;
node->data.pointer_type.op_expr = child_node; node->data.pointer_type.op_expr = child_node;
@ -379,7 +388,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
fn_def->data.fn_def.fn_proto = fn_proto; fn_def->data.fn_def.fn_proto = fn_proto;
fn_proto->data.fn_proto.fn_def_node = fn_def; fn_proto->data.fn_proto.fn_def_node = fn_def;
AstNode *unwrap_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, ref_node); AstNode *unwrap_node = trans_create_node_unwrap_null(c, ref_node);
AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr); AstNode *fn_call_node = trans_create_node(c, NodeTypeFnCallExpr);
fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node; fn_call_node->data.fn_call_expr.fn_ref_expr = unwrap_node;
@ -406,10 +415,6 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
return fn_def; return fn_def;
} }
static AstNode *trans_create_node_unwrap_null(Context *c, AstNode *child) {
return trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, child);
}
static AstNode *get_global(Context *c, Buf *name) { static AstNode *get_global(Context *c, Buf *name) {
{ {
auto entry = c->global_table.maybe_get(name); auto entry = c->global_table.maybe_get(name);
@ -731,6 +736,30 @@ static bool qual_type_has_wrapping_overflow(Context *c, QualType qt) {
} }
} }
static bool type_is_opaque(Context *c, const Type *ty, const SourceLocation &source_loc) {
switch (ty->getTypeClass()) {
case Type::Builtin: {
const BuiltinType *builtin_ty = static_cast<const BuiltinType*>(ty);
return builtin_ty->getKind() == BuiltinType::Void;
}
case Type::Record: {
const RecordType *record_ty = static_cast<const RecordType*>(ty);
return record_ty->getDecl()->getDefinition() == nullptr;
}
case Type::Elaborated: {
const ElaboratedType *elaborated_ty = static_cast<const ElaboratedType*>(ty);
return type_is_opaque(c, elaborated_ty->getNamedType().getTypePtr(), source_loc);
}
case Type::Typedef: {
const TypedefType *typedef_ty = static_cast<const TypedefType*>(ty);
const TypedefNameDecl *typedef_decl = typedef_ty->getDecl();
return type_is_opaque(c, typedef_decl->getUnderlyingType().getTypePtr(), source_loc);
}
default:
return false;
}
}
static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) { static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &source_loc) {
switch (ty->getTypeClass()) { switch (ty->getTypeClass()) {
case Type::Builtin: case Type::Builtin:
@ -859,12 +888,14 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
} }
if (qual_type_child_is_fn_proto(child_qt)) { if (qual_type_child_is_fn_proto(child_qt)) {
return trans_create_node_prefix_op(c, PrefixOpMaybe, child_node); return trans_create_node_prefix_op(c, PrefixOpOptional, child_node);
} }
PtrLen ptr_len = type_is_opaque(c, child_qt.getTypePtr(), source_loc) ? PtrLenSingle : PtrLenUnknown;
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_node); child_qt.isVolatileQualified(), child_node, ptr_len);
return trans_create_node_prefix_op(c, PrefixOpMaybe, pointer_node); return trans_create_node_prefix_op(c, PrefixOpOptional, pointer_node);
} }
case Type::Typedef: case Type::Typedef:
{ {
@ -1048,7 +1079,7 @@ static AstNode *trans_type(Context *c, const Type *ty, const SourceLocation &sou
return nullptr; return nullptr;
} }
AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(), AstNode *pointer_node = trans_create_node_ptr_type(c, child_qt.isConstQualified(),
child_qt.isVolatileQualified(), child_type_node); child_qt.isVolatileQualified(), child_type_node, PtrLenUnknown);
return pointer_node; return pointer_node;
} }
case Type::BlockPointer: case Type::BlockPointer:
@ -1941,7 +1972,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc
bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType()); bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType());
if (is_fn_ptr) if (is_fn_ptr)
return value_node; return value_node;
AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); AstNode *unwrapped = trans_create_node_unwrap_null(c, value_node);
return trans_create_node_ptr_deref(c, unwrapped); return trans_create_node_ptr_deref(c, unwrapped);
} }
case UO_Plus: case UO_Plus:
@ -2572,7 +2603,7 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *
} }
} }
if (callee_node == nullptr) { if (callee_node == nullptr) {
callee_node = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, callee_raw_node); callee_node = trans_create_node_unwrap_null(c, callee_raw_node);
} }
} else { } else {
callee_node = callee_raw_node; callee_node = callee_raw_node;
@ -4286,7 +4317,7 @@ static AstNode *trans_lookup_ast_maybe_fn(Context *c, AstNode *ref_node) {
return nullptr; return nullptr;
if (prefix_node->type != NodeTypePrefixOpExpr) if (prefix_node->type != NodeTypePrefixOpExpr)
return nullptr; return nullptr;
if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpMaybe) if (prefix_node->data.prefix_op_expr.prefix_op != PrefixOpOptional)
return nullptr; return nullptr;
AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr; AstNode *fn_proto_node = prefix_node->data.prefix_op_expr.primary_expr;
@ -4462,7 +4493,7 @@ static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *t
} else if (first_tok->id == CTokIdAsterisk) { } else if (first_tok->id == CTokIdAsterisk) {
*tok_i += 1; *tok_i += 1;
node = trans_create_node_ptr_type(c, false, false, node); node = trans_create_node_ptr_type(c, false, false, node, PtrLenUnknown);
} else { } else {
return node; return node;
} }

View File

@ -853,7 +853,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_
return lld::mach_o::link(array_ref_args, diag); return lld::mach_o::link(array_ref_args, diag);
case ZigLLVM_Wasm: case ZigLLVM_Wasm:
assert(false); // TODO ZigLLDLink for Wasm return lld::wasm::link(array_ref_args, false, diag);
} }
assert(false); // unreachable assert(false); // unreachable
abort(); abort();

View File

@ -1,6 +1,7 @@
const std = @import("index.zig"); const std = @import("index.zig");
const debug = std.debug; const debug = std.debug;
const assert = debug.assert; const assert = debug.assert;
const assertError = debug.assertError;
const mem = std.mem; const mem = std.mem;
const Allocator = mem.Allocator; const Allocator = mem.Allocator;
@ -28,20 +29,33 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
}; };
} }
pub fn deinit(l: *const Self) void { pub fn deinit(self: *const Self) void {
l.allocator.free(l.items); self.allocator.free(self.items);
} }
pub fn toSlice(l: *const Self) []align(A) T { pub fn toSlice(self: *const Self) []align(A) T {
return l.items[0..l.len]; return self.items[0..self.len];
} }
pub fn toSliceConst(l: *const Self) []align(A) const T { pub fn toSliceConst(self: *const Self) []align(A) const T {
return l.items[0..l.len]; return self.items[0..self.len];
} }
pub fn at(l: *const Self, n: usize) T { pub fn at(self: *const Self, n: usize) T {
return l.toSliceConst()[n]; return self.toSliceConst()[n];
}
/// Sets the value at index `i`, or returns `error.OutOfBounds` if
/// the index is not in range.
pub fn setOrError(self: *const Self, i: usize, item: *const T) !void {
if (i >= self.len) return error.OutOfBounds;
self.items[i] = item.*;
}
/// Sets the value at index `i`, asserting that the value is in range.
pub fn set(self: *const Self, i: usize, item: *const T) void {
assert(i < self.len);
self.items[i] = item.*;
} }
pub fn count(self: *const Self) usize { pub fn count(self: *const Self) usize {
@ -67,58 +81,58 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
return result; return result;
} }
pub fn insert(l: *Self, n: usize, item: *const T) !void { pub fn insert(self: *Self, n: usize, item: *const T) !void {
try l.ensureCapacity(l.len + 1); try self.ensureCapacity(self.len + 1);
l.len += 1; self.len += 1;
mem.copy(T, l.items[n + 1 .. l.len], l.items[n .. l.len - 1]); mem.copy(T, self.items[n + 1 .. self.len], self.items[n .. self.len - 1]);
l.items[n] = item.*; self.items[n] = item.*;
} }
pub fn insertSlice(l: *Self, n: usize, items: []align(A) const T) !void { pub fn insertSlice(self: *Self, n: usize, items: []align(A) const T) !void {
try l.ensureCapacity(l.len + items.len); try self.ensureCapacity(self.len + items.len);
l.len += items.len; self.len += items.len;
mem.copy(T, l.items[n + items.len .. l.len], l.items[n .. l.len - items.len]); mem.copy(T, self.items[n + items.len .. self.len], self.items[n .. self.len - items.len]);
mem.copy(T, l.items[n .. n + items.len], items); mem.copy(T, self.items[n .. n + items.len], items);
} }
pub fn append(l: *Self, item: *const T) !void { pub fn append(self: *Self, item: *const T) !void {
const new_item_ptr = try l.addOne(); const new_item_ptr = try self.addOne();
new_item_ptr.* = item.*; new_item_ptr.* = item.*;
} }
pub fn appendSlice(l: *Self, items: []align(A) const T) !void { pub fn appendSlice(self: *Self, items: []align(A) const T) !void {
try l.ensureCapacity(l.len + items.len); try self.ensureCapacity(self.len + items.len);
mem.copy(T, l.items[l.len..], items); mem.copy(T, self.items[self.len..], items);
l.len += items.len; self.len += items.len;
} }
pub fn resize(l: *Self, new_len: usize) !void { pub fn resize(self: *Self, new_len: usize) !void {
try l.ensureCapacity(new_len); try self.ensureCapacity(new_len);
l.len = new_len; self.len = new_len;
} }
pub fn shrink(l: *Self, new_len: usize) void { pub fn shrink(self: *Self, new_len: usize) void {
assert(new_len <= l.len); assert(new_len <= self.len);
l.len = new_len; self.len = new_len;
} }
pub fn ensureCapacity(l: *Self, new_capacity: usize) !void { pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
var better_capacity = l.items.len; var better_capacity = self.items.len;
if (better_capacity >= new_capacity) return; if (better_capacity >= new_capacity) return;
while (true) { while (true) {
better_capacity += better_capacity / 2 + 8; better_capacity += better_capacity / 2 + 8;
if (better_capacity >= new_capacity) break; if (better_capacity >= new_capacity) break;
} }
l.items = try l.allocator.alignedRealloc(T, A, l.items, better_capacity); self.items = try self.allocator.alignedRealloc(T, A, self.items, better_capacity);
} }
pub fn addOne(l: *Self) !*T { pub fn addOne(self: *Self) !*T {
const new_length = l.len + 1; const new_length = self.len + 1;
try l.ensureCapacity(new_length); try self.ensureCapacity(new_length);
const result = &l.items[l.len]; const result = &self.items[self.len];
l.len = new_length; self.len = new_length;
return result; return result;
} }
@ -159,9 +173,15 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
} }
test "basic ArrayList test" { test "basic ArrayList test" {
var list = ArrayList(i32).init(debug.global_allocator); var bytes: [1024]u8 = undefined;
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
var list = ArrayList(i32).init(allocator);
defer list.deinit(); defer list.deinit();
// setting on empty list is out of bounds
assertError(list.setOrError(0, 1), error.OutOfBounds);
{ {
var i: usize = 0; var i: usize = 0;
while (i < 10) : (i += 1) { while (i < 10) : (i += 1) {
@ -200,6 +220,16 @@ test "basic ArrayList test" {
list.appendSlice([]const i32{}) catch unreachable; list.appendSlice([]const i32{}) catch unreachable;
assert(list.len == 9); assert(list.len == 9);
// can only set on indices < self.len
list.set(7, 33);
list.set(8, 42);
assertError(list.setOrError(9, 99), error.OutOfBounds);
assertError(list.setOrError(10, 123), error.OutOfBounds);
assert(list.pop() == 42);
assert(list.pop() == 33);
} }
test "iterator ArrayList test" { test "iterator ArrayList test" {
@ -228,7 +258,7 @@ test "iterator ArrayList test" {
} }
it.reset(); it.reset();
assert(??it.next() == 1); assert(it.next().? == 1);
} }
test "insert ArrayList test" { test "insert ArrayList test" {

View File

@ -33,8 +33,8 @@ pub fn Queue(comptime T: type) type {
pub fn get(self: *Self) ?*Node { pub fn get(self: *Self) ?*Node {
var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst); var head = @atomicLoad(*Node, &self.head, AtomicOrder.SeqCst);
while (true) { while (true) {
const node = head.next ?? return null; const node = head.next orelse return null;
head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return node; head = @cmpxchgWeak(*Node, &self.head, head, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return node;
} }
} }
}; };
@ -94,8 +94,18 @@ test "std.atomic.queue" {
for (getters) |t| for (getters) |t|
t.wait(); t.wait();
std.debug.assert(context.put_sum == context.get_sum); if (context.put_sum != context.get_sum) {
std.debug.assert(context.get_count == puts_per_thread * put_thread_count); std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
}
if (context.get_count != puts_per_thread * put_thread_count) {
std.debug.panic(
"failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
context.get_count,
u32(puts_per_thread),
u32(put_thread_count),
);
}
} }
fn startPuts(ctx: *Context) u8 { fn startPuts(ctx: *Context) u8 {
@ -114,15 +124,14 @@ fn startPuts(ctx: *Context) u8 {
fn startGets(ctx: *Context) u8 { fn startGets(ctx: *Context) u8 {
while (true) { while (true) {
const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
while (ctx.queue.get()) |node| { while (ctx.queue.get()) |node| {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
} }
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { if (last) return 0;
break;
}
} }
return 0;
} }

View File

@ -28,14 +28,14 @@ pub fn Stack(comptime T: type) type {
var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
while (true) { while (true) {
node.next = root; node.next = root;
root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? break; root = @cmpxchgWeak(?*Node, &self.root, root, node, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse break;
} }
} }
pub fn pop(self: *Self) ?*Node { pub fn pop(self: *Self) ?*Node {
var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst); var root = @atomicLoad(?*Node, &self.root, AtomicOrder.SeqCst);
while (true) { while (true) {
root = @cmpxchgWeak(?*Node, &self.root, root, (root ?? return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) ?? return root; root = @cmpxchgWeak(?*Node, &self.root, root, (root orelse return null).next, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return root;
} }
} }
@ -97,8 +97,18 @@ test "std.atomic.stack" {
for (getters) |t| for (getters) |t|
t.wait(); t.wait();
std.debug.assert(context.put_sum == context.get_sum); if (context.put_sum != context.get_sum) {
std.debug.assert(context.get_count == puts_per_thread * put_thread_count); std.debug.panic("failure\nput_sum:{} != get_sum:{}", context.put_sum, context.get_sum);
}
if (context.get_count != puts_per_thread * put_thread_count) {
std.debug.panic(
"failure\nget_count:{} != puts_per_thread:{} * put_thread_count:{}",
context.get_count,
u32(puts_per_thread),
u32(put_thread_count),
);
}
} }
fn startPuts(ctx: *Context) u8 { fn startPuts(ctx: *Context) u8 {
@ -117,15 +127,14 @@ fn startPuts(ctx: *Context) u8 {
fn startGets(ctx: *Context) u8 { fn startGets(ctx: *Context) u8 {
while (true) { while (true) {
const last = @atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1;
while (ctx.stack.pop()) |node| { while (ctx.stack.pop()) |node| {
std.os.time.sleep(0, 1); // let the os scheduler be our fuzz std.os.time.sleep(0, 1); // let the os scheduler be our fuzz
_ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst); _ = @atomicRmw(isize, &ctx.get_sum, builtin.AtomicRmwOp.Add, node.data, builtin.AtomicOrder.SeqCst);
_ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst); _ = @atomicRmw(usize, &ctx.get_count, builtin.AtomicRmwOp.Add, 1, builtin.AtomicOrder.SeqCst);
} }
if (@atomicLoad(u8, &ctx.puts_done, builtin.AtomicOrder.SeqCst) == 1) { if (last) return 0;
break;
}
} }
return 0;
} }

View File

@ -19,7 +19,7 @@ pub const BufMap = struct {
pub fn deinit(self: *const BufMap) void { pub fn deinit(self: *const BufMap) void {
var it = self.hash_map.iterator(); var it = self.hash_map.iterator();
while (true) { while (true) {
const entry = it.next() ?? break; const entry = it.next() orelse break;
self.free(entry.key); self.free(entry.key);
self.free(entry.value); self.free(entry.value);
} }
@ -37,12 +37,12 @@ pub const BufMap = struct {
} }
pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 { pub fn get(self: *const BufMap, key: []const u8) ?[]const u8 {
const entry = self.hash_map.get(key) ?? return null; const entry = self.hash_map.get(key) orelse return null;
return entry.value; return entry.value;
} }
pub fn delete(self: *BufMap, key: []const u8) void { pub fn delete(self: *BufMap, key: []const u8) void {
const entry = self.hash_map.remove(key) ?? return; const entry = self.hash_map.remove(key) orelse return;
self.free(entry.key); self.free(entry.key);
self.free(entry.value); self.free(entry.value);
} }
@ -72,15 +72,15 @@ test "BufMap" {
defer bufmap.deinit(); defer bufmap.deinit();
try bufmap.set("x", "1"); try bufmap.set("x", "1");
assert(mem.eql(u8, ??bufmap.get("x"), "1")); assert(mem.eql(u8, bufmap.get("x").?, "1"));
assert(1 == bufmap.count()); assert(1 == bufmap.count());
try bufmap.set("x", "2"); try bufmap.set("x", "2");
assert(mem.eql(u8, ??bufmap.get("x"), "2")); assert(mem.eql(u8, bufmap.get("x").?, "2"));
assert(1 == bufmap.count()); assert(1 == bufmap.count());
try bufmap.set("x", "3"); try bufmap.set("x", "3");
assert(mem.eql(u8, ??bufmap.get("x"), "3")); assert(mem.eql(u8, bufmap.get("x").?, "3"));
assert(1 == bufmap.count()); assert(1 == bufmap.count());
bufmap.delete("x"); bufmap.delete("x");

View File

@ -17,7 +17,7 @@ pub const BufSet = struct {
pub fn deinit(self: *const BufSet) void { pub fn deinit(self: *const BufSet) void {
var it = self.hash_map.iterator(); var it = self.hash_map.iterator();
while (true) { while (true) {
const entry = it.next() ?? break; const entry = it.next() orelse break;
self.free(entry.key); self.free(entry.key);
} }
@ -33,7 +33,7 @@ pub const BufSet = struct {
} }
pub fn delete(self: *BufSet, key: []const u8) void { pub fn delete(self: *BufSet, key: []const u8) void {
const entry = self.hash_map.remove(key) ?? return; const entry = self.hash_map.remove(key) orelse return;
self.free(entry.key); self.free(entry.key);
} }

View File

@ -28,7 +28,6 @@ pub const Buffer = struct {
/// Must deinitialize with deinit. /// Must deinitialize with deinit.
/// None of the other operations are valid until you do one of these: /// None of the other operations are valid until you do one of these:
/// * ::replaceContents /// * ::replaceContents
/// * ::replaceContentsBuffer
/// * ::resize /// * ::resize
pub fn initNull(allocator: *Allocator) Buffer { pub fn initNull(allocator: *Allocator) Buffer {
return Buffer{ .list = ArrayList(u8).init(allocator) }; return Buffer{ .list = ArrayList(u8).init(allocator) };
@ -42,9 +41,9 @@ pub const Buffer = struct {
/// Buffer takes ownership of the passed in slice. The slice must have been /// Buffer takes ownership of the passed in slice. The slice must have been
/// allocated with `allocator`. /// allocated with `allocator`.
/// Must deinitialize with deinit. /// Must deinitialize with deinit.
pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) Buffer { pub fn fromOwnedSlice(allocator: *Allocator, slice: []u8) !Buffer {
var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) }; var self = Buffer{ .list = ArrayList(u8).fromOwnedSlice(allocator, slice) };
self.list.append(0); try self.list.append(0);
return self; return self;
} }
@ -116,7 +115,7 @@ pub const Buffer = struct {
return mem.eql(u8, self.list.items[start..l], m); return mem.eql(u8, self.list.items[start..l], m);
} }
pub fn replaceContents(self: *const Buffer, m: []const u8) !void { pub fn replaceContents(self: *Buffer, m: []const u8) !void {
try self.resize(m.len); try self.resize(m.len);
mem.copy(u8, self.list.toSlice(), m); mem.copy(u8, self.list.toSlice(), m);
} }

View File

@ -136,7 +136,7 @@ pub const Builder = struct {
} }
pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void { pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
self.prefix = maybe_prefix ?? "/usr/local"; // TODO better default self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable; self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;
self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable; self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;
} }
@ -312,9 +312,9 @@ pub const Builder = struct {
if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| { if (os.getEnvVarOwned(self.allocator, "NIX_CFLAGS_COMPILE")) |nix_cflags_compile| {
var it = mem.split(nix_cflags_compile, " "); var it = mem.split(nix_cflags_compile, " ");
while (true) { while (true) {
const word = it.next() ?? break; const word = it.next() orelse break;
if (mem.eql(u8, word, "-isystem")) { if (mem.eql(u8, word, "-isystem")) {
const include_path = it.next() ?? { const include_path = it.next() orelse {
warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n"); warn("Expected argument after -isystem in NIX_CFLAGS_COMPILE\n");
break; break;
}; };
@ -330,9 +330,9 @@ pub const Builder = struct {
if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| { if (os.getEnvVarOwned(self.allocator, "NIX_LDFLAGS")) |nix_ldflags| {
var it = mem.split(nix_ldflags, " "); var it = mem.split(nix_ldflags, " ");
while (true) { while (true) {
const word = it.next() ?? break; const word = it.next() orelse break;
if (mem.eql(u8, word, "-rpath")) { if (mem.eql(u8, word, "-rpath")) {
const rpath = it.next() ?? { const rpath = it.next() orelse {
warn("Expected argument after -rpath in NIX_LDFLAGS\n"); warn("Expected argument after -rpath in NIX_LDFLAGS\n");
break; break;
}; };
@ -362,7 +362,7 @@ pub const Builder = struct {
} }
self.available_options_list.append(available_option) catch unreachable; self.available_options_list.append(available_option) catch unreachable;
const entry = self.user_input_options.get(name) ?? return null; const entry = self.user_input_options.get(name) orelse return null;
entry.value.used = true; entry.value.used = true;
switch (type_id) { switch (type_id) {
TypeId.Bool => switch (entry.value.value) { TypeId.Bool => switch (entry.value.value) {
@ -416,9 +416,9 @@ pub const Builder = struct {
pub fn standardReleaseOptions(self: *Builder) builtin.Mode { pub fn standardReleaseOptions(self: *Builder) builtin.Mode {
if (self.release_mode) |mode| return mode; if (self.release_mode) |mode| return mode;
const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false; const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") orelse false;
const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false; const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") orelse false;
const release_small = self.option(bool, "release-small", "size optimizations on and safety off") ?? false; const release_small = self.option(bool, "release-small", "size optimizations on and safety off") orelse false;
const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: { const mode = if (release_safe and !release_fast and !release_small) builtin.Mode.ReleaseSafe else if (release_fast and !release_safe and !release_small) builtin.Mode.ReleaseFast else if (release_small and !release_fast and !release_safe) builtin.Mode.ReleaseSmall else if (!release_fast and !release_safe and !release_small) builtin.Mode.Debug else x: {
warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)"); warn("Multiple release modes (of -Drelease-safe, -Drelease-fast and -Drelease-small)");
@ -518,7 +518,7 @@ pub const Builder = struct {
// make sure all args are used // make sure all args are used
var it = self.user_input_options.iterator(); var it = self.user_input_options.iterator();
while (true) { while (true) {
const entry = it.next() ?? break; const entry = it.next() orelse break;
if (!entry.value.used) { if (!entry.value.used) {
warn("Invalid option: -D{}\n\n", entry.key); warn("Invalid option: -D{}\n\n", entry.key);
self.markInvalidUserInput(); self.markInvalidUserInput();
@ -617,7 +617,7 @@ pub const Builder = struct {
warn("cp {} {}\n", source_path, dest_path); warn("cp {} {}\n", source_path, dest_path);
} }
const dirname = os.path.dirname(dest_path); const dirname = os.path.dirname(dest_path) orelse ".";
const abs_source_path = self.pathFromRoot(source_path); const abs_source_path = self.pathFromRoot(source_path);
os.makePath(self.allocator, dirname) catch |err| { os.makePath(self.allocator, dirname) catch |err| {
warn("Unable to create path {}: {}\n", dirname, @errorName(err)); warn("Unable to create path {}: {}\n", dirname, @errorName(err));
@ -1246,7 +1246,7 @@ pub const LibExeObjStep = struct {
{ {
var it = self.link_libs.iterator(); var it = self.link_libs.iterator();
while (true) { while (true) {
const entry = it.next() ?? break; const entry = it.next() orelse break;
zig_args.append("--library") catch unreachable; zig_args.append("--library") catch unreachable;
zig_args.append(entry.key) catch unreachable; zig_args.append(entry.key) catch unreachable;
} }
@ -1395,8 +1395,9 @@ pub const LibExeObjStep = struct {
cc_args.append(abs_source_file) catch unreachable; cc_args.append(abs_source_file) catch unreachable;
const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable; const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
const cache_o_dir = os.path.dirname(cache_o_src); if (os.path.dirname(cache_o_src)) |cache_o_dir| {
try builder.makePath(cache_o_dir); try builder.makePath(cache_o_dir);
}
const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt()); const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt());
cc_args.append("-o") catch unreachable; cc_args.append("-o") catch unreachable;
cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable; cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable;
@ -1509,8 +1510,9 @@ pub const LibExeObjStep = struct {
cc_args.append(abs_source_file) catch unreachable; cc_args.append(abs_source_file) catch unreachable;
const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable; const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
const cache_o_dir = os.path.dirname(cache_o_src); if (os.path.dirname(cache_o_src)) |cache_o_dir| {
try builder.makePath(cache_o_dir); try builder.makePath(cache_o_dir);
}
const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt()); const cache_o_file = builder.fmt("{}{}", cache_o_src, self.target.oFileExt());
cc_args.append("-o") catch unreachable; cc_args.append("-o") catch unreachable;
cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable; cc_args.append(builder.pathFromRoot(cache_o_file)) catch unreachable;
@ -1696,7 +1698,7 @@ pub const TestStep = struct {
{ {
var it = self.link_libs.iterator(); var it = self.link_libs.iterator();
while (true) { while (true) {
const entry = it.next() ?? break; const entry = it.next() orelse break;
try zig_args.append("--library"); try zig_args.append("--library");
try zig_args.append(entry.key); try zig_args.append(entry.key);
} }
@ -1855,7 +1857,7 @@ pub const WriteFileStep = struct {
fn make(step: *Step) !void { fn make(step: *Step) !void {
const self = @fieldParentPtr(WriteFileStep, "step", step); const self = @fieldParentPtr(WriteFileStep, "step", step);
const full_path = self.builder.pathFromRoot(self.file_path); const full_path = self.builder.pathFromRoot(self.file_path);
const full_path_dir = os.path.dirname(full_path); const full_path_dir = os.path.dirname(full_path) orelse ".";
os.makePath(self.builder.allocator, full_path_dir) catch |err| { os.makePath(self.builder.allocator, full_path_dir) catch |err| {
warn("unable to make path {}: {}\n", full_path_dir, @errorName(err)); warn("unable to make path {}: {}\n", full_path_dir, @errorName(err));
return err; return err;
@ -1945,7 +1947,7 @@ pub const Step = struct {
}; };
fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void {
const out_dir = os.path.dirname(output_path); const out_dir = os.path.dirname(output_path) orelse ".";
const out_basename = os.path.basename(output_path); const out_basename = os.path.basename(output_path);
// sym link for libfoo.so.1 to libfoo.so.1.2.3 // sym link for libfoo.so.1 to libfoo.so.1.2.3
const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable; const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable;

View File

@ -20,11 +20,11 @@ pub extern "c" fn @"fstat$INODE64"(fd: c_int, buf: *Stat) c_int;
pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize; pub extern "c" fn lseek(fd: c_int, offset: isize, whence: c_int) isize;
pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int; pub extern "c" fn open(path: [*]const u8, oflag: c_int, ...) c_int;
pub extern "c" fn raise(sig: c_int) c_int; pub extern "c" fn raise(sig: c_int) c_int;
pub extern "c" fn read(fd: c_int, buf: [*]c_void, nbyte: usize) isize; pub extern "c" fn read(fd: c_int, buf: *c_void, nbyte: usize) isize;
pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
pub extern "c" fn write(fd: c_int, buf: [*]const c_void, nbyte: usize) isize; pub extern "c" fn write(fd: c_int, buf: *const c_void, nbyte: usize) isize;
pub extern "c" fn mmap(addr: ?[*]c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?[*]c_void; pub extern "c" fn mmap(addr: ?*c_void, len: usize, prot: c_int, flags: c_int, fd: c_int, offset: isize) ?*c_void;
pub extern "c" fn munmap(addr: [*]c_void, len: usize) c_int; pub extern "c" fn munmap(addr: *c_void, len: usize) c_int;
pub extern "c" fn unlink(path: [*]const u8) c_int; pub extern "c" fn unlink(path: [*]const u8) c_int;
pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_int, options: c_int) c_int;
@ -48,15 +48,15 @@ pub extern "c" fn setreuid(ruid: c_uint, euid: c_uint) c_int;
pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int; pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) c_int;
pub extern "c" fn rmdir(path: [*]const u8) c_int; pub extern "c" fn rmdir(path: [*]const u8) c_int;
pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?[*]c_void; pub extern "c" fn aligned_alloc(alignment: usize, size: usize) ?*c_void;
pub extern "c" fn malloc(usize) ?[*]c_void; pub extern "c" fn malloc(usize) ?*c_void;
pub extern "c" fn realloc([*]c_void, usize) ?[*]c_void; pub extern "c" fn realloc(*c_void, usize) ?*c_void;
pub extern "c" fn free([*]c_void) void; pub extern "c" fn free(*c_void) void;
pub extern "c" fn posix_memalign(memptr: *[*]c_void, alignment: usize, size: usize) c_int; pub extern "c" fn posix_memalign(memptr: **c_void, alignment: usize, size: usize) c_int;
pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int; pub extern "pthread" fn pthread_create(noalias newthread: *pthread_t, noalias attr: ?*const pthread_attr_t, start_routine: extern fn (?*c_void) ?*c_void, noalias arg: ?*c_void) c_int;
pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_attr_init(attr: *pthread_attr_t) c_int;
pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: [*]c_void, stacksize: usize) c_int; pub extern "pthread" fn pthread_attr_setstack(attr: *pthread_attr_t, stackaddr: *c_void, stacksize: usize) c_int;
pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int; pub extern "pthread" fn pthread_attr_destroy(attr: *pthread_attr_t) c_int;
pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int; pub extern "pthread" fn pthread_join(thread: pthread_t, arg_return: ?*?*c_void) c_int;

View File

@ -88,6 +88,16 @@ pub fn assert(ok: bool) void {
} }
} }
/// TODO: add `==` operator for `error_union == error_set`, and then
/// remove this function
pub fn assertError(value: var, expected_error: error) void {
if (value) {
@panic("expected error");
} else |actual_error| {
assert(actual_error == expected_error);
}
}
/// Call this function when you want to panic if the condition is not true. /// Call this function when you want to panic if the condition is not true.
/// If `ok` is `false`, this function will panic in every release mode. /// If `ok` is `false`, this function will panic in every release mode.
pub fn assertOrPanic(ok: bool) void { pub fn assertOrPanic(ok: bool) void {
@ -198,7 +208,7 @@ fn printSourceAtAddress(debug_info: *ElfStackTrace, out_stream: var, address: us
.name = "???", .name = "???",
.address = address, .address = address,
}; };
const symbol = debug_info.symbol_table.search(address) ?? &unknown; const symbol = debug_info.symbol_table.search(address) orelse &unknown;
try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address); try out_stream.print(WHITE ++ "{}" ++ RESET ++ ": " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n", symbol.name, address);
}, },
else => { else => {
@ -258,10 +268,10 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) !*ElfStackTrace {
try st.elf.openFile(allocator, &st.self_exe_file); try st.elf.openFile(allocator, &st.self_exe_file);
errdefer st.elf.close(); errdefer st.elf.close();
st.debug_info = (try st.elf.findSection(".debug_info")) ?? return error.MissingDebugInfo; st.debug_info = (try st.elf.findSection(".debug_info")) orelse return error.MissingDebugInfo;
st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo; st.debug_abbrev = (try st.elf.findSection(".debug_abbrev")) orelse return error.MissingDebugInfo;
st.debug_str = (try st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo; st.debug_str = (try st.elf.findSection(".debug_str")) orelse return error.MissingDebugInfo;
st.debug_line = (try st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo; st.debug_line = (try st.elf.findSection(".debug_line")) orelse return error.MissingDebugInfo;
st.debug_ranges = (try st.elf.findSection(".debug_ranges")); st.debug_ranges = (try st.elf.findSection(".debug_ranges"));
try scanAllCompileUnits(st); try scanAllCompileUnits(st);
return st; return st;
@ -433,7 +443,7 @@ const Die = struct {
} }
fn getAttrAddr(self: *const Die, id: u64) !u64 { fn getAttrAddr(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) { return switch (form_value.*) {
FormValue.Address => |value| value, FormValue.Address => |value| value,
else => error.InvalidDebugInfo, else => error.InvalidDebugInfo,
@ -441,7 +451,7 @@ const Die = struct {
} }
fn getAttrSecOffset(self: *const Die, id: u64) !u64 { fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) { return switch (form_value.*) {
FormValue.Const => |value| value.asUnsignedLe(), FormValue.Const => |value| value.asUnsignedLe(),
FormValue.SecOffset => |value| value, FormValue.SecOffset => |value| value,
@ -450,7 +460,7 @@ const Die = struct {
} }
fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 { fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) { return switch (form_value.*) {
FormValue.Const => |value| value.asUnsignedLe(), FormValue.Const => |value| value.asUnsignedLe(),
else => error.InvalidDebugInfo, else => error.InvalidDebugInfo,
@ -458,7 +468,7 @@ const Die = struct {
} }
fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 { fn getAttrString(self: *const Die, st: *ElfStackTrace, id: u64) ![]u8 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo; const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
return switch (form_value.*) { return switch (form_value.*) {
FormValue.String => |value| value, FormValue.String => |value| value,
FormValue.StrPtr => |offset| getString(st, offset), FormValue.StrPtr => |offset| getString(st, offset),
@ -738,7 +748,7 @@ fn parseDie(st: *ElfStackTrace, abbrev_table: *const AbbrevTable, is_64: bool) !
var in_file_stream = io.FileInStream.init(in_file); var in_file_stream = io.FileInStream.init(in_file);
const in_stream = &in_file_stream.stream; const in_stream = &in_file_stream.stream;
const abbrev_code = try readULeb128(in_stream); const abbrev_code = try readULeb128(in_stream);
const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) ?? return error.InvalidDebugInfo; const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
var result = Die{ var result = Die{
.tag_id = table_entry.tag_id, .tag_id = table_entry.tag_id,

View File

@ -40,9 +40,9 @@ pub const TcpServer = struct {
self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd)); self.listen_address = std.net.Address.initPosix(try std.os.posixGetSockName(self.sockfd));
self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self); self.accept_coro = try async<self.loop.allocator> TcpServer.handler(self);
errdefer cancel ??self.accept_coro; errdefer cancel self.accept_coro.?;
try self.loop.addFd(self.sockfd, ??self.accept_coro); try self.loop.addFd(self.sockfd, self.accept_coro.?);
errdefer self.loop.removeFd(self.sockfd); errdefer self.loop.removeFd(self.sockfd);
} }

View File

@ -97,7 +97,11 @@ pub fn formatType(
output: fn (@typeOf(context), []const u8) Errors!void, output: fn (@typeOf(context), []const u8) Errors!void,
) Errors!void { ) Errors!void {
const T = @typeOf(value); const T = @typeOf(value);
switch (@typeId(T)) { if (T == error) {
try output(context, "error.");
return output(context, @errorName(value));
}
switch (@typeInfo(T)) {
builtin.TypeId.Int, builtin.TypeId.Float => { builtin.TypeId.Int, builtin.TypeId.Float => {
return formatValue(value, fmt, context, Errors, output); return formatValue(value, fmt, context, Errors, output);
}, },
@ -107,7 +111,7 @@ pub fn formatType(
builtin.TypeId.Bool => { builtin.TypeId.Bool => {
return output(context, if (value) "true" else "false"); return output(context, if (value) "true" else "false");
}, },
builtin.TypeId.Nullable => { builtin.TypeId.Optional => {
if (value) |payload| { if (value) |payload| {
return formatType(payload, fmt, context, Errors, output); return formatType(payload, fmt, context, Errors, output);
} else { } else {
@ -125,12 +129,13 @@ pub fn formatType(
try output(context, "error."); try output(context, "error.");
return output(context, @errorName(value)); return output(context, @errorName(value));
}, },
builtin.TypeId.Pointer => { builtin.TypeId.Pointer => |ptr_info| switch (ptr_info.size) {
switch (@typeId(T.Child)) { builtin.TypeInfo.Pointer.Size.One => switch (@typeInfo(ptr_info.child)) {
builtin.TypeId.Array => { builtin.TypeId.Array => |info| {
if (T.Child.Child == u8) { if (info.child == u8) {
return formatText(value, fmt, context, Errors, output); return formatText(value, fmt, context, Errors, output);
} }
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
}, },
builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => { builtin.TypeId.Enum, builtin.TypeId.Union, builtin.TypeId.Struct => {
const has_cust_fmt = comptime cf: { const has_cust_fmt = comptime cf: {
@ -154,14 +159,24 @@ pub fn formatType(
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)); return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
}, },
else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)), else => return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value)),
} },
}, builtin.TypeInfo.Pointer.Size.Many => {
else => if (@canImplicitCast([]const u8, value)) { if (ptr_info.child == u8) {
const casted_value = ([]const u8)(value); //This is a bit of a hack, but it made more sense to
return output(context, casted_value); // do this check here than have formatText do it
} else { if (fmt[0] == 's') {
@compileError("Unable to format type '" ++ @typeName(T) ++ "'"); const len = std.cstr.len(value);
return formatText(value[0..len], fmt, context, Errors, output);
}
}
return format(context, Errors, output, "{}@{x}", @typeName(T.Child), @ptrToInt(value));
},
builtin.TypeInfo.Pointer.Size.Slice => {
const casted_value = ([]const u8)(value);
return output(context, casted_value);
},
}, },
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
} }
} }
@ -293,7 +308,7 @@ pub fn formatBuf(
var leftover_padding = if (width > buf.len) (width - buf.len) else return; var leftover_padding = if (width > buf.len) (width - buf.len) else return;
const pad_byte: u8 = ' '; const pad_byte: u8 = ' ';
while (leftover_padding > 0) : (leftover_padding -= 1) { while (leftover_padding > 0) : (leftover_padding -= 1) {
try output(context, (&pad_byte)[0..1]); try output(context, (*[1]u8)(&pad_byte)[0..1]);
} }
} }
@ -552,14 +567,19 @@ pub fn formatBytes(
return output(context, "0B"); return output(context, "0B");
} }
const mags = " KMGTPEZY"; const mags_si = " kMGTPEZY";
const mags_iec = " KMGTPEZY";
const magnitude = switch (radix) { const magnitude = switch (radix) {
1000 => math.min(math.log2(value) / comptime math.log2(1000), mags.len - 1), 1000 => math.min(math.log2(value) / comptime math.log2(1000), mags_si.len - 1),
1024 => math.min(math.log2(value) / 10, mags.len - 1), 1024 => math.min(math.log2(value) / 10, mags_iec.len - 1),
else => unreachable, else => unreachable,
}; };
const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude)); const new_value = f64(value) / math.pow(f64, f64(radix), f64(magnitude));
const suffix = mags[magnitude]; const suffix = switch (radix) {
1000 => mags_si[magnitude],
1024 => mags_iec[magnitude],
else => unreachable,
};
try formatFloatDecimal(new_value, width, context, Errors, output); try formatFloatDecimal(new_value, width, context, Errors, output);
@ -807,11 +827,11 @@ test "parse unsigned comptime" {
test "fmt.format" { test "fmt.format" {
{ {
const value: ?i32 = 1234; const value: ?i32 = 1234;
try testFmt("nullable: 1234\n", "nullable: {}\n", value); try testFmt("optional: 1234\n", "optional: {}\n", value);
} }
{ {
const value: ?i32 = null; const value: ?i32 = null;
try testFmt("nullable: null\n", "nullable: {}\n", value); try testFmt("optional: null\n", "optional: {}\n", value);
} }
{ {
const value: error!i32 = 1234; const value: error!i32 = 1234;
@ -829,6 +849,10 @@ test "fmt.format" {
const value: u8 = 'a'; const value: u8 = 'a';
try testFmt("u8: a\n", "u8: {c}\n", value); try testFmt("u8: a\n", "u8: {c}\n", value);
} }
try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
try testFmt("cstr: Test C \n", "cstr: {s10}\n", c"Test C");
try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024)); try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024)); try testFmt("file size: 66.06MB\n", "file size: {B2}\n", usize(63 * 1024 * 1024));
{ {

View File

@ -265,11 +265,11 @@ test "basic hash map usage" {
assert((map.put(4, 44) catch unreachable) == null); assert((map.put(4, 44) catch unreachable) == null);
assert((map.put(5, 55) catch unreachable) == null); assert((map.put(5, 55) catch unreachable) == null);
assert(??(map.put(5, 66) catch unreachable) == 55); assert((map.put(5, 66) catch unreachable).? == 55);
assert(??(map.put(5, 55) catch unreachable) == 66); assert((map.put(5, 55) catch unreachable).? == 66);
assert(map.contains(2)); assert(map.contains(2));
assert((??map.get(2)).value == 22); assert(map.get(2).?.value == 22);
_ = map.remove(2); _ = map.remove(2);
assert(map.remove(2) == null); assert(map.remove(2) == null);
assert(map.get(2) == null); assert(map.get(2) == null);
@ -317,7 +317,7 @@ test "iterator hash map" {
} }
it.reset(); it.reset();
var entry = ??it.next(); var entry = it.next().?;
assert(entry.key == keys[0]); assert(entry.key == keys[0]);
assert(entry.value == values[0]); assert(entry.value == values[0]);
} }

View File

@ -22,7 +22,7 @@ fn cAlloc(self: *Allocator, n: usize, alignment: u29) ![]u8 {
} }
fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 { fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![]u8 {
const old_ptr = @ptrCast([*]c_void, old_mem.ptr); const old_ptr = @ptrCast(*c_void, old_mem.ptr);
if (c.realloc(old_ptr, new_size)) |buf| { if (c.realloc(old_ptr, new_size)) |buf| {
return @ptrCast([*]u8, buf)[0..new_size]; return @ptrCast([*]u8, buf)[0..new_size];
} else if (new_size <= old_mem.len) { } else if (new_size <= old_mem.len) {
@ -33,7 +33,7 @@ fn cRealloc(self: *Allocator, old_mem: []u8, new_size: usize, alignment: u29) ![
} }
fn cFree(self: *Allocator, old_mem: []u8) void { fn cFree(self: *Allocator, old_mem: []u8) void {
const old_ptr = @ptrCast([*]c_void, old_mem.ptr); const old_ptr = @ptrCast(*c_void, old_mem.ptr);
c.free(old_ptr); c.free(old_ptr);
} }
@ -97,12 +97,12 @@ pub const DirectAllocator = struct {
}, },
Os.windows => { Os.windows => {
const amt = n + alignment + @sizeOf(usize); const amt = n + alignment + @sizeOf(usize);
const heap_handle = self.heap_handle ?? blk: { const heap_handle = self.heap_handle orelse blk: {
const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) ?? return error.OutOfMemory; const hh = os.windows.HeapCreate(os.windows.HEAP_NO_SERIALIZE, amt, 0) orelse return error.OutOfMemory;
self.heap_handle = hh; self.heap_handle = hh;
break :blk hh; break :blk hh;
}; };
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) ?? return error.OutOfMemory; const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr); const root_addr = @ptrToInt(ptr);
const rem = @rem(root_addr, alignment); const rem = @rem(root_addr, alignment);
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem); const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
@ -140,9 +140,9 @@ pub const DirectAllocator = struct {
const old_adjusted_addr = @ptrToInt(old_mem.ptr); const old_adjusted_addr = @ptrToInt(old_mem.ptr);
const old_record_addr = old_adjusted_addr + old_mem.len; const old_record_addr = old_adjusted_addr + old_mem.len;
const root_addr = @intToPtr(*align(1) usize, old_record_addr).*; const root_addr = @intToPtr(*align(1) usize, old_record_addr).*;
const old_ptr = @intToPtr([*]c_void, root_addr); const old_ptr = @intToPtr(*c_void, root_addr);
const amt = new_size + alignment + @sizeOf(usize); const amt = new_size + alignment + @sizeOf(usize);
const new_ptr = os.windows.HeapReAlloc(??self.heap_handle, 0, old_ptr, amt) ?? blk: { const new_ptr = os.windows.HeapReAlloc(self.heap_handle.?, 0, old_ptr, amt) orelse blk: {
if (new_size > old_mem.len) return error.OutOfMemory; if (new_size > old_mem.len) return error.OutOfMemory;
const new_record_addr = old_record_addr - new_size + old_mem.len; const new_record_addr = old_record_addr - new_size + old_mem.len;
@intToPtr(*align(1) usize, new_record_addr).* = root_addr; @intToPtr(*align(1) usize, new_record_addr).* = root_addr;
@ -170,8 +170,8 @@ pub const DirectAllocator = struct {
Os.windows => { Os.windows => {
const record_addr = @ptrToInt(bytes.ptr) + bytes.len; const record_addr = @ptrToInt(bytes.ptr) + bytes.len;
const root_addr = @intToPtr(*align(1) usize, record_addr).*; const root_addr = @intToPtr(*align(1) usize, record_addr).*;
const ptr = @intToPtr([*]c_void, root_addr); const ptr = @intToPtr(*c_void, root_addr);
_ = os.windows.HeapFree(??self.heap_handle, 0, ptr); _ = os.windows.HeapFree(self.heap_handle.?, 0, ptr);
}, },
else => @compileError("Unsupported OS"), else => @compileError("Unsupported OS"),
} }
@ -343,7 +343,7 @@ pub const ThreadSafeFixedBufferAllocator = struct {
if (new_end_index > self.buffer.len) { if (new_end_index > self.buffer.len) {
return error.OutOfMemory; return error.OutOfMemory;
} }
end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) ?? return self.buffer[adjusted_index..new_end_index]; end_index = @cmpxchgWeak(usize, &self.end_index, end_index, new_end_index, builtin.AtomicOrder.SeqCst, builtin.AtomicOrder.SeqCst) orelse return self.buffer[adjusted_index..new_end_index];
} }
} }

View File

@ -3,6 +3,7 @@
// https://tools.ietf.org/html/rfc8259 // https://tools.ietf.org/html/rfc8259
const std = @import("index.zig"); const std = @import("index.zig");
const debug = std.debug;
const mem = std.mem; const mem = std.mem;
const u1 = @IntType(false, 1); const u1 = @IntType(false, 1);
@ -86,7 +87,9 @@ pub const Token = struct {
// parsing state requires ~40-50 bytes of stack space. // parsing state requires ~40-50 bytes of stack space.
// //
// Conforms strictly to RFC8529. // Conforms strictly to RFC8529.
pub const StreamingJsonParser = struct { //
// For a non-byte based wrapper, consider using TokenStream instead.
pub const StreamingParser = struct {
// Current state // Current state
state: State, state: State,
// How many bytes we have counted for the current token // How many bytes we have counted for the current token
@ -109,13 +112,13 @@ pub const StreamingJsonParser = struct {
const array_bit = 1; const array_bit = 1;
const max_stack_size = @maxValue(u8); const max_stack_size = @maxValue(u8);
pub fn init() StreamingJsonParser { pub fn init() StreamingParser {
var p: StreamingJsonParser = undefined; var p: StreamingParser = undefined;
p.reset(); p.reset();
return p; return p;
} }
pub fn reset(p: *StreamingJsonParser) void { pub fn reset(p: *StreamingParser) void {
p.state = State.TopLevelBegin; p.state = State.TopLevelBegin;
p.count = 0; p.count = 0;
// Set before ever read in main transition function // Set before ever read in main transition function
@ -175,7 +178,7 @@ pub const StreamingJsonParser = struct {
// Only call this function to generate array/object final state. // Only call this function to generate array/object final state.
pub fn fromInt(x: var) State { pub fn fromInt(x: var) State {
std.debug.assert(x == 0 or x == 1); debug.assert(x == 0 or x == 1);
const T = @TagType(State); const T = @TagType(State);
return State(T(x)); return State(T(x));
} }
@ -205,7 +208,7 @@ pub const StreamingJsonParser = struct {
// tokens. token2 is always null if token1 is null. // tokens. token2 is always null if token1 is null.
// //
// There is currently no error recovery on a bad stream. // There is currently no error recovery on a bad stream.
pub fn feed(p: *StreamingJsonParser, c: u8, token1: *?Token, token2: *?Token) Error!void { pub fn feed(p: *StreamingParser, c: u8, token1: *?Token, token2: *?Token) Error!void {
token1.* = null; token1.* = null;
token2.* = null; token2.* = null;
p.count += 1; p.count += 1;
@ -217,7 +220,7 @@ pub const StreamingJsonParser = struct {
} }
// Perform a single transition on the state machine and return any possible token. // Perform a single transition on the state machine and return any possible token.
fn transition(p: *StreamingJsonParser, c: u8, token: *?Token) Error!bool { fn transition(p: *StreamingParser, c: u8, token: *?Token) Error!bool {
switch (p.state) { switch (p.state) {
State.TopLevelBegin => switch (c) { State.TopLevelBegin => switch (c) {
'{' => { '{' => {
@ -321,7 +324,9 @@ pub const StreamingJsonParser = struct {
p.complete = true; p.complete = true;
p.state = State.TopLevelEnd; p.state = State.TopLevelEnd;
}, },
else => {}, else => {
p.state = State.ValueEnd;
},
} }
token.* = Token.initMarker(Token.Id.ObjectEnd); token.* = Token.initMarker(Token.Id.ObjectEnd);
@ -345,7 +350,9 @@ pub const StreamingJsonParser = struct {
p.complete = true; p.complete = true;
p.state = State.TopLevelEnd; p.state = State.TopLevelEnd;
}, },
else => {}, else => {
p.state = State.ValueEnd;
},
} }
token.* = Token.initMarker(Token.Id.ArrayEnd); token.* = Token.initMarker(Token.Id.ArrayEnd);
@ -852,16 +859,122 @@ pub const StreamingJsonParser = struct {
} }
}; };
// A small wrapper over a StreamingParser for full slices. Returns a stream of json Tokens.
pub const TokenStream = struct {
i: usize,
slice: []const u8,
parser: StreamingParser,
token: ?Token,
pub fn init(slice: []const u8) TokenStream {
return TokenStream{
.i = 0,
.slice = slice,
.parser = StreamingParser.init(),
.token = null,
};
}
pub fn next(self: *TokenStream) !?Token {
if (self.token) |token| {
self.token = null;
return token;
}
var t1: ?Token = undefined;
var t2: ?Token = undefined;
while (self.i < self.slice.len) {
try self.parser.feed(self.slice[self.i], &t1, &t2);
self.i += 1;
if (t1) |token| {
self.token = t2;
return token;
}
}
if (self.i > self.slice.len) {
try self.parser.feed(' ', &t1, &t2);
self.i += 1;
if (t1) |token| {
return token;
}
}
return null;
}
};
fn checkNext(p: *TokenStream, id: Token.Id) void {
const token = (p.next() catch unreachable).?;
debug.assert(token.id == id);
}
test "token" {
const s =
\\{
\\ "Image": {
\\ "Width": 800,
\\ "Height": 600,
\\ "Title": "View from 15th Floor",
\\ "Thumbnail": {
\\ "Url": "http://www.example.com/image/481989943",
\\ "Height": 125,
\\ "Width": 100
\\ },
\\ "Animated" : false,
\\ "IDs": [116, 943, 234, 38793]
\\ }
\\}
;
var p = TokenStream.init(s);
checkNext(&p, Token.Id.ObjectBegin);
checkNext(&p, Token.Id.String); // Image
checkNext(&p, Token.Id.ObjectBegin);
checkNext(&p, Token.Id.String); // Width
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.String); // Height
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.String); // Title
checkNext(&p, Token.Id.String);
checkNext(&p, Token.Id.String); // Thumbnail
checkNext(&p, Token.Id.ObjectBegin);
checkNext(&p, Token.Id.String); // Url
checkNext(&p, Token.Id.String);
checkNext(&p, Token.Id.String); // Height
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.String); // Width
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.ObjectEnd);
checkNext(&p, Token.Id.String); // Animated
checkNext(&p, Token.Id.False);
checkNext(&p, Token.Id.String); // IDs
checkNext(&p, Token.Id.ArrayBegin);
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.Number);
checkNext(&p, Token.Id.ArrayEnd);
checkNext(&p, Token.Id.ObjectEnd);
checkNext(&p, Token.Id.ObjectEnd);
debug.assert((try p.next()) == null);
}
// Validate a JSON string. This does not limit number precision so a decoder may not necessarily // Validate a JSON string. This does not limit number precision so a decoder may not necessarily
// be able to decode the string even if this returns true. // be able to decode the string even if this returns true.
pub fn validate(s: []const u8) bool { pub fn validate(s: []const u8) bool {
var p = StreamingJsonParser.init(); var p = StreamingParser.init();
for (s) |c, i| { for (s) |c, i| {
var token1: ?Token = undefined; var token1: ?Token = undefined;
var token2: ?Token = undefined; var token2: ?Token = undefined;
p.feed(c, *token1, *token2) catch |err| { p.feed(c, &token1, &token2) catch |err| {
return false; return false;
}; };
} }
@ -869,6 +982,10 @@ pub fn validate(s: []const u8) bool {
return p.complete; return p.complete;
} }
test "json validate" {
debug.assert(validate("{}"));
}
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator; const ArenaAllocator = std.heap.ArenaAllocator;
const ArrayList = std.ArrayList; const ArrayList = std.ArrayList;
@ -897,46 +1014,46 @@ pub const Value = union(enum) {
pub fn dump(self: *const Value) void { pub fn dump(self: *const Value) void {
switch (self.*) { switch (self.*) {
Value.Null => { Value.Null => {
std.debug.warn("null"); debug.warn("null");
}, },
Value.Bool => |inner| { Value.Bool => |inner| {
std.debug.warn("{}", inner); debug.warn("{}", inner);
}, },
Value.Integer => |inner| { Value.Integer => |inner| {
std.debug.warn("{}", inner); debug.warn("{}", inner);
}, },
Value.Float => |inner| { Value.Float => |inner| {
std.debug.warn("{.5}", inner); debug.warn("{.5}", inner);
}, },
Value.String => |inner| { Value.String => |inner| {
std.debug.warn("\"{}\"", inner); debug.warn("\"{}\"", inner);
}, },
Value.Array => |inner| { Value.Array => |inner| {
var not_first = false; var not_first = false;
std.debug.warn("["); debug.warn("[");
for (inner.toSliceConst()) |value| { for (inner.toSliceConst()) |value| {
if (not_first) { if (not_first) {
std.debug.warn(","); debug.warn(",");
} }
not_first = true; not_first = true;
value.dump(); value.dump();
} }
std.debug.warn("]"); debug.warn("]");
}, },
Value.Object => |inner| { Value.Object => |inner| {
var not_first = false; var not_first = false;
std.debug.warn("{{"); debug.warn("{{");
var it = inner.iterator(); var it = inner.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
if (not_first) { if (not_first) {
std.debug.warn(","); debug.warn(",");
} }
not_first = true; not_first = true;
std.debug.warn("\"{}\":", entry.key); debug.warn("\"{}\":", entry.key);
entry.value.dump(); entry.value.dump();
} }
std.debug.warn("}}"); debug.warn("}}");
}, },
} }
} }
@ -952,53 +1069,53 @@ pub const Value = union(enum) {
fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void { fn dumpIndentLevel(self: *const Value, indent: usize, level: usize) void {
switch (self.*) { switch (self.*) {
Value.Null => { Value.Null => {
std.debug.warn("null"); debug.warn("null");
}, },
Value.Bool => |inner| { Value.Bool => |inner| {
std.debug.warn("{}", inner); debug.warn("{}", inner);
}, },
Value.Integer => |inner| { Value.Integer => |inner| {
std.debug.warn("{}", inner); debug.warn("{}", inner);
}, },
Value.Float => |inner| { Value.Float => |inner| {
std.debug.warn("{.5}", inner); debug.warn("{.5}", inner);
}, },
Value.String => |inner| { Value.String => |inner| {
std.debug.warn("\"{}\"", inner); debug.warn("\"{}\"", inner);
}, },
Value.Array => |inner| { Value.Array => |inner| {
var not_first = false; var not_first = false;
std.debug.warn("[\n"); debug.warn("[\n");
for (inner.toSliceConst()) |value| { for (inner.toSliceConst()) |value| {
if (not_first) { if (not_first) {
std.debug.warn(",\n"); debug.warn(",\n");
} }
not_first = true; not_first = true;
padSpace(level + indent); padSpace(level + indent);
value.dumpIndentLevel(indent, level + indent); value.dumpIndentLevel(indent, level + indent);
} }
std.debug.warn("\n"); debug.warn("\n");
padSpace(level); padSpace(level);
std.debug.warn("]"); debug.warn("]");
}, },
Value.Object => |inner| { Value.Object => |inner| {
var not_first = false; var not_first = false;
std.debug.warn("{{\n"); debug.warn("{{\n");
var it = inner.iterator(); var it = inner.iterator();
while (it.next()) |entry| { while (it.next()) |entry| {
if (not_first) { if (not_first) {
std.debug.warn(",\n"); debug.warn(",\n");
} }
not_first = true; not_first = true;
padSpace(level + indent); padSpace(level + indent);
std.debug.warn("\"{}\": ", entry.key); debug.warn("\"{}\": ", entry.key);
entry.value.dumpIndentLevel(indent, level + indent); entry.value.dumpIndentLevel(indent, level + indent);
} }
std.debug.warn("\n"); debug.warn("\n");
padSpace(level); padSpace(level);
std.debug.warn("}}"); debug.warn("}}");
}, },
} }
} }
@ -1006,13 +1123,13 @@ pub const Value = union(enum) {
fn padSpace(indent: usize) void { fn padSpace(indent: usize) void {
var i: usize = 0; var i: usize = 0;
while (i < indent) : (i += 1) { while (i < indent) : (i += 1) {
std.debug.warn(" "); debug.warn(" ");
} }
} }
}; };
// A non-stream JSON parser which constructs a tree of Value's. // A non-stream JSON parser which constructs a tree of Value's.
pub const JsonParser = struct { pub const Parser = struct {
allocator: *Allocator, allocator: *Allocator,
state: State, state: State,
copy_strings: bool, copy_strings: bool,
@ -1026,8 +1143,8 @@ pub const JsonParser = struct {
Simple, Simple,
}; };
pub fn init(allocator: *Allocator, copy_strings: bool) JsonParser { pub fn init(allocator: *Allocator, copy_strings: bool) Parser {
return JsonParser{ return Parser{
.allocator = allocator, .allocator = allocator,
.state = State.Simple, .state = State.Simple,
.copy_strings = copy_strings, .copy_strings = copy_strings,
@ -1035,52 +1152,26 @@ pub const JsonParser = struct {
}; };
} }
pub fn deinit(p: *JsonParser) void { pub fn deinit(p: *Parser) void {
p.stack.deinit(); p.stack.deinit();
} }
pub fn reset(p: *JsonParser) void { pub fn reset(p: *Parser) void {
p.state = State.Simple; p.state = State.Simple;
p.stack.shrink(0); p.stack.shrink(0);
} }
pub fn parse(p: *JsonParser, input: []const u8) !ValueTree { pub fn parse(p: *Parser, input: []const u8) !ValueTree {
var mp = StreamingJsonParser.init(); var s = TokenStream.init(input);
var arena = ArenaAllocator.init(p.allocator); var arena = ArenaAllocator.init(p.allocator);
errdefer arena.deinit(); errdefer arena.deinit();
for (input) |c, i| { while (try s.next()) |token| {
var mt1: ?Token = undefined; try p.transition(&arena.allocator, input, s.i - 1, token);
var mt2: ?Token = undefined;
try mp.feed(c, &mt1, &mt2);
if (mt1) |t1| {
try p.transition(&arena.allocator, input, i, t1);
if (mt2) |t2| {
try p.transition(&arena.allocator, input, i, t2);
}
}
} }
// Handle top-level lonely number values. debug.assert(p.stack.len == 1);
{
const i = input.len;
var mt1: ?Token = undefined;
var mt2: ?Token = undefined;
try mp.feed(' ', &mt1, &mt2);
if (mt1) |t1| {
try p.transition(&arena.allocator, input, i, t1);
}
}
if (!mp.complete) {
return error.IncompleteJsonInput;
}
std.debug.assert(p.stack.len == 1);
return ValueTree{ return ValueTree{
.arena = arena, .arena = arena,
@ -1090,7 +1181,7 @@ pub const JsonParser = struct {
// Even though p.allocator exists, we take an explicit allocator so that allocation state // Even though p.allocator exists, we take an explicit allocator so that allocation state
// can be cleaned up on error correctly during a `parse` on call. // can be cleaned up on error correctly during a `parse` on call.
fn transition(p: *JsonParser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void { fn transition(p: *Parser, allocator: *Allocator, input: []const u8, i: usize, token: *const Token) !void {
switch (p.state) { switch (p.state) {
State.ObjectKey => switch (token.id) { State.ObjectKey => switch (token.id) {
Token.Id.ObjectEnd => { Token.Id.ObjectEnd => {
@ -1147,7 +1238,7 @@ pub const JsonParser = struct {
_ = p.stack.pop(); _ = p.stack.pop();
p.state = State.ObjectKey; p.state = State.ObjectKey;
}, },
else => { Token.Id.ObjectEnd, Token.Id.ArrayEnd => {
unreachable; unreachable;
}, },
} }
@ -1187,7 +1278,7 @@ pub const JsonParser = struct {
Token.Id.Null => { Token.Id.Null => {
try array.append(Value.Null); try array.append(Value.Null);
}, },
else => { Token.Id.ObjectEnd => {
unreachable; unreachable;
}, },
} }
@ -1223,7 +1314,7 @@ pub const JsonParser = struct {
} }
} }
fn pushToParent(p: *JsonParser, value: *const Value) !void { fn pushToParent(p: *Parser, value: *const Value) !void {
switch (p.stack.at(p.stack.len - 1)) { switch (p.stack.at(p.stack.len - 1)) {
// Object Parent -> [ ..., object, <key>, value ] // Object Parent -> [ ..., object, <key>, value ]
Value.String => |key| { Value.String => |key| {
@ -1244,14 +1335,14 @@ pub const JsonParser = struct {
} }
} }
fn parseString(p: *JsonParser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value { fn parseString(p: *Parser, allocator: *Allocator, token: *const Token, input: []const u8, i: usize) !Value {
// TODO: We don't strictly have to copy values which do not contain any escape // TODO: We don't strictly have to copy values which do not contain any escape
// characters if flagged with the option. // characters if flagged with the option.
const slice = token.slice(input, i); const slice = token.slice(input, i);
return Value{ .String = try mem.dupe(p.allocator, u8, slice) }; return Value{ .String = try mem.dupe(p.allocator, u8, slice) };
} }
fn parseNumber(p: *JsonParser, token: *const Token, input: []const u8, i: usize) !Value { fn parseNumber(p: *Parser, token: *const Token, input: []const u8, i: usize) !Value {
return if (token.number_is_integer) return if (token.number_is_integer)
Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) } Value{ .Integer = try std.fmt.parseInt(i64, token.slice(input, i), 10) }
else else
@ -1259,10 +1350,8 @@ pub const JsonParser = struct {
} }
}; };
const debug = std.debug;
test "json parser dynamic" { test "json parser dynamic" {
var p = JsonParser.init(std.debug.global_allocator, false); var p = Parser.init(debug.global_allocator, false);
defer p.deinit(); defer p.deinit();
const s = const s =
@ -1287,17 +1376,17 @@ test "json parser dynamic" {
var root = tree.root; var root = tree.root;
var image = (??root.Object.get("Image")).value; var image = root.Object.get("Image").?.value;
const width = (??image.Object.get("Width")).value; const width = image.Object.get("Width").?.value;
debug.assert(width.Integer == 800); debug.assert(width.Integer == 800);
const height = (??image.Object.get("Height")).value; const height = image.Object.get("Height").?.value;
debug.assert(height.Integer == 600); debug.assert(height.Integer == 600);
const title = (??image.Object.get("Title")).value; const title = image.Object.get("Title").?.value;
debug.assert(mem.eql(u8, title.String, "View from 15th Floor")); debug.assert(mem.eql(u8, title.String, "View from 15th Floor"));
const animated = (??image.Object.get("Animated")).value; const animated = image.Object.get("Animated").?.value;
debug.assert(animated.Bool == false); debug.assert(animated.Bool == false);
} }

View File

@ -17,6 +17,16 @@ fn any(comptime s: []const u8) void {
std.debug.assert(true); std.debug.assert(true);
} }
////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Additional tests not part of test JSONTestSuite.
test "y_trailing_comma_after_empty" {
ok(
\\{"1":[],"2":{},"3":"4"}
);
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
test "y_array_arraysWithSpaces" { test "y_array_arraysWithSpaces" {

View File

@ -169,7 +169,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
/// Returns: /// Returns:
/// A pointer to the last node in the list. /// A pointer to the last node in the list.
pub fn pop(list: *Self) ?*Node { pub fn pop(list: *Self) ?*Node {
const last = list.last ?? return null; const last = list.last orelse return null;
list.remove(last); list.remove(last);
return last; return last;
} }
@ -179,7 +179,7 @@ fn BaseLinkedList(comptime T: type, comptime ParentType: type, comptime field_na
/// Returns: /// Returns:
/// A pointer to the first node in the list. /// A pointer to the first node in the list.
pub fn popFirst(list: *Self) ?*Node { pub fn popFirst(list: *Self) ?*Node {
const first = list.first ?? return null; const first = list.first orelse return null;
list.remove(first); list.remove(first);
return first; return first;
} }
@ -270,8 +270,8 @@ test "basic linked list test" {
var last = list.pop(); // {2, 3, 4} var last = list.pop(); // {2, 3, 4}
list.remove(three); // {2, 4} list.remove(three); // {2, 4}
assert((??list.first).data == 2); assert(list.first.?.data == 2);
assert((??list.last).data == 4); assert(list.last.?.data == 4);
assert(list.len == 2); assert(list.len == 2);
} }
@ -336,7 +336,7 @@ test "basic intrusive linked list test" {
var last = list.pop(); // {2, 3, 4} var last = list.pop(); // {2, 3, 4}
list.remove(&three.link); // {2, 4} list.remove(&three.link); // {2, 4}
assert((??list.first).toData().value == 2); assert(list.first.?.toData().value == 2);
assert((??list.last).toData().value == 4); assert(list.last.?.toData().value == 4);
assert(list.len == 2); assert(list.len == 2);
} }

View File

@ -130,7 +130,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
for (syms) |sym| { for (syms) |sym| {
if (!isSymbol(sym)) continue; if (!isSymbol(sym)) continue;
const start = sym.n_strx; const start = sym.n_strx;
const end = ??mem.indexOfScalarPos(u8, strings, start, 0); const end = mem.indexOfScalarPos(u8, strings, start, 0).?;
const name = strings[start..end]; const name = strings[start..end];
const address = sym.n_value; const address = sym.n_value;
symbols[nsym] = Symbol{ .name = name, .address = address }; symbols[nsym] = Symbol{ .name = name, .address = address };

5
std/math/big/index.zig Normal file
View File

@ -0,0 +1,5 @@
pub use @import("int.zig");
test "math.big" {
_ = @import("int.zig");
}

2023
std/math/big/int.zig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -132,6 +132,8 @@ pub const tan = @import("tan.zig").tan;
pub const complex = @import("complex/index.zig"); pub const complex = @import("complex/index.zig");
pub const Complex = complex.Complex; pub const Complex = complex.Complex;
pub const big = @import("big/index.zig");
test "math" { test "math" {
_ = @import("nan.zig"); _ = @import("nan.zig");
_ = @import("isnan.zig"); _ = @import("isnan.zig");
@ -177,6 +179,8 @@ test "math" {
_ = @import("tan.zig"); _ = @import("tan.zig");
_ = @import("complex/index.zig"); _ = @import("complex/index.zig");
_ = @import("big/index.zig");
} }
pub fn min(x: var, y: var) @typeOf(x + y) { pub fn min(x: var, y: var) @typeOf(x + y) {
@ -306,7 +310,14 @@ test "math.rotl" {
} }
pub fn Log2Int(comptime T: type) type { pub fn Log2Int(comptime T: type) type {
return @IntType(false, log2(T.bit_count)); // comptime ceil log2
comptime var count: usize = 0;
comptime var s = T.bit_count - 1;
inline while (s != 0) : (s >>= 1) {
count += 1;
}
return @IntType(false, count);
} }
test "math overflow functions" { test "math overflow functions" {

View File

@ -304,20 +304,20 @@ pub fn indexOfPos(comptime T: type, haystack: []const T, start_index: usize, nee
} }
test "mem.indexOf" { test "mem.indexOf" {
assert(??indexOf(u8, "one two three four", "four") == 14); assert(indexOf(u8, "one two three four", "four").? == 14);
assert(??lastIndexOf(u8, "one two three two four", "two") == 14); assert(lastIndexOf(u8, "one two three two four", "two").? == 14);
assert(indexOf(u8, "one two three four", "gour") == null); assert(indexOf(u8, "one two three four", "gour") == null);
assert(lastIndexOf(u8, "one two three four", "gour") == null); assert(lastIndexOf(u8, "one two three four", "gour") == null);
assert(??indexOf(u8, "foo", "foo") == 0); assert(indexOf(u8, "foo", "foo").? == 0);
assert(??lastIndexOf(u8, "foo", "foo") == 0); assert(lastIndexOf(u8, "foo", "foo").? == 0);
assert(indexOf(u8, "foo", "fool") == null); assert(indexOf(u8, "foo", "fool") == null);
assert(lastIndexOf(u8, "foo", "lfoo") == null); assert(lastIndexOf(u8, "foo", "lfoo") == null);
assert(lastIndexOf(u8, "foo", "fool") == null); assert(lastIndexOf(u8, "foo", "fool") == null);
assert(??indexOf(u8, "foo foo", "foo") == 0); assert(indexOf(u8, "foo foo", "foo").? == 0);
assert(??lastIndexOf(u8, "foo foo", "foo") == 4); assert(lastIndexOf(u8, "foo foo", "foo").? == 4);
assert(??lastIndexOfAny(u8, "boo, cat", "abo") == 6); assert(lastIndexOfAny(u8, "boo, cat", "abo").? == 6);
assert(??lastIndexOfScalar(u8, "boo", 'o') == 2); assert(lastIndexOfScalar(u8, "boo", 'o').? == 2);
} }
/// Reads an integer from memory with size equal to bytes.len. /// Reads an integer from memory with size equal to bytes.len.
@ -432,9 +432,9 @@ pub fn split(buffer: []const u8, split_bytes: []const u8) SplitIterator {
test "mem.split" { test "mem.split" {
var it = split(" abc def ghi ", " "); var it = split(" abc def ghi ", " ");
assert(eql(u8, ??it.next(), "abc")); assert(eql(u8, it.next().?, "abc"));
assert(eql(u8, ??it.next(), "def")); assert(eql(u8, it.next().?, "def"));
assert(eql(u8, ??it.next(), "ghi")); assert(eql(u8, it.next().?, "ghi"));
assert(it.next() == null); assert(it.next() == null);
} }

View File

@ -156,7 +156,7 @@ pub const ChildProcess = struct {
}; };
} }
try self.waitUnwrappedWindows(); try self.waitUnwrappedWindows();
return ??self.term; return self.term.?;
} }
pub fn killPosix(self: *ChildProcess) !Term { pub fn killPosix(self: *ChildProcess) !Term {
@ -175,7 +175,7 @@ pub const ChildProcess = struct {
}; };
} }
self.waitUnwrapped(); self.waitUnwrapped();
return ??self.term; return self.term.?;
} }
/// Blocks until child process terminates and then cleans up all resources. /// Blocks until child process terminates and then cleans up all resources.
@ -212,8 +212,8 @@ pub const ChildProcess = struct {
defer Buffer.deinit(&stdout); defer Buffer.deinit(&stdout);
defer Buffer.deinit(&stderr); defer Buffer.deinit(&stderr);
var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
@ -232,7 +232,7 @@ pub const ChildProcess = struct {
} }
try self.waitUnwrappedWindows(); try self.waitUnwrappedWindows();
return ??self.term; return self.term.?;
} }
fn waitPosix(self: *ChildProcess) !Term { fn waitPosix(self: *ChildProcess) !Term {
@ -242,7 +242,7 @@ pub const ChildProcess = struct {
} }
self.waitUnwrapped(); self.waitUnwrapped();
return ??self.term; return self.term.?;
} }
pub fn deinit(self: *ChildProcess) void { pub fn deinit(self: *ChildProcess) void {
@ -619,13 +619,13 @@ pub const ChildProcess = struct {
self.term = null; self.term = null;
if (self.stdin_behavior == StdIo.Pipe) { if (self.stdin_behavior == StdIo.Pipe) {
os.close(??g_hChildStd_IN_Rd); os.close(g_hChildStd_IN_Rd.?);
} }
if (self.stderr_behavior == StdIo.Pipe) { if (self.stderr_behavior == StdIo.Pipe) {
os.close(??g_hChildStd_ERR_Wr); os.close(g_hChildStd_ERR_Wr.?);
} }
if (self.stdout_behavior == StdIo.Pipe) { if (self.stdout_behavior == StdIo.Pipe) {
os.close(??g_hChildStd_OUT_Wr); os.close(g_hChildStd_OUT_Wr.?);
} }
} }

View File

@ -327,7 +327,7 @@ pub fn raise(sig: i32) usize {
} }
pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize { pub fn read(fd: i32, buf: [*]u8, nbyte: usize) usize {
return errnoWrap(c.read(fd, @ptrCast([*]c_void, buf), nbyte)); return errnoWrap(c.read(fd, @ptrCast(*c_void, buf), nbyte));
} }
pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize { pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
@ -335,17 +335,17 @@ pub fn stat(noalias path: [*]const u8, noalias buf: *stat) usize {
} }
pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize { pub fn write(fd: i32, buf: [*]const u8, nbyte: usize) usize {
return errnoWrap(c.write(fd, @ptrCast([*]const c_void, buf), nbyte)); return errnoWrap(c.write(fd, @ptrCast(*const c_void, buf), nbyte));
} }
pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize { pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, offset: isize) usize {
const ptr_result = c.mmap(@ptrCast([*]c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset); const ptr_result = c.mmap(@ptrCast(*c_void, address), length, @bitCast(c_int, c_uint(prot)), @bitCast(c_int, c_uint(flags)), fd, offset);
const isize_result = @bitCast(isize, @ptrToInt(ptr_result)); const isize_result = @bitCast(isize, @ptrToInt(ptr_result));
return errnoWrap(isize_result); return errnoWrap(isize_result);
} }
pub fn munmap(address: usize, length: usize) usize { pub fn munmap(address: usize, length: usize) usize {
return errnoWrap(c.munmap(@intToPtr([*]c_void, address), length)); return errnoWrap(c.munmap(@intToPtr(*c_void, address), length));
} }
pub fn unlink(path: [*]const u8) usize { pub fn unlink(path: [*]const u8) usize {

View File

@ -96,7 +96,20 @@ pub const File = struct {
return File{ .handle = handle }; return File{ .handle = handle };
} }
pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) !bool { pub const AccessError = error{
PermissionDenied,
NotFound,
NameTooLong,
BadMode,
BadPathName,
Io,
SystemResources,
OutOfMemory,
Unexpected,
};
pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
const path_with_null = try std.cstr.addNullByte(allocator, path); const path_with_null = try std.cstr.addNullByte(allocator, path);
defer allocator.free(path_with_null); defer allocator.free(path_with_null);
@ -123,7 +136,7 @@ pub const File = struct {
} }
return true; return true;
} else if (is_windows) { } else if (is_windows) {
if (os.windows.PathFileExists(path_with_null.ptr) == os.windows.TRUE) { if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
return true; return true;
} }
@ -334,7 +347,7 @@ pub const File = struct {
while (index < buffer.len) { while (index < buffer.len) {
const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index)); const want_read_count = windows.DWORD(math.min(windows.DWORD(@maxValue(windows.DWORD)), buffer.len - index));
var amt_read: windows.DWORD = undefined; var amt_read: windows.DWORD = undefined;
if (windows.ReadFile(self.handle, @ptrCast([*]c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) { if (windows.ReadFile(self.handle, @ptrCast(*c_void, buffer.ptr + index), want_read_count, &amt_read, null) == 0) {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
windows.ERROR.OPERATION_ABORTED => continue, windows.ERROR.OPERATION_ABORTED => continue,

View File

@ -422,10 +422,10 @@ pub fn posixExecve(argv: []const []const u8, env_map: *const BufMap, allocator:
const exe_path = argv[0]; const exe_path = argv[0];
if (mem.indexOfScalar(u8, exe_path, '/') != null) { if (mem.indexOfScalar(u8, exe_path, '/') != null) {
return posixExecveErrnoToErr(posix.getErrno(posix.execve(??argv_buf[0], argv_buf.ptr, envp_buf.ptr))); return posixExecveErrnoToErr(posix.getErrno(posix.execve(argv_buf[0].?, argv_buf.ptr, envp_buf.ptr)));
} }
const PATH = getEnvPosix("PATH") ?? "/usr/local/bin:/bin/:/usr/bin"; const PATH = getEnvPosix("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
// PATH.len because it is >= the largest search_path // PATH.len because it is >= the largest search_path
// +1 for the / to join the search path and exe_path // +1 for the / to join the search path and exe_path
// +1 for the null terminating byte // +1 for the null terminating byte
@ -490,7 +490,7 @@ pub fn getEnvMap(allocator: *Allocator) !BufMap {
errdefer result.deinit(); errdefer result.deinit();
if (is_windows) { if (is_windows) {
const ptr = windows.GetEnvironmentStringsA() ?? return error.OutOfMemory; const ptr = windows.GetEnvironmentStringsA() orelse return error.OutOfMemory;
defer assert(windows.FreeEnvironmentStringsA(ptr) != 0); defer assert(windows.FreeEnvironmentStringsA(ptr) != 0);
var i: usize = 0; var i: usize = 0;
@ -573,7 +573,7 @@ pub fn getEnvVarOwned(allocator: *mem.Allocator, key: []const u8) ![]u8 {
return allocator.shrink(u8, buf, result); return allocator.shrink(u8, buf, result);
} }
} else { } else {
const result = getEnvPosix(key) ?? return error.EnvironmentVariableNotFound; const result = getEnvPosix(key) orelse return error.EnvironmentVariableNotFound;
return mem.dupe(allocator, u8, result); return mem.dupe(allocator, u8, result);
} }
} }
@ -714,7 +714,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
else => return err, // TODO zig should know this set does not include PathAlreadyExists else => return err, // TODO zig should know this set does not include PathAlreadyExists
} }
const dirname = os.path.dirname(new_path); const dirname = os.path.dirname(new_path) orelse ".";
var rand_buf: [12]u8 = undefined; var rand_buf: [12]u8 = undefined;
const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len)); const tmp_path = try allocator.alloc(u8, dirname.len + 1 + base64.Base64Encoder.calcSize(rand_buf.len));
@ -734,7 +734,23 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
} }
} }
pub fn deleteFile(allocator: *Allocator, file_path: []const u8) !void { pub const DeleteFileError = error{
FileNotFound,
AccessDenied,
FileBusy,
FileSystem,
IsDir,
SymLinkLoop,
NameTooLong,
NotDir,
SystemResources,
ReadOnlyFileSystem,
OutOfMemory,
Unexpected,
};
pub fn deleteFile(allocator: *Allocator, file_path: []const u8) DeleteFileError!void {
if (builtin.os == Os.windows) { if (builtin.os == Os.windows) {
return deleteFileWindows(allocator, file_path); return deleteFileWindows(allocator, file_path);
} else { } else {
@ -844,14 +860,14 @@ pub const AtomicFile = struct {
var rand_buf: [12]u8 = undefined; var rand_buf: [12]u8 = undefined;
const dirname_component_len = if (dirname.len == 0) 0 else dirname.len + 1; const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
const tmp_path = try allocator.alloc(u8, dirname_component_len + const tmp_path = try allocator.alloc(u8, dirname_component_len +
base64.Base64Encoder.calcSize(rand_buf.len)); base64.Base64Encoder.calcSize(rand_buf.len));
errdefer allocator.free(tmp_path); errdefer allocator.free(tmp_path);
if (dirname.len != 0) { if (dirname) |dir| {
mem.copy(u8, tmp_path[0..], dirname); mem.copy(u8, tmp_path[0..], dir);
tmp_path[dirname.len] = os.path.sep; tmp_path[dir.len] = os.path.sep;
} }
while (true) { while (true) {
@ -1019,37 +1035,66 @@ pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
} }
} }
pub const DeleteDirError = error{
AccessDenied,
FileBusy,
SymLinkLoop,
NameTooLong,
FileNotFound,
SystemResources,
NotDir,
DirNotEmpty,
ReadOnlyFileSystem,
OutOfMemory,
Unexpected,
};
/// Returns ::error.DirNotEmpty if the directory is not empty. /// Returns ::error.DirNotEmpty if the directory is not empty.
/// To delete a directory recursively, see ::deleteTree /// To delete a directory recursively, see ::deleteTree
pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) !void { pub fn deleteDir(allocator: *Allocator, dir_path: []const u8) DeleteDirError!void {
const path_buf = try allocator.alloc(u8, dir_path.len + 1); const path_buf = try allocator.alloc(u8, dir_path.len + 1);
defer allocator.free(path_buf); defer allocator.free(path_buf);
mem.copy(u8, path_buf, dir_path); mem.copy(u8, path_buf, dir_path);
path_buf[dir_path.len] = 0; path_buf[dir_path.len] = 0;
const err = posix.getErrno(posix.rmdir(path_buf.ptr)); switch (builtin.os) {
if (err > 0) { Os.windows => {
return switch (err) { if (windows.RemoveDirectoryA(path_buf.ptr) == 0) {
posix.EACCES, posix.EPERM => error.AccessDenied, const err = windows.GetLastError();
posix.EBUSY => error.FileBusy, return switch (err) {
posix.EFAULT, posix.EINVAL => unreachable, windows.ERROR.PATH_NOT_FOUND => error.FileNotFound,
posix.ELOOP => error.SymLinkLoop, windows.ERROR.DIR_NOT_EMPTY => error.DirNotEmpty,
posix.ENAMETOOLONG => error.NameTooLong, else => unexpectedErrorWindows(err),
posix.ENOENT => error.FileNotFound, };
posix.ENOMEM => error.SystemResources, }
posix.ENOTDIR => error.NotDir, },
posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty, Os.linux, Os.macosx, Os.ios => {
posix.EROFS => error.ReadOnlyFileSystem, const err = posix.getErrno(posix.rmdir(path_buf.ptr));
else => unexpectedErrorPosix(err), if (err > 0) {
}; return switch (err) {
posix.EACCES, posix.EPERM => error.AccessDenied,
posix.EBUSY => error.FileBusy,
posix.EFAULT, posix.EINVAL => unreachable,
posix.ELOOP => error.SymLinkLoop,
posix.ENAMETOOLONG => error.NameTooLong,
posix.ENOENT => error.FileNotFound,
posix.ENOMEM => error.SystemResources,
posix.ENOTDIR => error.NotDir,
posix.EEXIST, posix.ENOTEMPTY => error.DirNotEmpty,
posix.EROFS => error.ReadOnlyFileSystem,
else => unexpectedErrorPosix(err),
};
}
},
else => @compileError("unimplemented"),
} }
} }
/// Whether ::full_path describes a symlink, file, or directory, this function /// Whether ::full_path describes a symlink, file, or directory, this function
/// removes it. If it cannot be removed because it is a non-empty directory, /// removes it. If it cannot be removed because it is a non-empty directory,
/// this function recursively removes its entries and then tries again. /// this function recursively removes its entries and then tries again.
/// TODO non-recursive implementation
const DeleteTreeError = error{ const DeleteTreeError = error{
OutOfMemory, OutOfMemory,
AccessDenied, AccessDenied,
@ -1128,7 +1173,7 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
try full_entry_buf.resize(full_path.len + entry.name.len + 1); try full_entry_buf.resize(full_path.len + entry.name.len + 1);
const full_entry_path = full_entry_buf.toSlice(); const full_entry_path = full_entry_buf.toSlice();
mem.copy(u8, full_entry_path, full_path); mem.copy(u8, full_entry_path, full_path);
full_entry_path[full_path.len] = '/'; full_entry_path[full_path.len] = path.sep;
mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name); mem.copy(u8, full_entry_path[full_path.len + 1 ..], entry.name);
try deleteTree(allocator, full_entry_path); try deleteTree(allocator, full_entry_path);
@ -1139,16 +1184,29 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
} }
pub const Dir = struct { pub const Dir = struct {
fd: i32, handle: Handle,
darwin_seek: darwin_seek_t,
allocator: *Allocator, allocator: *Allocator,
buf: []u8,
index: usize,
end_index: usize,
const darwin_seek_t = switch (builtin.os) { pub const Handle = switch (builtin.os) {
Os.macosx, Os.ios => i64, Os.macosx, Os.ios => struct {
else => void, fd: i32,
seek: i64,
buf: []u8,
index: usize,
end_index: usize,
},
Os.linux => struct {
fd: i32,
buf: []u8,
index: usize,
end_index: usize,
},
Os.windows => struct {
handle: windows.HANDLE,
find_file_data: windows.WIN32_FIND_DATAA,
first: bool,
},
else => @compileError("unimplemented"),
}; };
pub const Entry = struct { pub const Entry = struct {
@ -1168,81 +1226,122 @@ pub const Dir = struct {
}; };
}; };
pub fn open(allocator: *Allocator, dir_path: []const u8) !Dir { pub const OpenError = error{
const fd = switch (builtin.os) { PathNotFound,
Os.windows => @compileError("TODO support Dir.open for windows"), NotDir,
Os.linux => try posixOpen(allocator, dir_path, posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC, 0), AccessDenied,
Os.macosx, Os.ios => try posixOpen( FileTooBig,
allocator, IsDir,
dir_path, SymLinkLoop,
posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC, ProcessFdQuotaExceeded,
0, NameTooLong,
), SystemFdQuotaExceeded,
else => @compileError("Dir.open is not supported for this platform"), NoDevice,
}; SystemResources,
const darwin_seek_init = switch (builtin.os) { NoSpaceLeft,
Os.macosx, Os.ios => 0, PathAlreadyExists,
else => {}, OutOfMemory,
};
Unexpected,
};
pub fn open(allocator: *Allocator, dir_path: []const u8) OpenError!Dir {
return Dir{ return Dir{
.allocator = allocator, .allocator = allocator,
.fd = fd, .handle = switch (builtin.os) {
.darwin_seek = darwin_seek_init, Os.windows => blk: {
.index = 0, var find_file_data: windows.WIN32_FIND_DATAA = undefined;
.end_index = 0, const handle = try windows_util.windowsFindFirstFile(allocator, dir_path, &find_file_data);
.buf = []u8{}, break :blk Handle{
.handle = handle,
.find_file_data = find_file_data, // TODO guaranteed copy elision
.first = true,
};
},
Os.macosx, Os.ios => Handle{
.fd = try posixOpen(
allocator,
dir_path,
posix.O_RDONLY | posix.O_NONBLOCK | posix.O_DIRECTORY | posix.O_CLOEXEC,
0,
),
.seek = 0,
.index = 0,
.end_index = 0,
.buf = []u8{},
},
Os.linux => Handle{
.fd = try posixOpen(
allocator,
dir_path,
posix.O_RDONLY | posix.O_DIRECTORY | posix.O_CLOEXEC,
0,
),
.index = 0,
.end_index = 0,
.buf = []u8{},
},
else => @compileError("unimplemented"),
},
}; };
} }
pub fn close(self: *Dir) void { pub fn close(self: *Dir) void {
self.allocator.free(self.buf); switch (builtin.os) {
os.close(self.fd); Os.windows => {
_ = windows.FindClose(self.handle.handle);
},
Os.macosx, Os.ios, Os.linux => {
self.allocator.free(self.handle.buf);
os.close(self.handle.fd);
},
else => @compileError("unimplemented"),
}
} }
/// Memory such as file names referenced in this returned entry becomes invalid /// 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. /// with subsequent calls to next, as well as when this `Dir` is deinitialized.
pub fn next(self: *Dir) !?Entry { pub fn next(self: *Dir) !?Entry {
switch (builtin.os) { switch (builtin.os) {
Os.linux => return self.nextLinux(), Os.linux => return self.nextLinux(),
Os.macosx, Os.ios => return self.nextDarwin(), Os.macosx, Os.ios => return self.nextDarwin(),
Os.windows => return self.nextWindows(), Os.windows => return self.nextWindows(),
else => @compileError("Dir.next not supported on " ++ @tagName(builtin.os)), else => @compileError("unimplemented"),
} }
} }
fn nextDarwin(self: *Dir) !?Entry { fn nextDarwin(self: *Dir) !?Entry {
start_over: while (true) { start_over: while (true) {
if (self.index >= self.end_index) { if (self.handle.index >= self.handle.end_index) {
if (self.buf.len == 0) { if (self.handle.buf.len == 0) {
self.buf = try self.allocator.alloc(u8, page_size); self.handle.buf = try self.allocator.alloc(u8, page_size);
} }
while (true) { while (true) {
const result = posix.getdirentries64(self.fd, self.buf.ptr, self.buf.len, &self.darwin_seek); const result = posix.getdirentries64(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len, &self.handle.seek);
const err = posix.getErrno(result); const err = posix.getErrno(result);
if (err > 0) { if (err > 0) {
switch (err) { switch (err) {
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
posix.EINVAL => { posix.EINVAL => {
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2);
continue; continue;
}, },
else => return unexpectedErrorPosix(err), else => return unexpectedErrorPosix(err),
} }
} }
if (result == 0) return null; if (result == 0) return null;
self.index = 0; self.handle.index = 0;
self.end_index = result; self.handle.end_index = result;
break; break;
} }
} }
const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]); const darwin_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
const next_index = self.index + darwin_entry.d_reclen; const next_index = self.handle.index + darwin_entry.d_reclen;
self.index = next_index; self.handle.index = next_index;
const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen]; const name = @ptrCast([*]u8, &darwin_entry.d_name)[0..darwin_entry.d_namlen];
// skip . and .. entries
if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) { if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
continue :start_over; continue :start_over;
} }
@ -1266,38 +1365,59 @@ pub const Dir = struct {
} }
fn nextWindows(self: *Dir) !?Entry { fn nextWindows(self: *Dir) !?Entry {
@compileError("TODO support Dir.next for windows"); while (true) {
if (self.handle.first) {
self.handle.first = false;
} else {
if (!try windows_util.windowsFindNextFile(self.handle.handle, &self.handle.find_file_data))
return null;
}
const name = std.cstr.toSlice(self.handle.find_file_data.cFileName[0..].ptr);
if (mem.eql(u8, name, ".") or mem.eql(u8, name, ".."))
continue;
const kind = blk: {
const attrs = self.handle.find_file_data.dwFileAttributes;
if (attrs & windows.FILE_ATTRIBUTE_DIRECTORY != 0) break :blk Entry.Kind.Directory;
if (attrs & windows.FILE_ATTRIBUTE_REPARSE_POINT != 0) break :blk Entry.Kind.SymLink;
if (attrs & windows.FILE_ATTRIBUTE_NORMAL != 0) break :blk Entry.Kind.File;
break :blk Entry.Kind.Unknown;
};
return Entry{
.name = name,
.kind = kind,
};
}
} }
fn nextLinux(self: *Dir) !?Entry { fn nextLinux(self: *Dir) !?Entry {
start_over: while (true) { start_over: while (true) {
if (self.index >= self.end_index) { if (self.handle.index >= self.handle.end_index) {
if (self.buf.len == 0) { if (self.handle.buf.len == 0) {
self.buf = try self.allocator.alloc(u8, page_size); self.handle.buf = try self.allocator.alloc(u8, page_size);
} }
while (true) { while (true) {
const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len); const result = posix.getdents(self.handle.fd, self.handle.buf.ptr, self.handle.buf.len);
const err = posix.getErrno(result); const err = posix.getErrno(result);
if (err > 0) { if (err > 0) {
switch (err) { switch (err) {
posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable, posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
posix.EINVAL => { posix.EINVAL => {
self.buf = try self.allocator.realloc(u8, self.buf, self.buf.len * 2); self.handle.buf = try self.allocator.realloc(u8, self.handle.buf, self.handle.buf.len * 2);
continue; continue;
}, },
else => return unexpectedErrorPosix(err), else => return unexpectedErrorPosix(err),
} }
} }
if (result == 0) return null; if (result == 0) return null;
self.index = 0; self.handle.index = 0;
self.end_index = result; self.handle.end_index = result;
break; break;
} }
} }
const linux_entry = @ptrCast(*align(1) posix.dirent, &self.buf[self.index]); const linux_entry = @ptrCast(*align(1) posix.dirent, &self.handle.buf[self.handle.index]);
const next_index = self.index + linux_entry.d_reclen; const next_index = self.handle.index + linux_entry.d_reclen;
self.index = next_index; self.handle.index = next_index;
const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name)); const name = cstr.toSlice(@ptrCast([*]u8, &linux_entry.d_name));
@ -1306,7 +1426,7 @@ pub const Dir = struct {
continue :start_over; continue :start_over;
} }
const type_char = self.buf[next_index - 1]; const type_char = self.handle.buf[next_index - 1];
const entry_kind = switch (type_char) { const entry_kind = switch (type_char) {
posix.DT_BLK => Entry.Kind.BlockDevice, posix.DT_BLK => Entry.Kind.BlockDevice,
posix.DT_CHR => Entry.Kind.CharacterDevice, posix.DT_CHR => Entry.Kind.CharacterDevice,
@ -1641,7 +1761,7 @@ pub const ArgIterator = struct {
if (builtin.os == Os.windows) { if (builtin.os == Os.windows) {
return self.inner.next(allocator); return self.inner.next(allocator);
} else { } else {
return mem.dupe(allocator, u8, self.inner.next() ?? return null); return mem.dupe(allocator, u8, self.inner.next() orelse return null);
} }
} }
@ -1729,7 +1849,7 @@ test "windows arg parsing" {
fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void { fn testWindowsCmdLine(input_cmd_line: [*]const u8, expected_args: []const []const u8) void {
var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line); var it = ArgIteratorWindows.initWithCmdLine(input_cmd_line);
for (expected_args) |expected_arg| { for (expected_args) |expected_arg| {
const arg = ??it.next(debug.global_allocator) catch unreachable; const arg = it.next(debug.global_allocator).? catch unreachable;
assert(mem.eql(u8, arg, expected_arg)); assert(mem.eql(u8, arg, expected_arg));
} }
assert(it.next(debug.global_allocator) == null); assert(it.next(debug.global_allocator) == null);
@ -1845,13 +1965,13 @@ pub fn selfExeDirPath(allocator: *mem.Allocator) ![]u8 {
// the executable was in when it was run. // the executable was in when it was run.
const full_exe_path = try readLink(allocator, "/proc/self/exe"); const full_exe_path = try readLink(allocator, "/proc/self/exe");
errdefer allocator.free(full_exe_path); errdefer allocator.free(full_exe_path);
const dir = path.dirname(full_exe_path); const dir = path.dirname(full_exe_path) orelse ".";
return allocator.shrink(u8, full_exe_path, dir.len); return allocator.shrink(u8, full_exe_path, dir.len);
}, },
Os.windows, Os.macosx, Os.ios => { Os.windows, Os.macosx, Os.ios => {
const self_exe_path = try selfExePath(allocator); const self_exe_path = try selfExePath(allocator);
errdefer allocator.free(self_exe_path); errdefer allocator.free(self_exe_path);
const dirname = os.path.dirname(self_exe_path); const dirname = os.path.dirname(self_exe_path) orelse ".";
return allocator.shrink(u8, self_exe_path, dirname.len); return allocator.shrink(u8, self_exe_path, dirname.len);
}, },
else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)), else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),
@ -2362,7 +2482,7 @@ pub const Thread = struct {
}, },
builtin.Os.windows => struct { builtin.Os.windows => struct {
handle: windows.HANDLE, handle: windows.HANDLE,
alloc_start: [*]c_void, alloc_start: *c_void,
heap_handle: windows.HANDLE, heap_handle: windows.HANDLE,
}, },
else => @compileError("Unsupported OS"), else => @compileError("Unsupported OS"),
@ -2457,9 +2577,9 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
} }
}; };
const heap_handle = windows.GetProcessHeap() ?? return SpawnThreadError.OutOfMemory; const heap_handle = windows.GetProcessHeap() orelse return SpawnThreadError.OutOfMemory;
const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext); const byte_count = @alignOf(WinThread.OuterContext) + @sizeOf(WinThread.OuterContext);
const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) ?? return SpawnThreadError.OutOfMemory; const bytes_ptr = windows.HeapAlloc(heap_handle, 0, byte_count) orelse return SpawnThreadError.OutOfMemory;
errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0); errdefer assert(windows.HeapFree(heap_handle, 0, bytes_ptr) != 0);
const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count]; const bytes = @ptrCast([*]u8, bytes_ptr)[0..byte_count];
const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable; const outer_context = std.heap.FixedBufferAllocator.init(bytes).allocator.create(WinThread.OuterContext) catch unreachable;
@ -2468,7 +2588,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
outer_context.thread.data.alloc_start = bytes_ptr; outer_context.thread.data.alloc_start = bytes_ptr;
const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner); const parameter = if (@sizeOf(Context) == 0) null else @ptrCast(*c_void, &outer_context.inner);
outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) ?? { outer_context.thread.data.handle = windows.CreateThread(null, default_stack_size, WinThread.threadMain, parameter, 0, null) orelse {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
else => os.unexpectedErrorWindows(err), else => os.unexpectedErrorWindows(err),
@ -2533,7 +2653,7 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
// align to page // align to page
stack_end -= stack_end % os.page_size; stack_end -= stack_end % os.page_size;
assert(c.pthread_attr_setstack(&attr, @intToPtr([*]c_void, stack_addr), stack_end - stack_addr) == 0); assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg)); const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
switch (err) { switch (err) {

View File

@ -28,7 +28,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
} }
} }
} }
const dynv = maybe_dynv ?? return 0; const dynv = maybe_dynv orelse return 0;
if (base == @maxValue(usize)) return 0; if (base == @maxValue(usize)) return 0;
var maybe_strings: ?[*]u8 = null; var maybe_strings: ?[*]u8 = null;
@ -52,9 +52,9 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
} }
} }
const strings = maybe_strings ?? return 0; const strings = maybe_strings orelse return 0;
const syms = maybe_syms ?? return 0; const syms = maybe_syms orelse return 0;
const hashtab = maybe_hashtab ?? return 0; const hashtab = maybe_hashtab orelse return 0;
if (maybe_verdef == null) maybe_versym = null; if (maybe_verdef == null) maybe_versym = null;
const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON); const OK_TYPES = (1 << elf.STT_NOTYPE | 1 << elf.STT_OBJECT | 1 << elf.STT_FUNC | 1 << elf.STT_COMMON);
@ -67,7 +67,7 @@ pub fn lookup(vername: []const u8, name: []const u8) usize {
if (0 == syms[i].st_shndx) continue; if (0 == syms[i].st_shndx) continue;
if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue; if (!mem.eql(u8, name, cstr.toSliceConst(strings + syms[i].st_name))) continue;
if (maybe_versym) |versym| { if (maybe_versym) |versym| {
if (!checkver(??maybe_verdef, versym[i], vername, strings)) if (!checkver(maybe_verdef.?, versym[i], vername, strings))
continue; continue;
} }
return base + syms[i].st_value; return base + syms[i].st_value;

View File

@ -182,8 +182,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
} }
var it = mem.split(path, []u8{this_sep}); var it = mem.split(path, []u8{this_sep});
_ = (it.next() ?? return relative_path); _ = (it.next() orelse return relative_path);
_ = (it.next() ?? return relative_path); _ = (it.next() orelse return relative_path);
return WindowsPath{ return WindowsPath{
.is_abs = isAbsoluteWindows(path), .is_abs = isAbsoluteWindows(path),
.kind = WindowsPath.Kind.NetworkShare, .kind = WindowsPath.Kind.NetworkShare,
@ -200,8 +200,8 @@ pub fn windowsParsePath(path: []const u8) WindowsPath {
} }
var it = mem.split(path, []u8{this_sep}); var it = mem.split(path, []u8{this_sep});
_ = (it.next() ?? return relative_path); _ = (it.next() orelse return relative_path);
_ = (it.next() ?? return relative_path); _ = (it.next() orelse return relative_path);
return WindowsPath{ return WindowsPath{
.is_abs = isAbsoluteWindows(path), .is_abs = isAbsoluteWindows(path),
.kind = WindowsPath.Kind.NetworkShare, .kind = WindowsPath.Kind.NetworkShare,
@ -265,7 +265,7 @@ fn networkShareServersEql(ns1: []const u8, ns2: []const u8) bool {
var it2 = mem.split(ns2, []u8{sep2}); var it2 = mem.split(ns2, []u8{sep2});
// TODO ASCII is wrong, we actually need full unicode support to compare paths. // TODO ASCII is wrong, we actually need full unicode support to compare paths.
return asciiEqlIgnoreCase(??it1.next(), ??it2.next()); return asciiEqlIgnoreCase(it1.next().?, it2.next().?);
} }
fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool { fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8) bool {
@ -286,7 +286,7 @@ fn compareDiskDesignators(kind: WindowsPath.Kind, p1: []const u8, p2: []const u8
var it2 = mem.split(p2, []u8{sep2}); var it2 = mem.split(p2, []u8{sep2});
// TODO ASCII is wrong, we actually need full unicode support to compare paths. // TODO ASCII is wrong, we actually need full unicode support to compare paths.
return asciiEqlIgnoreCase(??it1.next(), ??it2.next()) and asciiEqlIgnoreCase(??it1.next(), ??it2.next()); return asciiEqlIgnoreCase(it1.next().?, it2.next().?) and asciiEqlIgnoreCase(it1.next().?, it2.next().?);
}, },
} }
} }
@ -414,8 +414,8 @@ pub fn resolveWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
WindowsPath.Kind.NetworkShare => { WindowsPath.Kind.NetworkShare => {
result = try allocator.alloc(u8, max_size); result = try allocator.alloc(u8, max_size);
var it = mem.split(paths[first_index], "/\\"); var it = mem.split(paths[first_index], "/\\");
const server_name = ??it.next(); const server_name = it.next().?;
const other_name = ??it.next(); const other_name = it.next().?;
result[result_index] = '\\'; result[result_index] = '\\';
result_index += 1; result_index += 1;
@ -648,8 +648,8 @@ fn testResolvePosix(paths: []const []const u8) []u8 {
} }
/// If the path is a file in the current directory (no directory component) /// If the path is a file in the current directory (no directory component)
/// then the returned slice has .len = 0. /// then returns null
pub fn dirname(path: []const u8) []const u8 { pub fn dirname(path: []const u8) ?[]const u8 {
if (is_windows) { if (is_windows) {
return dirnameWindows(path); return dirnameWindows(path);
} else { } else {
@ -657,9 +657,9 @@ pub fn dirname(path: []const u8) []const u8 {
} }
} }
pub fn dirnameWindows(path: []const u8) []const u8 { pub fn dirnameWindows(path: []const u8) ?[]const u8 {
if (path.len == 0) if (path.len == 0)
return path[0..0]; return null;
const root_slice = diskDesignatorWindows(path); const root_slice = diskDesignatorWindows(path);
if (path.len == root_slice.len) if (path.len == root_slice.len)
@ -671,13 +671,13 @@ pub fn dirnameWindows(path: []const u8) []const u8 {
while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > root_slice.len) { while ((path[end_index] == '/' or path[end_index] == '\\') and end_index > root_slice.len) {
if (end_index == 0) if (end_index == 0)
return path[0..0]; return null;
end_index -= 1; end_index -= 1;
} }
while (path[end_index] != '/' and path[end_index] != '\\' and end_index > root_slice.len) { while (path[end_index] != '/' and path[end_index] != '\\' and end_index > root_slice.len) {
if (end_index == 0) if (end_index == 0)
return path[0..0]; return null;
end_index -= 1; end_index -= 1;
} }
@ -685,12 +685,15 @@ pub fn dirnameWindows(path: []const u8) []const u8 {
end_index += 1; end_index += 1;
} }
if (end_index == 0)
return null;
return path[0..end_index]; return path[0..end_index];
} }
pub fn dirnamePosix(path: []const u8) []const u8 { pub fn dirnamePosix(path: []const u8) ?[]const u8 {
if (path.len == 0) if (path.len == 0)
return path[0..0]; return null;
var end_index: usize = path.len - 1; var end_index: usize = path.len - 1;
while (path[end_index] == '/') { while (path[end_index] == '/') {
@ -701,13 +704,16 @@ pub fn dirnamePosix(path: []const u8) []const u8 {
while (path[end_index] != '/') { while (path[end_index] != '/') {
if (end_index == 0) if (end_index == 0)
return path[0..0]; return null;
end_index -= 1; end_index -= 1;
} }
if (end_index == 0 and path[end_index] == '/') if (end_index == 0 and path[end_index] == '/')
return path[0..1]; return path[0..1];
if (end_index == 0)
return null;
return path[0..end_index]; return path[0..end_index];
} }
@ -717,10 +723,10 @@ test "os.path.dirnamePosix" {
testDirnamePosix("/a", "/"); testDirnamePosix("/a", "/");
testDirnamePosix("/", "/"); testDirnamePosix("/", "/");
testDirnamePosix("////", "/"); testDirnamePosix("////", "/");
testDirnamePosix("", ""); testDirnamePosix("", null);
testDirnamePosix("a", ""); testDirnamePosix("a", null);
testDirnamePosix("a/", ""); testDirnamePosix("a/", null);
testDirnamePosix("a//", ""); testDirnamePosix("a//", null);
} }
test "os.path.dirnameWindows" { test "os.path.dirnameWindows" {
@ -742,7 +748,7 @@ test "os.path.dirnameWindows" {
testDirnameWindows("c:foo\\bar", "c:foo"); testDirnameWindows("c:foo\\bar", "c:foo");
testDirnameWindows("c:foo\\bar\\", "c:foo"); testDirnameWindows("c:foo\\bar\\", "c:foo");
testDirnameWindows("c:foo\\bar\\baz", "c:foo\\bar"); testDirnameWindows("c:foo\\bar\\baz", "c:foo\\bar");
testDirnameWindows("file:stream", ""); testDirnameWindows("file:stream", null);
testDirnameWindows("dir\\file:stream", "dir"); testDirnameWindows("dir\\file:stream", "dir");
testDirnameWindows("\\\\unc\\share", "\\\\unc\\share"); testDirnameWindows("\\\\unc\\share", "\\\\unc\\share");
testDirnameWindows("\\\\unc\\share\\foo", "\\\\unc\\share\\"); testDirnameWindows("\\\\unc\\share\\foo", "\\\\unc\\share\\");
@ -753,18 +759,26 @@ test "os.path.dirnameWindows" {
testDirnameWindows("/a/b/", "/a"); testDirnameWindows("/a/b/", "/a");
testDirnameWindows("/a/b", "/a"); testDirnameWindows("/a/b", "/a");
testDirnameWindows("/a", "/"); testDirnameWindows("/a", "/");
testDirnameWindows("", ""); testDirnameWindows("", null);
testDirnameWindows("/", "/"); testDirnameWindows("/", "/");
testDirnameWindows("////", "/"); testDirnameWindows("////", "/");
testDirnameWindows("foo", ""); testDirnameWindows("foo", null);
} }
fn testDirnamePosix(input: []const u8, expected_output: []const u8) void { fn testDirnamePosix(input: []const u8, expected_output: ?[]const u8) void {
assert(mem.eql(u8, dirnamePosix(input), expected_output)); if (dirnamePosix(input)) |output| {
assert(mem.eql(u8, output, expected_output.?));
} else {
assert(expected_output == null);
}
} }
fn testDirnameWindows(input: []const u8, expected_output: []const u8) void { fn testDirnameWindows(input: []const u8, expected_output: ?[]const u8) void {
assert(mem.eql(u8, dirnameWindows(input), expected_output)); if (dirnameWindows(input)) |output| {
assert(mem.eql(u8, output, expected_output.?));
} else {
assert(expected_output == null);
}
} }
pub fn basename(path: []const u8) []const u8 { pub fn basename(path: []const u8) []const u8 {
@ -923,7 +937,7 @@ pub fn relativeWindows(allocator: *Allocator, from: []const u8, to: []const u8)
var from_it = mem.split(resolved_from, "/\\"); var from_it = mem.split(resolved_from, "/\\");
var to_it = mem.split(resolved_to, "/\\"); var to_it = mem.split(resolved_to, "/\\");
while (true) { while (true) {
const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
const to_rest = to_it.rest(); const to_rest = to_it.rest();
if (to_it.next()) |to_component| { if (to_it.next()) |to_component| {
// TODO ASCII is wrong, we actually need full unicode support to compare paths. // TODO ASCII is wrong, we actually need full unicode support to compare paths.
@ -974,7 +988,7 @@ pub fn relativePosix(allocator: *Allocator, from: []const u8, to: []const u8) ![
var from_it = mem.split(resolved_from, "/"); var from_it = mem.split(resolved_from, "/");
var to_it = mem.split(resolved_to, "/"); var to_it = mem.split(resolved_to, "/");
while (true) { while (true) {
const from_component = from_it.next() ?? return mem.dupe(allocator, u8, to_it.rest()); const from_component = from_it.next() orelse return mem.dupe(allocator, u8, to_it.rest());
const to_rest = to_it.rest(); const to_rest = to_it.rest();
if (to_it.next()) |to_component| { if (to_it.next()) |to_component| {
if (mem.eql(u8, from_component, to_component)) if (mem.eql(u8, from_component, to_component))

View File

@ -10,11 +10,6 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder; const AtomicOrder = builtin.AtomicOrder;
test "makePath, put some files in it, deleteTree" { test "makePath, put some files in it, deleteTree" {
if (builtin.os == builtin.Os.windows) {
// TODO implement os.Dir for windows
// https://github.com/ziglang/zig/issues/709
return;
}
try os.makePath(a, "os_test_tmp/b/c"); try os.makePath(a, "os_test_tmp/b/c");
try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense"); try io.writeFile(a, "os_test_tmp/b/c/file.txt", "nonsense");
try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah"); try io.writeFile(a, "os_test_tmp/b/file2.txt", "blah");
@ -27,10 +22,6 @@ test "makePath, put some files in it, deleteTree" {
} }
test "access file" { test "access file" {
if (builtin.os == builtin.Os.windows) {
return;
}
try os.makePath(a, "os_test_tmp"); try os.makePath(a, "os_test_tmp");
if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| { if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
unreachable; unreachable;

View File

@ -68,11 +68,13 @@ pub const milliTimestamp = switch (builtin.os) {
fn milliTimestampWindows() u64 { fn milliTimestampWindows() u64 {
//FileTime has a granularity of 100 nanoseconds //FileTime has a granularity of 100 nanoseconds
// and uses the NTFS/Windows epoch // and uses the NTFS/Windows epoch
var ft: i64 = undefined; var ft: windows.FILETIME = undefined;
windows.GetSystemTimeAsFileTime(&ft); windows.GetSystemTimeAsFileTime(&ft);
const hns_per_ms = (ns_per_s / 100) / ms_per_s; const hns_per_ms = (ns_per_s / 100) / ms_per_s;
const epoch_adj = epoch.windows * ms_per_s; const epoch_adj = epoch.windows * ms_per_s;
return u64(@divFloor(ft, hns_per_ms) + epoch_adj);
const ft64 = (u64(ft.dwHighDateTime) << 32) | ft.dwLowDateTime;
return @divFloor(ft64, hns_per_ms) - -epoch_adj;
} }
fn milliTimestampDarwin() u64 { fn milliTimestampDarwin() u64 {

View File

@ -1,3 +1,7 @@
test "import" {
_ = @import("util.zig");
}
pub const ERROR = @import("error.zig"); pub const ERROR = @import("error.zig");
pub extern "advapi32" stdcallcc fn CryptAcquireContextA( pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
@ -61,6 +65,10 @@ pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn; pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL; pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR; pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
@ -77,6 +85,8 @@ pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCo
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD; pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetLastError() DWORD; pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
@ -97,21 +107,21 @@ pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE; pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(?*FILETIME) void; pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE; pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL; pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void, dwBytes: SIZE_T) ?[*]c_void; pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) SIZE_T; pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]const c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T; pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL; pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE; pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?[*]c_void; pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: [*]c_void) BOOL; pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExA( pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: LPCSTR, lpExistingFileName: LPCSTR,
@ -123,16 +133,16 @@ pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL; pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn PathFileExists(pszPath: ?LPCTSTR) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile( pub extern "kernel32" stdcallcc fn ReadFile(
in_hFile: HANDLE, in_hFile: HANDLE,
out_lpBuffer: [*]c_void, out_lpBuffer: *c_void,
in_nNumberOfBytesToRead: DWORD, in_nNumberOfBytesToRead: DWORD,
out_lpNumberOfBytesRead: *DWORD, out_lpNumberOfBytesRead: *DWORD,
in_out_lpOverlapped: ?*OVERLAPPED, in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL; ) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn SetFilePointerEx( pub extern "kernel32" stdcallcc fn SetFilePointerEx(
in_fFile: HANDLE, in_fFile: HANDLE,
in_liDistanceToMove: LARGE_INTEGER, in_liDistanceToMove: LARGE_INTEGER,
@ -150,7 +160,7 @@ pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMillis
pub extern "kernel32" stdcallcc fn WriteFile( pub extern "kernel32" stdcallcc fn WriteFile(
in_hFile: HANDLE, in_hFile: HANDLE,
in_lpBuffer: [*]const c_void, in_lpBuffer: *const c_void,
in_nNumberOfBytesToWrite: DWORD, in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD, out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED, in_out_lpOverlapped: ?*OVERLAPPED,
@ -163,6 +173,8 @@ pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int; pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
pub const PROV_RSA_FULL = 1; pub const PROV_RSA_FULL = 1;
pub const BOOL = c_int; pub const BOOL = c_int;
@ -196,7 +208,6 @@ pub const UNICODE = false;
pub const WCHAR = u16; pub const WCHAR = u16;
pub const WORD = u16; pub const WORD = u16;
pub const LARGE_INTEGER = i64; pub const LARGE_INTEGER = i64;
pub const FILETIME = i64;
pub const TRUE = 1; pub const TRUE = 1;
pub const FALSE = 0; pub const FALSE = 0;
@ -212,6 +223,8 @@ pub const STD_ERROR_HANDLE = @maxValue(DWORD) - 12 + 1;
pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize)); pub const INVALID_HANDLE_VALUE = @intToPtr(HANDLE, @maxValue(usize));
pub const INVALID_FILE_ATTRIBUTES = DWORD(@maxValue(DWORD));
pub const OVERLAPPED = extern struct { pub const OVERLAPPED = extern struct {
Internal: ULONG_PTR, Internal: ULONG_PTR,
InternalHigh: ULONG_PTR, InternalHigh: ULONG_PTR,
@ -293,13 +306,24 @@ pub const OPEN_EXISTING = 3;
pub const TRUNCATE_EXISTING = 5; pub const TRUNCATE_EXISTING = 5;
pub const FILE_ATTRIBUTE_ARCHIVE = 0x20; pub const FILE_ATTRIBUTE_ARCHIVE = 0x20;
pub const FILE_ATTRIBUTE_COMPRESSED = 0x800;
pub const FILE_ATTRIBUTE_DEVICE = 0x40;
pub const FILE_ATTRIBUTE_DIRECTORY = 0x10;
pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000; pub const FILE_ATTRIBUTE_ENCRYPTED = 0x4000;
pub const FILE_ATTRIBUTE_HIDDEN = 0x2; pub const FILE_ATTRIBUTE_HIDDEN = 0x2;
pub const FILE_ATTRIBUTE_INTEGRITY_STREAM = 0x8000;
pub const FILE_ATTRIBUTE_NORMAL = 0x80; pub const FILE_ATTRIBUTE_NORMAL = 0x80;
pub const FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000;
pub const FILE_ATTRIBUTE_NO_SCRUB_DATA = 0x20000;
pub const FILE_ATTRIBUTE_OFFLINE = 0x1000; pub const FILE_ATTRIBUTE_OFFLINE = 0x1000;
pub const FILE_ATTRIBUTE_READONLY = 0x1; pub const FILE_ATTRIBUTE_READONLY = 0x1;
pub const FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x400000;
pub const FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x40000;
pub const FILE_ATTRIBUTE_REPARSE_POINT = 0x400;
pub const FILE_ATTRIBUTE_SPARSE_FILE = 0x200;
pub const FILE_ATTRIBUTE_SYSTEM = 0x4; pub const FILE_ATTRIBUTE_SYSTEM = 0x4;
pub const FILE_ATTRIBUTE_TEMPORARY = 0x100; pub const FILE_ATTRIBUTE_TEMPORARY = 0x100;
pub const FILE_ATTRIBUTE_VIRTUAL = 0x10000;
pub const PROCESS_INFORMATION = extern struct { pub const PROCESS_INFORMATION = extern struct {
hProcess: HANDLE, hProcess: HANDLE,
@ -372,6 +396,20 @@ pub const HEAP_NO_SERIALIZE = 0x00000001;
pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD; pub const PTHREAD_START_ROUTINE = extern fn (LPVOID) DWORD;
pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE; pub const LPTHREAD_START_ROUTINE = PTHREAD_START_ROUTINE;
test "import" { pub const WIN32_FIND_DATAA = extern struct {
_ = @import("util.zig"); dwFileAttributes: DWORD,
} ftCreationTime: FILETIME,
ftLastAccessTime: FILETIME,
ftLastWriteTime: FILETIME,
nFileSizeHigh: DWORD,
nFileSizeLow: DWORD,
dwReserved0: DWORD,
dwReserved1: DWORD,
cFileName: [260]CHAR,
cAlternateFileName: [14]CHAR,
};
pub const FILETIME = extern struct {
dwLowDateTime: DWORD,
dwHighDateTime: DWORD,
};

View File

@ -42,7 +42,7 @@ pub const WriteError = error{
}; };
pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void { pub fn windowsWrite(handle: windows.HANDLE, bytes: []const u8) WriteError!void {
if (windows.WriteFile(handle, @ptrCast([*]const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) { if (windows.WriteFile(handle, @ptrCast(*const c_void, bytes.ptr), u32(bytes.len), null, null) == 0) {
const err = windows.GetLastError(); const err = windows.GetLastError();
return switch (err) { return switch (err) {
windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources, windows.ERROR.INVALID_USER_BUFFER => WriteError.SystemResources,
@ -153,7 +153,7 @@ pub fn createWindowsEnvBlock(allocator: *mem.Allocator, env_map: *const BufMap)
pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE { pub fn windowsLoadDll(allocator: *mem.Allocator, dll_path: []const u8) !windows.HMODULE {
const padded_buff = try cstr.addNullByte(allocator, dll_path); const padded_buff = try cstr.addNullByte(allocator, dll_path);
defer allocator.free(padded_buff); defer allocator.free(padded_buff);
return windows.LoadLibraryA(padded_buff.ptr) ?? error.DllNotFound; return windows.LoadLibraryA(padded_buff.ptr) orelse error.DllNotFound;
} }
pub fn windowsUnloadDll(hModule: windows.HMODULE) void { pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
@ -170,3 +170,42 @@ test "InvalidDll" {
return; return;
}; };
} }
pub fn windowsFindFirstFile(
allocator: *mem.Allocator,
dir_path: []const u8,
find_file_data: *windows.WIN32_FIND_DATAA,
) !windows.HANDLE {
const wild_and_null = []u8{ '\\', '*', 0 };
const path_with_wild_and_null = try allocator.alloc(u8, dir_path.len + wild_and_null.len);
defer allocator.free(path_with_wild_and_null);
mem.copy(u8, path_with_wild_and_null, dir_path);
mem.copy(u8, path_with_wild_and_null[dir_path.len..], wild_and_null);
const handle = windows.FindFirstFileA(path_with_wild_and_null.ptr, find_file_data);
if (handle == windows.INVALID_HANDLE_VALUE) {
const err = windows.GetLastError();
switch (err) {
windows.ERROR.FILE_NOT_FOUND,
windows.ERROR.PATH_NOT_FOUND,
=> return error.PathNotFound,
else => return os.unexpectedErrorWindows(err),
}
}
return handle;
}
/// Returns `true` if there was another file, `false` otherwise.
pub fn windowsFindNextFile(handle: windows.HANDLE, find_file_data: *windows.WIN32_FIND_DATAA) !bool {
if (windows.FindNextFileA(handle, find_file_data) == 0) {
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.NO_MORE_FILES => false,
else => os.unexpectedErrorWindows(err),
};
}
return true;
}

View File

@ -364,7 +364,7 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {
assert(x == 0); assert(x == 0);
} }
assert(??list.pop() == 100); assert(list.pop().? == 100);
assert(list.len == 99); assert(list.len == 99);
try list.pushMany([]i32{ try list.pushMany([]i32{
@ -373,9 +373,9 @@ fn testSegmentedList(comptime prealloc: usize, allocator: *Allocator) !void {
3, 3,
}); });
assert(list.len == 102); assert(list.len == 102);
assert(??list.pop() == 3); assert(list.pop().? == 3);
assert(??list.pop() == 2); assert(list.pop().? == 2);
assert(??list.pop() == 1); assert(list.pop().? == 1);
assert(list.len == 99); assert(list.len == 99);
try list.pushMany([]const i32{}); try list.pushMany([]const i32{});

View File

@ -51,13 +51,13 @@ extern fn WinMainCRTStartup() noreturn {
// TODO https://github.com/ziglang/zig/issues/265 // TODO https://github.com/ziglang/zig/issues/265
fn posixCallMainAndExit() noreturn { fn posixCallMainAndExit() noreturn {
const argc = argc_ptr.*; const argc = argc_ptr[0];
const argv = @ptrCast([*][*]u8, argc_ptr + 1); const argv = @ptrCast([*][*]u8, argc_ptr + 1);
const envp_nullable = @ptrCast([*]?[*]u8, argv + argc + 1); const envp_optional = @ptrCast([*]?[*]u8, argv + argc + 1);
var envp_count: usize = 0; var envp_count: usize = 0;
while (envp_nullable[envp_count]) |_| : (envp_count += 1) {} while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*]u8, envp_nullable)[0..envp_count]; const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
if (builtin.os == builtin.Os.linux) { if (builtin.os == builtin.Os.linux) {
const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1); const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
var i: usize = 0; var i: usize = 0;

View File

@ -27,15 +27,15 @@ pub fn main() !void {
// skip my own exe name // skip my own exe name
_ = arg_it.skip(); _ = arg_it.skip();
const zig_exe = try unwrapArg(arg_it.next(allocator) ?? { const zig_exe = try unwrapArg(arg_it.next(allocator) orelse {
warn("Expected first argument to be path to zig compiler\n"); warn("Expected first argument to be path to zig compiler\n");
return error.InvalidArgs; return error.InvalidArgs;
}); });
const build_root = try unwrapArg(arg_it.next(allocator) ?? { const build_root = try unwrapArg(arg_it.next(allocator) orelse {
warn("Expected second argument to be build root directory path\n"); warn("Expected second argument to be build root directory path\n");
return error.InvalidArgs; return error.InvalidArgs;
}); });
const cache_root = try unwrapArg(arg_it.next(allocator) ?? { const cache_root = try unwrapArg(arg_it.next(allocator) orelse {
warn("Expected third argument to be cache root directory path\n"); warn("Expected third argument to be cache root directory path\n");
return error.InvalidArgs; return error.InvalidArgs;
}); });
@ -84,12 +84,12 @@ pub fn main() !void {
} else if (mem.eql(u8, arg, "--help")) { } else if (mem.eql(u8, arg, "--help")) {
return usage(&builder, false, try stdout_stream); return usage(&builder, false, try stdout_stream);
} else if (mem.eql(u8, arg, "--prefix")) { } else if (mem.eql(u8, arg, "--prefix")) {
prefix = try unwrapArg(arg_it.next(allocator) ?? { prefix = try unwrapArg(arg_it.next(allocator) orelse {
warn("Expected argument after --prefix\n\n"); warn("Expected argument after --prefix\n\n");
return usageAndErr(&builder, false, try stderr_stream); return usageAndErr(&builder, false, try stderr_stream);
}); });
} else if (mem.eql(u8, arg, "--search-prefix")) { } else if (mem.eql(u8, arg, "--search-prefix")) {
const search_prefix = try unwrapArg(arg_it.next(allocator) ?? { const search_prefix = try unwrapArg(arg_it.next(allocator) orelse {
warn("Expected argument after --search-prefix\n\n"); warn("Expected argument after --search-prefix\n\n");
return usageAndErr(&builder, false, try stderr_stream); return usageAndErr(&builder, false, try stderr_stream);
}); });

View File

@ -19,7 +19,7 @@ export fn memset(dest: ?[*]u8, c: u8, n: usize) ?[*]u8 {
var index: usize = 0; var index: usize = 0;
while (index != n) : (index += 1) while (index != n) : (index += 1)
(??dest)[index] = c; dest.?[index] = c;
return dest; return dest;
} }
@ -29,7 +29,7 @@ export fn memcpy(noalias dest: ?[*]u8, noalias src: ?[*]const u8, n: usize) ?[*]
var index: usize = 0; var index: usize = 0;
while (index != n) : (index += 1) while (index != n) : (index += 1)
(??dest)[index] = (??src)[index]; dest.?[index] = src.?[index];
return dest; return dest;
} }
@ -40,13 +40,13 @@ export fn memmove(dest: ?[*]u8, src: ?[*]const u8, n: usize) ?[*]u8 {
if (@ptrToInt(dest) < @ptrToInt(src)) { if (@ptrToInt(dest) < @ptrToInt(src)) {
var index: usize = 0; var index: usize = 0;
while (index != n) : (index += 1) { while (index != n) : (index += 1) {
(??dest)[index] = (??src)[index]; dest.?[index] = src.?[index];
} }
} else { } else {
var index = n; var index = n;
while (index != 0) { while (index != 0) {
index -= 1; index -= 1;
(??dest)[index] = (??src)[index]; dest.?[index] = src.?[index];
} }
} }

View File

@ -0,0 +1,26 @@
const udivmod = @import("udivmod.zig").udivmod;
const builtin = @import("builtin");
const compiler_rt = @import("index.zig");
pub extern fn __divti3(a: i128, b: i128) i128 {
@setRuntimeSafety(builtin.is_test);
const s_a = a >> (i128.bit_count - 1);
const s_b = b >> (i128.bit_count - 1);
const an = (a ^ s_a) -% s_a;
const bn = (b ^ s_b) -% s_b;
const r = udivmod(u128, @bitCast(u128, an), @bitCast(u128, bn), null);
const s = s_a ^ s_b;
return (i128(r) ^ s) -% s;
}
pub extern fn __divti3_windows_x86_64(a: *const i128, b: *const i128) void {
@setRuntimeSafety(builtin.is_test);
compiler_rt.setXmm0(i128, __divti3(a.*, b.*));
}
test "import divti3" {
_ = @import("divti3_test.zig");
}

View File

@ -0,0 +1,21 @@
const __divti3 = @import("divti3.zig").__divti3;
const assert = @import("std").debug.assert;
fn test__divti3(a: i128, b: i128, expected: i128) void {
const x = __divti3(a, b);
assert(x == expected);
}
test "divti3" {
test__divti3(0, 1, 0);
test__divti3(0, -1, 0);
test__divti3(2, 1, 2);
test__divti3(2, -1, -2);
test__divti3(-2, 1, -2);
test__divti3(-2, -1, 2);
test__divti3(@bitCast(i128, u128(0x8 << 124)), 1, @bitCast(i128, u128(0x8 << 124)));
test__divti3(@bitCast(i128, u128(0x8 << 124)), -1, @bitCast(i128, u128(0x8 << 124)));
test__divti3(@bitCast(i128, u128(0x8 << 124)), -2, @bitCast(i128, u128(0x4 << 124)));
test__divti3(@bitCast(i128, u128(0x8 << 124)), 2, @bitCast(i128, u128(0xc << 124)));
}

View File

@ -58,6 +58,8 @@ comptime {
@export("__chkstk", __chkstk, strong_linkage); @export("__chkstk", __chkstk, strong_linkage);
@export("___chkstk_ms", ___chkstk_ms, linkage); @export("___chkstk_ms", ___chkstk_ms, linkage);
} }
@export("__divti3", @import("divti3.zig").__divti3_windows_x86_64, linkage);
@export("__muloti4", @import("muloti4.zig").__muloti4_windows_x86_64, linkage);
@export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage); @export("__udivti3", @import("udivti3.zig").__udivti3_windows_x86_64, linkage);
@export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage); @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4_windows_x86_64, linkage);
@export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage); @export("__umodti3", @import("umodti3.zig").__umodti3_windows_x86_64, linkage);
@ -65,6 +67,8 @@ comptime {
else => {}, else => {},
} }
} else { } else {
@export("__divti3", @import("divti3.zig").__divti3, linkage);
@export("__muloti4", @import("muloti4.zig").__muloti4, linkage);
@export("__udivti3", @import("udivti3.zig").__udivti3, linkage); @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
@export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage); @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
@export("__umodti3", @import("umodti3.zig").__umodti3, linkage); @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);

View File

@ -0,0 +1,55 @@
const udivmod = @import("udivmod.zig").udivmod;
const builtin = @import("builtin");
const compiler_rt = @import("index.zig");
pub extern fn __muloti4(a: i128, b: i128, overflow: *c_int) i128 {
@setRuntimeSafety(builtin.is_test);
const min = @bitCast(i128, u128(1 << (i128.bit_count - 1)));
const max = ~min;
overflow.* = 0;
const r = a *% b;
if (a == min) {
if (b != 0 and b != 1) {
overflow.* = 1;
}
return r;
}
if (b == min) {
if (a != 0 and a != 1) {
overflow.* = 1;
}
return r;
}
const sa = a >> (i128.bit_count - 1);
const abs_a = (a ^ sa) -% sa;
const sb = b >> (i128.bit_count - 1);
const abs_b = (b ^ sb) -% sb;
if (abs_a < 2 or abs_b < 2) {
return r;
}
if (sa == sb) {
if (abs_a > @divFloor(max, abs_b)) {
overflow.* = 1;
}
} else {
if (abs_a > @divFloor(min, -abs_b)) {
overflow.* = 1;
}
}
return r;
}
pub extern fn __muloti4_windows_x86_64(a: *const i128, b: *const i128, overflow: *c_int) void {
@setRuntimeSafety(builtin.is_test);
compiler_rt.setXmm0(i128, __muloti4(a.*, b.*, overflow));
}
test "import muloti4" {
_ = @import("muloti4_test.zig");
}

View File

@ -0,0 +1,76 @@
const __muloti4 = @import("muloti4.zig").__muloti4;
const assert = @import("std").debug.assert;
fn test__muloti4(a: i128, b: i128, expected: i128, expected_overflow: c_int) void {
var overflow: c_int = undefined;
const x = __muloti4(a, b, &overflow);
assert(overflow == expected_overflow and (expected_overflow != 0 or x == expected));
}
test "muloti4" {
test__muloti4(0, 0, 0, 0);
test__muloti4(0, 1, 0, 0);
test__muloti4(1, 0, 0, 0);
test__muloti4(0, 10, 0, 0);
test__muloti4(10, 0, 0, 0);
test__muloti4(0, 81985529216486895, 0, 0);
test__muloti4(81985529216486895, 0, 0, 0);
test__muloti4(0, -1, 0, 0);
test__muloti4(-1, 0, 0, 0);
test__muloti4(0, -10, 0, 0);
test__muloti4(-10, 0, 0, 0);
test__muloti4(0, -81985529216486895, 0, 0);
test__muloti4(-81985529216486895, 0, 0, 0);
test__muloti4(3037000499, 3037000499, 9223372030926249001, 0);
test__muloti4(-3037000499, 3037000499, -9223372030926249001, 0);
test__muloti4(3037000499, -3037000499, -9223372030926249001, 0);
test__muloti4(-3037000499, -3037000499, 9223372030926249001, 0);
test__muloti4(4398046511103, 2097152, 9223372036852678656, 0);
test__muloti4(-4398046511103, 2097152, -9223372036852678656, 0);
test__muloti4(4398046511103, -2097152, -9223372036852678656, 0);
test__muloti4(-4398046511103, -2097152, 9223372036852678656, 0);
test__muloti4(2097152, 4398046511103, 9223372036852678656, 0);
test__muloti4(-2097152, 4398046511103, -9223372036852678656, 0);
test__muloti4(2097152, -4398046511103, -9223372036852678656, 0);
test__muloti4(-2097152, -4398046511103, 9223372036852678656, 0);
test__muloti4(@bitCast(i128, u128(0x00000000000000B504F333F9DE5BE000)), @bitCast(i128, u128(0x000000000000000000B504F333F9DE5B)), @bitCast(i128, u128(0x7FFFFFFFFFFFF328DF915DA296E8A000)), 0);
test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
test__muloti4(-2, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), -1, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
test__muloti4(-1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0, 0);
test__muloti4(0, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0, 0);
test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
test__muloti4(1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
test__muloti4(@bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
test__muloti4(2, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), -2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(-2, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), -1, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(-1, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 0, 0, 0);
test__muloti4(0, @bitCast(i128, u128(0x80000000000000000000000000000000)), 0, 0);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 1, @bitCast(i128, u128(0x80000000000000000000000000000000)), 0);
test__muloti4(1, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 0);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000000)), 2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(2, @bitCast(i128, u128(0x80000000000000000000000000000000)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), -2, @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
test__muloti4(-2, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 1);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), -1, @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
test__muloti4(-1, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF)), 0);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 0, 0, 0);
test__muloti4(0, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0, 0);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 1, @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
test__muloti4(1, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000001)), 0);
test__muloti4(@bitCast(i128, u128(0x80000000000000000000000000000001)), 2, @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
test__muloti4(2, @bitCast(i128, u128(0x80000000000000000000000000000001)), @bitCast(i128, u128(0x80000000000000000000000000000000)), 1);
}

View File

@ -220,7 +220,7 @@ const Utf8Iterator = struct {
} }
pub fn nextCodepoint(it: *Utf8Iterator) ?u32 { pub fn nextCodepoint(it: *Utf8Iterator) ?u32 {
const slice = it.nextCodepointSlice() ?? return null; const slice = it.nextCodepointSlice() orelse return null;
switch (slice.len) { switch (slice.len) {
1 => return u32(slice[0]), 1 => return u32(slice[0]),
@ -286,15 +286,15 @@ fn testUtf8IteratorOnAscii() void {
const s = Utf8View.initComptime("abc"); const s = Utf8View.initComptime("abc");
var it1 = s.iterator(); var it1 = s.iterator();
debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "a", it1.nextCodepointSlice().?));
debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "b", it1.nextCodepointSlice().?));
debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "c", it1.nextCodepointSlice().?));
debug.assert(it1.nextCodepointSlice() == null); debug.assert(it1.nextCodepointSlice() == null);
var it2 = s.iterator(); var it2 = s.iterator();
debug.assert(??it2.nextCodepoint() == 'a'); debug.assert(it2.nextCodepoint().? == 'a');
debug.assert(??it2.nextCodepoint() == 'b'); debug.assert(it2.nextCodepoint().? == 'b');
debug.assert(??it2.nextCodepoint() == 'c'); debug.assert(it2.nextCodepoint().? == 'c');
debug.assert(it2.nextCodepoint() == null); debug.assert(it2.nextCodepoint() == null);
} }
@ -321,15 +321,15 @@ fn testUtf8ViewOk() void {
const s = Utf8View.initComptime("東京市"); const s = Utf8View.initComptime("東京市");
var it1 = s.iterator(); var it1 = s.iterator();
debug.assert(std.mem.eql(u8, "", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "", it1.nextCodepointSlice().?));
debug.assert(std.mem.eql(u8, "", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "", it1.nextCodepointSlice().?));
debug.assert(std.mem.eql(u8, "", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "", it1.nextCodepointSlice().?));
debug.assert(it1.nextCodepointSlice() == null); debug.assert(it1.nextCodepointSlice() == null);
var it2 = s.iterator(); var it2 = s.iterator();
debug.assert(??it2.nextCodepoint() == 0x6771); debug.assert(it2.nextCodepoint().? == 0x6771);
debug.assert(??it2.nextCodepoint() == 0x4eac); debug.assert(it2.nextCodepoint().? == 0x4eac);
debug.assert(??it2.nextCodepoint() == 0x5e02); debug.assert(it2.nextCodepoint().? == 0x5e02);
debug.assert(it2.nextCodepoint() == null); debug.assert(it2.nextCodepoint() == null);
} }

View File

@ -734,7 +734,7 @@ pub const Node = struct {
var i = index; var i = index;
if (self.doc_comments) |comments| { if (self.doc_comments) |comments| {
if (i < 1) return *comments.base; if (i < 1) return &comments.base;
i -= 1; i -= 1;
} }
@ -1243,7 +1243,7 @@ pub const Node = struct {
i -= 1; i -= 1;
if (self.@"else") |@"else"| { if (self.@"else") |@"else"| {
if (i < 1) return *@"else".base; if (i < 1) return &@"else".base;
i -= 1; i -= 1;
} }
@ -1296,7 +1296,7 @@ pub const Node = struct {
i -= 1; i -= 1;
if (self.@"else") |@"else"| { if (self.@"else") |@"else"| {
if (i < 1) return *@"else".base; if (i < 1) return &@"else".base;
i -= 1; i -= 1;
} }
@ -1347,7 +1347,7 @@ pub const Node = struct {
i -= 1; i -= 1;
if (self.@"else") |@"else"| { if (self.@"else") |@"else"| {
if (i < 1) return *@"else".base; if (i < 1) return &@"else".base;
i -= 1; i -= 1;
} }
@ -1417,7 +1417,7 @@ pub const Node = struct {
Range, Range,
Sub, Sub,
SubWrap, SubWrap,
UnwrapMaybe, UnwrapOptional,
}; };
pub fn iterate(self: *InfixOp, index: usize) ?*Node { pub fn iterate(self: *InfixOp, index: usize) ?*Node {
@ -1475,7 +1475,7 @@ pub const Node = struct {
Op.Range, Op.Range,
Op.Sub, Op.Sub,
Op.SubWrap, Op.SubWrap,
Op.UnwrapMaybe, Op.UnwrapOptional,
=> {}, => {},
} }
@ -1507,14 +1507,13 @@ pub const Node = struct {
BitNot, BitNot,
BoolNot, BoolNot,
Cancel, Cancel,
MaybeType, OptionalType,
Negation, Negation,
NegationWrap, NegationWrap,
Resume, Resume,
PtrType: PtrInfo, PtrType: PtrInfo,
SliceType: PtrInfo, SliceType: PtrInfo,
Try, Try,
UnwrapMaybe,
}; };
pub const PtrInfo = struct { pub const PtrInfo = struct {
@ -1537,33 +1536,36 @@ pub const Node = struct {
var i = index; var i = index;
switch (self.op) { switch (self.op) {
// TODO https://github.com/ziglang/zig/issues/1107
Op.SliceType => |addr_of_info| { Op.SliceType => |addr_of_info| {
if (addr_of_info.align_info) |align_info| { if (addr_of_info.align_info) |align_info| {
if (i < 1) return align_info.node; if (i < 1) return align_info.node;
i -= 1; i -= 1;
} }
}, },
Op.AddrOf => |addr_of_info| {
Op.PtrType => |addr_of_info| {
if (addr_of_info.align_info) |align_info| { if (addr_of_info.align_info) |align_info| {
if (i < 1) return align_info.node; if (i < 1) return align_info.node;
i -= 1; i -= 1;
} }
}, },
Op.ArrayType => |size_expr| { Op.ArrayType => |size_expr| {
if (i < 1) return size_expr; if (i < 1) return size_expr;
i -= 1; i -= 1;
}, },
Op.AddressOf,
Op.Await, Op.Await,
Op.BitNot, Op.BitNot,
Op.BoolNot, Op.BoolNot,
Op.Cancel, Op.Cancel,
Op.MaybeType, Op.OptionalType,
Op.Negation, Op.Negation,
Op.NegationWrap, Op.NegationWrap,
Op.Try, Op.Try,
Op.Resume, Op.Resume,
Op.UnwrapMaybe,
Op.PointerType,
=> {}, => {},
} }
@ -1619,6 +1621,7 @@ pub const Node = struct {
ArrayInitializer: InitList, ArrayInitializer: InitList,
StructInitializer: InitList, StructInitializer: InitList,
Deref, Deref,
UnwrapOptional,
pub const InitList = SegmentedList(*Node, 2); pub const InitList = SegmentedList(*Node, 2);
@ -1667,7 +1670,9 @@ pub const Node = struct {
if (i < fields.len) return fields.at(i).*; if (i < fields.len) return fields.at(i).*;
i -= fields.len; i -= fields.len;
}, },
Op.Deref => {}, Op.UnwrapOptional,
Op.Deref,
=> {},
} }
return null; return null;
@ -2022,7 +2027,7 @@ pub const Node = struct {
switch (self.kind) { switch (self.kind) {
Kind.Variable => |variable_name| { Kind.Variable => |variable_name| {
if (i < 1) return *variable_name.base; if (i < 1) return &variable_name.base;
i -= 1; i -= 1;
}, },
Kind.Return => |return_type| { Kind.Return => |return_type| {
@ -2092,10 +2097,10 @@ pub const Node = struct {
pub fn iterate(self: *Asm, index: usize) ?*Node { pub fn iterate(self: *Asm, index: usize) ?*Node {
var i = index; var i = index;
if (i < self.outputs.len) return *(self.outputs.at(index).*).base; if (i < self.outputs.len) return &self.outputs.at(index).*.base;
i -= self.outputs.len; i -= self.outputs.len;
if (i < self.inputs.len) return *(self.inputs.at(index).*).base; if (i < self.inputs.len) return &self.inputs.at(index).*.base;
i -= self.inputs.len; i -= self.inputs.len;
return null; return null;
@ -2205,3 +2210,14 @@ pub const Node = struct {
} }
}; };
}; };
test "iterate" {
var root = Node.Root{
.base = Node{ .id = Node.Id.Root },
.doc_comments = null,
.decls = Node.Root.DeclList.init(std.debug.global_allocator),
.eof_token = 0,
};
var base = &root.base;
assert(base.iterate(0) == null);
}

View File

@ -43,7 +43,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
// skip over line comments at the top of the file // skip over line comments at the top of the file
while (true) { while (true) {
const next_tok = tok_it.peek() ?? break; const next_tok = tok_it.peek() orelse break;
if (next_tok.id != Token.Id.LineComment) break; if (next_tok.id != Token.Id.LineComment) break;
_ = tok_it.next(); _ = tok_it.next();
} }
@ -197,7 +197,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
const lib_name_token = nextToken(&tok_it, &tree); const lib_name_token = nextToken(&tok_it, &tree);
const lib_name_token_index = lib_name_token.index; const lib_name_token_index = lib_name_token.index;
const lib_name_token_ptr = lib_name_token.ptr; const lib_name_token_ptr = lib_name_token.ptr;
break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) ?? { break :blk (try parseStringLiteral(arena, &tok_it, lib_name_token_ptr, lib_name_token_index, &tree)) orelse {
prevToken(&tok_it, &tree); prevToken(&tok_it, &tree);
break :blk null; break :blk null;
}; };
@ -711,7 +711,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
else => { else => {
// TODO: this is a special case. Remove this when #760 is fixed // TODO: this is a special case. Remove this when #760 is fixed
if (token_ptr.id == Token.Id.Keyword_error) { if (token_ptr.id == Token.Id.Keyword_error) {
if ((??tok_it.peek()).id == Token.Id.LBrace) { if (tok_it.peek().?.id == Token.Id.LBrace) {
const error_type_node = try arena.construct(ast.Node.ErrorType{ const error_type_node = try arena.construct(ast.Node.ErrorType{
.base = ast.Node{ .id = ast.Node.Id.ErrorType }, .base = ast.Node{ .id = ast.Node.Id.ErrorType },
.token = token_index, .token = token_index,
@ -1434,14 +1434,14 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
try stack.append(State{ try stack.append(State{
.ExpectTokenSave = ExpectTokenSave{ .ExpectTokenSave = ExpectTokenSave{
.id = Token.Id.AngleBracketRight, .id = Token.Id.AngleBracketRight,
.ptr = &??async_node.rangle_bracket, .ptr = &async_node.rangle_bracket.?,
}, },
}); });
try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } }); try stack.append(State{ .TypeExprBegin = OptionalCtx{ .RequiredNull = &async_node.allocator_type } });
continue; continue;
}, },
State.AsyncEnd => |ctx| { State.AsyncEnd => |ctx| {
const node = ctx.ctx.get() ?? continue; const node = ctx.ctx.get() orelse continue;
switch (node.id) { switch (node.id) {
ast.Node.Id.FnProto => { ast.Node.Id.FnProto => {
@ -1567,7 +1567,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
.bit_range = null, .bit_range = null,
}; };
// TODO https://github.com/ziglang/zig/issues/1022 // TODO https://github.com/ziglang/zig/issues/1022
const align_info = &??addr_of_info.align_info; const align_info = &addr_of_info.align_info.?;
try stack.append(State{ .AlignBitRange = align_info }); try stack.append(State{ .AlignBitRange = align_info });
try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &align_info.node } });
@ -1604,7 +1604,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
switch (token.ptr.id) { switch (token.ptr.id) {
Token.Id.Colon => { Token.Id.Colon => {
align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined); align_info.bit_range = ast.Node.PrefixOp.PtrInfo.Align.BitRange(undefined);
const bit_range = &??align_info.bit_range; const bit_range = &align_info.bit_range.?;
try stack.append(State{ .ExpectToken = Token.Id.RParen }); try stack.append(State{ .ExpectToken = Token.Id.RParen });
try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } }); try stack.append(State{ .Expression = OptionalCtx{ .Required = &bit_range.end } });
@ -1814,7 +1814,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
continue; continue;
}, },
State.RangeExpressionEnd => |opt_ctx| { State.RangeExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| { if (eatToken(&tok_it, &tree, Token.Id.Ellipsis3)) |ellipsis3| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -1836,7 +1836,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.AssignmentExpressionEnd => |opt_ctx| { State.AssignmentExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -1866,7 +1866,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.UnwrapExpressionEnd => |opt_ctx| { State.UnwrapExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -1901,7 +1901,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.BoolOrExpressionEnd => |opt_ctx| { State.BoolOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_or)) |or_token| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -1925,7 +1925,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.BoolAndExpressionEnd => |opt_ctx| { State.BoolAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| { if (eatToken(&tok_it, &tree, Token.Id.Keyword_and)) |and_token| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -1949,7 +1949,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.ComparisonExpressionEnd => |opt_ctx| { State.ComparisonExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -1979,7 +1979,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.BinaryOrExpressionEnd => |opt_ctx| { State.BinaryOrExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| { if (eatToken(&tok_it, &tree, Token.Id.Pipe)) |pipe| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -2003,7 +2003,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.BinaryXorExpressionEnd => |opt_ctx| { State.BinaryXorExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| { if (eatToken(&tok_it, &tree, Token.Id.Caret)) |caret| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -2027,7 +2027,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.BinaryAndExpressionEnd => |opt_ctx| { State.BinaryAndExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| { if (eatToken(&tok_it, &tree, Token.Id.Ampersand)) |ampersand| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -2051,7 +2051,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.BitShiftExpressionEnd => |opt_ctx| { State.BitShiftExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -2081,7 +2081,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.AdditionExpressionEnd => |opt_ctx| { State.AdditionExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -2111,7 +2111,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.MultiplyExpressionEnd => |opt_ctx| { State.MultiplyExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -2142,9 +2142,9 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.CurlySuffixExpressionEnd => |opt_ctx| { State.CurlySuffixExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if ((??tok_it.peek()).id == Token.Id.Period) { if (tok_it.peek().?.id == Token.Id.Period) {
const node = try arena.construct(ast.Node.SuffixOp{ const node = try arena.construct(ast.Node.SuffixOp{
.base = ast.Node{ .id = ast.Node.Id.SuffixOp }, .base = ast.Node{ .id = ast.Node.Id.SuffixOp },
.lhs = lhs, .lhs = lhs,
@ -2190,7 +2190,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.TypeExprEnd => |opt_ctx| { State.TypeExprEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| { if (eatToken(&tok_it, &tree, Token.Id.Bang)) |bang| {
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
@ -2270,7 +2270,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
}, },
State.SuffixOpExpressionEnd => |opt_ctx| { State.SuffixOpExpressionEnd => |opt_ctx| {
const lhs = opt_ctx.get() ?? continue; const lhs = opt_ctx.get() orelse continue;
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
@ -2326,6 +2326,17 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable; stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
continue; continue;
} }
if (eatToken(&tok_it, &tree, Token.Id.QuestionMark)) |question_token| {
const node = try arena.construct(ast.Node.SuffixOp{
.base = ast.Node{ .id = ast.Node.Id.SuffixOp },
.lhs = lhs,
.op = ast.Node.SuffixOp.Op.UnwrapOptional,
.rtoken = question_token,
});
opt_ctx.store(&node.base);
stack.append(State{ .SuffixOpExpressionEnd = opt_ctx.toRequired() }) catch unreachable;
continue;
}
const node = try arena.construct(ast.Node.InfixOp{ const node = try arena.construct(ast.Node.InfixOp{
.base = ast.Node{ .id = ast.Node.Id.InfixOp }, .base = ast.Node{ .id = ast.Node.Id.InfixOp },
.lhs = lhs, .lhs = lhs,
@ -2403,12 +2414,12 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
.arrow_token = next_token_index, .arrow_token = next_token_index,
.return_type = undefined, .return_type = undefined,
}; };
const return_type_ptr = &((??node.result).return_type); const return_type_ptr = &node.result.?.return_type;
try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } }); try stack.append(State{ .Expression = OptionalCtx{ .Required = return_type_ptr } });
continue; continue;
}, },
Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => { Token.Id.StringLiteral, Token.Id.MultilineStringLiteralLine => {
opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) ?? unreachable); opt_ctx.store((try parseStringLiteral(arena, &tok_it, token.ptr, token.index, &tree)) orelse unreachable);
continue; continue;
}, },
Token.Id.LParen => { Token.Id.LParen => {
@ -2638,7 +2649,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
const token = nextToken(&tok_it, &tree); const token = nextToken(&tok_it, &tree);
const token_index = token.index; const token_index = token.index;
const token_ptr = token.ptr; const token_ptr = token.ptr;
opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) ?? { opt_ctx.store((try parseStringLiteral(arena, &tok_it, token_ptr, token_index, &tree)) orelse {
prevToken(&tok_it, &tree); prevToken(&tok_it, &tree);
if (opt_ctx != OptionalCtx.Optional) { if (opt_ctx != OptionalCtx.Optional) {
((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } }; ((try tree.errors.addOne())).* = Error{ .ExpectedPrimaryExpr = Error.ExpectedPrimaryExpr{ .token = token_index } };
@ -2875,7 +2886,7 @@ const OptionalCtx = union(enum) {
pub fn get(self: *const OptionalCtx) ?*ast.Node { pub fn get(self: *const OptionalCtx) ?*ast.Node {
switch (self.*) { switch (self.*) {
OptionalCtx.Optional => |ptr| return ptr.*, OptionalCtx.Optional => |ptr| return ptr.*,
OptionalCtx.RequiredNull => |ptr| return ??ptr.*, OptionalCtx.RequiredNull => |ptr| return ptr.*.?,
OptionalCtx.Required => |ptr| return ptr.*, OptionalCtx.Required => |ptr| return ptr.*,
} }
} }
@ -3237,7 +3248,7 @@ fn tokenIdToAssignment(id: *const Token.Id) ?ast.Node.InfixOp.Op {
fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op { fn tokenIdToUnwrapExpr(id: @TagType(Token.Id)) ?ast.Node.InfixOp.Op {
return switch (id) { return switch (id) {
Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null }, Token.Id.Keyword_catch => ast.Node.InfixOp.Op{ .Catch = null },
Token.Id.QuestionMarkQuestionMark => ast.Node.InfixOp.Op{ .UnwrapMaybe = void{} }, Token.Id.Keyword_orelse => ast.Node.InfixOp.Op{ .UnwrapOptional = void{} },
else => null, else => null,
}; };
} }
@ -3299,8 +3310,7 @@ fn tokenIdToPrefixOp(id: @TagType(Token.Id)) ?ast.Node.PrefixOp.Op {
.volatile_token = null, .volatile_token = null,
}, },
}, },
Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .MaybeType = void{} }, Token.Id.QuestionMark => ast.Node.PrefixOp.Op{ .OptionalType = void{} },
Token.Id.QuestionMarkQuestionMark => ast.Node.PrefixOp.Op{ .UnwrapMaybe = void{} },
Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} }, Token.Id.Keyword_await => ast.Node.PrefixOp.Op{ .Await = void{} },
Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} }, Token.Id.Keyword_try => ast.Node.PrefixOp.Op{ .Try = void{} },
else => null, else => null,
@ -3322,7 +3332,7 @@ fn createToCtxLiteral(arena: *mem.Allocator, opt_ctx: *const OptionalCtx, compti
} }
fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex { fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(Token.Id)) ?TokenIndex {
const token = ??tok_it.peek(); const token = tok_it.peek().?;
if (token.id == id) { if (token.id == id) {
return nextToken(tok_it, tree).index; return nextToken(tok_it, tree).index;
@ -3334,12 +3344,12 @@ fn eatToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree, id: @TagType(
fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken { fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedToken {
const result = AnnotatedToken{ const result = AnnotatedToken{
.index = tok_it.index, .index = tok_it.index,
.ptr = ??tok_it.next(), .ptr = tok_it.next().?,
}; };
assert(result.ptr.id != Token.Id.LineComment); assert(result.ptr.id != Token.Id.LineComment);
while (true) { while (true) {
const next_tok = tok_it.peek() ?? return result; const next_tok = tok_it.peek() orelse return result;
if (next_tok.id != Token.Id.LineComment) return result; if (next_tok.id != Token.Id.LineComment) return result;
_ = tok_it.next(); _ = tok_it.next();
} }
@ -3347,7 +3357,7 @@ fn nextToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) AnnotatedTok
fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void { fn prevToken(tok_it: *ast.Tree.TokenList.Iterator, tree: *ast.Tree) void {
while (true) { while (true) {
const prev_tok = tok_it.prev() ?? return; const prev_tok = tok_it.prev() orelse return;
if (prev_tok.id == Token.Id.LineComment) continue; if (prev_tok.id == Token.Id.LineComment) continue;
return; return;
} }

View File

@ -650,9 +650,10 @@ test "zig fmt: statements with empty line between" {
); );
} }
test "zig fmt: ptr deref operator" { test "zig fmt: ptr deref operator and unwrap optional operator" {
try testCanonical( try testCanonical(
\\const a = b.*; \\const a = b.*;
\\const a = b.?;
\\ \\
); );
} }
@ -1150,7 +1151,7 @@ test "zig fmt: infix operators" {
\\ _ = i!i; \\ _ = i!i;
\\ _ = i ** i; \\ _ = i ** i;
\\ _ = i ++ i; \\ _ = i ++ i;
\\ _ = i ?? i; \\ _ = i orelse i;
\\ _ = i % i; \\ _ = i % i;
\\ _ = i / i; \\ _ = i / i;
\\ _ = i *% i; \\ _ = i *% i;
@ -1209,7 +1210,7 @@ test "zig fmt: precedence" {
test "zig fmt: prefix operators" { test "zig fmt: prefix operators" {
try testCanonical( try testCanonical(
\\test "prefix operators" { \\test "prefix operators" {
\\ try return --%~??!*&0; \\ try return --%~!*&0;
\\} \\}
\\ \\
); );

View File

@ -83,7 +83,7 @@ fn renderRoot(
var start_col: usize = 0; var start_col: usize = 0;
var it = tree.root_node.decls.iterator(0); var it = tree.root_node.decls.iterator(0);
while (true) { while (true) {
var decl = (it.next() ?? return).*; var decl = (it.next() orelse return).*;
// look for zig fmt: off comment // look for zig fmt: off comment
var start_token_index = decl.firstToken(); var start_token_index = decl.firstToken();
zig_fmt_loop: while (start_token_index != 0) { zig_fmt_loop: while (start_token_index != 0) {
@ -112,7 +112,7 @@ fn renderRoot(
const start = tree.tokens.at(start_token_index + 1).start; const start = tree.tokens.at(start_token_index + 1).start;
try stream.print("{}\n", tree.source[start..end_token.end]); try stream.print("{}\n", tree.source[start..end_token.end]);
while (tree.tokens.at(decl.firstToken()).start < end_token.end) { while (tree.tokens.at(decl.firstToken()).start < end_token.end) {
decl = (it.next() ?? return).*; decl = (it.next() orelse return).*;
} }
break :zig_fmt_loop; break :zig_fmt_loop;
} }
@ -222,7 +222,7 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i
} }
} }
const value_expr = ??tag.value_expr; const value_expr = tag.value_expr.?;
try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // = try renderToken(tree, stream, tree.prevToken(value_expr.firstToken()), indent, start_col, Space.Space); // =
try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value, try renderExpression(allocator, stream, tree, indent, start_col, value_expr, Space.Comma); // value,
}, },
@ -465,8 +465,7 @@ fn renderExpression(
ast.Node.PrefixOp.Op.BoolNot, ast.Node.PrefixOp.Op.BoolNot,
ast.Node.PrefixOp.Op.Negation, ast.Node.PrefixOp.Op.Negation,
ast.Node.PrefixOp.Op.NegationWrap, ast.Node.PrefixOp.Op.NegationWrap,
ast.Node.PrefixOp.Op.UnwrapMaybe, ast.Node.PrefixOp.Op.OptionalType,
ast.Node.PrefixOp.Op.MaybeType,
ast.Node.PrefixOp.Op.AddressOf, ast.Node.PrefixOp.Op.AddressOf,
=> { => {
try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None);
@ -513,7 +512,7 @@ fn renderExpression(
var it = call_info.params.iterator(0); var it = call_info.params.iterator(0);
while (true) { while (true) {
const param_node = ??it.next(); const param_node = it.next().?;
const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: { const param_node_new_indent = if (param_node.*.id == ast.Node.Id.MultilineStringLiteral) blk: {
break :blk indent; break :blk indent;
@ -559,10 +558,10 @@ fn renderExpression(
return renderToken(tree, stream, rbracket, indent, start_col, space); // ] return renderToken(tree, stream, rbracket, indent, start_col, space); // ]
}, },
ast.Node.SuffixOp.Op.Deref => { ast.Node.SuffixOp.Op.Deref, ast.Node.SuffixOp.Op.UnwrapOptional => {
try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None);
try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // . try renderToken(tree, stream, tree.prevToken(suffix_op.rtoken), indent, start_col, Space.None); // .
return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // * or ?
}, },
@TagType(ast.Node.SuffixOp.Op).Slice => |range| { @TagType(ast.Node.SuffixOp.Op).Slice => |range| {
@ -595,7 +594,7 @@ fn renderExpression(
} }
if (field_inits.len == 1) blk: { if (field_inits.len == 1) blk: {
const field_init = ??field_inits.at(0).*.cast(ast.Node.FieldInitializer); const field_init = field_inits.at(0).*.cast(ast.Node.FieldInitializer).?;
if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| { if (field_init.expr.cast(ast.Node.SuffixOp)) |nested_suffix_op| {
if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) { if (nested_suffix_op.op == ast.Node.SuffixOp.Op.StructInitializer) {
@ -688,7 +687,7 @@ fn renderExpression(
var count: usize = 1; var count: usize = 1;
var it = exprs.iterator(0); var it = exprs.iterator(0);
while (true) { while (true) {
const expr = (??it.next()).*; const expr = it.next().?.*;
if (it.peek()) |next_expr| { if (it.peek()) |next_expr| {
const expr_last_token = expr.*.lastToken() + 1; const expr_last_token = expr.*.lastToken() + 1;
const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken()); const loc = tree.tokenLocation(tree.tokens.at(expr_last_token).end, next_expr.*.firstToken());
@ -806,7 +805,7 @@ fn renderExpression(
}, },
} }
return renderExpression(allocator, stream, tree, indent, start_col, ??flow_expr.rhs, space); return renderExpression(allocator, stream, tree, indent, start_col, flow_expr.rhs.?, space);
}, },
ast.Node.Id.Payload => { ast.Node.Id.Payload => {
@ -1245,7 +1244,7 @@ fn renderExpression(
} else { } else {
var it = switch_case.items.iterator(0); var it = switch_case.items.iterator(0);
while (true) { while (true) {
const node = ??it.next(); const node = it.next().?;
if (it.peek()) |next_node| { if (it.peek()) |next_node| {
try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None); try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);
@ -1550,7 +1549,7 @@ fn renderExpression(
var it = asm_node.outputs.iterator(0); var it = asm_node.outputs.iterator(0);
while (true) { while (true) {
const asm_output = ??it.next(); const asm_output = it.next().?;
const node = &(asm_output.*).base; const node = &(asm_output.*).base;
if (it.peek()) |next_asm_output| { if (it.peek()) |next_asm_output| {
@ -1588,7 +1587,7 @@ fn renderExpression(
var it = asm_node.inputs.iterator(0); var it = asm_node.inputs.iterator(0);
while (true) { while (true) {
const asm_input = ??it.next(); const asm_input = it.next().?;
const node = &(asm_input.*).base; const node = &(asm_input.*).base;
if (it.peek()) |next_asm_input| { if (it.peek()) |next_asm_input| {
@ -1620,7 +1619,7 @@ fn renderExpression(
var it = asm_node.clobbers.iterator(0); var it = asm_node.clobbers.iterator(0);
while (true) { while (true) {
const clobber_token = ??it.next(); const clobber_token = it.next().?;
if (it.peek() == null) { if (it.peek() == null) {
try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline); try renderToken(tree, stream, clobber_token.*, indent_once, start_col, Space.Newline);
@ -1994,7 +1993,7 @@ fn renderDocComments(
indent: usize, indent: usize,
start_col: *usize, start_col: *usize,
) (@typeOf(stream).Child.Error || Error)!void { ) (@typeOf(stream).Child.Error || Error)!void {
const comment = node.doc_comments ?? return; const comment = node.doc_comments orelse return;
var it = comment.lines.iterator(0); var it = comment.lines.iterator(0);
const first_token = node.firstToken(); const first_token = node.firstToken();
while (it.next()) |line_token_index| { while (it.next()) |line_token_index| {
@ -2022,7 +2021,7 @@ fn nodeIsBlock(base: *const ast.Node) bool {
} }
fn nodeCausesSliceOpSpace(base: *ast.Node) bool { fn nodeCausesSliceOpSpace(base: *ast.Node) bool {
const infix_op = base.cast(ast.Node.InfixOp) ?? return false; const infix_op = base.cast(ast.Node.InfixOp) orelse return false;
return switch (infix_op.op) { return switch (infix_op.op) {
ast.Node.InfixOp.Op.Period => false, ast.Node.InfixOp.Op.Period => false,
else => true, else => true,

View File

@ -39,6 +39,7 @@ pub const Token = struct {
Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias }, Keyword{ .bytes = "noalias", .id = Id.Keyword_noalias },
Keyword{ .bytes = "null", .id = Id.Keyword_null }, Keyword{ .bytes = "null", .id = Id.Keyword_null },
Keyword{ .bytes = "or", .id = Id.Keyword_or }, Keyword{ .bytes = "or", .id = Id.Keyword_or },
Keyword{ .bytes = "orelse", .id = Id.Keyword_orelse },
Keyword{ .bytes = "packed", .id = Id.Keyword_packed }, Keyword{ .bytes = "packed", .id = Id.Keyword_packed },
Keyword{ .bytes = "promise", .id = Id.Keyword_promise }, Keyword{ .bytes = "promise", .id = Id.Keyword_promise },
Keyword{ .bytes = "pub", .id = Id.Keyword_pub }, Keyword{ .bytes = "pub", .id = Id.Keyword_pub },
@ -129,7 +130,6 @@ pub const Token = struct {
Ampersand, Ampersand,
AmpersandEqual, AmpersandEqual,
QuestionMark, QuestionMark,
QuestionMarkQuestionMark,
AngleBracketLeft, AngleBracketLeft,
AngleBracketLeftEqual, AngleBracketLeftEqual,
AngleBracketAngleBracketLeft, AngleBracketAngleBracketLeft,
@ -171,6 +171,7 @@ pub const Token = struct {
Keyword_noalias, Keyword_noalias,
Keyword_null, Keyword_null,
Keyword_or, Keyword_or,
Keyword_orelse,
Keyword_packed, Keyword_packed,
Keyword_promise, Keyword_promise,
Keyword_pub, Keyword_pub,
@ -254,7 +255,6 @@ pub const Tokenizer = struct {
Ampersand, Ampersand,
Caret, Caret,
Percent, Percent,
QuestionMark,
Plus, Plus,
PlusPercent, PlusPercent,
AngleBracketLeft, AngleBracketLeft,
@ -345,6 +345,11 @@ pub const Tokenizer = struct {
self.index += 1; self.index += 1;
break; break;
}, },
'?' => {
result.id = Token.Id.QuestionMark;
self.index += 1;
break;
},
':' => { ':' => {
result.id = Token.Id.Colon; result.id = Token.Id.Colon;
self.index += 1; self.index += 1;
@ -359,9 +364,6 @@ pub const Tokenizer = struct {
'+' => { '+' => {
state = State.Plus; state = State.Plus;
}, },
'?' => {
state = State.QuestionMark;
},
'<' => { '<' => {
state = State.AngleBracketLeft; state = State.AngleBracketLeft;
}, },
@ -496,18 +498,6 @@ pub const Tokenizer = struct {
}, },
}, },
State.QuestionMark => switch (c) {
'?' => {
result.id = Token.Id.QuestionMarkQuestionMark;
self.index += 1;
break;
},
else => {
result.id = Token.Id.QuestionMark;
break;
},
},
State.Percent => switch (c) { State.Percent => switch (c) {
'=' => { '=' => {
result.id = Token.Id.PercentEqual; result.id = Token.Id.PercentEqual;
@ -1084,9 +1074,6 @@ pub const Tokenizer = struct {
State.Plus => { State.Plus => {
result.id = Token.Id.Plus; result.id = Token.Id.Plus;
}, },
State.QuestionMark => {
result.id = Token.Id.QuestionMark;
},
State.Percent => { State.Percent => {
result.id = Token.Id.Percent; result.id = Token.Id.Percent;
}, },

View File

@ -31,6 +31,7 @@ comptime {
_ = @import("cases/incomplete_struct_param_tld.zig"); _ = @import("cases/incomplete_struct_param_tld.zig");
_ = @import("cases/ir_block_deps.zig"); _ = @import("cases/ir_block_deps.zig");
_ = @import("cases/math.zig"); _ = @import("cases/math.zig");
_ = @import("cases/merge_error_sets.zig");
_ = @import("cases/misc.zig"); _ = @import("cases/misc.zig");
_ = @import("cases/namespace_depends_on_compile_var/index.zig"); _ = @import("cases/namespace_depends_on_compile_var/index.zig");
_ = @import("cases/new_stack_call.zig"); _ = @import("cases/new_stack_call.zig");

View File

@ -116,6 +116,15 @@ test "array len property" {
assert(@typeOf(x).len == 5); assert(@typeOf(x).len == 5);
} }
test "array len field" {
var arr = [4]u8{ 0, 0, 0, 0 };
var ptr = &arr;
assert(arr.len == 4);
comptime assert(arr.len == 4);
assert(ptr.len == 4);
comptime assert(ptr.len == 4);
}
test "single-item pointer to array indexing and slicing" { test "single-item pointer to array indexing and slicing" {
testSingleItemPtrArrayIndexSlice(); testSingleItemPtrArrayIndexSlice();
comptime testSingleItemPtrArrayIndexSlice(); comptime testSingleItemPtrArrayIndexSlice();
@ -143,4 +152,3 @@ fn testImplicitCastSingleItemPtr() void {
slice[0] += 1; slice[0] += 1;
assert(byte == 101); assert(byte == 101);
} }

View File

@ -9,7 +9,7 @@ const Value = struct {
align_expr: ?u32, align_expr: ?u32,
}; };
test "nullable if after an if in a switch prong of a switch with 2 prongs in an else" { test "optional if after an if in a switch prong of a switch with 2 prongs in an else" {
foo(false, true); foo(false, true);
} }

View File

@ -1,5 +1,6 @@
const assert = @import("std").debug.assert; const std = @import("std");
const mem = @import("std").mem; const assert = std.debug.assert;
const mem = std.mem;
test "int to ptr cast" { test "int to ptr cast" {
const x = usize(13); const x = usize(13);
@ -72,7 +73,7 @@ fn Struct(comptime T: type) type {
fn maybePointer(self: ?*const Self) Self { fn maybePointer(self: ?*const Self) Self {
const none = Self{ .x = if (T == void) void{} else 0 }; const none = Self{ .x = if (T == void) void{} else 0 };
return (self ?? &none).*; return (self orelse &none).*;
} }
}; };
} }
@ -86,7 +87,7 @@ const Union = union {
fn maybePointer(self: ?*const Union) Union { fn maybePointer(self: ?*const Union) Union {
const none = Union{ .x = 0 }; const none = Union{ .x = 0 };
return (self ?? &none).*; return (self orelse &none).*;
} }
}; };
@ -99,7 +100,7 @@ const Enum = enum {
} }
fn maybePointer(self: ?*const Enum) Enum { fn maybePointer(self: ?*const Enum) Enum {
return (self ?? &Enum.None).*; return (self orelse &Enum.None).*;
} }
}; };
@ -108,16 +109,16 @@ test "implicitly cast indirect pointer to maybe-indirect pointer" {
const Self = this; const Self = this;
x: u8, x: u8,
fn constConst(p: *const *const Self) u8 { fn constConst(p: *const *const Self) u8 {
return (p.*).x; return p.*.x;
} }
fn maybeConstConst(p: ?*const *const Self) u8 { fn maybeConstConst(p: ?*const *const Self) u8 {
return ((??p).*).x; return p.?.*.x;
} }
fn constConstConst(p: *const *const *const Self) u8 { fn constConstConst(p: *const *const *const Self) u8 {
return (p.*.*).x; return p.*.*.x;
} }
fn maybeConstConstConst(p: ?*const *const *const Self) u8 { fn maybeConstConstConst(p: ?*const *const *const Self) u8 {
return ((??p).*.*).x; return p.?.*.*.x;
} }
}; };
const s = S{ .x = 42 }; const s = S{ .x = 42 };
@ -176,56 +177,56 @@ test "string literal to &const []const u8" {
} }
test "implicitly cast from T to error!?T" { test "implicitly cast from T to error!?T" {
castToMaybeTypeError(1); castToOptionalTypeError(1);
comptime castToMaybeTypeError(1); comptime castToOptionalTypeError(1);
} }
const A = struct { const A = struct {
a: i32, a: i32,
}; };
fn castToMaybeTypeError(z: i32) void { fn castToOptionalTypeError(z: i32) void {
const x = i32(1); const x = i32(1);
const y: error!?i32 = x; const y: error!?i32 = x;
assert(??(try y) == 1); assert((try y).? == 1);
const f = z; const f = z;
const g: error!?i32 = f; const g: error!?i32 = f;
const a = A{ .a = z }; const a = A{ .a = z };
const b: error!?A = a; const b: error!?A = a;
assert((??(b catch unreachable)).a == 1); assert((b catch unreachable).?.a == 1);
} }
test "implicitly cast from int to error!?T" { test "implicitly cast from int to error!?T" {
implicitIntLitToMaybe(); implicitIntLitToOptional();
comptime implicitIntLitToMaybe(); comptime implicitIntLitToOptional();
} }
fn implicitIntLitToMaybe() void { fn implicitIntLitToOptional() void {
const f: ?i32 = 1; const f: ?i32 = 1;
const g: error!?i32 = 1; const g: error!?i32 = 1;
} }
test "return null from fn() error!?&T" { test "return null from fn() error!?&T" {
const a = returnNullFromMaybeTypeErrorRef(); const a = returnNullFromOptionalTypeErrorRef();
const b = returnNullLitFromMaybeTypeErrorRef(); const b = returnNullLitFromOptionalTypeErrorRef();
assert((try a) == null and (try b) == null); assert((try a) == null and (try b) == null);
} }
fn returnNullFromMaybeTypeErrorRef() error!?*A { fn returnNullFromOptionalTypeErrorRef() error!?*A {
const a: ?*A = null; const a: ?*A = null;
return a; return a;
} }
fn returnNullLitFromMaybeTypeErrorRef() error!?*A { fn returnNullLitFromOptionalTypeErrorRef() error!?*A {
return null; return null;
} }
test "peer type resolution: ?T and T" { test "peer type resolution: ?T and T" {
assert(??peerTypeTAndMaybeT(true, false) == 0); assert(peerTypeTAndOptionalT(true, false).? == 0);
assert(??peerTypeTAndMaybeT(false, false) == 3); assert(peerTypeTAndOptionalT(false, false).? == 3);
comptime { comptime {
assert(??peerTypeTAndMaybeT(true, false) == 0); assert(peerTypeTAndOptionalT(true, false).? == 0);
assert(??peerTypeTAndMaybeT(false, false) == 3); assert(peerTypeTAndOptionalT(false, false).? == 3);
} }
} }
fn peerTypeTAndMaybeT(c: bool, b: bool) ?usize { fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
if (c) { if (c) {
return if (b) null else usize(0); return if (b) null else usize(0);
} }
@ -250,11 +251,11 @@ fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
} }
test "implicitly cast from [N]T to ?[]const T" { test "implicitly cast from [N]T to ?[]const T" {
assert(mem.eql(u8, ??castToMaybeSlice(), "hi")); assert(mem.eql(u8, castToOptionalSlice().?, "hi"));
comptime assert(mem.eql(u8, ??castToMaybeSlice(), "hi")); comptime assert(mem.eql(u8, castToOptionalSlice().?, "hi"));
} }
fn castToMaybeSlice() ?[]const u8 { fn castToOptionalSlice() ?[]const u8 {
return "hi"; return "hi";
} }
@ -384,3 +385,24 @@ test "const slice widen cast" {
assert(@bitCast(u32, bytes) == 0x12121212); assert(@bitCast(u32, bytes) == 0x12121212);
} }
test "single-item pointer of array to slice and to unknown length pointer" {
testCastPtrOfArrayToSliceAndPtr();
comptime testCastPtrOfArrayToSliceAndPtr();
}
fn testCastPtrOfArrayToSliceAndPtr() void {
var array = "ao" ++ "eu"; // TODO https://github.com/ziglang/zig/issues/1076
const x: [*]u8 = &array;
x[0] += 1;
assert(mem.eql(u8, array[0..], "boeu"));
const y: []u8 = &array;
y[0] += 1;
assert(mem.eql(u8, array[0..], "coeu"));
}
test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
const window_name = [1][*]const u8{c"window name"};
const x: [*]const ?[*]const u8 = &window_name;
assert(mem.eql(u8, std.cstr.toSliceConst(x[0].?), "window name"));
}

View File

@ -883,3 +883,12 @@ test "empty extern enum with members" {
}; };
assert(@sizeOf(E) == @sizeOf(c_int)); assert(@sizeOf(E) == @sizeOf(c_int));
} }
test "aoeu" {
const LocalFoo = enum {
A = 1,
B = 0,
};
var b = LocalFoo.B;
assert(mem.eql(u8, @tagName(b), "B"));
}

View File

@ -140,7 +140,7 @@ fn testComptimeTestErrorEmptySet(x: EmptyErrorSet!i32) void {
if (x) |v| assert(v == 1234) else |err| @compileError("bad"); if (x) |v| assert(v == 1234) else |err| @compileError("bad");
} }
test "syntax: nullable operator in front of error union operator" { test "syntax: optional operator in front of error union operator" {
comptime { comptime {
assert(?error!i32 == ?(error!i32)); assert(?error!i32 == ?(error!i32));
} }

View File

@ -12,7 +12,7 @@ fn fibonacci(x: i32) i32 {
} }
fn unwrapAndAddOne(blah: ?i32) i32 { fn unwrapAndAddOne(blah: ?i32) i32 {
return ??blah + 1; return blah.? + 1;
} }
const should_be_1235 = unwrapAndAddOne(1234); const should_be_1235 = unwrapAndAddOne(1234);
test "static add one" { test "static add one" {
@ -610,3 +610,16 @@ test "slice of type" {
} }
} }
} }
const Wrapper = struct {
T: type,
};
fn wrap(comptime T: type) Wrapper {
return Wrapper{ .T = T };
}
test "function which returns struct with type field causes implicit comptime" {
const ty = wrap(i32).T;
assert(ty == i32);
}

View File

@ -127,7 +127,7 @@ test "generic fn with implicit cast" {
}) == 0); }) == 0);
} }
fn getByte(ptr: ?*const u8) u8 { fn getByte(ptr: ?*const u8) u8 {
return (??ptr).*; return ptr.?.*;
} }
fn getFirstByte(comptime T: type, mem: []const T) u8 { fn getFirstByte(comptime T: type, mem: []const T) u8 {
return getByte(@ptrCast(*const u8, &mem[0])); return getByte(@ptrCast(*const u8, &mem[0]));

View File

@ -0,0 +1,21 @@
const A = error{
PathNotFound,
NotDir,
};
const B = error{OutOfMemory};
const C = A || B;
fn foo() C!void {
return error.NotDir;
}
test "merge error sets" {
if (foo()) {
@panic("unexpected");
} else |err| switch (err) {
error.OutOfMemory => @panic("unexpected"),
error.PathNotFound => @panic("unexpected"),
error.NotDir => {},
}
}

View File

@ -505,7 +505,7 @@ test "@typeId" {
assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat); assert(@typeId(@typeOf(1.0)) == Tid.ComptimeFloat);
assert(@typeId(@typeOf(undefined)) == Tid.Undefined); assert(@typeId(@typeOf(undefined)) == Tid.Undefined);
assert(@typeId(@typeOf(null)) == Tid.Null); assert(@typeId(@typeOf(null)) == Tid.Null);
assert(@typeId(?i32) == Tid.Nullable); assert(@typeId(?i32) == Tid.Optional);
assert(@typeId(error!i32) == Tid.ErrorUnion); assert(@typeId(error!i32) == Tid.ErrorUnion);
assert(@typeId(error) == Tid.ErrorSet); assert(@typeId(error) == Tid.ErrorSet);
assert(@typeId(AnEnum) == Tid.Enum); assert(@typeId(AnEnum) == Tid.Enum);
@ -523,14 +523,6 @@ test "@typeId" {
} }
} }
test "@canImplicitCast" {
comptime {
assert(@canImplicitCast(i64, i32(3)));
assert(!@canImplicitCast(i32, f32(1.234)));
assert(@canImplicitCast([]const u8, "aoeu"));
}
}
test "@typeName" { test "@typeName" {
const Struct = struct {}; const Struct = struct {};
const Union = union { const Union = union {

View File

@ -1,6 +1,6 @@
const assert = @import("std").debug.assert; const assert = @import("std").debug.assert;
test "nullable type" { test "optional type" {
const x: ?bool = true; const x: ?bool = true;
if (x) |y| { if (x) |y| {
@ -15,13 +15,13 @@ test "nullable type" {
const next_x: ?i32 = null; const next_x: ?i32 = null;
const z = next_x ?? 1234; const z = next_x orelse 1234;
assert(z == 1234); assert(z == 1234);
const final_x: ?i32 = 13; const final_x: ?i32 = 13;
const num = final_x ?? unreachable; const num = final_x orelse unreachable;
assert(num == 13); assert(num == 13);
} }
@ -33,12 +33,12 @@ test "test maybe object and get a pointer to the inner value" {
b.* = false; b.* = false;
} }
assert(??maybe_bool == false); assert(maybe_bool.? == false);
} }
test "rhs maybe unwrap return" { test "rhs maybe unwrap return" {
const x: ?bool = true; const x: ?bool = true;
const y = x ?? return; const y = x orelse return;
} }
test "maybe return" { test "maybe return" {
@ -47,13 +47,13 @@ test "maybe return" {
} }
fn maybeReturnImpl() void { fn maybeReturnImpl() void {
assert(??foo(1235)); assert(foo(1235).?);
if (foo(null) != null) unreachable; if (foo(null) != null) unreachable;
assert(!??foo(1234)); assert(!foo(1234).?);
} }
fn foo(x: ?i32) ?bool { fn foo(x: ?i32) ?bool {
const value = x ?? return null; const value = x orelse return null;
return value > 1234; return value > 1234;
} }
@ -102,12 +102,12 @@ fn testTestNullRuntime(x: ?i32) void {
assert(!(x != null)); assert(!(x != null));
} }
test "nullable void" { test "optional void" {
nullableVoidImpl(); optionalVoidImpl();
comptime nullableVoidImpl(); comptime optionalVoidImpl();
} }
fn nullableVoidImpl() void { fn optionalVoidImpl() void {
assert(bar(null) == null); assert(bar(null) == null);
assert(bar({}) != null); assert(bar({}) != null);
} }
@ -120,19 +120,19 @@ fn bar(x: ?void) ?void {
} }
} }
const StructWithNullable = struct { const StructWithOptional = struct {
field: ?i32, field: ?i32,
}; };
var struct_with_nullable: StructWithNullable = undefined; var struct_with_optional: StructWithOptional = undefined;
test "unwrap nullable which is field of global var" { test "unwrap optional which is field of global var" {
struct_with_nullable.field = null; struct_with_optional.field = null;
if (struct_with_nullable.field) |payload| { if (struct_with_optional.field) |payload| {
unreachable; unreachable;
} }
struct_with_nullable.field = 1234; struct_with_optional.field = 1234;
if (struct_with_nullable.field) |payload| { if (struct_with_optional.field) |payload| {
assert(payload == 1234); assert(payload == 1234);
} else { } else {
unreachable; unreachable;
@ -140,6 +140,17 @@ test "unwrap nullable which is field of global var" {
} }
test "null with default unwrap" { test "null with default unwrap" {
const x: i32 = null ?? 1; const x: i32 = null orelse 1;
assert(x == 1); assert(x == 1);
} }
test "optional types" {
comptime {
const opt_type_struct = StructWithOptionalType { .t=u8, };
assert(opt_type_struct.t != null and opt_type_struct.t.? == u8);
}
}
const StructWithOptionalType = struct {
t: ?type,
};

View File

@ -2,7 +2,7 @@ const assert = @import("std").debug.assert;
const mem = @import("std").mem; const mem = @import("std").mem;
const reflection = this; const reflection = this;
test "reflection: array, pointer, nullable, error union type child" { test "reflection: array, pointer, optional, error union type child" {
comptime { comptime {
assert(([10]u8).Child == u8); assert(([10]u8).Child == u8);
assert((*u8).Child == u8); assert((*u8).Child == u8);

View File

@ -421,3 +421,20 @@ const Expr = union(enum) {
fn alloc(comptime T: type) []T { fn alloc(comptime T: type) []T {
return []T{}; return []T{};
} }
test "call method with mutable reference to struct with no fields" {
const S = struct {
fn doC(s: *const this) bool {
return true;
}
fn do(s: *this) bool {
return true;
}
};
var s = S{};
assert(S.doC(&s));
assert(s.doC());
assert(S.do(&s));
assert(s.do());
}

View File

@ -39,12 +39,28 @@ test "type info: pointer type info" {
fn testPointer() void { fn testPointer() void {
const u32_ptr_info = @typeInfo(*u32); const u32_ptr_info = @typeInfo(*u32);
assert(TypeId(u32_ptr_info) == TypeId.Pointer); assert(TypeId(u32_ptr_info) == TypeId.Pointer);
assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.One);
assert(u32_ptr_info.Pointer.is_const == false); assert(u32_ptr_info.Pointer.is_const == false);
assert(u32_ptr_info.Pointer.is_volatile == false); assert(u32_ptr_info.Pointer.is_volatile == false);
assert(u32_ptr_info.Pointer.alignment == 4); assert(u32_ptr_info.Pointer.alignment == @alignOf(u32));
assert(u32_ptr_info.Pointer.child == u32); assert(u32_ptr_info.Pointer.child == u32);
} }
test "type info: unknown length pointer type info" {
testUnknownLenPtr();
comptime testUnknownLenPtr();
}
fn testUnknownLenPtr() void {
const u32_ptr_info = @typeInfo([*]const volatile f64);
assert(TypeId(u32_ptr_info) == TypeId.Pointer);
assert(u32_ptr_info.Pointer.size == TypeInfo.Pointer.Size.Many);
assert(u32_ptr_info.Pointer.is_const == true);
assert(u32_ptr_info.Pointer.is_volatile == true);
assert(u32_ptr_info.Pointer.alignment == @alignOf(f64));
assert(u32_ptr_info.Pointer.child == f64);
}
test "type info: slice type info" { test "type info: slice type info" {
testSlice(); testSlice();
comptime testSlice(); comptime testSlice();
@ -52,11 +68,12 @@ test "type info: slice type info" {
fn testSlice() void { fn testSlice() void {
const u32_slice_info = @typeInfo([]u32); const u32_slice_info = @typeInfo([]u32);
assert(TypeId(u32_slice_info) == TypeId.Slice); assert(TypeId(u32_slice_info) == TypeId.Pointer);
assert(u32_slice_info.Slice.is_const == false); assert(u32_slice_info.Pointer.size == TypeInfo.Pointer.Size.Slice);
assert(u32_slice_info.Slice.is_volatile == false); assert(u32_slice_info.Pointer.is_const == false);
assert(u32_slice_info.Slice.alignment == 4); assert(u32_slice_info.Pointer.is_volatile == false);
assert(u32_slice_info.Slice.child == u32); assert(u32_slice_info.Pointer.alignment == 4);
assert(u32_slice_info.Pointer.child == u32);
} }
test "type info: array type info" { test "type info: array type info" {
@ -71,15 +88,15 @@ fn testArray() void {
assert(arr_info.Array.child == bool); assert(arr_info.Array.child == bool);
} }
test "type info: nullable type info" { test "type info: optional type info" {
testNullable(); testOptional();
comptime testNullable(); comptime testOptional();
} }
fn testNullable() void { fn testOptional() void {
const null_info = @typeInfo(?void); const null_info = @typeInfo(?void);
assert(TypeId(null_info) == TypeId.Nullable); assert(TypeId(null_info) == TypeId.Optional);
assert(null_info.Nullable.child == void); assert(null_info.Optional.child == void);
} }
test "type info: promise info" { test "type info: promise info" {
@ -149,11 +166,11 @@ fn testUnion() void {
assert(TypeId(typeinfo_info) == TypeId.Union); assert(TypeId(typeinfo_info) == TypeId.Union);
assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto); assert(typeinfo_info.Union.layout == TypeInfo.ContainerLayout.Auto);
assert(typeinfo_info.Union.tag_type == TypeId); assert(typeinfo_info.Union.tag_type == TypeId);
assert(typeinfo_info.Union.fields.len == 26); assert(typeinfo_info.Union.fields.len == 25);
assert(typeinfo_info.Union.fields[4].enum_field != null); assert(typeinfo_info.Union.fields[4].enum_field != null);
assert((??typeinfo_info.Union.fields[4].enum_field).value == 4); assert(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int)); assert(typeinfo_info.Union.fields[4].field_type == @typeOf(@typeInfo(u8).Int));
assert(typeinfo_info.Union.defs.len == 21); assert(typeinfo_info.Union.defs.len == 20);
const TestNoTagUnion = union { const TestNoTagUnion = union {
Foo: void, Foo: void,

View File

@ -81,7 +81,7 @@ test "while with else" {
assert(got_else == 1); assert(got_else == 1);
} }
test "while with nullable as condition" { test "while with optional as condition" {
numbers_left = 10; numbers_left = 10;
var sum: i32 = 0; var sum: i32 = 0;
while (getNumberOrNull()) |value| { while (getNumberOrNull()) |value| {
@ -90,7 +90,7 @@ test "while with nullable as condition" {
assert(sum == 45); assert(sum == 45);
} }
test "while with nullable as condition with else" { test "while with optional as condition with else" {
numbers_left = 10; numbers_left = 10;
var sum: i32 = 0; var sum: i32 = 0;
var got_else: i32 = 0; var got_else: i32 = 0;
@ -132,7 +132,7 @@ fn getNumberOrNull() ?i32 {
}; };
} }
test "while on nullable with else result follow else prong" { test "while on optional with else result follow else prong" {
const result = while (returnNull()) |value| { const result = while (returnNull()) |value| {
break value; break value;
} else } else
@ -140,8 +140,8 @@ test "while on nullable with else result follow else prong" {
assert(result == 2); assert(result == 2);
} }
test "while on nullable with else result follow break prong" { test "while on optional with else result follow break prong" {
const result = while (returnMaybe(10)) |value| { const result = while (returnOptional(10)) |value| {
break value; break value;
} else } else
i32(2); i32(2);
@ -210,7 +210,7 @@ fn testContinueOuter() void {
fn returnNull() ?i32 { fn returnNull() ?i32 {
return null; return null;
} }
fn returnMaybe(x: i32) ?i32 { fn returnOptional(x: i32) ?i32 {
return x; return x;
} }
fn returnError() error!i32 { fn returnError() error!i32 {

View File

@ -284,7 +284,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
cases.addC("expose function pointer to C land", cases.addC("expose function pointer to C land",
\\const c = @cImport(@cInclude("stdlib.h")); \\const c = @cImport(@cInclude("stdlib.h"));
\\ \\
\\export fn compare_fn(a: ?[*]const c_void, b: ?[*]const c_void) c_int { \\export fn compare_fn(a: ?*const c_void, b: ?*const c_void) c_int {
\\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a)); \\ const a_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), a));
\\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b)); \\ const b_int = @ptrCast(*const i32, @alignCast(@alignOf(i32), b));
\\ if (a_int.* < b_int.*) { \\ if (a_int.* < b_int.*) {
@ -299,7 +299,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
\\export fn main() c_int { \\export fn main() c_int {
\\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 }; \\ var array = []u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
\\ \\
\\ c.qsort(@ptrCast(?[*]c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn); \\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), c_ulong(array.len), @sizeOf(i32), compare_fn);
\\ \\
\\ for (array) |item, i| { \\ for (array) |item, i| {
\\ if (item != i) { \\ if (item != i) {

View File

@ -1,6 +1,57 @@
const tests = @import("tests.zig"); const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void { pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"use implicit casts to assign null to non-nullable pointer",
\\export fn entry() void {
\\ var x: i32 = 1234;
\\ var p: *i32 = &x;
\\ var pp: *?*i32 = &p;
\\ pp.* = null;
\\ var y = p.*;
\\}
,
".tmp_source.zig:4:23: error: expected type '*?*i32', found '**i32'",
);
cases.add(
"attempted implicit cast from T to [*]const T",
\\export fn entry() void {
\\ const x: [*]const bool = true;
\\}
,
".tmp_source.zig:2:30: error: expected type '[*]const bool', found 'bool'",
);
cases.add(
"dereference unknown length pointer",
\\export fn entry(x: [*]i32) i32 {
\\ return x.*;
\\}
,
".tmp_source.zig:2:13: error: index syntax required for unknown-length pointer type '[*]i32'",
);
cases.add(
"field access of unknown length pointer",
\\const Foo = extern struct {
\\ a: i32,
\\};
\\
\\export fn entry(foo: [*]Foo) void {
\\ foo.a += 1;
\\}
,
".tmp_source.zig:6:8: error: type '[*]Foo' does not support field access",
);
cases.add(
"unknown length pointer to opaque",
\\export const T = [*]@OpaqueType();
,
".tmp_source.zig:1:18: error: unknown-length pointer to opaque",
);
cases.add( cases.add(
"error when evaluating return type", "error when evaluating return type",
\\const Foo = struct { \\const Foo = struct {
@ -1303,7 +1354,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ if (true) |x| { } \\ if (true) |x| { }
\\} \\}
, ,
".tmp_source.zig:2:9: error: expected nullable type, found 'bool'", ".tmp_source.zig:2:9: error: expected optional type, found 'bool'",
); );
cases.add( cases.add(
@ -1742,7 +1793,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
); );
cases.add( cases.add(
"assign null to non-nullable pointer", "assign null to non-optional pointer",
\\const a: *u8 = null; \\const a: *u8 = null;
\\ \\
\\export fn entry() usize { return @sizeOf(@typeOf(a)); } \\export fn entry() usize { return @sizeOf(@typeOf(a)); }
@ -2258,7 +2309,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
\\ \\
\\ defer try canFail(); \\ defer try canFail();
\\ \\
\\ const a = maybeInt() ?? return; \\ const a = maybeInt() orelse return;
\\} \\}
\\ \\
\\fn canFail() error!void { } \\fn canFail() error!void { }
@ -2779,7 +2830,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
); );
cases.add( cases.add(
"while expected bool, got nullable", "while expected bool, got optional",
\\export fn foo() void { \\export fn foo() void {
\\ while (bar()) {} \\ while (bar()) {}
\\} \\}
@ -2799,23 +2850,23 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
); );
cases.add( cases.add(
"while expected nullable, got bool", "while expected optional, got bool",
\\export fn foo() void { \\export fn foo() void {
\\ while (bar()) |x| {} \\ while (bar()) |x| {}
\\} \\}
\\fn bar() bool { return true; } \\fn bar() bool { return true; }
, ,
".tmp_source.zig:2:15: error: expected nullable type, found 'bool'", ".tmp_source.zig:2:15: error: expected optional type, found 'bool'",
); );
cases.add( cases.add(
"while expected nullable, got error union", "while expected optional, got error union",
\\export fn foo() void { \\export fn foo() void {
\\ while (bar()) |x| {} \\ while (bar()) |x| {}
\\} \\}
\\fn bar() error!i32 { return 1; } \\fn bar() error!i32 { return 1; }
, ,
".tmp_source.zig:2:15: error: expected nullable type, found 'error!i32'", ".tmp_source.zig:2:15: error: expected optional type, found 'error!i32'",
); );
cases.add( cases.add(
@ -2829,7 +2880,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
); );
cases.add( cases.add(
"while expected error union, got nullable", "while expected error union, got optional",
\\export fn foo() void { \\export fn foo() void {
\\ while (bar()) |x| {} else |err| {} \\ while (bar()) |x| {} else |err| {}
\\} \\}
@ -3291,7 +3342,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime", ".tmp_source.zig:9:4: error: variable of type 'comptime_float' must be const or comptime",
".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime", ".tmp_source.zig:10:4: error: variable of type '(block)' must be const or comptime",
".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime", ".tmp_source.zig:11:4: error: variable of type '(null)' must be const or comptime",
".tmp_source.zig:12:4: error: variable of type 'Opaque' must be const or comptime", ".tmp_source.zig:12:4: error: variable of type 'Opaque' not allowed",
".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime", ".tmp_source.zig:13:4: error: variable of type 'type' must be const or comptime",
".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime", ".tmp_source.zig:14:4: error: variable of type '(namespace)' must be const or comptime",
".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime", ".tmp_source.zig:15:4: error: variable of type '(bound fn(*const Foo) void)' must be const or comptime",

View File

@ -282,8 +282,8 @@ pub const CompareOutputContext = struct {
var stdout = Buffer.initNull(b.allocator); var stdout = Buffer.initNull(b.allocator);
var stderr = Buffer.initNull(b.allocator); var stderr = Buffer.initNull(b.allocator);
var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable;
@ -601,8 +601,8 @@ pub const CompileErrorContext = struct {
var stdout_buf = Buffer.initNull(b.allocator); var stdout_buf = Buffer.initNull(b.allocator);
var stderr_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator);
var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;
@ -872,8 +872,8 @@ pub const TranslateCContext = struct {
var stdout_buf = Buffer.initNull(b.allocator); var stdout_buf = Buffer.initNull(b.allocator);
var stderr_buf = Buffer.initNull(b.allocator); var stderr_buf = Buffer.initNull(b.allocator);
var stdout_file_in_stream = io.FileInStream.init(&??child.stdout); var stdout_file_in_stream = io.FileInStream.init(&child.stdout.?);
var stderr_file_in_stream = io.FileInStream.init(&??child.stderr); var stderr_file_in_stream = io.FileInStream.init(&child.stderr.?);
stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable;
stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable;

View File

@ -99,7 +99,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("restrict -> noalias", cases.add("restrict -> noalias",
\\void foo(void *restrict bar, void *restrict); \\void foo(void *restrict bar, void *restrict);
, ,
\\pub extern fn foo(noalias bar: ?[*]c_void, noalias arg1: ?[*]c_void) void; \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void;
); );
cases.add("simple struct", cases.add("simple struct",
@ -172,7 +172,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, ,
\\pub const struct_Foo = @OpaqueType(); \\pub const struct_Foo = @OpaqueType();
, ,
\\pub extern fn some_func(foo: ?[*]struct_Foo, x: c_int) ?[*]struct_Foo; \\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
, ,
\\pub const Foo = struct_Foo; \\pub const Foo = struct_Foo;
); );
@ -233,7 +233,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
, ,
\\pub const Foo = c_void; \\pub const Foo = c_void;
, ,
\\pub extern fn fun(a: ?[*]Foo) Foo; \\pub extern fn fun(a: ?*Foo) Foo;
); );
cases.add("generate inline func for #define global extern fn", cases.add("generate inline func for #define global extern fn",
@ -246,13 +246,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub extern var fn_ptr: ?extern fn() void; \\pub extern var fn_ptr: ?extern fn() void;
, ,
\\pub inline fn foo() void { \\pub inline fn foo() void {
\\ return (??fn_ptr)(); \\ return fn_ptr.?();
\\} \\}
, ,
\\pub extern var fn_ptr2: ?extern fn(c_int, f32) u8; \\pub extern var fn_ptr2: ?extern fn(c_int, f32) u8;
, ,
\\pub inline fn bar(arg0: c_int, arg1: f32) u8 { \\pub inline fn bar(arg0: c_int, arg1: f32) u8 {
\\ return (??fn_ptr2)(arg0, arg1); \\ return fn_ptr2.?(arg0, arg1);
\\} \\}
); );
@ -505,7 +505,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 6; \\ return 6;
\\} \\}
, ,
\\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ if ((a != 0) and (b != 0)) return 0; \\ if ((a != 0) and (b != 0)) return 0;
\\ if ((b != 0) and (c != null)) return 1; \\ if ((b != 0) and (c != null)) return 1;
\\ if ((a != 0) and (c != null)) return 2; \\ if ((a != 0) and (c != null)) return 2;
@ -608,7 +608,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ field: c_int, \\ field: c_int,
\\}; \\};
\\pub export fn read_field(foo: ?[*]struct_Foo) c_int { \\pub export fn read_field(foo: ?[*]struct_Foo) c_int {
\\ return (??foo).field; \\ return foo.?.field;
\\} \\}
); );
@ -653,8 +653,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return x; \\ return x;
\\} \\}
, ,
\\pub export fn foo(x: ?[*]c_ushort) ?[*]c_void { \\pub export fn foo(x: ?[*]c_ushort) ?*c_void {
\\ return @ptrCast(?[*]c_void, x); \\ return @ptrCast(?*c_void, x);
\\} \\}
); );
@ -969,11 +969,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub export fn bar() void { \\pub export fn bar() void {
\\ var f: ?extern fn() void = foo; \\ var f: ?extern fn() void = foo;
\\ var b: ?extern fn() c_int = baz; \\ var b: ?extern fn() c_int = baz;
\\ (??f)(); \\ f.?();
\\ (??f)(); \\ f.?();
\\ foo(); \\ foo();
\\ _ = (??b)(); \\ _ = b.?();
\\ _ = (??b)(); \\ _ = b.?();
\\ _ = baz(); \\ _ = baz();
\\} \\}
); );
@ -984,7 +984,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\} \\}
, ,
\\pub export fn foo(x: ?[*]c_int) void { \\pub export fn foo(x: ?[*]c_int) void {
\\ (??x).* = 1; \\ x.?.* = 1;
\\} \\}
); );
@ -1012,7 +1012,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub fn foo() c_int { \\pub fn foo() c_int {
\\ var x: c_int = 1234; \\ var x: c_int = 1234;
\\ var ptr: ?[*]c_int = &x; \\ var ptr: ?[*]c_int = &x;
\\ return (??ptr).*; \\ return ptr.?.*;
\\} \\}
); );
@ -1119,7 +1119,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const glClearPFN = PFNGLCLEARPROC; \\pub const glClearPFN = PFNGLCLEARPROC;
, ,
\\pub inline fn glClearUnion(arg0: GLbitfield) void { \\pub inline fn glClearUnion(arg0: GLbitfield) void {
\\ return (??glProcs.gl.Clear)(arg0); \\ return glProcs.gl.Clear.?(arg0);
\\} \\}
, ,
\\pub const OpenGLProcs = union_OpenGLProcs; \\pub const OpenGLProcs = union_OpenGLProcs;
@ -1173,7 +1173,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return !c; \\ return !c;
\\} \\}
, ,
\\pub fn foo(a: c_int, b: f32, c: ?[*]c_void) c_int { \\pub fn foo(a: c_int, b: f32, c: ?*c_void) c_int {
\\ return !(a == 0); \\ return !(a == 0);
\\ return !(a != 0); \\ return !(a != 0);
\\ return !(b != 0); \\ return !(b != 0);
@ -1231,7 +1231,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ B, \\ B,
\\ C, \\ C,
\\}; \\};
\\pub fn if_none_bool(a: c_int, b: f32, c: ?[*]c_void, d: enum_SomeEnum) c_int { \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int {
\\ if (a != 0) return 0; \\ if (a != 0) return 0;
\\ if (b != 0) return 1; \\ if (b != 0) return 1;
\\ if (c != null) return 2; \\ if (c != null) return 2;
@ -1248,7 +1248,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 3; \\ return 3;
\\} \\}
, ,
\\pub fn while_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ while (a != 0) return 0; \\ while (a != 0) return 0;
\\ while (b != 0) return 1; \\ while (b != 0) return 1;
\\ while (c != null) return 2; \\ while (c != null) return 2;
@ -1264,7 +1264,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ return 3; \\ return 3;
\\} \\}
, ,
\\pub fn for_none_bool(a: c_int, b: f32, c: ?[*]c_void) c_int { \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int {
\\ while (a != 0) return 0; \\ while (a != 0) return 0;
\\ while (b != 0) return 1; \\ while (b != 0) return 1;
\\ while (c != null) return 2; \\ while (c != null) return 2;