Merge remote-tracking branch 'origin/master' into llvm11
commit
c6e0df6213
|
@ -77,6 +77,9 @@ pub fn build(b: *Builder) !void {
|
|||
const link_libc = b.option(bool, "force-link-libc", "Force self-hosted compiler to link libc") orelse false;
|
||||
if (link_libc) exe.linkLibC();
|
||||
|
||||
const log_scopes = b.option([]const []const u8, "log", "Which log scopes to enable") orelse &[0][]const u8{};
|
||||
|
||||
exe.addBuildOption([]const []const u8, "log_scopes", log_scopes);
|
||||
exe.addBuildOption(bool, "enable_tracy", tracy != null);
|
||||
if (tracy) |tracy_path| {
|
||||
const client_cpp = fs.path.join(
|
||||
|
@ -104,6 +107,12 @@ pub fn build(b: *Builder) !void {
|
|||
const is_wasmtime_enabled = b.option(bool, "enable-wasmtime", "Use Wasmtime to enable and run WASI libstd tests") orelse false;
|
||||
const glibc_multi_dir = b.option([]const u8, "enable-foreign-glibc", "Provide directory with glibc installations to run cross compiled tests that link glibc");
|
||||
|
||||
|
||||
test_stage2.addBuildOption(bool, "enable_qemu", is_qemu_enabled);
|
||||
test_stage2.addBuildOption(bool, "enable_wine", is_wine_enabled);
|
||||
test_stage2.addBuildOption(bool, "enable_wasmtime", is_wasmtime_enabled);
|
||||
test_stage2.addBuildOption(?[]const u8, "glibc_multi_install_dir", glibc_multi_dir);
|
||||
|
||||
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
|
||||
test_stage2_step.dependOn(&test_stage2.step);
|
||||
test_step.dependOn(test_stage2_step);
|
||||
|
|
|
@ -41,7 +41,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- powershell: |
|
||||
(New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-06-02/msys2-base-x86_64-20200602.sfx.exe", "sfx.exe")
|
||||
(New-Object Net.WebClient).DownloadFile("https://github.com/msys2/msys2-installer/releases/download/2020-07-20/msys2-base-x86_64-20200720.sfx.exe", "sfx.exe")
|
||||
.\sfx.exe -y -o\
|
||||
del sfx.exe
|
||||
displayName: Download/Extract/Install MSYS2
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Documentation - The Zig Programming Language</title>
|
||||
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAgklEQVR4AWMYWuD7EllJIM4G4g4g5oIJ/odhOJ8wToOxSTXgNxDHoeiBMfA4+wGShjyYOCkG/IGqWQziEzYAoUAeiF9D5U+DxEg14DRU7jWIT5IBIOdCxf+A+CQZAAoopEB7QJwBCBwHiip8UYmRdrAlDpIMgApwQZNnNii5Dq0MBgCxxycBnwEd+wAAAABJRU5ErkJggg=="/>
|
||||
<style>
|
||||
|
@ -7473,7 +7474,7 @@ export fn @"A function name that is a complete sentence."() void {}
|
|||
<p>
|
||||
When looking at the resulting object, you can see the symbol is used verbatim:
|
||||
</p>
|
||||
<pre>00000000000001f0 T A function name that is a complete sentence.</pre>
|
||||
<pre><code>00000000000001f0 T A function name that is a complete sentence.</code></pre>
|
||||
{#see_also|Exporting a C Library#}
|
||||
{#header_close#}
|
||||
|
||||
|
|
|
@ -108,6 +108,33 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
|||
mem.copy(T, self.items[i .. i + items.len], items);
|
||||
}
|
||||
|
||||
/// Replace range of elements `list[start..start+len]` with `new_items`
|
||||
/// grows list if `len < new_items.len`. may allocate
|
||||
/// shrinks list if `len > new_items.len`
|
||||
pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void {
|
||||
const after_range = start + len;
|
||||
const range = self.items[start..after_range];
|
||||
|
||||
if (range.len == new_items.len)
|
||||
mem.copy(T, range, new_items)
|
||||
else if (range.len < new_items.len) {
|
||||
const first = new_items[0..range.len];
|
||||
const rest = new_items[range.len..];
|
||||
|
||||
mem.copy(T, range, first);
|
||||
try self.insertSlice(after_range, rest);
|
||||
} else {
|
||||
mem.copy(T, range, new_items);
|
||||
const after_subrange = start + new_items.len;
|
||||
|
||||
for (self.items[after_range..]) |item, i| {
|
||||
self.items[after_subrange..][i] = item;
|
||||
}
|
||||
|
||||
self.items.len -= len - new_items.len;
|
||||
}
|
||||
}
|
||||
|
||||
/// Extend the list by 1 element. Allocates more memory as necessary.
|
||||
pub fn append(self: *Self, item: T) !void {
|
||||
const new_item_ptr = try self.addOne();
|
||||
|
@ -189,6 +216,15 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
|
|||
mem.set(T, self.items[old_len..self.items.len], value);
|
||||
}
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// Asserts the capacity is enough.
|
||||
pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
|
||||
const new_len = self.items.len + n;
|
||||
assert(new_len <= self.capacity);
|
||||
mem.set(T, self.items.ptr[self.items.len..new_len], value);
|
||||
self.items.len = new_len;
|
||||
}
|
||||
|
||||
/// Adjust the list's length to `new_len`.
|
||||
/// Does not initialize added items if any.
|
||||
pub fn resize(self: *Self, new_len: usize) !void {
|
||||
|
@ -366,6 +402,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
|||
mem.copy(T, self.items[i .. i + items.len], items);
|
||||
}
|
||||
|
||||
/// Replace range of elements `list[start..start+len]` with `new_items`
|
||||
/// grows list if `len < new_items.len`. may allocate
|
||||
/// shrinks list if `len > new_items.len`
|
||||
pub fn replaceRange(self: *Self, start: usize, len: usize, new_items: SliceConst) !void {
|
||||
var managed = self.toManaged(allocator);
|
||||
try managed.replaceRange(start, len, new_items);
|
||||
self.* = managed.toUnmanaged();
|
||||
}
|
||||
|
||||
/// Extend the list by 1 element. Allocates more memory as necessary.
|
||||
pub fn append(self: *Self, allocator: *Allocator, item: T) !void {
|
||||
const new_item_ptr = try self.addOne(allocator);
|
||||
|
@ -437,6 +482,15 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
|
|||
mem.set(T, self.items[old_len..self.items.len], value);
|
||||
}
|
||||
|
||||
/// Append a value to the list `n` times.
|
||||
/// Asserts the capacity is enough.
|
||||
pub fn appendNTimesAssumeCapacity(self: *Self, value: T, n: usize) void {
|
||||
const new_len = self.items.len + n;
|
||||
assert(new_len <= self.capacity);
|
||||
mem.set(T, self.items.ptr[self.items.len..new_len], value);
|
||||
self.items.len = new_len;
|
||||
}
|
||||
|
||||
/// Adjust the list's length to `new_len`.
|
||||
/// Does not initialize added items if any.
|
||||
pub fn resize(self: *Self, allocator: *Allocator, new_len: usize) !void {
|
||||
|
@ -714,6 +768,38 @@ test "std.ArrayList.insertSlice" {
|
|||
testing.expect(list.items[0] == 1);
|
||||
}
|
||||
|
||||
test "std.ArrayList.replaceRange" {
|
||||
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const alloc = &arena.allocator;
|
||||
const init = [_]i32{ 1, 2, 3, 4, 5 };
|
||||
const new = [_]i32{ 0, 0, 0 };
|
||||
|
||||
var list_zero = ArrayList(i32).init(alloc);
|
||||
var list_eq = ArrayList(i32).init(alloc);
|
||||
var list_lt = ArrayList(i32).init(alloc);
|
||||
var list_gt = ArrayList(i32).init(alloc);
|
||||
|
||||
try list_zero.appendSlice(&init);
|
||||
try list_eq.appendSlice(&init);
|
||||
try list_lt.appendSlice(&init);
|
||||
try list_gt.appendSlice(&init);
|
||||
|
||||
try list_zero.replaceRange(1, 0, &new);
|
||||
try list_eq.replaceRange(1, 3, &new);
|
||||
try list_lt.replaceRange(1, 2, &new);
|
||||
|
||||
// after_range > new_items.len in function body
|
||||
testing.expect(1 + 4 > new.len);
|
||||
try list_gt.replaceRange(1, 4, &new);
|
||||
|
||||
testing.expectEqualSlices(i32, list_zero.items, &[_]i32{ 1, 0, 0, 0, 2, 3, 4, 5 });
|
||||
testing.expectEqualSlices(i32, list_eq.items, &[_]i32{ 1, 0, 0, 0, 5 });
|
||||
testing.expectEqualSlices(i32, list_lt.items, &[_]i32{ 1, 0, 0, 0, 4, 5 });
|
||||
testing.expectEqualSlices(i32, list_gt.items, &[_]i32{ 1, 0, 0, 0 });
|
||||
}
|
||||
|
||||
const Item = struct {
|
||||
integer: i32,
|
||||
sub_items: ArrayList(Item),
|
||||
|
|
|
@ -430,9 +430,9 @@ pub const Builder = struct {
|
|||
const entry = self.user_input_options.getEntry(name) orelse return null;
|
||||
entry.value.used = true;
|
||||
switch (type_id) {
|
||||
TypeId.Bool => switch (entry.value.value) {
|
||||
UserValue.Flag => return true,
|
||||
UserValue.Scalar => |s| {
|
||||
.Bool => switch (entry.value.value) {
|
||||
.Flag => return true,
|
||||
.Scalar => |s| {
|
||||
if (mem.eql(u8, s, "true")) {
|
||||
return true;
|
||||
} else if (mem.eql(u8, s, "false")) {
|
||||
|
@ -443,21 +443,21 @@ pub const Builder = struct {
|
|||
return null;
|
||||
}
|
||||
},
|
||||
UserValue.List => {
|
||||
.List => {
|
||||
warn("Expected -D{} to be a boolean, but received a list.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
},
|
||||
TypeId.Int => panic("TODO integer options to build script", .{}),
|
||||
TypeId.Float => panic("TODO float options to build script", .{}),
|
||||
TypeId.Enum => switch (entry.value.value) {
|
||||
UserValue.Flag => {
|
||||
.Int => panic("TODO integer options to build script", .{}),
|
||||
.Float => panic("TODO float options to build script", .{}),
|
||||
.Enum => switch (entry.value.value) {
|
||||
.Flag => {
|
||||
warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
UserValue.Scalar => |s| {
|
||||
.Scalar => |s| {
|
||||
if (std.meta.stringToEnum(T, s)) |enum_lit| {
|
||||
return enum_lit;
|
||||
} else {
|
||||
|
@ -466,33 +466,35 @@ pub const Builder = struct {
|
|||
return null;
|
||||
}
|
||||
},
|
||||
UserValue.List => {
|
||||
.List => {
|
||||
warn("Expected -D{} to be a string, but received a list.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
},
|
||||
TypeId.String => switch (entry.value.value) {
|
||||
UserValue.Flag => {
|
||||
.String => switch (entry.value.value) {
|
||||
.Flag => {
|
||||
warn("Expected -D{} to be a string, but received a boolean.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
UserValue.List => {
|
||||
.List => {
|
||||
warn("Expected -D{} to be a string, but received a list.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
UserValue.Scalar => |s| return s,
|
||||
.Scalar => |s| return s,
|
||||
},
|
||||
TypeId.List => switch (entry.value.value) {
|
||||
UserValue.Flag => {
|
||||
.List => switch (entry.value.value) {
|
||||
.Flag => {
|
||||
warn("Expected -D{} to be a list, but received a boolean.\n", .{name});
|
||||
self.markInvalidUserInput();
|
||||
return null;
|
||||
},
|
||||
UserValue.Scalar => |s| return &[_][]const u8{s},
|
||||
UserValue.List => |lst| return lst.span(),
|
||||
.Scalar => |s| {
|
||||
return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable;
|
||||
},
|
||||
.List => |lst| return lst.span(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -1151,6 +1153,7 @@ pub const LibExeObjStep = struct {
|
|||
bundle_compiler_rt: bool,
|
||||
disable_stack_probing: bool,
|
||||
disable_sanitize_c: bool,
|
||||
rdynamic: bool,
|
||||
c_std: Builder.CStd,
|
||||
override_lib_dir: ?[]const u8,
|
||||
main_pkg_path: ?[]const u8,
|
||||
|
@ -1311,6 +1314,7 @@ pub const LibExeObjStep = struct {
|
|||
.bundle_compiler_rt = false,
|
||||
.disable_stack_probing = false,
|
||||
.disable_sanitize_c = false,
|
||||
.rdynamic = false,
|
||||
.output_dir = null,
|
||||
.single_threaded = false,
|
||||
.installed_path = null,
|
||||
|
@ -1704,13 +1708,23 @@ pub const LibExeObjStep = struct {
|
|||
|
||||
pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
|
||||
const out = self.build_options_contents.outStream();
|
||||
if (T == []const []const u8) {
|
||||
out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{name}) catch unreachable;
|
||||
for (value) |slice| {
|
||||
out.writeAll(" ") catch unreachable;
|
||||
std.zig.renderStringLiteral(slice, out) catch unreachable;
|
||||
out.writeAll(",\n") catch unreachable;
|
||||
}
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
return;
|
||||
}
|
||||
switch (@typeInfo(T)) {
|
||||
.Enum => |enum_info| {
|
||||
out.print("const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
|
||||
out.print("pub const {} = enum {{\n", .{@typeName(T)}) catch unreachable;
|
||||
inline for (enum_info.fields) |field| {
|
||||
out.print(" {},\n", .{field.name}) catch unreachable;
|
||||
}
|
||||
out.print("}};\n", .{}) catch unreachable;
|
||||
out.writeAll("};\n") catch unreachable;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
@ -1843,10 +1857,10 @@ pub const LibExeObjStep = struct {
|
|||
zig_args.append(builder.zig_exe) catch unreachable;
|
||||
|
||||
const cmd = switch (self.kind) {
|
||||
Kind.Lib => "build-lib",
|
||||
Kind.Exe => "build-exe",
|
||||
Kind.Obj => "build-obj",
|
||||
Kind.Test => "test",
|
||||
.Lib => "build-lib",
|
||||
.Exe => "build-exe",
|
||||
.Obj => "build-obj",
|
||||
.Test => "test",
|
||||
};
|
||||
zig_args.append(cmd) catch unreachable;
|
||||
|
||||
|
@ -1994,6 +2008,9 @@ pub const LibExeObjStep = struct {
|
|||
if (self.disable_sanitize_c) {
|
||||
try zig_args.append("-fno-sanitize-c");
|
||||
}
|
||||
if (self.rdynamic) {
|
||||
try zig_args.append("-rdynamic");
|
||||
}
|
||||
|
||||
if (self.code_model != .default) {
|
||||
try zig_args.append("-code-model");
|
||||
|
|
|
@ -46,9 +46,10 @@ const BinaryElfOutput = struct {
|
|||
.segments = ArrayList(*BinaryElfSegment).init(allocator),
|
||||
.sections = ArrayList(*BinaryElfSection).init(allocator),
|
||||
};
|
||||
const elf_hdrs = try std.elf.readAllHeaders(allocator, elf_file);
|
||||
const elf_hdr = try std.elf.readHeader(elf_file);
|
||||
|
||||
for (elf_hdrs.section_headers) |section, i| {
|
||||
var section_headers = elf_hdr.section_header_iterator(elf_file);
|
||||
while (try section_headers.next()) |section| {
|
||||
if (sectionValidForOutput(section)) {
|
||||
const newSection = try allocator.create(BinaryElfSection);
|
||||
|
||||
|
@ -61,7 +62,8 @@ const BinaryElfOutput = struct {
|
|||
}
|
||||
}
|
||||
|
||||
for (elf_hdrs.program_headers) |phdr, i| {
|
||||
var program_headers = elf_hdr.program_header_iterator(elf_file);
|
||||
while (try program_headers.next()) |phdr| {
|
||||
if (phdr.p_type == elf.PT_LOAD) {
|
||||
const newSegment = try allocator.create(BinaryElfSegment);
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ const build = std.build;
|
|||
const Step = build.Step;
|
||||
const Builder = build.Builder;
|
||||
const LibExeObjStep = build.LibExeObjStep;
|
||||
const WriteFileStep = build.WriteFileStep;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
const process = std.process;
|
||||
|
@ -42,6 +43,10 @@ pub const RunStep = struct {
|
|||
|
||||
pub const Arg = union(enum) {
|
||||
Artifact: *LibExeObjStep,
|
||||
WriteFile: struct {
|
||||
step: *WriteFileStep,
|
||||
file_name: []const u8,
|
||||
},
|
||||
Bytes: []u8,
|
||||
};
|
||||
|
||||
|
@ -62,6 +67,16 @@ pub const RunStep = struct {
|
|||
self.step.dependOn(&artifact.step);
|
||||
}
|
||||
|
||||
pub fn addWriteFileArg(self: *RunStep, write_file: *WriteFileStep, file_name: []const u8) void {
|
||||
self.argv.append(Arg{
|
||||
.WriteFile = .{
|
||||
.step = write_file,
|
||||
.file_name = file_name,
|
||||
},
|
||||
}) catch unreachable;
|
||||
self.step.dependOn(&write_file.step);
|
||||
}
|
||||
|
||||
pub fn addArg(self: *RunStep, arg: []const u8) void {
|
||||
self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable;
|
||||
}
|
||||
|
@ -142,6 +157,9 @@ pub const RunStep = struct {
|
|||
for (self.argv.span()) |arg| {
|
||||
switch (arg) {
|
||||
Arg.Bytes => |bytes| try argv_list.append(bytes),
|
||||
Arg.WriteFile => |file| {
|
||||
try argv_list.append(file.step.getOutputPath(file.file_name));
|
||||
},
|
||||
Arg.Artifact => |artifact| {
|
||||
if (artifact.target.isWindows()) {
|
||||
// On Windows we don't have rpaths so we have to add .dll search paths to PATH
|
||||
|
|
|
@ -8,6 +8,10 @@ pub const Tokenizer = tokenizer.Tokenizer;
|
|||
pub const parse = @import("c/parse.zig").parse;
|
||||
pub const ast = @import("c/ast.zig");
|
||||
|
||||
test "" {
|
||||
_ = tokenizer;
|
||||
}
|
||||
|
||||
pub usingnamespace @import("os/bits.zig");
|
||||
|
||||
pub usingnamespace switch (std.Target.current.os.tag) {
|
||||
|
|
|
@ -1,19 +1,10 @@
|
|||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
pub const Source = struct {
|
||||
buffer: []const u8,
|
||||
file_name: []const u8,
|
||||
tokens: TokenList,
|
||||
|
||||
pub const TokenList = std.SegmentedList(Token, 64);
|
||||
};
|
||||
|
||||
pub const Token = struct {
|
||||
id: Id,
|
||||
start: usize,
|
||||
end: usize,
|
||||
source: *Source,
|
||||
|
||||
pub const Id = union(enum) {
|
||||
Invalid,
|
||||
|
@ -251,31 +242,6 @@ pub const Token = struct {
|
|||
}
|
||||
};
|
||||
|
||||
pub fn eql(a: Token, b: Token) bool {
|
||||
// do we really need this cast here
|
||||
if (@as(@TagType(Id), a.id) != b.id) return false;
|
||||
return mem.eql(u8, a.slice(), b.slice());
|
||||
}
|
||||
|
||||
pub fn slice(tok: Token) []const u8 {
|
||||
return tok.source.buffer[tok.start..tok.end];
|
||||
}
|
||||
|
||||
pub const Keyword = struct {
|
||||
bytes: []const u8,
|
||||
id: Id,
|
||||
hash: u32,
|
||||
|
||||
fn init(bytes: []const u8, id: Id) Keyword {
|
||||
@setEvalBranchQuota(2000);
|
||||
return .{
|
||||
.bytes = bytes,
|
||||
.id = id,
|
||||
.hash = std.hash_map.hashString(bytes),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
// TODO extensions
|
||||
pub const keywords = std.ComptimeStringMap(Id, .{
|
||||
.{ "auto", .Keyword_auto },
|
||||
|
@ -355,26 +321,26 @@ pub const Token = struct {
|
|||
}
|
||||
|
||||
pub const NumSuffix = enum {
|
||||
None,
|
||||
F,
|
||||
L,
|
||||
U,
|
||||
LU,
|
||||
LL,
|
||||
LLU,
|
||||
none,
|
||||
f,
|
||||
l,
|
||||
u,
|
||||
lu,
|
||||
ll,
|
||||
llu,
|
||||
};
|
||||
|
||||
pub const StrKind = enum {
|
||||
None,
|
||||
Wide,
|
||||
Utf8,
|
||||
Utf16,
|
||||
Utf32,
|
||||
none,
|
||||
wide,
|
||||
utf_8,
|
||||
utf_16,
|
||||
utf_32,
|
||||
};
|
||||
};
|
||||
|
||||
pub const Tokenizer = struct {
|
||||
source: *Source,
|
||||
buffer: []const u8,
|
||||
index: usize = 0,
|
||||
prev_tok_id: @TagType(Token.Id) = .Invalid,
|
||||
pp_directive: bool = false,
|
||||
|
@ -385,7 +351,6 @@ pub const Tokenizer = struct {
|
|||
.id = .Eof,
|
||||
.start = self.index,
|
||||
.end = undefined,
|
||||
.source = self.source,
|
||||
};
|
||||
var state: enum {
|
||||
Start,
|
||||
|
@ -446,8 +411,8 @@ pub const Tokenizer = struct {
|
|||
} = .Start;
|
||||
var string = false;
|
||||
var counter: u32 = 0;
|
||||
while (self.index < self.source.buffer.len) : (self.index += 1) {
|
||||
const c = self.source.buffer[self.index];
|
||||
while (self.index < self.buffer.len) : (self.index += 1) {
|
||||
const c = self.buffer[self.index];
|
||||
switch (state) {
|
||||
.Start => switch (c) {
|
||||
'\n' => {
|
||||
|
@ -460,11 +425,11 @@ pub const Tokenizer = struct {
|
|||
state = .Cr;
|
||||
},
|
||||
'"' => {
|
||||
result.id = .{ .StringLiteral = .None };
|
||||
result.id = .{ .StringLiteral = .none };
|
||||
state = .StringLiteral;
|
||||
},
|
||||
'\'' => {
|
||||
result.id = .{ .CharLiteral = .None };
|
||||
result.id = .{ .CharLiteral = .none };
|
||||
state = .CharLiteralStart;
|
||||
},
|
||||
'u' => {
|
||||
|
@ -641,11 +606,11 @@ pub const Tokenizer = struct {
|
|||
state = .u8;
|
||||
},
|
||||
'\'' => {
|
||||
result.id = .{ .CharLiteral = .Utf16 };
|
||||
result.id = .{ .CharLiteral = .utf_16 };
|
||||
state = .CharLiteralStart;
|
||||
},
|
||||
'\"' => {
|
||||
result.id = .{ .StringLiteral = .Utf16 };
|
||||
result.id = .{ .StringLiteral = .utf_16 };
|
||||
state = .StringLiteral;
|
||||
},
|
||||
else => {
|
||||
|
@ -655,7 +620,7 @@ pub const Tokenizer = struct {
|
|||
},
|
||||
.u8 => switch (c) {
|
||||
'\"' => {
|
||||
result.id = .{ .StringLiteral = .Utf8 };
|
||||
result.id = .{ .StringLiteral = .utf_8 };
|
||||
state = .StringLiteral;
|
||||
},
|
||||
else => {
|
||||
|
@ -665,11 +630,11 @@ pub const Tokenizer = struct {
|
|||
},
|
||||
.U => switch (c) {
|
||||
'\'' => {
|
||||
result.id = .{ .CharLiteral = .Utf32 };
|
||||
result.id = .{ .CharLiteral = .utf_32 };
|
||||
state = .CharLiteralStart;
|
||||
},
|
||||
'\"' => {
|
||||
result.id = .{ .StringLiteral = .Utf32 };
|
||||
result.id = .{ .StringLiteral = .utf_32 };
|
||||
state = .StringLiteral;
|
||||
},
|
||||
else => {
|
||||
|
@ -679,11 +644,11 @@ pub const Tokenizer = struct {
|
|||
},
|
||||
.L => switch (c) {
|
||||
'\'' => {
|
||||
result.id = .{ .CharLiteral = .Wide };
|
||||
result.id = .{ .CharLiteral = .wide };
|
||||
state = .CharLiteralStart;
|
||||
},
|
||||
'\"' => {
|
||||
result.id = .{ .StringLiteral = .Wide };
|
||||
result.id = .{ .StringLiteral = .wide };
|
||||
state = .StringLiteral;
|
||||
},
|
||||
else => {
|
||||
|
@ -808,7 +773,7 @@ pub const Tokenizer = struct {
|
|||
.Identifier => switch (c) {
|
||||
'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
|
||||
else => {
|
||||
result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
|
||||
result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
|
||||
if (self.prev_tok_id == .Hash)
|
||||
self.pp_directive = true;
|
||||
break;
|
||||
|
@ -1137,7 +1102,7 @@ pub const Tokenizer = struct {
|
|||
state = .IntegerSuffixL;
|
||||
},
|
||||
else => {
|
||||
result.id = .{ .IntegerLiteral = .None };
|
||||
result.id = .{ .IntegerLiteral = .none };
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
@ -1146,7 +1111,7 @@ pub const Tokenizer = struct {
|
|||
state = .IntegerSuffixUL;
|
||||
},
|
||||
else => {
|
||||
result.id = .{ .IntegerLiteral = .U };
|
||||
result.id = .{ .IntegerLiteral = .u };
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
@ -1155,34 +1120,34 @@ pub const Tokenizer = struct {
|
|||
state = .IntegerSuffixLL;
|
||||
},
|
||||
'u', 'U' => {
|
||||
result.id = .{ .IntegerLiteral = .LU };
|
||||
result.id = .{ .IntegerLiteral = .lu };
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = .{ .IntegerLiteral = .L };
|
||||
result.id = .{ .IntegerLiteral = .l };
|
||||
break;
|
||||
},
|
||||
},
|
||||
.IntegerSuffixLL => switch (c) {
|
||||
'u', 'U' => {
|
||||
result.id = .{ .IntegerLiteral = .LLU };
|
||||
result.id = .{ .IntegerLiteral = .llu };
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = .{ .IntegerLiteral = .LL };
|
||||
result.id = .{ .IntegerLiteral = .ll };
|
||||
break;
|
||||
},
|
||||
},
|
||||
.IntegerSuffixUL => switch (c) {
|
||||
'l', 'L' => {
|
||||
result.id = .{ .IntegerLiteral = .LLU };
|
||||
result.id = .{ .IntegerLiteral = .llu };
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = .{ .IntegerLiteral = .LU };
|
||||
result.id = .{ .IntegerLiteral = .lu };
|
||||
break;
|
||||
},
|
||||
},
|
||||
|
@ -1230,26 +1195,26 @@ pub const Tokenizer = struct {
|
|||
},
|
||||
.FloatSuffix => switch (c) {
|
||||
'l', 'L' => {
|
||||
result.id = .{ .FloatLiteral = .L };
|
||||
result.id = .{ .FloatLiteral = .l };
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
'f', 'F' => {
|
||||
result.id = .{ .FloatLiteral = .F };
|
||||
result.id = .{ .FloatLiteral = .f };
|
||||
self.index += 1;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
result.id = .{ .FloatLiteral = .None };
|
||||
result.id = .{ .FloatLiteral = .none };
|
||||
break;
|
||||
},
|
||||
},
|
||||
}
|
||||
} else if (self.index == self.source.buffer.len) {
|
||||
} else if (self.index == self.buffer.len) {
|
||||
switch (state) {
|
||||
.Start => {},
|
||||
.u, .u8, .U, .L, .Identifier => {
|
||||
result.id = Token.getKeyword(self.source.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
|
||||
result.id = Token.getKeyword(self.buffer[result.start..self.index], self.prev_tok_id == .Hash and !self.pp_directive) orelse .Identifier;
|
||||
},
|
||||
|
||||
.Cr,
|
||||
|
@ -1270,11 +1235,11 @@ pub const Tokenizer = struct {
|
|||
.MacroString,
|
||||
=> result.id = .Invalid,
|
||||
|
||||
.FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .None },
|
||||
.FloatExponentDigits => result.id = if (counter == 0) .Invalid else .{ .FloatLiteral = .none },
|
||||
|
||||
.FloatFraction,
|
||||
.FloatFractionHex,
|
||||
=> result.id = .{ .FloatLiteral = .None },
|
||||
=> result.id = .{ .FloatLiteral = .none },
|
||||
|
||||
.IntegerLiteralOct,
|
||||
.IntegerLiteralBinary,
|
||||
|
@ -1282,13 +1247,13 @@ pub const Tokenizer = struct {
|
|||
.IntegerLiteral,
|
||||
.IntegerSuffix,
|
||||
.Zero,
|
||||
=> result.id = .{ .IntegerLiteral = .None },
|
||||
.IntegerSuffixU => result.id = .{ .IntegerLiteral = .U },
|
||||
.IntegerSuffixL => result.id = .{ .IntegerLiteral = .L },
|
||||
.IntegerSuffixLL => result.id = .{ .IntegerLiteral = .LL },
|
||||
.IntegerSuffixUL => result.id = .{ .IntegerLiteral = .LU },
|
||||
=> result.id = .{ .IntegerLiteral = .none },
|
||||
.IntegerSuffixU => result.id = .{ .IntegerLiteral = .u },
|
||||
.IntegerSuffixL => result.id = .{ .IntegerLiteral = .l },
|
||||
.IntegerSuffixLL => result.id = .{ .IntegerLiteral = .ll },
|
||||
.IntegerSuffixUL => result.id = .{ .IntegerLiteral = .lu },
|
||||
|
||||
.FloatSuffix => result.id = .{ .FloatLiteral = .None },
|
||||
.FloatSuffix => result.id = .{ .FloatLiteral = .none },
|
||||
.Equal => result.id = .Equal,
|
||||
.Bang => result.id = .Bang,
|
||||
.Minus => result.id = .Minus,
|
||||
|
@ -1466,7 +1431,7 @@ test "preprocessor keywords" {
|
|||
.Hash,
|
||||
.Identifier,
|
||||
.AngleBracketLeft,
|
||||
.{ .IntegerLiteral = .None },
|
||||
.{ .IntegerLiteral = .none },
|
||||
.Nl,
|
||||
.Hash,
|
||||
.Keyword_ifdef,
|
||||
|
@ -1499,18 +1464,18 @@ test "line continuation" {
|
|||
.Identifier,
|
||||
.Identifier,
|
||||
.Nl,
|
||||
.{ .StringLiteral = .None },
|
||||
.{ .StringLiteral = .none },
|
||||
.Nl,
|
||||
.Hash,
|
||||
.Keyword_define,
|
||||
.{ .StringLiteral = .None },
|
||||
.{ .StringLiteral = .none },
|
||||
.Nl,
|
||||
.{ .StringLiteral = .None },
|
||||
.{ .StringLiteral = .none },
|
||||
.Nl,
|
||||
.Hash,
|
||||
.Keyword_define,
|
||||
.{ .StringLiteral = .None },
|
||||
.{ .StringLiteral = .None },
|
||||
.{ .StringLiteral = .none },
|
||||
.{ .StringLiteral = .none },
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1527,23 +1492,23 @@ test "string prefix" {
|
|||
\\L'foo'
|
||||
\\
|
||||
, &[_]Token.Id{
|
||||
.{ .StringLiteral = .None },
|
||||
.{ .StringLiteral = .none },
|
||||
.Nl,
|
||||
.{ .StringLiteral = .Utf16 },
|
||||
.{ .StringLiteral = .utf_16 },
|
||||
.Nl,
|
||||
.{ .StringLiteral = .Utf8 },
|
||||
.{ .StringLiteral = .utf_8 },
|
||||
.Nl,
|
||||
.{ .StringLiteral = .Utf32 },
|
||||
.{ .StringLiteral = .utf_32 },
|
||||
.Nl,
|
||||
.{ .StringLiteral = .Wide },
|
||||
.{ .StringLiteral = .wide },
|
||||
.Nl,
|
||||
.{ .CharLiteral = .None },
|
||||
.{ .CharLiteral = .none },
|
||||
.Nl,
|
||||
.{ .CharLiteral = .Utf16 },
|
||||
.{ .CharLiteral = .utf_16 },
|
||||
.Nl,
|
||||
.{ .CharLiteral = .Utf32 },
|
||||
.{ .CharLiteral = .utf_32 },
|
||||
.Nl,
|
||||
.{ .CharLiteral = .Wide },
|
||||
.{ .CharLiteral = .wide },
|
||||
.Nl,
|
||||
});
|
||||
}
|
||||
|
@ -1555,33 +1520,29 @@ test "num suffixes" {
|
|||
\\ 1u 1ul 1ull 1
|
||||
\\
|
||||
, &[_]Token.Id{
|
||||
.{ .FloatLiteral = .F },
|
||||
.{ .FloatLiteral = .L },
|
||||
.{ .FloatLiteral = .None },
|
||||
.{ .FloatLiteral = .None },
|
||||
.{ .FloatLiteral = .None },
|
||||
.{ .FloatLiteral = .f },
|
||||
.{ .FloatLiteral = .l },
|
||||
.{ .FloatLiteral = .none },
|
||||
.{ .FloatLiteral = .none },
|
||||
.{ .FloatLiteral = .none },
|
||||
.Nl,
|
||||
.{ .IntegerLiteral = .L },
|
||||
.{ .IntegerLiteral = .LU },
|
||||
.{ .IntegerLiteral = .LL },
|
||||
.{ .IntegerLiteral = .LLU },
|
||||
.{ .IntegerLiteral = .None },
|
||||
.{ .IntegerLiteral = .l },
|
||||
.{ .IntegerLiteral = .lu },
|
||||
.{ .IntegerLiteral = .ll },
|
||||
.{ .IntegerLiteral = .llu },
|
||||
.{ .IntegerLiteral = .none },
|
||||
.Nl,
|
||||
.{ .IntegerLiteral = .U },
|
||||
.{ .IntegerLiteral = .LU },
|
||||
.{ .IntegerLiteral = .LLU },
|
||||
.{ .IntegerLiteral = .None },
|
||||
.{ .IntegerLiteral = .u },
|
||||
.{ .IntegerLiteral = .lu },
|
||||
.{ .IntegerLiteral = .llu },
|
||||
.{ .IntegerLiteral = .none },
|
||||
.Nl,
|
||||
});
|
||||
}
|
||||
|
||||
fn expectTokens(source: []const u8, expected_tokens: []const Token.Id) void {
|
||||
var tokenizer = Tokenizer{
|
||||
.source = &Source{
|
||||
.buffer = source,
|
||||
.file_name = undefined,
|
||||
.tokens = undefined,
|
||||
},
|
||||
.buffer = source,
|
||||
};
|
||||
for (expected_tokens) |expected_token_id| {
|
||||
const token = tokenizer.next();
|
||||
|
|
|
@ -364,6 +364,7 @@ pub const ChildProcess = struct {
|
|||
error.FileTooBig => unreachable,
|
||||
error.DeviceBusy => unreachable,
|
||||
error.FileLocksNotSupported => unreachable,
|
||||
error.BadPathName => unreachable, // Windows-only
|
||||
else => |e| return e,
|
||||
}
|
||||
else
|
||||
|
@ -480,25 +481,20 @@ pub const ChildProcess = struct {
|
|||
|
||||
const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
|
||||
|
||||
// TODO use CreateFileW here since we are using a string literal for the path
|
||||
const nul_handle = if (any_ignore)
|
||||
windows.CreateFile(
|
||||
"NUL",
|
||||
windows.GENERIC_READ,
|
||||
windows.FILE_SHARE_READ,
|
||||
null,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_ATTRIBUTE_NORMAL,
|
||||
null,
|
||||
) catch |err| switch (err) {
|
||||
error.SharingViolation => unreachable, // not possible for "NUL"
|
||||
windows.OpenFile(&[_]u16{ 'N', 'U', 'L' }, .{
|
||||
.dir = std.fs.cwd().fd,
|
||||
.access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
|
||||
.share_access = windows.FILE_SHARE_READ,
|
||||
.creation = windows.OPEN_EXISTING,
|
||||
.io_mode = .blocking,
|
||||
}) catch |err| switch (err) {
|
||||
error.PathAlreadyExists => unreachable, // not possible for "NUL"
|
||||
error.PipeBusy => unreachable, // not possible for "NUL"
|
||||
error.InvalidUtf8 => unreachable, // not possible for "NUL"
|
||||
error.BadPathName => unreachable, // not possible for "NUL"
|
||||
error.FileNotFound => unreachable, // not possible for "NUL"
|
||||
error.AccessDenied => unreachable, // not possible for "NUL"
|
||||
error.NameTooLong => unreachable, // not possible for "NUL"
|
||||
error.WouldBlock => unreachable, // not possible for "NUL"
|
||||
else => |e| return e,
|
||||
}
|
||||
else
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
const mem = @import("../mem.zig");
|
||||
const math = @import("../math.zig");
|
||||
const endian = @import("../endian.zig");
|
||||
const debug = @import("../debug.zig");
|
||||
const builtin = @import("builtin");
|
||||
const debug = @import("../debug.zig");
|
||||
const math = @import("../math.zig");
|
||||
const htest = @import("test.zig");
|
||||
|
||||
const RoundParam = struct {
|
||||
|
@ -31,7 +30,7 @@ fn Rp(a: usize, b: usize, c: usize, d: usize, x: usize, y: usize) RoundParam {
|
|||
pub const Blake2s224 = Blake2s(224);
|
||||
pub const Blake2s256 = Blake2s(256);
|
||||
|
||||
fn Blake2s(comptime out_len: usize) type {
|
||||
pub fn Blake2s(comptime out_len: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const block_length = 64;
|
||||
|
@ -67,10 +66,17 @@ fn Blake2s(comptime out_len: usize) type {
|
|||
buf: [64]u8,
|
||||
buf_len: u8,
|
||||
|
||||
key: []const u8,
|
||||
|
||||
pub fn init() Self {
|
||||
return init_keyed("");
|
||||
}
|
||||
|
||||
pub fn init_keyed(key: []const u8) Self {
|
||||
debug.assert(8 <= out_len and out_len <= 512);
|
||||
|
||||
var s: Self = undefined;
|
||||
s.key = key;
|
||||
s.reset();
|
||||
return s;
|
||||
}
|
||||
|
@ -78,14 +84,24 @@ fn Blake2s(comptime out_len: usize) type {
|
|||
pub fn reset(d: *Self) void {
|
||||
mem.copy(u32, d.h[0..], iv[0..]);
|
||||
|
||||
// No key plus default parameters
|
||||
d.h[0] ^= 0x01010000 ^ @intCast(u32, out_len >> 3);
|
||||
// default parameters
|
||||
d.h[0] ^= 0x01010000 ^ @truncate(u32, d.key.len << 8) ^ @intCast(u32, out_len >> 3);
|
||||
d.t = 0;
|
||||
d.buf_len = 0;
|
||||
|
||||
if (d.key.len > 0) {
|
||||
mem.set(u8, d.buf[d.key.len..], 0);
|
||||
d.update(d.key);
|
||||
d.buf_len = 64;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash(b: []const u8, out: []u8) void {
|
||||
var d = Self.init();
|
||||
Self.hash_keyed("", b, out);
|
||||
}
|
||||
|
||||
pub fn hash_keyed(key: []const u8, b: []const u8, out: []u8) void {
|
||||
var d = Self.init_keyed(key);
|
||||
d.update(b);
|
||||
d.final(out);
|
||||
}
|
||||
|
@ -94,7 +110,7 @@ fn Blake2s(comptime out_len: usize) type {
|
|||
var off: usize = 0;
|
||||
|
||||
// Partial buffer exists from previous update. Copy into buffer then hash.
|
||||
if (d.buf_len != 0 and d.buf_len + b.len >= 64) {
|
||||
if (d.buf_len != 0 and d.buf_len + b.len > 64) {
|
||||
off += 64 - d.buf_len;
|
||||
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
|
||||
d.t += 64;
|
||||
|
@ -103,7 +119,7 @@ fn Blake2s(comptime out_len: usize) type {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 64 <= b.len) : (off += 64) {
|
||||
while (off + 64 < b.len) : (off += 64) {
|
||||
d.t += 64;
|
||||
d.round(b[off .. off + 64], false);
|
||||
}
|
||||
|
@ -123,7 +139,7 @@ fn Blake2s(comptime out_len: usize) type {
|
|||
const rr = d.h[0 .. out_len / 32];
|
||||
|
||||
for (rr) |s, j| {
|
||||
mem.writeIntLittle(u32, out[4 * j ..][0..4], s);
|
||||
mem.writeIntSliceLittle(u32, out[4 * j ..], s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -188,6 +204,9 @@ test "blake2s224 single" {
|
|||
|
||||
const h3 = "e4e5cb6c7cae41982b397bf7b7d2d9d1949823ae78435326e8db4912";
|
||||
htest.assertEqualHash(Blake2s224, h3, "The quick brown fox jumps over the lazy dog");
|
||||
|
||||
const h4 = "557381a78facd2b298640f4e32113e58967d61420af1aa939d0cfe01";
|
||||
htest.assertEqualHash(Blake2s224, h4, "a" ** 32 ++ "b" ** 32);
|
||||
}
|
||||
|
||||
test "blake2s224 streaming" {
|
||||
|
@ -212,6 +231,37 @@ test "blake2s224 streaming" {
|
|||
h.update("c");
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
|
||||
const h3 = "557381a78facd2b298640f4e32113e58967d61420af1aa939d0cfe01";
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 32);
|
||||
h.update("b" ** 32);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 32 ++ "b" ** 32);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
}
|
||||
|
||||
test "comptime blake2s224" {
|
||||
comptime {
|
||||
@setEvalBranchQuota(6000);
|
||||
var block = [_]u8{0} ** Blake2s224.block_length;
|
||||
var out: [Blake2s224.digest_length]u8 = undefined;
|
||||
|
||||
const h1 = "86b7611563293f8c73627df7a6d6ba25ca0548c2a6481f7d116ee576";
|
||||
|
||||
htest.assertEqualHash(Blake2s224, h1, block[0..]);
|
||||
|
||||
var h = Blake2s224.init();
|
||||
h.update(&block);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
}
|
||||
}
|
||||
|
||||
test "blake2s256 single" {
|
||||
|
@ -223,6 +273,9 @@ test "blake2s256 single" {
|
|||
|
||||
const h3 = "606beeec743ccbeff6cbcdf5d5302aa855c256c29b88c8ed331ea1a6bf3c8812";
|
||||
htest.assertEqualHash(Blake2s256, h3, "The quick brown fox jumps over the lazy dog");
|
||||
|
||||
const h4 = "8d8711dade07a6b92b9a3ea1f40bee9b2c53ff3edd2a273dec170b0163568977";
|
||||
htest.assertEqualHash(Blake2s256, h4, "a" ** 32 ++ "b" ** 32);
|
||||
}
|
||||
|
||||
test "blake2s256 streaming" {
|
||||
|
@ -247,15 +300,60 @@ test "blake2s256 streaming" {
|
|||
h.update("c");
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
|
||||
const h3 = "8d8711dade07a6b92b9a3ea1f40bee9b2c53ff3edd2a273dec170b0163568977";
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 32);
|
||||
h.update("b" ** 32);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 32 ++ "b" ** 32);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
}
|
||||
|
||||
test "blake2s256 aligned final" {
|
||||
var block = [_]u8{0} ** Blake2s256.block_length;
|
||||
var out: [Blake2s256.digest_length]u8 = undefined;
|
||||
test "blake2s256 keyed" {
|
||||
var out: [32]u8 = undefined;
|
||||
|
||||
var h = Blake2s256.init();
|
||||
h.update(&block);
|
||||
const h1 = "10f918da4d74fab3302e48a5d67d03804b1ec95372a62a0f33b7c9fa28ba1ae6";
|
||||
const key = "secret_key";
|
||||
|
||||
Blake2s256.hash_keyed(key, "a" ** 64 ++ "b" ** 64, &out);
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
|
||||
var h = Blake2s256.init_keyed(key);
|
||||
h.update("a" ** 64 ++ "b" ** 64);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 64);
|
||||
h.update("b" ** 64);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
}
|
||||
|
||||
test "comptime blake2s256" {
|
||||
comptime {
|
||||
@setEvalBranchQuota(6000);
|
||||
var block = [_]u8{0} ** Blake2s256.block_length;
|
||||
var out: [Blake2s256.digest_length]u8 = undefined;
|
||||
|
||||
const h1 = "ae09db7cd54f42b490ef09b6bc541af688e4959bb8c53f359a6f56e38ab454a3";
|
||||
|
||||
htest.assertEqualHash(Blake2s256, h1, block[0..]);
|
||||
|
||||
var h = Blake2s256.init();
|
||||
h.update(&block);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
|
@ -264,7 +362,7 @@ test "blake2s256 aligned final" {
|
|||
pub const Blake2b384 = Blake2b(384);
|
||||
pub const Blake2b512 = Blake2b(512);
|
||||
|
||||
fn Blake2b(comptime out_len: usize) type {
|
||||
pub fn Blake2b(comptime out_len: usize) type {
|
||||
return struct {
|
||||
const Self = @This();
|
||||
pub const block_length = 128;
|
||||
|
@ -302,10 +400,17 @@ fn Blake2b(comptime out_len: usize) type {
|
|||
buf: [128]u8,
|
||||
buf_len: u8,
|
||||
|
||||
key: []const u8,
|
||||
|
||||
pub fn init() Self {
|
||||
return init_keyed("");
|
||||
}
|
||||
|
||||
pub fn init_keyed(key: []const u8) Self {
|
||||
debug.assert(8 <= out_len and out_len <= 512);
|
||||
|
||||
var s: Self = undefined;
|
||||
s.key = key;
|
||||
s.reset();
|
||||
return s;
|
||||
}
|
||||
|
@ -313,14 +418,24 @@ fn Blake2b(comptime out_len: usize) type {
|
|||
pub fn reset(d: *Self) void {
|
||||
mem.copy(u64, d.h[0..], iv[0..]);
|
||||
|
||||
// No key plus default parameters
|
||||
d.h[0] ^= 0x01010000 ^ (out_len >> 3);
|
||||
// default parameters
|
||||
d.h[0] ^= 0x01010000 ^ (d.key.len << 8) ^ (out_len >> 3);
|
||||
d.t = 0;
|
||||
d.buf_len = 0;
|
||||
|
||||
if (d.key.len > 0) {
|
||||
mem.set(u8, d.buf[d.key.len..], 0);
|
||||
d.update(d.key);
|
||||
d.buf_len = 128;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hash(b: []const u8, out: []u8) void {
|
||||
var d = Self.init();
|
||||
Self.hash_keyed("", b, out);
|
||||
}
|
||||
|
||||
pub fn hash_keyed(key: []const u8, b: []const u8, out: []u8) void {
|
||||
var d = Self.init_keyed(key);
|
||||
d.update(b);
|
||||
d.final(out);
|
||||
}
|
||||
|
@ -329,7 +444,7 @@ fn Blake2b(comptime out_len: usize) type {
|
|||
var off: usize = 0;
|
||||
|
||||
// Partial buffer exists from previous update. Copy into buffer then hash.
|
||||
if (d.buf_len != 0 and d.buf_len + b.len >= 128) {
|
||||
if (d.buf_len != 0 and d.buf_len + b.len > 128) {
|
||||
off += 128 - d.buf_len;
|
||||
mem.copy(u8, d.buf[d.buf_len..], b[0..off]);
|
||||
d.t += 128;
|
||||
|
@ -338,7 +453,7 @@ fn Blake2b(comptime out_len: usize) type {
|
|||
}
|
||||
|
||||
// Full middle blocks.
|
||||
while (off + 128 <= b.len) : (off += 128) {
|
||||
while (off + 128 < b.len) : (off += 128) {
|
||||
d.t += 128;
|
||||
d.round(b[off .. off + 128], false);
|
||||
}
|
||||
|
@ -356,7 +471,7 @@ fn Blake2b(comptime out_len: usize) type {
|
|||
const rr = d.h[0 .. out_len / 64];
|
||||
|
||||
for (rr) |s, j| {
|
||||
mem.writeIntLittle(u64, out[8 * j ..][0..8], s);
|
||||
mem.writeIntSliceLittle(u64, out[8 * j ..], s);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,6 +536,9 @@ test "blake2b384 single" {
|
|||
|
||||
const h3 = "b7c81b228b6bd912930e8f0b5387989691c1cee1e65aade4da3b86a3c9f678fc8018f6ed9e2906720c8d2a3aeda9c03d";
|
||||
htest.assertEqualHash(Blake2b384, h3, "The quick brown fox jumps over the lazy dog");
|
||||
|
||||
const h4 = "b7283f0172fecbbd7eca32ce10d8a6c06b453cb3cf675b33eb4246f0da2bb94a6c0bdd6eec0b5fd71ec4fd51be80bf4c";
|
||||
htest.assertEqualHash(Blake2b384, h4, "a" ** 64 ++ "b" ** 64);
|
||||
}
|
||||
|
||||
test "blake2b384 streaming" {
|
||||
|
@ -445,6 +563,37 @@ test "blake2b384 streaming" {
|
|||
h.update("c");
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
|
||||
const h3 = "b7283f0172fecbbd7eca32ce10d8a6c06b453cb3cf675b33eb4246f0da2bb94a6c0bdd6eec0b5fd71ec4fd51be80bf4c";
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 64 ++ "b" ** 64);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 64);
|
||||
h.update("b" ** 64);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
}
|
||||
|
||||
test "comptime blake2b384" {
|
||||
comptime {
|
||||
@setEvalBranchQuota(7000);
|
||||
var block = [_]u8{0} ** Blake2b384.block_length;
|
||||
var out: [Blake2b384.digest_length]u8 = undefined;
|
||||
|
||||
const h1 = "e8aa1931ea0422e4446fecdd25c16cf35c240b10cb4659dd5c776eddcaa4d922397a589404b46eb2e53d78132d05fd7d";
|
||||
|
||||
htest.assertEqualHash(Blake2b384, h1, block[0..]);
|
||||
|
||||
var h = Blake2b384.init();
|
||||
h.update(&block);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
}
|
||||
}
|
||||
|
||||
test "blake2b512 single" {
|
||||
|
@ -456,6 +605,9 @@ test "blake2b512 single" {
|
|||
|
||||
const h3 = "a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918";
|
||||
htest.assertEqualHash(Blake2b512, h3, "The quick brown fox jumps over the lazy dog");
|
||||
|
||||
const h4 = "049980af04d6a2cf16b4b49793c3ed7e40732073788806f2c989ebe9547bda0541d63abe298ec8955d08af48ae731f2e8a0bd6d201655a5473b4aa79d211b920";
|
||||
htest.assertEqualHash(Blake2b512, h4, "a" ** 64 ++ "b" ** 64);
|
||||
}
|
||||
|
||||
test "blake2b512 streaming" {
|
||||
|
@ -480,13 +632,58 @@ test "blake2b512 streaming" {
|
|||
h.update("c");
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h2, out[0..]);
|
||||
}
|
||||
|
||||
test "blake2b512 aligned final" {
|
||||
var block = [_]u8{0} ** Blake2b512.block_length;
|
||||
var out: [Blake2b512.digest_length]u8 = undefined;
|
||||
const h3 = "049980af04d6a2cf16b4b49793c3ed7e40732073788806f2c989ebe9547bda0541d63abe298ec8955d08af48ae731f2e8a0bd6d201655a5473b4aa79d211b920";
|
||||
|
||||
var h = Blake2b512.init();
|
||||
h.update(&block);
|
||||
h.reset();
|
||||
h.update("a" ** 64 ++ "b" ** 64);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 64);
|
||||
h.update("b" ** 64);
|
||||
h.final(out[0..]);
|
||||
htest.assertEqual(h3, out[0..]);
|
||||
}
|
||||
|
||||
test "blake2b512 keyed" {
|
||||
var out: [64]u8 = undefined;
|
||||
|
||||
const h1 = "8a978060ccaf582f388f37454363071ac9a67e3a704585fd879fb8a419a447e389c7c6de790faa20a7a7dccf197de736bc5b40b98a930b36df5bee7555750c4d";
|
||||
const key = "secret_key";
|
||||
|
||||
Blake2b512.hash_keyed(key, "a" ** 64 ++ "b" ** 64, &out);
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
|
||||
var h = Blake2b512.init_keyed(key);
|
||||
h.update("a" ** 64 ++ "b" ** 64);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
|
||||
h.reset();
|
||||
h.update("a" ** 64);
|
||||
h.update("b" ** 64);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
}
|
||||
|
||||
test "comptime blake2b512" {
|
||||
comptime {
|
||||
@setEvalBranchQuota(8000);
|
||||
var block = [_]u8{0} ** Blake2b512.block_length;
|
||||
var out: [Blake2b512.digest_length]u8 = undefined;
|
||||
|
||||
const h1 = "865939e120e6805438478841afb739ae4250cf372653078a065cdcfffca4caf798e6d462b65d658fc165782640eded70963449ae1500fb0f24981d7727e22c41";
|
||||
|
||||
htest.assertEqualHash(Blake2b512, h1, block[0..]);
|
||||
|
||||
var h = Blake2b512.init();
|
||||
h.update(&block);
|
||||
h.final(out[0..]);
|
||||
|
||||
htest.assertEqual(h1, out[0..]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,6 +159,50 @@ pub fn writeILEB128Mem(ptr: []u8, int_value: anytype) !usize {
|
|||
return buf.pos;
|
||||
}
|
||||
|
||||
/// This is an "advanced" function. It allows one to use a fixed amount of memory to store a
|
||||
/// ULEB128. This defeats the entire purpose of using this data encoding; it will no longer use
|
||||
/// fewer bytes to store smaller numbers. The advantage of using a fixed width is that it makes
|
||||
/// fields have a predictable size and so depending on the use case this tradeoff can be worthwhile.
|
||||
/// An example use case of this is in emitting DWARF info where one wants to make a ULEB128 field
|
||||
/// "relocatable", meaning that it becomes possible to later go back and patch the number to be a
|
||||
/// different value without shifting all the following code.
|
||||
pub fn writeUnsignedFixed(comptime l: usize, ptr: *[l]u8, int: std.meta.Int(false, l * 7)) void {
|
||||
const T = @TypeOf(int);
|
||||
const U = if (T.bit_count < 8) u8 else T;
|
||||
var value = @intCast(U, int);
|
||||
|
||||
comptime var i = 0;
|
||||
inline while (i < (l - 1)) : (i += 1) {
|
||||
const byte = @truncate(u8, value) | 0b1000_0000;
|
||||
value >>= 7;
|
||||
ptr[i] = byte;
|
||||
}
|
||||
ptr[i] = @truncate(u8, value);
|
||||
}
|
||||
|
||||
test "writeUnsignedFixed" {
|
||||
{
|
||||
var buf: [4]u8 = undefined;
|
||||
writeUnsignedFixed(4, &buf, 0);
|
||||
testing.expect((try test_read_uleb128(u64, &buf)) == 0);
|
||||
}
|
||||
{
|
||||
var buf: [4]u8 = undefined;
|
||||
writeUnsignedFixed(4, &buf, 1);
|
||||
testing.expect((try test_read_uleb128(u64, &buf)) == 1);
|
||||
}
|
||||
{
|
||||
var buf: [4]u8 = undefined;
|
||||
writeUnsignedFixed(4, &buf, 1000);
|
||||
testing.expect((try test_read_uleb128(u64, &buf)) == 1000);
|
||||
}
|
||||
{
|
||||
var buf: [4]u8 = undefined;
|
||||
writeUnsignedFixed(4, &buf, 10000000);
|
||||
testing.expect((try test_read_uleb128(u64, &buf)) == 10000000);
|
||||
}
|
||||
}
|
||||
|
||||
// tests
|
||||
fn test_read_stream_ileb128(comptime T: type, encoded: []const u8) !T {
|
||||
var reader = std.io.fixedBufferStream(encoded);
|
||||
|
|
|
@ -9,7 +9,7 @@ const leb = @import("debug/leb128.zig");
|
|||
|
||||
const ArrayList = std.ArrayList;
|
||||
|
||||
usingnamespace @import("dwarf_bits.zig");
|
||||
pub usingnamespace @import("dwarf_bits.zig");
|
||||
|
||||
const PcRange = struct {
|
||||
start: u64,
|
||||
|
|
|
@ -680,3 +680,20 @@ pub const LANG_HP_Basic91 = 0x8004;
|
|||
pub const LANG_HP_Pascal91 = 0x8005;
|
||||
pub const LANG_HP_IMacro = 0x8006;
|
||||
pub const LANG_HP_Assembler = 0x8007;
|
||||
|
||||
pub const UT_compile = 0x01;
|
||||
pub const UT_type = 0x02;
|
||||
pub const UT_partial = 0x03;
|
||||
pub const UT_skeleton = 0x04;
|
||||
pub const UT_split_compile = 0x05;
|
||||
pub const UT_split_type = 0x06;
|
||||
pub const UT_lo_user = 0x80;
|
||||
pub const UT_hi_user = 0xff;
|
||||
|
||||
pub const LNCT_path = 0x1;
|
||||
pub const LNCT_directory_index = 0x2;
|
||||
pub const LNCT_timestamp = 0x3;
|
||||
pub const LNCT_size = 0x4;
|
||||
pub const LNCT_MD5 = 0x5;
|
||||
pub const LNCT_lo_user = 0x2000;
|
||||
pub const LNCT_hi_user = 0x3fff;
|
||||
|
|
267
lib/std/elf.zig
267
lib/std/elf.zig
|
@ -341,6 +341,20 @@ const Header = struct {
|
|||
shentsize: u16,
|
||||
shnum: u16,
|
||||
shstrndx: u16,
|
||||
|
||||
pub fn program_header_iterator(self: Header, file: File) ProgramHeaderIterator {
|
||||
return .{
|
||||
.elf_header = self,
|
||||
.file = file,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn section_header_iterator(self: Header, file: File) SectionHeaderIterator {
|
||||
return .{
|
||||
.elf_header = self,
|
||||
.file = file,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn readHeader(file: File) !Header {
|
||||
|
@ -378,144 +392,137 @@ pub fn readHeader(file: File) !Header {
|
|||
});
|
||||
}
|
||||
|
||||
/// All integers are native endian.
|
||||
pub const AllHeaders = struct {
|
||||
header: Header,
|
||||
section_headers: []Elf64_Shdr,
|
||||
program_headers: []Elf64_Phdr,
|
||||
allocator: *mem.Allocator,
|
||||
pub const ProgramHeaderIterator = struct {
|
||||
elf_header: Header,
|
||||
file: File,
|
||||
index: usize = 0,
|
||||
|
||||
pub fn next(self: *ProgramHeaderIterator) !?Elf64_Phdr {
|
||||
if (self.index >= self.elf_header.phnum) return null;
|
||||
defer self.index += 1;
|
||||
|
||||
if (self.elf_header.is_64) {
|
||||
var phdr: Elf64_Phdr = undefined;
|
||||
const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
|
||||
try preadNoEof(self.file, mem.asBytes(&phdr), offset);
|
||||
|
||||
// ELF endianness matches native endianness.
|
||||
if (self.elf_header.endian == std.builtin.endian) return phdr;
|
||||
|
||||
// Convert fields to native endianness.
|
||||
return Elf64_Phdr{
|
||||
.p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
|
||||
.p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
|
||||
.p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
|
||||
.p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
|
||||
.p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
|
||||
.p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
|
||||
.p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
|
||||
.p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
|
||||
};
|
||||
}
|
||||
|
||||
var phdr: Elf32_Phdr = undefined;
|
||||
const offset = self.elf_header.phoff + @sizeOf(@TypeOf(phdr)) * self.index;
|
||||
try preadNoEof(self.file, mem.asBytes(&phdr), offset);
|
||||
|
||||
// ELF endianness does NOT match native endianness.
|
||||
if (self.elf_header.endian != std.builtin.endian) {
|
||||
// Convert fields to native endianness.
|
||||
phdr = .{
|
||||
.p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
|
||||
.p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
|
||||
.p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
|
||||
.p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
|
||||
.p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
|
||||
.p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
|
||||
.p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
|
||||
.p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
|
||||
};
|
||||
}
|
||||
|
||||
// Convert 32-bit header to 64-bit.
|
||||
return Elf64_Phdr{
|
||||
.p_type = phdr.p_type,
|
||||
.p_offset = phdr.p_offset,
|
||||
.p_vaddr = phdr.p_vaddr,
|
||||
.p_paddr = phdr.p_paddr,
|
||||
.p_filesz = phdr.p_filesz,
|
||||
.p_memsz = phdr.p_memsz,
|
||||
.p_flags = phdr.p_flags,
|
||||
.p_align = phdr.p_align,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn readAllHeaders(allocator: *mem.Allocator, file: File) !AllHeaders {
|
||||
var hdrs: AllHeaders = .{
|
||||
.allocator = allocator,
|
||||
.header = try readHeader(file),
|
||||
.section_headers = undefined,
|
||||
.program_headers = undefined,
|
||||
};
|
||||
const is_64 = hdrs.header.is_64;
|
||||
const need_bswap = hdrs.header.endian != std.builtin.endian;
|
||||
pub const SectionHeaderIterator = struct {
|
||||
elf_header: Header,
|
||||
file: File,
|
||||
index: usize = 0,
|
||||
|
||||
hdrs.section_headers = try allocator.alloc(Elf64_Shdr, hdrs.header.shnum);
|
||||
errdefer allocator.free(hdrs.section_headers);
|
||||
pub fn next(self: *SectionHeaderIterator) !?Elf64_Shdr {
|
||||
if (self.index >= self.elf_header.shnum) return null;
|
||||
defer self.index += 1;
|
||||
|
||||
hdrs.program_headers = try allocator.alloc(Elf64_Phdr, hdrs.header.phnum);
|
||||
errdefer allocator.free(hdrs.program_headers);
|
||||
if (self.elf_header.is_64) {
|
||||
var shdr: Elf64_Shdr = undefined;
|
||||
const offset = self.elf_header.phoff + @sizeOf(@TypeOf(shdr)) * self.index;
|
||||
try preadNoEof(self.file, mem.asBytes(&shdr), offset);
|
||||
|
||||
// If the ELF file is 64-bit and same-endianness, then all we have to do is
|
||||
// yeet the bytes into memory.
|
||||
// If only the endianness is different, they can be simply byte swapped.
|
||||
if (is_64) {
|
||||
const shdr_buf = std.mem.sliceAsBytes(hdrs.section_headers);
|
||||
const phdr_buf = std.mem.sliceAsBytes(hdrs.program_headers);
|
||||
try preadNoEof(file, shdr_buf, hdrs.header.shoff);
|
||||
try preadNoEof(file, phdr_buf, hdrs.header.phoff);
|
||||
// ELF endianness matches native endianness.
|
||||
if (self.elf_header.endian == std.builtin.endian) return shdr;
|
||||
|
||||
if (need_bswap) {
|
||||
for (hdrs.section_headers) |*shdr| {
|
||||
shdr.* = .{
|
||||
.sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
|
||||
.sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
|
||||
.sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
|
||||
.sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
|
||||
.sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
|
||||
.sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
|
||||
.sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
|
||||
.sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
|
||||
.sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
|
||||
.sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
|
||||
};
|
||||
}
|
||||
for (hdrs.program_headers) |*phdr| {
|
||||
phdr.* = .{
|
||||
.p_type = @byteSwap(@TypeOf(phdr.p_type), phdr.p_type),
|
||||
.p_offset = @byteSwap(@TypeOf(phdr.p_offset), phdr.p_offset),
|
||||
.p_vaddr = @byteSwap(@TypeOf(phdr.p_vaddr), phdr.p_vaddr),
|
||||
.p_paddr = @byteSwap(@TypeOf(phdr.p_paddr), phdr.p_paddr),
|
||||
.p_filesz = @byteSwap(@TypeOf(phdr.p_filesz), phdr.p_filesz),
|
||||
.p_memsz = @byteSwap(@TypeOf(phdr.p_memsz), phdr.p_memsz),
|
||||
.p_flags = @byteSwap(@TypeOf(phdr.p_flags), phdr.p_flags),
|
||||
.p_align = @byteSwap(@TypeOf(phdr.p_align), phdr.p_align),
|
||||
};
|
||||
}
|
||||
// Convert fields to native endianness.
|
||||
return Elf64_Shdr{
|
||||
.sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
|
||||
.sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
|
||||
.sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
|
||||
.sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
|
||||
.sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
|
||||
.sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
|
||||
.sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
|
||||
.sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
|
||||
.sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
|
||||
.sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
|
||||
};
|
||||
}
|
||||
|
||||
return hdrs;
|
||||
var shdr: Elf32_Shdr = undefined;
|
||||
const offset = self.elf_header.shoff + @sizeOf(@TypeOf(shdr)) * self.index;
|
||||
try preadNoEof(self.file, mem.asBytes(&shdr), offset);
|
||||
|
||||
// ELF endianness does NOT match native endianness.
|
||||
if (self.elf_header.endian != std.builtin.endian) {
|
||||
// Convert fields to native endianness.
|
||||
shdr = .{
|
||||
.sh_name = @byteSwap(@TypeOf(shdr.sh_name), shdr.sh_name),
|
||||
.sh_type = @byteSwap(@TypeOf(shdr.sh_type), shdr.sh_type),
|
||||
.sh_flags = @byteSwap(@TypeOf(shdr.sh_flags), shdr.sh_flags),
|
||||
.sh_addr = @byteSwap(@TypeOf(shdr.sh_addr), shdr.sh_addr),
|
||||
.sh_offset = @byteSwap(@TypeOf(shdr.sh_offset), shdr.sh_offset),
|
||||
.sh_size = @byteSwap(@TypeOf(shdr.sh_size), shdr.sh_size),
|
||||
.sh_link = @byteSwap(@TypeOf(shdr.sh_link), shdr.sh_link),
|
||||
.sh_info = @byteSwap(@TypeOf(shdr.sh_info), shdr.sh_info),
|
||||
.sh_addralign = @byteSwap(@TypeOf(shdr.sh_addralign), shdr.sh_addralign),
|
||||
.sh_entsize = @byteSwap(@TypeOf(shdr.sh_entsize), shdr.sh_entsize),
|
||||
};
|
||||
}
|
||||
|
||||
// Convert 32-bit header to 64-bit.
|
||||
return Elf64_Shdr{
|
||||
.sh_name = shdr.sh_name,
|
||||
.sh_type = shdr.sh_type,
|
||||
.sh_flags = shdr.sh_flags,
|
||||
.sh_addr = shdr.sh_addr,
|
||||
.sh_offset = shdr.sh_offset,
|
||||
.sh_size = shdr.sh_size,
|
||||
.sh_link = shdr.sh_link,
|
||||
.sh_info = shdr.sh_info,
|
||||
.sh_addralign = shdr.sh_addralign,
|
||||
.sh_entsize = shdr.sh_entsize,
|
||||
};
|
||||
}
|
||||
|
||||
const shdrs_32 = try allocator.alloc(Elf32_Shdr, hdrs.header.shnum);
|
||||
defer allocator.free(shdrs_32);
|
||||
|
||||
const phdrs_32 = try allocator.alloc(Elf32_Phdr, hdrs.header.phnum);
|
||||
defer allocator.free(phdrs_32);
|
||||
|
||||
const shdr_buf = std.mem.sliceAsBytes(shdrs_32);
|
||||
const phdr_buf = std.mem.sliceAsBytes(phdrs_32);
|
||||
try preadNoEof(file, shdr_buf, hdrs.header.shoff);
|
||||
try preadNoEof(file, phdr_buf, hdrs.header.phoff);
|
||||
|
||||
if (need_bswap) {
|
||||
for (hdrs.section_headers) |*shdr, i| {
|
||||
const o = shdrs_32[i];
|
||||
shdr.* = .{
|
||||
.sh_name = @byteSwap(@TypeOf(o.sh_name), o.sh_name),
|
||||
.sh_type = @byteSwap(@TypeOf(o.sh_type), o.sh_type),
|
||||
.sh_flags = @byteSwap(@TypeOf(o.sh_flags), o.sh_flags),
|
||||
.sh_addr = @byteSwap(@TypeOf(o.sh_addr), o.sh_addr),
|
||||
.sh_offset = @byteSwap(@TypeOf(o.sh_offset), o.sh_offset),
|
||||
.sh_size = @byteSwap(@TypeOf(o.sh_size), o.sh_size),
|
||||
.sh_link = @byteSwap(@TypeOf(o.sh_link), o.sh_link),
|
||||
.sh_info = @byteSwap(@TypeOf(o.sh_info), o.sh_info),
|
||||
.sh_addralign = @byteSwap(@TypeOf(o.sh_addralign), o.sh_addralign),
|
||||
.sh_entsize = @byteSwap(@TypeOf(o.sh_entsize), o.sh_entsize),
|
||||
};
|
||||
}
|
||||
for (hdrs.program_headers) |*phdr, i| {
|
||||
const o = phdrs_32[i];
|
||||
phdr.* = .{
|
||||
.p_type = @byteSwap(@TypeOf(o.p_type), o.p_type),
|
||||
.p_offset = @byteSwap(@TypeOf(o.p_offset), o.p_offset),
|
||||
.p_vaddr = @byteSwap(@TypeOf(o.p_vaddr), o.p_vaddr),
|
||||
.p_paddr = @byteSwap(@TypeOf(o.p_paddr), o.p_paddr),
|
||||
.p_filesz = @byteSwap(@TypeOf(o.p_filesz), o.p_filesz),
|
||||
.p_memsz = @byteSwap(@TypeOf(o.p_memsz), o.p_memsz),
|
||||
.p_flags = @byteSwap(@TypeOf(o.p_flags), o.p_flags),
|
||||
.p_align = @byteSwap(@TypeOf(o.p_align), o.p_align),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
for (hdrs.section_headers) |*shdr, i| {
|
||||
const o = shdrs_32[i];
|
||||
shdr.* = .{
|
||||
.sh_name = o.sh_name,
|
||||
.sh_type = o.sh_type,
|
||||
.sh_flags = o.sh_flags,
|
||||
.sh_addr = o.sh_addr,
|
||||
.sh_offset = o.sh_offset,
|
||||
.sh_size = o.sh_size,
|
||||
.sh_link = o.sh_link,
|
||||
.sh_info = o.sh_info,
|
||||
.sh_addralign = o.sh_addralign,
|
||||
.sh_entsize = o.sh_entsize,
|
||||
};
|
||||
}
|
||||
for (hdrs.program_headers) |*phdr, i| {
|
||||
const o = phdrs_32[i];
|
||||
phdr.* = .{
|
||||
.p_type = o.p_type,
|
||||
.p_offset = o.p_offset,
|
||||
.p_vaddr = o.p_vaddr,
|
||||
.p_paddr = o.p_paddr,
|
||||
.p_filesz = o.p_filesz,
|
||||
.p_memsz = o.p_memsz,
|
||||
.p_flags = o.p_flags,
|
||||
.p_align = o.p_align,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return hdrs;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn int(is_64: bool, need_bswap: bool, int_32: anytype, int_64: anytype) @TypeOf(int_64) {
|
||||
if (is_64) {
|
||||
|
@ -538,7 +545,7 @@ pub fn int32(need_bswap: bool, int_32: anytype, comptime Int64: anytype) Int64 {
|
|||
}
|
||||
|
||||
fn preadNoEof(file: std.fs.File, buf: []u8, offset: u64) !void {
|
||||
var i: u64 = 0;
|
||||
var i: usize = 0;
|
||||
while (i < buf.len) {
|
||||
const len = file.pread(buf[i .. buf.len - i], offset + i) catch |err| switch (err) {
|
||||
error.SystemResources => return error.SystemResources,
|
||||
|
|
|
@ -224,6 +224,7 @@ pub fn LinearFifo(
|
|||
pub fn reader(self: *Self) std.io.Reader(*Self, error{}, readFn) {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
/// Deprecated: `use reader`
|
||||
pub fn inStream(self: *Self) std.io.InStream(*Self, error{}, readFn) {
|
||||
return .{ .context = self };
|
||||
|
@ -315,6 +316,11 @@ pub fn LinearFifo(
|
|||
return bytes.len;
|
||||
}
|
||||
|
||||
pub fn writer(self: *Self) std.io.Writer(*Self, error{OutOfMemory}, appendWrite) {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
||||
/// Deprecated: `use writer`
|
||||
pub fn outStream(self: *Self) std.io.OutStream(*Self, error{OutOfMemory}, appendWrite) {
|
||||
return .{ .context = self };
|
||||
}
|
||||
|
@ -426,14 +432,14 @@ test "LinearFifo(u8, .Dynamic)" {
|
|||
fifo.shrink(0);
|
||||
|
||||
{
|
||||
try fifo.outStream().print("{}, {}!", .{ "Hello", "World" });
|
||||
try fifo.writer().print("{}, {}!", .{ "Hello", "World" });
|
||||
var result: [30]u8 = undefined;
|
||||
testing.expectEqualSlices(u8, "Hello, World!", result[0..fifo.read(&result)]);
|
||||
testing.expectEqual(@as(usize, 0), fifo.readableLength());
|
||||
}
|
||||
|
||||
{
|
||||
try fifo.outStream().writeAll("This is a test");
|
||||
try fifo.writer().writeAll("This is a test");
|
||||
var result: [30]u8 = undefined;
|
||||
testing.expectEqualSlices(u8, "This", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
|
||||
testing.expectEqualSlices(u8, "is", (try fifo.reader().readUntilDelimiterOrEof(&result, ' ')).?);
|
||||
|
|
|
@ -88,6 +88,8 @@ pub fn format(
|
|||
if (args.len > ArgSetType.bit_count) {
|
||||
@compileError("32 arguments max are supported per format call");
|
||||
}
|
||||
if (args.len == 0)
|
||||
return writer.writeAll(fmt);
|
||||
|
||||
const State = enum {
|
||||
Start,
|
||||
|
|
|
@ -84,7 +84,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
|
|||
try crypto.randomBytes(rand_buf[0..]);
|
||||
base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf);
|
||||
|
||||
if (cwd().symLink(existing_path, new_path, .{})) {
|
||||
if (cwd().symLink(existing_path, tmp_path, .{})) {
|
||||
return rename(tmp_path, new_path);
|
||||
} else |err| switch (err) {
|
||||
error.PathAlreadyExists => continue,
|
||||
|
@ -225,8 +225,7 @@ pub fn makeDirAbsoluteZ(absolute_path_z: [*:0]const u8) !void {
|
|||
/// Same as `makeDirAbsolute` except the parameter is a null-terminated WTF-16 encoded string.
|
||||
pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
|
||||
assert(path.isAbsoluteWindowsW(absolute_path_w));
|
||||
const handle = try os.windows.CreateDirectoryW(null, absolute_path_w, null);
|
||||
os.windows.CloseHandle(handle);
|
||||
return os.mkdirW(absolute_path_w, default_new_dir_mode);
|
||||
}
|
||||
|
||||
pub const deleteDir = @compileError("deprecated; use dir.deleteDir or deleteDirAbsolute");
|
||||
|
@ -881,8 +880,7 @@ pub const Dir = struct {
|
|||
}
|
||||
|
||||
pub fn makeDirW(self: Dir, sub_path: [*:0]const u16) !void {
|
||||
const handle = try os.windows.CreateDirectoryW(self.fd, sub_path, null);
|
||||
os.windows.CloseHandle(handle);
|
||||
try os.mkdiratW(self.fd, sub_path, default_new_dir_mode);
|
||||
}
|
||||
|
||||
/// Calls makeDir recursively to make an entire path. Returns success if the path
|
||||
|
@ -1119,7 +1117,7 @@ pub const Dir = struct {
|
|||
pub fn deleteFile(self: Dir, sub_path: []const u8) DeleteFileError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.deleteFileW(sub_path_w.span().ptr);
|
||||
return self.deleteFileW(sub_path_w.span());
|
||||
} else if (builtin.os.tag == .wasi) {
|
||||
os.unlinkatWasi(self.fd, sub_path, 0) catch |err| switch (err) {
|
||||
error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
|
||||
|
@ -1153,7 +1151,7 @@ pub const Dir = struct {
|
|||
}
|
||||
|
||||
/// Same as `deleteFile` except the parameter is WTF-16 encoded.
|
||||
pub fn deleteFileW(self: Dir, sub_path_w: [*:0]const u16) DeleteFileError!void {
|
||||
pub fn deleteFileW(self: Dir, sub_path_w: []const u16) DeleteFileError!void {
|
||||
os.unlinkatW(self.fd, sub_path_w, 0) catch |err| switch (err) {
|
||||
error.DirNotEmpty => unreachable, // not passing AT_REMOVEDIR
|
||||
else => |e| return e,
|
||||
|
@ -1182,7 +1180,7 @@ pub const Dir = struct {
|
|||
pub fn deleteDir(self: Dir, sub_path: []const u8) DeleteDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.deleteDirW(sub_path_w.span().ptr);
|
||||
return self.deleteDirW(sub_path_w.span());
|
||||
} else if (builtin.os.tag == .wasi) {
|
||||
os.unlinkat(self.fd, sub_path, os.AT_REMOVEDIR) catch |err| switch (err) {
|
||||
error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
|
||||
|
@ -1204,7 +1202,7 @@ pub const Dir = struct {
|
|||
|
||||
/// Same as `deleteDir` except the parameter is UTF16LE, NT prefixed.
|
||||
/// This function is Windows-only.
|
||||
pub fn deleteDirW(self: Dir, sub_path_w: [*:0]const u16) DeleteDirError!void {
|
||||
pub fn deleteDirW(self: Dir, sub_path_w: []const u16) DeleteDirError!void {
|
||||
os.unlinkatW(self.fd, sub_path_w, os.AT_REMOVEDIR) catch |err| switch (err) {
|
||||
error.IsDir => unreachable, // not possible since we pass AT_REMOVEDIR
|
||||
else => |e| return e,
|
||||
|
@ -1263,11 +1261,11 @@ pub const Dir = struct {
|
|||
/// are null-terminated, WTF16 encoded.
|
||||
pub fn symLinkW(
|
||||
self: Dir,
|
||||
target_path_w: [:0]const u16,
|
||||
sym_link_path_w: [:0]const u16,
|
||||
target_path_w: []const u16,
|
||||
sym_link_path_w: []const u16,
|
||||
flags: SymLinkFlags,
|
||||
) !void {
|
||||
return os.windows.CreateSymbolicLinkW(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
return os.windows.CreateSymbolicLink(self.fd, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
}
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
|
@ -1278,7 +1276,8 @@ pub const Dir = struct {
|
|||
return self.readLinkWasi(sub_path, buffer);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
return os.windows.ReadLink(self.fd, sub_path, buffer);
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.readLinkW(sub_path_w.span(), buffer);
|
||||
}
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.readLinkZ(&sub_path_c, buffer);
|
||||
|
@ -1295,15 +1294,15 @@ pub const Dir = struct {
|
|||
pub fn readLinkZ(self: Dir, sub_path_c: [*:0]const u8, buffer: []u8) ![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
|
||||
return self.readLinkW(sub_path_w, buffer);
|
||||
return self.readLinkW(sub_path_w.span(), buffer);
|
||||
}
|
||||
return os.readlinkatZ(self.fd, sub_path_c, buffer);
|
||||
}
|
||||
|
||||
/// Windows-only. Same as `readLink` except the pathname parameter
|
||||
/// is null-terminated, WTF16 encoded.
|
||||
pub fn readLinkW(self: Dir, sub_path_w: [*:0]const u16, buffer: []u8) ![]u8 {
|
||||
return os.windows.ReadLinkW(self.fd, sub_path_w, buffer);
|
||||
pub fn readLinkW(self: Dir, sub_path_w: []const u16, buffer: []u8) ![]u8 {
|
||||
return os.windows.ReadLink(self.fd, sub_path_w, buffer);
|
||||
}
|
||||
|
||||
/// On success, caller owns returned buffer.
|
||||
|
@ -1813,7 +1812,9 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
|
|||
assert(path.isAbsolute(target_path));
|
||||
assert(path.isAbsolute(sym_link_path));
|
||||
if (builtin.os.tag == .windows) {
|
||||
return os.windows.CreateSymbolicLink(null, sym_link_path, target_path, flags.is_directory);
|
||||
const target_path_w = try os.windows.sliceToPrefixedFileW(target_path);
|
||||
const sym_link_path_w = try os.windows.sliceToPrefixedFileW(sym_link_path);
|
||||
return os.windows.CreateSymbolicLink(null, sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
|
||||
}
|
||||
return os.symlink(target_path, sym_link_path);
|
||||
}
|
||||
|
@ -1822,10 +1823,10 @@ pub fn symLinkAbsolute(target_path: []const u8, sym_link_path: []const u8, flags
|
|||
/// Note that this function will by default try creating a symbolic link to a file. If you would
|
||||
/// like to create a symbolic link to a directory, specify this with `SymLinkFlags{ .is_directory = true }`.
|
||||
/// See also `symLinkAbsolute`, `symLinkAbsoluteZ`.
|
||||
pub fn symLinkAbsoluteW(target_path_w: [:0]const u16, sym_link_path_w: [:0]const u16, flags: SymLinkFlags) !void {
|
||||
assert(path.isAbsoluteWindowsW(target_path_w));
|
||||
assert(path.isAbsoluteWindowsW(sym_link_path_w));
|
||||
return os.windows.CreateSymbolicLinkW(null, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
pub fn symLinkAbsoluteW(target_path_w: []const u16, sym_link_path_w: []const u16, flags: SymLinkFlags) !void {
|
||||
assert(path.isAbsoluteWindowsWTF16(target_path_w));
|
||||
assert(path.isAbsoluteWindowsWTF16(sym_link_path_w));
|
||||
return os.windows.CreateSymbolicLink(null, sym_link_path_w, target_path_w, flags.is_directory);
|
||||
}
|
||||
|
||||
/// Same as `symLinkAbsolute` except the parameters are null-terminated pointers.
|
||||
|
@ -1836,7 +1837,7 @@ pub fn symLinkAbsoluteZ(target_path_c: [*:0]const u8, sym_link_path_c: [*:0]cons
|
|||
if (builtin.os.tag == .windows) {
|
||||
const target_path_w = try os.windows.cStrToWin32PrefixedFileW(target_path_c);
|
||||
const sym_link_path_w = try os.windows.cStrToWin32PrefixedFileW(sym_link_path_c);
|
||||
return os.windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, flags.is_directory);
|
||||
return os.windows.CreateSymbolicLink(sym_link_path_w.span(), target_path_w.span(), flags.is_directory);
|
||||
}
|
||||
return os.symlinkZ(target_path_c, sym_link_path_c);
|
||||
}
|
||||
|
@ -1938,7 +1939,20 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
|
|||
return walker;
|
||||
}
|
||||
|
||||
pub const OpenSelfExeError = os.OpenError || os.windows.CreateFileError || SelfExePathError || os.FlockError;
|
||||
pub const OpenSelfExeError = error{
|
||||
SharingViolation,
|
||||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
NameTooLong,
|
||||
/// On Windows, file paths must be valid Unicode.
|
||||
InvalidUtf8,
|
||||
/// On Windows, file paths cannot contain these characters:
|
||||
/// '/', '*', '?', '"', '<', '>', '|'
|
||||
BadPathName,
|
||||
Unexpected,
|
||||
} || os.OpenError || SelfExePathError || os.FlockError;
|
||||
|
||||
pub fn openSelfExe(flags: File.OpenFlags) OpenSelfExeError!File {
|
||||
if (builtin.os.tag == .linux) {
|
||||
|
|
|
@ -47,7 +47,20 @@ pub const File = struct {
|
|||
else => 0o666,
|
||||
};
|
||||
|
||||
pub const OpenError = windows.CreateFileError || os.OpenError || os.FlockError;
|
||||
pub const OpenError = error{
|
||||
SharingViolation,
|
||||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
NameTooLong,
|
||||
/// On Windows, file paths must be valid Unicode.
|
||||
InvalidUtf8,
|
||||
/// On Windows, file paths cannot contain these characters:
|
||||
/// '/', '*', '?', '"', '<', '>', '|'
|
||||
BadPathName,
|
||||
Unexpected,
|
||||
} || os.OpenError || os.FlockError;
|
||||
|
||||
pub const Lock = enum { None, Shared, Exclusive };
|
||||
|
||||
|
|
|
@ -374,15 +374,13 @@ pub fn Watch(comptime V: type) type {
|
|||
defer if (!basename_utf16le_null_consumed) self.allocator.free(basename_utf16le_null);
|
||||
const basename_utf16le_no_null = basename_utf16le_null[0 .. basename_utf16le_null.len - 1];
|
||||
|
||||
const dir_handle = try windows.CreateFileW(
|
||||
dirname_utf16le.ptr,
|
||||
windows.FILE_LIST_DIRECTORY,
|
||||
windows.FILE_SHARE_READ | windows.FILE_SHARE_DELETE | windows.FILE_SHARE_WRITE,
|
||||
null,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_FLAG_BACKUP_SEMANTICS | windows.FILE_FLAG_OVERLAPPED,
|
||||
null,
|
||||
);
|
||||
const dir_handle = try windows.OpenFile(dirname_utf16le, .{
|
||||
.dir = std.fs.cwd().fd,
|
||||
.access_mask = windows.FILE_LIST_DIRECTORY,
|
||||
.creation = windows.FILE_OPEN,
|
||||
.io_mode = .blocking,
|
||||
.open_dir = true,
|
||||
});
|
||||
var dir_handle_consumed = false;
|
||||
defer if (!dir_handle_consumed) windows.CloseHandle(dir_handle);
|
||||
|
||||
|
|
|
@ -56,9 +56,6 @@ pub fn hashPointer(hasher: anytype, key: anytype, comptime strat: HashStrategy)
|
|||
pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
|
||||
switch (strat) {
|
||||
.Shallow => {
|
||||
// TODO detect via a trait when Key has no padding bits to
|
||||
// hash it as an array of bytes.
|
||||
// Otherwise, hash every element.
|
||||
for (key) |element| {
|
||||
hash(hasher, element, .Shallow);
|
||||
}
|
||||
|
@ -75,30 +72,34 @@ pub fn hashArray(hasher: anytype, key: anytype, comptime strat: HashStrategy) vo
|
|||
/// Strategy is provided to determine if pointers should be followed or not.
|
||||
pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
|
||||
const Key = @TypeOf(key);
|
||||
|
||||
if (strat == .Shallow and comptime meta.trait.hasUniqueRepresentation(Key)) {
|
||||
@call(.{ .modifier = .always_inline }, hasher.update, .{mem.asBytes(&key)});
|
||||
return;
|
||||
}
|
||||
|
||||
switch (@typeInfo(Key)) {
|
||||
.NoReturn,
|
||||
.Opaque,
|
||||
.Undefined,
|
||||
.Void,
|
||||
.Null,
|
||||
.BoundFn,
|
||||
.ComptimeFloat,
|
||||
.ComptimeInt,
|
||||
.Type,
|
||||
.EnumLiteral,
|
||||
.Frame,
|
||||
.Float,
|
||||
=> @compileError("cannot hash this type"),
|
||||
|
||||
// Help the optimizer see that hashing an int is easy by inlining!
|
||||
// TODO Check if the situation is better after #561 is resolved.
|
||||
.Int => @call(.{ .modifier = .always_inline }, hasher.update, .{std.mem.asBytes(&key)}),
|
||||
|
||||
.Float => |info| hash(hasher, @bitCast(std.meta.Int(false, info.bits), key), strat),
|
||||
|
||||
.Bool => hash(hasher, @boolToInt(key), strat),
|
||||
.Enum => hash(hasher, @enumToInt(key), strat),
|
||||
.ErrorSet => hash(hasher, @errorToInt(key), strat),
|
||||
.AnyFrame, .Fn => hash(hasher, @ptrToInt(key), strat),
|
||||
.AnyFrame, .BoundFn, .Fn => hash(hasher, @ptrToInt(key), strat),
|
||||
|
||||
.Pointer => @call(.{ .modifier = .always_inline }, hashPointer, .{ hasher, key, strat }),
|
||||
|
||||
|
@ -121,9 +122,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void {
|
|||
},
|
||||
|
||||
.Struct => |info| {
|
||||
// TODO detect via a trait when Key has no padding bits to
|
||||
// hash it as an array of bytes.
|
||||
// Otherwise, hash every field.
|
||||
inline for (info.fields) |field| {
|
||||
// We reuse the hash of the previous field as the seed for the
|
||||
// next one so that they're dependant.
|
||||
|
@ -266,12 +264,12 @@ test "hash slice deep" {
|
|||
test "hash struct deep" {
|
||||
const Foo = struct {
|
||||
a: u32,
|
||||
b: f64,
|
||||
b: u16,
|
||||
c: *bool,
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub fn init(allocator: *mem.Allocator, a_: u32, b_: f64, c_: bool) !Self {
|
||||
pub fn init(allocator: *mem.Allocator, a_: u32, b_: u16, c_: bool) !Self {
|
||||
const ptr = try allocator.create(bool);
|
||||
ptr.* = c_;
|
||||
return Self{ .a = a_, .b = b_, .c = ptr };
|
||||
|
@ -279,9 +277,9 @@ test "hash struct deep" {
|
|||
};
|
||||
|
||||
const allocator = std.testing.allocator;
|
||||
const foo = try Foo.init(allocator, 123, 1.0, true);
|
||||
const bar = try Foo.init(allocator, 123, 1.0, true);
|
||||
const baz = try Foo.init(allocator, 123, 1.0, false);
|
||||
const foo = try Foo.init(allocator, 123, 10, true);
|
||||
const bar = try Foo.init(allocator, 123, 10, true);
|
||||
const baz = try Foo.init(allocator, 123, 10, false);
|
||||
defer allocator.destroy(foo.c);
|
||||
defer allocator.destroy(bar.c);
|
||||
defer allocator.destroy(baz.c);
|
||||
|
@ -338,12 +336,12 @@ test "testHash struct" {
|
|||
test "testHash union" {
|
||||
const Foo = union(enum) {
|
||||
A: u32,
|
||||
B: f32,
|
||||
B: bool,
|
||||
C: u32,
|
||||
};
|
||||
|
||||
const a = Foo{ .A = 18 };
|
||||
var b = Foo{ .B = 12.34 };
|
||||
var b = Foo{ .B = true };
|
||||
const c = Foo{ .C = 18 };
|
||||
testing.expect(testHash(a) == testHash(a));
|
||||
testing.expect(testHash(a) != testHash(b));
|
||||
|
|
|
@ -5,6 +5,7 @@ const testing = std.testing;
|
|||
const math = std.math;
|
||||
const mem = std.mem;
|
||||
const meta = std.meta;
|
||||
const trait = meta.trait;
|
||||
const autoHash = std.hash.autoHash;
|
||||
const Wyhash = std.hash.Wyhash;
|
||||
const Allocator = mem.Allocator;
|
||||
|
@ -195,6 +196,10 @@ pub fn HashMap(
|
|||
return self.unmanaged.getEntry(key);
|
||||
}
|
||||
|
||||
pub fn getIndex(self: Self, key: K) ?usize {
|
||||
return self.unmanaged.getIndex(key);
|
||||
}
|
||||
|
||||
pub fn get(self: Self, key: K) ?V {
|
||||
return self.unmanaged.get(key);
|
||||
}
|
||||
|
@ -478,17 +483,21 @@ pub fn HashMapUnmanaged(
|
|||
}
|
||||
|
||||
pub fn getEntry(self: Self, key: K) ?*Entry {
|
||||
const index = self.getIndex(key) orelse return null;
|
||||
return &self.entries.items[index];
|
||||
}
|
||||
|
||||
pub fn getIndex(self: Self, key: K) ?usize {
|
||||
const header = self.index_header orelse {
|
||||
// Linear scan.
|
||||
const h = if (store_hash) hash(key) else {};
|
||||
for (self.entries.items) |*item| {
|
||||
for (self.entries.items) |*item, i| {
|
||||
if (item.hash == h and eql(key, item.key)) {
|
||||
return item;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
switch (header.capacityIndexType()) {
|
||||
.u8 => return self.getInternal(key, header, u8),
|
||||
.u16 => return self.getInternal(key, header, u16),
|
||||
|
@ -710,7 +719,7 @@ pub fn HashMapUnmanaged(
|
|||
unreachable;
|
||||
}
|
||||
|
||||
fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry {
|
||||
fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?usize {
|
||||
const indexes = header.indexes(I);
|
||||
const h = hash(key);
|
||||
const start_index = header.constrainIndex(h);
|
||||
|
@ -724,7 +733,7 @@ pub fn HashMapUnmanaged(
|
|||
const entry = &self.entries.items[index.entry_index];
|
||||
const hash_match = if (store_hash) h == entry.hash else true;
|
||||
if (hash_match and eql(key, entry.key))
|
||||
return entry;
|
||||
return index.entry_index;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1023,9 +1032,13 @@ pub fn getTrivialEqlFn(comptime K: type) (fn (K, K) bool) {
|
|||
pub fn getAutoHashFn(comptime K: type) (fn (K) u32) {
|
||||
return struct {
|
||||
fn hash(key: K) u32 {
|
||||
var hasher = Wyhash.init(0);
|
||||
autoHash(&hasher, key);
|
||||
return @truncate(u32, hasher.final());
|
||||
if (comptime trait.hasUniqueRepresentation(K)) {
|
||||
return @truncate(u32, Wyhash.hash(0, std.mem.asBytes(&key)));
|
||||
} else {
|
||||
var hasher = Wyhash.init(0);
|
||||
autoHash(&hasher, key);
|
||||
return @truncate(u32, hasher.final());
|
||||
}
|
||||
}
|
||||
}.hash;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ pub const Mutable = struct {
|
|||
pub fn toManaged(self: Mutable, allocator: *Allocator) Managed {
|
||||
return .{
|
||||
.allocator = allocator,
|
||||
.limbs = limbs,
|
||||
.limbs = self.limbs,
|
||||
.metadata = if (self.positive)
|
||||
self.len & ~Managed.sign_bit
|
||||
else
|
||||
|
|
|
@ -2,6 +2,7 @@ const std = @import("../../std.zig");
|
|||
const mem = std.mem;
|
||||
const testing = std.testing;
|
||||
const Managed = std.math.big.int.Managed;
|
||||
const Mutable = std.math.big.int.Mutable;
|
||||
const Limb = std.math.big.Limb;
|
||||
const DoubleLimb = std.math.big.DoubleLimb;
|
||||
const maxInt = std.math.maxInt;
|
||||
|
@ -1453,3 +1454,24 @@ test "big.int gcd one large" {
|
|||
|
||||
testing.expect((try r.to(u64)) == 1);
|
||||
}
|
||||
|
||||
test "big.int mutable to managed" {
|
||||
const allocator = testing.allocator;
|
||||
var limbs_buf = try allocator.alloc(Limb, 8);
|
||||
defer allocator.free(limbs_buf);
|
||||
|
||||
var a = Mutable.init(limbs_buf, 0xdeadbeef);
|
||||
var a_managed = a.toManaged(allocator);
|
||||
|
||||
testing.expect(a.toConst().eq(a_managed.toConst()));
|
||||
}
|
||||
|
||||
test "big.int const to managed" {
|
||||
var a = try Managed.initSet(testing.allocator, 123423453456);
|
||||
defer a.deinit();
|
||||
|
||||
var b = try a.toConst().toManaged(testing.allocator);
|
||||
defer b.deinit();
|
||||
|
||||
testing.expect(a.toConst().eq(b.toConst()));
|
||||
}
|
||||
|
|
|
@ -2030,6 +2030,79 @@ test "rotate" {
|
|||
testing.expect(eql(i32, &arr, &[_]i32{ 1, 2, 4, 5, 3 }));
|
||||
}
|
||||
|
||||
/// Replace needle with replacement as many times as possible, writing to an output buffer which is assumed to be of
|
||||
/// appropriate size. Use replacementSize to calculate an appropriate buffer size.
|
||||
pub fn replace(comptime T: type, input: []const T, needle: []const T, replacement: []const T, output: []T) usize {
|
||||
var i: usize = 0;
|
||||
var slide: usize = 0;
|
||||
var replacements: usize = 0;
|
||||
while (slide < input.len) {
|
||||
if (mem.indexOf(T, input[slide..], needle) == @as(usize, 0)) {
|
||||
mem.copy(T, output[i..i + replacement.len], replacement);
|
||||
i += replacement.len;
|
||||
slide += needle.len;
|
||||
replacements += 1;
|
||||
} else {
|
||||
output[i] = input[slide];
|
||||
i += 1;
|
||||
slide += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return replacements;
|
||||
}
|
||||
|
||||
test "replace" {
|
||||
var output: [29]u8 = undefined;
|
||||
var replacements = replace(u8, "All your base are belong to us", "base", "Zig", output[0..]);
|
||||
testing.expect(replacements == 1);
|
||||
testing.expect(eql(u8, output[0..], "All your Zig are belong to us"));
|
||||
|
||||
replacements = replace(u8, "Favor reading code over writing code.", "code", "", output[0..]);
|
||||
testing.expect(replacements == 2);
|
||||
testing.expect(eql(u8, output[0..], "Favor reading over writing ."));
|
||||
}
|
||||
|
||||
/// Calculate the size needed in an output buffer to perform a replacement.
|
||||
pub fn replacementSize(comptime T: type, input: []const T, needle: []const T, replacement: []const T) usize {
|
||||
var i: usize = 0;
|
||||
var size: usize = input.len;
|
||||
while (i < input.len) : (i += 1) {
|
||||
if (mem.indexOf(T, input[i..], needle) == @as(usize, 0)) {
|
||||
size = size - needle.len + replacement.len;
|
||||
i += needle.len;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
test "replacementSize" {
|
||||
testing.expect(replacementSize(u8, "All your base are belong to us", "base", "Zig") == 29);
|
||||
testing.expect(replacementSize(u8, "", "", "") == 0);
|
||||
testing.expect(replacementSize(u8, "Favor reading code over writing code.", "code", "") == 29);
|
||||
testing.expect(replacementSize(u8, "Only one obvious way to do things.", "things.", "things in Zig.") == 41);
|
||||
}
|
||||
|
||||
/// Perform a replacement on an allocated buffer of pre-determined size. Caller must free returned memory.
|
||||
pub fn replaceOwned(comptime T: type, allocator: *Allocator, input: []const T, needle: []const T, replacement: []const T) Allocator.Error![]T {
|
||||
var output = try allocator.alloc(T, replacementSize(T, input, needle, replacement));
|
||||
_ = replace(T, input, needle, replacement, output);
|
||||
return output;
|
||||
}
|
||||
|
||||
test "replaceOwned" {
|
||||
const allocator = std.heap.page_allocator;
|
||||
|
||||
const base_replace = replaceOwned(u8, allocator, "All your base are belong to us", "base", "Zig") catch unreachable;
|
||||
defer allocator.free(base_replace);
|
||||
testing.expect(eql(u8, base_replace, "All your Zig are belong to us"));
|
||||
|
||||
const zen_replace = replaceOwned(u8, allocator, "Favor reading code over writing code.", " code", "") catch unreachable;
|
||||
defer allocator.free(zen_replace);
|
||||
testing.expect(eql(u8, zen_replace, "Favor reading over writing."));
|
||||
}
|
||||
|
||||
/// Converts a little-endian integer to host endianness.
|
||||
pub fn littleToNative(comptime T: type, x: T) T {
|
||||
return switch (builtin.endian) {
|
||||
|
|
|
@ -429,3 +429,71 @@ test "std.meta.trait.hasFunctions" {
|
|||
testing.expect(!hasFunctions(TestStruct2, .{ "a", "b", "c" }));
|
||||
testing.expect(!hasFunctions(TestStruct2, tuple));
|
||||
}
|
||||
|
||||
/// True if every value of the type `T` has a unique bit pattern representing it.
|
||||
/// In other words, `T` has no unused bits and no padding.
|
||||
pub fn hasUniqueRepresentation(comptime T: type) bool {
|
||||
switch (@typeInfo(T)) {
|
||||
else => return false, // TODO can we know if it's true for some of these types ?
|
||||
|
||||
.AnyFrame,
|
||||
.Bool,
|
||||
.BoundFn,
|
||||
.Enum,
|
||||
.ErrorSet,
|
||||
.Fn,
|
||||
.Int, // TODO check that it is still true
|
||||
.Pointer,
|
||||
=> return true,
|
||||
|
||||
.Array => |info| return comptime hasUniqueRepresentation(info.child),
|
||||
|
||||
.Struct => |info| {
|
||||
var sum_size = @as(usize, 0);
|
||||
|
||||
inline for (info.fields) |field| {
|
||||
const FieldType = field.field_type;
|
||||
if (comptime !hasUniqueRepresentation(FieldType)) return false;
|
||||
sum_size += @sizeOf(FieldType);
|
||||
}
|
||||
|
||||
return @sizeOf(T) == sum_size;
|
||||
},
|
||||
|
||||
.Vector => |info| return comptime hasUniqueRepresentation(info.child),
|
||||
}
|
||||
}
|
||||
|
||||
test "std.meta.trait.hasUniqueRepresentation" {
|
||||
const TestStruct1 = struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
};
|
||||
|
||||
testing.expect(hasUniqueRepresentation(TestStruct1));
|
||||
|
||||
const TestStruct2 = struct {
|
||||
a: u32,
|
||||
b: u16,
|
||||
};
|
||||
|
||||
testing.expect(!hasUniqueRepresentation(TestStruct2));
|
||||
|
||||
const TestStruct3 = struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
};
|
||||
|
||||
testing.expect(hasUniqueRepresentation(TestStruct3));
|
||||
|
||||
testing.expect(hasUniqueRepresentation(i1));
|
||||
testing.expect(hasUniqueRepresentation(u2));
|
||||
testing.expect(hasUniqueRepresentation(i3));
|
||||
testing.expect(hasUniqueRepresentation(u4));
|
||||
testing.expect(hasUniqueRepresentation(i5));
|
||||
testing.expect(hasUniqueRepresentation(u6));
|
||||
testing.expect(hasUniqueRepresentation(i7));
|
||||
testing.expect(hasUniqueRepresentation(u8));
|
||||
testing.expect(hasUniqueRepresentation(i9));
|
||||
testing.expect(hasUniqueRepresentation(u10));
|
||||
}
|
||||
|
|
481
lib/std/net.zig
481
lib/std/net.zig
|
@ -14,8 +14,8 @@ const has_unix_sockets = @hasDecl(os, "sockaddr_un");
|
|||
|
||||
pub const Address = extern union {
|
||||
any: os.sockaddr,
|
||||
in: os.sockaddr_in,
|
||||
in6: os.sockaddr_in6,
|
||||
in: Ip4Address,
|
||||
in6: Ip6Address,
|
||||
un: if (has_unix_sockets) os.sockaddr_un else void,
|
||||
|
||||
// TODO this crashed the compiler. https://github.com/ziglang/zig/issues/3512
|
||||
|
@ -76,19 +76,227 @@ pub const Address = extern union {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn parseIp6(buf: []const u8, port: u16) !Address {
|
||||
return Address{.in6 = try Ip6Address.parse(buf, port) };
|
||||
}
|
||||
|
||||
pub fn resolveIp6(buf: []const u8, port: u16) !Address {
|
||||
return Address{.in6 = try Ip6Address.resolve(buf, port) };
|
||||
}
|
||||
|
||||
pub fn parseIp4(buf: []const u8, port: u16) !Address {
|
||||
return Address {.in = try Ip4Address.parse(buf, port) };
|
||||
}
|
||||
|
||||
pub fn initIp4(addr: [4]u8, port: u16) Address {
|
||||
return Address{.in = Ip4Address.init(addr, port) };
|
||||
}
|
||||
|
||||
pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
|
||||
return Address{.in6 = Ip6Address.init(addr, port, flowinfo, scope_id) };
|
||||
}
|
||||
|
||||
pub fn initUnix(path: []const u8) !Address {
|
||||
var sock_addr = os.sockaddr_un{
|
||||
.family = os.AF_UNIX,
|
||||
.path = undefined,
|
||||
};
|
||||
|
||||
// this enables us to have the proper length of the socket in getOsSockLen
|
||||
mem.set(u8, &sock_addr.path, 0);
|
||||
|
||||
if (path.len > sock_addr.path.len) return error.NameTooLong;
|
||||
mem.copy(u8, &sock_addr.path, path);
|
||||
|
||||
return Address{ .un = sock_addr };
|
||||
}
|
||||
|
||||
/// Returns the port in native endian.
|
||||
/// Asserts that the address is ip4 or ip6.
|
||||
pub fn getPort(self: Address) u16 {
|
||||
return switch (self.any.family) {
|
||||
os.AF_INET => self.in.getPort(),
|
||||
os.AF_INET6 => self.in6.getPort(),
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// `port` is native-endian.
|
||||
/// Asserts that the address is ip4 or ip6.
|
||||
pub fn setPort(self: *Address, port: u16) void {
|
||||
switch (self.any.family) {
|
||||
os.AF_INET => self.in.setPort(port),
|
||||
os.AF_INET6 => self.in6.setPort(port),
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that `addr` is an IP address.
|
||||
/// This function will read past the end of the pointer, with a size depending
|
||||
/// on the address family.
|
||||
pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
|
||||
switch (addr.family) {
|
||||
os.AF_INET => return Address{ .in = Ip4Address{ .sa = @ptrCast(*const os.sockaddr_in, addr).*} },
|
||||
os.AF_INET6 => return Address{ .in6 = Ip6Address{ .sa = @ptrCast(*const os.sockaddr_in6, addr).*} },
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: Address,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
out_stream: anytype,
|
||||
) !void {
|
||||
switch (self.any.family) {
|
||||
os.AF_INET => try self.in.format(fmt, options, out_stream),
|
||||
os.AF_INET6 => try self.in6.format(fmt, options, out_stream),
|
||||
os.AF_UNIX => {
|
||||
if (!has_unix_sockets) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
try std.fmt.format(out_stream, "{}", .{&self.un.path});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eql(a: Address, b: Address) bool {
|
||||
const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];
|
||||
const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()];
|
||||
return mem.eql(u8, a_bytes, b_bytes);
|
||||
}
|
||||
|
||||
pub fn getOsSockLen(self: Address) os.socklen_t {
|
||||
switch (self.any.family) {
|
||||
os.AF_INET => return self.in.getOsSockLen(),
|
||||
os.AF_INET6 => return self.in6.getOsSockLen(),
|
||||
os.AF_UNIX => {
|
||||
if (!has_unix_sockets) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
|
||||
return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ip4Address = extern struct {
|
||||
sa: os.sockaddr_in,
|
||||
|
||||
pub fn parse(buf: []const u8, port: u16) !Ip4Address {
|
||||
var result = Ip4Address{
|
||||
.sa = .{
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.addr = undefined,
|
||||
}
|
||||
};
|
||||
const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.sa.addr)[0..]);
|
||||
|
||||
var x: u8 = 0;
|
||||
var index: u8 = 0;
|
||||
var saw_any_digits = false;
|
||||
for (buf) |c| {
|
||||
if (c == '.') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
if (index == 3) {
|
||||
return error.InvalidEnd;
|
||||
}
|
||||
out_ptr[index] = x;
|
||||
index += 1;
|
||||
x = 0;
|
||||
saw_any_digits = false;
|
||||
} else if (c >= '0' and c <= '9') {
|
||||
saw_any_digits = true;
|
||||
x = try std.math.mul(u8, x, 10);
|
||||
x = try std.math.add(u8, x, c - '0');
|
||||
} else {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
}
|
||||
if (index == 3 and saw_any_digits) {
|
||||
out_ptr[index] = x;
|
||||
return result;
|
||||
}
|
||||
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
pub fn resolveIp(name: []const u8, port: u16) !Ip4Address {
|
||||
if (parse(name, port)) |ip4| return ip4 else |err| switch (err) {
|
||||
error.Overflow,
|
||||
error.InvalidEnd,
|
||||
error.InvalidCharacter,
|
||||
error.Incomplete,
|
||||
=> {},
|
||||
}
|
||||
return error.InvalidIPAddressFormat;
|
||||
}
|
||||
|
||||
pub fn init(addr: [4]u8, port: u16) Ip4Address {
|
||||
return Ip4Address {
|
||||
.sa = os.sockaddr_in{
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.addr = @ptrCast(*align(1) const u32, &addr).*,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns the port in native endian.
|
||||
/// Asserts that the address is ip4 or ip6.
|
||||
pub fn getPort(self: Ip4Address) u16 {
|
||||
return mem.bigToNative(u16, self.sa.port);
|
||||
}
|
||||
|
||||
/// `port` is native-endian.
|
||||
/// Asserts that the address is ip4 or ip6.
|
||||
pub fn setPort(self: *Ip4Address, port: u16) void {
|
||||
self.sa.port = mem.nativeToBig(u16, port);
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: Ip4Address,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
out_stream: anytype,
|
||||
) !void {
|
||||
const bytes = @ptrCast(*const [4]u8, &self.sa.addr);
|
||||
try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
|
||||
bytes[0],
|
||||
bytes[1],
|
||||
bytes[2],
|
||||
bytes[3],
|
||||
self.getPort(),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn getOsSockLen(self: Ip4Address) os.socklen_t {
|
||||
return @sizeOf(os.sockaddr_in);
|
||||
}
|
||||
};
|
||||
|
||||
pub const Ip6Address = extern struct {
|
||||
sa: os.sockaddr_in6,
|
||||
|
||||
/// Parse a given IPv6 address string into an Address.
|
||||
/// Assumes the Scope ID of the address is fully numeric.
|
||||
/// For non-numeric addresses, see `resolveIp6`.
|
||||
pub fn parseIp6(buf: []const u8, port: u16) !Address {
|
||||
var result = Address{
|
||||
.in6 = os.sockaddr_in6{
|
||||
pub fn parse(buf: []const u8, port: u16) !Ip6Address {
|
||||
var result = Ip6Address{
|
||||
.sa = os.sockaddr_in6{
|
||||
.scope_id = 0,
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.flowinfo = 0,
|
||||
.addr = undefined,
|
||||
},
|
||||
};
|
||||
var ip_slice = result.in6.addr[0..];
|
||||
var ip_slice = result.sa.addr[0..];
|
||||
|
||||
var tail: [16]u8 = undefined;
|
||||
|
||||
|
@ -101,10 +309,10 @@ pub const Address = extern union {
|
|||
if (scope_id) {
|
||||
if (c >= '0' and c <= '9') {
|
||||
const digit = c - '0';
|
||||
if (@mulWithOverflow(u32, result.in6.scope_id, 10, &result.in6.scope_id)) {
|
||||
if (@mulWithOverflow(u32, result.sa.scope_id, 10, &result.sa.scope_id)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
if (@addWithOverflow(u32, result.in6.scope_id, digit, &result.in6.scope_id)) {
|
||||
if (@addWithOverflow(u32, result.sa.scope_id, digit, &result.sa.scope_id)) {
|
||||
return error.Overflow;
|
||||
}
|
||||
} else {
|
||||
|
@ -141,10 +349,10 @@ pub const Address = extern union {
|
|||
return error.InvalidIpv4Mapping;
|
||||
}
|
||||
const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
|
||||
const addr = (parseIp4(buf[start_index..], 0) catch {
|
||||
const addr = (Ip4Address.parse(buf[start_index..], 0) catch {
|
||||
return error.InvalidIpv4Mapping;
|
||||
}).in.addr;
|
||||
ip_slice = result.in6.addr[0..];
|
||||
}).sa.addr;
|
||||
ip_slice = result.sa.addr[0..];
|
||||
ip_slice[10] = 0xff;
|
||||
ip_slice[11] = 0xff;
|
||||
|
||||
|
@ -180,22 +388,22 @@ pub const Address = extern union {
|
|||
index += 1;
|
||||
ip_slice[index] = @truncate(u8, x);
|
||||
index += 1;
|
||||
mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
|
||||
mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolveIp6(buf: []const u8, port: u16) !Address {
|
||||
pub fn resolve(buf: []const u8, port: u16) !Ip6Address {
|
||||
// TODO: Unify the implementations of resolveIp6 and parseIp6.
|
||||
var result = Address{
|
||||
.in6 = os.sockaddr_in6{
|
||||
var result = Ip6Address{
|
||||
.sa = os.sockaddr_in6{
|
||||
.scope_id = 0,
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.flowinfo = 0,
|
||||
.addr = undefined,
|
||||
},
|
||||
};
|
||||
var ip_slice = result.in6.addr[0..];
|
||||
var ip_slice = result.sa.addr[0..];
|
||||
|
||||
var tail: [16]u8 = undefined;
|
||||
|
||||
|
@ -256,10 +464,10 @@ pub const Address = extern union {
|
|||
return error.InvalidIpv4Mapping;
|
||||
}
|
||||
const start_index = mem.lastIndexOfScalar(u8, buf[0..i], ':').? + 1;
|
||||
const addr = (parseIp4(buf[start_index..], 0) catch {
|
||||
const addr = (Ip4Address.parse(buf[start_index..], 0) catch {
|
||||
return error.InvalidIpv4Mapping;
|
||||
}).in.addr;
|
||||
ip_slice = result.in6.addr[0..];
|
||||
}).sa.addr;
|
||||
ip_slice = result.sa.addr[0..];
|
||||
ip_slice[10] = 0xff;
|
||||
ip_slice[11] = 0xff;
|
||||
|
||||
|
@ -299,7 +507,7 @@ pub const Address = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
result.in6.scope_id = resolved_scope_id;
|
||||
result.sa.scope_id = resolved_scope_id;
|
||||
|
||||
if (index == 14) {
|
||||
ip_slice[14] = @truncate(u8, x >> 8);
|
||||
|
@ -310,63 +518,14 @@ pub const Address = extern union {
|
|||
index += 1;
|
||||
ip_slice[index] = @truncate(u8, x);
|
||||
index += 1;
|
||||
mem.copy(u8, result.in6.addr[16 - index ..], ip_slice[0..index]);
|
||||
mem.copy(u8, result.sa.addr[16 - index ..], ip_slice[0..index]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parseIp4(buf: []const u8, port: u16) !Address {
|
||||
var result = Address{
|
||||
.in = os.sockaddr_in{
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.addr = undefined,
|
||||
},
|
||||
};
|
||||
const out_ptr = mem.sliceAsBytes(@as(*[1]u32, &result.in.addr)[0..]);
|
||||
|
||||
var x: u8 = 0;
|
||||
var index: u8 = 0;
|
||||
var saw_any_digits = false;
|
||||
for (buf) |c| {
|
||||
if (c == '.') {
|
||||
if (!saw_any_digits) {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
if (index == 3) {
|
||||
return error.InvalidEnd;
|
||||
}
|
||||
out_ptr[index] = x;
|
||||
index += 1;
|
||||
x = 0;
|
||||
saw_any_digits = false;
|
||||
} else if (c >= '0' and c <= '9') {
|
||||
saw_any_digits = true;
|
||||
x = try std.math.mul(u8, x, 10);
|
||||
x = try std.math.add(u8, x, c - '0');
|
||||
} else {
|
||||
return error.InvalidCharacter;
|
||||
}
|
||||
}
|
||||
if (index == 3 and saw_any_digits) {
|
||||
out_ptr[index] = x;
|
||||
return result;
|
||||
}
|
||||
|
||||
return error.Incomplete;
|
||||
}
|
||||
|
||||
pub fn initIp4(addr: [4]u8, port: u16) Address {
|
||||
return Address{
|
||||
.in = os.sockaddr_in{
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.addr = @ptrCast(*align(1) const u32, &addr).*,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn initIp6(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Address {
|
||||
return Address{
|
||||
.in6 = os.sockaddr_in6{
|
||||
pub fn init(addr: [16]u8, port: u16, flowinfo: u32, scope_id: u32) Ip6Address {
|
||||
return Ip6Address{
|
||||
.sa = os.sockaddr_in6{
|
||||
.addr = addr,
|
||||
.port = mem.nativeToBig(u16, port),
|
||||
.flowinfo = flowinfo,
|
||||
|
@ -375,147 +534,71 @@ pub const Address = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn initUnix(path: []const u8) !Address {
|
||||
var sock_addr = os.sockaddr_un{
|
||||
.family = os.AF_UNIX,
|
||||
.path = undefined,
|
||||
};
|
||||
|
||||
// this enables us to have the proper length of the socket in getOsSockLen
|
||||
mem.set(u8, &sock_addr.path, 0);
|
||||
|
||||
if (path.len > sock_addr.path.len) return error.NameTooLong;
|
||||
mem.copy(u8, &sock_addr.path, path);
|
||||
|
||||
return Address{ .un = sock_addr };
|
||||
}
|
||||
|
||||
/// Returns the port in native endian.
|
||||
/// Asserts that the address is ip4 or ip6.
|
||||
pub fn getPort(self: Address) u16 {
|
||||
const big_endian_port = switch (self.any.family) {
|
||||
os.AF_INET => self.in.port,
|
||||
os.AF_INET6 => self.in6.port,
|
||||
else => unreachable,
|
||||
};
|
||||
return mem.bigToNative(u16, big_endian_port);
|
||||
pub fn getPort(self: Ip6Address) u16 {
|
||||
return mem.bigToNative(u16, self.sa.port);
|
||||
}
|
||||
|
||||
/// `port` is native-endian.
|
||||
/// Asserts that the address is ip4 or ip6.
|
||||
pub fn setPort(self: *Address, port: u16) void {
|
||||
const ptr = switch (self.any.family) {
|
||||
os.AF_INET => &self.in.port,
|
||||
os.AF_INET6 => &self.in6.port,
|
||||
else => unreachable,
|
||||
};
|
||||
ptr.* = mem.nativeToBig(u16, port);
|
||||
}
|
||||
|
||||
/// Asserts that `addr` is an IP address.
|
||||
/// This function will read past the end of the pointer, with a size depending
|
||||
/// on the address family.
|
||||
pub fn initPosix(addr: *align(4) const os.sockaddr) Address {
|
||||
switch (addr.family) {
|
||||
os.AF_INET => return Address{ .in = @ptrCast(*const os.sockaddr_in, addr).* },
|
||||
os.AF_INET6 => return Address{ .in6 = @ptrCast(*const os.sockaddr_in6, addr).* },
|
||||
else => unreachable,
|
||||
}
|
||||
pub fn setPort(self: *Ip6Address, port: u16) void {
|
||||
self.sa.port = mem.nativeToBig(u16, port);
|
||||
}
|
||||
|
||||
pub fn format(
|
||||
self: Address,
|
||||
self: Ip6Address,
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
out_stream: anytype,
|
||||
) !void {
|
||||
switch (self.any.family) {
|
||||
os.AF_INET => {
|
||||
const port = mem.bigToNative(u16, self.in.port);
|
||||
const bytes = @ptrCast(*const [4]u8, &self.in.addr);
|
||||
try std.fmt.format(out_stream, "{}.{}.{}.{}:{}", .{
|
||||
bytes[0],
|
||||
bytes[1],
|
||||
bytes[2],
|
||||
bytes[3],
|
||||
port,
|
||||
});
|
||||
},
|
||||
os.AF_INET6 => {
|
||||
const port = mem.bigToNative(u16, self.in6.port);
|
||||
if (mem.eql(u8, self.in6.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
|
||||
try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
|
||||
self.in6.addr[12],
|
||||
self.in6.addr[13],
|
||||
self.in6.addr[14],
|
||||
self.in6.addr[15],
|
||||
port,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.in6.addr);
|
||||
const native_endian_parts = switch (builtin.endian) {
|
||||
.Big => big_endian_parts.*,
|
||||
.Little => blk: {
|
||||
var buf: [8]u16 = undefined;
|
||||
for (big_endian_parts) |part, i| {
|
||||
buf[i] = mem.bigToNative(u16, part);
|
||||
}
|
||||
break :blk buf;
|
||||
},
|
||||
};
|
||||
try out_stream.writeAll("[");
|
||||
var i: usize = 0;
|
||||
var abbrv = false;
|
||||
while (i < native_endian_parts.len) : (i += 1) {
|
||||
if (native_endian_parts[i] == 0) {
|
||||
if (!abbrv) {
|
||||
try out_stream.writeAll(if (i == 0) "::" else ":");
|
||||
abbrv = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
|
||||
if (i != native_endian_parts.len - 1) {
|
||||
try out_stream.writeAll(":");
|
||||
}
|
||||
}
|
||||
try std.fmt.format(out_stream, "]:{}", .{port});
|
||||
},
|
||||
os.AF_UNIX => {
|
||||
if (!has_unix_sockets) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
try std.fmt.format(out_stream, "{}", .{&self.un.path});
|
||||
},
|
||||
else => unreachable,
|
||||
const port = mem.bigToNative(u16, self.sa.port);
|
||||
if (mem.eql(u8, self.sa.addr[0..12], &[_]u8{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff })) {
|
||||
try std.fmt.format(out_stream, "[::ffff:{}.{}.{}.{}]:{}", .{
|
||||
self.sa.addr[12],
|
||||
self.sa.addr[13],
|
||||
self.sa.addr[14],
|
||||
self.sa.addr[15],
|
||||
port,
|
||||
});
|
||||
return;
|
||||
}
|
||||
const big_endian_parts = @ptrCast(*align(1) const [8]u16, &self.sa.addr);
|
||||
const native_endian_parts = switch (builtin.endian) {
|
||||
.Big => big_endian_parts.*,
|
||||
.Little => blk: {
|
||||
var buf: [8]u16 = undefined;
|
||||
for (big_endian_parts) |part, i| {
|
||||
buf[i] = mem.bigToNative(u16, part);
|
||||
}
|
||||
break :blk buf;
|
||||
},
|
||||
};
|
||||
try out_stream.writeAll("[");
|
||||
var i: usize = 0;
|
||||
var abbrv = false;
|
||||
while (i < native_endian_parts.len) : (i += 1) {
|
||||
if (native_endian_parts[i] == 0) {
|
||||
if (!abbrv) {
|
||||
try out_stream.writeAll(if (i == 0) "::" else ":");
|
||||
abbrv = true;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
try std.fmt.format(out_stream, "{x}", .{native_endian_parts[i]});
|
||||
if (i != native_endian_parts.len - 1) {
|
||||
try out_stream.writeAll(":");
|
||||
}
|
||||
}
|
||||
try std.fmt.format(out_stream, "]:{}", .{port});
|
||||
}
|
||||
|
||||
pub fn eql(a: Address, b: Address) bool {
|
||||
const a_bytes = @ptrCast([*]const u8, &a.any)[0..a.getOsSockLen()];
|
||||
const b_bytes = @ptrCast([*]const u8, &b.any)[0..b.getOsSockLen()];
|
||||
return mem.eql(u8, a_bytes, b_bytes);
|
||||
}
|
||||
|
||||
pub fn getOsSockLen(self: Address) os.socklen_t {
|
||||
switch (self.any.family) {
|
||||
os.AF_INET => return @sizeOf(os.sockaddr_in),
|
||||
os.AF_INET6 => return @sizeOf(os.sockaddr_in6),
|
||||
os.AF_UNIX => {
|
||||
if (!has_unix_sockets) {
|
||||
unreachable;
|
||||
}
|
||||
|
||||
const path_len = std.mem.len(@ptrCast([*:0]const u8, &self.un.path));
|
||||
return @intCast(os.socklen_t, @sizeOf(os.sockaddr_un) - self.un.path.len + path_len);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
pub fn getOsSockLen(self: Ip6Address) os.socklen_t {
|
||||
return @sizeOf(os.sockaddr_in6);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
pub fn connectUnixSocket(path: []const u8) !fs.File {
|
||||
const opt_non_block = if (std.io.is_async) os.SOCK_NONBLOCK else 0;
|
||||
const sockfd = try os.socket(
|
||||
|
@ -777,7 +860,7 @@ fn linuxLookupName(
|
|||
@memset(@ptrCast([*]u8, &sa6), 0, @sizeOf(os.sockaddr_in6));
|
||||
var da6 = os.sockaddr_in6{
|
||||
.family = os.AF_INET6,
|
||||
.scope_id = addr.addr.in6.scope_id,
|
||||
.scope_id = addr.addr.in6.sa.scope_id,
|
||||
.port = 65535,
|
||||
.flowinfo = 0,
|
||||
.addr = [1]u8{0} ** 16,
|
||||
|
@ -795,7 +878,7 @@ fn linuxLookupName(
|
|||
var salen: os.socklen_t = undefined;
|
||||
var dalen: os.socklen_t = undefined;
|
||||
if (addr.addr.any.family == os.AF_INET6) {
|
||||
mem.copy(u8, &da6.addr, &addr.addr.in6.addr);
|
||||
mem.copy(u8, &da6.addr, &addr.addr.in6.sa.addr);
|
||||
da = @ptrCast(*os.sockaddr, &da6);
|
||||
dalen = @sizeOf(os.sockaddr_in6);
|
||||
sa = @ptrCast(*os.sockaddr, &sa6);
|
||||
|
@ -803,8 +886,8 @@ fn linuxLookupName(
|
|||
} else {
|
||||
mem.copy(u8, &sa6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
|
||||
mem.copy(u8, &da6.addr, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff");
|
||||
mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr);
|
||||
da4.addr = addr.addr.in.addr;
|
||||
mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.sa.addr);
|
||||
da4.addr = addr.addr.in.sa.addr;
|
||||
da = @ptrCast(*os.sockaddr, &da4);
|
||||
dalen = @sizeOf(os.sockaddr_in);
|
||||
sa = @ptrCast(*os.sockaddr, &sa4);
|
||||
|
|
282
lib/std/os.zig
282
lib/std/os.zig
|
@ -1041,6 +1041,9 @@ pub const OpenError = error{
|
|||
|
||||
/// The underlying filesystem does not support file locks
|
||||
FileLocksNotSupported,
|
||||
|
||||
BadPathName,
|
||||
InvalidUtf8,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
|
@ -1092,18 +1095,65 @@ pub fn openZ(file_path: [*:0]const u8, flags: u32, perm: mode_t) OpenError!fd_t
|
|||
}
|
||||
}
|
||||
|
||||
fn openOptionsFromFlags(flags: u32) windows.OpenFileOptions {
|
||||
const w = windows;
|
||||
|
||||
var access_mask: w.ULONG = w.READ_CONTROL | w.FILE_WRITE_ATTRIBUTES | w.SYNCHRONIZE;
|
||||
if (flags & O_RDWR != 0) {
|
||||
access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
|
||||
} else if (flags & O_WRONLY != 0) {
|
||||
access_mask |= w.GENERIC_WRITE;
|
||||
} else {
|
||||
access_mask |= w.GENERIC_READ | w.GENERIC_WRITE;
|
||||
}
|
||||
|
||||
const open_dir: bool = flags & O_DIRECTORY != 0;
|
||||
const follow_symlinks: bool = flags & O_NOFOLLOW == 0;
|
||||
|
||||
const creation: w.ULONG = blk: {
|
||||
if (flags & O_CREAT != 0) {
|
||||
if (flags & O_EXCL != 0) {
|
||||
break :blk w.FILE_CREATE;
|
||||
}
|
||||
}
|
||||
break :blk w.FILE_OPEN;
|
||||
};
|
||||
|
||||
return .{
|
||||
.access_mask = access_mask,
|
||||
.io_mode = .blocking,
|
||||
.creation = creation,
|
||||
.open_dir = open_dir,
|
||||
.follow_symlinks = follow_symlinks,
|
||||
};
|
||||
}
|
||||
|
||||
/// Windows-only. The path parameter is
|
||||
/// [WTF-16](https://simonsapin.github.io/wtf-8/#potentially-ill-formed-utf-16) encoded.
|
||||
/// Translates the POSIX open API call to a Windows API call.
|
||||
pub fn openW(file_path_w: []const u16, flags: u32, perm: usize) OpenError!fd_t {
|
||||
@compileError("TODO implement openW for windows");
|
||||
/// TODO currently, this function does not handle all flag combinations
|
||||
/// or makes use of perm argument.
|
||||
pub fn openW(file_path_w: []const u16, flags: u32, perm: mode_t) OpenError!fd_t {
|
||||
var options = openOptionsFromFlags(flags);
|
||||
options.dir = std.fs.cwd().fd;
|
||||
return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||
/// See also `openatC`.
|
||||
/// TODO support windows
|
||||
pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("use openatWasi instead");
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return openatZ(dir_fd, &file_path_c, flags, mode);
|
||||
}
|
||||
|
@ -1145,8 +1195,11 @@ pub const openatC = @compileError("deprecated: renamed to openatZ");
|
|||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// `file_path` is relative to the open directory handle `dir_fd`.
|
||||
/// See also `openat`.
|
||||
/// TODO support windows
|
||||
pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return openatW(dir_fd, file_path_w.span(), flags, mode);
|
||||
}
|
||||
while (true) {
|
||||
const rc = system.openat(dir_fd, file_path, flags, mode);
|
||||
switch (errno(rc)) {
|
||||
|
@ -1177,6 +1230,20 @@ pub fn openatZ(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t)
|
|||
}
|
||||
}
|
||||
|
||||
/// Windows-only. Similar to `openat` but with pathname argument null-terminated
|
||||
/// WTF16 encoded.
|
||||
/// TODO currently, this function does not handle all flag combinations
|
||||
/// or makes use of perm argument.
|
||||
pub fn openatW(dir_fd: fd_t, file_path_w: []const u16, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
var options = openOptionsFromFlags(flags);
|
||||
options.dir = dir_fd;
|
||||
return windows.OpenFile(file_path_w, options) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
|
||||
while (true) {
|
||||
switch (errno(system.dup2(old_fd, new_fd))) {
|
||||
|
@ -1683,7 +1750,7 @@ pub fn unlink(file_path: []const u8) UnlinkError!void {
|
|||
@compileError("unlink is not supported in WASI; use unlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return windows.DeleteFileW(file_path_w.span().ptr);
|
||||
return unlinkW(file_path_w.span());
|
||||
} else {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return unlinkZ(&file_path_c);
|
||||
|
@ -1696,7 +1763,7 @@ pub const unlinkC = @compileError("deprecated: renamed to unlinkZ");
|
|||
pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return windows.DeleteFileW(file_path_w.span().ptr);
|
||||
return unlinkW(file_path_w.span());
|
||||
}
|
||||
switch (errno(system.unlink(file_path))) {
|
||||
0 => return,
|
||||
|
@ -1717,6 +1784,11 @@ pub fn unlinkZ(file_path: [*:0]const u8) UnlinkError!void {
|
|||
}
|
||||
}
|
||||
|
||||
/// Windows-only. Same as `unlink` except the parameter is null-terminated, WTF16 encoded.
|
||||
pub fn unlinkW(file_path_w: []const u16) UnlinkError!void {
|
||||
return windows.DeleteFile(file_path_w, .{ .dir = std.fs.cwd().fd });
|
||||
}
|
||||
|
||||
pub const UnlinkatError = UnlinkError || error{
|
||||
/// When passing `AT_REMOVEDIR`, this error occurs when the named directory is not empty.
|
||||
DirNotEmpty,
|
||||
|
@ -1727,7 +1799,7 @@ pub const UnlinkatError = UnlinkError || error{
|
|||
pub fn unlinkat(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return unlinkatW(dirfd, file_path_w.span().ptr, flags);
|
||||
return unlinkatW(dirfd, file_path_w.span(), flags);
|
||||
} else if (builtin.os.tag == .wasi) {
|
||||
return unlinkatWasi(dirfd, file_path, flags);
|
||||
} else {
|
||||
|
@ -1774,7 +1846,7 @@ pub fn unlinkatWasi(dirfd: fd_t, file_path: []const u8, flags: u32) UnlinkatErro
|
|||
pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path_c);
|
||||
return unlinkatW(dirfd, file_path_w.span().ptr, flags);
|
||||
return unlinkatW(dirfd, file_path_w.span(), flags);
|
||||
}
|
||||
switch (errno(system.unlinkat(dirfd, file_path_c, flags))) {
|
||||
0 => return,
|
||||
|
@ -1800,67 +1872,9 @@ pub fn unlinkatZ(dirfd: fd_t, file_path_c: [*:0]const u8, flags: u32) UnlinkatEr
|
|||
}
|
||||
|
||||
/// Same as `unlinkat` but `sub_path_w` is UTF16LE, NT prefixed. Windows only.
|
||||
pub fn unlinkatW(dirfd: fd_t, sub_path_w: [*:0]const u16, flags: u32) UnlinkatError!void {
|
||||
const w = windows;
|
||||
|
||||
const want_rmdir_behavior = (flags & AT_REMOVEDIR) != 0;
|
||||
const create_options_flags = if (want_rmdir_behavior)
|
||||
@as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT)
|
||||
else
|
||||
@as(w.ULONG, w.FILE_DELETE_ON_CLOSE | w.FILE_NON_DIRECTORY_FILE | w.FILE_OPEN_REPARSE_POINT); // would we ever want to delete the target instead?
|
||||
|
||||
const path_len_bytes = @intCast(u16, mem.lenZ(sub_path_w) * 2);
|
||||
var nt_name = w.UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
// The Windows API makes this mutable, but it will not mutate here.
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
||||
// Can't remove the parent directory with an open handle.
|
||||
return error.FileBusy;
|
||||
}
|
||||
|
||||
var attr = w.OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: w.IO_STATUS_BLOCK = undefined;
|
||||
var tmp_handle: w.HANDLE = undefined;
|
||||
var rc = w.ntdll.NtCreateFile(
|
||||
&tmp_handle,
|
||||
w.SYNCHRONIZE | w.DELETE,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
0,
|
||||
w.FILE_SHARE_READ | w.FILE_SHARE_WRITE | w.FILE_SHARE_DELETE,
|
||||
w.FILE_OPEN,
|
||||
create_options_flags,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
if (rc == .SUCCESS) {
|
||||
rc = w.ntdll.NtClose(tmp_handle);
|
||||
}
|
||||
switch (rc) {
|
||||
.SUCCESS => return,
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.FILE_IS_A_DIRECTORY => return error.IsDir,
|
||||
.NOT_A_DIRECTORY => return error.NotDir,
|
||||
else => return w.unexpectedStatus(rc),
|
||||
}
|
||||
pub fn unlinkatW(dirfd: fd_t, sub_path_w: []const u16, flags: u32) UnlinkatError!void {
|
||||
const remove_dir = (flags & AT_REMOVEDIR) != 0;
|
||||
return windows.DeleteFile(sub_path_w, .{ .dir = dirfd, .remove_dir = remove_dir });
|
||||
}
|
||||
|
||||
const RenameError = error{
|
||||
|
@ -2087,7 +2101,7 @@ pub fn renameatW(
|
|||
pub fn mkdirat(dir_fd: fd_t, sub_dir_path: []const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_dir_path_w = try windows.sliceToPrefixedFileW(sub_dir_path);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span().ptr, mode);
|
||||
return mkdiratW(dir_fd, sub_dir_path_w.span(), mode);
|
||||
} else if (builtin.os.tag == .wasi) {
|
||||
return mkdiratWasi(dir_fd, sub_dir_path, mode);
|
||||
} else {
|
||||
|
@ -2145,8 +2159,19 @@ pub fn mkdiratZ(dir_fd: fd_t, sub_dir_path: [*:0]const u8, mode: u32) MakeDirErr
|
|||
}
|
||||
}
|
||||
|
||||
pub fn mkdiratW(dir_fd: fd_t, sub_path_w: [*:0]const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = try windows.CreateDirectoryW(dir_fd, sub_path_w, null);
|
||||
pub fn mkdiratW(dir_fd: fd_t, sub_path_w: []const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = windows.OpenFile(sub_path_w, .{
|
||||
.dir = dir_fd,
|
||||
.access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
|
||||
.creation = windows.FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
.open_dir = true,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
error.WouldBlock => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
}
|
||||
|
||||
|
@ -2175,9 +2200,8 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
|||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("mkdir is not supported in WASI; use mkdirat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const sub_dir_handle = try windows.CreateDirectory(null, dir_path, null);
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
return;
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return mkdirW(dir_path_w.span(), mode);
|
||||
} else {
|
||||
const dir_path_c = try toPosixPath(dir_path);
|
||||
return mkdirZ(&dir_path_c, mode);
|
||||
|
@ -2188,9 +2212,7 @@ pub fn mkdir(dir_path: []const u8, mode: u32) MakeDirError!void {
|
|||
pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
|
||||
const sub_dir_handle = try windows.CreateDirectoryW(null, dir_path_w.span().ptr, null);
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
return;
|
||||
return mkdirW(dir_path_w.span(), mode);
|
||||
}
|
||||
switch (errno(system.mkdir(dir_path, mode))) {
|
||||
0 => return,
|
||||
|
@ -2211,6 +2233,23 @@ pub fn mkdirZ(dir_path: [*:0]const u8, mode: u32) MakeDirError!void {
|
|||
}
|
||||
}
|
||||
|
||||
/// Windows-only. Same as `mkdir` but the parameters is WTF16 encoded.
|
||||
pub fn mkdirW(dir_path_w: []const u16, mode: u32) MakeDirError!void {
|
||||
const sub_dir_handle = windows.OpenFile(dir_path_w, .{
|
||||
.dir = std.fs.cwd().fd,
|
||||
.access_mask = windows.GENERIC_READ | windows.SYNCHRONIZE,
|
||||
.creation = windows.FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
.open_dir = true,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
error.WouldBlock => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
windows.CloseHandle(sub_dir_handle);
|
||||
}
|
||||
|
||||
pub const DeleteDirError = error{
|
||||
AccessDenied,
|
||||
FileBusy,
|
||||
|
@ -2231,7 +2270,7 @@ pub fn rmdir(dir_path: []const u8) DeleteDirError!void {
|
|||
@compileError("rmdir is not supported in WASI; use unlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.sliceToPrefixedFileW(dir_path);
|
||||
return windows.RemoveDirectoryW(dir_path_w.span().ptr);
|
||||
return rmdirW(dir_path_w.span());
|
||||
} else {
|
||||
const dir_path_c = try toPosixPath(dir_path);
|
||||
return rmdirZ(&dir_path_c);
|
||||
|
@ -2244,7 +2283,7 @@ pub const rmdirC = @compileError("deprecated: renamed to rmdirZ");
|
|||
pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const dir_path_w = try windows.cStrToPrefixedFileW(dir_path);
|
||||
return windows.RemoveDirectoryW(dir_path_w.span().ptr);
|
||||
return rmdirW(dir_path_w.span());
|
||||
}
|
||||
switch (errno(system.rmdir(dir_path))) {
|
||||
0 => return,
|
||||
|
@ -2265,6 +2304,14 @@ pub fn rmdirZ(dir_path: [*:0]const u8) DeleteDirError!void {
|
|||
}
|
||||
}
|
||||
|
||||
/// Windows-only. Same as `rmdir` except the parameter is WTF16 encoded.
|
||||
pub fn rmdirW(dir_path_w: []const u16) DeleteDirError!void {
|
||||
return windows.DeleteFile(dir_path_w, .{ .dir = std.fs.cwd().fd, .remove_dir = true }) catch |err| switch (err) {
|
||||
error.IsDir => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
|
||||
pub const ChangeCurDirError = error{
|
||||
AccessDenied,
|
||||
FileSystem,
|
||||
|
@ -2354,7 +2401,8 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
|||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("readlink is not supported in WASI; use readlinkat instead");
|
||||
} else if (builtin.os.tag == .windows) {
|
||||
return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return readlinkW(file_path_w.span(), out_buffer);
|
||||
} else {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return readlinkZ(&file_path_c, out_buffer);
|
||||
|
@ -2363,17 +2411,17 @@ pub fn readlink(file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
|||
|
||||
pub const readlinkC = @compileError("deprecated: renamed to readlinkZ");
|
||||
|
||||
/// Windows-only. Same as `readlink` except `file_path` is null-terminated, WTF16 encoded.
|
||||
/// Windows-only. Same as `readlink` except `file_path` is WTF16 encoded.
|
||||
/// See also `readlinkZ`.
|
||||
pub fn readlinkW(file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLinkW(std.fs.cwd().fd, file_path, out_buffer);
|
||||
pub fn readlinkW(file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLink(std.fs.cwd().fd, file_path, out_buffer);
|
||||
}
|
||||
|
||||
/// Same as `readlink` except `file_path` is null-terminated.
|
||||
pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToWin32PrefixedFileW(file_path);
|
||||
return readlinkW(file_path_w.span().ptr, out_buffer);
|
||||
return readlinkW(file_path_w.span(), out_buffer);
|
||||
}
|
||||
const rc = system.readlink(file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
|
@ -2399,7 +2447,8 @@ pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLink
|
|||
return readlinkatWasi(dirfd, file_path, out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .windows) {
|
||||
return windows.ReadLink(dirfd, file_path, out_buffer);
|
||||
const file_path_w = try windows.sliceToPrefixedFileW(file_path);
|
||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
||||
}
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return readlinkatZ(dirfd, &file_path_c, out_buffer);
|
||||
|
@ -2429,8 +2478,8 @@ pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) Read
|
|||
|
||||
/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
|
||||
/// See also `readlinkat`.
|
||||
pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLinkW(dirfd, file_path, out_buffer);
|
||||
pub fn readlinkatW(dirfd: fd_t, file_path: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
return windows.ReadLink(dirfd, file_path, out_buffer);
|
||||
}
|
||||
|
||||
/// Same as `readlinkat` except `file_path` is null-terminated.
|
||||
|
@ -2438,7 +2487,7 @@ pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) Rea
|
|||
pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
|
||||
return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
|
||||
return readlinkatW(dirfd, file_path_w.span(), out_buffer);
|
||||
}
|
||||
const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
|
||||
switch (errno(rc)) {
|
||||
|
@ -3959,7 +4008,7 @@ pub const RealPathError = error{
|
|||
pub fn realpath(pathname: []const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const pathname_w = try windows.sliceToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span().ptr, out_buffer);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .wasi) {
|
||||
@compileError("Use std.fs.wasi.PreopenList to obtain valid Dir handles instead of using absolute paths");
|
||||
|
@ -3974,7 +4023,7 @@ pub const realpathC = @compileError("deprecated: renamed realpathZ");
|
|||
pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const pathname_w = try windows.cStrToPrefixedFileW(pathname);
|
||||
return realpathW(pathname_w.span().ptr, out_buffer);
|
||||
return realpathW(pathname_w.span(), out_buffer);
|
||||
}
|
||||
if (builtin.os.tag == .linux and !builtin.link_libc) {
|
||||
const fd = openZ(pathname, linux.O_PATH | linux.O_NONBLOCK | linux.O_CLOEXEC, 0) catch |err| switch (err) {
|
||||
|
@ -4010,22 +4059,43 @@ pub fn realpathZ(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
|
|||
return mem.spanZ(result_path);
|
||||
}
|
||||
|
||||
/// Same as `realpath` except `pathname` is null-terminated and UTF16LE-encoded.
|
||||
/// TODO use ntdll for better semantics
|
||||
pub fn realpathW(pathname: [*:0]const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
const h_file = try windows.CreateFileW(
|
||||
pathname,
|
||||
windows.GENERIC_READ,
|
||||
windows.FILE_SHARE_READ,
|
||||
null,
|
||||
windows.OPEN_EXISTING,
|
||||
windows.FILE_FLAG_BACKUP_SEMANTICS,
|
||||
null,
|
||||
);
|
||||
defer windows.CloseHandle(h_file);
|
||||
/// Same as `realpath` except `pathname` is UTF16LE-encoded.
|
||||
/// TODO use ntdll to emulate `GetFinalPathNameByHandleW` routine
|
||||
pub fn realpathW(pathname: []const u16, out_buffer: *[MAX_PATH_BYTES]u8) RealPathError![]u8 {
|
||||
const w = windows;
|
||||
|
||||
var wide_buf: [windows.PATH_MAX_WIDE]u16 = undefined;
|
||||
const wide_slice = try windows.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, windows.VOLUME_NAME_DOS);
|
||||
const dir = std.fs.cwd().fd;
|
||||
const access_mask = w.GENERIC_READ | w.SYNCHRONIZE;
|
||||
const share_access = w.FILE_SHARE_READ;
|
||||
const creation = w.FILE_OPEN;
|
||||
const h_file = blk: {
|
||||
const res = w.OpenFile(pathname, .{
|
||||
.dir = dir,
|
||||
.access_mask = access_mask,
|
||||
.share_access = share_access,
|
||||
.creation = creation,
|
||||
.io_mode = .blocking,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => break :blk w.OpenFile(pathname, .{
|
||||
.dir = dir,
|
||||
.access_mask = access_mask,
|
||||
.share_access = share_access,
|
||||
.creation = creation,
|
||||
.io_mode = .blocking,
|
||||
.open_dir = true,
|
||||
}) catch |er| switch (er) {
|
||||
error.WouldBlock => unreachable,
|
||||
else => |e2| return e2,
|
||||
},
|
||||
error.WouldBlock => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
break :blk res;
|
||||
};
|
||||
defer w.CloseHandle(h_file);
|
||||
|
||||
var wide_buf: [w.PATH_MAX_WIDE]u16 = undefined;
|
||||
const wide_slice = try w.GetFinalPathNameByHandleW(h_file, &wide_buf, wide_buf.len, w.VOLUME_NAME_DOS);
|
||||
|
||||
// Windows returns \\?\ prepended to the path.
|
||||
// We strip it to make this function consistent across platforms.
|
||||
|
|
|
@ -237,3 +237,28 @@ pub const IPPROTO_TCP = ws2_32.IPPROTO_TCP;
|
|||
pub const IPPROTO_UDP = ws2_32.IPPROTO_UDP;
|
||||
pub const IPPROTO_ICMPV6 = ws2_32.IPPROTO_ICMPV6;
|
||||
pub const IPPROTO_RM = ws2_32.IPPROTO_RM;
|
||||
|
||||
pub const O_RDONLY = 0o0;
|
||||
pub const O_WRONLY = 0o1;
|
||||
pub const O_RDWR = 0o2;
|
||||
|
||||
pub const O_CREAT = 0o100;
|
||||
pub const O_EXCL = 0o200;
|
||||
pub const O_NOCTTY = 0o400;
|
||||
pub const O_TRUNC = 0o1000;
|
||||
pub const O_APPEND = 0o2000;
|
||||
pub const O_NONBLOCK = 0o4000;
|
||||
pub const O_DSYNC = 0o10000;
|
||||
pub const O_SYNC = 0o4010000;
|
||||
pub const O_RSYNC = 0o4010000;
|
||||
pub const O_DIRECTORY = 0o200000;
|
||||
pub const O_NOFOLLOW = 0o400000;
|
||||
pub const O_CLOEXEC = 0o2000000;
|
||||
|
||||
pub const O_ASYNC = 0o20000;
|
||||
pub const O_DIRECT = 0o40000;
|
||||
pub const O_LARGEFILE = 0;
|
||||
pub const O_NOATIME = 0o1000000;
|
||||
pub const O_PATH = 0o10000000;
|
||||
pub const O_TMPFILE = 0o20200000;
|
||||
pub const O_NDELAY = O_NONBLOCK;
|
|
@ -3,6 +3,7 @@ const os = std.os;
|
|||
const testing = std.testing;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const expectError = testing.expectError;
|
||||
const io = std.io;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
|
@ -19,6 +20,95 @@ const tmpDir = std.testing.tmpDir;
|
|||
const Dir = std.fs.Dir;
|
||||
const ArenaAllocator = std.heap.ArenaAllocator;
|
||||
|
||||
test "open smoke test" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
// TODO verify file attributes using `fstat`
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
// Get base abs path
|
||||
var arena = ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
const base_path = blk: {
|
||||
const relative_path = try fs.path.join(&arena.allocator, &[_][]const u8{ "zig-cache", "tmp", tmp.sub_path[0..] });
|
||||
break :blk try fs.realpathAlloc(&arena.allocator, relative_path);
|
||||
};
|
||||
|
||||
var file_path: []u8 = undefined;
|
||||
var fd: os.fd_t = undefined;
|
||||
const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `open`.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
fd = try os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
expectError(error.PathAlreadyExists, os.open(file_path, os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
|
||||
|
||||
// Try opening without `O_EXCL` flag.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
fd = try os.open(file_path, os.O_RDWR | os.O_CREAT, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as a directory which should fail.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_file" });
|
||||
expectError(error.NotDir, os.open(file_path, os.O_RDWR | os.O_DIRECTORY, mode));
|
||||
|
||||
// Create some directory
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
try os.mkdir(file_path, mode);
|
||||
|
||||
// Open dir using `open`
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
fd = try os.open(file_path, os.O_RDONLY | os.O_DIRECTORY, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as file which should fail.
|
||||
file_path = try fs.path.join(&arena.allocator, &[_][]const u8{ base_path, "some_dir" });
|
||||
expectError(error.IsDir, os.open(file_path, os.O_RDWR, mode));
|
||||
}
|
||||
|
||||
test "openat smoke test" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
// TODO verify file attributes using `fstatat`
|
||||
|
||||
var tmp = tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
var fd: os.fd_t = undefined;
|
||||
const mode: os.mode_t = if (builtin.os.tag == .windows) 0 else 0o666;
|
||||
|
||||
// Create some file using `openat`.
|
||||
fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try this again with the same flags. This op should fail with error.PathAlreadyExists.
|
||||
expectError(error.PathAlreadyExists, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT | os.O_EXCL, mode));
|
||||
|
||||
// Try opening without `O_EXCL` flag.
|
||||
fd = try os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_CREAT, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as a directory which should fail.
|
||||
expectError(error.NotDir, os.openat(tmp.dir.fd, "some_file", os.O_RDWR | os.O_DIRECTORY, mode));
|
||||
|
||||
// Create some directory
|
||||
try os.mkdirat(tmp.dir.fd, "some_dir", mode);
|
||||
|
||||
// Open dir using `open`
|
||||
fd = try os.openat(tmp.dir.fd, "some_dir", os.O_RDONLY | os.O_DIRECTORY, mode);
|
||||
os.close(fd);
|
||||
|
||||
// Try opening as file which should fail.
|
||||
expectError(error.IsDir, os.openat(tmp.dir.fd, "some_dir", os.O_RDWR, mode));
|
||||
}
|
||||
|
||||
test "symlink with relative paths" {
|
||||
if (builtin.os.tag == .wasi) return error.SkipZigTest;
|
||||
|
||||
|
@ -27,7 +117,7 @@ test "symlink with relative paths" {
|
|||
try cwd.writeFile("file.txt", "nonsense");
|
||||
|
||||
if (builtin.os.tag == .windows) {
|
||||
try os.windows.CreateSymbolicLink(cwd.fd, "symlinked", "file.txt", false);
|
||||
try os.windows.CreateSymbolicLink(cwd.fd, &[_]u16{ 's', 'y', 'm', 'l', 'i', 'n', 'k', 'e', 'd' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
|
||||
} else {
|
||||
try os.symlink("file.txt", "symlinked");
|
||||
}
|
||||
|
@ -85,7 +175,7 @@ test "readlinkat" {
|
|||
|
||||
// create a symbolic link
|
||||
if (builtin.os.tag == .windows) {
|
||||
try os.windows.CreateSymbolicLink(tmp.dir.fd, "link", "file.txt", false);
|
||||
try os.windows.CreateSymbolicLink(tmp.dir.fd, &[_]u16{ 'l', 'i', 'n', 'k' }, &[_]u16{ 'f', 'i', 'l', 'e', '.', 't', 'x', 't' }, false);
|
||||
} else {
|
||||
try os.symlinkat("file.txt", tmp.dir.fd, "link");
|
||||
}
|
||||
|
|
|
@ -25,76 +25,11 @@ pub usingnamespace @import("windows/bits.zig");
|
|||
|
||||
pub const self_process_handle = @intToPtr(HANDLE, maxInt(usize));
|
||||
|
||||
pub const CreateFileError = error{
|
||||
SharingViolation,
|
||||
PathAlreadyExists,
|
||||
|
||||
/// When any of the path components can not be found or the file component can not
|
||||
/// be found. Some operating systems distinguish between path components not found and
|
||||
/// file components not found, but they are collapsed into FileNotFound to gain
|
||||
/// consistency across operating systems.
|
||||
FileNotFound,
|
||||
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
NameTooLong,
|
||||
|
||||
/// On Windows, file paths must be valid Unicode.
|
||||
InvalidUtf8,
|
||||
|
||||
/// On Windows, file paths cannot contain these characters:
|
||||
/// '/', '*', '?', '"', '<', '>', '|'
|
||||
BadPathName,
|
||||
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn CreateFile(
|
||||
file_path: []const u8,
|
||||
desired_access: DWORD,
|
||||
share_mode: DWORD,
|
||||
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
||||
creation_disposition: DWORD,
|
||||
flags_and_attrs: DWORD,
|
||||
hTemplateFile: ?HANDLE,
|
||||
) CreateFileError!HANDLE {
|
||||
const file_path_w = try sliceToPrefixedFileW(file_path);
|
||||
return CreateFileW(file_path_w.span().ptr, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
|
||||
}
|
||||
|
||||
pub fn CreateFileW(
|
||||
file_path_w: [*:0]const u16,
|
||||
desired_access: DWORD,
|
||||
share_mode: DWORD,
|
||||
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
|
||||
creation_disposition: DWORD,
|
||||
flags_and_attrs: DWORD,
|
||||
hTemplateFile: ?HANDLE,
|
||||
) CreateFileError!HANDLE {
|
||||
const result = kernel32.CreateFileW(file_path_w, desired_access, share_mode, lpSecurityAttributes, creation_disposition, flags_and_attrs, hTemplateFile);
|
||||
|
||||
if (result == INVALID_HANDLE_VALUE) {
|
||||
switch (kernel32.GetLastError()) {
|
||||
.SHARING_VIOLATION => return error.SharingViolation,
|
||||
.ALREADY_EXISTS => return error.PathAlreadyExists,
|
||||
.FILE_EXISTS => return error.PathAlreadyExists,
|
||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.PIPE_BUSY => return error.PipeBusy,
|
||||
.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
IsDir,
|
||||
NotDir,
|
||||
FileNotFound,
|
||||
NoDevice,
|
||||
SharingViolation,
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
PathAlreadyExists,
|
||||
|
@ -111,15 +46,21 @@ pub const OpenFileOptions = struct {
|
|||
share_access_nonblocking: bool = false,
|
||||
creation: ULONG,
|
||||
io_mode: std.io.ModeOverride,
|
||||
/// If true, tries to open path as a directory.
|
||||
/// Defaults to false.
|
||||
open_dir: bool = false,
|
||||
/// If false, tries to open path as a reparse point without dereferencing it.
|
||||
/// Defaults to true.
|
||||
follow_symlinks: bool = true,
|
||||
};
|
||||
|
||||
/// TODO when share_access_nonblocking is false, this implementation uses
|
||||
/// untinterruptible sleep() to block. This is not the final iteration of the API.
|
||||
pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HANDLE {
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{'.'})) {
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{'.'}) and !options.open_dir) {
|
||||
return error.IsDir;
|
||||
}
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' })) {
|
||||
if (mem.eql(u16, sub_path_w, &[_]u16{ '.', '.' }) and !options.open_dir) {
|
||||
return error.IsDir;
|
||||
}
|
||||
|
||||
|
@ -142,11 +83,13 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
|
|||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
|
||||
const file_or_dir_flag: ULONG = if (options.open_dir) FILE_DIRECTORY_FILE else FILE_NON_DIRECTORY_FILE;
|
||||
// If we're not following symlinks, we need to ensure we don't pass in any synchronization flags such as FILE_SYNCHRONOUS_IO_NONALERT.
|
||||
const flags: ULONG = if (options.follow_symlinks) file_or_dir_flag | blocking_flag else file_or_dir_flag | FILE_OPEN_REPARSE_POINT;
|
||||
|
||||
var delay: usize = 1;
|
||||
while (true) {
|
||||
var flags: ULONG = undefined;
|
||||
const blocking_flag: ULONG = if (options.io_mode == .blocking) FILE_SYNCHRONOUS_IO_NONALERT else 0;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result,
|
||||
options.access_mask,
|
||||
|
@ -156,7 +99,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
|
|||
FILE_ATTRIBUTE_NORMAL,
|
||||
options.share_access,
|
||||
options.creation,
|
||||
FILE_NON_DIRECTORY_FILE | blocking_flag,
|
||||
flags,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
|
@ -184,6 +127,7 @@ pub fn OpenFile(sub_path_w: []const u16, options: OpenFileOptions) OpenError!HAN
|
|||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
.FILE_IS_A_DIRECTORY => return error.IsDir,
|
||||
.NOT_A_DIRECTORY => return error.NotDir,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
@ -215,30 +159,61 @@ pub fn CreateEventExW(attributes: ?*SECURITY_ATTRIBUTES, nameW: [*:0]const u16,
|
|||
}
|
||||
}
|
||||
|
||||
pub const DeviceIoControlError = error{Unexpected};
|
||||
|
||||
/// A Zig wrapper around `NtDeviceIoControlFile` and `NtFsControlFile` syscalls.
|
||||
/// It implements similar behavior to `DeviceIoControl` and is meant to serve
|
||||
/// as a direct substitute for that call.
|
||||
/// TODO work out if we need to expose other arguments to the underlying syscalls.
|
||||
pub fn DeviceIoControl(
|
||||
h: HANDLE,
|
||||
ioControlCode: DWORD,
|
||||
ioControlCode: ULONG,
|
||||
in: ?[]const u8,
|
||||
out: ?[]u8,
|
||||
overlapped: ?*OVERLAPPED,
|
||||
) !DWORD {
|
||||
var bytes: DWORD = undefined;
|
||||
if (kernel32.DeviceIoControl(
|
||||
h,
|
||||
ioControlCode,
|
||||
if (in) |i| i.ptr else null,
|
||||
if (in) |i| @intCast(u32, i.len) else 0,
|
||||
if (out) |o| o.ptr else null,
|
||||
if (out) |o| @intCast(u32, o.len) else 0,
|
||||
&bytes,
|
||||
overlapped,
|
||||
) == 0) {
|
||||
switch (kernel32.GetLastError()) {
|
||||
.IO_PENDING => if (overlapped == null) unreachable,
|
||||
else => |err| return unexpectedError(err),
|
||||
) DeviceIoControlError!void {
|
||||
// Logic from: https://doxygen.reactos.org/d3/d74/deviceio_8c.html
|
||||
const is_fsctl = (ioControlCode >> 16) == FILE_DEVICE_FILE_SYSTEM;
|
||||
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
const in_ptr = if (in) |i| i.ptr else null;
|
||||
const in_len = if (in) |i| @intCast(ULONG, i.len) else 0;
|
||||
const out_ptr = if (out) |o| o.ptr else null;
|
||||
const out_len = if (out) |o| @intCast(ULONG, o.len) else 0;
|
||||
|
||||
const rc = blk: {
|
||||
if (is_fsctl) {
|
||||
break :blk ntdll.NtFsControlFile(
|
||||
h,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
&io,
|
||||
ioControlCode,
|
||||
in_ptr,
|
||||
in_len,
|
||||
out_ptr,
|
||||
out_len,
|
||||
);
|
||||
} else {
|
||||
break :blk ntdll.NtDeviceIoControlFile(
|
||||
h,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
&io,
|
||||
ioControlCode,
|
||||
in_ptr,
|
||||
in_len,
|
||||
out_ptr,
|
||||
out_len,
|
||||
);
|
||||
}
|
||||
};
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
pub fn GetOverlappedResult(h: HANDLE, overlapped: *OVERLAPPED, wait: bool) !DWORD {
|
||||
|
@ -607,27 +582,14 @@ pub const CreateSymbolicLinkError = error{
|
|||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
NameTooLong,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
NoDevice,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
pub fn CreateSymbolicLink(
|
||||
dir: ?HANDLE,
|
||||
sym_link_path: []const u8,
|
||||
target_path: []const u8,
|
||||
is_directory: bool,
|
||||
) CreateSymbolicLinkError!void {
|
||||
const sym_link_path_w = try sliceToPrefixedFileW(sym_link_path);
|
||||
const target_path_w = try sliceToPrefixedFileW(target_path);
|
||||
return CreateSymbolicLinkW(dir, sym_link_path_w.span(), target_path_w.span(), is_directory);
|
||||
}
|
||||
|
||||
pub fn CreateSymbolicLinkW(
|
||||
dir: ?HANDLE,
|
||||
sym_link_path: [:0]const u16,
|
||||
target_path: [:0]const u16,
|
||||
sym_link_path: []const u16,
|
||||
target_path: []const u16,
|
||||
is_directory: bool,
|
||||
) CreateSymbolicLinkError!void {
|
||||
const SYMLINK_DATA = extern struct {
|
||||
|
@ -641,71 +603,19 @@ pub fn CreateSymbolicLinkW(
|
|||
Flags: ULONG,
|
||||
};
|
||||
|
||||
var symlink_handle: HANDLE = undefined;
|
||||
if (is_directory) {
|
||||
const sym_link_len_bytes = math.cast(u16, sym_link_path.len * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
};
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = sym_link_len_bytes,
|
||||
.MaximumLength = sym_link_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sym_link_path.ptr)),
|
||||
};
|
||||
|
||||
if (sym_link_path[0] == '.' and sym_link_path[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sym_link_path)) null else dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&symlink_handle,
|
||||
GENERIC_READ | SYNCHRONIZE | FILE_WRITE_ATTRIBUTES,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ,
|
||||
FILE_CREATE,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
} else {
|
||||
symlink_handle = OpenFile(sym_link_path, .{
|
||||
.access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
||||
.dir = dir,
|
||||
.creation = FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
}) catch |err| switch (err) {
|
||||
error.WouldBlock => unreachable,
|
||||
error.IsDir => return error.PathAlreadyExists,
|
||||
error.PipeBusy => unreachable,
|
||||
error.SharingViolation => return error.AccessDenied,
|
||||
else => |e| return e,
|
||||
};
|
||||
}
|
||||
const symlink_handle = OpenFile(sym_link_path, .{
|
||||
.access_mask = SYNCHRONIZE | GENERIC_READ | GENERIC_WRITE,
|
||||
.dir = dir,
|
||||
.creation = FILE_CREATE,
|
||||
.io_mode = .blocking,
|
||||
.open_dir = is_directory,
|
||||
}) catch |err| switch (err) {
|
||||
error.IsDir => return error.PathAlreadyExists,
|
||||
error.NotDir => unreachable,
|
||||
error.WouldBlock => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer CloseHandle(symlink_handle);
|
||||
|
||||
// prepare reparse data buffer
|
||||
|
@ -727,8 +637,7 @@ pub fn CreateSymbolicLinkW(
|
|||
@memcpy(buffer[@sizeOf(SYMLINK_DATA)..], @ptrCast([*]const u8, target_path), target_path.len * 2);
|
||||
const paths_start = @sizeOf(SYMLINK_DATA) + target_path.len * 2;
|
||||
@memcpy(buffer[paths_start..].ptr, @ptrCast([*]const u8, target_path), target_path.len * 2);
|
||||
// TODO replace with NtDeviceIoControl
|
||||
_ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null, null);
|
||||
_ = try DeviceIoControl(symlink_handle, FSCTL_SET_REPARSE_POINT, buffer[0..buf_len], null);
|
||||
}
|
||||
|
||||
pub const ReadLinkError = error{
|
||||
|
@ -737,44 +646,32 @@ pub const ReadLinkError = error{
|
|||
Unexpected,
|
||||
NameTooLong,
|
||||
UnsupportedReparsePointType,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
};
|
||||
|
||||
pub fn ReadLink(
|
||||
dir: ?HANDLE,
|
||||
sub_path: []const u8,
|
||||
out_buffer: []u8,
|
||||
) ReadLinkError![]u8 {
|
||||
const sub_path_w = try sliceToPrefixedFileW(sub_path);
|
||||
return ReadLinkW(dir, sub_path_w.span().ptr, out_buffer);
|
||||
}
|
||||
|
||||
pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
|
||||
pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLinkError![]u8 {
|
||||
// Here, we use `NtCreateFile` to shave off one syscall if we were to use `OpenFile` wrapper.
|
||||
// With the latter, we'd need to call `NtCreateFile` twice, once for file symlink, and if that
|
||||
// failed, again for dir symlink. Omitting any mention of file/dir flags makes it possible
|
||||
// to open the symlink there and then.
|
||||
const path_len_bytes = math.cast(u16, sub_path_w.len * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
};
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
var result_handle: HANDLE = undefined;
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result_handle,
|
||||
FILE_READ_ATTRIBUTES,
|
||||
|
@ -806,7 +703,7 @@ pub fn ReadLinkW(dir: ?HANDLE, sub_path_w: [*:0]const u16, out_buffer: []u8) Rea
|
|||
defer CloseHandle(result_handle);
|
||||
|
||||
var reparse_buf: [MAXIMUM_REPARSE_DATA_BUFFER_SIZE]u8 = undefined;
|
||||
_ = try DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..], null);
|
||||
_ = try DeviceIoControl(result_handle, FSCTL_GET_REPARSE_POINT, null, reparse_buf[0..]);
|
||||
|
||||
const reparse_struct = @ptrCast(*const REPARSE_DATA_BUFFER, @alignCast(@alignOf(REPARSE_DATA_BUFFER), &reparse_buf[0]));
|
||||
switch (reparse_struct.ReparseTag) {
|
||||
|
@ -848,24 +745,69 @@ pub const DeleteFileError = error{
|
|||
NameTooLong,
|
||||
FileBusy,
|
||||
Unexpected,
|
||||
NotDir,
|
||||
IsDir,
|
||||
};
|
||||
|
||||
pub fn DeleteFile(filename: []const u8) DeleteFileError!void {
|
||||
const filename_w = try sliceToPrefixedFileW(filename);
|
||||
return DeleteFileW(filename_w.span().ptr);
|
||||
}
|
||||
pub const DeleteFileOptions = struct {
|
||||
dir: ?HANDLE,
|
||||
remove_dir: bool = false,
|
||||
};
|
||||
|
||||
pub fn DeleteFileW(filename: [*:0]const u16) DeleteFileError!void {
|
||||
if (kernel32.DeleteFileW(filename) == 0) {
|
||||
switch (kernel32.GetLastError()) {
|
||||
.FILE_NOT_FOUND => return error.FileNotFound,
|
||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.FILENAME_EXCED_RANGE => return error.NameTooLong,
|
||||
.INVALID_PARAMETER => return error.NameTooLong,
|
||||
.SHARING_VIOLATION => return error.FileBusy,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
pub fn DeleteFile(sub_path_w: []const u16, options: DeleteFileOptions) DeleteFileError!void {
|
||||
const create_options_flags: ULONG = if (options.remove_dir)
|
||||
FILE_DELETE_ON_CLOSE | FILE_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT
|
||||
else
|
||||
FILE_DELETE_ON_CLOSE | FILE_NON_DIRECTORY_FILE | FILE_OPEN_REPARSE_POINT; // would we ever want to delete the target instead?
|
||||
|
||||
const path_len_bytes = @intCast(u16, sub_path_w.len * 2);
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
// The Windows API makes this mutable, but it will not mutate here.
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w.ptr)),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
||||
// Can't remove the parent directory with an open handle.
|
||||
return error.FileBusy;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsWTF16(sub_path_w)) null else options.dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
var tmp_handle: HANDLE = undefined;
|
||||
var rc = ntdll.NtCreateFile(
|
||||
&tmp_handle,
|
||||
SYNCHRONIZE | DELETE,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
0,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||
FILE_OPEN,
|
||||
create_options_flags,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => return CloseHandle(tmp_handle),
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.FILE_IS_A_DIRECTORY => return error.IsDir,
|
||||
.NOT_A_DIRECTORY => return error.NotDir,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -885,103 +827,6 @@ pub fn MoveFileExW(old_path: [*:0]const u16, new_path: [*:0]const u16, flags: DW
|
|||
}
|
||||
}
|
||||
|
||||
pub const CreateDirectoryError = error{
|
||||
NameTooLong,
|
||||
PathAlreadyExists,
|
||||
FileNotFound,
|
||||
NoDevice,
|
||||
AccessDenied,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
Unexpected,
|
||||
};
|
||||
|
||||
/// Returns an open directory handle which the caller is responsible for closing with `CloseHandle`.
|
||||
pub fn CreateDirectory(dir: ?HANDLE, pathname: []const u8, sa: ?*SECURITY_ATTRIBUTES) CreateDirectoryError!HANDLE {
|
||||
const pathname_w = try sliceToPrefixedFileW(pathname);
|
||||
return CreateDirectoryW(dir, pathname_w.span().ptr, sa);
|
||||
}
|
||||
|
||||
/// Same as `CreateDirectory` except takes a WTF-16 encoded path.
|
||||
pub fn CreateDirectoryW(
|
||||
dir: ?HANDLE,
|
||||
sub_path_w: [*:0]const u16,
|
||||
sa: ?*SECURITY_ATTRIBUTES,
|
||||
) CreateDirectoryError!HANDLE {
|
||||
const path_len_bytes = math.cast(u16, mem.lenZ(sub_path_w) * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
};
|
||||
var nt_name = UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
||||
};
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
// Windows does not recognize this, but it does work with empty string.
|
||||
nt_name.Length = 0;
|
||||
}
|
||||
|
||||
var attr = OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dir,
|
||||
.Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here.
|
||||
.ObjectName = &nt_name,
|
||||
.SecurityDescriptor = if (sa) |ptr| ptr.lpSecurityDescriptor else null,
|
||||
.SecurityQualityOfService = null,
|
||||
};
|
||||
var io: IO_STATUS_BLOCK = undefined;
|
||||
var result_handle: HANDLE = undefined;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result_handle,
|
||||
GENERIC_READ | SYNCHRONIZE,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_READ,
|
||||
FILE_CREATE,
|
||||
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => return result_handle,
|
||||
.OBJECT_NAME_INVALID => unreachable,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.NO_MEDIA_IN_DEVICE => return error.NoDevice,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const RemoveDirectoryError = error{
|
||||
FileNotFound,
|
||||
DirNotEmpty,
|
||||
Unexpected,
|
||||
NotDir,
|
||||
};
|
||||
|
||||
pub fn RemoveDirectory(dir_path: []const u8) RemoveDirectoryError!void {
|
||||
const dir_path_w = try sliceToPrefixedFileW(dir_path);
|
||||
return RemoveDirectoryW(dir_path_w.span().ptr);
|
||||
}
|
||||
|
||||
pub fn RemoveDirectoryW(dir_path_w: [*:0]const u16) RemoveDirectoryError!void {
|
||||
if (kernel32.RemoveDirectoryW(dir_path_w) == 0) {
|
||||
switch (kernel32.GetLastError()) {
|
||||
.PATH_NOT_FOUND => return error.FileNotFound,
|
||||
.DIR_NOT_EMPTY => return error.DirNotEmpty,
|
||||
.DIRECTORY => return error.NotDir,
|
||||
else => |err| return unexpectedError(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const GetStdHandleError = error{
|
||||
NoStandardHandleAttached,
|
||||
Unexpected,
|
||||
|
@ -1463,8 +1308,7 @@ pub fn cStrToPrefixedFileW(s: [*:0]const u8) !PathSpace {
|
|||
}
|
||||
|
||||
/// Converts the path `s` to WTF16, null-terminated. If the path is absolute,
|
||||
/// it will get NT-style prefix `\??\` prepended automatically. For prepending
|
||||
/// Win32-style prefix, see `sliceToWin32PrefixedFileW` instead.
|
||||
/// it will get NT-style prefix `\??\` prepended automatically.
|
||||
pub fn sliceToPrefixedFileW(s: []const u8) !PathSpace {
|
||||
// TODO https://github.com/ziglang/zig/issues/2765
|
||||
var path_space: PathSpace = undefined;
|
||||
|
|
|
@ -54,6 +54,18 @@ pub extern "NtDll" fn NtDeviceIoControlFile(
|
|||
OutputBuffer: ?PVOID,
|
||||
OutputBufferLength: ULONG,
|
||||
) callconv(.Stdcall) NTSTATUS;
|
||||
pub extern "NtDll" fn NtFsControlFile(
|
||||
FileHandle: HANDLE,
|
||||
Event: ?HANDLE,
|
||||
ApcRoutine: ?IO_APC_ROUTINE,
|
||||
ApcContext: ?*c_void,
|
||||
IoStatusBlock: *IO_STATUS_BLOCK,
|
||||
FsControlCode: ULONG,
|
||||
InputBuffer: ?*const c_void,
|
||||
InputBufferLength: ULONG,
|
||||
OutputBuffer: ?PVOID,
|
||||
OutputBufferLength: ULONG,
|
||||
) callconv(.Stdcall) NTSTATUS;
|
||||
pub extern "NtDll" fn NtClose(Handle: HANDLE) callconv(.Stdcall) NTSTATUS;
|
||||
pub extern "NtDll" fn RtlDosPathNameToNtPathName_U(
|
||||
DosPathName: [*:0]const u16,
|
||||
|
|
|
@ -92,6 +92,7 @@ comptime {
|
|||
@export(@import("compiler_rt/floatunsidf.zig").__floatunsidf, .{ .name = "__floatunsidf", .linkage = linkage });
|
||||
@export(@import("compiler_rt/floatundidf.zig").__floatundidf, .{ .name = "__floatundidf", .linkage = linkage });
|
||||
|
||||
@export(@import("compiler_rt/floatditf.zig").__floatditf, .{ .name = "__floatditf", .linkage = linkage });
|
||||
@export(@import("compiler_rt/floattitf.zig").__floattitf, .{ .name = "__floattitf", .linkage = linkage });
|
||||
@export(@import("compiler_rt/floattidf.zig").__floattidf, .{ .name = "__floattidf", .linkage = linkage });
|
||||
@export(@import("compiler_rt/floattisf.zig").__floattisf, .{ .name = "__floattisf", .linkage = linkage });
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
const builtin = @import("builtin");
|
||||
const is_test = builtin.is_test;
|
||||
const std = @import("std");
|
||||
const maxInt = std.math.maxInt;
|
||||
|
||||
const significandBits = 112;
|
||||
const exponentBias = 16383;
|
||||
const implicitBit = (@as(u128, 1) << significandBits);
|
||||
|
||||
pub fn __floatditf(arg: i64) callconv(.C) f128 {
|
||||
@setRuntimeSafety(is_test);
|
||||
|
||||
if (arg == 0)
|
||||
return 0.0;
|
||||
|
||||
// All other cases begin by extracting the sign and absolute value of a
|
||||
var sign: u128 = 0;
|
||||
var aAbs = @bitCast(u64, arg);
|
||||
if (arg < 0) {
|
||||
sign = 1 << 127;
|
||||
aAbs = ~@bitCast(u64, arg)+ 1;
|
||||
}
|
||||
|
||||
// Exponent of (fp_t)a is the width of abs(a).
|
||||
const exponent = 63 - @clz(u64, aAbs);
|
||||
var result: u128 = undefined;
|
||||
|
||||
// Shift a into the significand field, rounding if it is a right-shift
|
||||
const shift = significandBits - exponent;
|
||||
result = @as(u128, aAbs) << shift ^ implicitBit;
|
||||
|
||||
result += (@as(u128, exponent) + exponentBias) << significandBits;
|
||||
return @bitCast(f128, result | sign);
|
||||
}
|
||||
|
||||
test "import floatditf" {
|
||||
_ = @import("floatditf_test.zig");
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
const __floatditf = @import("floatditf.zig").__floatditf;
|
||||
const testing = @import("std").testing;
|
||||
|
||||
fn test__floatditf(a: i64, expected: f128) void {
|
||||
const x = __floatditf(a);
|
||||
testing.expect(x == expected);
|
||||
}
|
||||
|
||||
test "floatditf" {
|
||||
test__floatditf(0x7fffffffffffffff, make_ti(0x403dffffffffffff, 0xfffc000000000000));
|
||||
test__floatditf(0x123456789abcdef1, make_ti(0x403b23456789abcd, 0xef10000000000000));
|
||||
test__floatditf(0x2, make_ti(0x4000000000000000, 0x0));
|
||||
test__floatditf(0x1, make_ti(0x3fff000000000000, 0x0));
|
||||
test__floatditf(0x0, make_ti(0x0, 0x0));
|
||||
test__floatditf(@bitCast(i64, @as(u64, 0xffffffffffffffff)), make_ti(0xbfff000000000000, 0x0));
|
||||
test__floatditf(@bitCast(i64, @as(u64, 0xfffffffffffffffe)), make_ti(0xc000000000000000, 0x0));
|
||||
test__floatditf(-0x123456789abcdef1, make_ti(0xc03b23456789abcd, 0xef10000000000000));
|
||||
test__floatditf(@bitCast(i64, @as(u64, 0x8000000000000000)), make_ti(0xc03e000000000000, 0x0));
|
||||
}
|
||||
|
||||
fn make_ti(high: u64, low: u64) f128 {
|
||||
var result: u128 = high;
|
||||
result <<= 64;
|
||||
result |= low;
|
||||
return @bitCast(f128, result);
|
||||
}
|
|
@ -171,6 +171,59 @@ test "expectEqual.union(enum)" {
|
|||
expectEqual(a10, a10);
|
||||
}
|
||||
|
||||
/// This function is intended to be used only in tests. When the actual value is not
|
||||
/// within the margin of the expected value,
|
||||
/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
|
||||
/// The types must be floating point
|
||||
pub fn expectWithinMargin(expected: anytype, actual: @TypeOf(expected), margin: @TypeOf(expected)) void {
|
||||
std.debug.assert(margin >= 0.0);
|
||||
|
||||
switch (@typeInfo(@TypeOf(actual))) {
|
||||
.Float,
|
||||
.ComptimeFloat,
|
||||
=> {
|
||||
if (@fabs(expected - actual) > margin) {
|
||||
std.debug.panic("actual {}, not within margin {} of expected {}", .{ actual, margin, expected });
|
||||
}
|
||||
},
|
||||
else => @compileError("Unable to compare non floating point values"),
|
||||
}
|
||||
}
|
||||
|
||||
test "expectWithinMargin.f32" {
|
||||
const x: f32 = 12.0;
|
||||
const y: f32 = 12.06;
|
||||
|
||||
expectWithinMargin(x, y, 0.1);
|
||||
}
|
||||
|
||||
/// This function is intended to be used only in tests. When the actual value is not
|
||||
/// within the epsilon of the expected value,
|
||||
/// prints diagnostics to stderr to show exactly how they are not equal, then aborts.
|
||||
/// The types must be floating point
|
||||
pub fn expectWithinEpsilon(expected: anytype, actual: @TypeOf(expected), epsilon: @TypeOf(expected)) void {
|
||||
std.debug.assert(epsilon >= 0.0 and epsilon <= 1.0);
|
||||
|
||||
const margin = epsilon * expected;
|
||||
switch (@typeInfo(@TypeOf(actual))) {
|
||||
.Float,
|
||||
.ComptimeFloat,
|
||||
=> {
|
||||
if (@fabs(expected - actual) > margin) {
|
||||
std.debug.panic("actual {}, not within epsilon {}, of expected {}", .{ actual, epsilon, expected });
|
||||
}
|
||||
},
|
||||
else => @compileError("Unable to compare non floating point values"),
|
||||
}
|
||||
}
|
||||
|
||||
test "expectWithinEpsilon.f32" {
|
||||
const x: f32 = 12.0;
|
||||
const y: f32 = 13.2;
|
||||
|
||||
expectWithinEpsilon(x, y, 0.1);
|
||||
}
|
||||
|
||||
/// This function is intended to be used only in tests. When the two slices are not
|
||||
/// equal, prints diagnostics to stderr to show exactly how they are not equal,
|
||||
/// then aborts.
|
||||
|
|
|
@ -43,6 +43,22 @@ pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usi
|
|||
return .{ .line = line, .column = column };
|
||||
}
|
||||
|
||||
pub fn lineDelta(source: []const u8, start: usize, end: usize) isize {
|
||||
var line: isize = 0;
|
||||
if (end >= start) {
|
||||
for (source[start..end]) |byte| switch (byte) {
|
||||
'\n' => line += 1,
|
||||
else => continue,
|
||||
};
|
||||
} else {
|
||||
for (source[end..start]) |byte| switch (byte) {
|
||||
'\n' => line -= 1,
|
||||
else => continue,
|
||||
};
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
/// Returns the standard file system basename of a binary generated by the Zig compiler.
|
||||
pub fn binNameAlloc(
|
||||
allocator: *std.mem.Allocator,
|
||||
|
|
|
@ -1299,6 +1299,10 @@ pub const Node = struct {
|
|||
});
|
||||
}
|
||||
|
||||
pub fn body(self: *const FnProto) ?*Node {
|
||||
return self.getTrailer("body_node");
|
||||
}
|
||||
|
||||
pub fn getTrailer(self: *const FnProto, comptime name: []const u8) ?TrailerFlags.Field(name) {
|
||||
const trailers_start = @alignCast(
|
||||
@alignOf(ParamDecl),
|
||||
|
@ -1381,7 +1385,7 @@ pub const Node = struct {
|
|||
.Invalid => {},
|
||||
}
|
||||
|
||||
if (self.getTrailer("body_node")) |body_node| {
|
||||
if (self.body()) |body_node| {
|
||||
if (i < 1) return body_node;
|
||||
i -= 1;
|
||||
}
|
||||
|
@ -1397,7 +1401,7 @@ pub const Node = struct {
|
|||
}
|
||||
|
||||
pub fn lastToken(self: *const FnProto) TokenIndex {
|
||||
if (self.getTrailer("body_node")) |body_node| return body_node.lastToken();
|
||||
if (self.body()) |body_node| return body_node.lastToken();
|
||||
switch (self.return_type) {
|
||||
.Explicit, .InferErrorSet => |node| return node.lastToken(),
|
||||
.Invalid => |tok| return tok,
|
||||
|
|
|
@ -201,7 +201,16 @@ const Parser = struct {
|
|||
p.findNextContainerMember();
|
||||
const next = p.token_ids[p.tok_i];
|
||||
switch (next) {
|
||||
.Eof => break,
|
||||
.Eof => {
|
||||
// no invalid tokens were found
|
||||
if (index == p.tok_i) break;
|
||||
|
||||
// Invalid tokens, add error and exit
|
||||
try p.errors.append(p.gpa, .{
|
||||
.ExpectedToken = .{ .token = index, .expected_id = .Comma },
|
||||
});
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
if (next == .RBrace) {
|
||||
if (!top_level) break;
|
||||
|
|
|
@ -293,6 +293,14 @@ test "zig fmt: decl between fields" {
|
|||
});
|
||||
}
|
||||
|
||||
test "zig fmt: eof after missing comma" {
|
||||
try testError(
|
||||
\\foo()
|
||||
, &[_]Error{
|
||||
.ExpectedToken,
|
||||
});
|
||||
}
|
||||
|
||||
test "zig fmt: errdefer with payload" {
|
||||
try testCanonical(
|
||||
\\pub fn main() anyerror!void {
|
||||
|
|
|
@ -104,7 +104,6 @@ pub fn parse(
|
|||
return error.InvalidCharacter;
|
||||
},
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
unreachable;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,8 +1,11 @@
|
|||
pub const Table = std.StringHashMap(*Package);
|
||||
|
||||
/// This should be used for file operations.
|
||||
root_src_dir: std.fs.Dir,
|
||||
/// Relative to `root_src_dir`.
|
||||
root_src_path: []const u8,
|
||||
/// This is for metadata purposes, for example putting into debug information.
|
||||
root_src_dir_path: []u8,
|
||||
/// Relative to `root_src_dir` and `root_src_dir_path`.
|
||||
root_src_path: []u8,
|
||||
table: Table,
|
||||
|
||||
/// No references to `root_src_dir` and `root_src_path` are kept.
|
||||
|
@ -18,8 +21,11 @@ pub fn create(
|
|||
errdefer allocator.destroy(ptr);
|
||||
const root_src_path_dupe = try mem.dupe(allocator, u8, root_src_path);
|
||||
errdefer allocator.free(root_src_path_dupe);
|
||||
const root_src_dir_path = try mem.dupe(allocator, u8, root_src_dir);
|
||||
errdefer allocator.free(root_src_dir_path);
|
||||
ptr.* = .{
|
||||
.root_src_dir = try base_dir.openDir(root_src_dir, .{}),
|
||||
.root_src_dir_path = root_src_dir_path,
|
||||
.root_src_path = root_src_path_dupe,
|
||||
.table = Table.init(allocator),
|
||||
};
|
||||
|
@ -30,6 +36,7 @@ pub fn destroy(self: *Package) void {
|
|||
const allocator = self.table.allocator;
|
||||
self.root_src_dir.close();
|
||||
allocator.free(self.root_src_path);
|
||||
allocator.free(self.root_src_dir_path);
|
||||
{
|
||||
var it = self.table.iterator();
|
||||
while (it.next()) |kv| {
|
||||
|
@ -41,10 +48,9 @@ pub fn destroy(self: *Package) void {
|
|||
}
|
||||
|
||||
pub fn add(self: *Package, name: []const u8, package: *Package) !void {
|
||||
try self.table.ensureCapacity(self.table.items().len + 1);
|
||||
const name_dupe = try mem.dupe(self.table.allocator, u8, name);
|
||||
errdefer self.table.allocator.deinit(name_dupe);
|
||||
const entry = try self.table.put(name_dupe, package);
|
||||
assert(entry == null);
|
||||
self.table.putAssumeCapacityNoClobber(name_dupe, package);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
|
|
|
@ -17,6 +17,9 @@ pub const ResultLoc = union(enum) {
|
|||
discard,
|
||||
/// The expression has an inferred type, and it will be evaluated as an rvalue.
|
||||
none,
|
||||
/// The expression must generate a pointer rather than a value. For example, the left hand side
|
||||
/// of an assignment uses an "LValue" result location.
|
||||
lvalue,
|
||||
/// The expression will be type coerced into this type, but it will be evaluated as an rvalue.
|
||||
ty: *zir.Inst,
|
||||
/// The expression must store its result into this typed pointer.
|
||||
|
@ -33,7 +36,7 @@ pub const ResultLoc = union(enum) {
|
|||
|
||||
pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
|
||||
const type_src = scope.tree().token_locs[type_node.firstToken()].start;
|
||||
const type_type = try mod.addZIRInstConst(scope, type_src, .{
|
||||
const type_type = try addZIRInstConst(mod, scope, type_src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
|
@ -46,18 +49,45 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr
|
|||
switch (node.tag) {
|
||||
.VarDecl => unreachable, // Handled in `blockExpr`.
|
||||
.Assign => unreachable, // Handled in `blockExpr`.
|
||||
.AssignBitAnd => unreachable, // Handled in `blockExpr`.
|
||||
.AssignBitOr => unreachable, // Handled in `blockExpr`.
|
||||
.AssignBitShiftLeft => unreachable, // Handled in `blockExpr`.
|
||||
.AssignBitShiftRight => unreachable, // Handled in `blockExpr`.
|
||||
.AssignBitXor => unreachable, // Handled in `blockExpr`.
|
||||
.AssignDiv => unreachable, // Handled in `blockExpr`.
|
||||
.AssignSub => unreachable, // Handled in `blockExpr`.
|
||||
.AssignSubWrap => unreachable, // Handled in `blockExpr`.
|
||||
.AssignMod => unreachable, // Handled in `blockExpr`.
|
||||
.AssignAdd => unreachable, // Handled in `blockExpr`.
|
||||
.AssignAddWrap => unreachable, // Handled in `blockExpr`.
|
||||
.AssignMul => unreachable, // Handled in `blockExpr`.
|
||||
.AssignMulWrap => unreachable, // Handled in `blockExpr`.
|
||||
|
||||
.Add => return arithmetic(mod, scope, rl, node.castTag(.Add).?, .add),
|
||||
.Sub => return arithmetic(mod, scope, rl, node.castTag(.Sub).?, .sub),
|
||||
.Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
|
||||
.AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
|
||||
.Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
|
||||
.SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
|
||||
.Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
|
||||
.MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
|
||||
.Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
|
||||
.Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
|
||||
.BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bitand),
|
||||
.BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bitor),
|
||||
.BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
|
||||
.BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
|
||||
.BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
|
||||
|
||||
.BangEqual => return cmp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
|
||||
.EqualEqual => return cmp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
|
||||
.GreaterThan => return cmp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
|
||||
.GreaterOrEqual => return cmp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
|
||||
.LessThan => return cmp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
|
||||
.LessOrEqual => return cmp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
|
||||
.BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
|
||||
.EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
|
||||
.GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
|
||||
.GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
|
||||
.LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
|
||||
.LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
|
||||
|
||||
.Identifier => return rlWrap(mod, scope, rl, try identifier(mod, scope, node.castTag(.Identifier).?)),
|
||||
.ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
|
||||
.ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
|
||||
|
||||
.Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
|
||||
.Asm => return rlWrap(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
|
||||
.StringLiteral => return rlWrap(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
|
||||
.IntegerLiteral => return rlWrap(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
|
||||
|
@ -90,6 +120,8 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
|
|||
|
||||
var scope = parent_scope;
|
||||
for (block_node.statements()) |statement| {
|
||||
const src = scope.tree().token_locs[statement.firstToken()].start;
|
||||
_ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
|
||||
switch (statement.tag) {
|
||||
.VarDecl => {
|
||||
const var_decl_node = statement.castTag(.VarDecl).?;
|
||||
|
@ -99,10 +131,25 @@ pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block
|
|||
const ass = statement.castTag(.Assign).?;
|
||||
try assign(mod, scope, ass);
|
||||
},
|
||||
.AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bitand),
|
||||
.AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bitor),
|
||||
.AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
|
||||
.AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
|
||||
.AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
|
||||
.AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
|
||||
.AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
|
||||
.AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
|
||||
.AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
|
||||
.AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
|
||||
.AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
|
||||
.AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
|
||||
.AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
|
||||
|
||||
else => {
|
||||
const possibly_unused_result = try expr(mod, scope, .none, statement);
|
||||
const src = scope.tree().token_locs[statement.firstToken()].start;
|
||||
_ = try mod.addZIRUnOp(scope, src, .ensure_result_used, possibly_unused_result);
|
||||
if (!possibly_unused_result.tag.isNoReturn()) {
|
||||
_ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +180,7 @@ fn varDecl(
|
|||
if (nodeMayNeedMemoryLocation(init_node)) {
|
||||
if (node.getTrailer("type_node")) |type_node| {
|
||||
const type_inst = try typeExpr(mod, scope, type_node);
|
||||
const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst);
|
||||
const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
|
||||
const result_loc: ResultLoc = .{ .ptr = alloc };
|
||||
const init_inst = try expr(mod, scope, result_loc, init_node);
|
||||
const sub_scope = try block_arena.create(Scope.LocalVal);
|
||||
|
@ -145,7 +192,7 @@ fn varDecl(
|
|||
};
|
||||
return &sub_scope.base;
|
||||
} else {
|
||||
const alloc = try mod.addZIRNoOpT(scope, name_src, .alloc_inferred);
|
||||
const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
|
||||
const result_loc: ResultLoc = .{ .inferred_ptr = alloc };
|
||||
const init_inst = try expr(mod, scope, result_loc, init_node);
|
||||
const sub_scope = try block_arena.create(Scope.LocalVal);
|
||||
|
@ -176,7 +223,7 @@ fn varDecl(
|
|||
.Keyword_var => {
|
||||
if (node.getTrailer("type_node")) |type_node| {
|
||||
const type_inst = try typeExpr(mod, scope, type_node);
|
||||
const alloc = try mod.addZIRUnOp(scope, name_src, .alloc, type_inst);
|
||||
const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
|
||||
const result_loc: ResultLoc = .{ .ptr = alloc };
|
||||
const init_inst = try expr(mod, scope, result_loc, init_node);
|
||||
const sub_scope = try block_arena.create(Scope.LocalPtr);
|
||||
|
@ -188,7 +235,7 @@ fn varDecl(
|
|||
};
|
||||
return &sub_scope.base;
|
||||
} else {
|
||||
const alloc = try mod.addZIRNoOp(scope, name_src, .alloc_inferred);
|
||||
const alloc = try addZIRNoOp(mod, scope, name_src, .alloc_inferred);
|
||||
const result_loc = .{ .inferred_ptr = alloc.castTag(.alloc_inferred).? };
|
||||
const init_inst = try expr(mod, scope, result_loc, init_node);
|
||||
const sub_scope = try block_arena.create(Scope.LocalPtr);
|
||||
|
@ -207,28 +254,44 @@ fn varDecl(
|
|||
|
||||
fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
|
||||
if (infix_node.lhs.castTag(.Identifier)) |ident| {
|
||||
const tree = scope.tree();
|
||||
const ident_name = try identifierTokenString(mod, scope, ident.token);
|
||||
// This intentionally does not support @"_" syntax.
|
||||
const ident_name = scope.tree().tokenSlice(ident.token);
|
||||
if (std.mem.eql(u8, ident_name, "_")) {
|
||||
_ = try expr(mod, scope, .discard, infix_node.rhs);
|
||||
return;
|
||||
} else {
|
||||
return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
|
||||
}
|
||||
} else {
|
||||
return mod.failNode(scope, &infix_node.base, "TODO implement infix operator assign", .{});
|
||||
}
|
||||
const lvalue = try expr(mod, scope, .lvalue, infix_node.lhs);
|
||||
_ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
|
||||
}
|
||||
|
||||
fn assignOp(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
infix_node: *ast.Node.SimpleInfixOp,
|
||||
op_inst_tag: zir.Inst.Tag,
|
||||
) InnerError!void {
|
||||
const lhs_ptr = try expr(mod, scope, .lvalue, infix_node.lhs);
|
||||
const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
|
||||
const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
|
||||
const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
|
||||
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[infix_node.op_token].start;
|
||||
|
||||
const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
|
||||
_ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
|
||||
}
|
||||
|
||||
fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.op_token].start;
|
||||
const bool_type = try mod.addZIRInstConst(scope, src, .{
|
||||
const bool_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.bool_type),
|
||||
});
|
||||
const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
|
||||
return mod.addZIRUnOp(scope, src, .boolnot, operand);
|
||||
return addZIRUnOp(mod, scope, src, .boolnot, operand);
|
||||
}
|
||||
|
||||
/// Identifier token -> String (allocated in scope.arena())
|
||||
|
@ -257,7 +320,7 @@ pub fn identifierStringInst(mod: *Module, scope: *Scope, node: *ast.Node.OneToke
|
|||
|
||||
const ident_name = try identifierTokenString(mod, scope, node.token);
|
||||
|
||||
return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
|
||||
return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = ident_name }, .{});
|
||||
}
|
||||
|
||||
fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
|
||||
|
@ -268,47 +331,31 @@ fn field(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp) InnerError!
|
|||
const lhs = try expr(mod, scope, .none, node.lhs);
|
||||
const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
|
||||
|
||||
const pointer = try mod.addZIRInst(scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
|
||||
return mod.addZIRUnOp(scope, src, .deref, pointer);
|
||||
const pointer = try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{});
|
||||
return addZIRUnOp(mod, scope, src, .deref, pointer);
|
||||
}
|
||||
|
||||
fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.rtoken].start;
|
||||
const lhs = try expr(mod, scope, .none, node.lhs);
|
||||
return mod.addZIRUnOp(scope, src, .deref, lhs);
|
||||
return addZIRUnOp(mod, scope, src, .deref, lhs);
|
||||
}
|
||||
|
||||
fn cmp(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
infix_node: *ast.Node.SimpleInfixOp,
|
||||
cmp_inst_tag: zir.Inst.Tag,
|
||||
) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[infix_node.op_token].start;
|
||||
|
||||
const lhs = try expr(mod, scope, .none, infix_node.lhs);
|
||||
const rhs = try expr(mod, scope, .none, infix_node.rhs);
|
||||
const result = try mod.addZIRBinOp(scope, src, cmp_inst_tag, lhs, rhs);
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
|
||||
fn arithmetic(
|
||||
fn simpleBinOp(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
rl: ResultLoc,
|
||||
infix_node: *ast.Node.SimpleInfixOp,
|
||||
op_inst_tag: zir.Inst.Tag,
|
||||
) InnerError!*zir.Inst {
|
||||
const lhs = try expr(mod, scope, .none, infix_node.lhs);
|
||||
const rhs = try expr(mod, scope, .none, infix_node.rhs);
|
||||
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[infix_node.op_token].start;
|
||||
|
||||
const result = try mod.addZIRBinOp(scope, src, op_inst_tag, lhs, rhs);
|
||||
const lhs = try expr(mod, scope, .none, infix_node.lhs);
|
||||
const rhs = try expr(mod, scope, .none, infix_node.rhs);
|
||||
|
||||
const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
|
||||
|
@ -331,19 +378,19 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
|
|||
|
||||
const tree = scope.tree();
|
||||
const if_src = tree.token_locs[if_node.if_token].start;
|
||||
const bool_type = try mod.addZIRInstConst(scope, if_src, .{
|
||||
const bool_type = try addZIRInstConst(mod, scope, if_src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.bool_type),
|
||||
});
|
||||
const cond = try expr(mod, &block_scope.base, .{ .ty = bool_type }, if_node.condition);
|
||||
|
||||
const condbr = try mod.addZIRInstSpecial(&block_scope.base, if_src, zir.Inst.CondBr, .{
|
||||
const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
|
||||
.condition = cond,
|
||||
.then_body = undefined, // populated below
|
||||
.else_body = undefined, // populated below
|
||||
}, .{});
|
||||
|
||||
const block = try mod.addZIRInstBlock(scope, if_src, .{
|
||||
const block = try addZIRInstBlock(mod, scope, if_src, .{
|
||||
.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
|
||||
});
|
||||
var then_scope: Scope.GenZIR = .{
|
||||
|
@ -359,14 +406,14 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
|
|||
// proper type inference requires peer type resolution on the if's
|
||||
// branches.
|
||||
const branch_rl: ResultLoc = switch (rl) {
|
||||
.discard, .none, .ty, .ptr => rl,
|
||||
.discard, .none, .ty, .ptr, .lvalue => rl,
|
||||
.inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
|
||||
};
|
||||
|
||||
const then_result = try expr(mod, &then_scope.base, branch_rl, if_node.body);
|
||||
if (!then_result.tag.isNoReturn()) {
|
||||
const then_src = tree.token_locs[if_node.body.lastToken()].start;
|
||||
_ = try mod.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{
|
||||
_ = try addZIRInst(mod, &then_scope.base, then_src, zir.Inst.Break, .{
|
||||
.block = block,
|
||||
.operand = then_result,
|
||||
}, .{});
|
||||
|
@ -387,7 +434,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
|
|||
const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
|
||||
if (!else_result.tag.isNoReturn()) {
|
||||
const else_src = tree.token_locs[else_node.body.lastToken()].start;
|
||||
_ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{
|
||||
_ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
|
||||
.block = block,
|
||||
.operand = else_result,
|
||||
}, .{});
|
||||
|
@ -396,7 +443,7 @@ fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) Inn
|
|||
// TODO Optimization opportunity: we can avoid an allocation and a memcpy here
|
||||
// by directly allocating the body for this one instruction.
|
||||
const else_src = tree.token_locs[if_node.lastToken()].start;
|
||||
_ = try mod.addZIRInst(&else_scope.base, else_src, zir.Inst.BreakVoid, .{
|
||||
_ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
|
||||
.block = block,
|
||||
}, .{});
|
||||
}
|
||||
|
@ -412,20 +459,20 @@ fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerE
|
|||
const src = tree.token_locs[cfe.ltoken].start;
|
||||
if (cfe.getRHS()) |rhs_node| {
|
||||
if (nodeMayNeedMemoryLocation(rhs_node)) {
|
||||
const ret_ptr = try mod.addZIRNoOp(scope, src, .ret_ptr);
|
||||
const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
|
||||
const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
|
||||
return mod.addZIRUnOp(scope, src, .@"return", operand);
|
||||
return addZIRUnOp(mod, scope, src, .@"return", operand);
|
||||
} else {
|
||||
const fn_ret_ty = try mod.addZIRNoOp(scope, src, .ret_type);
|
||||
const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
|
||||
const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
|
||||
return mod.addZIRUnOp(scope, src, .@"return", operand);
|
||||
return addZIRUnOp(mod, scope, src, .@"return", operand);
|
||||
}
|
||||
} else {
|
||||
return mod.addZIRNoOp(scope, src, .returnvoid);
|
||||
return addZIRNoOp(mod, scope, src, .returnvoid);
|
||||
}
|
||||
}
|
||||
|
||||
fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
|
||||
fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
|
||||
const tracy = trace(@src());
|
||||
defer tracy.end();
|
||||
|
||||
|
@ -437,7 +484,8 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
|
|||
}
|
||||
|
||||
if (getSimplePrimitiveValue(ident_name)) |typed_value| {
|
||||
return mod.addZIRInstConst(scope, src, typed_value);
|
||||
const result = try addZIRInstConst(mod, scope, src, typed_value);
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
|
||||
if (ident_name.len >= 2) integer: {
|
||||
|
@ -461,16 +509,18 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
|
|||
else => {
|
||||
const int_type_payload = try scope.arena().create(Value.Payload.IntType);
|
||||
int_type_payload.* = .{ .signed = is_signed, .bits = bit_count };
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
const result = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.comptime_int),
|
||||
.val = Value.initPayload(&int_type_payload.base),
|
||||
});
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
},
|
||||
};
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
const result = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = val,
|
||||
});
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,14 +531,19 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
|
|||
.local_val => {
|
||||
const local_val = s.cast(Scope.LocalVal).?;
|
||||
if (mem.eql(u8, local_val.name, ident_name)) {
|
||||
return local_val.inst;
|
||||
return rlWrap(mod, scope, rl, local_val.inst);
|
||||
}
|
||||
s = local_val.parent;
|
||||
},
|
||||
.local_ptr => {
|
||||
const local_ptr = s.cast(Scope.LocalPtr).?;
|
||||
if (mem.eql(u8, local_ptr.name, ident_name)) {
|
||||
return try mod.addZIRUnOp(scope, src, .deref, local_ptr.ptr);
|
||||
if (rl == .lvalue) {
|
||||
return local_ptr.ptr;
|
||||
} else {
|
||||
const result = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
}
|
||||
s = local_ptr.parent;
|
||||
},
|
||||
|
@ -498,7 +553,9 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.OneToken) InnerError
|
|||
}
|
||||
|
||||
if (mod.lookupDeclName(scope, ident_name)) |decl| {
|
||||
return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
|
||||
// TODO handle lvalues
|
||||
const result = try addZIRInst(mod, scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
|
||||
return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name});
|
||||
|
@ -520,7 +577,7 @@ fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) Inner
|
|||
};
|
||||
|
||||
const src = tree.token_locs[str_lit.token].start;
|
||||
return mod.addZIRInst(scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
|
||||
return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
|
||||
}
|
||||
|
||||
fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
|
||||
|
@ -545,7 +602,7 @@ fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) Inne
|
|||
const int_payload = try arena.create(Value.Payload.Int_u64);
|
||||
int_payload.* = .{ .int = small_int };
|
||||
const src = tree.token_locs[int_lit.token].start;
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
return addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.comptime_int),
|
||||
.val = Value.initPayload(&int_payload.base),
|
||||
});
|
||||
|
@ -568,7 +625,7 @@ fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) Inne
|
|||
const float_payload = try arena.create(Value.Payload.Float_128);
|
||||
float_payload.* = .{ .val = val };
|
||||
const src = tree.token_locs[float_lit.token].start;
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
return addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.comptime_float),
|
||||
.val = Value.initPayload(&float_payload.base),
|
||||
});
|
||||
|
@ -578,7 +635,7 @@ fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerErro
|
|||
const arena = scope.arena();
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.token].start;
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
return addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.@"undefined"),
|
||||
.val = Value.initTag(.undef),
|
||||
});
|
||||
|
@ -588,7 +645,7 @@ fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
|
|||
const arena = scope.arena();
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.token].start;
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
return addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.bool),
|
||||
.val = switch (tree.token_ids[node.token]) {
|
||||
.Keyword_true => Value.initTag(.bool_true),
|
||||
|
@ -602,7 +659,7 @@ fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError
|
|||
const arena = scope.arena();
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[node.token].start;
|
||||
return mod.addZIRInstConst(scope, src, .{
|
||||
return addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.@"null"),
|
||||
.val = Value.initTag(.null_value),
|
||||
});
|
||||
|
@ -620,7 +677,7 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
|
|||
|
||||
const src = tree.token_locs[asm_node.asm_token].start;
|
||||
|
||||
const str_type = try mod.addZIRInstConst(scope, src, .{
|
||||
const str_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.const_slice_u8_type),
|
||||
});
|
||||
|
@ -632,11 +689,11 @@ fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zi
|
|||
args[i] = try expr(mod, scope, .none, input.expr);
|
||||
}
|
||||
|
||||
const return_type = try mod.addZIRInstConst(scope, src, .{
|
||||
const return_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.void_type),
|
||||
});
|
||||
const asm_inst = try mod.addZIRInst(scope, src, zir.Inst.Asm, .{
|
||||
const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
|
||||
.asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
|
||||
.return_type = return_type,
|
||||
}, .{
|
||||
|
@ -666,14 +723,14 @@ fn simpleCast(
|
|||
try ensureBuiltinParamCount(mod, scope, call, 2);
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[call.builtin_token].start;
|
||||
const type_type = try mod.addZIRInstConst(scope, src, .{
|
||||
const type_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
const params = call.params();
|
||||
const dest_type = try expr(mod, scope, .{ .ty = type_type }, params[0]);
|
||||
const rhs = try expr(mod, scope, .none, params[1]);
|
||||
const result = try mod.addZIRBinOp(scope, src, inst_tag, dest_type, rhs);
|
||||
const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
|
||||
return rlWrap(mod, scope, rl, result);
|
||||
}
|
||||
|
||||
|
@ -682,7 +739,7 @@ fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError
|
|||
const operand = try expr(mod, scope, .none, call.params()[0]);
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[call.builtin_token].start;
|
||||
return mod.addZIRUnOp(scope, src, .ptrtoint, operand);
|
||||
return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
|
||||
}
|
||||
|
||||
fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
|
||||
|
@ -695,15 +752,19 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
|
|||
.none => return try expr(mod, scope, .{ .ty = dest_type }, params[1]),
|
||||
.discard => {
|
||||
const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
|
||||
_ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
|
||||
_ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
|
||||
return result;
|
||||
},
|
||||
.lvalue => {
|
||||
const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
|
||||
return addZIRUnOp(mod, scope, result.src, .ref, result);
|
||||
},
|
||||
.ty => |result_ty| {
|
||||
const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
|
||||
return mod.addZIRBinOp(scope, src, .as, result_ty, result);
|
||||
return addZIRBinOp(mod, scope, src, .as, result_ty, result);
|
||||
},
|
||||
.ptr => |result_ptr| {
|
||||
const casted_result_ptr = try mod.addZIRBinOp(scope, src, .coerce_result_ptr, dest_type, result_ptr);
|
||||
const casted_result_ptr = try addZIRBinOp(mod, scope, src, .coerce_result_ptr, dest_type, result_ptr);
|
||||
return expr(mod, scope, .{ .ptr = casted_result_ptr }, params[1]);
|
||||
},
|
||||
.bitcasted_ptr => |bitcasted_ptr| {
|
||||
|
@ -715,7 +776,7 @@ fn as(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) I
|
|||
return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
|
||||
},
|
||||
.block_ptr => |block_ptr| {
|
||||
const casted_block_ptr = try mod.addZIRInst(scope, src, zir.Inst.CoerceResultBlockPtr, .{
|
||||
const casted_block_ptr = try addZIRInst(mod, scope, src, zir.Inst.CoerceResultBlockPtr, .{
|
||||
.dest_type = dest_type,
|
||||
.block = block_ptr,
|
||||
}, .{});
|
||||
|
@ -728,7 +789,7 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
|
|||
try ensureBuiltinParamCount(mod, scope, call, 2);
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[call.builtin_token].start;
|
||||
const type_type = try mod.addZIRInstConst(scope, src, .{
|
||||
const type_type = try addZIRInstConst(mod, scope, src, .{
|
||||
.ty = Type.initTag(.type),
|
||||
.val = Value.initTag(.type_type),
|
||||
});
|
||||
|
@ -737,21 +798,26 @@ fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCa
|
|||
switch (rl) {
|
||||
.none => {
|
||||
const operand = try expr(mod, scope, .none, params[1]);
|
||||
return mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);
|
||||
return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
|
||||
},
|
||||
.discard => {
|
||||
const operand = try expr(mod, scope, .none, params[1]);
|
||||
const result = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, operand);
|
||||
_ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
|
||||
const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
|
||||
_ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
|
||||
return result;
|
||||
},
|
||||
.lvalue => {
|
||||
const operand = try expr(mod, scope, .lvalue, params[1]);
|
||||
const result = try addZIRBinOp(mod, scope, src, .bitcast_lvalue, dest_type, operand);
|
||||
return result;
|
||||
},
|
||||
.ty => |result_ty| {
|
||||
const result = try expr(mod, scope, .none, params[1]);
|
||||
const bitcasted = try mod.addZIRBinOp(scope, src, .bitcast, dest_type, result);
|
||||
return mod.addZIRBinOp(scope, src, .as, result_ty, bitcasted);
|
||||
const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
|
||||
return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
|
||||
},
|
||||
.ptr => |result_ptr| {
|
||||
const casted_result_ptr = try mod.addZIRUnOp(scope, src, .bitcast_result_ptr, result_ptr);
|
||||
const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
|
||||
return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
|
||||
},
|
||||
.bitcasted_ptr => |bitcasted_ptr| {
|
||||
|
@ -799,7 +865,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
|
|||
const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
|
||||
for (param_nodes) |param_node, i| {
|
||||
const param_src = tree.token_locs[param_node.firstToken()].start;
|
||||
const param_type = try mod.addZIRInst(scope, param_src, zir.Inst.ParamType, .{
|
||||
const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
|
||||
.func = lhs,
|
||||
.arg_index = i,
|
||||
}, .{});
|
||||
|
@ -807,7 +873,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
|
|||
}
|
||||
|
||||
const src = tree.token_locs[node.lhs.firstToken()].start;
|
||||
const result = try mod.addZIRInst(scope, src, zir.Inst.Call, .{
|
||||
const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
|
||||
.func = lhs,
|
||||
.args = args,
|
||||
}, .{});
|
||||
|
@ -818,7 +884,7 @@ fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) In
|
|||
fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
|
||||
const tree = scope.tree();
|
||||
const src = tree.token_locs[unreach_node.token].start;
|
||||
return mod.addZIRNoOp(scope, src, .@"unreachable");
|
||||
return addZIRNoOp(mod, scope, src, .@"unreachable");
|
||||
}
|
||||
|
||||
fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
|
||||
|
@ -1000,19 +1066,20 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
|
|||
.none => return result,
|
||||
.discard => {
|
||||
// Emit a compile error for discarding error values.
|
||||
_ = try mod.addZIRUnOp(scope, result.src, .ensure_result_non_error, result);
|
||||
_ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
|
||||
return result;
|
||||
},
|
||||
.ty => |ty_inst| return mod.addZIRBinOp(scope, result.src, .as, ty_inst, result),
|
||||
.lvalue => {
|
||||
// We need a pointer but we have a value.
|
||||
return addZIRUnOp(mod, scope, result.src, .ref, result);
|
||||
},
|
||||
.ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
|
||||
.ptr => |ptr_inst| {
|
||||
const casted_result = try mod.addZIRInst(scope, result.src, zir.Inst.CoerceToPtrElem, .{
|
||||
const casted_result = try addZIRInst(mod, scope, result.src, zir.Inst.CoerceToPtrElem, .{
|
||||
.ptr = ptr_inst,
|
||||
.value = result,
|
||||
}, .{});
|
||||
_ = try mod.addZIRInst(scope, result.src, zir.Inst.Store, .{
|
||||
.ptr = ptr_inst,
|
||||
.value = casted_result,
|
||||
}, .{});
|
||||
_ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, casted_result);
|
||||
return casted_result;
|
||||
},
|
||||
.bitcasted_ptr => |bitcasted_ptr| {
|
||||
|
@ -1026,3 +1093,121 @@ fn rlWrap(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerEr
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn addZIRInstSpecial(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
src: usize,
|
||||
comptime T: type,
|
||||
positionals: std.meta.fieldInfo(T, "positionals").field_type,
|
||||
kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
|
||||
) !*T {
|
||||
const gen_zir = scope.getGenZIR();
|
||||
try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
|
||||
const inst = try gen_zir.arena.create(T);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.tag = T.base_tag,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = positionals,
|
||||
.kw_args = kw_args,
|
||||
};
|
||||
gen_zir.instructions.appendAssumeCapacity(&inst.base);
|
||||
return inst;
|
||||
}
|
||||
|
||||
pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
|
||||
const gen_zir = scope.getGenZIR();
|
||||
try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
|
||||
const inst = try gen_zir.arena.create(zir.Inst.NoOp);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.tag = tag,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{},
|
||||
.kw_args = .{},
|
||||
};
|
||||
gen_zir.instructions.appendAssumeCapacity(&inst.base);
|
||||
return inst;
|
||||
}
|
||||
|
||||
pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
|
||||
const inst = try addZIRNoOpT(mod, scope, src, tag);
|
||||
return &inst.base;
|
||||
}
|
||||
|
||||
pub fn addZIRUnOp(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
src: usize,
|
||||
tag: zir.Inst.Tag,
|
||||
operand: *zir.Inst,
|
||||
) !*zir.Inst {
|
||||
const gen_zir = scope.getGenZIR();
|
||||
try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
|
||||
const inst = try gen_zir.arena.create(zir.Inst.UnOp);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.tag = tag,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{
|
||||
.operand = operand,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
gen_zir.instructions.appendAssumeCapacity(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
|
||||
pub fn addZIRBinOp(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
src: usize,
|
||||
tag: zir.Inst.Tag,
|
||||
lhs: *zir.Inst,
|
||||
rhs: *zir.Inst,
|
||||
) !*zir.Inst {
|
||||
const gen_zir = scope.getGenZIR();
|
||||
try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
|
||||
const inst = try gen_zir.arena.create(zir.Inst.BinOp);
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.tag = tag,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{
|
||||
.lhs = lhs,
|
||||
.rhs = rhs,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
gen_zir.instructions.appendAssumeCapacity(&inst.base);
|
||||
return &inst.base;
|
||||
}
|
||||
|
||||
pub fn addZIRInst(
|
||||
mod: *Module,
|
||||
scope: *Scope,
|
||||
src: usize,
|
||||
comptime T: type,
|
||||
positionals: std.meta.fieldInfo(T, "positionals").field_type,
|
||||
kw_args: std.meta.fieldInfo(T, "kw_args").field_type,
|
||||
) !*zir.Inst {
|
||||
const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
|
||||
return &inst_special.base;
|
||||
}
|
||||
|
||||
/// TODO The existence of this function is a workaround for a bug in stage1.
|
||||
pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
|
||||
const P = std.meta.fieldInfo(zir.Inst.Const, "positionals").field_type;
|
||||
return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
|
||||
}
|
||||
|
||||
/// TODO The existence of this function is a workaround for a bug in stage1.
|
||||
pub fn addZIRInstBlock(mod: *Module, scope: *Scope, src: usize, body: zir.Module.Body) !*zir.Inst.Block {
|
||||
const P = std.meta.fieldInfo(zir.Inst.Block, "positionals").field_type;
|
||||
return addZIRInstSpecial(mod, scope, src, zir.Inst.Block, P{ .body = body }, .{});
|
||||
}
|
||||
|
|
|
@ -828,6 +828,14 @@ pub const ZigClangExpr_ConstExprUsage = extern enum {
|
|||
EvaluateForMangling,
|
||||
};
|
||||
|
||||
pub const ZigClangUnaryExprOrTypeTrait_Kind = extern enum {
|
||||
SizeOf,
|
||||
AlignOf,
|
||||
VecStep,
|
||||
OpenMPRequiredSimdAlign,
|
||||
PreferredAlignOf,
|
||||
};
|
||||
|
||||
pub extern fn ZigClangSourceManager_getSpellingLoc(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) struct_ZigClangSourceLocation;
|
||||
pub extern fn ZigClangSourceManager_getFilename(self: *const struct_ZigClangSourceManager, SpellingLoc: struct_ZigClangSourceLocation) ?[*:0]const u8;
|
||||
pub extern fn ZigClangSourceManager_getSpellingLineNumber(self: ?*const struct_ZigClangSourceManager, Loc: struct_ZigClangSourceLocation) c_uint;
|
||||
|
@ -1225,6 +1233,7 @@ pub extern fn ZigClangCallExpr_getArgs(*const ZigClangCallExpr) [*]const *const
|
|||
|
||||
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangQualType;
|
||||
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangSourceLocation;
|
||||
pub extern fn ZigClangUnaryExprOrTypeTraitExpr_getKind(*const ZigClangUnaryExprOrTypeTraitExpr) ZigClangUnaryExprOrTypeTrait_Kind;
|
||||
|
||||
pub extern fn ZigClangUnaryOperator_getOpcode(*const ZigClangUnaryOperator) ZigClangUO;
|
||||
pub extern fn ZigClangUnaryOperator_getType(*const ZigClangUnaryOperator) ZigClangQualType;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -89,17 +89,17 @@ fn genFn(file: *C, decl: *Decl) !void {
|
|||
const func: *Module.Fn = tv.val.cast(Value.Payload.Function).?.func;
|
||||
const instructions = func.analysis.success.instructions;
|
||||
if (instructions.len > 0) {
|
||||
try writer.writeAll("\n");
|
||||
for (instructions) |inst| {
|
||||
try writer.writeAll("\n ");
|
||||
switch (inst.tag) {
|
||||
.assembly => try genAsm(file, inst.castTag(.assembly).?, decl),
|
||||
.call => try genCall(file, inst.castTag(.call).?, decl),
|
||||
.ret => try genRet(file, inst.castTag(.ret).?, decl, tv.ty.fnReturnType()),
|
||||
.retvoid => try file.main.writer().print("return;", .{}),
|
||||
.retvoid => try file.main.writer().print(" return;\n", .{}),
|
||||
.dbg_stmt => try genDbgStmt(file, inst.castTag(.dbg_stmt).?, decl),
|
||||
else => |e| return file.fail(decl.src(), "TODO implement C codegen for {}", .{e}),
|
||||
}
|
||||
}
|
||||
try writer.writeAll("\n");
|
||||
}
|
||||
|
||||
try writer.writeAll("}\n\n");
|
||||
|
@ -112,6 +112,7 @@ fn genRet(file: *C, inst: *Inst.UnOp, decl: *Decl, expected_return_type: Type) !
|
|||
fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
|
||||
const writer = file.main.writer();
|
||||
const header = file.header.writer();
|
||||
try writer.writeAll(" ");
|
||||
if (inst.func.castTag(.constant)) |func_inst| {
|
||||
if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
|
||||
const target = func_val.func.owner_decl;
|
||||
|
@ -126,7 +127,7 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
|
|||
try renderFunctionSignature(file, header, target);
|
||||
try header.writeAll(";\n");
|
||||
}
|
||||
try writer.print("{}();", .{tname});
|
||||
try writer.print("{}();\n", .{tname});
|
||||
} else {
|
||||
return file.fail(decl.src(), "TODO non-function call target?", .{});
|
||||
}
|
||||
|
@ -138,8 +139,13 @@ fn genCall(file: *C, inst: *Inst.Call, decl: *Decl) !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn genDbgStmt(file: *C, inst: *Inst.NoOp, decl: *Decl) !void {
|
||||
// TODO emit #line directive here with line number and filename
|
||||
}
|
||||
|
||||
fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
|
||||
const writer = file.main.writer();
|
||||
try writer.writeAll(" ");
|
||||
for (as.inputs) |i, index| {
|
||||
if (i[0] == '{' and i[i.len - 1] == '}') {
|
||||
const reg = i[1 .. i.len - 1];
|
||||
|
@ -187,5 +193,5 @@ fn genAsm(file: *C, as: *Inst.Assembly, decl: *Decl) !void {
|
|||
}
|
||||
}
|
||||
}
|
||||
try writer.writeAll(");");
|
||||
try writer.writeAll(");\n");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
const std = @import("std");
|
||||
|
||||
pub const instructions = struct {
|
||||
pub const CallBreak = packed struct {
|
||||
pub const Mode = packed enum(u12) { ecall, ebreak };
|
||||
opcode: u7 = 0b1110011,
|
||||
unused1: u5 = 0,
|
||||
unused2: u3 = 0,
|
||||
unused3: u5 = 0,
|
||||
mode: u12, //: Mode
|
||||
};
|
||||
// I-type
|
||||
pub const Addi = packed struct {
|
||||
pub const Mode = packed enum(u3) { addi = 0b000, slti = 0b010, sltiu = 0b011, xori = 0b100, ori = 0b110, andi = 0b111 };
|
||||
opcode: u7 = 0b0010011,
|
||||
rd: u5,
|
||||
mode: u3, //: Mode
|
||||
rs1: u5,
|
||||
imm: i12,
|
||||
};
|
||||
pub const Lui = packed struct {
|
||||
opcode: u7 = 0b0110111,
|
||||
rd: u5,
|
||||
imm: i20,
|
||||
};
|
||||
// I_type
|
||||
pub const Load = packed struct {
|
||||
pub const Mode = packed enum(u3) { ld = 0b011, lwu = 0b110 };
|
||||
opcode: u7 = 0b0000011,
|
||||
rd: u5,
|
||||
mode: u3, //: Mode
|
||||
rs1: u5,
|
||||
offset: i12,
|
||||
};
|
||||
// I-type
|
||||
pub const Jalr = packed struct {
|
||||
opcode: u7 = 0b1100111,
|
||||
rd: u5,
|
||||
mode: u3 = 0,
|
||||
rs1: u5,
|
||||
offset: i12,
|
||||
};
|
||||
};
|
||||
|
||||
// zig fmt: off
|
||||
pub const RawRegister = enum(u8) {
|
||||
x0, x1, x2, x3, x4, x5, x6, x7,
|
||||
x8, x9, x10, x11, x12, x13, x14, x15,
|
||||
x16, x17, x18, x19, x20, x21, x22, x23,
|
||||
x24, x25, x26, x27, x28, x29, x30, x31,
|
||||
};
|
||||
|
||||
pub const Register = enum(u8) {
|
||||
// 64 bit registers
|
||||
zero, // zero
|
||||
ra, // return address. caller saved
|
||||
sp, // stack pointer. callee saved.
|
||||
gp, // global pointer
|
||||
tp, // thread pointer
|
||||
t0, t1, t2, // temporaries. caller saved.
|
||||
s0, // s0/fp, callee saved.
|
||||
s1, // callee saved.
|
||||
a0, a1, // fn args/return values. caller saved.
|
||||
a2, a3, a4, a5, a6, a7, // fn args. caller saved.
|
||||
s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, // saved registers. callee saved.
|
||||
t3, t4, t5, t6, // caller saved
|
||||
|
||||
pub fn parseRegName(name: []const u8) ?Register {
|
||||
if(std.meta.stringToEnum(Register, name)) |reg| return reg;
|
||||
if(std.meta.stringToEnum(RawRegister, name)) |rawreg| return @intToEnum(Register, @enumToInt(rawreg));
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Returns the register's id.
|
||||
pub fn id(self: @This()) u5 {
|
||||
return @truncate(u5, @enumToInt(self));
|
||||
}
|
||||
|
||||
/// Returns the index into `callee_preserved_regs`.
|
||||
pub fn allocIndex(self: Register) ?u4 {
|
||||
inline for(callee_preserved_regs) |cpreg, i| {
|
||||
if(self == cpreg) return i;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// zig fmt: on
|
||||
|
||||
pub const callee_preserved_regs = [_]Register{
|
||||
.s0, .s1, .s2, .s3, .s4, .s5, .s6, .s7, .s8, .s9, .s10, .s11,
|
||||
};
|
|
@ -81,6 +81,26 @@ pub const Register = enum(u8) {
|
|||
else => null,
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert from any register to its 64 bit alias.
|
||||
pub fn to64(self: Register) Register {
|
||||
return @intToEnum(Register, self.id());
|
||||
}
|
||||
|
||||
/// Convert from any register to its 32 bit alias.
|
||||
pub fn to32(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.id()) + 16);
|
||||
}
|
||||
|
||||
/// Convert from any register to its 16 bit alias.
|
||||
pub fn to16(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.id()) + 32);
|
||||
}
|
||||
|
||||
/// Convert from any register to its 8 bit alias.
|
||||
pub fn to8(self: Register) Register {
|
||||
return @intToEnum(Register, @as(u8, self.id()) + 48);
|
||||
}
|
||||
};
|
||||
|
||||
// zig fmt: on
|
||||
|
@ -88,3 +108,4 @@ pub const Register = enum(u8) {
|
|||
/// These registers belong to the called function.
|
||||
pub const callee_preserved_regs = [_]Register{ .rax, .rcx, .rdx, .rsi, .rdi, .r8, .r9, .r10, .r11 };
|
||||
pub const c_abi_int_param_regs = [_]Register{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
|
||||
pub const c_abi_int_return_regs = [_]Register{ .rax, .rdx };
|
||||
|
|
|
@ -4,6 +4,7 @@ const Type = @import("type.zig").Type;
|
|||
const Module = @import("Module.zig");
|
||||
const assert = std.debug.assert;
|
||||
const codegen = @import("codegen.zig");
|
||||
const ast = std.zig.ast;
|
||||
|
||||
/// These are in-memory, analyzed instructions. See `zir.Inst` for the representation
|
||||
/// of instructions that correspond to the ZIR text format.
|
||||
|
@ -47,6 +48,7 @@ pub const Inst = struct {
|
|||
|
||||
pub const Tag = enum {
|
||||
add,
|
||||
alloc,
|
||||
arg,
|
||||
assembly,
|
||||
bitcast,
|
||||
|
@ -63,28 +65,34 @@ pub const Inst = struct {
|
|||
cmp_neq,
|
||||
condbr,
|
||||
constant,
|
||||
dbg_stmt,
|
||||
isnonnull,
|
||||
isnull,
|
||||
/// Read a value from a pointer.
|
||||
load,
|
||||
ptrtoint,
|
||||
ref,
|
||||
ret,
|
||||
retvoid,
|
||||
/// Write a value to a pointer. LHS is pointer, RHS is value.
|
||||
store,
|
||||
sub,
|
||||
unreach,
|
||||
not,
|
||||
floatcast,
|
||||
intcast,
|
||||
|
||||
/// There is one-to-one correspondence between tag and type for now,
|
||||
/// but this will not always be the case. For example, binary operations
|
||||
/// such as + and - will have different tags but the same type.
|
||||
pub fn Type(tag: Tag) type {
|
||||
return switch (tag) {
|
||||
.alloc,
|
||||
.retvoid,
|
||||
.unreach,
|
||||
.arg,
|
||||
.breakpoint,
|
||||
.dbg_stmt,
|
||||
=> NoOp,
|
||||
|
||||
.ref,
|
||||
.ret,
|
||||
.bitcast,
|
||||
.not,
|
||||
|
@ -93,6 +101,7 @@ pub const Inst = struct {
|
|||
.ptrtoint,
|
||||
.floatcast,
|
||||
.intcast,
|
||||
.load,
|
||||
=> UnOp,
|
||||
|
||||
.add,
|
||||
|
@ -103,6 +112,7 @@ pub const Inst = struct {
|
|||
.cmp_gte,
|
||||
.cmp_gt,
|
||||
.cmp_neq,
|
||||
.store,
|
||||
=> BinOp,
|
||||
|
||||
.assembly => Assembly,
|
||||
|
@ -157,8 +167,7 @@ pub const Inst = struct {
|
|||
|
||||
/// Returns `null` if runtime-known.
|
||||
pub fn value(base: *Inst) ?Value {
|
||||
if (base.ty.onePossibleValue())
|
||||
return Value.initTag(.the_one_possible_value);
|
||||
if (base.ty.onePossibleValue()) |opv| return opv;
|
||||
|
||||
const inst = base.cast(Constant) orelse return null;
|
||||
return inst.val;
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -10,9 +10,7 @@ const Module = @import("Module.zig");
|
|||
const link = @import("link.zig");
|
||||
const Package = @import("Package.zig");
|
||||
const zir = @import("zir.zig");
|
||||
|
||||
// TODO Improve async I/O enough that we feel comfortable doing this.
|
||||
//pub const io_mode = .evented;
|
||||
const build_options = @import("build_options");
|
||||
|
||||
pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
|
||||
|
||||
|
@ -47,18 +45,16 @@ pub fn log(
|
|||
if (@enumToInt(level) > @enumToInt(std.log.level))
|
||||
return;
|
||||
|
||||
const scope_prefix = "(" ++ switch (scope) {
|
||||
// Uncomment to hide logs
|
||||
//.compiler,
|
||||
.module,
|
||||
.liveness,
|
||||
.link,
|
||||
=> return,
|
||||
const scope_name = @tagName(scope);
|
||||
const ok = comptime for (build_options.log_scopes) |log_scope| {
|
||||
if (mem.eql(u8, log_scope, scope_name))
|
||||
break true;
|
||||
} else false;
|
||||
|
||||
else => @tagName(scope),
|
||||
} ++ "): ";
|
||||
if (!ok)
|
||||
return;
|
||||
|
||||
const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
|
||||
const prefix = "[" ++ @tagName(level) ++ "] " ++ "(" ++ @tagName(scope) ++ "): ";
|
||||
|
||||
// Print the message to stderr, silently ignoring any errors
|
||||
std.debug.print(prefix ++ format, args);
|
||||
|
@ -94,6 +90,8 @@ pub fn main() !void {
|
|||
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
|
||||
} else if (mem.eql(u8, cmd, "version")) {
|
||||
// Need to set up the build script to give the version as a comptime value.
|
||||
// TODO when you solve this, also take a look at link.zig, there is a placeholder
|
||||
// that says "TODO version here".
|
||||
std.debug.print("TODO version command not implemented yet\n", .{});
|
||||
return error.Unimplemented;
|
||||
} else if (mem.eql(u8, cmd, "zen")) {
|
||||
|
@ -492,6 +490,7 @@ fn buildOutputType(
|
|||
defer root_pkg.destroy();
|
||||
|
||||
var module = try Module.init(gpa, .{
|
||||
.root_name = root_name,
|
||||
.target = target_info.target,
|
||||
.output_mode = output_mode,
|
||||
.root_pkg = root_pkg,
|
||||
|
|
|
@ -4,6 +4,11 @@ const Module = @import("Module.zig");
|
|||
const Allocator = std.mem.Allocator;
|
||||
const zir = @import("zir.zig");
|
||||
const Package = @import("Package.zig");
|
||||
const build_options = @import("build_options");
|
||||
const enable_qemu: bool = build_options.enable_qemu;
|
||||
const enable_wine: bool = build_options.enable_wine;
|
||||
const enable_wasmtime: bool = build_options.enable_wasmtime;
|
||||
const glibc_multi_install_dir: ?[]const u8 = build_options.glibc_multi_install_dir;
|
||||
|
||||
const cheader = @embedFile("cbe.h");
|
||||
|
||||
|
@ -401,8 +406,6 @@ pub const TestContext = struct {
|
|||
const root_node = try progress.start("tests", self.cases.items.len);
|
||||
defer root_node.end();
|
||||
|
||||
const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{});
|
||||
|
||||
for (self.cases.items) |case| {
|
||||
std.testing.base_allocator_instance.reset();
|
||||
|
||||
|
@ -415,13 +418,19 @@ pub const TestContext = struct {
|
|||
progress.initial_delay_ns = 0;
|
||||
progress.refresh_rate_ns = 0;
|
||||
|
||||
const info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
|
||||
try self.runOneCase(std.testing.allocator, &prg_node, case, info.target);
|
||||
try self.runOneCase(std.testing.allocator, &prg_node, case);
|
||||
try std.testing.allocator_instance.validate();
|
||||
}
|
||||
}
|
||||
|
||||
fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case, target: std.Target) !void {
|
||||
fn runOneCase(self: *TestContext, allocator: *Allocator, root_node: *std.Progress.Node, case: Case) !void {
|
||||
const target_info = try std.zig.system.NativeTargetInfo.detect(std.testing.allocator, case.target);
|
||||
const target = target_info.target;
|
||||
|
||||
var arena_allocator = std.heap.ArenaAllocator.init(allocator);
|
||||
defer arena_allocator.deinit();
|
||||
const arena = &arena_allocator.allocator;
|
||||
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
|
@ -429,10 +438,10 @@ pub const TestContext = struct {
|
|||
const root_pkg = try Package.create(allocator, tmp.dir, ".", tmp_src_path);
|
||||
defer root_pkg.destroy();
|
||||
|
||||
const bin_name = try std.zig.binNameAlloc(allocator, "test_case", target, case.output_mode, null);
|
||||
defer allocator.free(bin_name);
|
||||
const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null);
|
||||
|
||||
var module = try Module.init(allocator, .{
|
||||
.root_name = "test_case",
|
||||
.target = target,
|
||||
// TODO: support tests for object file building, and library builds
|
||||
// and linking. This will require a rework to support multi-file
|
||||
|
@ -484,8 +493,7 @@ pub const TestContext = struct {
|
|||
// incremental updates
|
||||
var file = try tmp.dir.openFile(bin_name, .{ .read = true });
|
||||
defer file.close();
|
||||
var out = file.reader().readAllAlloc(allocator, 1024 * 1024) catch @panic("Unable to read C output!");
|
||||
defer allocator.free(out);
|
||||
var out = file.reader().readAllAlloc(arena, 1024 * 1024) catch @panic("Unable to read C output!");
|
||||
|
||||
if (expected_output.len != out.len) {
|
||||
std.debug.warn("\nTransformed C length differs:\n================\nExpected:\n================\n{}\n================\nFound:\n================\n{}\n================\nTest failed.\n", .{ expected_output, out });
|
||||
|
@ -532,8 +540,7 @@ pub const TestContext = struct {
|
|||
var test_node = update_node.start("assert", null);
|
||||
test_node.activate();
|
||||
defer test_node.end();
|
||||
var handled_errors = try allocator.alloc(bool, e.len);
|
||||
defer allocator.free(handled_errors);
|
||||
var handled_errors = try arena.alloc(bool, e.len);
|
||||
for (handled_errors) |*h| {
|
||||
h.* = false;
|
||||
}
|
||||
|
@ -568,14 +575,59 @@ pub const TestContext = struct {
|
|||
exec_node.activate();
|
||||
defer exec_node.end();
|
||||
|
||||
try module.makeBinFileExecutable();
|
||||
var argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
const exe_path = try std.fmt.allocPrint(allocator, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
|
||||
defer allocator.free(exe_path);
|
||||
const exe_path = try std.fmt.allocPrint(arena, "." ++ std.fs.path.sep_str ++ "{}", .{bin_name});
|
||||
|
||||
switch (case.target.getExternalExecutor()) {
|
||||
.native => try argv.append(exe_path),
|
||||
.unavailable => return, // No executor available; pass test.
|
||||
|
||||
.qemu => |qemu_bin_name| if (enable_qemu) {
|
||||
// TODO Ability for test cases to specify whether to link libc.
|
||||
const need_cross_glibc = false; // target.isGnuLibC() and self.is_linking_libc;
|
||||
const glibc_dir_arg = if (need_cross_glibc)
|
||||
glibc_multi_install_dir orelse return // glibc dir not available; pass test
|
||||
else
|
||||
null;
|
||||
try argv.append(qemu_bin_name);
|
||||
if (glibc_dir_arg) |dir| {
|
||||
const linux_triple = try target.linuxTriple(arena);
|
||||
const full_dir = try std.fs.path.join(arena, &[_][]const u8{
|
||||
dir,
|
||||
linux_triple,
|
||||
});
|
||||
|
||||
try argv.append("-L");
|
||||
try argv.append(full_dir);
|
||||
}
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // QEMU not available; pass test.
|
||||
},
|
||||
|
||||
.wine => |wine_bin_name| if (enable_wine) {
|
||||
try argv.append(wine_bin_name);
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // Wine not available; pass test.
|
||||
},
|
||||
|
||||
.wasmtime => |wasmtime_bin_name| if (enable_wasmtime) {
|
||||
try argv.append(wasmtime_bin_name);
|
||||
try argv.append("--dir=.");
|
||||
try argv.append(exe_path);
|
||||
} else {
|
||||
return; // wasmtime not available; pass test.
|
||||
},
|
||||
}
|
||||
|
||||
try module.makeBinFileExecutable();
|
||||
|
||||
break :x try std.ChildProcess.exec(.{
|
||||
.allocator = allocator,
|
||||
.argv = &[_][]const u8{exe_path},
|
||||
.argv = argv.items,
|
||||
.cwd_dir = tmp.dir,
|
||||
});
|
||||
};
|
||||
|
|
|
@ -8,7 +8,6 @@ const Token = std.zig.Token;
|
|||
usingnamespace @import("clang.zig");
|
||||
const ctok = std.c.tokenizer;
|
||||
const CToken = std.c.Token;
|
||||
const CTokenList = std.c.tokenizer.Source.TokenList;
|
||||
const mem = std.mem;
|
||||
const math = std.math;
|
||||
|
||||
|
@ -2864,7 +2863,6 @@ fn transCharLiteral(
|
|||
"TODO: support character literal kind {}",
|
||||
.{kind},
|
||||
),
|
||||
else => unreachable,
|
||||
};
|
||||
if (suppress_as == .no_as) {
|
||||
return maybeSuppressResult(rp, scope, result_used, int_lit_node);
|
||||
|
@ -3070,13 +3068,30 @@ fn transUnaryExprOrTypeTraitExpr(
|
|||
stmt: *const ZigClangUnaryExprOrTypeTraitExpr,
|
||||
result_used: ResultUsed,
|
||||
) TransError!*ast.Node {
|
||||
const loc = ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt);
|
||||
const type_node = try transQualType(
|
||||
rp,
|
||||
ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(stmt),
|
||||
ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(stmt),
|
||||
loc,
|
||||
);
|
||||
|
||||
const builtin_node = try rp.c.createBuiltinCall("@sizeOf", 1);
|
||||
const kind = ZigClangUnaryExprOrTypeTraitExpr_getKind(stmt);
|
||||
const kind_str = switch (kind) {
|
||||
.SizeOf => "@sizeOf",
|
||||
.AlignOf => "@alignOf",
|
||||
.PreferredAlignOf,
|
||||
.VecStep,
|
||||
.OpenMPRequiredSimdAlign,
|
||||
=> return revertAndWarn(
|
||||
rp,
|
||||
error.UnsupportedTranslation,
|
||||
loc,
|
||||
"Unsupported type trait kind {}",
|
||||
.{kind},
|
||||
),
|
||||
};
|
||||
|
||||
const builtin_node = try rp.c.createBuiltinCall(kind_str, 1);
|
||||
builtin_node.params()[0] = type_node;
|
||||
builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
|
||||
return maybeSuppressResult(rp, scope, result_used, &builtin_node.base);
|
||||
|
@ -4515,7 +4530,7 @@ const CtrlFlow = struct {
|
|||
const ltoken = try appendToken(c, kw, kw_text);
|
||||
const label_token = if (label) |l| blk: {
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
break :blk try appendToken(c, .Identifier, l);
|
||||
break :blk try appendIdentifier(c, l);
|
||||
} else null;
|
||||
return CtrlFlow{
|
||||
.c = c,
|
||||
|
@ -5197,16 +5212,39 @@ pub fn freeErrors(errors: []ClangErrMsg) void {
|
|||
ZigClangErrorMsg_delete(errors.ptr, errors.len);
|
||||
}
|
||||
|
||||
const CTokIterator = struct {
|
||||
source: []const u8,
|
||||
list: []const CToken,
|
||||
i: usize = 0,
|
||||
|
||||
fn peek(self: *CTokIterator) ?CToken.Id {
|
||||
if (self.i >= self.list.len) return null;
|
||||
return self.list[self.i + 1].id;
|
||||
}
|
||||
|
||||
fn next(self: *CTokIterator) ?CToken.Id {
|
||||
if (self.i >= self.list.len) return null;
|
||||
self.i += 1;
|
||||
return self.list[self.i].id;
|
||||
}
|
||||
|
||||
fn slice(self: *CTokIterator, index: usize) []const u8 {
|
||||
const tok = self.list[index];
|
||||
return self.source[tok.start..tok.end];
|
||||
}
|
||||
};
|
||||
|
||||
fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
|
||||
// TODO if we see #undef, delete it from the table
|
||||
var it = ZigClangASTUnit_getLocalPreprocessingEntities_begin(unit);
|
||||
const it_end = ZigClangASTUnit_getLocalPreprocessingEntities_end(unit);
|
||||
var tok_list = CTokenList.init(c.arena);
|
||||
var tok_list = std.ArrayList(CToken).init(c.gpa);
|
||||
defer tok_list.deinit();
|
||||
const scope = c.global_scope;
|
||||
|
||||
while (it.I != it_end.I) : (it.I += 1) {
|
||||
const entity = ZigClangPreprocessingRecord_iterator_deref(it);
|
||||
tok_list.shrink(0);
|
||||
tok_list.items.len = 0;
|
||||
switch (ZigClangPreprocessedEntity_getKind(entity)) {
|
||||
.MacroDefinitionKind => {
|
||||
const macro = @ptrCast(*ZigClangMacroDefinitionRecord, entity);
|
||||
|
@ -5224,38 +5262,34 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
|
|||
const begin_c = ZigClangSourceManager_getCharacterData(c.source_manager, begin_loc);
|
||||
const slice = begin_c[0..mem.len(begin_c)];
|
||||
|
||||
tok_list.shrink(0);
|
||||
var tokenizer = std.c.Tokenizer{
|
||||
.source = &std.c.tokenizer.Source{
|
||||
.buffer = slice,
|
||||
.file_name = undefined,
|
||||
.tokens = undefined,
|
||||
},
|
||||
.buffer = slice,
|
||||
};
|
||||
while (true) {
|
||||
const tok = tokenizer.next();
|
||||
switch (tok.id) {
|
||||
.Nl, .Eof => {
|
||||
try tok_list.push(tok);
|
||||
try tok_list.append(tok);
|
||||
break;
|
||||
},
|
||||
.LineComment, .MultiLineComment => continue,
|
||||
else => {},
|
||||
}
|
||||
try tok_list.push(tok);
|
||||
try tok_list.append(tok);
|
||||
}
|
||||
|
||||
var tok_it = tok_list.iterator(0);
|
||||
const first_tok = tok_it.next().?;
|
||||
assert(mem.eql(u8, slice[first_tok.start..first_tok.end], name));
|
||||
var tok_it = CTokIterator{
|
||||
.source = slice,
|
||||
.list = tok_list.items,
|
||||
};
|
||||
assert(mem.eql(u8, tok_it.slice(0), name));
|
||||
|
||||
var macro_fn = false;
|
||||
const next = tok_it.peek().?;
|
||||
switch (next.id) {
|
||||
switch (tok_it.peek().?) {
|
||||
.Identifier => {
|
||||
// if it equals itself, ignore. for example, from stdio.h:
|
||||
// #define stdin stdin
|
||||
if (mem.eql(u8, name, slice[next.start..next.end])) {
|
||||
if (mem.eql(u8, name, tok_it.slice(1))) {
|
||||
continue;
|
||||
}
|
||||
},
|
||||
|
@ -5266,15 +5300,15 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
|
|||
},
|
||||
.LParen => {
|
||||
// if the name is immediately followed by a '(' then it is a function
|
||||
macro_fn = first_tok.end == next.start;
|
||||
macro_fn = tok_it.list[0].end == tok_it.list[1].start;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
(if (macro_fn)
|
||||
transMacroFnDefine(c, &tok_it, slice, mangled_name, begin_loc)
|
||||
transMacroFnDefine(c, &tok_it, mangled_name, begin_loc)
|
||||
else
|
||||
transMacroDefine(c, &tok_it, slice, mangled_name, begin_loc)) catch |err| switch (err) {
|
||||
transMacroDefine(c, &tok_it, mangled_name, begin_loc)) catch |err| switch (err) {
|
||||
error.ParseError => continue,
|
||||
error.OutOfMemory => |e| return e,
|
||||
};
|
||||
|
@ -5284,7 +5318,7 @@ fn transPreprocessorEntities(c: *Context, unit: *ZigClangASTUnit) Error!void {
|
|||
}
|
||||
}
|
||||
|
||||
fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
|
||||
fn transMacroDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
|
||||
const scope = &c.global_scope.base;
|
||||
|
||||
const visib_tok = try appendToken(c, .Keyword_pub, "pub");
|
||||
|
@ -5292,15 +5326,15 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
|
|||
const name_tok = try appendIdentifier(c, name);
|
||||
const eq_token = try appendToken(c, .Equal, "=");
|
||||
|
||||
const init_node = try parseCExpr(c, it, source, source_loc, scope);
|
||||
const init_node = try parseCExpr(c, it, source_loc, scope);
|
||||
const last = it.next().?;
|
||||
if (last.id != .Eof and last.id != .Nl)
|
||||
if (last != .Eof and last != .Nl)
|
||||
return failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
name,
|
||||
"unable to translate C expr: unexpected token .{}",
|
||||
.{@tagName(last.id)},
|
||||
.{@tagName(last)},
|
||||
);
|
||||
|
||||
const semicolon_token = try appendToken(c, .Semicolon, ";");
|
||||
|
@ -5316,7 +5350,7 @@ fn transMacroDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, n
|
|||
_ = try c.global_scope.macro_table.put(name, &node.base);
|
||||
}
|
||||
|
||||
fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
|
||||
fn transMacroFnDefine(c: *Context, it: *CTokIterator, name: []const u8, source_loc: ZigClangSourceLocation) ParseError!void {
|
||||
var block_scope = try Scope.Block.init(c, &c.global_scope.base, null);
|
||||
defer block_scope.deinit();
|
||||
const scope = &block_scope.base;
|
||||
|
@ -5327,7 +5361,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
const name_tok = try appendIdentifier(c, name);
|
||||
_ = try appendToken(c, .LParen, "(");
|
||||
|
||||
if (it.next().?.id != .LParen) {
|
||||
if (it.next().? != .LParen) {
|
||||
return failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
|
@ -5341,8 +5375,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
defer fn_params.deinit();
|
||||
|
||||
while (true) {
|
||||
const param_tok = it.next().?;
|
||||
if (param_tok.id != .Identifier) {
|
||||
if (it.next().? != .Identifier) {
|
||||
return failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
|
@ -5352,7 +5385,7 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
);
|
||||
}
|
||||
|
||||
const mangled_name = try block_scope.makeMangledName(c, source[param_tok.start..param_tok.end]);
|
||||
const mangled_name = try block_scope.makeMangledName(c, it.slice(it.i));
|
||||
const param_name_tok = try appendIdentifier(c, mangled_name);
|
||||
_ = try appendToken(c, .Colon, ":");
|
||||
|
||||
|
@ -5370,13 +5403,13 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
.param_type = .{ .any_type = &any_type.base },
|
||||
};
|
||||
|
||||
if (it.peek().?.id != .Comma)
|
||||
if (it.peek().? != .Comma)
|
||||
break;
|
||||
_ = it.next();
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
}
|
||||
|
||||
if (it.next().?.id != .RParen) {
|
||||
if (it.next().? != .RParen) {
|
||||
return failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
|
@ -5391,15 +5424,15 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
const type_of = try c.createBuiltinCall("@TypeOf", 1);
|
||||
|
||||
const return_kw = try appendToken(c, .Keyword_return, "return");
|
||||
const expr = try parseCExpr(c, it, source, source_loc, scope);
|
||||
const expr = try parseCExpr(c, it, source_loc, scope);
|
||||
const last = it.next().?;
|
||||
if (last.id != .Eof and last.id != .Nl)
|
||||
if (last != .Eof and last != .Nl)
|
||||
return failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
name,
|
||||
"unable to translate C expr: unexpected token .{}",
|
||||
.{@tagName(last.id)},
|
||||
.{@tagName(last)},
|
||||
);
|
||||
_ = try appendToken(c, .Semicolon, ";");
|
||||
const type_of_arg = if (expr.tag != .Block) expr else blk: {
|
||||
|
@ -5436,28 +5469,27 @@ fn transMacroFnDefine(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
|
||||
const ParseError = Error || error{ParseError};
|
||||
|
||||
fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
const node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
switch (it.next().?.id) {
|
||||
fn parseCExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
switch (it.next().?) {
|
||||
.QuestionMark => {
|
||||
// must come immediately after expr
|
||||
_ = try appendToken(c, .RParen, ")");
|
||||
const if_node = try transCreateNodeIf(c);
|
||||
if_node.condition = node;
|
||||
if_node.body = try parseCPrimaryExpr(c, it, source, source_loc, scope);
|
||||
if (it.next().?.id != .Colon) {
|
||||
const first_tok = it.list.at(0);
|
||||
if_node.body = try parseCPrimaryExpr(c, it, source_loc, scope);
|
||||
if (it.next().? != .Colon) {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected ':'",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
}
|
||||
if_node.@"else" = try transCreateNodeElse(c);
|
||||
if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source, source_loc, scope);
|
||||
if_node.@"else".?.body = try parseCPrimaryExpr(c, it, source_loc, scope);
|
||||
return &if_node.base;
|
||||
},
|
||||
.Comma => {
|
||||
|
@ -5480,10 +5512,10 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
|
|||
};
|
||||
try block_scope.statements.append(&op_node.base);
|
||||
|
||||
last = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
last = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
_ = try appendToken(c, .Semicolon, ";");
|
||||
if (it.next().?.id != .Comma) {
|
||||
_ = it.prev();
|
||||
if (it.next().? != .Comma) {
|
||||
it.i -= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -5494,70 +5526,74 @@ fn parseCExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_
|
|||
return &block_node.base;
|
||||
},
|
||||
else => {
|
||||
_ = it.prev();
|
||||
it.i -= 1;
|
||||
return node;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn parseCNumLit(c: *Context, tok: *CToken, source: []const u8, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
|
||||
var lit_bytes = source[tok.start..tok.end];
|
||||
fn parseCNumLit(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation) ParseError!*ast.Node {
|
||||
var lit_bytes = it.slice(it.i);
|
||||
|
||||
if (tok.id == .IntegerLiteral) {
|
||||
if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
|
||||
switch (lit_bytes[1]) {
|
||||
'0'...'7' => {
|
||||
// Octal
|
||||
lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
|
||||
},
|
||||
'X' => {
|
||||
// Hexadecimal with capital X, valid in C but not in Zig
|
||||
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
|
||||
},
|
||||
else => {},
|
||||
switch (it.list[it.i].id) {
|
||||
.IntegerLiteral => |suffix| {
|
||||
if (lit_bytes.len > 2 and lit_bytes[0] == '0') {
|
||||
switch (lit_bytes[1]) {
|
||||
'0'...'7' => {
|
||||
// Octal
|
||||
lit_bytes = try std.fmt.allocPrint(c.arena, "0o{}", .{lit_bytes});
|
||||
},
|
||||
'X' => {
|
||||
// Hexadecimal with capital X, valid in C but not in Zig
|
||||
lit_bytes = try std.fmt.allocPrint(c.arena, "0x{}", .{lit_bytes[2..]});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tok.id.IntegerLiteral == .None) {
|
||||
return transCreateNodeInt(c, lit_bytes);
|
||||
}
|
||||
if (suffix == .none) {
|
||||
return transCreateNodeInt(c, lit_bytes);
|
||||
}
|
||||
|
||||
const cast_node = try c.createBuiltinCall("@as", 2);
|
||||
cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (tok.id.IntegerLiteral) {
|
||||
.U => "c_uint",
|
||||
.L => "c_long",
|
||||
.LU => "c_ulong",
|
||||
.LL => "c_longlong",
|
||||
.LLU => "c_ulonglong",
|
||||
else => unreachable,
|
||||
});
|
||||
lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (tok.id.IntegerLiteral) {
|
||||
.U, .L => @as(u8, 1),
|
||||
.LU, .LL => 2,
|
||||
.LLU => 3,
|
||||
else => unreachable,
|
||||
}];
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
|
||||
cast_node.rparen_token = try appendToken(c, .RParen, ")");
|
||||
return &cast_node.base;
|
||||
} else if (tok.id == .FloatLiteral) {
|
||||
if (lit_bytes[0] == '.')
|
||||
lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
|
||||
if (tok.id.FloatLiteral == .None) {
|
||||
return transCreateNodeFloat(c, lit_bytes);
|
||||
}
|
||||
const cast_node = try c.createBuiltinCall("@as", 2);
|
||||
cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (tok.id.FloatLiteral) {
|
||||
.F => "f32",
|
||||
.L => "c_longdouble",
|
||||
else => unreachable,
|
||||
});
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
|
||||
cast_node.rparen_token = try appendToken(c, .RParen, ")");
|
||||
return &cast_node.base;
|
||||
} else unreachable;
|
||||
const cast_node = try c.createBuiltinCall("@as", 2);
|
||||
cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
|
||||
.u => "c_uint",
|
||||
.l => "c_long",
|
||||
.lu => "c_ulong",
|
||||
.ll => "c_longlong",
|
||||
.llu => "c_ulonglong",
|
||||
else => unreachable,
|
||||
});
|
||||
lit_bytes = lit_bytes[0 .. lit_bytes.len - switch (suffix) {
|
||||
.u, .l => @as(u8, 1),
|
||||
.lu, .ll => 2,
|
||||
.llu => 3,
|
||||
else => unreachable,
|
||||
}];
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
cast_node.params()[1] = try transCreateNodeInt(c, lit_bytes);
|
||||
cast_node.rparen_token = try appendToken(c, .RParen, ")");
|
||||
return &cast_node.base;
|
||||
},
|
||||
.FloatLiteral => |suffix| {
|
||||
if (lit_bytes[0] == '.')
|
||||
lit_bytes = try std.fmt.allocPrint(c.arena, "0{}", .{lit_bytes});
|
||||
if (suffix == .none) {
|
||||
return transCreateNodeFloat(c, lit_bytes);
|
||||
}
|
||||
const cast_node = try c.createBuiltinCall("@as", 2);
|
||||
cast_node.params()[0] = try transCreateNodeIdentifier(c, switch (suffix) {
|
||||
.f => "f32",
|
||||
.l => "c_longdouble",
|
||||
else => unreachable,
|
||||
});
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
cast_node.params()[1] = try transCreateNodeFloat(c, lit_bytes[0 .. lit_bytes.len - 1]);
|
||||
cast_node.rparen_token = try appendToken(c, .RParen, ")");
|
||||
return &cast_node.base;
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const u8, source_loc: ZigClangSourceLocation) ![]const u8 {
|
||||
|
@ -5720,13 +5756,13 @@ fn zigifyEscapeSequences(ctx: *Context, source_bytes: []const u8, name: []const
|
|||
return bytes[0..i];
|
||||
}
|
||||
|
||||
fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
fn parseCPrimaryExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
const tok = it.next().?;
|
||||
switch (tok.id) {
|
||||
const slice = it.slice(it.i);
|
||||
switch (tok) {
|
||||
.CharLiteral => {
|
||||
const first_tok = it.list.at(0);
|
||||
if (source[tok.start] != '\'' or source[tok.start + 1] == '\\' or tok.end - tok.start == 3) {
|
||||
const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
|
||||
if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
|
||||
const token = try appendToken(c, .CharLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
|
||||
const node = try c.arena.create(ast.Node.OneToken);
|
||||
node.* = .{
|
||||
.base = .{ .tag = .CharLiteral },
|
||||
|
@ -5734,7 +5770,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
};
|
||||
return &node.base;
|
||||
} else {
|
||||
const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{source[tok.start + 1 .. tok.end - 1]});
|
||||
const token = try appendTokenFmt(c, .IntegerLiteral, "0x{x}", .{slice[1 .. slice.len - 1]});
|
||||
const node = try c.arena.create(ast.Node.OneToken);
|
||||
node.* = .{
|
||||
.base = .{ .tag = .IntegerLiteral },
|
||||
|
@ -5744,8 +5780,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
}
|
||||
},
|
||||
.StringLiteral => {
|
||||
const first_tok = it.list.at(0);
|
||||
const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, source[tok.start..tok.end], source[first_tok.start..first_tok.end], source_loc));
|
||||
const token = try appendToken(c, .StringLiteral, try zigifyEscapeSequences(c, slice, it.slice(0), source_loc));
|
||||
const node = try c.arena.create(ast.Node.OneToken);
|
||||
node.* = .{
|
||||
.base = .{ .tag = .StringLiteral },
|
||||
|
@ -5754,7 +5789,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
return &node.base;
|
||||
},
|
||||
.IntegerLiteral, .FloatLiteral => {
|
||||
return parseCNumLit(c, tok, source, source_loc);
|
||||
return parseCNumLit(c, it, source_loc);
|
||||
},
|
||||
// eventually this will be replaced by std.c.parse which will handle these correctly
|
||||
.Keyword_void => return transCreateNodeIdentifierUnchecked(c, "c_void"),
|
||||
|
@ -5764,22 +5799,50 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
.Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
|
||||
.Keyword_float => return transCreateNodeIdentifierUnchecked(c, "f32"),
|
||||
.Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
|
||||
.Keyword_char => return transCreateNodeIdentifierUnchecked(c, "c_char"),
|
||||
.Keyword_unsigned => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
|
||||
.Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
|
||||
.Keyword_unsigned => if (it.next()) |t| switch (t) {
|
||||
.Keyword_char => return transCreateNodeIdentifierUnchecked(c, "u8"),
|
||||
.Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_ushort"),
|
||||
.Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_uint"),
|
||||
.Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {
|
||||
_ = it.next();
|
||||
return transCreateNodeIdentifierUnchecked(c, "c_ulonglong");
|
||||
} else return transCreateNodeIdentifierUnchecked(c, "c_ulong"),
|
||||
else => {
|
||||
it.i -= 1;
|
||||
return transCreateNodeIdentifierUnchecked(c, "c_uint");
|
||||
},
|
||||
} else {
|
||||
return transCreateNodeIdentifierUnchecked(c, "c_uint");
|
||||
},
|
||||
.Keyword_signed => if (it.next()) |t| switch (t) {
|
||||
.Keyword_char => return transCreateNodeIdentifierUnchecked(c, "i8"),
|
||||
.Keyword_short => return transCreateNodeIdentifierUnchecked(c, "c_short"),
|
||||
.Keyword_int => return transCreateNodeIdentifierUnchecked(c, "c_int"),
|
||||
.Keyword_long => if (it.peek() != null and it.peek().? == .Keyword_long) {
|
||||
_ = it.next();
|
||||
return transCreateNodeIdentifierUnchecked(c, "c_longlong");
|
||||
} else return transCreateNodeIdentifierUnchecked(c, "c_long"),
|
||||
else => {
|
||||
it.i -= 1;
|
||||
return transCreateNodeIdentifierUnchecked(c, "c_int");
|
||||
},
|
||||
} else {
|
||||
return transCreateNodeIdentifierUnchecked(c, "c_int");
|
||||
},
|
||||
.Identifier => {
|
||||
const mangled_name = scope.getAlias(source[tok.start..tok.end]);
|
||||
const mangled_name = scope.getAlias(it.slice(it.i));
|
||||
return transCreateNodeIdentifier(c, mangled_name);
|
||||
},
|
||||
.LParen => {
|
||||
const inner_node = try parseCExpr(c, it, source, source_loc, scope);
|
||||
const inner_node = try parseCExpr(c, it, source_loc, scope);
|
||||
|
||||
const next_id = it.next().?.id;
|
||||
const next_id = it.next().?;
|
||||
if (next_id != .RParen) {
|
||||
const first_tok = it.list.at(0);
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected ')'' instead got: {}",
|
||||
.{@tagName(next_id)},
|
||||
);
|
||||
|
@ -5787,7 +5850,7 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
}
|
||||
var saw_l_paren = false;
|
||||
var saw_integer_literal = false;
|
||||
switch (it.peek().?.id) {
|
||||
switch (it.peek().?) {
|
||||
// (type)(to_cast)
|
||||
.LParen => {
|
||||
saw_l_paren = true;
|
||||
|
@ -5805,14 +5868,13 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
// hack to get zig fmt to render a comma in builtin calls
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
|
||||
const node_to_cast = try parseCExpr(c, it, source, source_loc, scope);
|
||||
const node_to_cast = try parseCExpr(c, it, source_loc, scope);
|
||||
|
||||
if (saw_l_paren and it.next().?.id != .RParen) {
|
||||
const first_tok = it.list.at(0);
|
||||
if (saw_l_paren and it.next().? != .RParen) {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected ')''",
|
||||
.{},
|
||||
);
|
||||
|
@ -5843,13 +5905,12 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
return &group_node.base;
|
||||
},
|
||||
else => {
|
||||
const first_tok = it.list.at(0);
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: unexpected token .{}",
|
||||
.{@tagName(tok.id)},
|
||||
.{@tagName(tok)},
|
||||
);
|
||||
return error.ParseError;
|
||||
},
|
||||
|
@ -5957,61 +6018,52 @@ fn macroIntToBool(c: *Context, node: *ast.Node) !*ast.Node {
|
|||
return &group_node.base;
|
||||
}
|
||||
|
||||
fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
var node = try parseCPrimaryExpr(c, it, source, source_loc, scope);
|
||||
fn parseCSuffixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
var node = try parseCPrimaryExpr(c, it, source_loc, scope);
|
||||
while (true) {
|
||||
const tok = it.next().?;
|
||||
var op_token: ast.TokenIndex = undefined;
|
||||
var op_id: ast.Node.Tag = undefined;
|
||||
var bool_op = false;
|
||||
switch (tok.id) {
|
||||
switch (it.next().?) {
|
||||
.Period => {
|
||||
const name_tok = it.next().?;
|
||||
if (name_tok.id != .Identifier) {
|
||||
const first_tok = it.list.at(0);
|
||||
if (it.next().? != .Identifier) {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected identifier",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
node = try transCreateNodeFieldAccess(c, node, source[name_tok.start..name_tok.end]);
|
||||
node = try transCreateNodeFieldAccess(c, node, it.slice(it.i));
|
||||
continue;
|
||||
},
|
||||
.Arrow => {
|
||||
const name_tok = it.next().?;
|
||||
if (name_tok.id != .Identifier) {
|
||||
const first_tok = it.list.at(0);
|
||||
if (it.next().? != .Identifier) {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected identifier",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
}
|
||||
const deref = try transCreateNodePtrDeref(c, node);
|
||||
node = try transCreateNodeFieldAccess(c, deref, source[name_tok.start..name_tok.end]);
|
||||
node = try transCreateNodeFieldAccess(c, deref, it.slice(it.i));
|
||||
continue;
|
||||
},
|
||||
.Asterisk => {
|
||||
if (it.peek().?.id == .RParen) {
|
||||
if (it.peek().? == .RParen) {
|
||||
// type *)
|
||||
|
||||
// hack to get zig fmt to render a comma in builtin calls
|
||||
_ = try appendToken(c, .Comma, ",");
|
||||
|
||||
// * token
|
||||
_ = it.prev();
|
||||
// last token of `node`
|
||||
const prev_id = it.prev().?.id;
|
||||
_ = it.next();
|
||||
_ = it.next();
|
||||
const prev_id = it.list[it.i - 1].id;
|
||||
|
||||
if (prev_id == .Keyword_void) {
|
||||
const ptr = try transCreateNodePtrType(c, false, false, .Asterisk);
|
||||
|
@ -6082,15 +6134,14 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
},
|
||||
.LBracket => {
|
||||
const arr_node = try transCreateNodeArrayAccess(c, node);
|
||||
arr_node.index_expr = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
arr_node.index_expr = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
arr_node.rtoken = try appendToken(c, .RBracket, "]");
|
||||
node = &arr_node.base;
|
||||
if (it.next().?.id != .RBracket) {
|
||||
const first_tok = it.list.at(0);
|
||||
if (it.next().? != .RBracket) {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected ']'",
|
||||
.{},
|
||||
);
|
||||
|
@ -6103,23 +6154,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
var call_params = std.ArrayList(*ast.Node).init(c.gpa);
|
||||
defer call_params.deinit();
|
||||
while (true) {
|
||||
const arg = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
const arg = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
try call_params.append(arg);
|
||||
const next = it.next().?;
|
||||
if (next.id == .Comma)
|
||||
_ = try appendToken(c, .Comma, ",")
|
||||
else if (next.id == .RParen)
|
||||
break
|
||||
else {
|
||||
const first_tok = it.list.at(0);
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
"unable to translate C expr: expected ',' or ')'",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
switch (it.next().?) {
|
||||
.Comma => _ = try appendToken(c, .Comma, ","),
|
||||
.RParen => break,
|
||||
else => {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected ',' or ')'",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
},
|
||||
}
|
||||
}
|
||||
const call_node = try ast.Node.Call.alloc(c.arena, call_params.items.len);
|
||||
|
@ -6144,23 +6193,21 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
defer init_vals.deinit();
|
||||
|
||||
while (true) {
|
||||
const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
const val = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
try init_vals.append(val);
|
||||
const next = it.next().?;
|
||||
if (next.id == .Comma)
|
||||
_ = try appendToken(c, .Comma, ",")
|
||||
else if (next.id == .RBrace)
|
||||
break
|
||||
else {
|
||||
const first_tok = it.list.at(0);
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
source[first_tok.start..first_tok.end],
|
||||
"unable to translate C expr: expected ',' or '}}'",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
switch (it.next().?) {
|
||||
.Comma => _ = try appendToken(c, .Comma, ","),
|
||||
.RBrace => break,
|
||||
else => {
|
||||
try failDecl(
|
||||
c,
|
||||
source_loc,
|
||||
it.slice(0),
|
||||
"unable to translate C expr: expected ',' or '}}'",
|
||||
.{},
|
||||
);
|
||||
return error.ParseError;
|
||||
},
|
||||
}
|
||||
}
|
||||
const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
|
||||
|
@ -6207,22 +6254,22 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
op_id = .ArrayCat;
|
||||
op_token = try appendToken(c, .PlusPlus, "++");
|
||||
|
||||
_ = it.prev();
|
||||
it.i -= 1;
|
||||
},
|
||||
.Identifier => {
|
||||
op_id = .ArrayCat;
|
||||
op_token = try appendToken(c, .PlusPlus, "++");
|
||||
|
||||
_ = it.prev();
|
||||
it.i -= 1;
|
||||
},
|
||||
else => {
|
||||
_ = it.prev();
|
||||
it.i -= 1;
|
||||
return node;
|
||||
},
|
||||
}
|
||||
const cast_fn = if (bool_op) macroIntToBool else macroBoolToInt;
|
||||
const lhs_node = try cast_fn(c, node);
|
||||
const rhs_node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
const rhs_node = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
const op_node = try c.arena.create(ast.Node.SimpleInfixOp);
|
||||
op_node.* = .{
|
||||
.base = .{ .tag = op_id },
|
||||
|
@ -6234,38 +6281,36 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||
}
|
||||
}
|
||||
|
||||
fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
const op_tok = it.next().?;
|
||||
|
||||
switch (op_tok.id) {
|
||||
fn parseCPrefixOpExpr(c: *Context, it: *CTokIterator, source_loc: ZigClangSourceLocation, scope: *Scope) ParseError!*ast.Node {
|
||||
switch (it.next().?) {
|
||||
.Bang => {
|
||||
const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!");
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
return &node.base;
|
||||
},
|
||||
.Minus => {
|
||||
const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-");
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
return &node.base;
|
||||
},
|
||||
.Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope),
|
||||
.Plus => return try parseCPrefixOpExpr(c, it, source_loc, scope),
|
||||
.Tilde => {
|
||||
const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~");
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
return &node.base;
|
||||
},
|
||||
.Asterisk => {
|
||||
const node = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
const node = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
return try transCreateNodePtrDeref(c, node);
|
||||
},
|
||||
.Ampersand => {
|
||||
const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&");
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
|
||||
node.rhs = try parseCPrefixOpExpr(c, it, source_loc, scope);
|
||||
return &node.base;
|
||||
},
|
||||
else => {
|
||||
_ = it.prev();
|
||||
return try parseCSuffixOpExpr(c, it, source, source_loc, scope);
|
||||
it.i -= 1;
|
||||
return try parseCSuffixOpExpr(c, it, source_loc, scope);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ pub const Type = extern union {
|
|||
|
||||
.array, .array_u8_sentinel_0 => return .Array,
|
||||
.single_const_pointer => return .Pointer,
|
||||
.single_mut_pointer => return .Pointer,
|
||||
.single_const_pointer_to_comptime_int => return .Pointer,
|
||||
.const_slice_u8 => return .Pointer,
|
||||
}
|
||||
|
@ -261,6 +262,15 @@ pub const Type = extern union {
|
|||
};
|
||||
return Type{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.single_mut_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", self.ptr_otherwise);
|
||||
const new_payload = try allocator.create(Payload.SingleMutPointer);
|
||||
new_payload.* = .{
|
||||
.base = payload.base,
|
||||
.pointee_type = try payload.pointee_type.copy(allocator),
|
||||
};
|
||||
return Type{ .ptr_otherwise = &new_payload.base };
|
||||
},
|
||||
.int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned),
|
||||
.int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned),
|
||||
.function => {
|
||||
|
@ -368,6 +378,12 @@ pub const Type = extern union {
|
|||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.single_mut_pointer => {
|
||||
const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", ty.ptr_otherwise);
|
||||
try out_stream.writeAll("*");
|
||||
ty = payload.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.int_signed => {
|
||||
const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise);
|
||||
return out_stream.print("i{}", .{payload.bits});
|
||||
|
@ -467,6 +483,7 @@ pub const Type = extern union {
|
|||
.array_u8_sentinel_0,
|
||||
.array, // TODO check for zero bits
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.int_signed, // TODO check for zero bits
|
||||
.int_unsigned, // TODO check for zero bits
|
||||
=> true,
|
||||
|
@ -493,13 +510,18 @@ pub const Type = extern union {
|
|||
.u8,
|
||||
.i8,
|
||||
.bool,
|
||||
.array_u8_sentinel_0,
|
||||
=> return 1,
|
||||
|
||||
.fn_noreturn_no_args, // represents machine code; not a pointer
|
||||
.fn_void_no_args, // represents machine code; not a pointer
|
||||
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
|
||||
.fn_ccc_void_no_args, // represents machine code; not a pointer
|
||||
.function, // represents machine code; not a pointer
|
||||
.array_u8_sentinel_0,
|
||||
=> return 1,
|
||||
=> return switch (target.cpu.arch) {
|
||||
.riscv64 => 2,
|
||||
else => 1,
|
||||
},
|
||||
|
||||
.i16, .u16 => return 2,
|
||||
.i32, .u32 => return 4,
|
||||
|
@ -510,6 +532,7 @@ pub const Type = extern union {
|
|||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8),
|
||||
|
||||
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
|
||||
|
@ -591,6 +614,7 @@ pub const Type = extern union {
|
|||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
=> return @divExact(target.cpu.arch.ptrBitWidth(), 8),
|
||||
|
||||
.c_short => return @divExact(CType.short.sizeInBits(target), 8),
|
||||
|
@ -671,6 +695,7 @@ pub const Type = extern union {
|
|||
=> false,
|
||||
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
=> true,
|
||||
};
|
||||
|
@ -714,6 +739,7 @@ pub const Type = extern union {
|
|||
.array,
|
||||
.array_u8_sentinel_0,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.fn_noreturn_no_args,
|
||||
.fn_void_no_args,
|
||||
|
@ -728,8 +754,7 @@ pub const Type = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a pointer type.
|
||||
pub fn pointerIsConst(self: Type) bool {
|
||||
pub fn isConstPtr(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.u8,
|
||||
.i8,
|
||||
|
@ -773,7 +798,8 @@ pub const Type = extern union {
|
|||
.function,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
=> unreachable,
|
||||
.single_mut_pointer,
|
||||
=> false,
|
||||
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
|
@ -782,6 +808,58 @@ pub const Type = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn isVolatilePtr(self: Type) bool {
|
||||
return switch (self.tag()) {
|
||||
.u8,
|
||||
.i8,
|
||||
.u16,
|
||||
.i16,
|
||||
.u32,
|
||||
.i32,
|
||||
.u64,
|
||||
.i64,
|
||||
.usize,
|
||||
.isize,
|
||||
.c_short,
|
||||
.c_ushort,
|
||||
.c_int,
|
||||
.c_uint,
|
||||
.c_long,
|
||||
.c_ulong,
|
||||
.c_longlong,
|
||||
.c_ulonglong,
|
||||
.c_longdouble,
|
||||
.f16,
|
||||
.f32,
|
||||
.f64,
|
||||
.f128,
|
||||
.c_void,
|
||||
.bool,
|
||||
.void,
|
||||
.type,
|
||||
.anyerror,
|
||||
.comptime_int,
|
||||
.comptime_float,
|
||||
.noreturn,
|
||||
.@"null",
|
||||
.@"undefined",
|
||||
.array,
|
||||
.array_u8_sentinel_0,
|
||||
.fn_noreturn_no_args,
|
||||
.fn_void_no_args,
|
||||
.fn_naked_noreturn_no_args,
|
||||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.int_unsigned,
|
||||
.int_signed,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Asserts the type is a pointer or array type.
|
||||
pub fn elemType(self: Type) Type {
|
||||
return switch (self.tag()) {
|
||||
|
@ -829,6 +907,7 @@ pub const Type = extern union {
|
|||
|
||||
.array => self.cast(Payload.Array).?.elem_type,
|
||||
.single_const_pointer => self.cast(Payload.SingleConstPointer).?.pointee_type,
|
||||
.single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type,
|
||||
.array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8),
|
||||
.single_const_pointer_to_comptime_int => Type.initTag(.comptime_int),
|
||||
};
|
||||
|
@ -876,6 +955,7 @@ pub const Type = extern union {
|
|||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
|
@ -929,6 +1009,7 @@ pub const Type = extern union {
|
|||
.fn_ccc_void_no_args,
|
||||
.function,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.const_slice_u8,
|
||||
.int_unsigned,
|
||||
|
@ -970,6 +1051,7 @@ pub const Type = extern union {
|
|||
.function,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1024,6 +1106,7 @@ pub const Type = extern union {
|
|||
.function,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1078,6 +1161,7 @@ pub const Type = extern union {
|
|||
.function,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1130,6 +1214,7 @@ pub const Type = extern union {
|
|||
.function,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1211,6 +1296,7 @@ pub const Type = extern union {
|
|||
.@"undefined",
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1268,6 +1354,7 @@ pub const Type = extern union {
|
|||
.@"undefined",
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1324,6 +1411,7 @@ pub const Type = extern union {
|
|||
.@"undefined",
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1380,6 +1468,7 @@ pub const Type = extern union {
|
|||
.@"undefined",
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1433,6 +1522,7 @@ pub const Type = extern union {
|
|||
.@"undefined",
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1486,6 +1576,7 @@ pub const Type = extern union {
|
|||
.@"undefined",
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1559,6 +1650,7 @@ pub const Type = extern union {
|
|||
.function,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
|
@ -1566,7 +1658,7 @@ pub const Type = extern union {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn onePossibleValue(self: Type) bool {
|
||||
pub fn onePossibleValue(self: Type) ?Value {
|
||||
var ty = self;
|
||||
while (true) switch (ty.tag()) {
|
||||
.f16,
|
||||
|
@ -1605,21 +1697,32 @@ pub const Type = extern union {
|
|||
.single_const_pointer_to_comptime_int,
|
||||
.array_u8_sentinel_0,
|
||||
.const_slice_u8,
|
||||
=> return false,
|
||||
|
||||
.c_void,
|
||||
.void,
|
||||
.noreturn,
|
||||
.@"null",
|
||||
.@"undefined",
|
||||
=> return true,
|
||||
=> return null,
|
||||
|
||||
.int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0,
|
||||
.int_signed => return ty.cast(Payload.IntSigned).?.bits == 0,
|
||||
.void => return Value.initTag(.void_value),
|
||||
.noreturn => return Value.initTag(.unreachable_value),
|
||||
.@"null" => return Value.initTag(.null_value),
|
||||
.@"undefined" => return Value.initTag(.undef),
|
||||
|
||||
.int_unsigned => {
|
||||
if (ty.cast(Payload.IntUnsigned).?.bits == 0) {
|
||||
return Value.initTag(.zero);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.int_signed => {
|
||||
if (ty.cast(Payload.IntSigned).?.bits == 0) {
|
||||
return Value.initTag(.zero);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
.array => {
|
||||
const array = ty.cast(Payload.Array).?;
|
||||
if (array.len == 0)
|
||||
return true;
|
||||
return Value.initTag(.empty_array);
|
||||
ty = array.elem_type;
|
||||
continue;
|
||||
},
|
||||
|
@ -1628,6 +1731,11 @@ pub const Type = extern union {
|
|||
ty = ptr.pointee_type;
|
||||
continue;
|
||||
},
|
||||
.single_mut_pointer => {
|
||||
const ptr = ty.cast(Payload.SingleMutPointer).?;
|
||||
ty = ptr.pointee_type;
|
||||
continue;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1678,6 +1786,7 @@ pub const Type = extern union {
|
|||
.int_signed,
|
||||
.array,
|
||||
.single_const_pointer,
|
||||
.single_mut_pointer,
|
||||
=> return false,
|
||||
};
|
||||
}
|
||||
|
@ -1734,6 +1843,7 @@ pub const Type = extern union {
|
|||
array_u8_sentinel_0,
|
||||
array,
|
||||
single_const_pointer,
|
||||
single_mut_pointer,
|
||||
int_signed,
|
||||
int_unsigned,
|
||||
function,
|
||||
|
@ -1764,6 +1874,12 @@ pub const Type = extern union {
|
|||
pointee_type: Type,
|
||||
};
|
||||
|
||||
pub const SingleMutPointer = struct {
|
||||
base: Payload = Payload{ .tag = .single_mut_pointer },
|
||||
|
||||
pointee_type: Type,
|
||||
};
|
||||
|
||||
pub const IntSigned = struct {
|
||||
base: Payload = Payload{ .tag = .int_signed },
|
||||
|
||||
|
|
|
@ -63,7 +63,9 @@ pub const Value = extern union {
|
|||
|
||||
undef,
|
||||
zero,
|
||||
the_one_possible_value, // when the type only has one possible value
|
||||
void_value,
|
||||
unreachable_value,
|
||||
empty_array,
|
||||
null_value,
|
||||
bool_true,
|
||||
bool_false, // See last_no_payload_tag below.
|
||||
|
@ -164,7 +166,9 @@ pub const Value = extern union {
|
|||
.const_slice_u8_type,
|
||||
.undef,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
.null_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
|
@ -285,7 +289,8 @@ pub const Value = extern union {
|
|||
.null_value => return out_stream.writeAll("null"),
|
||||
.undef => return out_stream.writeAll("undefined"),
|
||||
.zero => return out_stream.writeAll("0"),
|
||||
.the_one_possible_value => return out_stream.writeAll("(one possible value)"),
|
||||
.void_value => return out_stream.writeAll("{}"),
|
||||
.unreachable_value => return out_stream.writeAll("unreachable"),
|
||||
.bool_true => return out_stream.writeAll("true"),
|
||||
.bool_false => return out_stream.writeAll("false"),
|
||||
.ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream),
|
||||
|
@ -312,6 +317,7 @@ pub const Value = extern union {
|
|||
try out_stream.print("&[{}] ", .{elem_ptr.index});
|
||||
val = elem_ptr.array_ptr;
|
||||
},
|
||||
.empty_array => return out_stream.writeAll(".{}"),
|
||||
.bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream),
|
||||
.repeated => {
|
||||
try out_stream.writeAll("(repeated) ");
|
||||
|
@ -388,7 +394,9 @@ pub const Value = extern union {
|
|||
|
||||
.undef,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
|
@ -460,15 +468,18 @@ pub const Value = extern union {
|
|||
.decl_ref,
|
||||
.elem_ptr,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // An integer with one possible value is always zero.
|
||||
.undef => unreachable,
|
||||
|
||||
.zero,
|
||||
.bool_false,
|
||||
=> return BigIntMutable.init(&space.limbs, 0).toConst(),
|
||||
|
@ -532,16 +543,19 @@ pub const Value = extern union {
|
|||
.decl_ref,
|
||||
.elem_ptr,
|
||||
.bytes,
|
||||
.undef,
|
||||
.repeated,
|
||||
.float_16,
|
||||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.undef => unreachable,
|
||||
|
||||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> return 0,
|
||||
|
||||
|
@ -570,10 +584,9 @@ pub const Value = extern union {
|
|||
.float_64 => @floatCast(T, self.cast(Payload.Float_64).?.val),
|
||||
.float_128 => @floatCast(T, self.cast(Payload.Float_128).?.val),
|
||||
|
||||
.zero, .the_one_possible_value => 0,
|
||||
.zero => 0,
|
||||
.int_u64 => @intToFloat(T, self.cast(Payload.Int_u64).?.int),
|
||||
// .int_i64 => @intToFloat(f128, self.cast(Payload.Int_i64).?.int),
|
||||
.int_i64 => @panic("TODO lld: error: undefined symbol: __floatditf"),
|
||||
.int_i64 => @intToFloat(T, self.cast(Payload.Int_i64).?.int),
|
||||
|
||||
.int_big_positive, .int_big_negative => @panic("big int to f128"),
|
||||
else => unreachable,
|
||||
|
@ -637,9 +650,11 @@ pub const Value = extern union {
|
|||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.zero,
|
||||
.bool_false,
|
||||
=> return 0,
|
||||
|
@ -714,11 +729,13 @@ pub const Value = extern union {
|
|||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
.undef,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> return true,
|
||||
|
||||
|
@ -797,13 +814,13 @@ pub const Value = extern union {
|
|||
// return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
32 => {
|
||||
var res_payload = Value.Payload.Float_32{.val = self.toFloat(f32)};
|
||||
var res_payload = Value.Payload.Float_32{ .val = self.toFloat(f32) };
|
||||
if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
return error.Overflow;
|
||||
return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
},
|
||||
64 => {
|
||||
var res_payload = Value.Payload.Float_64{.val = self.toFloat(f64)};
|
||||
var res_payload = Value.Payload.Float_64{ .val = self.toFloat(f64) };
|
||||
if (!self.eql(Value.initPayload(&res_payload.base)))
|
||||
return error.Overflow;
|
||||
return Value.initPayload(&res_payload.base).copy(allocator);
|
||||
|
@ -875,7 +892,9 @@ pub const Value = extern union {
|
|||
.int_i64,
|
||||
.int_big_positive,
|
||||
.int_big_negative,
|
||||
.the_one_possible_value,
|
||||
.empty_array,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
=> unreachable,
|
||||
|
||||
.zero => false,
|
||||
|
@ -939,10 +958,12 @@ pub const Value = extern union {
|
|||
.bytes,
|
||||
.repeated,
|
||||
.undef,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.zero,
|
||||
.the_one_possible_value, // an integer with one possible value is always zero
|
||||
.bool_false,
|
||||
=> .eq,
|
||||
|
||||
|
@ -964,8 +985,8 @@ pub const Value = extern union {
|
|||
pub fn order(lhs: Value, rhs: Value) std.math.Order {
|
||||
const lhs_tag = lhs.tag();
|
||||
const rhs_tag = rhs.tag();
|
||||
const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value;
|
||||
const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value;
|
||||
const lhs_is_zero = lhs_tag == .zero;
|
||||
const rhs_is_zero = rhs_tag == .zero;
|
||||
if (lhs_is_zero) return rhs.orderAgainstZero().invert();
|
||||
if (rhs_is_zero) return lhs.orderAgainstZero();
|
||||
|
||||
|
@ -1071,9 +1092,11 @@ pub const Value = extern union {
|
|||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
.empty_array,
|
||||
=> unreachable,
|
||||
|
||||
.the_one_possible_value => Value.initTag(.the_one_possible_value),
|
||||
.ref_val => self.cast(Payload.RefVal).?.val,
|
||||
.decl_ref => self.cast(Payload.DeclRef).?.decl.value(),
|
||||
.elem_ptr => {
|
||||
|
@ -1130,7 +1153,6 @@ pub const Value = extern union {
|
|||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.null_value,
|
||||
|
@ -1147,8 +1169,12 @@ pub const Value = extern union {
|
|||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
.unreachable_value,
|
||||
=> unreachable,
|
||||
|
||||
.empty_array => unreachable, // out of bounds array index
|
||||
|
||||
.bytes => {
|
||||
const int_payload = try allocator.create(Payload.Int_u64);
|
||||
int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] };
|
||||
|
@ -1175,8 +1201,7 @@ pub const Value = extern union {
|
|||
return self.tag() == .undef;
|
||||
}
|
||||
|
||||
/// Valid for all types. Asserts the value is not undefined.
|
||||
/// `.the_one_possible_value` is reported as not null.
|
||||
/// Valid for all types. Asserts the value is not undefined and not unreachable.
|
||||
pub fn isNull(self: Value) bool {
|
||||
return switch (self.tag()) {
|
||||
.ty,
|
||||
|
@ -1221,7 +1246,7 @@ pub const Value = extern union {
|
|||
.single_const_pointer_to_comptime_int_type,
|
||||
.const_slice_u8_type,
|
||||
.zero,
|
||||
.the_one_possible_value,
|
||||
.empty_array,
|
||||
.bool_true,
|
||||
.bool_false,
|
||||
.function,
|
||||
|
@ -1238,9 +1263,11 @@ pub const Value = extern union {
|
|||
.float_32,
|
||||
.float_64,
|
||||
.float_128,
|
||||
.void_value,
|
||||
=> false,
|
||||
|
||||
.undef => unreachable,
|
||||
.unreachable_value => unreachable,
|
||||
.null_value => true,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -34,25 +34,63 @@ pub const Inst = struct {
|
|||
|
||||
/// These names are used directly as the instruction names in the text format.
|
||||
pub const Tag = enum {
|
||||
/// Arithmetic addition, asserts no integer overflow.
|
||||
add,
|
||||
/// Twos complement wrapping integer addition.
|
||||
addwrap,
|
||||
/// Allocates stack local memory. Its lifetime ends when the block ends that contains
|
||||
/// this instruction.
|
||||
/// this instruction. The operand is the type of the allocated object.
|
||||
alloc,
|
||||
/// Same as `alloc` except the type is inferred.
|
||||
alloc_inferred,
|
||||
/// Array concatenation. `a ++ b`
|
||||
array_cat,
|
||||
/// Array multiplication `a ** b`
|
||||
array_mul,
|
||||
/// Function parameter value. These must be first in a function's main block,
|
||||
/// in respective order with the parameters.
|
||||
arg,
|
||||
/// Type coercion.
|
||||
as,
|
||||
/// Inline assembly.
|
||||
@"asm",
|
||||
/// Bitwise AND. `&`
|
||||
bitand,
|
||||
/// TODO delete this instruction, it has no purpose.
|
||||
bitcast,
|
||||
/// An arbitrary typed pointer, which is to be used as an L-Value, is pointer-casted
|
||||
/// to a new L-Value. The destination type is given by LHS. The cast is to be evaluated
|
||||
/// as if it were a bit-cast operation from the operand pointer element type to the
|
||||
/// provided destination type.
|
||||
bitcast_lvalue,
|
||||
/// A typed result location pointer is bitcasted to a new result location pointer.
|
||||
/// The new result location pointer has an inferred type.
|
||||
bitcast_result_ptr,
|
||||
/// Bitwise OR. `|`
|
||||
bitor,
|
||||
/// A labeled block of code, which can return a value.
|
||||
block,
|
||||
/// Boolean NOT. See also `bitnot`.
|
||||
boolnot,
|
||||
/// Return a value from a `Block`.
|
||||
@"break",
|
||||
breakpoint,
|
||||
/// Same as `break` but without an operand; the operand is assumed to be the void value.
|
||||
breakvoid,
|
||||
/// Function call.
|
||||
call,
|
||||
/// `<`
|
||||
cmp_lt,
|
||||
/// `<=`
|
||||
cmp_lte,
|
||||
/// `==`
|
||||
cmp_eq,
|
||||
/// `>=`
|
||||
cmp_gte,
|
||||
/// `>`
|
||||
cmp_gt,
|
||||
/// `!=`
|
||||
cmp_neq,
|
||||
/// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
|
||||
/// as type coercion from the new element type to the old element type.
|
||||
/// LHS is destination element type, RHS is result pointer.
|
||||
|
@ -65,8 +103,12 @@ pub const Inst = struct {
|
|||
coerce_to_ptr_elem,
|
||||
/// Emit an error message and fail compilation.
|
||||
compileerror,
|
||||
/// Conditional branch. Splits control flow based on a boolean condition value.
|
||||
condbr,
|
||||
/// Special case, has no textual representation.
|
||||
@"const",
|
||||
/// Declares the beginning of a statement. Used for debug info.
|
||||
dbg_stmt,
|
||||
/// Represents a pointer to a global decl by name.
|
||||
declref,
|
||||
/// Represents a pointer to a global decl by string name.
|
||||
|
@ -76,61 +118,108 @@ pub const Inst = struct {
|
|||
declval,
|
||||
/// Same as declval but the parameter is a `*Module.Decl` rather than a name.
|
||||
declval_in_module,
|
||||
/// Load the value from a pointer.
|
||||
deref,
|
||||
/// Arithmetic division. Asserts no integer overflow.
|
||||
div,
|
||||
/// Given a pointer to an array, slice, or pointer, returns a pointer to the element at
|
||||
/// the provided index.
|
||||
elemptr,
|
||||
/// Emits a compile error if the operand is not `void`.
|
||||
ensure_result_used,
|
||||
/// Emits a compile error if an error is ignored.
|
||||
ensure_result_non_error,
|
||||
boolnot,
|
||||
/// Export the provided Decl as the provided name in the compilation's output object file.
|
||||
@"export",
|
||||
/// Given a pointer to a struct or object that contains virtual fields, returns a pointer
|
||||
/// to the named field.
|
||||
fieldptr,
|
||||
/// Convert a larger float type to any other float type, possibly causing a loss of precision.
|
||||
floatcast,
|
||||
/// Declare a function body.
|
||||
@"fn",
|
||||
/// Returns a function type.
|
||||
fntype,
|
||||
/// Integer literal.
|
||||
int,
|
||||
/// Convert an integer value to another integer type, asserting that the destination type
|
||||
/// can hold the same mathematical value.
|
||||
intcast,
|
||||
/// Make an integer type out of signedness and bit count.
|
||||
inttype,
|
||||
/// Return a boolean false if an optional is null. `x != null`
|
||||
isnonnull,
|
||||
/// Return a boolean true if an optional is null. `x == null`
|
||||
isnull,
|
||||
/// Ambiguously remainder division or modulus. If the computation would possibly have
|
||||
/// a different value depending on whether the operation is remainder division or modulus,
|
||||
/// a compile error is emitted. Otherwise the computation is performed.
|
||||
mod_rem,
|
||||
/// Arithmetic multiplication. Asserts no integer overflow.
|
||||
mul,
|
||||
/// Twos complement wrapping integer multiplication.
|
||||
mulwrap,
|
||||
/// Given a reference to a function and a parameter index, returns the
|
||||
/// type of the parameter. TODO what happens when the parameter is `anytype`?
|
||||
param_type,
|
||||
/// An alternative to using `const` for simple primitive values such as `true` or `u8`.
|
||||
/// TODO flatten so that each primitive has its own ZIR Inst Tag.
|
||||
primitive,
|
||||
/// Convert a pointer to a `usize` integer.
|
||||
ptrtoint,
|
||||
/// Turns an R-Value into a const L-Value. In other words, it takes a value,
|
||||
/// stores it in a memory location, and returns a const pointer to it. If the value
|
||||
/// is `comptime`, the memory location is global static constant data. Otherwise,
|
||||
/// the memory location is in the stack frame, local to the scope containing the
|
||||
/// instruction.
|
||||
ref,
|
||||
/// Obtains a pointer to the return value.
|
||||
ret_ptr,
|
||||
/// Obtains the return type of the in-scope function.
|
||||
ret_type,
|
||||
/// Write a value to a pointer.
|
||||
/// Sends control flow back to the function's callee. Takes an operand as the return value.
|
||||
@"return",
|
||||
/// Same as `return` but there is no operand; the operand is implicitly the void value.
|
||||
returnvoid,
|
||||
/// Integer shift-left. Zeroes are shifted in from the right hand side.
|
||||
shl,
|
||||
/// Integer shift-right. Arithmetic or logical depending on the signedness of the integer type.
|
||||
shr,
|
||||
/// Create a const pointer type based on the element type. `*const T`
|
||||
single_const_ptr_type,
|
||||
/// Create a mutable pointer type based on the element type. `*T`
|
||||
single_mut_ptr_type,
|
||||
/// Write a value to a pointer. For loading, see `deref`.
|
||||
store,
|
||||
/// String Literal. Makes an anonymous Decl and then takes a pointer to it.
|
||||
str,
|
||||
int,
|
||||
inttype,
|
||||
ptrtoint,
|
||||
fieldptr,
|
||||
deref,
|
||||
as,
|
||||
@"asm",
|
||||
@"unreachable",
|
||||
@"return",
|
||||
returnvoid,
|
||||
@"fn",
|
||||
fntype,
|
||||
@"export",
|
||||
/// Given a reference to a function and a parameter index, returns the
|
||||
/// type of the parameter. TODO what happens when the parameter is `anytype`?
|
||||
param_type,
|
||||
primitive,
|
||||
intcast,
|
||||
bitcast,
|
||||
floatcast,
|
||||
elemptr,
|
||||
add,
|
||||
/// Arithmetic subtraction. Asserts no integer overflow.
|
||||
sub,
|
||||
cmp_lt,
|
||||
cmp_lte,
|
||||
cmp_eq,
|
||||
cmp_gte,
|
||||
cmp_gt,
|
||||
cmp_neq,
|
||||
condbr,
|
||||
isnull,
|
||||
isnonnull,
|
||||
/// Twos complement wrapping integer subtraction.
|
||||
subwrap,
|
||||
/// Returns the type of a value.
|
||||
typeof,
|
||||
/// Asserts control-flow will not reach this instruction. Not safety checked - the compiler
|
||||
/// will assume the correctness of this instruction.
|
||||
unreach_nocheck,
|
||||
/// Asserts control-flow will not reach this instruction. In safety-checked modes,
|
||||
/// this will generate a call to the panic function unless it can be proven unreachable
|
||||
/// by the compiler.
|
||||
@"unreachable",
|
||||
/// Bitwise XOR. `^`
|
||||
xor,
|
||||
|
||||
pub fn Type(tag: Tag) type {
|
||||
return switch (tag) {
|
||||
.arg,
|
||||
.breakpoint,
|
||||
.@"unreachable",
|
||||
.dbg_stmt,
|
||||
.returnvoid,
|
||||
.alloc_inferred,
|
||||
.ret_ptr,
|
||||
.ret_type,
|
||||
.unreach_nocheck,
|
||||
.@"unreachable",
|
||||
=> NoOp,
|
||||
|
||||
.boolnot,
|
||||
|
@ -143,10 +232,28 @@ pub const Inst = struct {
|
|||
.ensure_result_used,
|
||||
.ensure_result_non_error,
|
||||
.bitcast_result_ptr,
|
||||
.ref,
|
||||
.bitcast_lvalue,
|
||||
.typeof,
|
||||
.single_const_ptr_type,
|
||||
.single_mut_ptr_type,
|
||||
=> UnOp,
|
||||
|
||||
.add,
|
||||
.addwrap,
|
||||
.array_cat,
|
||||
.array_mul,
|
||||
.bitand,
|
||||
.bitor,
|
||||
.div,
|
||||
.mod_rem,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.shl,
|
||||
.shr,
|
||||
.store,
|
||||
.sub,
|
||||
.subwrap,
|
||||
.cmp_lt,
|
||||
.cmp_lte,
|
||||
.cmp_eq,
|
||||
|
@ -158,6 +265,7 @@ pub const Inst = struct {
|
|||
.intcast,
|
||||
.bitcast,
|
||||
.coerce_result_ptr,
|
||||
.xor,
|
||||
=> BinOp,
|
||||
|
||||
.block => Block,
|
||||
|
@ -172,7 +280,6 @@ pub const Inst = struct {
|
|||
.coerce_result_block_ptr => CoerceResultBlockPtr,
|
||||
.compileerror => CompileError,
|
||||
.@"const" => Const,
|
||||
.store => Store,
|
||||
.str => Str,
|
||||
.int => Int,
|
||||
.inttype => IntType,
|
||||
|
@ -192,63 +299,83 @@ pub const Inst = struct {
|
|||
/// Function calls do not count.
|
||||
pub fn isNoReturn(tag: Tag) bool {
|
||||
return switch (tag) {
|
||||
.add,
|
||||
.addwrap,
|
||||
.alloc,
|
||||
.alloc_inferred,
|
||||
.array_cat,
|
||||
.array_mul,
|
||||
.arg,
|
||||
.bitcast_result_ptr,
|
||||
.block,
|
||||
.breakpoint,
|
||||
.call,
|
||||
.coerce_result_ptr,
|
||||
.coerce_result_block_ptr,
|
||||
.coerce_to_ptr_elem,
|
||||
.@"const",
|
||||
.declref,
|
||||
.declref_str,
|
||||
.declval,
|
||||
.declval_in_module,
|
||||
.ensure_result_used,
|
||||
.ensure_result_non_error,
|
||||
.ret_ptr,
|
||||
.ret_type,
|
||||
.store,
|
||||
.str,
|
||||
.int,
|
||||
.inttype,
|
||||
.ptrtoint,
|
||||
.fieldptr,
|
||||
.deref,
|
||||
.as,
|
||||
.@"asm",
|
||||
.@"fn",
|
||||
.fntype,
|
||||
.@"export",
|
||||
.param_type,
|
||||
.primitive,
|
||||
.intcast,
|
||||
.bitand,
|
||||
.bitcast,
|
||||
.floatcast,
|
||||
.elemptr,
|
||||
.add,
|
||||
.sub,
|
||||
.bitcast_lvalue,
|
||||
.bitcast_result_ptr,
|
||||
.bitor,
|
||||
.block,
|
||||
.boolnot,
|
||||
.breakpoint,
|
||||
.call,
|
||||
.cmp_lt,
|
||||
.cmp_lte,
|
||||
.cmp_eq,
|
||||
.cmp_gte,
|
||||
.cmp_gt,
|
||||
.cmp_neq,
|
||||
.isnull,
|
||||
.coerce_result_ptr,
|
||||
.coerce_result_block_ptr,
|
||||
.coerce_to_ptr_elem,
|
||||
.@"const",
|
||||
.dbg_stmt,
|
||||
.declref,
|
||||
.declref_str,
|
||||
.declval,
|
||||
.declval_in_module,
|
||||
.deref,
|
||||
.div,
|
||||
.elemptr,
|
||||
.ensure_result_used,
|
||||
.ensure_result_non_error,
|
||||
.@"export",
|
||||
.floatcast,
|
||||
.fieldptr,
|
||||
.@"fn",
|
||||
.fntype,
|
||||
.int,
|
||||
.intcast,
|
||||
.inttype,
|
||||
.isnonnull,
|
||||
.boolnot,
|
||||
.isnull,
|
||||
.mod_rem,
|
||||
.mul,
|
||||
.mulwrap,
|
||||
.param_type,
|
||||
.primitive,
|
||||
.ptrtoint,
|
||||
.ref,
|
||||
.ret_ptr,
|
||||
.ret_type,
|
||||
.shl,
|
||||
.shr,
|
||||
.single_const_ptr_type,
|
||||
.single_mut_ptr_type,
|
||||
.store,
|
||||
.str,
|
||||
.sub,
|
||||
.subwrap,
|
||||
.typeof,
|
||||
.xor,
|
||||
=> false,
|
||||
|
||||
.condbr,
|
||||
.@"unreachable",
|
||||
.@"return",
|
||||
.returnvoid,
|
||||
.@"break",
|
||||
.breakvoid,
|
||||
.condbr,
|
||||
.compileerror,
|
||||
.@"return",
|
||||
.returnvoid,
|
||||
.unreach_nocheck,
|
||||
.@"unreachable",
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
|
@ -430,17 +557,6 @@ pub const Inst = struct {
|
|||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Store = struct {
|
||||
pub const base_tag = Tag.store;
|
||||
base: Inst,
|
||||
|
||||
positionals: struct {
|
||||
ptr: *Inst,
|
||||
value: *Inst,
|
||||
},
|
||||
kw_args: struct {},
|
||||
};
|
||||
|
||||
pub const Str = struct {
|
||||
pub const base_tag = Tag.str;
|
||||
base: Inst,
|
||||
|
@ -630,7 +746,7 @@ pub const Inst = struct {
|
|||
.@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) },
|
||||
.@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) },
|
||||
.@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) },
|
||||
.void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) },
|
||||
.void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.void_value) },
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -1486,6 +1602,21 @@ const EmitZIR = struct {
|
|||
const decl = decl_ref.decl;
|
||||
return try self.emitUnnamedDecl(try self.emitDeclRef(src, decl));
|
||||
}
|
||||
if (typed_value.val.isUndef()) {
|
||||
const as_inst = try self.arena.allocator.create(Inst.BinOp);
|
||||
as_inst.* = .{
|
||||
.base = .{
|
||||
.tag = .as,
|
||||
.src = src,
|
||||
},
|
||||
.positionals = .{
|
||||
.lhs = (try self.emitType(src, typed_value.ty)).inst,
|
||||
.rhs = (try self.emitPrimitive(src, .@"undefined")).inst,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
return self.emitUnnamedDecl(&as_inst.base);
|
||||
}
|
||||
switch (typed_value.ty.zigTypeTag()) {
|
||||
.Pointer => {
|
||||
const ptr_elem_type = typed_value.ty.elemType();
|
||||
|
@ -1716,15 +1847,19 @@ const EmitZIR = struct {
|
|||
.breakpoint => try self.emitNoOp(inst.src, .breakpoint),
|
||||
.unreach => try self.emitNoOp(inst.src, .@"unreachable"),
|
||||
.retvoid => try self.emitNoOp(inst.src, .returnvoid),
|
||||
.dbg_stmt => try self.emitNoOp(inst.src, .dbg_stmt),
|
||||
|
||||
.not => try self.emitUnOp(inst.src, new_body, inst.castTag(.not).?, .boolnot),
|
||||
.ret => try self.emitUnOp(inst.src, new_body, inst.castTag(.ret).?, .@"return"),
|
||||
.ptrtoint => try self.emitUnOp(inst.src, new_body, inst.castTag(.ptrtoint).?, .ptrtoint),
|
||||
.isnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnull).?, .isnull),
|
||||
.isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull),
|
||||
.load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref),
|
||||
.ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref),
|
||||
|
||||
.add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add),
|
||||
.sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub),
|
||||
.store => try self.emitBinOp(inst.src, new_body, inst.castTag(.store).?, .store),
|
||||
.cmp_lt => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lt).?, .cmp_lt),
|
||||
.cmp_lte => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_lte).?, .cmp_lte),
|
||||
.cmp_eq => try self.emitBinOp(inst.src, new_body, inst.castTag(.cmp_eq).?, .cmp_eq),
|
||||
|
@ -1736,6 +1871,21 @@ const EmitZIR = struct {
|
|||
.intcast => try self.emitCast(inst.src, new_body, inst.castTag(.intcast).?, .intcast),
|
||||
.floatcast => try self.emitCast(inst.src, new_body, inst.castTag(.floatcast).?, .floatcast),
|
||||
|
||||
.alloc => blk: {
|
||||
const new_inst = try self.arena.allocator.create(Inst.UnOp);
|
||||
new_inst.* = .{
|
||||
.base = .{
|
||||
.src = inst.src,
|
||||
.tag = .alloc,
|
||||
},
|
||||
.positionals = .{
|
||||
.operand = (try self.emitType(inst.src, inst.ty)).inst,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
break :blk &new_inst.base;
|
||||
},
|
||||
|
||||
.block => blk: {
|
||||
const old_inst = inst.castTag(.block).?;
|
||||
const new_inst = try self.arena.allocator.create(Inst.Block);
|
||||
|
@ -1973,6 +2123,25 @@ const EmitZIR = struct {
|
|||
};
|
||||
return self.emitUnnamedDecl(&inttype_inst.base);
|
||||
},
|
||||
.Pointer => {
|
||||
if (ty.isSinglePointer()) {
|
||||
const inst = try self.arena.allocator.create(Inst.UnOp);
|
||||
const tag: Inst.Tag = if (ty.isConstPtr()) .single_const_ptr_type else .single_mut_ptr_type;
|
||||
inst.* = .{
|
||||
.base = .{
|
||||
.src = src,
|
||||
.tag = tag,
|
||||
},
|
||||
.positionals = .{
|
||||
.operand = (try self.emitType(src, ty.elemType())).inst,
|
||||
},
|
||||
.kw_args = .{},
|
||||
};
|
||||
return self.emitUnnamedDecl(&inst.base);
|
||||
} else {
|
||||
std.debug.panic("TODO implement emitType for {}", .{ty});
|
||||
}
|
||||
},
|
||||
else => std.debug.panic("TODO implement emitType for {}", .{ty}),
|
||||
},
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -4110,7 +4110,7 @@ struct IrInstSrcCheckSwitchProngs {
|
|||
IrInstSrc *target_value;
|
||||
IrInstSrcCheckSwitchProngsRange *ranges;
|
||||
size_t range_count;
|
||||
bool have_else_prong;
|
||||
AstNode* else_prong;
|
||||
bool have_underscore_prong;
|
||||
};
|
||||
|
||||
|
|
|
@ -1490,6 +1490,20 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
|
|||
}
|
||||
|
||||
ZigType *analyze_type_expr(CodeGen *g, Scope *scope, AstNode *node) {
|
||||
Error err;
|
||||
// Hot path for simple identifiers, to avoid unnecessary memory allocations.
|
||||
if (node->type == NodeTypeSymbol) {
|
||||
Buf *variable_name = node->data.symbol_expr.symbol;
|
||||
if (buf_eql_str(variable_name, "_"))
|
||||
goto abort_hot_path;
|
||||
ZigType *primitive_type;
|
||||
if ((err = get_primitive_type(g, variable_name, &primitive_type))) {
|
||||
goto abort_hot_path;
|
||||
} else {
|
||||
return primitive_type;
|
||||
}
|
||||
abort_hot_path:;
|
||||
}
|
||||
ZigValue *result = analyze_const_value(g, scope, node, g->builtin_types.entry_type,
|
||||
nullptr, UndefBad);
|
||||
if (type_is_invalid(result->type))
|
||||
|
|
74
src/ir.cpp
74
src/ir.cpp
|
@ -4300,14 +4300,14 @@ static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode
|
|||
|
||||
static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
|
||||
IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count,
|
||||
bool have_else_prong, bool have_underscore_prong)
|
||||
AstNode* else_prong, bool have_underscore_prong)
|
||||
{
|
||||
IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction<IrInstSrcCheckSwitchProngs>(
|
||||
irb, scope, source_node);
|
||||
instruction->target_value = target_value;
|
||||
instruction->ranges = ranges;
|
||||
instruction->range_count = range_count;
|
||||
instruction->have_else_prong = have_else_prong;
|
||||
instruction->else_prong = else_prong;
|
||||
instruction->have_underscore_prong = have_underscore_prong;
|
||||
|
||||
ir_ref_instruction(target_value, irb->current_basic_block);
|
||||
|
@ -9347,7 +9347,7 @@ static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *n
|
|||
}
|
||||
|
||||
IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value,
|
||||
check_ranges.items, check_ranges.length, else_prong != nullptr, underscore_prong != nullptr);
|
||||
check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr);
|
||||
|
||||
IrInstSrc *br_instruction;
|
||||
if (cases.length == 0) {
|
||||
|
@ -20604,17 +20604,25 @@ static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr,
|
|||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
|
||||
ZigType *expected_return_type = result_loc->value->type->data.pointer.child_type;
|
||||
|
||||
IrInstGen *dummy_value = ir_const(ira, source_instr, return_type);
|
||||
dummy_value->value->special = ConstValSpecialRuntime;
|
||||
IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr,
|
||||
dummy_value, result_loc->value->type->data.pointer.child_type);
|
||||
if (type_is_invalid(dummy_result->value->type))
|
||||
dummy_value, expected_return_type);
|
||||
if (type_is_invalid(dummy_result->value->type)) {
|
||||
if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) &&
|
||||
expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet)
|
||||
{
|
||||
add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
|
||||
ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error"));
|
||||
}
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
ZigType *res_child_type = result_loc->value->type->data.pointer.child_type;
|
||||
if (res_child_type == ira->codegen->builtin_types.entry_anytype) {
|
||||
res_child_type = return_type;
|
||||
}
|
||||
if (!handle_is_ptr(ira->codegen, res_child_type)) {
|
||||
if (expected_return_type == ira->codegen->builtin_types.entry_anytype) {
|
||||
expected_return_type = return_type;
|
||||
}
|
||||
if (!handle_is_ptr(ira->codegen, expected_return_type)) {
|
||||
ir_reset_result(call_result_loc);
|
||||
result_loc = nullptr;
|
||||
}
|
||||
|
@ -28828,7 +28836,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
|||
buf_ptr(enum_field->name)));
|
||||
}
|
||||
}
|
||||
} else if (!instruction->have_else_prong) {
|
||||
} else if (instruction->else_prong == nullptr) {
|
||||
if (switch_type->data.enumeration.non_exhaustive) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong"));
|
||||
|
@ -28843,6 +28851,10 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
|||
buf_ptr(enum_field->name)));
|
||||
}
|
||||
}
|
||||
} else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) {
|
||||
ir_add_error_node(ira, instruction->else_prong,
|
||||
buf_sprintf("unreachable else prong, all cases already handled"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
} else if (switch_type->id == ZigTypeIdErrorSet) {
|
||||
if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) {
|
||||
|
@ -28889,7 +28901,7 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
|||
}
|
||||
field_prev_uses[start_index] = start_value->base.source_node;
|
||||
}
|
||||
if (!instruction->have_else_prong) {
|
||||
if (instruction->else_prong == nullptr) {
|
||||
if (type_is_global_error_set(switch_type)) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("else prong required when switching on type 'anyerror'"));
|
||||
|
@ -28951,16 +28963,20 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
|||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
if (!instruction->have_else_prong) {
|
||||
|
||||
BigInt min_val;
|
||||
eval_min_max_value_int(ira->codegen, switch_type, &min_val, false);
|
||||
BigInt max_val;
|
||||
eval_min_max_value_int(ira->codegen, switch_type, &max_val, true);
|
||||
if (!rangeset_spans(&rs, &min_val, &max_val)) {
|
||||
bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val);
|
||||
if (!handles_all_cases && instruction->else_prong == nullptr) {
|
||||
ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
} else if(handles_all_cases && instruction->else_prong != nullptr) {
|
||||
ir_add_error_node(ira, instruction->else_prong,
|
||||
buf_sprintf("unreachable else prong, all cases already handled"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
} else if (switch_type->id == ZigTypeIdBool) {
|
||||
int seenTrue = 0;
|
||||
int seenFalse = 0;
|
||||
|
@ -28990,11 +29006,17 @@ static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira,
|
|||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
if (((seenTrue < 1) || (seenFalse < 1)) && !instruction->have_else_prong) {
|
||||
if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) {
|
||||
ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
} else if (!instruction->have_else_prong) {
|
||||
|
||||
if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) {
|
||||
ir_add_error_node(ira, instruction->else_prong,
|
||||
buf_sprintf("unreachable else prong, all cases already handled"));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
} else if (instruction->else_prong == nullptr) {
|
||||
ir_add_error(ira, &instruction->base.base,
|
||||
buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name)));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
|
@ -29077,6 +29099,19 @@ static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t alig
|
|||
ZigType *result_type;
|
||||
uint32_t old_align_bytes;
|
||||
|
||||
ZigType *actual_ptr = target_type;
|
||||
if (actual_ptr->id == ZigTypeIdOptional) {
|
||||
actual_ptr = actual_ptr->data.maybe.child_type;
|
||||
} else if (is_slice(actual_ptr)) {
|
||||
actual_ptr = actual_ptr->data.structure.fields[slice_ptr_index]->type_entry;
|
||||
}
|
||||
|
||||
if (safety_check_on && !type_has_bits(ira->codegen, actual_ptr)) {
|
||||
ir_add_error(ira, &target->base,
|
||||
buf_sprintf("cannot adjust alignment of zero sized type '%s'", buf_ptr(&target_type->name)));
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
|
||||
if (target_type->id == ZigTypeIdPointer) {
|
||||
result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes);
|
||||
if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes)))
|
||||
|
@ -30894,6 +30929,13 @@ static IrInstGen *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstSrcEndEx
|
|||
IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value,
|
||||
instruction->result_loc->allow_write_through_const);
|
||||
if (type_is_invalid(store_ptr->value->type)) {
|
||||
if (instruction->result_loc->id == ResultLocIdReturn &&
|
||||
(value->value->type->id == ZigTypeIdErrorUnion || value->value->type->id == ZigTypeIdErrorSet) &&
|
||||
ira->explicit_return_type->id != ZigTypeIdErrorUnion && ira->explicit_return_type->id != ZigTypeIdErrorSet)
|
||||
{
|
||||
add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg,
|
||||
ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error"));
|
||||
}
|
||||
return ira->codegen->invalid_inst_gen;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2175,7 +2175,7 @@ static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchPr
|
|||
fprintf(irp->f, "...");
|
||||
ir_print_other_inst_src(irp, instruction->ranges[i].end);
|
||||
}
|
||||
const char *have_else_str = instruction->have_else_prong ? "yes" : "no";
|
||||
const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no";
|
||||
fprintf(irp->f, ")else:%s", have_else_str);
|
||||
}
|
||||
|
||||
|
|
|
@ -2107,6 +2107,10 @@ static void construct_linker_job_wasm(LinkJob *lj) {
|
|||
lj->args.append("-z");
|
||||
lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size)));
|
||||
|
||||
// put stack before globals so that stack overflow results in segfault immediately before corrupting globals
|
||||
// see https://github.com/ziglang/zig/issues/4496
|
||||
lj->args.append("--stack-first");
|
||||
|
||||
if (g->out_type != OutTypeExe) {
|
||||
lj->args.append("--no-entry"); // So lld doesn't look for _start.
|
||||
|
||||
|
|
|
@ -2823,6 +2823,14 @@ struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(
|
|||
return bitcast(casted->getBeginLoc());
|
||||
}
|
||||
|
||||
|
||||
enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(
|
||||
const struct ZigClangUnaryExprOrTypeTraitExpr *self)
|
||||
{
|
||||
auto casted = reinterpret_cast<const clang::UnaryExprOrTypeTraitExpr *>(self);
|
||||
return (ZigClangUnaryExprOrTypeTrait_Kind)casted->getKind();
|
||||
}
|
||||
|
||||
const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *self) {
|
||||
auto casted = reinterpret_cast<const clang::DoStmt *>(self);
|
||||
return reinterpret_cast<const struct ZigClangStmt *>(casted->getBody());
|
||||
|
|
|
@ -901,6 +901,14 @@ enum ZigClangExpr_ConstExprUsage {
|
|||
ZigClangExpr_EvaluateForMangling,
|
||||
};
|
||||
|
||||
enum ZigClangUnaryExprOrTypeTrait_Kind {
|
||||
ZigClangUnaryExprOrTypeTrait_KindSizeOf,
|
||||
ZigClangUnaryExprOrTypeTrait_KindAlignOf,
|
||||
ZigClangUnaryExprOrTypeTrait_KindVecStep,
|
||||
ZigClangUnaryExprOrTypeTrait_KindOpenMPRequiredSimdAlign,
|
||||
ZigClangUnaryExprOrTypeTrait_KindPreferredAlignOf,
|
||||
};
|
||||
|
||||
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangSourceManager_getSpellingLoc(const struct ZigClangSourceManager *,
|
||||
struct ZigClangSourceLocation Loc);
|
||||
ZIG_EXTERN_C const char *ZigClangSourceManager_getFilename(const struct ZigClangSourceManager *,
|
||||
|
@ -1190,6 +1198,7 @@ ZIG_EXTERN_C const struct ZigClangExpr *ZigClangArraySubscriptExpr_getIdx(const
|
|||
|
||||
ZIG_EXTERN_C struct ZigClangQualType ZigClangUnaryExprOrTypeTraitExpr_getTypeOfArgument(const struct ZigClangUnaryExprOrTypeTraitExpr *);
|
||||
ZIG_EXTERN_C struct ZigClangSourceLocation ZigClangUnaryExprOrTypeTraitExpr_getBeginLoc(const struct ZigClangUnaryExprOrTypeTraitExpr *);
|
||||
ZIG_EXTERN_C enum ZigClangUnaryExprOrTypeTrait_Kind ZigClangUnaryExprOrTypeTraitExpr_getKind(const struct ZigClangUnaryExprOrTypeTraitExpr *);
|
||||
|
||||
ZIG_EXTERN_C const struct ZigClangStmt *ZigClangDoStmt_getBody(const struct ZigClangDoStmt *);
|
||||
ZIG_EXTERN_C const struct ZigClangExpr *ZigClangDoStmt_getCond(const struct ZigClangDoStmt *);
|
||||
|
|
|
@ -2,6 +2,32 @@ const tests = @import("tests.zig");
|
|||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.addTest("@alignCast of zero sized types",
|
||||
\\export fn foo() void {
|
||||
\\ const a: *void = undefined;
|
||||
\\ _ = @alignCast(2, a);
|
||||
\\}
|
||||
\\export fn bar() void {
|
||||
\\ const a: ?*void = undefined;
|
||||
\\ _ = @alignCast(2, a);
|
||||
\\}
|
||||
\\export fn baz() void {
|
||||
\\ const a: []void = undefined;
|
||||
\\ _ = @alignCast(2, a);
|
||||
\\}
|
||||
\\export fn qux() void {
|
||||
\\ const a = struct {
|
||||
\\ fn a(comptime b: u32) void {}
|
||||
\\ }.a;
|
||||
\\ _ = @alignCast(2, a);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:3:23: error: cannot adjust alignment of zero sized type '*void'",
|
||||
"tmp.zig:7:23: error: cannot adjust alignment of zero sized type '?*void'",
|
||||
"tmp.zig:11:23: error: cannot adjust alignment of zero sized type '[]void'",
|
||||
"tmp.zig:17:23: error: cannot adjust alignment of zero sized type 'fn(u32) anytype'",
|
||||
});
|
||||
|
||||
cases.addTest("invalid pointer with @Type",
|
||||
\\export fn entry() void {
|
||||
\\ _ = @Type(.{ .Pointer = .{
|
||||
|
@ -18,6 +44,28 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||
"tmp.zig:2:16: error: sentinels are only allowed on slices and unknown-length pointers",
|
||||
});
|
||||
|
||||
cases.addTest("helpful return type error message",
|
||||
\\export fn foo() u32 {
|
||||
\\ return error.Ohno;
|
||||
\\}
|
||||
\\fn bar() !u32 {
|
||||
\\ return error.Ohno;
|
||||
\\}
|
||||
\\export fn baz() void {
|
||||
\\ try bar();
|
||||
\\}
|
||||
\\export fn quux() u32 {
|
||||
\\ return bar();
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:17: error: expected type 'u32', found 'error{Ohno}'",
|
||||
"tmp.zig:1:17: note: function cannot return an error",
|
||||
"tmp.zig:8:5: error: expected type 'void', found '@TypeOf(bar).ReturnType.ErrorSet'",
|
||||
"tmp.zig:7:17: note: function cannot return an error",
|
||||
"tmp.zig:11:15: error: expected type 'u32', found '@TypeOf(bar).ReturnType.ErrorSet!u32'",
|
||||
"tmp.zig:10:18: note: function cannot return an error",
|
||||
});
|
||||
|
||||
cases.addTest("int/float conversion to comptime_int/float",
|
||||
\\export fn foo() void {
|
||||
\\ var a: f32 = 2;
|
||||
|
@ -509,6 +557,102 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||
"tmp.zig:12:5: error: switch on non-exhaustive enum must include `else` or `_` prong",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (bool)",
|
||||
\\fn foo(x: bool) void {
|
||||
\\ switch (x) {
|
||||
\\ true => {},
|
||||
\\ false => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:5:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (u1)",
|
||||
\\fn foo(x: u1) void {
|
||||
\\ switch (x) {
|
||||
\\ 0 => {},
|
||||
\\ 1 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:5:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (u2)",
|
||||
\\fn foo(x: u2) void {
|
||||
\\ switch (x) {
|
||||
\\ 0 => {},
|
||||
\\ 1 => {},
|
||||
\\ 2 => {},
|
||||
\\ 3 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:7:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (range u8)",
|
||||
\\fn foo(x: u8) void {
|
||||
\\ switch (x) {
|
||||
\\ 0 => {},
|
||||
\\ 1 => {},
|
||||
\\ 2 => {},
|
||||
\\ 3 => {},
|
||||
\\ 4...255 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:8:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (range i8)",
|
||||
\\fn foo(x: i8) void {
|
||||
\\ switch (x) {
|
||||
\\ -128...0 => {},
|
||||
\\ 1 => {},
|
||||
\\ 2 => {},
|
||||
\\ 3 => {},
|
||||
\\ 4...127 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:8:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.add("switch expression - unreachable else prong (enum)",
|
||||
\\const TestEnum = enum{ T1, T2 };
|
||||
\\
|
||||
\\fn err(x: u8) TestEnum {
|
||||
\\ switch (x) {
|
||||
\\ 0 => return TestEnum.T1,
|
||||
\\ else => return TestEnum.T2,
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
\\fn foo(x: u8) void {
|
||||
\\ switch (err(x)) {
|
||||
\\ TestEnum.T1 => {},
|
||||
\\ TestEnum.T2 => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
\\export fn entry() usize { return @sizeOf(@TypeOf(foo)); }
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:14:9: error: unreachable else prong, all cases already handled",
|
||||
});
|
||||
|
||||
cases.addTest("@export with empty name string",
|
||||
\\pub export fn entry() void { }
|
||||
\\comptime {
|
||||
|
|
|
@ -7,6 +7,5 @@ test "issue 1111 fixed" {
|
|||
|
||||
switch (v) {
|
||||
Foo.Bar => return,
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,11 @@ const linux_x64 = std.zig.CrossTarget{
|
|||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
const linux_riscv64 = std.zig.CrossTarget{
|
||||
.cpu_arch = .riscv64,
|
||||
.os_tag = .linux,
|
||||
};
|
||||
|
||||
pub fn addCases(ctx: *TestContext) !void {
|
||||
if (std.Target.current.os.tag != .linux or
|
||||
std.Target.current.cpu.arch != .x86_64)
|
||||
|
@ -118,6 +123,42 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||
\\
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("hello world", linux_riscv64);
|
||||
// Regular old hello world
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ print();
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn print() void {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (64),
|
||||
\\ [arg1] "{a0}" (1),
|
||||
\\ [arg2] "{a1}" (@ptrToInt("Hello, World!\n")),
|
||||
\\ [arg3] "{a2}" ("Hello, World!\n".len)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ return;
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("ecall"
|
||||
\\ :
|
||||
\\ : [number] "{a7}" (94),
|
||||
\\ [arg1] "{a0}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"Hello, World!\n",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
var case = ctx.exe("adding numbers at comptime", linux_x64);
|
||||
|
@ -333,5 +374,69 @@ pub fn addCases(ctx: *TestContext) !void {
|
|||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Now we test integer return values.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 7);
|
||||
\\ assert(add(20, 10) == 30);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ return a + b;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
|
||||
// Local mutable variables.
|
||||
case.addCompareOutput(
|
||||
\\export fn _start() noreturn {
|
||||
\\ assert(add(3, 4) == 7);
|
||||
\\ assert(add(20, 10) == 30);
|
||||
\\
|
||||
\\ exit();
|
||||
\\}
|
||||
\\
|
||||
\\fn add(a: u32, b: u32) u32 {
|
||||
\\ var x: u32 = undefined;
|
||||
\\ x = 0;
|
||||
\\ x += a;
|
||||
\\ x += b;
|
||||
\\ return x;
|
||||
\\}
|
||||
\\
|
||||
\\pub fn assert(ok: bool) void {
|
||||
\\ if (!ok) unreachable; // assertion failure
|
||||
\\}
|
||||
\\
|
||||
\\fn exit() noreturn {
|
||||
\\ asm volatile ("syscall"
|
||||
\\ :
|
||||
\\ : [number] "{rax}" (231),
|
||||
\\ [arg1] "{rdi}" (0)
|
||||
\\ : "rcx", "r11", "memory"
|
||||
\\ );
|
||||
\\ unreachable;
|
||||
\\}
|
||||
,
|
||||
"",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,16 @@ const std = @import("std");
|
|||
const CrossTarget = std.zig.CrossTarget;
|
||||
|
||||
pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
cases.add("alignof",
|
||||
\\int main() {
|
||||
\\ int a = _Alignof(int);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
\\pub export fn main() c_int {
|
||||
\\ var a: c_int = @bitCast(c_int, @truncate(c_uint, @alignOf(c_int)));
|
||||
\\}
|
||||
});
|
||||
|
||||
cases.add("initializer list macro",
|
||||
\\typedef struct Color {
|
||||
\\ unsigned char r;
|
||||
|
@ -2715,6 +2725,16 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
|||
\\pub const BAR = (@import("std").meta.cast(?*c_void, a));
|
||||
});
|
||||
|
||||
cases.add("macro with cast to unsigned short, long, and long long",
|
||||
\\#define CURLAUTH_BASIC_BUT_USHORT ((unsigned short) 1)
|
||||
\\#define CURLAUTH_BASIC ((unsigned long) 1)
|
||||
\\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
|
||||
, &[_][]const u8{
|
||||
\\pub const CURLAUTH_BASIC_BUT_USHORT = (@import("std").meta.cast(c_ushort, 1));
|
||||
\\pub const CURLAUTH_BASIC = (@import("std").meta.cast(c_ulong, 1));
|
||||
\\pub const CURLAUTH_BASIC_BUT_ULONGLONG = (@import("std").meta.cast(c_ulonglong, 1));
|
||||
});
|
||||
|
||||
cases.add("macro conditional operator",
|
||||
\\#define FOO a ? b : c
|
||||
, &[_][]const u8{
|
||||
|
|
Loading…
Reference in New Issue