mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge branch 'master' into feature-file-locks
This commit is contained in:
commit
113b217593
@ -622,7 +622,6 @@ set(BUILD_LIBSTAGE2_ARGS "build-lib"
|
||||
--cache on
|
||||
--output-dir "${CMAKE_BINARY_DIR}"
|
||||
${LIBSTAGE2_RELEASE_ARG}
|
||||
--disable-gen-h
|
||||
--bundle-compiler-rt
|
||||
-fPIC
|
||||
-lc
|
||||
|
||||
17
build.zig
17
build.zig
@ -134,7 +134,8 @@ pub fn build(b: *Builder) !void {
|
||||
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
|
||||
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
|
||||
test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter));
|
||||
test_step.dependOn(tests.addGenHTests(b, test_filter));
|
||||
// tests for this feature are disabled until we have the self-hosted compiler available
|
||||
//test_step.dependOn(tests.addGenHTests(b, test_filter));
|
||||
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
|
||||
test_step.dependOn(docs_step);
|
||||
}
|
||||
@ -298,10 +299,14 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
|
||||
dependOnLib(b, exe, ctx.llvm);
|
||||
|
||||
if (exe.target.getOsTag() == .linux) {
|
||||
try addCxxKnownPath(b, ctx, exe, "libstdc++.a",
|
||||
\\Unable to determine path to libstdc++.a
|
||||
\\On Fedora, install libstdc++-static and try again.
|
||||
);
|
||||
// First we try to static link against gcc libstdc++. If that doesn't work,
|
||||
// we fall back to -lc++ and cross our fingers.
|
||||
addCxxKnownPath(b, ctx, exe, "libstdc++.a", "") catch |err| switch (err) {
|
||||
error.RequiredLibraryNotFound => {
|
||||
exe.linkSystemLibrary("c++");
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
exe.linkSystemLibrary("pthread");
|
||||
} else if (exe.target.isFreeBSD()) {
|
||||
@ -320,7 +325,7 @@ fn configureStage2(b: *Builder, exe: var, ctx: Context) !void {
|
||||
// System compiler, not gcc.
|
||||
exe.linkSystemLibrary("c++");
|
||||
},
|
||||
else => return err,
|
||||
else => |e| return e,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,7 +26,8 @@ make -j$(nproc) install
|
||||
# TODO test-cli is hitting https://github.com/ziglang/zig/issues/3526
|
||||
./zig build test-asm-link test-runtime-safety
|
||||
# TODO test-translate-c is hitting https://github.com/ziglang/zig/issues/3526
|
||||
./zig build test-gen-h
|
||||
# TODO disabled until we are shipping self-hosted
|
||||
#./zig build test-gen-h
|
||||
# TODO test-compile-errors is hitting https://github.com/ziglang/zig/issues/3526
|
||||
# TODO building docs is hitting https://github.com/ziglang/zig/issues/3526
|
||||
|
||||
|
||||
@ -42,7 +42,8 @@ release/bin/zig build test-asm-link
|
||||
release/bin/zig build test-runtime-safety
|
||||
release/bin/zig build test-translate-c
|
||||
release/bin/zig build test-run-translated-c
|
||||
release/bin/zig build test-gen-h
|
||||
# TODO disabled until we are shipping self-hosted
|
||||
#release/bin/zig build test-gen-h
|
||||
release/bin/zig build test-compile-errors
|
||||
release/bin/zig build docs
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ pub fn main() !void {
|
||||
var toc = try genToc(allocator, &tokenizer);
|
||||
|
||||
try fs.cwd().makePath(tmp_dir_name);
|
||||
defer fs.deleteTree(tmp_dir_name) catch {};
|
||||
defer fs.cwd().deleteTree(tmp_dir_name) catch {};
|
||||
|
||||
try genHtml(allocator, &tokenizer, &toc, buffered_out_stream.outStream(), zig_exe);
|
||||
try buffered_out_stream.flush();
|
||||
@ -1096,6 +1096,9 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
try build_args.append("-lc");
|
||||
try out.print(" -lc", .{});
|
||||
}
|
||||
const target = try std.zig.CrossTarget.parse(.{
|
||||
.arch_os_abi = code.target_str orelse "native",
|
||||
});
|
||||
if (code.target_str) |triple| {
|
||||
try build_args.appendSlice(&[_][]const u8{ "-target", triple });
|
||||
if (!code.is_inline) {
|
||||
@ -1150,7 +1153,15 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
}
|
||||
}
|
||||
|
||||
const path_to_exe = mem.trim(u8, exec_result.stdout, " \r\n");
|
||||
const path_to_exe_dir = mem.trim(u8, exec_result.stdout, " \r\n");
|
||||
const path_to_exe_basename = try std.fmt.allocPrint(allocator, "{}{}", .{
|
||||
code.name,
|
||||
target.exeFileExt(),
|
||||
});
|
||||
const path_to_exe = try fs.path.join(allocator, &[_][]const u8{
|
||||
path_to_exe_dir,
|
||||
path_to_exe_basename,
|
||||
});
|
||||
const run_args = &[_][]const u8{path_to_exe};
|
||||
|
||||
var exited_with_signal = false;
|
||||
@ -1486,7 +1497,12 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
|
||||
}
|
||||
|
||||
fn exec(allocator: *mem.Allocator, env_map: *std.BufMap, args: []const []const u8) !ChildProcess.ExecResult {
|
||||
const result = try ChildProcess.exec(allocator, args, null, env_map, max_doc_file_size);
|
||||
const result = try ChildProcess.exec2(.{
|
||||
.allocator = allocator,
|
||||
.argv = args,
|
||||
.env_map = env_map,
|
||||
.max_output_bytes = max_doc_file_size,
|
||||
});
|
||||
switch (result.term) {
|
||||
.Exited => |exit_code| {
|
||||
if (exit_code != 0) {
|
||||
|
||||
@ -885,6 +885,12 @@ const hex_int = 0xff;
|
||||
const another_hex_int = 0xFF;
|
||||
const octal_int = 0o755;
|
||||
const binary_int = 0b11110000;
|
||||
|
||||
// underscores may be placed between two digits as a visual separator
|
||||
const one_billion = 1_000_000_000;
|
||||
const binary_mask = 0b1_1111_1111;
|
||||
const permissions = 0o7_5_5;
|
||||
const big_address = 0xFF80_0000_0000_0000;
|
||||
{#code_end#}
|
||||
{#header_close#}
|
||||
{#header_open|Runtime Integer Values#}
|
||||
@ -947,6 +953,11 @@ const yet_another = 123.0e+77;
|
||||
const hex_floating_point = 0x103.70p-5;
|
||||
const another_hex_float = 0x103.70;
|
||||
const yet_another_hex_float = 0x103.70P-5;
|
||||
|
||||
// underscores may be placed between two digits as a visual separator
|
||||
const lightspeed = 299_792_458.000_000;
|
||||
const nanosecond = 0.000_000_001;
|
||||
const more_hex = 0x1234_5678.9ABC_CDEFp-10;
|
||||
{#code_end#}
|
||||
<p>
|
||||
There is no syntax for NaN, infinity, or negative infinity. For these special values,
|
||||
@ -2093,8 +2104,9 @@ var foo: u8 align(4) = 100;
|
||||
test "global variable alignment" {
|
||||
assert(@TypeOf(&foo).alignment == 4);
|
||||
assert(@TypeOf(&foo) == *align(4) u8);
|
||||
const slice = @as(*[1]u8, &foo)[0..];
|
||||
assert(@TypeOf(slice) == []align(4) u8);
|
||||
const as_pointer_to_array: *[1]u8 = &foo;
|
||||
const as_slice: []u8 = as_pointer_to_array;
|
||||
assert(@TypeOf(as_slice) == []align(4) u8);
|
||||
}
|
||||
|
||||
fn derp() align(@sizeOf(usize) * 2) i32 { return 1234; }
|
||||
@ -2187,7 +2199,8 @@ test "basic slices" {
|
||||
// a slice is that the array's length is part of the type and known at
|
||||
// compile-time, whereas the slice's length is known at runtime.
|
||||
// Both can be accessed with the `len` field.
|
||||
const slice = array[0..array.len];
|
||||
var known_at_runtime_zero: usize = 0;
|
||||
const slice = array[known_at_runtime_zero..array.len];
|
||||
assert(&slice[0] == &array[0]);
|
||||
assert(slice.len == array.len);
|
||||
|
||||
@ -2207,13 +2220,15 @@ test "basic slices" {
|
||||
{#code_end#}
|
||||
<p>This is one reason we prefer slices to pointers.</p>
|
||||
{#code_begin|test|slices#}
|
||||
const assert = @import("std").debug.assert;
|
||||
const mem = @import("std").mem;
|
||||
const fmt = @import("std").fmt;
|
||||
const std = @import("std");
|
||||
const assert = std.debug.assert;
|
||||
const mem = std.mem;
|
||||
const fmt = std.fmt;
|
||||
|
||||
test "using slices for strings" {
|
||||
// Zig has no concept of strings. String literals are arrays of u8, and
|
||||
// in general the string type is []u8 (slice of u8).
|
||||
// Zig has no concept of strings. String literals are const pointers to
|
||||
// arrays of u8, and by convention parameters that are "strings" are
|
||||
// expected to be UTF-8 encoded slices of u8.
|
||||
// Here we coerce [5]u8 to []const u8
|
||||
const hello: []const u8 = "hello";
|
||||
const world: []const u8 = "世界";
|
||||
@ -2222,7 +2237,7 @@ test "using slices for strings" {
|
||||
// You can use slice syntax on an array to convert an array into a slice.
|
||||
const all_together_slice = all_together[0..];
|
||||
// String concatenation example.
|
||||
const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{hello, world});
|
||||
const hello_world = try fmt.bufPrint(all_together_slice, "{} {}", .{ hello, world });
|
||||
|
||||
// Generally, you can use UTF-8 and not worry about whether something is a
|
||||
// string. If you don't need to deal with individual characters, no need
|
||||
@ -2239,23 +2254,15 @@ test "slice pointer" {
|
||||
slice[2] = 3;
|
||||
assert(slice[2] == 3);
|
||||
// The slice is mutable because we sliced a mutable pointer.
|
||||
assert(@TypeOf(slice) == []u8);
|
||||
// Furthermore, it is actually a pointer to an array, since the start
|
||||
// and end indexes were both comptime-known.
|
||||
assert(@TypeOf(slice) == *[5]u8);
|
||||
|
||||
// You can also slice a slice:
|
||||
const slice2 = slice[2..3];
|
||||
assert(slice2.len == 1);
|
||||
assert(slice2[0] == 3);
|
||||
}
|
||||
|
||||
test "slice widening" {
|
||||
// Zig supports slice widening and slice narrowing. Cast a slice of u8
|
||||
// to a slice of anything else, and Zig will perform the length conversion.
|
||||
const array align(@alignOf(u32)) = [_]u8{ 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13 };
|
||||
const slice = mem.bytesAsSlice(u32, array[0..]);
|
||||
assert(slice.len == 2);
|
||||
assert(slice[0] == 0x12121212);
|
||||
assert(slice[1] == 0x13131313);
|
||||
}
|
||||
{#code_end#}
|
||||
{#see_also|Pointers|for|Arrays#}
|
||||
|
||||
|
||||
@ -193,6 +193,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
|
||||
|
||||
29
|
||||
|
||||
29
|
||||
|
||||
29
|
||||
@ -514,6 +515,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
|
||||
@ -697,6 +699,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
29
|
||||
@ -819,6 +822,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
@ -904,6 +908,9 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
|
||||
29
|
||||
29
|
||||
29
|
||||
@ -1004,6 +1011,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
@ -1033,6 +1041,7 @@ aarch64-linux-gnu aarch64_be-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
@ -3920,6 +3929,7 @@ s390x-linux-gnu
|
||||
|
||||
|
||||
5
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -4241,6 +4251,7 @@ s390x-linux-gnu
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
11
|
||||
27
|
||||
|
||||
@ -4424,6 +4435,7 @@ s390x-linux-gnu
|
||||
19
|
||||
19
|
||||
5
|
||||
|
||||
5
|
||||
5
|
||||
28
|
||||
@ -4543,6 +4555,7 @@ s390x-linux-gnu
|
||||
27
|
||||
|
||||
16
|
||||
|
||||
5
|
||||
5
|
||||
15
|
||||
@ -4631,6 +4644,9 @@ s390x-linux-gnu
|
||||
16
|
||||
5
|
||||
5
|
||||
|
||||
|
||||
12
|
||||
5
|
||||
5
|
||||
5
|
||||
@ -4731,6 +4747,7 @@ s390x-linux-gnu
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
|
||||
5
|
||||
5
|
||||
@ -4756,6 +4773,7 @@ s390x-linux-gnu
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
31 5
|
||||
24 5 12 16
|
||||
24 5 12 16
|
||||
@ -7645,6 +7663,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
|
||||
@ -7968,6 +7987,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
|
||||
27
|
||||
|
||||
@ -8151,6 +8171,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
19
|
||||
19
|
||||
16
|
||||
|
||||
16
|
||||
16
|
||||
28
|
||||
@ -8273,6 +8294,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
|
||||
16
|
||||
16
|
||||
@ -8358,6 +8380,9 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
|
||||
|
||||
16
|
||||
16
|
||||
16
|
||||
@ -8458,6 +8483,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
|
||||
16
|
||||
16
|
||||
@ -8484,6 +8510,7 @@ arm-linux-gnueabi armeb-linux-gnueabi arm-linux-gnueabihf armeb-linux-gnueabihf
|
||||
16
|
||||
16
|
||||
16
|
||||
16
|
||||
24 16
|
||||
24 16
|
||||
16
|
||||
@ -11374,6 +11401,7 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
|
||||
|
||||
0
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -11693,6 +11721,7 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
0
|
||||
0
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
||||
3 11
|
||||
@ -11878,6 +11907,7 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
19
|
||||
19
|
||||
0
|
||||
|
||||
0
|
||||
1
|
||||
28
|
||||
@ -11997,6 +12027,7 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
33
|
||||
|
||||
16
|
||||
|
||||
5
|
||||
0
|
||||
15
|
||||
@ -12085,6 +12116,9 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
|
||||
|
||||
1
|
||||
1
|
||||
1
|
||||
@ -12183,6 +12217,7 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
||||
|
||||
@ -12207,6 +12242,7 @@ sparc-linux-gnu sparcel-linux-gnu
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
@ -15101,6 +15137,7 @@ sparcv9-linux-gnu
|
||||
5
|
||||
5
|
||||
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -15422,6 +15459,7 @@ sparcv9-linux-gnu
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
11
|
||||
27
|
||||
|
||||
@ -15605,6 +15643,7 @@ sparcv9-linux-gnu
|
||||
19
|
||||
19
|
||||
5
|
||||
|
||||
5
|
||||
5
|
||||
28
|
||||
@ -15724,6 +15763,7 @@ sparcv9-linux-gnu
|
||||
27
|
||||
|
||||
16
|
||||
|
||||
5
|
||||
5
|
||||
15
|
||||
@ -15812,6 +15852,9 @@ sparcv9-linux-gnu
|
||||
16
|
||||
5
|
||||
5
|
||||
12
|
||||
|
||||
|
||||
5
|
||||
5
|
||||
5
|
||||
@ -15912,6 +15955,7 @@ sparcv9-linux-gnu
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
|
||||
5
|
||||
5
|
||||
@ -15938,6 +15982,7 @@ sparcv9-linux-gnu
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
24 28 5 12 16
|
||||
24 28 5 12 16
|
||||
5 14
|
||||
@ -18828,6 +18873,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
|
||||
|
||||
0
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -19147,6 +19193,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
0
|
||||
0
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
11
|
||||
@ -19332,6 +19379,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
19
|
||||
19
|
||||
0
|
||||
|
||||
0
|
||||
5
|
||||
28
|
||||
@ -19450,6 +19498,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
27
|
||||
27
|
||||
|
||||
16
|
||||
16
|
||||
5
|
||||
0
|
||||
@ -19539,6 +19588,9 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
|
||||
|
||||
5
|
||||
5
|
||||
5
|
||||
@ -19637,6 +19689,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
0
|
||||
@ -19661,6 +19714,7 @@ mips64el-linux-gnuabi64 mips64-linux-gnuabi64
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
@ -22555,6 +22609,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
|
||||
|
||||
0
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -22874,6 +22929,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
0
|
||||
0
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
11
|
||||
@ -23059,6 +23115,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
19
|
||||
19
|
||||
0
|
||||
|
||||
0
|
||||
5
|
||||
28
|
||||
@ -23177,6 +23234,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
27
|
||||
27
|
||||
|
||||
16
|
||||
16
|
||||
5
|
||||
0
|
||||
@ -23266,6 +23324,9 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
|
||||
|
||||
5
|
||||
5
|
||||
5
|
||||
@ -23364,6 +23425,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
0
|
||||
@ -23388,6 +23450,7 @@ mips64el-linux-gnuabin32 mips64-linux-gnuabin32
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
@ -26282,6 +26345,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
|
||||
|
||||
0
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -26601,6 +26665,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
0
|
||||
0
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
11
|
||||
@ -26786,6 +26851,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
19
|
||||
19
|
||||
0
|
||||
|
||||
0
|
||||
5
|
||||
28
|
||||
@ -26904,6 +26970,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
27
|
||||
|
||||
|
||||
16
|
||||
16
|
||||
5
|
||||
0
|
||||
@ -26993,6 +27060,9 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
|
||||
|
||||
5
|
||||
5
|
||||
5
|
||||
@ -27091,6 +27161,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
0
|
||||
@ -27115,6 +27186,7 @@ mipsel-linux-gnueabihf mips-linux-gnueabihf
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
@ -30009,6 +30081,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
|
||||
|
||||
0
|
||||
|
||||
27
|
||||
|
||||
27
|
||||
@ -30328,6 +30401,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
0
|
||||
0
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
11
|
||||
@ -30513,6 +30587,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
19
|
||||
19
|
||||
0
|
||||
|
||||
0
|
||||
5
|
||||
28
|
||||
@ -30631,6 +30706,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
27
|
||||
|
||||
|
||||
16
|
||||
16
|
||||
5
|
||||
0
|
||||
@ -30720,6 +30796,9 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
|
||||
|
||||
5
|
||||
5
|
||||
5
|
||||
@ -30818,6 +30897,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
5
|
||||
5
|
||||
5
|
||||
5
|
||||
0
|
||||
0
|
||||
0
|
||||
@ -30842,6 +30922,7 @@ mipsel-linux-gnueabi mips-linux-gnueabi
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
@ -33734,6 +33815,7 @@ x86_64-linux-gnu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
|
||||
@ -34057,6 +34139,7 @@ x86_64-linux-gnu
|
||||
10
|
||||
10
|
||||
10
|
||||
10
|
||||
11
|
||||
27
|
||||
36
|
||||
@ -34240,6 +34323,7 @@ x86_64-linux-gnu
|
||||
19
|
||||
19
|
||||
10
|
||||
|
||||
10
|
||||
10
|
||||
28
|
||||
@ -34359,6 +34443,7 @@ x86_64-linux-gnu
|
||||
27
|
||||
|
||||
16
|
||||
|
||||
10
|
||||
10
|
||||
15
|
||||
@ -34447,6 +34532,9 @@ x86_64-linux-gnu
|
||||
16
|
||||
10
|
||||
10
|
||||
12
|
||||
|
||||
|
||||
10
|
||||
10
|
||||
10
|
||||
@ -34547,6 +34635,7 @@ x86_64-linux-gnu
|
||||
10
|
||||
10
|
||||
10
|
||||
10
|
||||
|
||||
10
|
||||
10
|
||||
@ -34573,6 +34662,7 @@ x86_64-linux-gnu
|
||||
10
|
||||
10
|
||||
10
|
||||
10
|
||||
24 10 12 16
|
||||
24 10 12 16
|
||||
10 14
|
||||
@ -37461,6 +37551,7 @@ x86_64-linux-gnux32
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
28
|
||||
@ -37784,6 +37875,7 @@ x86_64-linux-gnux32
|
||||
28
|
||||
28
|
||||
28
|
||||
28
|
||||
|
||||
28
|
||||
36
|
||||
@ -37967,6 +38059,7 @@ x86_64-linux-gnux32
|
||||
28
|
||||
28
|
||||
28
|
||||
|
||||
28
|
||||
28
|
||||
28
|
||||
@ -38086,6 +38179,7 @@ x86_64-linux-gnux32
|
||||
28
|
||||
|
||||
28
|
||||
|
||||
28
|
||||
28
|
||||
28
|
||||
@ -38174,6 +38268,9 @@ x86_64-linux-gnux32
|
||||
28
|
||||
28
|
||||
28
|
||||
28
|
||||
|
||||
|
||||
28
|
||||
28
|
||||
28
|
||||
@ -38274,6 +38371,7 @@ x86_64-linux-gnux32
|
||||
28
|
||||
28
|
||||
28
|
||||
28
|
||||
|
||||
28
|
||||
28
|
||||
@ -38303,6 +38401,7 @@ x86_64-linux-gnux32
|
||||
28
|
||||
28
|
||||
28
|
||||
28
|
||||
|
||||
28
|
||||
28
|
||||
@ -41190,6 +41289,7 @@ i386-linux-gnu
|
||||
|
||||
|
||||
0
|
||||
12
|
||||
27
|
||||
36
|
||||
27
|
||||
@ -41509,6 +41609,7 @@ i386-linux-gnu
|
||||
0
|
||||
0
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
||||
3 11
|
||||
@ -41694,6 +41795,7 @@ i386-linux-gnu
|
||||
19
|
||||
19
|
||||
0
|
||||
|
||||
0
|
||||
1
|
||||
28
|
||||
@ -41813,6 +41915,7 @@ i386-linux-gnu
|
||||
27
|
||||
|
||||
16
|
||||
|
||||
5
|
||||
0
|
||||
15
|
||||
@ -41901,6 +42004,9 @@ i386-linux-gnu
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
|
||||
|
||||
1
|
||||
1
|
||||
1
|
||||
@ -41999,6 +42105,7 @@ i386-linux-gnu
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
||||
|
||||
@ -42023,6 +42130,7 @@ i386-linux-gnu
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
@ -44915,6 +45023,7 @@ powerpc64le-linux-gnu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
29
|
||||
@ -45238,6 +45347,7 @@ powerpc64le-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
36
|
||||
@ -45421,6 +45531,7 @@ powerpc64le-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
33
|
||||
29
|
||||
29
|
||||
29
|
||||
@ -45540,6 +45651,7 @@ powerpc64le-linux-gnu
|
||||
29
|
||||
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
29
|
||||
@ -45628,6 +45740,9 @@ powerpc64le-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
32
|
||||
|
||||
29
|
||||
29
|
||||
29
|
||||
@ -45728,6 +45843,7 @@ powerpc64le-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
@ -45757,6 +45873,7 @@ powerpc64le-linux-gnu
|
||||
29
|
||||
29
|
||||
29
|
||||
29
|
||||
|
||||
29
|
||||
29
|
||||
@ -48642,6 +48759,7 @@ powerpc64-linux-gnu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
|
||||
@ -48965,6 +49083,7 @@ powerpc64-linux-gnu
|
||||
12
|
||||
12
|
||||
12
|
||||
12
|
||||
|
||||
27
|
||||
|
||||
@ -49148,6 +49267,7 @@ powerpc64-linux-gnu
|
||||
19
|
||||
19
|
||||
12
|
||||
33
|
||||
12
|
||||
12
|
||||
28
|
||||
@ -49267,6 +49387,7 @@ powerpc64-linux-gnu
|
||||
27
|
||||
|
||||
16
|
||||
|
||||
12
|
||||
12
|
||||
15
|
||||
@ -49355,6 +49476,9 @@ powerpc64-linux-gnu
|
||||
16
|
||||
12
|
||||
12
|
||||
12
|
||||
32
|
||||
|
||||
12
|
||||
12
|
||||
12
|
||||
@ -49455,6 +49579,7 @@ powerpc64-linux-gnu
|
||||
12
|
||||
12
|
||||
12
|
||||
12
|
||||
|
||||
12
|
||||
12
|
||||
@ -49480,6 +49605,7 @@ powerpc64-linux-gnu
|
||||
12
|
||||
12
|
||||
12
|
||||
12
|
||||
12 15
|
||||
24 12 16
|
||||
24 12 16
|
||||
@ -52369,6 +52495,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
|
||||
@ -52690,6 +52817,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
0
|
||||
0
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
||||
3 11
|
||||
@ -52875,6 +53003,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
19
|
||||
19
|
||||
0
|
||||
33
|
||||
0
|
||||
1
|
||||
28
|
||||
@ -52994,6 +53123,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
27
|
||||
13
|
||||
16
|
||||
|
||||
5
|
||||
0
|
||||
15
|
||||
@ -53082,6 +53212,9 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
16
|
||||
0
|
||||
0
|
||||
12
|
||||
32
|
||||
|
||||
1
|
||||
1
|
||||
1
|
||||
@ -53180,6 +53313,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
0
|
||||
0
|
||||
|
||||
@ -53204,6 +53338,7 @@ powerpc-linux-gnueabi powerpc-linux-gnueabihf
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
5
|
||||
0
|
||||
0
|
||||
|
||||
@ -192,6 +192,7 @@ _Qp_uitoq c
|
||||
_Qp_uxtoq c
|
||||
_Qp_xtoq c
|
||||
___brk_addr c
|
||||
___tls_get_addr ld
|
||||
__acos_finite m
|
||||
__acosf128_finite m
|
||||
__acosf_finite m
|
||||
@ -511,6 +512,7 @@ __libc_memalign c
|
||||
__libc_pvalloc c
|
||||
__libc_realloc c
|
||||
__libc_sa_len c
|
||||
__libc_stack_end ld
|
||||
__libc_start_main c
|
||||
__libc_valloc c
|
||||
__libpthread_version_placeholder pthread
|
||||
@ -696,6 +698,7 @@ __open_2 c
|
||||
__openat64_2 c
|
||||
__openat_2 c
|
||||
__overflow c
|
||||
__parse_hwcap_and_convert_at_platform ld
|
||||
__pipe c
|
||||
__poll c
|
||||
__poll_chk c
|
||||
@ -815,6 +818,7 @@ __sqrtf_finite m
|
||||
__sqrtl_finite m
|
||||
__sqrtsf2 c
|
||||
__stack_chk_fail c
|
||||
__stack_chk_guard ld
|
||||
__statfs c
|
||||
__stpcpy c
|
||||
__stpcpy_chk c
|
||||
@ -903,6 +907,9 @@ __sysctl c
|
||||
__syslog_chk c
|
||||
__sysv_signal c
|
||||
__timezone c
|
||||
__tls_get_addr ld
|
||||
__tls_get_addr_opt ld
|
||||
__tls_get_offset ld
|
||||
__toascii_l c
|
||||
__tolower_l c
|
||||
__toupper_l c
|
||||
@ -999,6 +1006,7 @@ __ynf128_finite m
|
||||
__ynf_finite m
|
||||
__ynl_finite m
|
||||
_authenticate c
|
||||
_dl_mcount ld
|
||||
_dl_mcount_wrapper c
|
||||
_dl_mcount_wrapper_check c
|
||||
_environ c
|
||||
@ -1024,6 +1032,7 @@ _pthread_cleanup_pop pthread
|
||||
_pthread_cleanup_pop_restore pthread
|
||||
_pthread_cleanup_push pthread
|
||||
_pthread_cleanup_push_defer pthread
|
||||
_r_debug ld
|
||||
_res c
|
||||
_res_hconf c
|
||||
_rpc_dtablesize c
|
||||
|
||||
@ -377,7 +377,7 @@ pub const Builder = struct {
|
||||
if (self.verbose) {
|
||||
warn("rm {}\n", .{full_path});
|
||||
}
|
||||
fs.deleteTree(full_path) catch {};
|
||||
fs.cwd().deleteTree(full_path) catch {};
|
||||
}
|
||||
|
||||
// TODO remove empty directories
|
||||
@ -847,7 +847,8 @@ pub const Builder = struct {
|
||||
if (self.verbose) {
|
||||
warn("cp {} {} ", .{ source_path, dest_path });
|
||||
}
|
||||
const prev_status = try fs.updateFile(source_path, dest_path);
|
||||
const cwd = fs.cwd();
|
||||
const prev_status = try fs.Dir.updateFile(cwd, source_path, cwd, dest_path, .{});
|
||||
if (self.verbose) switch (prev_status) {
|
||||
.stale => warn("# installed\n", .{}),
|
||||
.fresh => warn("# up-to-date\n", .{}),
|
||||
@ -1120,7 +1121,7 @@ pub const LibExeObjStep = struct {
|
||||
emit_llvm_ir: bool = false,
|
||||
emit_asm: bool = false,
|
||||
emit_bin: bool = true,
|
||||
disable_gen_h: bool,
|
||||
emit_h: bool = false,
|
||||
bundle_compiler_rt: bool,
|
||||
disable_stack_probing: bool,
|
||||
disable_sanitize_c: bool,
|
||||
@ -1157,8 +1158,14 @@ pub const LibExeObjStep = struct {
|
||||
|
||||
valgrind_support: ?bool = null,
|
||||
|
||||
/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF
|
||||
/// file.
|
||||
link_eh_frame_hdr: bool = false,
|
||||
|
||||
/// Place every function in its own section so that unused ones may be
|
||||
/// safely garbage-collected during the linking phase.
|
||||
link_function_sections: bool = false,
|
||||
|
||||
/// Uses system Wine installation to run cross compiled Windows build artifacts.
|
||||
enable_wine: bool = false,
|
||||
|
||||
@ -1274,7 +1281,6 @@ pub const LibExeObjStep = struct {
|
||||
.exec_cmd_args = null,
|
||||
.name_prefix = "",
|
||||
.filter = null,
|
||||
.disable_gen_h = false,
|
||||
.bundle_compiler_rt = false,
|
||||
.disable_stack_probing = false,
|
||||
.disable_sanitize_c = false,
|
||||
@ -1593,8 +1599,9 @@ pub const LibExeObjStep = struct {
|
||||
self.main_pkg_path = dir_path;
|
||||
}
|
||||
|
||||
pub fn setDisableGenH(self: *LibExeObjStep, value: bool) void {
|
||||
self.disable_gen_h = value;
|
||||
/// Deprecated; just set the field directly.
|
||||
pub fn setDisableGenH(self: *LibExeObjStep, is_disabled: bool) void {
|
||||
self.emit_h = !is_disabled;
|
||||
}
|
||||
|
||||
pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?[]const u8) void {
|
||||
@ -1625,7 +1632,7 @@ pub const LibExeObjStep = struct {
|
||||
/// the make step, from a step that has declared a dependency on this one.
|
||||
pub fn getOutputHPath(self: *LibExeObjStep) []const u8 {
|
||||
assert(self.kind != Kind.Exe);
|
||||
assert(!self.disable_gen_h);
|
||||
assert(self.emit_h);
|
||||
return fs.path.join(
|
||||
self.builder.allocator,
|
||||
&[_][]const u8{ self.output_dir.?, self.out_h_filename },
|
||||
@ -1672,7 +1679,7 @@ pub const LibExeObjStep = struct {
|
||||
}
|
||||
|
||||
pub fn addBuildOption(self: *LibExeObjStep, comptime T: type, name: []const u8, value: T) void {
|
||||
const out = &std.io.BufferOutStream.init(&self.build_options_contents).stream;
|
||||
const out = self.build_options_contents.outStream();
|
||||
out.print("pub const {} = {};\n", .{ name, value }) catch unreachable;
|
||||
}
|
||||
|
||||
@ -1877,6 +1884,7 @@ pub const LibExeObjStep = struct {
|
||||
if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir");
|
||||
if (self.emit_asm) try zig_args.append("-femit-asm");
|
||||
if (!self.emit_bin) try zig_args.append("-fno-emit-bin");
|
||||
if (self.emit_h) try zig_args.append("-femit-h");
|
||||
|
||||
if (self.strip) {
|
||||
try zig_args.append("--strip");
|
||||
@ -1884,7 +1892,9 @@ pub const LibExeObjStep = struct {
|
||||
if (self.link_eh_frame_hdr) {
|
||||
try zig_args.append("--eh-frame-hdr");
|
||||
}
|
||||
|
||||
if (self.link_function_sections) {
|
||||
try zig_args.append("-ffunction-sections");
|
||||
}
|
||||
if (self.single_threaded) {
|
||||
try zig_args.append("--single-threaded");
|
||||
}
|
||||
@ -1920,9 +1930,6 @@ pub const LibExeObjStep = struct {
|
||||
if (self.is_dynamic) {
|
||||
try zig_args.append("-dynamic");
|
||||
}
|
||||
if (self.disable_gen_h) {
|
||||
try zig_args.append("--disable-gen-h");
|
||||
}
|
||||
if (self.bundle_compiler_rt) {
|
||||
try zig_args.append("--bundle-compiler-rt");
|
||||
}
|
||||
@ -2060,7 +2067,7 @@ pub const LibExeObjStep = struct {
|
||||
try zig_args.append("-isystem");
|
||||
try zig_args.append(self.builder.pathFromRoot(include_path));
|
||||
},
|
||||
.OtherStep => |other| if (!other.disable_gen_h) {
|
||||
.OtherStep => |other| if (other.emit_h) {
|
||||
const h_path = other.getOutputHPath();
|
||||
try zig_args.append("-isystem");
|
||||
try zig_args.append(fs.path.dirname(h_path).?);
|
||||
@ -2144,17 +2151,22 @@ pub const LibExeObjStep = struct {
|
||||
try zig_args.append("--cache");
|
||||
try zig_args.append("on");
|
||||
|
||||
const output_path_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step);
|
||||
const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
|
||||
const output_dir_nl = try builder.execFromStep(zig_args.toSliceConst(), &self.step);
|
||||
const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n");
|
||||
|
||||
if (self.output_dir) |output_dir| {
|
||||
const full_dest = try fs.path.join(builder.allocator, &[_][]const u8{
|
||||
output_dir,
|
||||
fs.path.basename(output_path),
|
||||
});
|
||||
try builder.updateFile(output_path, full_dest);
|
||||
var src_dir = try std.fs.cwd().openDir(build_output_dir, .{ .iterate = true });
|
||||
defer src_dir.close();
|
||||
|
||||
var dest_dir = try std.fs.cwd().openDir(output_dir, .{});
|
||||
defer dest_dir.close();
|
||||
|
||||
var it = src_dir.iterate();
|
||||
while (try it.next()) |entry| {
|
||||
_ = try src_dir.updateFile(entry.name, dest_dir, entry.name, .{});
|
||||
}
|
||||
} else {
|
||||
self.output_dir = fs.path.dirname(output_path).?;
|
||||
self.output_dir = build_output_dir;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2195,7 +2207,7 @@ const InstallArtifactStep = struct {
|
||||
break :blk InstallDir.Lib;
|
||||
}
|
||||
} else null,
|
||||
.h_dir = if (artifact.kind == .Lib and !artifact.disable_gen_h) .Header else null,
|
||||
.h_dir = if (artifact.kind == .Lib and artifact.emit_h) .Header else null,
|
||||
};
|
||||
self.step.dependOn(&artifact.step);
|
||||
artifact.install_step = self;
|
||||
@ -2352,7 +2364,7 @@ pub const RemoveDirStep = struct {
|
||||
const self = @fieldParentPtr(RemoveDirStep, "step", step);
|
||||
|
||||
const full_path = self.builder.pathFromRoot(self.dir_path);
|
||||
fs.deleteTree(full_path) catch |err| {
|
||||
fs.cwd().deleteTree(full_path) catch |err| {
|
||||
warn("Unable to remove {}: {}\n", .{ full_path, @errorName(err) });
|
||||
return err;
|
||||
};
|
||||
|
||||
@ -29,6 +29,8 @@ pub const RunStep = struct {
|
||||
stdout_action: StdIoAction = .inherit,
|
||||
stderr_action: StdIoAction = .inherit,
|
||||
|
||||
stdin_behavior: std.ChildProcess.StdIo = .Inherit,
|
||||
|
||||
expected_exit_code: u8 = 0,
|
||||
|
||||
pub const StdIoAction = union(enum) {
|
||||
@ -159,7 +161,7 @@ pub const RunStep = struct {
|
||||
child.cwd = cwd;
|
||||
child.env_map = self.env_map orelse self.builder.env_map;
|
||||
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdin_behavior = self.stdin_behavior;
|
||||
child.stdout_behavior = stdIoActionToBehavior(self.stdout_action);
|
||||
child.stderr_behavior = stdIoActionToBehavior(self.stderr_action);
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@ pub const WriteFileStep = struct {
|
||||
warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) });
|
||||
return err;
|
||||
};
|
||||
var dir = try fs.cwd().openDirTraverse(self.output_dir);
|
||||
var dir = try fs.cwd().openDir(self.output_dir, .{});
|
||||
defer dir.close();
|
||||
for (self.files.toSliceConst()) |file| {
|
||||
dir.writeFile(file.basename, file.bytes) catch |err| {
|
||||
|
||||
@ -106,6 +106,7 @@ pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
|
||||
pub extern "c" fn mkdirat(dirfd: fd_t, path: [*:0]const u8, mode: u32) c_int;
|
||||
pub extern "c" fn symlink(existing: [*:0]const u8, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn rename(old: [*:0]const u8, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn renameat(olddirfd: fd_t, old: [*:0]const u8, newdirfd: fd_t, new: [*:0]const u8) c_int;
|
||||
pub extern "c" fn chdir(path: [*:0]const u8) c_int;
|
||||
pub extern "c" fn fchdir(fd: fd_t) c_int;
|
||||
pub extern "c" fn execve(path: [*:0]const u8, argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) c_int;
|
||||
|
||||
@ -15,10 +15,10 @@ fn rotw(w: u32) u32 {
|
||||
|
||||
// Encrypt one block from src into dst, using the expanded key xk.
|
||||
fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
|
||||
var s0 = mem.readIntSliceBig(u32, src[0..4]);
|
||||
var s1 = mem.readIntSliceBig(u32, src[4..8]);
|
||||
var s2 = mem.readIntSliceBig(u32, src[8..12]);
|
||||
var s3 = mem.readIntSliceBig(u32, src[12..16]);
|
||||
var s0 = mem.readIntBig(u32, src[0..4]);
|
||||
var s1 = mem.readIntBig(u32, src[4..8]);
|
||||
var s2 = mem.readIntBig(u32, src[8..12]);
|
||||
var s3 = mem.readIntBig(u32, src[12..16]);
|
||||
|
||||
// First round just XORs input with key.
|
||||
s0 ^= xk[0];
|
||||
@ -58,18 +58,18 @@ fn encryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
|
||||
s2 ^= xk[k + 2];
|
||||
s3 ^= xk[k + 3];
|
||||
|
||||
mem.writeIntSliceBig(u32, dst[0..4], s0);
|
||||
mem.writeIntSliceBig(u32, dst[4..8], s1);
|
||||
mem.writeIntSliceBig(u32, dst[8..12], s2);
|
||||
mem.writeIntSliceBig(u32, dst[12..16], s3);
|
||||
mem.writeIntBig(u32, dst[0..4], s0);
|
||||
mem.writeIntBig(u32, dst[4..8], s1);
|
||||
mem.writeIntBig(u32, dst[8..12], s2);
|
||||
mem.writeIntBig(u32, dst[12..16], s3);
|
||||
}
|
||||
|
||||
// Decrypt one block from src into dst, using the expanded key xk.
|
||||
pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
|
||||
var s0 = mem.readIntSliceBig(u32, src[0..4]);
|
||||
var s1 = mem.readIntSliceBig(u32, src[4..8]);
|
||||
var s2 = mem.readIntSliceBig(u32, src[8..12]);
|
||||
var s3 = mem.readIntSliceBig(u32, src[12..16]);
|
||||
var s0 = mem.readIntBig(u32, src[0..4]);
|
||||
var s1 = mem.readIntBig(u32, src[4..8]);
|
||||
var s2 = mem.readIntBig(u32, src[8..12]);
|
||||
var s3 = mem.readIntBig(u32, src[12..16]);
|
||||
|
||||
// First round just XORs input with key.
|
||||
s0 ^= xk[0];
|
||||
@ -109,10 +109,10 @@ pub fn decryptBlock(xk: []const u32, dst: []u8, src: []const u8) void {
|
||||
s2 ^= xk[k + 2];
|
||||
s3 ^= xk[k + 3];
|
||||
|
||||
mem.writeIntSliceBig(u32, dst[0..4], s0);
|
||||
mem.writeIntSliceBig(u32, dst[4..8], s1);
|
||||
mem.writeIntSliceBig(u32, dst[8..12], s2);
|
||||
mem.writeIntSliceBig(u32, dst[12..16], s3);
|
||||
mem.writeIntBig(u32, dst[0..4], s0);
|
||||
mem.writeIntBig(u32, dst[4..8], s1);
|
||||
mem.writeIntBig(u32, dst[8..12], s2);
|
||||
mem.writeIntBig(u32, dst[12..16], s3);
|
||||
}
|
||||
|
||||
fn xorBytes(dst: []u8, a: []const u8, b: []const u8) usize {
|
||||
@ -154,8 +154,8 @@ fn AES(comptime keysize: usize) type {
|
||||
var n: usize = 0;
|
||||
while (n < src.len) {
|
||||
ctx.encrypt(keystream[0..], ctrbuf[0..]);
|
||||
var ctr_i = std.mem.readIntSliceBig(u128, ctrbuf[0..]);
|
||||
std.mem.writeIntSliceBig(u128, ctrbuf[0..], ctr_i +% 1);
|
||||
var ctr_i = std.mem.readIntBig(u128, ctrbuf[0..]);
|
||||
std.mem.writeIntBig(u128, ctrbuf[0..], ctr_i +% 1);
|
||||
|
||||
n += xorBytes(dst[n..], src[n..], &keystream);
|
||||
}
|
||||
@ -251,7 +251,7 @@ fn expandKey(key: []const u8, enc: []u32, dec: []u32) void {
|
||||
var i: usize = 0;
|
||||
var nk = key.len / 4;
|
||||
while (i < nk) : (i += 1) {
|
||||
enc[i] = mem.readIntSliceBig(u32, key[4 * i .. 4 * i + 4]);
|
||||
enc[i] = mem.readIntBig(u32, key[4 * i ..][0..4]);
|
||||
}
|
||||
while (i < enc.len) : (i += 1) {
|
||||
var t = enc[i - 1];
|
||||
|
||||
@ -123,8 +123,7 @@ fn Blake2s(comptime out_len: usize) type {
|
||||
const rr = d.h[0 .. out_len / 32];
|
||||
|
||||
for (rr) |s, j| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s);
|
||||
mem.writeIntLittle(u32, out[4 * j ..][0..4], s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,8 +134,7 @@ fn Blake2s(comptime out_len: usize) type {
|
||||
var v: [16]u32 = undefined;
|
||||
|
||||
for (m) |*r, i| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
r.* = mem.readIntSliceLittle(u32, b[4 * i .. 4 * i + 4]);
|
||||
r.* = mem.readIntLittle(u32, b[4 * i ..][0..4]);
|
||||
}
|
||||
|
||||
var k: usize = 0;
|
||||
@ -358,8 +356,7 @@ fn Blake2b(comptime out_len: usize) type {
|
||||
const rr = d.h[0 .. out_len / 64];
|
||||
|
||||
for (rr) |s, j| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceLittle(u64, out[8 * j .. 8 * j + 8], s);
|
||||
mem.writeIntLittle(u64, out[8 * j ..][0..8], s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,7 +367,7 @@ fn Blake2b(comptime out_len: usize) type {
|
||||
var v: [16]u64 = undefined;
|
||||
|
||||
for (m) |*r, i| {
|
||||
r.* = mem.readIntSliceLittle(u64, b[8 * i .. 8 * i + 8]);
|
||||
r.* = mem.readIntLittle(u64, b[8 * i ..][0..8]);
|
||||
}
|
||||
|
||||
var k: usize = 0;
|
||||
|
||||
@ -61,8 +61,7 @@ fn salsa20_wordtobyte(out: []u8, input: [16]u32) void {
|
||||
}
|
||||
|
||||
for (x) |_, i| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceLittle(u32, out[4 * i .. 4 * i + 4], x[i] +% input[i]);
|
||||
mem.writeIntLittle(u32, out[4 * i ..][0..4], x[i] +% input[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,10 +72,10 @@ fn chaCha20_internal(out: []u8, in: []const u8, key: [8]u32, counter: [4]u32) vo
|
||||
|
||||
const c = "expand 32-byte k";
|
||||
const constant_le = [_]u32{
|
||||
mem.readIntSliceLittle(u32, c[0..4]),
|
||||
mem.readIntSliceLittle(u32, c[4..8]),
|
||||
mem.readIntSliceLittle(u32, c[8..12]),
|
||||
mem.readIntSliceLittle(u32, c[12..16]),
|
||||
mem.readIntLittle(u32, c[0..4]),
|
||||
mem.readIntLittle(u32, c[4..8]),
|
||||
mem.readIntLittle(u32, c[8..12]),
|
||||
mem.readIntLittle(u32, c[12..16]),
|
||||
};
|
||||
|
||||
mem.copy(u32, ctx[0..], constant_le[0..4]);
|
||||
@ -120,19 +119,19 @@ pub fn chaCha20IETF(out: []u8, in: []const u8, counter: u32, key: [32]u8, nonce:
|
||||
var k: [8]u32 = undefined;
|
||||
var c: [4]u32 = undefined;
|
||||
|
||||
k[0] = mem.readIntSliceLittle(u32, key[0..4]);
|
||||
k[1] = mem.readIntSliceLittle(u32, key[4..8]);
|
||||
k[2] = mem.readIntSliceLittle(u32, key[8..12]);
|
||||
k[3] = mem.readIntSliceLittle(u32, key[12..16]);
|
||||
k[4] = mem.readIntSliceLittle(u32, key[16..20]);
|
||||
k[5] = mem.readIntSliceLittle(u32, key[20..24]);
|
||||
k[6] = mem.readIntSliceLittle(u32, key[24..28]);
|
||||
k[7] = mem.readIntSliceLittle(u32, key[28..32]);
|
||||
k[0] = mem.readIntLittle(u32, key[0..4]);
|
||||
k[1] = mem.readIntLittle(u32, key[4..8]);
|
||||
k[2] = mem.readIntLittle(u32, key[8..12]);
|
||||
k[3] = mem.readIntLittle(u32, key[12..16]);
|
||||
k[4] = mem.readIntLittle(u32, key[16..20]);
|
||||
k[5] = mem.readIntLittle(u32, key[20..24]);
|
||||
k[6] = mem.readIntLittle(u32, key[24..28]);
|
||||
k[7] = mem.readIntLittle(u32, key[28..32]);
|
||||
|
||||
c[0] = counter;
|
||||
c[1] = mem.readIntSliceLittle(u32, nonce[0..4]);
|
||||
c[2] = mem.readIntSliceLittle(u32, nonce[4..8]);
|
||||
c[3] = mem.readIntSliceLittle(u32, nonce[8..12]);
|
||||
c[1] = mem.readIntLittle(u32, nonce[0..4]);
|
||||
c[2] = mem.readIntLittle(u32, nonce[4..8]);
|
||||
c[3] = mem.readIntLittle(u32, nonce[8..12]);
|
||||
chaCha20_internal(out, in, k, c);
|
||||
}
|
||||
|
||||
@ -147,19 +146,19 @@ pub fn chaCha20With64BitNonce(out: []u8, in: []const u8, counter: u64, key: [32]
|
||||
var k: [8]u32 = undefined;
|
||||
var c: [4]u32 = undefined;
|
||||
|
||||
k[0] = mem.readIntSliceLittle(u32, key[0..4]);
|
||||
k[1] = mem.readIntSliceLittle(u32, key[4..8]);
|
||||
k[2] = mem.readIntSliceLittle(u32, key[8..12]);
|
||||
k[3] = mem.readIntSliceLittle(u32, key[12..16]);
|
||||
k[4] = mem.readIntSliceLittle(u32, key[16..20]);
|
||||
k[5] = mem.readIntSliceLittle(u32, key[20..24]);
|
||||
k[6] = mem.readIntSliceLittle(u32, key[24..28]);
|
||||
k[7] = mem.readIntSliceLittle(u32, key[28..32]);
|
||||
k[0] = mem.readIntLittle(u32, key[0..4]);
|
||||
k[1] = mem.readIntLittle(u32, key[4..8]);
|
||||
k[2] = mem.readIntLittle(u32, key[8..12]);
|
||||
k[3] = mem.readIntLittle(u32, key[12..16]);
|
||||
k[4] = mem.readIntLittle(u32, key[16..20]);
|
||||
k[5] = mem.readIntLittle(u32, key[20..24]);
|
||||
k[6] = mem.readIntLittle(u32, key[24..28]);
|
||||
k[7] = mem.readIntLittle(u32, key[28..32]);
|
||||
|
||||
c[0] = @truncate(u32, counter);
|
||||
c[1] = @truncate(u32, counter >> 32);
|
||||
c[2] = mem.readIntSliceLittle(u32, nonce[0..4]);
|
||||
c[3] = mem.readIntSliceLittle(u32, nonce[4..8]);
|
||||
c[2] = mem.readIntLittle(u32, nonce[0..4]);
|
||||
c[3] = mem.readIntLittle(u32, nonce[4..8]);
|
||||
|
||||
const block_size = (1 << 6);
|
||||
// The full block size is greater than the address space on a 32bit machine
|
||||
@ -463,8 +462,8 @@ pub fn chacha20poly1305Seal(dst: []u8, plaintext: []const u8, data: []const u8,
|
||||
mac.update(zeros[0..padding]);
|
||||
}
|
||||
var lens: [16]u8 = undefined;
|
||||
mem.writeIntSliceLittle(u64, lens[0..8], data.len);
|
||||
mem.writeIntSliceLittle(u64, lens[8..16], plaintext.len);
|
||||
mem.writeIntLittle(u64, lens[0..8], data.len);
|
||||
mem.writeIntLittle(u64, lens[8..16], plaintext.len);
|
||||
mac.update(lens[0..]);
|
||||
mac.final(dst[plaintext.len..]);
|
||||
}
|
||||
@ -500,8 +499,8 @@ pub fn chacha20poly1305Open(dst: []u8, msgAndTag: []const u8, data: []const u8,
|
||||
mac.update(zeros[0..padding]);
|
||||
}
|
||||
var lens: [16]u8 = undefined;
|
||||
mem.writeIntSliceLittle(u64, lens[0..8], data.len);
|
||||
mem.writeIntSliceLittle(u64, lens[8..16], ciphertext.len);
|
||||
mem.writeIntLittle(u64, lens[0..8], data.len);
|
||||
mem.writeIntLittle(u64, lens[8..16], ciphertext.len);
|
||||
mac.update(lens[0..]);
|
||||
var computedTag: [16]u8 = undefined;
|
||||
mac.final(computedTag[0..]);
|
||||
|
||||
@ -112,8 +112,7 @@ pub const Md5 = struct {
|
||||
d.round(d.buf[0..]);
|
||||
|
||||
for (d.s) |s, j| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceLittle(u32, out[4 * j .. 4 * j + 4], s);
|
||||
mem.writeIntLittle(u32, out[4 * j ..][0..4], s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -3,11 +3,11 @@
|
||||
// https://monocypher.org/
|
||||
|
||||
const std = @import("../std.zig");
|
||||
const builtin = @import("builtin");
|
||||
const builtin = std.builtin;
|
||||
|
||||
const Endian = builtin.Endian;
|
||||
const readIntSliceLittle = std.mem.readIntSliceLittle;
|
||||
const writeIntSliceLittle = std.mem.writeIntSliceLittle;
|
||||
const readIntLittle = std.mem.readIntLittle;
|
||||
const writeIntLittle = std.mem.writeIntLittle;
|
||||
|
||||
pub const Poly1305 = struct {
|
||||
const Self = @This();
|
||||
@ -59,19 +59,19 @@ pub const Poly1305 = struct {
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 1) : (i += 1) {
|
||||
ctx.r[0] = readIntSliceLittle(u32, key[0..4]) & 0x0fffffff;
|
||||
ctx.r[0] = readIntLittle(u32, key[0..4]) & 0x0fffffff;
|
||||
}
|
||||
}
|
||||
{
|
||||
var i: usize = 1;
|
||||
while (i < 4) : (i += 1) {
|
||||
ctx.r[i] = readIntSliceLittle(u32, key[i * 4 .. i * 4 + 4]) & 0x0ffffffc;
|
||||
ctx.r[i] = readIntLittle(u32, key[i * 4 ..][0..4]) & 0x0ffffffc;
|
||||
}
|
||||
}
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < 4) : (i += 1) {
|
||||
ctx.pad[i] = readIntSliceLittle(u32, key[i * 4 + 16 .. i * 4 + 16 + 4]);
|
||||
ctx.pad[i] = readIntLittle(u32, key[i * 4 + 16 ..][0..4]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,10 +168,10 @@ pub const Poly1305 = struct {
|
||||
const nb_blocks = nmsg.len >> 4;
|
||||
var i: usize = 0;
|
||||
while (i < nb_blocks) : (i += 1) {
|
||||
ctx.c[0] = readIntSliceLittle(u32, nmsg[0..4]);
|
||||
ctx.c[1] = readIntSliceLittle(u32, nmsg[4..8]);
|
||||
ctx.c[2] = readIntSliceLittle(u32, nmsg[8..12]);
|
||||
ctx.c[3] = readIntSliceLittle(u32, nmsg[12..16]);
|
||||
ctx.c[0] = readIntLittle(u32, nmsg[0..4]);
|
||||
ctx.c[1] = readIntLittle(u32, nmsg[4..8]);
|
||||
ctx.c[2] = readIntLittle(u32, nmsg[8..12]);
|
||||
ctx.c[3] = readIntLittle(u32, nmsg[12..16]);
|
||||
polyBlock(ctx);
|
||||
nmsg = nmsg[16..];
|
||||
}
|
||||
@ -210,11 +210,10 @@ pub const Poly1305 = struct {
|
||||
const uu2 = (uu1 >> 32) + ctx.h[2] + ctx.pad[2]; // <= 2_00000000
|
||||
const uu3 = (uu2 >> 32) + ctx.h[3] + ctx.pad[3]; // <= 2_00000000
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
writeIntSliceLittle(u32, out[0..], @truncate(u32, uu0));
|
||||
writeIntSliceLittle(u32, out[4..], @truncate(u32, uu1));
|
||||
writeIntSliceLittle(u32, out[8..], @truncate(u32, uu2));
|
||||
writeIntSliceLittle(u32, out[12..], @truncate(u32, uu3));
|
||||
writeIntLittle(u32, out[0..4], @truncate(u32, uu0));
|
||||
writeIntLittle(u32, out[4..8], @truncate(u32, uu1));
|
||||
writeIntLittle(u32, out[8..12], @truncate(u32, uu2));
|
||||
writeIntLittle(u32, out[12..16], @truncate(u32, uu3));
|
||||
|
||||
ctx.secureZero();
|
||||
}
|
||||
|
||||
@ -109,8 +109,7 @@ pub const Sha1 = struct {
|
||||
d.round(d.buf[0..]);
|
||||
|
||||
for (d.s) |s, j| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s);
|
||||
mem.writeIntBig(u32, out[4 * j ..][0..4], s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -167,8 +167,7 @@ fn Sha2_32(comptime params: Sha2Params32) type {
|
||||
const rr = d.s[0 .. params.out_len / 32];
|
||||
|
||||
for (rr) |s, j| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceBig(u32, out[4 * j .. 4 * j + 4], s);
|
||||
mem.writeIntBig(u32, out[4 * j ..][0..4], s);
|
||||
}
|
||||
}
|
||||
|
||||
@ -509,8 +508,7 @@ fn Sha2_64(comptime params: Sha2Params64) type {
|
||||
const rr = d.s[0 .. params.out_len / 64];
|
||||
|
||||
for (rr) |s, j| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceBig(u64, out[8 * j .. 8 * j + 8], s);
|
||||
mem.writeIntBig(u64, out[8 * j ..][0..8], s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -120,7 +120,7 @@ fn keccak_f(comptime F: usize, d: []u8) void {
|
||||
var c = [_]u64{0} ** 5;
|
||||
|
||||
for (s) |*r, i| {
|
||||
r.* = mem.readIntSliceLittle(u64, d[8 * i .. 8 * i + 8]);
|
||||
r.* = mem.readIntLittle(u64, d[8 * i ..][0..8]);
|
||||
}
|
||||
|
||||
comptime var x: usize = 0;
|
||||
@ -167,8 +167,7 @@ fn keccak_f(comptime F: usize, d: []u8) void {
|
||||
}
|
||||
|
||||
for (s) |r, i| {
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntSliceLittle(u64, d[8 * i .. 8 * i + 8], r);
|
||||
mem.writeIntLittle(u64, d[8 * i ..][0..8], r);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,8 +7,8 @@ const builtin = @import("builtin");
|
||||
const fmt = std.fmt;
|
||||
|
||||
const Endian = builtin.Endian;
|
||||
const readIntSliceLittle = std.mem.readIntSliceLittle;
|
||||
const writeIntSliceLittle = std.mem.writeIntSliceLittle;
|
||||
const readIntLittle = std.mem.readIntLittle;
|
||||
const writeIntLittle = std.mem.writeIntLittle;
|
||||
|
||||
// Based on Supercop's ref10 implementation.
|
||||
pub const X25519 = struct {
|
||||
@ -255,16 +255,16 @@ const Fe = struct {
|
||||
|
||||
var t: [10]i64 = undefined;
|
||||
|
||||
t[0] = readIntSliceLittle(u32, s[0..4]);
|
||||
t[1] = @as(u32, readIntSliceLittle(u24, s[4..7])) << 6;
|
||||
t[2] = @as(u32, readIntSliceLittle(u24, s[7..10])) << 5;
|
||||
t[3] = @as(u32, readIntSliceLittle(u24, s[10..13])) << 3;
|
||||
t[4] = @as(u32, readIntSliceLittle(u24, s[13..16])) << 2;
|
||||
t[5] = readIntSliceLittle(u32, s[16..20]);
|
||||
t[6] = @as(u32, readIntSliceLittle(u24, s[20..23])) << 7;
|
||||
t[7] = @as(u32, readIntSliceLittle(u24, s[23..26])) << 5;
|
||||
t[8] = @as(u32, readIntSliceLittle(u24, s[26..29])) << 4;
|
||||
t[9] = (@as(u32, readIntSliceLittle(u24, s[29..32])) & 0x7fffff) << 2;
|
||||
t[0] = readIntLittle(u32, s[0..4]);
|
||||
t[1] = @as(u32, readIntLittle(u24, s[4..7])) << 6;
|
||||
t[2] = @as(u32, readIntLittle(u24, s[7..10])) << 5;
|
||||
t[3] = @as(u32, readIntLittle(u24, s[10..13])) << 3;
|
||||
t[4] = @as(u32, readIntLittle(u24, s[13..16])) << 2;
|
||||
t[5] = readIntLittle(u32, s[16..20]);
|
||||
t[6] = @as(u32, readIntLittle(u24, s[20..23])) << 7;
|
||||
t[7] = @as(u32, readIntLittle(u24, s[23..26])) << 5;
|
||||
t[8] = @as(u32, readIntLittle(u24, s[26..29])) << 4;
|
||||
t[9] = (@as(u32, readIntLittle(u24, s[29..32])) & 0x7fffff) << 2;
|
||||
|
||||
carry1(h, t[0..]);
|
||||
}
|
||||
@ -544,15 +544,14 @@ const Fe = struct {
|
||||
ut[i] = @bitCast(u32, @intCast(i32, t[i]));
|
||||
}
|
||||
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
writeIntSliceLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26));
|
||||
writeIntSliceLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19));
|
||||
writeIntSliceLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13));
|
||||
writeIntSliceLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6));
|
||||
writeIntSliceLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25));
|
||||
writeIntSliceLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19));
|
||||
writeIntSliceLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12));
|
||||
writeIntSliceLittle(u32, s[28..], (ut[8] >> 20) | (ut[9] << 6));
|
||||
writeIntLittle(u32, s[0..4], (ut[0] >> 0) | (ut[1] << 26));
|
||||
writeIntLittle(u32, s[4..8], (ut[1] >> 6) | (ut[2] << 19));
|
||||
writeIntLittle(u32, s[8..12], (ut[2] >> 13) | (ut[3] << 13));
|
||||
writeIntLittle(u32, s[12..16], (ut[3] >> 19) | (ut[4] << 6));
|
||||
writeIntLittle(u32, s[16..20], (ut[5] >> 0) | (ut[6] << 25));
|
||||
writeIntLittle(u32, s[20..24], (ut[6] >> 7) | (ut[7] << 19));
|
||||
writeIntLittle(u32, s[24..28], (ut[7] >> 13) | (ut[8] << 12));
|
||||
writeIntLittle(u32, s[28..32], (ut[8] >> 20) | (ut[9] << 6));
|
||||
|
||||
std.mem.secureZero(i64, t[0..]);
|
||||
}
|
||||
|
||||
@ -235,9 +235,17 @@ pub fn panic(comptime format: []const u8, args: var) noreturn {
|
||||
panicExtra(null, first_trace_addr, format, args);
|
||||
}
|
||||
|
||||
/// TODO multithreaded awareness
|
||||
/// Non-zero whenever the program triggered a panic.
|
||||
/// The counter is incremented/decremented atomically.
|
||||
var panicking: u8 = 0;
|
||||
|
||||
// Locked to avoid interleaving panic messages from multiple threads.
|
||||
var panic_mutex = std.Mutex.init();
|
||||
|
||||
/// Counts how many times the panic handler is invoked by this thread.
|
||||
/// This is used to catch and handle panics triggered by the panic handler.
|
||||
threadlocal var panic_stage: usize = 0;
|
||||
|
||||
pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {
|
||||
@setCold(true);
|
||||
|
||||
@ -247,25 +255,50 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
|
||||
resetSegfaultHandler();
|
||||
}
|
||||
|
||||
switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) {
|
||||
switch (panic_stage) {
|
||||
0 => {
|
||||
const stderr = getStderrStream();
|
||||
noasync stderr.print(format ++ "\n", args) catch os.abort();
|
||||
if (trace) |t| {
|
||||
dumpStackTrace(t.*);
|
||||
panic_stage = 1;
|
||||
|
||||
_ = @atomicRmw(u8, &panicking, .Add, 1, .SeqCst);
|
||||
|
||||
// Make sure to release the mutex when done
|
||||
{
|
||||
const held = panic_mutex.acquire();
|
||||
defer held.release();
|
||||
|
||||
const stderr = getStderrStream();
|
||||
noasync stderr.print(format ++ "\n", args) catch os.abort();
|
||||
if (trace) |t| {
|
||||
dumpStackTrace(t.*);
|
||||
}
|
||||
dumpCurrentStackTrace(first_trace_addr);
|
||||
}
|
||||
|
||||
if (@atomicRmw(u8, &panicking, .Sub, 1, .SeqCst) != 1) {
|
||||
// Another thread is panicking, wait for the last one to finish
|
||||
// and call abort()
|
||||
|
||||
// Sleep forever without hammering the CPU
|
||||
var event = std.ResetEvent.init();
|
||||
event.wait();
|
||||
|
||||
unreachable;
|
||||
}
|
||||
dumpCurrentStackTrace(first_trace_addr);
|
||||
},
|
||||
1 => {
|
||||
// TODO detect if a different thread caused the panic, because in that case
|
||||
// we would want to return here instead of calling abort, so that the thread
|
||||
// which first called panic can finish printing a stack trace.
|
||||
warn("Panicked during a panic. Aborting.\n", .{});
|
||||
panic_stage = 2;
|
||||
|
||||
// A panic happened while trying to print a previous panic message,
|
||||
// we're still holding the mutex but that's fine as we're going to
|
||||
// call abort()
|
||||
const stderr = getStderrStream();
|
||||
noasync stderr.print("Panicked during a panic. Aborting.\n", .{}) catch os.abort();
|
||||
},
|
||||
else => {
|
||||
// Panicked while printing "Panicked during a panic."
|
||||
},
|
||||
}
|
||||
|
||||
os.abort();
|
||||
}
|
||||
|
||||
|
||||
@ -717,8 +717,7 @@ pub const DwarfInfo = struct {
|
||||
const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
|
||||
|
||||
const version = try in.readInt(u16, di.endian);
|
||||
// TODO support 3 and 5
|
||||
if (version != 2 and version != 4) return error.InvalidDebugInfo;
|
||||
if (version < 2 or version > 4) return error.InvalidDebugInfo;
|
||||
|
||||
const prologue_length = if (is_64) try in.readInt(u64, di.endian) else try in.readInt(u32, di.endian);
|
||||
const prog_start_offset = (try seekable.getPos()) + prologue_length;
|
||||
|
||||
@ -1223,7 +1223,8 @@ test "slice" {
|
||||
try testFmt("slice: abc\n", "slice: {}\n", .{value});
|
||||
}
|
||||
{
|
||||
const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[0..0];
|
||||
var runtime_zero: usize = 0;
|
||||
const value = @intToPtr([*]align(1) const []const u8, 0xdeadbeef)[runtime_zero..runtime_zero];
|
||||
try testFmt("slice: []const u8@deadbeef\n", "slice: {}\n", .{value});
|
||||
}
|
||||
|
||||
|
||||
546
lib/std/fs.zig
546
lib/std/fs.zig
@ -81,134 +81,74 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
|
||||
}
|
||||
}
|
||||
|
||||
// TODO fix enum literal not casting to error union
|
||||
const PrevStatus = enum {
|
||||
pub const PrevStatus = enum {
|
||||
stale,
|
||||
fresh,
|
||||
};
|
||||
|
||||
pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus {
|
||||
return updateFileMode(source_path, dest_path, null);
|
||||
}
|
||||
pub const CopyFileOptions = struct {
|
||||
/// When this is `null` the mode is copied from the source file.
|
||||
override_mode: ?File.Mode = null,
|
||||
};
|
||||
|
||||
/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing.
|
||||
/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime,
|
||||
/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
|
||||
/// Returns the previous status of the file before updating.
|
||||
/// If any of the directories do not exist for dest_path, they are created.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus {
|
||||
/// Same as `Dir.updateFile`, except asserts that both `source_path` and `dest_path`
|
||||
/// are absolute. See `Dir.updateFile` for a function that operates on both
|
||||
/// absolute and relative paths.
|
||||
pub fn updateFileAbsolute(
|
||||
source_path: []const u8,
|
||||
dest_path: []const u8,
|
||||
args: CopyFileOptions,
|
||||
) !PrevStatus {
|
||||
assert(path.isAbsolute(source_path));
|
||||
assert(path.isAbsolute(dest_path));
|
||||
const my_cwd = cwd();
|
||||
|
||||
var src_file = try my_cwd.openFile(source_path, .{});
|
||||
defer src_file.close();
|
||||
|
||||
const src_stat = try src_file.stat();
|
||||
check_dest_stat: {
|
||||
const dest_stat = blk: {
|
||||
var dest_file = my_cwd.openFile(dest_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => break :check_dest_stat,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer dest_file.close();
|
||||
|
||||
break :blk try dest_file.stat();
|
||||
};
|
||||
|
||||
if (src_stat.size == dest_stat.size and
|
||||
src_stat.mtime == dest_stat.mtime and
|
||||
src_stat.mode == dest_stat.mode)
|
||||
{
|
||||
return PrevStatus.fresh;
|
||||
}
|
||||
}
|
||||
const actual_mode = mode orelse src_stat.mode;
|
||||
|
||||
if (path.dirname(dest_path)) |dirname| {
|
||||
try cwd().makePath(dirname);
|
||||
}
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, actual_mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });
|
||||
try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
|
||||
try atomic_file.finish();
|
||||
return PrevStatus.stale;
|
||||
return Dir.updateFile(my_cwd, source_path, my_cwd, dest_path, args);
|
||||
}
|
||||
|
||||
/// Guaranteed to be atomic.
|
||||
/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// Destination file will have the same mode as the source file.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn copyFile(source_path: []const u8, dest_path: []const u8) !void {
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
const stat = try in_file.stat();
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, stat.mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
try atomic_file.file.writeFileAll(in_file, .{ .in_len = stat.size });
|
||||
return atomic_file.finish();
|
||||
/// Same as `Dir.copyFile`, except asserts that both `source_path` and `dest_path`
|
||||
/// are absolute. See `Dir.copyFile` for a function that operates on both
|
||||
/// absolute and relative paths.
|
||||
pub fn copyFileAbsolute(source_path: []const u8, dest_path: []const u8, args: CopyFileOptions) !void {
|
||||
assert(path.isAbsolute(source_path));
|
||||
assert(path.isAbsolute(dest_path));
|
||||
const my_cwd = cwd();
|
||||
return Dir.copyFile(my_cwd, source_path, my_cwd, dest_path, args);
|
||||
}
|
||||
|
||||
/// Guaranteed to be atomic.
|
||||
/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
/// TODO rework this to integrate with Dir
|
||||
pub fn copyFileMode(source_path: []const u8, dest_path: []const u8, mode: File.Mode) !void {
|
||||
var in_file = try cwd().openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
var atomic_file = try AtomicFile.init(dest_path, mode);
|
||||
defer atomic_file.deinit();
|
||||
|
||||
try atomic_file.file.writeFileAll(in_file, .{});
|
||||
return atomic_file.finish();
|
||||
}
|
||||
|
||||
/// TODO update this API to avoid a getrandom syscall for every operation. It
|
||||
/// should accept a random interface.
|
||||
/// TODO rework this to integrate with Dir
|
||||
/// TODO update this API to avoid a getrandom syscall for every operation.
|
||||
pub const AtomicFile = struct {
|
||||
file: File,
|
||||
tmp_path_buf: [MAX_PATH_BYTES]u8,
|
||||
tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8,
|
||||
dest_path: []const u8,
|
||||
finished: bool,
|
||||
file_open: bool,
|
||||
file_exists: bool,
|
||||
dir: Dir,
|
||||
|
||||
const InitError = File.OpenError;
|
||||
|
||||
/// dest_path must remain valid for the lifetime of AtomicFile
|
||||
/// call finish to atomically replace dest_path with contents
|
||||
pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
|
||||
/// TODO rename this. Callers should go through Dir API
|
||||
pub fn init2(dest_path: []const u8, mode: File.Mode, dir: Dir) InitError!AtomicFile {
|
||||
const dirname = path.dirname(dest_path);
|
||||
var rand_buf: [12]u8 = undefined;
|
||||
const dirname_component_len = if (dirname) |d| d.len + 1 else 0;
|
||||
const encoded_rand_len = comptime base64.Base64Encoder.calcSize(rand_buf.len);
|
||||
const tmp_path_len = dirname_component_len + encoded_rand_len;
|
||||
var tmp_path_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
if (tmp_path_len >= tmp_path_buf.len) return error.NameTooLong;
|
||||
var tmp_path_buf: [MAX_PATH_BYTES - 1:0]u8 = undefined;
|
||||
if (tmp_path_len > tmp_path_buf.len) return error.NameTooLong;
|
||||
|
||||
if (dirname) |dir| {
|
||||
mem.copy(u8, tmp_path_buf[0..], dir);
|
||||
tmp_path_buf[dir.len] = path.sep;
|
||||
if (dirname) |dn| {
|
||||
mem.copy(u8, tmp_path_buf[0..], dn);
|
||||
tmp_path_buf[dn.len] = path.sep;
|
||||
}
|
||||
|
||||
tmp_path_buf[tmp_path_len] = 0;
|
||||
const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0];
|
||||
|
||||
const my_cwd = cwd();
|
||||
|
||||
while (true) {
|
||||
try crypto.randomBytes(rand_buf[0..]);
|
||||
base64_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf);
|
||||
|
||||
const file = my_cwd.createFileC(
|
||||
const file = dir.createFileC(
|
||||
tmp_path_slice,
|
||||
.{ .mode = mode, .exclusive = true },
|
||||
) catch |err| switch (err) {
|
||||
@ -220,33 +160,46 @@ pub const AtomicFile = struct {
|
||||
.file = file,
|
||||
.tmp_path_buf = tmp_path_buf,
|
||||
.dest_path = dest_path,
|
||||
.finished = false,
|
||||
.file_open = true,
|
||||
.file_exists = true,
|
||||
.dir = dir,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Deprecated. Use `Dir.atomicFile`.
|
||||
pub fn init(dest_path: []const u8, mode: File.Mode) InitError!AtomicFile {
|
||||
return init2(dest_path, mode, cwd());
|
||||
}
|
||||
|
||||
/// always call deinit, even after successful finish()
|
||||
pub fn deinit(self: *AtomicFile) void {
|
||||
if (!self.finished) {
|
||||
if (self.file_open) {
|
||||
self.file.close();
|
||||
cwd().deleteFileC(@ptrCast([*:0]u8, &self.tmp_path_buf)) catch {};
|
||||
self.finished = true;
|
||||
self.file_open = false;
|
||||
}
|
||||
if (self.file_exists) {
|
||||
self.dir.deleteFileC(&self.tmp_path_buf) catch {};
|
||||
self.file_exists = false;
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
pub fn finish(self: *AtomicFile) !void {
|
||||
assert(!self.finished);
|
||||
assert(self.file_exists);
|
||||
if (self.file_open) {
|
||||
self.file.close();
|
||||
self.file_open = false;
|
||||
}
|
||||
if (std.Target.current.os.tag == .windows) {
|
||||
const dest_path_w = try os.windows.sliceToPrefixedFileW(self.dest_path);
|
||||
const tmp_path_w = try os.windows.cStrToPrefixedFileW(@ptrCast([*:0]u8, &self.tmp_path_buf));
|
||||
self.file.close();
|
||||
self.finished = true;
|
||||
return os.renameW(&tmp_path_w, &dest_path_w);
|
||||
const tmp_path_w = try os.windows.cStrToPrefixedFileW(&self.tmp_path_buf);
|
||||
try os.renameatW(self.dir.fd, &tmp_path_w, self.dir.fd, &dest_path_w, os.windows.TRUE);
|
||||
self.file_exists = false;
|
||||
} else {
|
||||
const dest_path_c = try os.toPosixPath(self.dest_path);
|
||||
self.file.close();
|
||||
self.finished = true;
|
||||
return os.renameC(@ptrCast([*:0]u8, &self.tmp_path_buf), &dest_path_c);
|
||||
try os.renameatZ(self.dir.fd, &self.tmp_path_buf, self.dir.fd, &dest_path_c);
|
||||
self.file_exists = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -274,44 +227,21 @@ pub fn makeDirAbsoluteW(absolute_path_w: [*:0]const u16) !void {
|
||||
os.windows.CloseHandle(handle);
|
||||
}
|
||||
|
||||
/// Returns `error.DirNotEmpty` if the directory is not empty.
|
||||
/// To delete a directory recursively, see `deleteTree`.
|
||||
/// Deprecated; use `Dir.deleteDir`.
|
||||
pub fn deleteDir(dir_path: []const u8) !void {
|
||||
return os.rmdir(dir_path);
|
||||
}
|
||||
|
||||
/// Same as `deleteDir` except the parameter is a null-terminated UTF8-encoded string.
|
||||
/// Deprecated; use `Dir.deleteDirC`.
|
||||
pub fn deleteDirC(dir_path: [*:0]const u8) !void {
|
||||
return os.rmdirC(dir_path);
|
||||
}
|
||||
|
||||
/// Same as `deleteDir` except the parameter is a null-terminated UTF16LE-encoded string.
|
||||
/// Deprecated; use `Dir.deleteDirW`.
|
||||
pub fn deleteDirW(dir_path: [*:0]const u16) !void {
|
||||
return os.rmdirW(dir_path);
|
||||
}
|
||||
|
||||
/// Removes a symlink, file, or directory.
|
||||
/// If `full_path` is relative, this is equivalent to `Dir.deleteTree` with the
|
||||
/// current working directory as the open directory handle.
|
||||
/// If `full_path` is absolute, this is equivalent to `Dir.deleteTree` with the
|
||||
/// base directory.
|
||||
pub fn deleteTree(full_path: []const u8) !void {
|
||||
if (path.isAbsolute(full_path)) {
|
||||
const dirname = path.dirname(full_path) orelse return error{
|
||||
/// Attempt to remove the root file system path.
|
||||
/// This error is unreachable if `full_path` is relative.
|
||||
CannotDeleteRootDirectory,
|
||||
}.CannotDeleteRootDirectory;
|
||||
|
||||
var dir = try cwd().openDirList(dirname);
|
||||
defer dir.close();
|
||||
|
||||
return dir.deleteTree(path.basename(full_path));
|
||||
} else {
|
||||
return cwd().deleteTree(full_path);
|
||||
}
|
||||
}
|
||||
|
||||
pub const Dir = struct {
|
||||
fd: os.fd_t,
|
||||
|
||||
@ -368,7 +298,7 @@ pub const Dir = struct {
|
||||
if (rc == 0) return null;
|
||||
if (rc < 0) {
|
||||
switch (os.errno(rc)) {
|
||||
os.EBADF => unreachable,
|
||||
os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
|
||||
os.EFAULT => unreachable,
|
||||
os.ENOTDIR => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
@ -411,13 +341,13 @@ pub const Dir = struct {
|
||||
if (self.index >= self.end_index) {
|
||||
const rc = os.system.getdirentries(
|
||||
self.dir.fd,
|
||||
self.buf[0..].ptr,
|
||||
&self.buf,
|
||||
self.buf.len,
|
||||
&self.seek,
|
||||
);
|
||||
switch (os.errno(rc)) {
|
||||
0 => {},
|
||||
os.EBADF => unreachable,
|
||||
os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
|
||||
os.EFAULT => unreachable,
|
||||
os.ENOTDIR => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
@ -473,7 +403,7 @@ pub const Dir = struct {
|
||||
const rc = os.linux.getdents64(self.dir.fd, &self.buf, self.buf.len);
|
||||
switch (os.linux.getErrno(rc)) {
|
||||
0 => {},
|
||||
os.EBADF => unreachable,
|
||||
os.EBADF => unreachable, // Dir is invalid or was opened without iteration ability
|
||||
os.EFAULT => unreachable,
|
||||
os.ENOTDIR => unreachable,
|
||||
os.EINVAL => unreachable,
|
||||
@ -547,7 +477,8 @@ pub const Dir = struct {
|
||||
self.end_index = io.Information;
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.ACCESS_DENIED => return error.AccessDenied, // Double-check that the Dir was opened with iteration ability
|
||||
|
||||
else => return w.unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
@ -625,16 +556,6 @@ pub const Dir = struct {
|
||||
DeviceBusy,
|
||||
} || os.UnexpectedError;
|
||||
|
||||
/// Deprecated; call `cwd().openDirList` directly.
|
||||
pub fn open(dir_path: []const u8) OpenError!Dir {
|
||||
return cwd().openDirList(dir_path);
|
||||
}
|
||||
|
||||
/// Deprecated; call `cwd().openDirListC` directly.
|
||||
pub fn openC(dir_path_c: [*:0]const u8) OpenError!Dir {
|
||||
return cwd().openDirListC(dir_path_c);
|
||||
}
|
||||
|
||||
pub fn close(self: *Dir) void {
|
||||
if (need_async_thread) {
|
||||
std.event.Loop.instance.?.close(self.fd);
|
||||
@ -696,7 +617,7 @@ pub const Dir = struct {
|
||||
var flock = mem.zeroes(os.Flock);
|
||||
flock.l_type = if (flags.write) os.F_WRLCK else os.F_RDLCK;
|
||||
flock.l_whence = os.SEEK_SET;
|
||||
try os.fcntl(fd, os.F_SETLKW, &flock);
|
||||
_ = try os.fcntl(fd, os.F_SETLKW, @ptrToInt(&flock));
|
||||
}
|
||||
|
||||
return File{
|
||||
@ -721,7 +642,10 @@ pub const Dir = struct {
|
||||
(if (flags.write) @as(os.windows.ULONG, 0) else w.FILE_SHARE_READ)
|
||||
else
|
||||
null;
|
||||
return self.openFileWindows(sub_path_w, access_mask, share_access, w.FILE_OPEN);
|
||||
return @as(File, .{
|
||||
.handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, w.FILE_OPEN),
|
||||
.io_mode = .blocking,
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates, opens, or overwrites a file with write access.
|
||||
@ -765,7 +689,7 @@ pub const Dir = struct {
|
||||
var flock = mem.zeroes(os.Flock);
|
||||
flock.l_type = os.F_WRLCK;
|
||||
flock.l_whence = os.SEEK_SET;
|
||||
try os.fcntl(fd, os.F_SETLKW, &flock);
|
||||
_ = try os.fcntl(fd, os.F_SETLKW, @ptrToInt(&flock));
|
||||
}
|
||||
|
||||
return File{ .handle = fd, .io_mode = .blocking };
|
||||
@ -788,7 +712,10 @@ pub const Dir = struct {
|
||||
@as(os.windows.ULONG, w.FILE_SHARE_DELETE)
|
||||
else
|
||||
null;
|
||||
return self.openFileWindows(sub_path_w, access_mask, share_access, creation);
|
||||
return @as(File, .{
|
||||
.handle = try os.windows.OpenFileW(self.fd, sub_path_w, null, access_mask, share_access, creation),
|
||||
.io_mode = .blocking,
|
||||
});
|
||||
}
|
||||
|
||||
/// Deprecated; call `openFile` directly.
|
||||
@ -806,87 +733,6 @@ pub const Dir = struct {
|
||||
return self.openFileW(sub_path, .{});
|
||||
}
|
||||
|
||||
pub fn openFileWindows(
|
||||
self: Dir,
|
||||
sub_path_w: [*:0]const u16,
|
||||
access_mask: os.windows.ACCESS_MASK,
|
||||
share_access_opt: ?os.windows.ULONG,
|
||||
creation: os.windows.ULONG,
|
||||
) File.OpenError!File {
|
||||
var delay: usize = 1;
|
||||
while (true) {
|
||||
const w = os.windows;
|
||||
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
return error.IsDir;
|
||||
}
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
||||
return error.IsDir;
|
||||
}
|
||||
|
||||
var result = File{
|
||||
.handle = undefined,
|
||||
.io_mode = .blocking,
|
||||
};
|
||||
|
||||
const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) {
|
||||
error.Overflow => return error.NameTooLong,
|
||||
};
|
||||
var nt_name = w.UNICODE_STRING{
|
||||
.Length = path_len_bytes,
|
||||
.MaximumLength = path_len_bytes,
|
||||
.Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)),
|
||||
};
|
||||
var attr = w.OBJECT_ATTRIBUTES{
|
||||
.Length = @sizeOf(w.OBJECT_ATTRIBUTES),
|
||||
.RootDirectory = if (path.isAbsoluteWindowsW(sub_path_w)) null else self.fd,
|
||||
.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;
|
||||
const share_access = share_access_opt orelse w.FILE_SHARE_WRITE | w.FILE_SHARE_READ | w.FILE_SHARE_DELETE;
|
||||
const rc = w.ntdll.NtCreateFile(
|
||||
&result.handle,
|
||||
access_mask,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
w.FILE_ATTRIBUTE_NORMAL,
|
||||
share_access,
|
||||
creation,
|
||||
w.FILE_NON_DIRECTORY_FILE | w.FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => return result,
|
||||
.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,
|
||||
.SHARING_VIOLATION => {
|
||||
// TODO: check if async or blocking
|
||||
//return error.SharingViolation
|
||||
// Sleep so we don't consume a ton of CPU waiting to get lock on file
|
||||
std.time.sleep(delay);
|
||||
// Increase sleep time as long as it is less than 5 seconds
|
||||
if (delay < 5 * std.time.ns_per_s) {
|
||||
delay *= 2;
|
||||
}
|
||||
continue;
|
||||
},
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.PIPE_BUSY => return error.PipeBusy,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return w.unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn makeDir(self: Dir, sub_path: []const u8) !void {
|
||||
try os.mkdirat(self.fd, sub_path, default_new_dir_mode);
|
||||
}
|
||||
@ -945,77 +791,61 @@ pub const Dir = struct {
|
||||
try os.fchdir(self.fd);
|
||||
}
|
||||
|
||||
/// Deprecated; call `openDirList` directly.
|
||||
pub fn openDir(self: Dir, sub_path: []const u8) OpenError!Dir {
|
||||
return self.openDirList(sub_path);
|
||||
}
|
||||
pub const OpenDirOptions = struct {
|
||||
/// `true` means the opened directory can be used as the `Dir` parameter
|
||||
/// for functions which operate based on an open directory handle. When `false`,
|
||||
/// such operations are Illegal Behavior.
|
||||
access_sub_paths: bool = true,
|
||||
|
||||
/// Deprecated; call `openDirListC` directly.
|
||||
pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
|
||||
return self.openDirListC(sub_path_c);
|
||||
}
|
||||
/// `true` means the opened directory can be scanned for the files and sub-directories
|
||||
/// of the result. It means the `iterate` function can be called.
|
||||
iterate: bool = false,
|
||||
};
|
||||
|
||||
/// Opens a directory at the given path with the ability to access subpaths
|
||||
/// of the result. Calling `iterate` on the result is illegal behavior; to
|
||||
/// list the contents of a directory, open it with `openDirList`.
|
||||
///
|
||||
/// Call `close` on the result when done.
|
||||
/// Opens a directory at the given path. The directory is a system resource that remains
|
||||
/// open until `close` is called on the result.
|
||||
///
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn openDirTraverse(self: Dir, sub_path: []const u8) OpenError!Dir {
|
||||
pub fn openDir(self: Dir, sub_path: []const u8, args: OpenDirOptions) OpenError!Dir {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.openDirTraverseW(&sub_path_w);
|
||||
return self.openDirW(&sub_path_w, args);
|
||||
} else {
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.openDirC(&sub_path_c, args);
|
||||
}
|
||||
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.openDirTraverseC(&sub_path_c);
|
||||
}
|
||||
|
||||
/// Opens a directory at the given path with the ability to access subpaths and list contents
|
||||
/// of the result. If the ability to list contents is unneeded, `openDirTraverse` acts the
|
||||
/// same and may be more efficient.
|
||||
///
|
||||
/// Call `close` on the result when done.
|
||||
///
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn openDirList(self: Dir, sub_path: []const u8) OpenError!Dir {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path);
|
||||
return self.openDirListW(&sub_path_w);
|
||||
}
|
||||
|
||||
const sub_path_c = try os.toPosixPath(sub_path);
|
||||
return self.openDirListC(&sub_path_c);
|
||||
}
|
||||
|
||||
/// Same as `openDirTraverse` except the parameter is null-terminated.
|
||||
pub fn openDirTraverseC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
|
||||
/// Same as `openDir` except the parameter is null-terminated.
|
||||
pub fn openDirC(self: Dir, sub_path_c: [*:0]const u8, args: OpenDirOptions) OpenError!Dir {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
|
||||
return self.openDirTraverseW(&sub_path_w);
|
||||
} else {
|
||||
return self.openDirW(&sub_path_w, args);
|
||||
} else if (!args.iterate) {
|
||||
const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0;
|
||||
return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC | O_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `openDirList` except the parameter is null-terminated.
|
||||
pub fn openDirListC(self: Dir, sub_path_c: [*:0]const u8) OpenError!Dir {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path_c);
|
||||
return self.openDirListW(&sub_path_w);
|
||||
return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH);
|
||||
} else {
|
||||
return self.openDirFlagsC(sub_path_c, os.O_RDONLY | os.O_CLOEXEC);
|
||||
return self.openDirFlagsC(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `openDir` except the path parameter is WTF-16 encoded, NT-prefixed.
|
||||
/// This function asserts the target OS is Windows.
|
||||
pub fn openDirW(self: Dir, sub_path_w: [*:0]const u16, args: OpenDirOptions) OpenError!Dir {
|
||||
const w = os.windows;
|
||||
// TODO remove some of these flags if args.access_sub_paths is false
|
||||
const base_flags = w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA |
|
||||
w.SYNCHRONIZE | w.FILE_TRAVERSE;
|
||||
const flags: u32 = if (args.iterate) base_flags | w.FILE_LIST_DIRECTORY else base_flags;
|
||||
return self.openDirAccessMaskW(sub_path_w, flags);
|
||||
}
|
||||
|
||||
/// `flags` must contain `os.O_DIRECTORY`.
|
||||
fn openDirFlagsC(self: Dir, sub_path_c: [*:0]const u8, flags: u32) OpenError!Dir {
|
||||
const os_flags = flags | os.O_DIRECTORY;
|
||||
const result = if (need_async_thread)
|
||||
std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, os_flags, 0)
|
||||
std.event.Loop.instance.?.openatZ(self.fd, sub_path_c, flags, 0)
|
||||
else
|
||||
os.openatC(self.fd, sub_path_c, os_flags, 0);
|
||||
os.openatC(self.fd, sub_path_c, flags, 0);
|
||||
const fd = result catch |err| switch (err) {
|
||||
error.FileTooBig => unreachable, // can't happen for directories
|
||||
error.IsDir => unreachable, // we're providing O_DIRECTORY
|
||||
@ -1026,22 +856,6 @@ pub const Dir = struct {
|
||||
return Dir{ .fd = fd };
|
||||
}
|
||||
|
||||
/// Same as `openDirTraverse` except the path parameter is UTF16LE, NT-prefixed.
|
||||
/// This function is Windows-only.
|
||||
pub fn openDirTraverseW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir {
|
||||
const w = os.windows;
|
||||
|
||||
return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE);
|
||||
}
|
||||
|
||||
/// Same as `openDirList` except the path parameter is UTF16LE, NT-prefixed.
|
||||
/// This function is Windows-only.
|
||||
pub fn openDirListW(self: Dir, sub_path_w: [*:0]const u16) OpenError!Dir {
|
||||
const w = os.windows;
|
||||
|
||||
return self.openDirAccessMaskW(sub_path_w, w.STANDARD_RIGHTS_READ | w.FILE_READ_ATTRIBUTES | w.FILE_READ_EA | w.SYNCHRONIZE | w.FILE_TRAVERSE | w.FILE_LIST_DIRECTORY);
|
||||
}
|
||||
|
||||
fn openDirAccessMaskW(self: Dir, sub_path_w: [*:0]const u16, access_mask: u32) OpenError!Dir {
|
||||
const w = os.windows;
|
||||
|
||||
@ -1262,7 +1076,7 @@ pub const Dir = struct {
|
||||
error.Unexpected,
|
||||
=> |e| return e,
|
||||
}
|
||||
var dir = self.openDirList(sub_path) catch |err| switch (err) {
|
||||
var dir = self.openDir(sub_path, .{ .iterate = true }) catch |err| switch (err) {
|
||||
error.NotDir => {
|
||||
if (got_access_denied) {
|
||||
return error.AccessDenied;
|
||||
@ -1295,7 +1109,6 @@ pub const Dir = struct {
|
||||
|
||||
var dir_name_buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
var dir_name: []const u8 = sub_path;
|
||||
var parent_dir = self;
|
||||
|
||||
// Here we must avoid recursion, in order to provide O(1) memory guarantee of this function.
|
||||
// Go through each entry and if it is not a directory, delete it. If it is a directory,
|
||||
@ -1327,7 +1140,7 @@ pub const Dir = struct {
|
||||
=> |e| return e,
|
||||
}
|
||||
|
||||
const new_dir = dir.openDirList(entry.name) catch |err| switch (err) {
|
||||
const new_dir = dir.openDir(entry.name, .{ .iterate = true }) catch |err| switch (err) {
|
||||
error.NotDir => {
|
||||
if (got_access_denied) {
|
||||
return error.AccessDenied;
|
||||
@ -1434,9 +1247,96 @@ pub const Dir = struct {
|
||||
pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
|
||||
return os.faccessatW(self.fd, sub_path_w, 0, 0);
|
||||
}
|
||||
|
||||
/// Check the file size, mtime, and mode of `source_path` and `dest_path`. If they are equal, does nothing.
|
||||
/// Otherwise, atomically copies `source_path` to `dest_path`. The destination file gains the mtime,
|
||||
/// atime, and mode of the source file so that the next call to `updateFile` will not need a copy.
|
||||
/// Returns the previous status of the file before updating.
|
||||
/// If any of the directories do not exist for dest_path, they are created.
|
||||
pub fn updateFile(
|
||||
source_dir: Dir,
|
||||
source_path: []const u8,
|
||||
dest_dir: Dir,
|
||||
dest_path: []const u8,
|
||||
options: CopyFileOptions,
|
||||
) !PrevStatus {
|
||||
var src_file = try source_dir.openFile(source_path, .{});
|
||||
defer src_file.close();
|
||||
|
||||
const src_stat = try src_file.stat();
|
||||
const actual_mode = options.override_mode orelse src_stat.mode;
|
||||
check_dest_stat: {
|
||||
const dest_stat = blk: {
|
||||
var dest_file = dest_dir.openFile(dest_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => break :check_dest_stat,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer dest_file.close();
|
||||
|
||||
break :blk try dest_file.stat();
|
||||
};
|
||||
|
||||
if (src_stat.size == dest_stat.size and
|
||||
src_stat.mtime == dest_stat.mtime and
|
||||
actual_mode == dest_stat.mode)
|
||||
{
|
||||
return PrevStatus.fresh;
|
||||
}
|
||||
}
|
||||
|
||||
if (path.dirname(dest_path)) |dirname| {
|
||||
try dest_dir.makePath(dirname);
|
||||
}
|
||||
|
||||
var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = actual_mode });
|
||||
defer atomic_file.deinit();
|
||||
|
||||
try atomic_file.file.writeFileAll(src_file, .{ .in_len = src_stat.size });
|
||||
try atomic_file.file.updateTimes(src_stat.atime, src_stat.mtime);
|
||||
try atomic_file.finish();
|
||||
return PrevStatus.stale;
|
||||
}
|
||||
|
||||
/// Guaranteed to be atomic.
|
||||
/// On Linux, until https://patchwork.kernel.org/patch/9636735/ is merged and readily available,
|
||||
/// there is a possibility of power loss or application termination leaving temporary files present
|
||||
/// in the same directory as dest_path.
|
||||
pub fn copyFile(
|
||||
source_dir: Dir,
|
||||
source_path: []const u8,
|
||||
dest_dir: Dir,
|
||||
dest_path: []const u8,
|
||||
options: CopyFileOptions,
|
||||
) !void {
|
||||
var in_file = try source_dir.openFile(source_path, .{});
|
||||
defer in_file.close();
|
||||
|
||||
var size: ?u64 = null;
|
||||
const mode = options.override_mode orelse blk: {
|
||||
const stat = try in_file.stat();
|
||||
size = stat.size;
|
||||
break :blk stat.mode;
|
||||
};
|
||||
|
||||
var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
|
||||
defer atomic_file.deinit();
|
||||
|
||||
try atomic_file.file.writeFileAll(in_file, .{ .in_len = size });
|
||||
return atomic_file.finish();
|
||||
}
|
||||
|
||||
pub const AtomicFileOptions = struct {
|
||||
mode: File.Mode = File.default_mode,
|
||||
};
|
||||
|
||||
/// `dest_path` must remain valid for the lifetime of `AtomicFile`.
|
||||
/// Call `AtomicFile.finish` to atomically replace `dest_path` with contents.
|
||||
pub fn atomicFile(self: Dir, dest_path: []const u8, options: AtomicFileOptions) !AtomicFile {
|
||||
return AtomicFile.init2(dest_path, options.mode, self);
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns an handle to the current working directory that is open for traversal.
|
||||
/// Returns an handle to the current working directory. It is not opened with iteration capability.
|
||||
/// Closing the returned `Dir` is checked illegal behavior. Iterating over the result is illegal behavior.
|
||||
/// On POSIX targets, this function is comptime-callable.
|
||||
pub fn cwd() Dir {
|
||||
@ -1514,6 +1414,25 @@ pub fn deleteFileAbsoluteW(absolute_path_w: [*:0]const u16) DeleteFileError!void
|
||||
return cwd().deleteFileW(absolute_path_w);
|
||||
}
|
||||
|
||||
/// Removes a symlink, file, or directory.
|
||||
/// This is equivalent to `Dir.deleteTree` with the base directory.
|
||||
/// Asserts that the path is absolute. See `Dir.deleteTree` for a function that
|
||||
/// operates on both absolute and relative paths.
|
||||
/// Asserts that the path parameter has no null bytes.
|
||||
pub fn deleteTreeAbsolute(absolute_path: []const u8) !void {
|
||||
assert(path.isAbsolute(absolute_path));
|
||||
const dirname = path.dirname(absolute_path) orelse return error{
|
||||
/// Attempt to remove the root file system path.
|
||||
/// This error is unreachable if `absolute_path` is relative.
|
||||
CannotDeleteRootDirectory,
|
||||
}.CannotDeleteRootDirectory;
|
||||
|
||||
var dir = try cwd().openDir(dirname, .{});
|
||||
defer dir.close();
|
||||
|
||||
return dir.deleteTree(path.basename(absolute_path));
|
||||
}
|
||||
|
||||
pub const Walker = struct {
|
||||
stack: std.ArrayList(StackItem),
|
||||
name_buffer: std.Buffer,
|
||||
@ -1548,7 +1467,7 @@ pub const Walker = struct {
|
||||
try self.name_buffer.appendByte(path.sep);
|
||||
try self.name_buffer.append(base.name);
|
||||
if (base.kind == .Directory) {
|
||||
var new_dir = top.dir_it.dir.openDirList(base.name) catch |err| switch (err) {
|
||||
var new_dir = top.dir_it.dir.openDir(base.name, .{ .iterate = true }) catch |err| switch (err) {
|
||||
error.NameTooLong => unreachable, // no path sep in base.name
|
||||
else => |e| return e,
|
||||
};
|
||||
@ -1586,7 +1505,7 @@ pub const Walker = struct {
|
||||
pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
|
||||
assert(!mem.endsWith(u8, dir_path, path.sep_str));
|
||||
|
||||
var dir = try cwd().openDirList(dir_path);
|
||||
var dir = try cwd().openDir(dir_path, .{ .iterate = true });
|
||||
errdefer dir.close();
|
||||
|
||||
var name_buffer = try std.Buffer.init(allocator, dir_path);
|
||||
@ -1605,13 +1524,12 @@ pub fn walkPath(allocator: *Allocator, dir_path: []const u8) !Walker {
|
||||
return walker;
|
||||
}
|
||||
|
||||
/// Read value of a symbolic link.
|
||||
/// The return value is a slice of buffer, from index `0`.
|
||||
/// Deprecated; use `Dir.readLink`.
|
||||
pub fn readLink(pathname: []const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
|
||||
return os.readlink(pathname, buffer);
|
||||
}
|
||||
|
||||
/// Same as `readLink`, except the parameter is null-terminated.
|
||||
/// Deprecated; use `Dir.readLinkC`.
|
||||
pub fn readLinkC(pathname_c: [*]const u8, buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
|
||||
return os.readlinkC(pathname_c, buffer);
|
||||
}
|
||||
@ -1718,6 +1636,7 @@ pub fn selfExeDirPath(out_buffer: *[MAX_PATH_BYTES]u8) SelfExePathError![]const
|
||||
}
|
||||
|
||||
/// `realpath`, except caller must free the returned memory.
|
||||
/// TODO integrate with `Dir`
|
||||
pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
|
||||
var buf: [MAX_PATH_BYTES]u8 = undefined;
|
||||
return mem.dupe(allocator, u8, try os.realpath(pathname, &buf));
|
||||
@ -1726,6 +1645,9 @@ pub fn realpathAlloc(allocator: *Allocator, pathname: []const u8) ![]u8 {
|
||||
test "" {
|
||||
_ = makeDirAbsolute;
|
||||
_ = makeDirAbsoluteZ;
|
||||
_ = copyFileAbsolute;
|
||||
_ = updateFileAbsolute;
|
||||
_ = Dir.copyFile;
|
||||
_ = @import("fs/path.zig");
|
||||
_ = @import("fs/file.zig");
|
||||
_ = @import("fs/get_app_data_dir.zig");
|
||||
|
||||
@ -619,7 +619,7 @@ test "write a file, watch it, write it again" {
|
||||
if (true) return error.SkipZigTest;
|
||||
|
||||
try fs.cwd().makePath(test_tmp_dir);
|
||||
defer os.deleteTree(test_tmp_dir) catch {};
|
||||
defer fs.cwd().deleteTree(test_tmp_dir) catch {};
|
||||
|
||||
const allocator = std.heap.page_allocator;
|
||||
return testFsWatch(&allocator);
|
||||
|
||||
@ -40,7 +40,9 @@ pub fn hashPointer(hasher: var, key: var, comptime strat: HashStrategy) void {
|
||||
.DeepRecursive => hashArray(hasher, key, .DeepRecursive),
|
||||
},
|
||||
|
||||
.Many, .C, => switch (strat) {
|
||||
.Many,
|
||||
.C,
|
||||
=> switch (strat) {
|
||||
.Shallow => hash(hasher, @ptrToInt(key), .Shallow),
|
||||
else => @compileError(
|
||||
\\ unknown-length pointers and C pointers cannot be hashed deeply.
|
||||
@ -236,9 +238,11 @@ test "hash slice shallow" {
|
||||
defer std.testing.allocator.destroy(array1);
|
||||
array1.* = [_]u32{ 1, 2, 3, 4, 5, 6 };
|
||||
const array2 = [_]u32{ 1, 2, 3, 4, 5, 6 };
|
||||
const a = array1[0..];
|
||||
const b = array2[0..];
|
||||
const c = array1[0..3];
|
||||
// TODO audit deep/shallow - maybe it has the wrong behavior with respect to array pointers and slices
|
||||
var runtime_zero: usize = 0;
|
||||
const a = array1[runtime_zero..];
|
||||
const b = array2[runtime_zero..];
|
||||
const c = array1[runtime_zero..3];
|
||||
testing.expect(testHashShallow(a) == testHashShallow(a));
|
||||
testing.expect(testHashShallow(a) != testHashShallow(array1));
|
||||
testing.expect(testHashShallow(a) != testHashShallow(b));
|
||||
|
||||
@ -39,8 +39,8 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round
|
||||
pub fn init(key: []const u8) Self {
|
||||
assert(key.len >= 16);
|
||||
|
||||
const k0 = mem.readIntSliceLittle(u64, key[0..8]);
|
||||
const k1 = mem.readIntSliceLittle(u64, key[8..16]);
|
||||
const k0 = mem.readIntLittle(u64, key[0..8]);
|
||||
const k1 = mem.readIntLittle(u64, key[8..16]);
|
||||
|
||||
var d = Self{
|
||||
.v0 = k0 ^ 0x736f6d6570736575,
|
||||
@ -111,7 +111,7 @@ fn SipHashStateless(comptime T: type, comptime c_rounds: usize, comptime d_round
|
||||
fn round(self: *Self, b: []const u8) void {
|
||||
assert(b.len == 8);
|
||||
|
||||
const m = mem.readIntSliceLittle(u64, b[0..]);
|
||||
const m = mem.readIntLittle(u64, b[0..8]);
|
||||
self.v3 ^= m;
|
||||
|
||||
// TODO this is a workaround, should be able to supply the value without a separate variable
|
||||
|
||||
@ -11,7 +11,7 @@ const primes = [_]u64{
|
||||
|
||||
fn read_bytes(comptime bytes: u8, data: []const u8) u64 {
|
||||
const T = std.meta.IntType(false, 8 * bytes);
|
||||
return mem.readIntSliceLittle(T, data[0..bytes]);
|
||||
return mem.readIntLittle(T, data[0..bytes]);
|
||||
}
|
||||
|
||||
fn read_8bytes_swapped(data: []const u8) u64 {
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
const std = @import("../std.zig");
|
||||
const builtin = std.builtin;
|
||||
const io = std.io;
|
||||
const assert = std.debug.assert;
|
||||
const math = std.math;
|
||||
const meta = std.meta;
|
||||
const trait = meta.trait;
|
||||
|
||||
pub const Packing = enum {
|
||||
/// Pack data to byte alignment
|
||||
@ -252,7 +256,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime packing: Packing, co
|
||||
byte.* = if (t_bit_count < u8_bit_count) v else @truncate(u8, v);
|
||||
}
|
||||
|
||||
try self.out_stream.write(&buffer);
|
||||
try self.out_stream.writeAll(&buffer);
|
||||
}
|
||||
|
||||
/// Serializes the passed value into the stream
|
||||
|
||||
@ -2249,11 +2249,16 @@ pub const StringifyOptions = struct {
|
||||
// TODO: allow picking if []u8 is string or array?
|
||||
};
|
||||
|
||||
pub const StringifyError = error{
|
||||
TooMuchData,
|
||||
DifferentData,
|
||||
};
|
||||
|
||||
pub fn stringify(
|
||||
value: var,
|
||||
options: StringifyOptions,
|
||||
out_stream: var,
|
||||
) !void {
|
||||
) StringifyError!void {
|
||||
const T = @TypeOf(value);
|
||||
switch (@typeInfo(T)) {
|
||||
.Float, .ComptimeFloat => {
|
||||
@ -2320,9 +2325,15 @@ pub fn stringify(
|
||||
return;
|
||||
},
|
||||
.Pointer => |ptr_info| switch (ptr_info.size) {
|
||||
.One => {
|
||||
// TODO: avoid loops?
|
||||
return try stringify(value.*, options, out_stream);
|
||||
.One => switch (@typeInfo(ptr_info.child)) {
|
||||
.Array => {
|
||||
const Slice = []const std.meta.Elem(ptr_info.child);
|
||||
return stringify(@as(Slice, value), options, out_stream);
|
||||
},
|
||||
else => {
|
||||
// TODO: avoid loops?
|
||||
return stringify(value.*, options, out_stream);
|
||||
},
|
||||
},
|
||||
// TODO: .Many when there is a sentinel (waiting for https://github.com/ziglang/zig/pull/3972)
|
||||
.Slice => {
|
||||
@ -2381,9 +2392,7 @@ pub fn stringify(
|
||||
},
|
||||
else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
|
||||
},
|
||||
.Array => |info| {
|
||||
return try stringify(value[0..], options, out_stream);
|
||||
},
|
||||
.Array => return stringify(&value, options, out_stream),
|
||||
else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
|
||||
}
|
||||
unreachable;
|
||||
|
||||
@ -373,6 +373,7 @@ pub const Int = struct {
|
||||
const d = switch (ch) {
|
||||
'0'...'9' => ch - '0',
|
||||
'a'...'f' => (ch - 'a') + 0xa,
|
||||
'A'...'F' => (ch - 'A') + 0xa,
|
||||
else => return error.InvalidCharForDigit,
|
||||
};
|
||||
|
||||
@ -393,8 +394,9 @@ pub const Int = struct {
|
||||
|
||||
/// Set self from the string representation `value`.
|
||||
///
|
||||
/// value must contain only digits <= `base`. Base prefixes are not allowed (e.g. 0x43 should
|
||||
/// simply be 43).
|
||||
/// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are
|
||||
/// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are
|
||||
/// ignored and can be used as digit separators.
|
||||
///
|
||||
/// Returns an error if memory could not be allocated or `value` has invalid digits for the
|
||||
/// requested base.
|
||||
@ -415,6 +417,9 @@ pub const Int = struct {
|
||||
try self.set(0);
|
||||
|
||||
for (value[i..]) |ch| {
|
||||
if (ch == '_') {
|
||||
continue;
|
||||
}
|
||||
const d = try charToDigit(ch, base);
|
||||
|
||||
const ap_d = Int.initFixed(([_]Limb{d})[0..]);
|
||||
@ -520,13 +525,13 @@ pub const Int = struct {
|
||||
comptime fmt: []const u8,
|
||||
options: std.fmt.FormatOptions,
|
||||
out_stream: var,
|
||||
) FmtError!void {
|
||||
) !void {
|
||||
self.assertWritable();
|
||||
// TODO look at fmt and support other bases
|
||||
// TODO support read-only fixed integers
|
||||
const str = self.toString(self.allocator.?, 10) catch @panic("TODO make this non allocating");
|
||||
defer self.allocator.?.free(str);
|
||||
return out_stream.print(str);
|
||||
return out_stream.writeAll(str);
|
||||
}
|
||||
|
||||
/// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
|
||||
@ -1582,6 +1587,22 @@ test "big.int string negative" {
|
||||
testing.expect((try a.to(i32)) == -1023);
|
||||
}
|
||||
|
||||
test "big.int string set number with underscores" {
|
||||
var a = try Int.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___");
|
||||
testing.expect((try a.to(u128)) == 120317241209124781241290847124);
|
||||
}
|
||||
|
||||
test "big.int string set case insensitive number" {
|
||||
var a = try Int.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
try a.setString(16, "aB_cD_eF");
|
||||
testing.expect((try a.to(u32)) == 0xabcdef);
|
||||
}
|
||||
|
||||
test "big.int string set bad char error" {
|
||||
var a = try Int.init(testing.allocator);
|
||||
defer a.deinit();
|
||||
|
||||
212
lib/std/mem.zig
212
lib/std/mem.zig
@ -116,7 +116,7 @@ pub const Allocator = struct {
|
||||
pub fn allocSentinel(self: *Allocator, comptime Elem: type, n: usize, comptime sentinel: Elem) Error![:sentinel]Elem {
|
||||
var ptr = try self.alloc(Elem, n + 1);
|
||||
ptr[n] = sentinel;
|
||||
return ptr[0 .. n :sentinel];
|
||||
return ptr[0..n :sentinel];
|
||||
}
|
||||
|
||||
pub fn alignedAlloc(
|
||||
@ -496,14 +496,14 @@ pub fn eql(comptime T: type, a: []const T, b: []const T) bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Deprecated. Use `span`.
|
||||
/// Deprecated. Use `spanZ`.
|
||||
pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {
|
||||
return ptr[0..len(ptr) :0];
|
||||
return ptr[0..lenZ(ptr) :0];
|
||||
}
|
||||
|
||||
/// Deprecated. Use `span`.
|
||||
/// Deprecated. Use `spanZ`.
|
||||
pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {
|
||||
return ptr[0..len(ptr) :0];
|
||||
return ptr[0..lenZ(ptr) :0];
|
||||
}
|
||||
|
||||
/// Takes a pointer to an array, a sentinel-terminated pointer, or a slice, and
|
||||
@ -548,6 +548,9 @@ test "Span" {
|
||||
/// returns a slice. If there is a sentinel on the input type, there will be a
|
||||
/// sentinel on the output type. The constness of the output type matches
|
||||
/// the constness of the input type.
|
||||
///
|
||||
/// When there is both a sentinel and an array length or slice length, the
|
||||
/// length value is used instead of the sentinel.
|
||||
pub fn span(ptr: var) Span(@TypeOf(ptr)) {
|
||||
const Result = Span(@TypeOf(ptr));
|
||||
const l = len(ptr);
|
||||
@ -560,20 +563,42 @@ pub fn span(ptr: var) Span(@TypeOf(ptr)) {
|
||||
|
||||
test "span" {
|
||||
var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
|
||||
const ptr = array[0..2 :3].ptr;
|
||||
const ptr = @as([*:3]u16, array[0..2 :3]);
|
||||
testing.expect(eql(u16, span(ptr), &[_]u16{ 1, 2 }));
|
||||
testing.expect(eql(u16, span(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
|
||||
}
|
||||
|
||||
/// Same as `span`, except when there is both a sentinel and an array
|
||||
/// length or slice length, scans the memory for the sentinel value
|
||||
/// rather than using the length.
|
||||
pub fn spanZ(ptr: var) Span(@TypeOf(ptr)) {
|
||||
const Result = Span(@TypeOf(ptr));
|
||||
const l = lenZ(ptr);
|
||||
if (@typeInfo(Result).Pointer.sentinel) |s| {
|
||||
return ptr[0..l :s];
|
||||
} else {
|
||||
return ptr[0..l];
|
||||
}
|
||||
}
|
||||
|
||||
test "spanZ" {
|
||||
var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
|
||||
const ptr = @as([*:3]u16, array[0..2 :3]);
|
||||
testing.expect(eql(u16, spanZ(ptr), &[_]u16{ 1, 2 }));
|
||||
testing.expect(eql(u16, spanZ(&array), &[_]u16{ 1, 2, 3, 4, 5 }));
|
||||
}
|
||||
|
||||
/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
|
||||
/// or a slice, and returns the length.
|
||||
/// In the case of a sentinel-terminated array, it uses the array length.
|
||||
/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
|
||||
pub fn len(ptr: var) usize {
|
||||
return switch (@typeInfo(@TypeOf(ptr))) {
|
||||
.Array => |info| info.len,
|
||||
.Pointer => |info| switch (info.size) {
|
||||
.One => switch (@typeInfo(info.child)) {
|
||||
.Array => |x| x.len,
|
||||
else => @compileError("invalid type given to std.mem.length"),
|
||||
.Array => ptr.len,
|
||||
else => @compileError("invalid type given to std.mem.len"),
|
||||
},
|
||||
.Many => if (info.sentinel) |sentinel|
|
||||
indexOfSentinel(info.child, sentinel, ptr)
|
||||
@ -582,7 +607,7 @@ pub fn len(ptr: var) usize {
|
||||
.C => indexOfSentinel(info.child, 0, ptr),
|
||||
.Slice => ptr.len,
|
||||
},
|
||||
else => @compileError("invalid type given to std.mem.length"),
|
||||
else => @compileError("invalid type given to std.mem.len"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -594,9 +619,67 @@ test "len" {
|
||||
testing.expect(len(&array) == 5);
|
||||
testing.expect(len(array[0..3]) == 3);
|
||||
array[2] = 0;
|
||||
const ptr = array[0..2 :0].ptr;
|
||||
const ptr = @as([*:0]u16, array[0..2 :0]);
|
||||
testing.expect(len(ptr) == 2);
|
||||
}
|
||||
{
|
||||
var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
|
||||
testing.expect(len(&array) == 5);
|
||||
array[2] = 0;
|
||||
testing.expect(len(&array) == 5);
|
||||
}
|
||||
}
|
||||
|
||||
/// Takes a pointer to an array, an array, a sentinel-terminated pointer,
|
||||
/// or a slice, and returns the length.
|
||||
/// In the case of a sentinel-terminated array, it scans the array
|
||||
/// for a sentinel and uses that for the length, rather than using the array length.
|
||||
/// For C pointers it assumes it is a pointer-to-many with a 0 sentinel.
|
||||
pub fn lenZ(ptr: var) usize {
|
||||
return switch (@typeInfo(@TypeOf(ptr))) {
|
||||
.Array => |info| if (info.sentinel) |sentinel|
|
||||
indexOfSentinel(info.child, sentinel, &ptr)
|
||||
else
|
||||
info.len,
|
||||
.Pointer => |info| switch (info.size) {
|
||||
.One => switch (@typeInfo(info.child)) {
|
||||
.Array => |x| if (x.sentinel) |sentinel|
|
||||
indexOfSentinel(x.child, sentinel, ptr)
|
||||
else
|
||||
ptr.len,
|
||||
else => @compileError("invalid type given to std.mem.lenZ"),
|
||||
},
|
||||
.Many => if (info.sentinel) |sentinel|
|
||||
indexOfSentinel(info.child, sentinel, ptr)
|
||||
else
|
||||
@compileError("length of pointer with no sentinel"),
|
||||
.C => indexOfSentinel(info.child, 0, ptr),
|
||||
.Slice => if (info.sentinel) |sentinel|
|
||||
indexOfSentinel(info.child, sentinel, ptr.ptr)
|
||||
else
|
||||
ptr.len,
|
||||
},
|
||||
else => @compileError("invalid type given to std.mem.lenZ"),
|
||||
};
|
||||
}
|
||||
|
||||
test "lenZ" {
|
||||
testing.expect(lenZ("aoeu") == 4);
|
||||
|
||||
{
|
||||
var array: [5]u16 = [_]u16{ 1, 2, 3, 4, 5 };
|
||||
testing.expect(lenZ(&array) == 5);
|
||||
testing.expect(lenZ(array[0..3]) == 3);
|
||||
array[2] = 0;
|
||||
const ptr = @as([*:0]u16, array[0..2 :0]);
|
||||
testing.expect(lenZ(ptr) == 2);
|
||||
}
|
||||
{
|
||||
var array: [5:0]u16 = [_:0]u16{ 1, 2, 3, 4, 5 };
|
||||
testing.expect(lenZ(&array) == 5);
|
||||
array[2] = 0;
|
||||
testing.expect(lenZ(&array) == 2);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn indexOfSentinel(comptime Elem: type, comptime sentinel: Elem, ptr: [*:sentinel]const Elem) usize {
|
||||
@ -810,8 +893,7 @@ pub const readIntBig = switch (builtin.endian) {
|
||||
pub fn readIntSliceNative(comptime T: type, bytes: []const u8) T {
|
||||
const n = @divExact(T.bit_count, 8);
|
||||
assert(bytes.len >= n);
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
return readIntNative(T, @ptrCast(*const [n]u8, bytes.ptr));
|
||||
return readIntNative(T, bytes[0..n]);
|
||||
}
|
||||
|
||||
/// Asserts that bytes.len >= T.bit_count / 8. Reads the integer starting from index 0
|
||||
@ -849,8 +931,7 @@ pub fn readInt(comptime T: type, bytes: *const [@divExact(T.bit_count, 8)]u8, en
|
||||
pub fn readIntSlice(comptime T: type, bytes: []const u8, endian: builtin.Endian) T {
|
||||
const n = @divExact(T.bit_count, 8);
|
||||
assert(bytes.len >= n);
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
return readInt(T, @ptrCast(*const [n]u8, bytes.ptr), endian);
|
||||
return readInt(T, bytes[0..n], endian);
|
||||
}
|
||||
|
||||
test "comptime read/write int" {
|
||||
@ -1572,24 +1653,24 @@ pub fn nativeToBig(comptime T: type, x: T) T {
|
||||
}
|
||||
|
||||
fn AsBytesReturnType(comptime P: type) type {
|
||||
if (comptime !trait.isSingleItemPtr(P))
|
||||
if (!trait.isSingleItemPtr(P))
|
||||
@compileError("expected single item pointer, passed " ++ @typeName(P));
|
||||
|
||||
const size = @as(usize, @sizeOf(meta.Child(P)));
|
||||
const alignment = comptime meta.alignment(P);
|
||||
const size = @sizeOf(meta.Child(P));
|
||||
const alignment = meta.alignment(P);
|
||||
|
||||
if (alignment == 0) {
|
||||
if (comptime trait.isConstPtr(P))
|
||||
if (trait.isConstPtr(P))
|
||||
return *const [size]u8;
|
||||
return *[size]u8;
|
||||
}
|
||||
|
||||
if (comptime trait.isConstPtr(P))
|
||||
if (trait.isConstPtr(P))
|
||||
return *align(alignment) const [size]u8;
|
||||
return *align(alignment) [size]u8;
|
||||
}
|
||||
|
||||
///Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
|
||||
/// Given a pointer to a single item, returns a slice of the underlying bytes, preserving constness.
|
||||
pub fn asBytes(ptr: var) AsBytesReturnType(@TypeOf(ptr)) {
|
||||
const P = @TypeOf(ptr);
|
||||
return @ptrCast(AsBytesReturnType(P), ptr);
|
||||
@ -1736,34 +1817,50 @@ fn BytesAsSliceReturnType(comptime T: type, comptime bytesType: type) type {
|
||||
}
|
||||
|
||||
pub fn bytesAsSlice(comptime T: type, bytes: var) BytesAsSliceReturnType(T, @TypeOf(bytes)) {
|
||||
const bytesSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(bytes))) bytes[0..] else bytes;
|
||||
|
||||
// let's not give an undefined pointer to @ptrCast
|
||||
// it may be equal to zero and fail a null check
|
||||
if (bytesSlice.len == 0) {
|
||||
if (bytes.len == 0) {
|
||||
return &[0]T{};
|
||||
}
|
||||
|
||||
const bytesType = @TypeOf(bytesSlice);
|
||||
const alignment = comptime meta.alignment(bytesType);
|
||||
const Bytes = @TypeOf(bytes);
|
||||
const alignment = comptime meta.alignment(Bytes);
|
||||
|
||||
const castTarget = if (comptime trait.isConstPtr(bytesType)) [*]align(alignment) const T else [*]align(alignment) T;
|
||||
const cast_target = if (comptime trait.isConstPtr(Bytes)) [*]align(alignment) const T else [*]align(alignment) T;
|
||||
|
||||
return @ptrCast(castTarget, bytesSlice.ptr)[0..@divExact(bytes.len, @sizeOf(T))];
|
||||
return @ptrCast(cast_target, bytes)[0..@divExact(bytes.len, @sizeOf(T))];
|
||||
}
|
||||
|
||||
test "bytesAsSlice" {
|
||||
const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
|
||||
const slice = bytesAsSlice(u16, bytes[0..]);
|
||||
testing.expect(slice.len == 2);
|
||||
testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
|
||||
testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
|
||||
{
|
||||
const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
|
||||
const slice = bytesAsSlice(u16, bytes[0..]);
|
||||
testing.expect(slice.len == 2);
|
||||
testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
|
||||
testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
|
||||
}
|
||||
{
|
||||
const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
|
||||
var runtime_zero: usize = 0;
|
||||
const slice = bytesAsSlice(u16, bytes[runtime_zero..]);
|
||||
testing.expect(slice.len == 2);
|
||||
testing.expect(bigToNative(u16, slice[0]) == 0xDEAD);
|
||||
testing.expect(bigToNative(u16, slice[1]) == 0xBEEF);
|
||||
}
|
||||
}
|
||||
|
||||
test "bytesAsSlice keeps pointer alignment" {
|
||||
var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
|
||||
const numbers = bytesAsSlice(u32, bytes[0..]);
|
||||
comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
|
||||
{
|
||||
var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
|
||||
const numbers = bytesAsSlice(u32, bytes[0..]);
|
||||
comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
|
||||
}
|
||||
{
|
||||
var bytes = [_]u8{ 0x01, 0x02, 0x03, 0x04 };
|
||||
var runtime_zero: usize = 0;
|
||||
const numbers = bytesAsSlice(u32, bytes[runtime_zero..]);
|
||||
comptime testing.expect(@TypeOf(numbers) == []align(@alignOf(@TypeOf(bytes))) u32);
|
||||
}
|
||||
}
|
||||
|
||||
test "bytesAsSlice on a packed struct" {
|
||||
@ -1799,21 +1896,19 @@ fn SliceAsBytesReturnType(comptime sliceType: type) type {
|
||||
}
|
||||
|
||||
pub fn sliceAsBytes(slice: var) SliceAsBytesReturnType(@TypeOf(slice)) {
|
||||
const actualSlice = if (comptime trait.isPtrTo(.Array)(@TypeOf(slice))) slice[0..] else slice;
|
||||
const actualSliceTypeInfo = @typeInfo(@TypeOf(actualSlice)).Pointer;
|
||||
const Slice = @TypeOf(slice);
|
||||
|
||||
// let's not give an undefined pointer to @ptrCast
|
||||
// it may be equal to zero and fail a null check
|
||||
if (actualSlice.len == 0 and actualSliceTypeInfo.sentinel == null) {
|
||||
if (slice.len == 0 and comptime meta.sentinel(Slice) == null) {
|
||||
return &[0]u8{};
|
||||
}
|
||||
|
||||
const sliceType = @TypeOf(actualSlice);
|
||||
const alignment = comptime meta.alignment(sliceType);
|
||||
const alignment = comptime meta.alignment(Slice);
|
||||
|
||||
const castTarget = if (comptime trait.isConstPtr(sliceType)) [*]align(alignment) const u8 else [*]align(alignment) u8;
|
||||
const cast_target = if (comptime trait.isConstPtr(Slice)) [*]align(alignment) const u8 else [*]align(alignment) u8;
|
||||
|
||||
return @ptrCast(castTarget, actualSlice.ptr)[0 .. actualSlice.len * @sizeOf(comptime meta.Child(sliceType))];
|
||||
return @ptrCast(cast_target, slice)[0 .. slice.len * @sizeOf(meta.Elem(Slice))];
|
||||
}
|
||||
|
||||
test "sliceAsBytes" {
|
||||
@ -1883,39 +1978,6 @@ test "sliceAsBytes and bytesAsSlice back" {
|
||||
testing.expect(bytes[11] == math.maxInt(u8));
|
||||
}
|
||||
|
||||
fn SubArrayPtrReturnType(comptime T: type, comptime length: usize) type {
|
||||
if (trait.isConstPtr(T))
|
||||
return *const [length]meta.Child(meta.Child(T));
|
||||
return *[length]meta.Child(meta.Child(T));
|
||||
}
|
||||
|
||||
/// Given a pointer to an array, returns a pointer to a portion of that array, preserving constness.
|
||||
/// TODO this will be obsoleted by https://github.com/ziglang/zig/issues/863
|
||||
pub fn subArrayPtr(
|
||||
ptr: var,
|
||||
comptime start: usize,
|
||||
comptime length: usize,
|
||||
) SubArrayPtrReturnType(@TypeOf(ptr), length) {
|
||||
assert(start + length <= ptr.*.len);
|
||||
|
||||
const ReturnType = SubArrayPtrReturnType(@TypeOf(ptr), length);
|
||||
const T = meta.Child(meta.Child(@TypeOf(ptr)));
|
||||
return @ptrCast(ReturnType, &ptr[start]);
|
||||
}
|
||||
|
||||
test "subArrayPtr" {
|
||||
const a1: [6]u8 = "abcdef".*;
|
||||
const sub1 = subArrayPtr(&a1, 2, 3);
|
||||
testing.expect(eql(u8, sub1, "cde"));
|
||||
|
||||
var a2: [6]u8 = "abcdef".*;
|
||||
var sub2 = subArrayPtr(&a2, 2, 3);
|
||||
|
||||
testing.expect(eql(u8, sub2, "cde"));
|
||||
sub2[1] = 'X';
|
||||
testing.expect(eql(u8, &a2, "abcXef"));
|
||||
}
|
||||
|
||||
/// Round an address up to the nearest aligned address
|
||||
/// The alignment must be a power of 2 and greater than 0.
|
||||
pub fn alignForward(addr: usize, alignment: usize) usize {
|
||||
|
||||
@ -104,7 +104,7 @@ pub fn Child(comptime T: type) type {
|
||||
.Array => |info| info.child,
|
||||
.Pointer => |info| info.child,
|
||||
.Optional => |info| info.child,
|
||||
else => @compileError("Expected pointer, optional, or array type, " ++ "found '" ++ @typeName(T) ++ "'"),
|
||||
else => @compileError("Expected pointer, optional, or array type, found '" ++ @typeName(T) ++ "'"),
|
||||
};
|
||||
}
|
||||
|
||||
@ -115,30 +115,65 @@ test "std.meta.Child" {
|
||||
testing.expect(Child(?u8) == u8);
|
||||
}
|
||||
|
||||
/// Given a type with a sentinel e.g. `[:0]u8`, returns the sentinel
|
||||
pub fn Sentinel(comptime T: type) Child(T) {
|
||||
// comptime asserts that ptr has a sentinel
|
||||
/// Given a "memory span" type, returns the "element type".
|
||||
pub fn Elem(comptime T: type) type {
|
||||
switch (@typeInfo(T)) {
|
||||
.Array => |arrayInfo| {
|
||||
return comptime arrayInfo.sentinel.?;
|
||||
.Array => |info| return info.child,
|
||||
.Pointer => |info| switch (info.size) {
|
||||
.One => switch (@typeInfo(info.child)) {
|
||||
.Array => |array_info| return array_info.child,
|
||||
else => {},
|
||||
},
|
||||
.Many, .C, .Slice => return info.child,
|
||||
},
|
||||
.Pointer => |ptrInfo| {
|
||||
switch (ptrInfo.size) {
|
||||
.Many, .Slice => {
|
||||
return comptime ptrInfo.sentinel.?;
|
||||
else => {},
|
||||
}
|
||||
@compileError("Expected pointer, slice, or array, found '" ++ @typeName(T) ++ "'");
|
||||
}
|
||||
|
||||
test "std.meta.Elem" {
|
||||
testing.expect(Elem([1]u8) == u8);
|
||||
testing.expect(Elem([*]u8) == u8);
|
||||
testing.expect(Elem([]u8) == u8);
|
||||
testing.expect(Elem(*[10]u8) == u8);
|
||||
}
|
||||
|
||||
/// Given a type which can have a sentinel e.g. `[:0]u8`, returns the sentinel value,
|
||||
/// or `null` if there is not one.
|
||||
/// Types which cannot possibly have a sentinel will be a compile error.
|
||||
pub fn sentinel(comptime T: type) ?Elem(T) {
|
||||
switch (@typeInfo(T)) {
|
||||
.Array => |info| return info.sentinel,
|
||||
.Pointer => |info| {
|
||||
switch (info.size) {
|
||||
.Many, .Slice => return info.sentinel,
|
||||
.One => switch (@typeInfo(info.child)) {
|
||||
.Array => |array_info| return array_info.sentinel,
|
||||
else => {},
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
@compileError("not a sentinel type, found '" ++ @typeName(T) ++ "'");
|
||||
@compileError("type '" ++ @typeName(T) ++ "' cannot possibly have a sentinel");
|
||||
}
|
||||
|
||||
test "std.meta.Sentinel" {
|
||||
testing.expectEqual(@as(u8, 0), Sentinel([:0]u8));
|
||||
testing.expectEqual(@as(u8, 0), Sentinel([*:0]u8));
|
||||
testing.expectEqual(@as(u8, 0), Sentinel([5:0]u8));
|
||||
test "std.meta.sentinel" {
|
||||
testSentinel();
|
||||
comptime testSentinel();
|
||||
}
|
||||
|
||||
fn testSentinel() void {
|
||||
testing.expectEqual(@as(u8, 0), sentinel([:0]u8).?);
|
||||
testing.expectEqual(@as(u8, 0), sentinel([*:0]u8).?);
|
||||
testing.expectEqual(@as(u8, 0), sentinel([5:0]u8).?);
|
||||
testing.expectEqual(@as(u8, 0), sentinel(*const [5:0]u8).?);
|
||||
|
||||
testing.expect(sentinel([]u8) == null);
|
||||
testing.expect(sentinel([*]u8) == null);
|
||||
testing.expect(sentinel([5]u8) == null);
|
||||
testing.expect(sentinel(*const [5]u8) == null);
|
||||
}
|
||||
|
||||
pub fn containerLayout(comptime T: type) TypeInfo.ContainerLayout {
|
||||
|
||||
@ -230,9 +230,10 @@ pub fn isSingleItemPtr(comptime T: type) bool {
|
||||
|
||||
test "std.meta.trait.isSingleItemPtr" {
|
||||
const array = [_]u8{0} ** 10;
|
||||
testing.expect(isSingleItemPtr(@TypeOf(&array[0])));
|
||||
testing.expect(!isSingleItemPtr(@TypeOf(array)));
|
||||
testing.expect(!isSingleItemPtr(@TypeOf(array[0..1])));
|
||||
comptime testing.expect(isSingleItemPtr(@TypeOf(&array[0])));
|
||||
comptime testing.expect(!isSingleItemPtr(@TypeOf(array)));
|
||||
var runtime_zero: usize = 0;
|
||||
testing.expect(!isSingleItemPtr(@TypeOf(array[runtime_zero..1])));
|
||||
}
|
||||
|
||||
pub fn isManyItemPtr(comptime T: type) bool {
|
||||
@ -259,7 +260,8 @@ pub fn isSlice(comptime T: type) bool {
|
||||
|
||||
test "std.meta.trait.isSlice" {
|
||||
const array = [_]u8{0} ** 10;
|
||||
testing.expect(isSlice(@TypeOf(array[0..])));
|
||||
var runtime_zero: usize = 0;
|
||||
testing.expect(isSlice(@TypeOf(array[runtime_zero..])));
|
||||
testing.expect(!isSlice(@TypeOf(array)));
|
||||
testing.expect(!isSlice(@TypeOf(&array[0])));
|
||||
}
|
||||
@ -276,7 +278,7 @@ pub fn isIndexable(comptime T: type) bool {
|
||||
|
||||
test "std.meta.trait.isIndexable" {
|
||||
const array = [_]u8{0} ** 10;
|
||||
const slice = array[0..];
|
||||
const slice = @as([]const u8, &array);
|
||||
|
||||
testing.expect(isIndexable(@TypeOf(array)));
|
||||
testing.expect(isIndexable(@TypeOf(&array)));
|
||||
|
||||
@ -612,8 +612,7 @@ 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");
|
||||
// TODO https://github.com/ziglang/zig/issues/863
|
||||
mem.writeIntNative(u32, @ptrCast(*[4]u8, da6.addr[12..].ptr), addr.addr.in.addr);
|
||||
mem.writeIntNative(u32, da6.addr[12..], addr.addr.in.addr);
|
||||
da4.addr = addr.addr.in.addr;
|
||||
da = @ptrCast(*os.sockaddr, &da4);
|
||||
dalen = @sizeOf(os.sockaddr_in);
|
||||
@ -821,7 +820,7 @@ fn linuxLookupNameFromHosts(
|
||||
// Skip to the delimiter in the stream, to fix parsing
|
||||
try stream.skipUntilDelimiterOrEof('\n');
|
||||
// Use the truncated line. A truncated comment or hostname will be handled correctly.
|
||||
break :blk line_buf[0..];
|
||||
break :blk &line_buf;
|
||||
},
|
||||
else => |e| return e,
|
||||
}) |line| {
|
||||
@ -958,7 +957,10 @@ fn linuxLookupNameFromDns(
|
||||
}
|
||||
}
|
||||
|
||||
var ap = [2][]u8{ apbuf[0][0..0], apbuf[1][0..0] };
|
||||
var ap = [2][]u8{ apbuf[0], apbuf[1] };
|
||||
ap[0].len = 0;
|
||||
ap[1].len = 0;
|
||||
|
||||
try resMSendRc(qp[0..nq], ap[0..nq], apbuf[0..nq], rc);
|
||||
|
||||
var i: usize = 0;
|
||||
|
||||
166
lib/std/os.zig
166
lib/std/os.zig
@ -461,13 +461,11 @@ pub fn ftruncate(fd: fd_t, length: u64) TruncateError!void {
|
||||
);
|
||||
|
||||
switch (rc) {
|
||||
.SUCCESS => {},
|
||||
.SUCCESS => return,
|
||||
.INVALID_HANDLE => unreachable, // Handle not open for writing
|
||||
.ACCESS_DENIED => return error.CannotTruncate,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
@ -852,6 +850,7 @@ pub const OpenError = error{
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// See also `openC`.
|
||||
/// TODO support windows
|
||||
pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return openC(&file_path_c, flags, perm);
|
||||
@ -859,6 +858,7 @@ pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
|
||||
|
||||
/// Open and possibly create a file. Keeps trying if it gets interrupted.
|
||||
/// See also `open`.
|
||||
/// TODO support windows
|
||||
pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {
|
||||
while (true) {
|
||||
const rc = system.open(file_path, flags, perm);
|
||||
@ -892,6 +892,7 @@ pub fn openC(file_path: [*:0]const u8, flags: u32, perm: usize) OpenError!fd_t {
|
||||
/// 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 {
|
||||
const file_path_c = try toPosixPath(file_path);
|
||||
return openatC(dir_fd, &file_path_c, flags, mode);
|
||||
@ -900,6 +901,7 @@ pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: mode_t) Ope
|
||||
/// 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 openatC(dir_fd: fd_t, file_path: [*:0]const u8, flags: u32, mode: mode_t) OpenError!fd_t {
|
||||
while (true) {
|
||||
const rc = system.openat(dir_fd, file_path, flags, mode);
|
||||
@ -1140,24 +1142,6 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8)
|
||||
allocator.free(envp_buf);
|
||||
}
|
||||
|
||||
pub const FcntlError = error{
|
||||
/// The file is locked by another process
|
||||
FileLocked,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Attempts to get lock the file, blocking if the file is locked.
|
||||
pub fn fcntl(fd: fd_t, cmd: i32, flock_p: *Flock) FcntlError!void {
|
||||
while (true) {
|
||||
switch (errno(system.fcntl(fd, cmd, flock_p))) {
|
||||
0 => return,
|
||||
EACCES => return error.FileLocked,
|
||||
EAGAIN => return error.FileLocked,
|
||||
EINTR => continue,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an environment variable.
|
||||
/// See also `getenvZ`.
|
||||
pub fn getenv(key: []const u8) ?[]const u8 {
|
||||
@ -1545,6 +1529,9 @@ const RenameError = error{
|
||||
RenameAcrossMountPoints,
|
||||
InvalidUtf8,
|
||||
BadPathName,
|
||||
NoDevice,
|
||||
SharingViolation,
|
||||
PipeBusy,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// Change the name or location of a file.
|
||||
@ -1598,6 +1585,113 @@ pub fn renameW(old_path: [*:0]const u16, new_path: [*:0]const u16) RenameError!v
|
||||
return windows.MoveFileExW(old_path, new_path, flags);
|
||||
}
|
||||
|
||||
/// Change the name or location of a file based on an open directory handle.
|
||||
pub fn renameat(
|
||||
old_dir_fd: fd_t,
|
||||
old_path: []const u8,
|
||||
new_dir_fd: fd_t,
|
||||
new_path: []const u8,
|
||||
) RenameError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const old_path_w = try windows.sliceToPrefixedFileW(old_path);
|
||||
const new_path_w = try windows.sliceToPrefixedFileW(new_path);
|
||||
return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE);
|
||||
} else {
|
||||
const old_path_c = try toPosixPath(old_path);
|
||||
const new_path_c = try toPosixPath(new_path);
|
||||
return renameatZ(old_dir_fd, &old_path_c, new_dir_fd, &new_path_c);
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `renameat` except the parameters are null-terminated byte arrays.
|
||||
pub fn renameatZ(
|
||||
old_dir_fd: fd_t,
|
||||
old_path: [*:0]const u8,
|
||||
new_dir_fd: fd_t,
|
||||
new_path: [*:0]const u8,
|
||||
) RenameError!void {
|
||||
if (builtin.os.tag == .windows) {
|
||||
const old_path_w = try windows.cStrToPrefixedFileW(old_path);
|
||||
const new_path_w = try windows.cStrToPrefixedFileW(new_path);
|
||||
return renameatW(old_dir_fd, &old_path_w, new_dir_fd, &new_path_w, windows.TRUE);
|
||||
}
|
||||
|
||||
switch (errno(system.renameat(old_dir_fd, old_path, new_dir_fd, new_path))) {
|
||||
0 => return,
|
||||
EACCES => return error.AccessDenied,
|
||||
EPERM => return error.AccessDenied,
|
||||
EBUSY => return error.FileBusy,
|
||||
EDQUOT => return error.DiskQuota,
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable,
|
||||
EISDIR => return error.IsDir,
|
||||
ELOOP => return error.SymLinkLoop,
|
||||
EMLINK => return error.LinkQuotaExceeded,
|
||||
ENAMETOOLONG => return error.NameTooLong,
|
||||
ENOENT => return error.FileNotFound,
|
||||
ENOTDIR => return error.NotDir,
|
||||
ENOMEM => return error.SystemResources,
|
||||
ENOSPC => return error.NoSpaceLeft,
|
||||
EEXIST => return error.PathAlreadyExists,
|
||||
ENOTEMPTY => return error.PathAlreadyExists,
|
||||
EROFS => return error.ReadOnlyFileSystem,
|
||||
EXDEV => return error.RenameAcrossMountPoints,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as `renameat` except the parameters are null-terminated UTF16LE encoded byte arrays.
|
||||
/// Assumes target is Windows.
|
||||
/// TODO these args can actually be slices when using ntdll. audit the rest of the W functions too.
|
||||
pub fn renameatW(
|
||||
old_dir_fd: fd_t,
|
||||
old_path: [*:0]const u16,
|
||||
new_dir_fd: fd_t,
|
||||
new_path_w: [*:0]const u16,
|
||||
ReplaceIfExists: windows.BOOLEAN,
|
||||
) RenameError!void {
|
||||
const access_mask = windows.SYNCHRONIZE | windows.GENERIC_WRITE | windows.DELETE;
|
||||
const src_fd = try windows.OpenFileW(old_dir_fd, old_path, null, access_mask, windows.FILE_OPEN);
|
||||
defer windows.CloseHandle(src_fd);
|
||||
|
||||
const struct_buf_len = @sizeOf(windows.FILE_RENAME_INFORMATION) + (MAX_PATH_BYTES - 1);
|
||||
var rename_info_buf: [struct_buf_len]u8 align(@alignOf(windows.FILE_RENAME_INFORMATION)) = undefined;
|
||||
const new_path = mem.span(new_path_w);
|
||||
const struct_len = @sizeOf(windows.FILE_RENAME_INFORMATION) - 1 + new_path.len * 2;
|
||||
if (struct_len > struct_buf_len) return error.NameTooLong;
|
||||
|
||||
const rename_info = @ptrCast(*windows.FILE_RENAME_INFORMATION, &rename_info_buf);
|
||||
|
||||
rename_info.* = .{
|
||||
.ReplaceIfExists = ReplaceIfExists,
|
||||
.RootDirectory = if (std.fs.path.isAbsoluteWindowsW(new_path_w)) null else new_dir_fd,
|
||||
.FileNameLength = @intCast(u32, new_path.len * 2), // already checked error.NameTooLong
|
||||
.FileName = undefined,
|
||||
};
|
||||
std.mem.copy(u16, @as([*]u16, &rename_info.FileName)[0..new_path.len], new_path);
|
||||
|
||||
var io_status_block: windows.IO_STATUS_BLOCK = undefined;
|
||||
|
||||
const rc = windows.ntdll.NtSetInformationFile(
|
||||
src_fd,
|
||||
&io_status_block,
|
||||
rename_info,
|
||||
@intCast(u32, struct_len), // already checked for error.NameTooLong
|
||||
.FileRenameInformation,
|
||||
);
|
||||
|
||||
switch (rc) {
|
||||
.SUCCESS => return,
|
||||
.INVALID_HANDLE => unreachable,
|
||||
.INVALID_PARAMETER => unreachable,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.OBJECT_NAME_NOT_FOUND => return error.FileNotFound,
|
||||
.OBJECT_PATH_NOT_FOUND => return error.FileNotFound,
|
||||
else => return windows.unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const MakeDirError = error{
|
||||
AccessDenied,
|
||||
DiskQuota,
|
||||
@ -2090,7 +2184,7 @@ const ListenError = error{
|
||||
OperationNotSupported,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn listen(sockfd: i32, backlog: u32) ListenError!void {
|
||||
pub fn listen(sockfd: fd_t, backlog: u32) ListenError!void {
|
||||
const rc = system.listen(sockfd, backlog);
|
||||
switch (errno(rc)) {
|
||||
0 => return,
|
||||
@ -2381,7 +2475,7 @@ pub fn connect(sockfd: fd_t, sock_addr: *const sockaddr, len: socklen_t) Connect
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getsockoptError(sockfd: i32) ConnectError!void {
|
||||
pub fn getsockoptError(sockfd: fd_t) ConnectError!void {
|
||||
var err_code: u32 = undefined;
|
||||
var size: u32 = @sizeOf(u32);
|
||||
const rc = system.getsockopt(sockfd, SOL_SOCKET, SO_ERROR, @ptrCast([*]u8, &err_code), &size);
|
||||
@ -3069,6 +3163,31 @@ pub fn lseek_CUR_get(fd: fd_t) SeekError!u64 {
|
||||
}
|
||||
}
|
||||
|
||||
pub const FcntlError = error{
|
||||
PermissionDenied,
|
||||
FileBusy,
|
||||
ProcessFdQuotaExceeded,
|
||||
Locked,
|
||||
} || UnexpectedError;
|
||||
|
||||
pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) FcntlError!usize {
|
||||
while (true) {
|
||||
const rc = system.fcntl(fd, cmd, arg);
|
||||
switch (errno(rc)) {
|
||||
0 => return @intCast(usize, rc),
|
||||
EINTR => continue,
|
||||
EACCES => return error.Locked,
|
||||
EBADF => unreachable,
|
||||
EBUSY => return error.FileBusy,
|
||||
EINVAL => unreachable, // invalid parameters
|
||||
EPERM => return error.PermissionDenied,
|
||||
EMFILE => return error.ProcessFdQuotaExceeded,
|
||||
ENOTDIR => unreachable, // invalid parameter
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub const RealPathError = error{
|
||||
FileNotFound,
|
||||
AccessDenied,
|
||||
@ -3143,6 +3262,7 @@ pub fn realpathC(pathname: [*:0]const u8, out_buffer: *[MAX_PATH_BYTES]u8) RealP
|
||||
}
|
||||
|
||||
/// 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,
|
||||
|
||||
@ -283,6 +283,8 @@ pub const F_LOCK = 1;
|
||||
pub const F_TLOCK = 2;
|
||||
pub const F_TEST = 3;
|
||||
|
||||
pub const FD_CLOEXEC = 1;
|
||||
|
||||
pub const AT_FDCWD = -328243;
|
||||
pub const AT_SYMLINK_NOFOLLOW = 1;
|
||||
pub const AT_REMOVEDIR = 2;
|
||||
|
||||
@ -372,6 +372,8 @@ pub const F_GETOWN_EX = 16;
|
||||
|
||||
pub const F_GETOWNER_UIDS = 17;
|
||||
|
||||
pub const FD_CLOEXEC = 1;
|
||||
|
||||
pub const SEEK_SET = 0;
|
||||
pub const SEEK_CUR = 1;
|
||||
pub const SEEK_END = 2;
|
||||
|
||||
@ -136,6 +136,8 @@ pub const MAP_FIXED_NOREPLACE = 0x100000;
|
||||
/// For anonymous mmap, memory could be uninitialized
|
||||
pub const MAP_UNINITIALIZED = 0x4000000;
|
||||
|
||||
pub const FD_CLOEXEC = 1;
|
||||
|
||||
pub const F_OK = 0;
|
||||
pub const X_OK = 1;
|
||||
pub const W_OK = 2;
|
||||
|
||||
@ -327,6 +327,8 @@ pub const F_RDLCK = 1;
|
||||
pub const F_WRLCK = 3;
|
||||
pub const F_UNLCK = 2;
|
||||
|
||||
pub const FD_CLOEXEC = 1;
|
||||
|
||||
pub const SEEK_SET = 0;
|
||||
pub const SEEK_CUR = 1;
|
||||
pub const SEEK_END = 2;
|
||||
|
||||
@ -219,10 +219,6 @@ pub fn mmap(address: ?[*]u8, length: usize, prot: usize, flags: u32, fd: i32, of
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fcntl(fd: fd_t, cmd: i32, arg: ?*c_void) usize {
|
||||
return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), @ptrToInt(arg));
|
||||
}
|
||||
|
||||
pub fn mprotect(address: [*]const u8, length: usize, protection: usize) usize {
|
||||
return syscall3(SYS_mprotect, @ptrToInt(address), length, protection);
|
||||
}
|
||||
@ -469,17 +465,17 @@ pub fn renameat(oldfd: i32, oldpath: [*]const u8, newfd: i32, newpath: [*]const
|
||||
return syscall4(
|
||||
SYS_renameat,
|
||||
@bitCast(usize, @as(isize, oldfd)),
|
||||
@ptrToInt(old),
|
||||
@ptrToInt(oldpath),
|
||||
@bitCast(usize, @as(isize, newfd)),
|
||||
@ptrToInt(new),
|
||||
@ptrToInt(newpath),
|
||||
);
|
||||
} else {
|
||||
return syscall5(
|
||||
SYS_renameat2,
|
||||
@bitCast(usize, @as(isize, oldfd)),
|
||||
@ptrToInt(old),
|
||||
@ptrToInt(oldpath),
|
||||
@bitCast(usize, @as(isize, newfd)),
|
||||
@ptrToInt(new),
|
||||
@ptrToInt(newpath),
|
||||
0,
|
||||
);
|
||||
}
|
||||
@ -592,6 +588,10 @@ pub fn waitpid(pid: pid_t, status: *u32, flags: u32) usize {
|
||||
return syscall4(SYS_wait4, @bitCast(usize, @as(isize, pid)), @ptrToInt(status), flags, 0);
|
||||
}
|
||||
|
||||
pub fn fcntl(fd: fd_t, cmd: i32, arg: usize) usize {
|
||||
return syscall3(SYS_fcntl, @bitCast(usize, @as(isize, fd)), @bitCast(usize, @as(isize, cmd)), arg);
|
||||
}
|
||||
|
||||
var vdso_clock_gettime = @ptrCast(?*const c_void, init_vdso_clock_gettime);
|
||||
|
||||
// We must follow the C calling convention when we call into the VDSO
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
const std = @import("../std.zig");
|
||||
const os = std.os;
|
||||
const testing = std.testing;
|
||||
const expect = std.testing.expect;
|
||||
const expect = testing.expect;
|
||||
const expectEqual = testing.expectEqual;
|
||||
const io = std.io;
|
||||
const fs = std.fs;
|
||||
const mem = std.mem;
|
||||
@ -19,8 +20,8 @@ test "makePath, put some files in it, deleteTree" {
|
||||
try fs.cwd().makePath("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c");
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "c" ++ fs.path.sep_str ++ "file.txt", "nonsense");
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "b" ++ fs.path.sep_str ++ "file2.txt", "blah");
|
||||
try fs.deleteTree("os_test_tmp");
|
||||
if (fs.cwd().openDirTraverse("os_test_tmp")) |dir| {
|
||||
try fs.cwd().deleteTree("os_test_tmp");
|
||||
if (fs.cwd().openDir("os_test_tmp", .{})) |dir| {
|
||||
@panic("expected error");
|
||||
} else |err| {
|
||||
expect(err == error.FileNotFound);
|
||||
@ -37,7 +38,7 @@ test "access file" {
|
||||
|
||||
try io.writeFile("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", "");
|
||||
try os.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", os.F_OK);
|
||||
try fs.deleteTree("os_test_tmp");
|
||||
try fs.cwd().deleteTree("os_test_tmp");
|
||||
}
|
||||
|
||||
fn testThreadIdFn(thread_id: *Thread.Id) void {
|
||||
@ -46,9 +47,9 @@ fn testThreadIdFn(thread_id: *Thread.Id) void {
|
||||
|
||||
test "sendfile" {
|
||||
try fs.cwd().makePath("os_test_tmp");
|
||||
defer fs.deleteTree("os_test_tmp") catch {};
|
||||
defer fs.cwd().deleteTree("os_test_tmp") catch {};
|
||||
|
||||
var dir = try fs.cwd().openDirList("os_test_tmp");
|
||||
var dir = try fs.cwd().openDir("os_test_tmp", .{});
|
||||
defer dir.close();
|
||||
|
||||
const line1 = "line1\n";
|
||||
@ -112,14 +113,16 @@ test "fs.copyFile" {
|
||||
const dest_file = "tmp_test_copy_file2.txt";
|
||||
const dest_file2 = "tmp_test_copy_file3.txt";
|
||||
|
||||
try fs.cwd().writeFile(src_file, data);
|
||||
defer fs.cwd().deleteFile(src_file) catch {};
|
||||
const cwd = fs.cwd();
|
||||
|
||||
try fs.copyFile(src_file, dest_file);
|
||||
defer fs.cwd().deleteFile(dest_file) catch {};
|
||||
try cwd.writeFile(src_file, data);
|
||||
defer cwd.deleteFile(src_file) catch {};
|
||||
|
||||
try fs.copyFileMode(src_file, dest_file2, File.default_mode);
|
||||
defer fs.cwd().deleteFile(dest_file2) catch {};
|
||||
try cwd.copyFile(src_file, cwd, dest_file, .{});
|
||||
defer cwd.deleteFile(dest_file) catch {};
|
||||
|
||||
try cwd.copyFile(src_file, cwd, dest_file2, .{ .override_mode = File.default_mode });
|
||||
defer cwd.deleteFile(dest_file2) catch {};
|
||||
|
||||
try expectFileContents(dest_file, data);
|
||||
try expectFileContents(dest_file2, data);
|
||||
@ -446,3 +449,32 @@ test "getenv" {
|
||||
expect(os.getenvZ("BOGUSDOESNOTEXISTENVVAR") == null);
|
||||
}
|
||||
}
|
||||
|
||||
test "fcntl" {
|
||||
if (builtin.os.tag == .windows)
|
||||
return error.SkipZigTest;
|
||||
|
||||
const test_out_file = "os_tmp_test";
|
||||
|
||||
const file = try fs.cwd().createFile(test_out_file, .{});
|
||||
defer {
|
||||
file.close();
|
||||
fs.cwd().deleteFile(test_out_file) catch {};
|
||||
}
|
||||
|
||||
// Note: The test assumes createFile opens the file with O_CLOEXEC
|
||||
{
|
||||
const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
|
||||
expect((flags & os.FD_CLOEXEC) != 0);
|
||||
}
|
||||
{
|
||||
_ = try os.fcntl(file.handle, os.F_SETFD, 0);
|
||||
const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
|
||||
expect((flags & os.FD_CLOEXEC) == 0);
|
||||
}
|
||||
{
|
||||
_ = try os.fcntl(file.handle, os.F_SETFD, os.FD_CLOEXEC);
|
||||
const flags = try os.fcntl(file.handle, os.F_GETFD, 0);
|
||||
expect((flags & os.FD_CLOEXEC) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,6 +88,82 @@ pub fn CreateFileW(
|
||||
return result;
|
||||
}
|
||||
|
||||
pub const OpenError = error{
|
||||
IsDir,
|
||||
FileNotFound,
|
||||
NoDevice,
|
||||
SharingViolation,
|
||||
AccessDenied,
|
||||
PipeBusy,
|
||||
PathAlreadyExists,
|
||||
Unexpected,
|
||||
NameTooLong,
|
||||
};
|
||||
|
||||
/// TODO rename to CreateFileW
|
||||
/// TODO actually we don't need the path parameter to be null terminated
|
||||
pub fn OpenFileW(
|
||||
dir: ?HANDLE,
|
||||
sub_path_w: [*:0]const u16,
|
||||
sa: ?*SECURITY_ATTRIBUTES,
|
||||
access_mask: ACCESS_MASK,
|
||||
creation: ULONG,
|
||||
) OpenError!HANDLE {
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == 0) {
|
||||
return error.IsDir;
|
||||
}
|
||||
if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) {
|
||||
return error.IsDir;
|
||||
}
|
||||
|
||||
var result: HANDLE = undefined;
|
||||
|
||||
const path_len_bytes = math.cast(u16, mem.toSliceConst(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)),
|
||||
};
|
||||
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;
|
||||
const rc = ntdll.NtCreateFile(
|
||||
&result,
|
||||
access_mask,
|
||||
&attr,
|
||||
&io,
|
||||
null,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
|
||||
creation,
|
||||
FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
|
||||
null,
|
||||
0,
|
||||
);
|
||||
switch (rc) {
|
||||
.SUCCESS => return result,
|
||||
.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,
|
||||
.SHARING_VIOLATION => return error.SharingViolation,
|
||||
.ACCESS_DENIED => return error.AccessDenied,
|
||||
.PIPE_BUSY => return error.PipeBusy,
|
||||
.OBJECT_PATH_SYNTAX_BAD => unreachable,
|
||||
.OBJECT_NAME_COLLISION => return error.PathAlreadyExists,
|
||||
else => return unexpectedStatus(rc),
|
||||
}
|
||||
}
|
||||
|
||||
pub const CreatePipeError = error{Unexpected};
|
||||
|
||||
pub fn CreatePipe(rd: *HANDLE, wr: *HANDLE, sattr: *const SECURITY_ATTRIBUTES) CreatePipeError!void {
|
||||
@ -1200,7 +1276,15 @@ pub fn unexpectedError(err: Win32Error) std.os.UnexpectedError {
|
||||
// 614 is the length of the longest windows error desciption
|
||||
var buf_u16: [614]u16 = undefined;
|
||||
var buf_u8: [614]u8 = undefined;
|
||||
var len = kernel32.FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, err, MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT), buf_u16[0..].ptr, buf_u16.len / @sizeOf(TCHAR), null);
|
||||
const len = kernel32.FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
null,
|
||||
err,
|
||||
MAKELANGID(LANG.NEUTRAL, SUBLANG.DEFAULT),
|
||||
&buf_u16,
|
||||
buf_u16.len / @sizeOf(TCHAR),
|
||||
null,
|
||||
);
|
||||
_ = std.unicode.utf16leToUtf8(&buf_u8, buf_u16[0..len]) catch unreachable;
|
||||
std.debug.warn("error.Unexpected: GetLastError({}): {}\n", .{ @enumToInt(err), buf_u8[0..len] });
|
||||
std.debug.dumpCurrentStackTrace(null);
|
||||
|
||||
@ -242,6 +242,13 @@ pub const FILE_NAME_INFORMATION = extern struct {
|
||||
FileName: [1]WCHAR,
|
||||
};
|
||||
|
||||
pub const FILE_RENAME_INFORMATION = extern struct {
|
||||
ReplaceIfExists: BOOLEAN,
|
||||
RootDirectory: ?HANDLE,
|
||||
FileNameLength: ULONG,
|
||||
FileName: [1]WCHAR,
|
||||
};
|
||||
|
||||
pub const IO_STATUS_BLOCK = extern struct {
|
||||
// "DUMMYUNIONNAME" expands to "u"
|
||||
u: extern union {
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
// ```
|
||||
// var buf: [8]u8 = undefined;
|
||||
// try std.crypto.randomBytes(buf[0..]);
|
||||
// const seed = mem.readIntSliceLittle(u64, buf[0..8]);
|
||||
// const seed = mem.readIntLittle(u64, buf[0..8]);
|
||||
//
|
||||
// var r = DefaultPrng.init(seed);
|
||||
//
|
||||
|
||||
@ -69,23 +69,23 @@ test "floatundisf" {
|
||||
test__floatundisf(0, 0.0);
|
||||
test__floatundisf(1, 1.0);
|
||||
test__floatundisf(2, 2.0);
|
||||
test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62F);
|
||||
test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62F);
|
||||
test__floatundisf(0x8000008000000000, 0x1p+63F);
|
||||
test__floatundisf(0x8000010000000000, 0x1.000002p+63F);
|
||||
test__floatundisf(0x8000000000000000, 0x1p+63F);
|
||||
test__floatundisf(0x8000000000000001, 0x1p+63F);
|
||||
test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64F);
|
||||
test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64F);
|
||||
test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50F);
|
||||
test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50F);
|
||||
test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50F);
|
||||
test__floatundisf(0x7FFFFF8000000000, 0x1.FFFFFEp+62);
|
||||
test__floatundisf(0x7FFFFF0000000000, 0x1.FFFFFCp+62);
|
||||
test__floatundisf(0x8000008000000000, 0x1p+63);
|
||||
test__floatundisf(0x8000010000000000, 0x1.000002p+63);
|
||||
test__floatundisf(0x8000000000000000, 0x1p+63);
|
||||
test__floatundisf(0x8000000000000001, 0x1p+63);
|
||||
test__floatundisf(0xFFFFFFFFFFFFFFFE, 0x1p+64);
|
||||
test__floatundisf(0xFFFFFFFFFFFFFFFF, 0x1p+64);
|
||||
test__floatundisf(0x0007FB72E8000000, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72EA000000, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72EB000000, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72EBFFFFFF, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72EC000000, 0x1.FEDCBCp+50);
|
||||
test__floatundisf(0x0007FB72E8000001, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72E6000000, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72E7000000, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72E7FFFFFF, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72E4000001, 0x1.FEDCBAp+50);
|
||||
test__floatundisf(0x0007FB72E4000000, 0x1.FEDCB8p+50);
|
||||
}
|
||||
|
||||
@ -41,6 +41,10 @@ fn _DllMainCRTStartup(
|
||||
fdwReason: std.os.windows.DWORD,
|
||||
lpReserved: std.os.windows.LPVOID,
|
||||
) callconv(.Stdcall) std.os.windows.BOOL {
|
||||
if (!builtin.single_threaded) {
|
||||
_ = @import("start_windows_tls.zig");
|
||||
}
|
||||
|
||||
if (@hasDecl(root, "DllMain")) {
|
||||
return root.DllMain(hinstDLL, fdwReason, lpReserved);
|
||||
}
|
||||
|
||||
@ -6,6 +6,8 @@ const windows = std.os.windows;
|
||||
const c = std.c;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
const bad_startfn_ret = "expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'";
|
||||
|
||||
pub const Thread = struct {
|
||||
data: Data,
|
||||
|
||||
@ -158,15 +160,34 @@ pub const Thread = struct {
|
||||
};
|
||||
fn threadMain(raw_arg: windows.LPVOID) callconv(.C) windows.DWORD {
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @ptrCast(*Context, @alignCast(@alignOf(Context), raw_arg)).*;
|
||||
|
||||
switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
|
||||
.Int => {
|
||||
return startFn(arg);
|
||||
.NoReturn => {
|
||||
startFn(arg);
|
||||
},
|
||||
.Void => {
|
||||
startFn(arg);
|
||||
return 0;
|
||||
},
|
||||
else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
|
||||
.Int => |info| {
|
||||
if (info.bits != 8) {
|
||||
@compileError(bad_startfn_ret);
|
||||
}
|
||||
return startFn(arg);
|
||||
},
|
||||
.ErrorUnion => |info| {
|
||||
if (info.payload != void) {
|
||||
@compileError(bad_startfn_ret);
|
||||
}
|
||||
startFn(arg) catch |err| {
|
||||
std.debug.warn("error: {}\n", .{@errorName(err)});
|
||||
if (@errorReturnTrace()) |trace| {
|
||||
std.debug.dumpStackTrace(trace.*);
|
||||
}
|
||||
};
|
||||
return 0;
|
||||
},
|
||||
else => @compileError(bad_startfn_ret),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -202,14 +223,32 @@ pub const Thread = struct {
|
||||
const arg = if (@sizeOf(Context) == 0) {} else @intToPtr(*const Context, ctx_addr).*;
|
||||
|
||||
switch (@typeInfo(@TypeOf(startFn).ReturnType)) {
|
||||
.Int => {
|
||||
return startFn(arg);
|
||||
.NoReturn => {
|
||||
startFn(arg);
|
||||
},
|
||||
.Void => {
|
||||
startFn(arg);
|
||||
return 0;
|
||||
},
|
||||
else => @compileError("expected return type of startFn to be 'u8', 'noreturn', 'void', or '!void'"),
|
||||
.Int => |info| {
|
||||
if (info.bits != 8) {
|
||||
@compileError(bad_startfn_ret);
|
||||
}
|
||||
return startFn(arg);
|
||||
},
|
||||
.ErrorUnion => |info| {
|
||||
if (info.payload != void) {
|
||||
@compileError(bad_startfn_ret);
|
||||
}
|
||||
startFn(arg) catch |err| {
|
||||
std.debug.warn("error: {}\n", .{@errorName(err)});
|
||||
if (@errorReturnTrace()) |trace| {
|
||||
std.debug.dumpStackTrace(trace.*);
|
||||
}
|
||||
};
|
||||
return 0;
|
||||
},
|
||||
else => @compileError(bad_startfn_ret),
|
||||
}
|
||||
}
|
||||
fn posixThreadMain(ctx: ?*c_void) callconv(.C) ?*c_void {
|
||||
|
||||
@ -251,12 +251,12 @@ pub const Utf16LeIterator = struct {
|
||||
pub fn nextCodepoint(it: *Utf16LeIterator) !?u21 {
|
||||
assert(it.i <= it.bytes.len);
|
||||
if (it.i == it.bytes.len) return null;
|
||||
const c0: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]);
|
||||
const c0: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]);
|
||||
if (c0 & ~@as(u21, 0x03ff) == 0xd800) {
|
||||
// surrogate pair
|
||||
it.i += 2;
|
||||
if (it.i >= it.bytes.len) return error.DanglingSurrogateHalf;
|
||||
const c1: u21 = mem.readIntSliceLittle(u16, it.bytes[it.i .. it.i + 2]);
|
||||
const c1: u21 = mem.readIntLittle(u16, it.bytes[it.i..][0..2]);
|
||||
if (c1 & ~@as(u21, 0x03ff) != 0xdc00) return error.ExpectedSecondSurrogateHalf;
|
||||
it.i += 2;
|
||||
return 0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff));
|
||||
@ -630,11 +630,11 @@ test "utf8ToUtf16LeWithNull" {
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts a UTF-8 string literal into a UTF-16LE string literal.
|
||||
pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8) :0] u16 {
|
||||
/// Converts a UTF-8 string literal into a UTF-16LE string literal.
|
||||
pub fn utf8ToUtf16LeStringLiteral(comptime utf8: []const u8) *const [calcUtf16LeLen(utf8):0]u16 {
|
||||
comptime {
|
||||
const len: usize = calcUtf16LeLen(utf8);
|
||||
var utf16le: [len :0]u16 = [_ :0]u16{0} ** len;
|
||||
var utf16le: [len:0]u16 = [_:0]u16{0} ** len;
|
||||
const utf16le_len = utf8ToUtf16Le(&utf16le, utf8[0..]) catch |err| @compileError(err);
|
||||
assert(len == utf16le_len);
|
||||
return &utf16le;
|
||||
@ -660,8 +660,8 @@ fn calcUtf16LeLen(utf8: []const u8) usize {
|
||||
}
|
||||
|
||||
test "utf8ToUtf16LeStringLiteral" {
|
||||
{
|
||||
const bytes = [_:0]u16{ 0x41 };
|
||||
{
|
||||
const bytes = [_:0]u16{0x41};
|
||||
const utf16 = utf8ToUtf16LeStringLiteral("A");
|
||||
testing.expectEqualSlices(u16, &bytes, utf16);
|
||||
testing.expect(utf16[1] == 0);
|
||||
@ -673,19 +673,19 @@ test "utf8ToUtf16LeStringLiteral" {
|
||||
testing.expect(utf16[2] == 0);
|
||||
}
|
||||
{
|
||||
const bytes = [_:0]u16{ 0x02FF };
|
||||
const bytes = [_:0]u16{0x02FF};
|
||||
const utf16 = utf8ToUtf16LeStringLiteral("\u{02FF}");
|
||||
testing.expectEqualSlices(u16, &bytes, utf16);
|
||||
testing.expect(utf16[1] == 0);
|
||||
}
|
||||
{
|
||||
const bytes = [_:0]u16{ 0x7FF };
|
||||
const bytes = [_:0]u16{0x7FF};
|
||||
const utf16 = utf8ToUtf16LeStringLiteral("\u{7FF}");
|
||||
testing.expectEqualSlices(u16, &bytes, utf16);
|
||||
testing.expect(utf16[1] == 0);
|
||||
}
|
||||
{
|
||||
const bytes = [_:0]u16{ 0x801 };
|
||||
const bytes = [_:0]u16{0x801};
|
||||
const utf16 = utf8ToUtf16LeStringLiteral("\u{801}");
|
||||
testing.expectEqualSlices(u16, &bytes, utf16);
|
||||
testing.expect(utf16[1] == 0);
|
||||
|
||||
@ -740,11 +740,11 @@ pub const Node = struct {
|
||||
var i = index;
|
||||
|
||||
switch (self.init_arg_expr) {
|
||||
InitArg.Type => |t| {
|
||||
.Type => |t| {
|
||||
if (i < 1) return t;
|
||||
i -= 1;
|
||||
},
|
||||
InitArg.None, InitArg.Enum => {},
|
||||
.None, .Enum => {},
|
||||
}
|
||||
|
||||
if (i < self.fields_and_decls.len) return self.fields_and_decls.at(i).*;
|
||||
@ -904,12 +904,7 @@ pub const Node = struct {
|
||||
}
|
||||
|
||||
switch (self.return_type) {
|
||||
// TODO allow this and next prong to share bodies since the types are the same
|
||||
ReturnType.Explicit => |node| {
|
||||
if (i < 1) return node;
|
||||
i -= 1;
|
||||
},
|
||||
ReturnType.InferErrorSet => |node| {
|
||||
.Explicit, .InferErrorSet => |node| {
|
||||
if (i < 1) return node;
|
||||
i -= 1;
|
||||
},
|
||||
@ -934,9 +929,7 @@ pub const Node = struct {
|
||||
pub fn lastToken(self: *const FnProto) TokenIndex {
|
||||
if (self.body_node) |body_node| return body_node.lastToken();
|
||||
switch (self.return_type) {
|
||||
// TODO allow this and next prong to share bodies since the types are the same
|
||||
ReturnType.Explicit => |node| return node.lastToken(),
|
||||
ReturnType.InferErrorSet => |node| return node.lastToken(),
|
||||
.Explicit, .InferErrorSet => |node| return node.lastToken(),
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1039,6 +1032,7 @@ pub const Node = struct {
|
||||
pub const Defer = struct {
|
||||
base: Node = Node{ .id = .Defer },
|
||||
defer_token: TokenIndex,
|
||||
payload: ?*Node,
|
||||
expr: *Node,
|
||||
|
||||
pub fn iterate(self: *Defer, index: usize) ?*Node {
|
||||
@ -1512,55 +1506,55 @@ pub const Node = struct {
|
||||
i -= 1;
|
||||
|
||||
switch (self.op) {
|
||||
Op.Catch => |maybe_payload| {
|
||||
.Catch => |maybe_payload| {
|
||||
if (maybe_payload) |payload| {
|
||||
if (i < 1) return payload;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
|
||||
Op.Add,
|
||||
Op.AddWrap,
|
||||
Op.ArrayCat,
|
||||
Op.ArrayMult,
|
||||
Op.Assign,
|
||||
Op.AssignBitAnd,
|
||||
Op.AssignBitOr,
|
||||
Op.AssignBitShiftLeft,
|
||||
Op.AssignBitShiftRight,
|
||||
Op.AssignBitXor,
|
||||
Op.AssignDiv,
|
||||
Op.AssignSub,
|
||||
Op.AssignSubWrap,
|
||||
Op.AssignMod,
|
||||
Op.AssignAdd,
|
||||
Op.AssignAddWrap,
|
||||
Op.AssignMul,
|
||||
Op.AssignMulWrap,
|
||||
Op.BangEqual,
|
||||
Op.BitAnd,
|
||||
Op.BitOr,
|
||||
Op.BitShiftLeft,
|
||||
Op.BitShiftRight,
|
||||
Op.BitXor,
|
||||
Op.BoolAnd,
|
||||
Op.BoolOr,
|
||||
Op.Div,
|
||||
Op.EqualEqual,
|
||||
Op.ErrorUnion,
|
||||
Op.GreaterOrEqual,
|
||||
Op.GreaterThan,
|
||||
Op.LessOrEqual,
|
||||
Op.LessThan,
|
||||
Op.MergeErrorSets,
|
||||
Op.Mod,
|
||||
Op.Mul,
|
||||
Op.MulWrap,
|
||||
Op.Period,
|
||||
Op.Range,
|
||||
Op.Sub,
|
||||
Op.SubWrap,
|
||||
Op.UnwrapOptional,
|
||||
.Add,
|
||||
.AddWrap,
|
||||
.ArrayCat,
|
||||
.ArrayMult,
|
||||
.Assign,
|
||||
.AssignBitAnd,
|
||||
.AssignBitOr,
|
||||
.AssignBitShiftLeft,
|
||||
.AssignBitShiftRight,
|
||||
.AssignBitXor,
|
||||
.AssignDiv,
|
||||
.AssignSub,
|
||||
.AssignSubWrap,
|
||||
.AssignMod,
|
||||
.AssignAdd,
|
||||
.AssignAddWrap,
|
||||
.AssignMul,
|
||||
.AssignMulWrap,
|
||||
.BangEqual,
|
||||
.BitAnd,
|
||||
.BitOr,
|
||||
.BitShiftLeft,
|
||||
.BitShiftRight,
|
||||
.BitXor,
|
||||
.BoolAnd,
|
||||
.BoolOr,
|
||||
.Div,
|
||||
.EqualEqual,
|
||||
.ErrorUnion,
|
||||
.GreaterOrEqual,
|
||||
.GreaterThan,
|
||||
.LessOrEqual,
|
||||
.LessThan,
|
||||
.MergeErrorSets,
|
||||
.Mod,
|
||||
.Mul,
|
||||
.MulWrap,
|
||||
.Period,
|
||||
.Range,
|
||||
.Sub,
|
||||
.SubWrap,
|
||||
.UnwrapOptional,
|
||||
=> {},
|
||||
}
|
||||
|
||||
@ -1591,7 +1585,6 @@ pub const Node = struct {
|
||||
Await,
|
||||
BitNot,
|
||||
BoolNot,
|
||||
Cancel,
|
||||
OptionalType,
|
||||
Negation,
|
||||
NegationWrap,
|
||||
@ -1628,8 +1621,7 @@ pub const Node = struct {
|
||||
var i = index;
|
||||
|
||||
switch (self.op) {
|
||||
// TODO https://github.com/ziglang/zig/issues/1107
|
||||
Op.SliceType => |addr_of_info| {
|
||||
.PtrType, .SliceType => |addr_of_info| {
|
||||
if (addr_of_info.sentinel) |sentinel| {
|
||||
if (i < 1) return sentinel;
|
||||
i -= 1;
|
||||
@ -1641,14 +1633,7 @@ pub const Node = struct {
|
||||
}
|
||||
},
|
||||
|
||||
Op.PtrType => |addr_of_info| {
|
||||
if (addr_of_info.align_info) |align_info| {
|
||||
if (i < 1) return align_info.node;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
|
||||
Op.ArrayType => |array_info| {
|
||||
.ArrayType => |array_info| {
|
||||
if (i < 1) return array_info.len_expr;
|
||||
i -= 1;
|
||||
if (array_info.sentinel) |sentinel| {
|
||||
@ -1657,16 +1642,15 @@ pub const Node = struct {
|
||||
}
|
||||
},
|
||||
|
||||
Op.AddressOf,
|
||||
Op.Await,
|
||||
Op.BitNot,
|
||||
Op.BoolNot,
|
||||
Op.Cancel,
|
||||
Op.OptionalType,
|
||||
Op.Negation,
|
||||
Op.NegationWrap,
|
||||
Op.Try,
|
||||
Op.Resume,
|
||||
.AddressOf,
|
||||
.Await,
|
||||
.BitNot,
|
||||
.BoolNot,
|
||||
.OptionalType,
|
||||
.Negation,
|
||||
.NegationWrap,
|
||||
.Try,
|
||||
.Resume,
|
||||
=> {},
|
||||
}
|
||||
|
||||
@ -1850,19 +1834,13 @@ pub const Node = struct {
|
||||
var i = index;
|
||||
|
||||
switch (self.kind) {
|
||||
Kind.Break => |maybe_label| {
|
||||
.Break, .Continue => |maybe_label| {
|
||||
if (maybe_label) |label| {
|
||||
if (i < 1) return label;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
Kind.Continue => |maybe_label| {
|
||||
if (maybe_label) |label| {
|
||||
if (i < 1) return label;
|
||||
i -= 1;
|
||||
}
|
||||
},
|
||||
Kind.Return => {},
|
||||
.Return => {},
|
||||
}
|
||||
|
||||
if (self.rhs) |rhs| {
|
||||
@ -1883,17 +1861,12 @@ pub const Node = struct {
|
||||
}
|
||||
|
||||
switch (self.kind) {
|
||||
Kind.Break => |maybe_label| {
|
||||
.Break, .Continue => |maybe_label| {
|
||||
if (maybe_label) |label| {
|
||||
return label.lastToken();
|
||||
}
|
||||
},
|
||||
Kind.Continue => |maybe_label| {
|
||||
if (maybe_label) |label| {
|
||||
return label.lastToken();
|
||||
}
|
||||
},
|
||||
Kind.Return => return self.ltoken,
|
||||
.Return => return self.ltoken,
|
||||
}
|
||||
|
||||
return self.ltoken;
|
||||
@ -2134,11 +2107,11 @@ pub const Node = struct {
|
||||
i -= 1;
|
||||
|
||||
switch (self.kind) {
|
||||
Kind.Variable => |variable_name| {
|
||||
.Variable => |variable_name| {
|
||||
if (i < 1) return &variable_name.base;
|
||||
i -= 1;
|
||||
},
|
||||
Kind.Return => |return_type| {
|
||||
.Return => |return_type| {
|
||||
if (i < 1) return return_type;
|
||||
i -= 1;
|
||||
},
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,16 @@
|
||||
test "zig fmt: errdefer with payload" {
|
||||
try testCanonical(
|
||||
\\pub fn main() anyerror!void {
|
||||
\\ errdefer |a| x += 1;
|
||||
\\ errdefer |a| {}
|
||||
\\ errdefer |a| {
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: noasync block" {
|
||||
try testCanonical(
|
||||
\\pub fn main() anyerror!void {
|
||||
@ -1509,6 +1522,8 @@ test "zig fmt: error set declaration" {
|
||||
\\const Error = error{OutOfMemory};
|
||||
\\const Error = error{};
|
||||
\\
|
||||
\\const Error = error{ OutOfMemory, OutOfTime };
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
@ -2800,6 +2815,75 @@ test "zig fmt: extern without container keyword returns error" {
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: integer literals with underscore separators" {
|
||||
try testTransform(
|
||||
\\const
|
||||
\\ x =
|
||||
\\ 1_234_567
|
||||
\\ +(0b0_1-0o7_0+0xff_FF ) + 0_0;
|
||||
,
|
||||
\\const x = 1_234_567 + (0b0_1 - 0o7_0 + 0xff_FF) + 0_0;
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: hex literals with underscore separators" {
|
||||
try testTransform(
|
||||
\\pub fn orMask(a: [ 1_000 ]u64, b: [ 1_000] u64) [1_000]u64 {
|
||||
\\ var c: [1_000]u64 = [1]u64{ 0xFFFF_FFFF_FFFF_FFFF}**1_000;
|
||||
\\ for (c [ 0_0 .. ]) |_, i| {
|
||||
\\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
|
||||
\\ }
|
||||
\\ return c;
|
||||
\\}
|
||||
\\
|
||||
\\
|
||||
,
|
||||
\\pub fn orMask(a: [1_000]u64, b: [1_000]u64) [1_000]u64 {
|
||||
\\ var c: [1_000]u64 = [1]u64{0xFFFF_FFFF_FFFF_FFFF} ** 1_000;
|
||||
\\ for (c[0_0..]) |_, i| {
|
||||
\\ c[i] = (a[i] | b[i]) & 0xCCAA_CCAA_CCAA_CCAA;
|
||||
\\ }
|
||||
\\ return c;
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: decimal float literals with underscore separators" {
|
||||
try testTransform(
|
||||
\\pub fn main() void {
|
||||
\\ const a:f64=(10.0e-0+(10.e+0))+10_00.00_00e-2+00_00.00_10e+4;
|
||||
\\ const b:f64=010.0--0_10.+0_1_0.0_0+1e2;
|
||||
\\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
|
||||
\\}
|
||||
,
|
||||
\\pub fn main() void {
|
||||
\\ const a: f64 = (10.0e-0 + (10.e+0)) + 10_00.00_00e-2 + 00_00.00_10e+4;
|
||||
\\ const b: f64 = 010.0 - -0_10. + 0_1_0.0_0 + 1e2;
|
||||
\\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
test "zig fmt: hexadeciaml float literals with underscore separators" {
|
||||
try testTransform(
|
||||
\\pub fn main() void {
|
||||
\\ const a: f64 = (0x10.0p-0+(0x10.p+0))+0x10_00.00_00p-8+0x00_00.00_10p+16;
|
||||
\\ const b: f64 = 0x0010.0--0x00_10.+0x10.00+0x1p4;
|
||||
\\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
|
||||
\\}
|
||||
,
|
||||
\\pub fn main() void {
|
||||
\\ const a: f64 = (0x10.0p-0 + (0x10.p+0)) + 0x10_00.00_00p-8 + 0x00_00.00_10p+16;
|
||||
\\ const b: f64 = 0x0010.0 - -0x00_10. + 0x10.00 + 0x1p4;
|
||||
\\ std.debug.warn("a: {}, b: {} -> a+b: {}\n", .{ a, b, a + b });
|
||||
\\}
|
||||
\\
|
||||
);
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const warn = std.debug.warn;
|
||||
|
||||
@ -376,6 +376,9 @@ fn renderExpression(
|
||||
const defer_node = @fieldParentPtr(ast.Node.Defer, "base", base);
|
||||
|
||||
try renderToken(tree, stream, defer_node.defer_token, indent, start_col, Space.Space);
|
||||
if (defer_node.payload) |payload| {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
|
||||
}
|
||||
return renderExpression(allocator, stream, tree, indent, start_col, defer_node.expr, space);
|
||||
},
|
||||
.Comptime => {
|
||||
@ -583,7 +586,6 @@ fn renderExpression(
|
||||
},
|
||||
|
||||
.Try,
|
||||
.Cancel,
|
||||
.Resume,
|
||||
=> {
|
||||
try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space);
|
||||
@ -1269,25 +1271,51 @@ fn renderExpression(
|
||||
}
|
||||
|
||||
try renderToken(tree, stream, err_set_decl.error_token, indent, start_col, Space.None); // error
|
||||
try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // {
|
||||
const new_indent = indent + indent_delta;
|
||||
|
||||
var it = err_set_decl.decls.iterator(0);
|
||||
while (it.next()) |node| {
|
||||
try stream.writeByteNTimes(' ', new_indent);
|
||||
const src_has_trailing_comma = blk: {
|
||||
const maybe_comma = tree.prevToken(err_set_decl.rbrace_token);
|
||||
break :blk tree.tokens.at(maybe_comma).id == .Comma;
|
||||
};
|
||||
|
||||
if (it.peek()) |next_node| {
|
||||
try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None);
|
||||
try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // ,
|
||||
if (src_has_trailing_comma) {
|
||||
try renderToken(tree, stream, lbrace, indent, start_col, Space.Newline); // {
|
||||
const new_indent = indent + indent_delta;
|
||||
|
||||
try renderExtraNewline(tree, stream, start_col, next_node.*);
|
||||
} else {
|
||||
try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma);
|
||||
var it = err_set_decl.decls.iterator(0);
|
||||
while (it.next()) |node| {
|
||||
try stream.writeByteNTimes(' ', new_indent);
|
||||
|
||||
if (it.peek()) |next_node| {
|
||||
try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.None);
|
||||
try renderToken(tree, stream, tree.nextToken(node.*.lastToken()), new_indent, start_col, Space.Newline); // ,
|
||||
|
||||
try renderExtraNewline(tree, stream, start_col, next_node.*);
|
||||
} else {
|
||||
try renderExpression(allocator, stream, tree, new_indent, start_col, node.*, Space.Comma);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
|
||||
} else {
|
||||
try renderToken(tree, stream, lbrace, indent, start_col, Space.Space); // {
|
||||
|
||||
var it = err_set_decl.decls.iterator(0);
|
||||
while (it.next()) |node| {
|
||||
if (it.peek()) |next_node| {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);
|
||||
|
||||
const comma_token = tree.nextToken(node.*.lastToken());
|
||||
assert(tree.tokens.at(comma_token).id == .Comma);
|
||||
try renderToken(tree, stream, comma_token, indent, start_col, Space.Space); // ,
|
||||
try renderExtraNewline(tree, stream, start_col, next_node.*);
|
||||
} else {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Space);
|
||||
}
|
||||
}
|
||||
|
||||
return renderToken(tree, stream, err_set_decl.rbrace_token, indent, start_col, space); // }
|
||||
}
|
||||
},
|
||||
|
||||
.ErrorTag => {
|
||||
@ -1590,8 +1618,7 @@ fn renderExpression(
|
||||
}
|
||||
} else {
|
||||
var it = switch_case.items.iterator(0);
|
||||
while (true) {
|
||||
const node = it.next().?;
|
||||
while (it.next()) |node| {
|
||||
if (it.peek()) |next_node| {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.None);
|
||||
|
||||
@ -1602,7 +1629,6 @@ fn renderExpression(
|
||||
} else {
|
||||
try renderExpression(allocator, stream, tree, indent, start_col, node.*, Space.Comma);
|
||||
try stream.writeByteNTimes(' ', indent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -468,6 +468,9 @@ pub const NativeTargetInfo = struct {
|
||||
error.InvalidUtf8 => unreachable,
|
||||
error.BadPathName => unreachable,
|
||||
error.PipeBusy => unreachable,
|
||||
error.PermissionDenied => unreachable,
|
||||
error.FileBusy => unreachable,
|
||||
error.Locked => unreachable,
|
||||
|
||||
error.IsDir,
|
||||
error.NotDir,
|
||||
@ -754,7 +757,7 @@ pub const NativeTargetInfo = struct {
|
||||
const rpath_list = mem.toSliceConst(u8, @ptrCast([*:0]u8, strtab[rpoff..].ptr));
|
||||
var it = mem.tokenize(rpath_list, ":");
|
||||
while (it.next()) |rpath| {
|
||||
var dir = fs.cwd().openDirList(rpath) catch |err| switch (err) {
|
||||
var dir = fs.cwd().openDir(rpath, .{}) catch |err| switch (err) {
|
||||
error.NameTooLong => unreachable,
|
||||
error.InvalidUtf8 => unreachable,
|
||||
error.BadPathName => unreachable,
|
||||
|
||||
@ -387,17 +387,23 @@ pub const Tokenizer = struct {
|
||||
DocComment,
|
||||
ContainerDocComment,
|
||||
Zero,
|
||||
IntegerLiteral,
|
||||
IntegerLiteralWithRadix,
|
||||
IntegerLiteralWithRadixHex,
|
||||
NumberDot,
|
||||
IntegerLiteralDec,
|
||||
IntegerLiteralDecNoUnderscore,
|
||||
IntegerLiteralBin,
|
||||
IntegerLiteralBinNoUnderscore,
|
||||
IntegerLiteralOct,
|
||||
IntegerLiteralOctNoUnderscore,
|
||||
IntegerLiteralHex,
|
||||
IntegerLiteralHexNoUnderscore,
|
||||
NumberDotDec,
|
||||
NumberDotHex,
|
||||
FloatFraction,
|
||||
FloatFractionDec,
|
||||
FloatFractionDecNoUnderscore,
|
||||
FloatFractionHex,
|
||||
FloatFractionHexNoUnderscore,
|
||||
FloatExponentUnsigned,
|
||||
FloatExponentUnsignedHex,
|
||||
FloatExponentNumber,
|
||||
FloatExponentNumberHex,
|
||||
FloatExponentNumberNoUnderscore,
|
||||
Ampersand,
|
||||
Caret,
|
||||
Percent,
|
||||
@ -412,6 +418,10 @@ pub const Tokenizer = struct {
|
||||
SawAtSign,
|
||||
};
|
||||
|
||||
fn isIdentifierChar(char: u8) bool {
|
||||
return std.ascii.isAlNum(char) or char == '_';
|
||||
}
|
||||
|
||||
pub fn next(self: *Tokenizer) Token {
|
||||
if (self.pending_invalid_token) |token| {
|
||||
self.pending_invalid_token = null;
|
||||
@ -550,7 +560,7 @@ pub const Tokenizer = struct {
|
||||
result.id = Token.Id.IntegerLiteral;
|
||||
},
|
||||
'1'...'9' => {
|
||||
state = State.IntegerLiteral;
|
||||
state = State.IntegerLiteralDec;
|
||||
result.id = Token.Id.IntegerLiteral;
|
||||
},
|
||||
else => {
|
||||
@ -1048,55 +1058,145 @@ pub const Tokenizer = struct {
|
||||
else => self.checkLiteralCharacter(),
|
||||
},
|
||||
State.Zero => switch (c) {
|
||||
'b', 'o' => {
|
||||
state = State.IntegerLiteralWithRadix;
|
||||
'b' => {
|
||||
state = State.IntegerLiteralBinNoUnderscore;
|
||||
},
|
||||
'o' => {
|
||||
state = State.IntegerLiteralOctNoUnderscore;
|
||||
},
|
||||
'x' => {
|
||||
state = State.IntegerLiteralWithRadixHex;
|
||||
state = State.IntegerLiteralHexNoUnderscore;
|
||||
},
|
||||
'0'...'9', '_', '.', 'e', 'E' => {
|
||||
// reinterpret as a decimal number
|
||||
self.index -= 1;
|
||||
state = State.IntegerLiteralDec;
|
||||
},
|
||||
else => {
|
||||
// reinterpret as a normal number
|
||||
self.index -= 1;
|
||||
state = State.IntegerLiteral;
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteral => switch (c) {
|
||||
'.' => {
|
||||
state = State.NumberDot;
|
||||
State.IntegerLiteralBinNoUnderscore => switch (c) {
|
||||
'0'...'1' => {
|
||||
state = State.IntegerLiteralBin;
|
||||
},
|
||||
'p', 'P', 'e', 'E' => {
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteralBin => switch (c) {
|
||||
'_' => {
|
||||
state = State.IntegerLiteralBinNoUnderscore;
|
||||
},
|
||||
'0'...'1' => {},
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteralOctNoUnderscore => switch (c) {
|
||||
'0'...'7' => {
|
||||
state = State.IntegerLiteralOct;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteralOct => switch (c) {
|
||||
'_' => {
|
||||
state = State.IntegerLiteralOctNoUnderscore;
|
||||
},
|
||||
'0'...'7' => {},
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteralDecNoUnderscore => switch (c) {
|
||||
'0'...'9' => {
|
||||
state = State.IntegerLiteralDec;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteralDec => switch (c) {
|
||||
'_' => {
|
||||
state = State.IntegerLiteralDecNoUnderscore;
|
||||
},
|
||||
'.' => {
|
||||
state = State.NumberDotDec;
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
},
|
||||
'e', 'E' => {
|
||||
state = State.FloatExponentUnsigned;
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
},
|
||||
'0'...'9' => {},
|
||||
else => break,
|
||||
},
|
||||
State.IntegerLiteralWithRadix => switch (c) {
|
||||
'.' => {
|
||||
state = State.NumberDot;
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
'0'...'9' => {},
|
||||
else => break,
|
||||
},
|
||||
State.IntegerLiteralWithRadixHex => switch (c) {
|
||||
State.IntegerLiteralHexNoUnderscore => switch (c) {
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {
|
||||
state = State.IntegerLiteralHex;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.IntegerLiteralHex => switch (c) {
|
||||
'_' => {
|
||||
state = State.IntegerLiteralHexNoUnderscore;
|
||||
},
|
||||
'.' => {
|
||||
state = State.NumberDotHex;
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
},
|
||||
'p', 'P' => {
|
||||
state = State.FloatExponentUnsignedHex;
|
||||
state = State.FloatExponentUnsigned;
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
},
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {},
|
||||
else => break,
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.NumberDot => switch (c) {
|
||||
State.NumberDotDec => switch (c) {
|
||||
'.' => {
|
||||
self.index -= 1;
|
||||
state = State.Start;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
self.index -= 1;
|
||||
'e', 'E' => {
|
||||
state = State.FloatExponentUnsigned;
|
||||
},
|
||||
'0'...'9' => {
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
state = State.FloatFraction;
|
||||
state = State.FloatFractionDec;
|
||||
},
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.NumberDotHex => switch (c) {
|
||||
@ -1105,65 +1205,112 @@ pub const Tokenizer = struct {
|
||||
state = State.Start;
|
||||
break;
|
||||
},
|
||||
else => {
|
||||
self.index -= 1;
|
||||
'p', 'P' => {
|
||||
state = State.FloatExponentUnsigned;
|
||||
},
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {
|
||||
result.id = Token.Id.FloatLiteral;
|
||||
state = State.FloatFractionHex;
|
||||
},
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.FloatFraction => switch (c) {
|
||||
State.FloatFractionDecNoUnderscore => switch (c) {
|
||||
'0'...'9' => {
|
||||
state = State.FloatFractionDec;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.FloatFractionDec => switch (c) {
|
||||
'_' => {
|
||||
state = State.FloatFractionDecNoUnderscore;
|
||||
},
|
||||
'e', 'E' => {
|
||||
state = State.FloatExponentUnsigned;
|
||||
},
|
||||
'0'...'9' => {},
|
||||
else => break,
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.FloatFractionHexNoUnderscore => switch (c) {
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {
|
||||
state = State.FloatFractionHex;
|
||||
},
|
||||
else => {
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.FloatFractionHex => switch (c) {
|
||||
'_' => {
|
||||
state = State.FloatFractionHexNoUnderscore;
|
||||
},
|
||||
'p', 'P' => {
|
||||
state = State.FloatExponentUnsignedHex;
|
||||
state = State.FloatExponentUnsigned;
|
||||
},
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {},
|
||||
else => break,
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.FloatExponentUnsigned => switch (c) {
|
||||
'+', '-' => {
|
||||
state = State.FloatExponentNumber;
|
||||
state = State.FloatExponentNumberNoUnderscore;
|
||||
},
|
||||
else => {
|
||||
// reinterpret as a normal exponent number
|
||||
self.index -= 1;
|
||||
state = State.FloatExponentNumber;
|
||||
state = State.FloatExponentNumberNoUnderscore;
|
||||
},
|
||||
},
|
||||
State.FloatExponentUnsignedHex => switch (c) {
|
||||
'+', '-' => {
|
||||
state = State.FloatExponentNumberHex;
|
||||
State.FloatExponentNumberNoUnderscore => switch (c) {
|
||||
'0'...'9' => {
|
||||
state = State.FloatExponentNumber;
|
||||
},
|
||||
else => {
|
||||
// reinterpret as a normal exponent number
|
||||
self.index -= 1;
|
||||
state = State.FloatExponentNumberHex;
|
||||
result.id = Token.Id.Invalid;
|
||||
break;
|
||||
},
|
||||
},
|
||||
State.FloatExponentNumber => switch (c) {
|
||||
'_' => {
|
||||
state = State.FloatExponentNumberNoUnderscore;
|
||||
},
|
||||
'0'...'9' => {},
|
||||
else => break,
|
||||
},
|
||||
State.FloatExponentNumberHex => switch (c) {
|
||||
'0'...'9', 'a'...'f', 'A'...'F' => {},
|
||||
else => break,
|
||||
else => {
|
||||
if (isIdentifierChar(c)) {
|
||||
result.id = Token.Id.Invalid;
|
||||
}
|
||||
break;
|
||||
},
|
||||
},
|
||||
}
|
||||
} else if (self.index == self.buffer.len) {
|
||||
switch (state) {
|
||||
State.Start,
|
||||
State.IntegerLiteral,
|
||||
State.IntegerLiteralWithRadix,
|
||||
State.IntegerLiteralWithRadixHex,
|
||||
State.FloatFraction,
|
||||
State.IntegerLiteralDec,
|
||||
State.IntegerLiteralBin,
|
||||
State.IntegerLiteralOct,
|
||||
State.IntegerLiteralHex,
|
||||
State.NumberDotDec,
|
||||
State.NumberDotHex,
|
||||
State.FloatFractionDec,
|
||||
State.FloatFractionHex,
|
||||
State.FloatExponentNumber,
|
||||
State.FloatExponentNumberHex,
|
||||
State.StringLiteral, // find this error later
|
||||
State.MultilineStringLiteralLine,
|
||||
State.Builtin,
|
||||
@ -1184,10 +1331,14 @@ pub const Tokenizer = struct {
|
||||
result.id = Token.Id.ContainerDocComment;
|
||||
},
|
||||
|
||||
State.NumberDot,
|
||||
State.NumberDotHex,
|
||||
State.IntegerLiteralDecNoUnderscore,
|
||||
State.IntegerLiteralBinNoUnderscore,
|
||||
State.IntegerLiteralOctNoUnderscore,
|
||||
State.IntegerLiteralHexNoUnderscore,
|
||||
State.FloatFractionDecNoUnderscore,
|
||||
State.FloatFractionHexNoUnderscore,
|
||||
State.FloatExponentNumberNoUnderscore,
|
||||
State.FloatExponentUnsigned,
|
||||
State.FloatExponentUnsignedHex,
|
||||
State.SawAtSign,
|
||||
State.Backslash,
|
||||
State.CharLiteral,
|
||||
@ -1585,6 +1736,236 @@ test "correctly parse pointer assignment" {
|
||||
});
|
||||
}
|
||||
|
||||
test "tokenizer - number literals decimal" {
|
||||
testTokenize("0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("1", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("2", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("3", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("4", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("5", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("6", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("7", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("8", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("9", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0a", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("9b", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1z", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1z_1", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("9z3", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
|
||||
testTokenize("0_0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0001", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("01234567890", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("012_345_6789_0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0_1_2_3_4_5_6_7_8_9_0", &[_]Token.Id{.IntegerLiteral});
|
||||
|
||||
testTokenize("00_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0_0_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0__0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0_0f", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0_0_f", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0_0_f_00", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1_,", &[_]Token.Id{ .Invalid, .Comma });
|
||||
|
||||
testTokenize("1.", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0.0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1.0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("10.0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0e0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1e0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1e100", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1.e100", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1.0e100", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1.0e+100", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1.0e-100", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1_0_0_0.0_0_0_0_0_1e1_0_0_0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("1.+", &[_]Token.Id{ .FloatLiteral, .Plus });
|
||||
|
||||
testTokenize("1e", &[_]Token.Id{.Invalid});
|
||||
testTokenize("1.0e1f0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0p100", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0p-100", &[_]Token.Id{ .Invalid, .Identifier, .Minus, .IntegerLiteral });
|
||||
testTokenize("1.0p1f0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0_,", &[_]Token.Id{ .Invalid, .Comma });
|
||||
testTokenize("1_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });
|
||||
testTokenize("1._", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.a", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.z", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1._0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1._+", &[_]Token.Id{ .Invalid, .Identifier, .Plus });
|
||||
testTokenize("1._e", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0e", &[_]Token.Id{.Invalid});
|
||||
testTokenize("1.0e,", &[_]Token.Id{ .Invalid, .Comma });
|
||||
testTokenize("1.0e_", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0e+_", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0e-_", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("1.0e0_+", &[_]Token.Id{ .Invalid, .Plus });
|
||||
}
|
||||
|
||||
test "tokenizer - number literals binary" {
|
||||
testTokenize("0b0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0b1", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0b2", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b3", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b4", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b5", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b6", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b7", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b8", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0b9", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0ba", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0bb", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0bc", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0bd", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0be", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0bf", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0bz", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
|
||||
testTokenize("0b0000_0000", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0b1111_1111", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0b10_10_10_10", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0b0_1_0_1_0_1_0_1", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0b1.", &[_]Token.Id{ .IntegerLiteral, .Period });
|
||||
testTokenize("0b1.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });
|
||||
|
||||
testTokenize("0B0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b_", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b_0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b1_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0b0__1", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b0_1_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0b1e", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b1p", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b1e0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b1p0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0b1_,", &[_]Token.Id{ .Invalid, .Comma });
|
||||
}
|
||||
|
||||
test "tokenizer - number literals octal" {
|
||||
testTokenize("0o0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o1", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o2", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o3", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o4", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o5", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o6", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o7", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o8", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0o9", &[_]Token.Id{ .Invalid, .IntegerLiteral });
|
||||
testTokenize("0oa", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0ob", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0oc", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0od", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0oe", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0of", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0oz", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
|
||||
testTokenize("0o01234567", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o0123_4567", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o01_23_45_67", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o0_1_2_3_4_5_6_7", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0o7.", &[_]Token.Id{ .IntegerLiteral, .Period });
|
||||
testTokenize("0o7.0", &[_]Token.Id{ .IntegerLiteral, .Period, .IntegerLiteral });
|
||||
|
||||
testTokenize("0O0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o_", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o_0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o1_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0o0__1", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o0_1_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0o1e", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o1p", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o1e0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o1p0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0o_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });
|
||||
}
|
||||
|
||||
test "tokenizer - number literals hexadeciaml" {
|
||||
testTokenize("0x0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x1", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x2", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x3", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x4", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x5", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x6", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x7", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x8", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x9", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xa", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xb", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xc", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xd", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xe", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xf", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xA", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xB", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xC", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xD", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xE", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0xF", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x0z", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0xz", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
|
||||
testTokenize("0x0123456789ABCDEF", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x0123_4567_89AB_CDEF", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x01_23_45_67_89AB_CDE_F", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x0_1_2_3_4_5_6_7_8_9_A_B_C_D_E_F", &[_]Token.Id{.IntegerLiteral});
|
||||
|
||||
testTokenize("0X0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x_", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x_1", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x1_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0x0__1", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0_1_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0x_,", &[_]Token.Id{ .Invalid, .Identifier, .Comma });
|
||||
|
||||
testTokenize("0x1.", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x1.0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xF.", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xF.0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xF.F", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xF.Fp0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xF.FP0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x1p0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xfp0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x1.+0xF.", &[_]Token.Id{ .FloatLiteral, .Plus, .FloatLiteral });
|
||||
|
||||
testTokenize("0x0123456.789ABCDEF", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x0_123_456.789_ABC_DEF", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x0_1_2_3_4_5_6.7_8_9_A_B_C_D_E_F", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x0p0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0x0.0p0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xff.ffp10", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xff.ffP10", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xff.p10", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xffp10", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xff_ff.ff_ffp1_0_0_0", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xf_f_f_f.f_f_f_fp+1_000", &[_]Token.Id{.FloatLiteral});
|
||||
testTokenize("0xf_f_f_f.f_f_f_fp-1_00_0", &[_]Token.Id{.FloatLiteral});
|
||||
|
||||
testTokenize("0x1e", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x1e0", &[_]Token.Id{.IntegerLiteral});
|
||||
testTokenize("0x1p", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0xfp0z1", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0xff.ffpff", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.p", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0x0.z", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0._", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0_.0", &[_]Token.Id{ .Invalid, .Period, .IntegerLiteral });
|
||||
testTokenize("0x0_.0.0", &[_]Token.Id{ .Invalid, .Period, .FloatLiteral });
|
||||
testTokenize("0x0._0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.0_", &[_]Token.Id{.Invalid});
|
||||
testTokenize("0x0_p0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0_.p0", &[_]Token.Id{ .Invalid, .Period, .Identifier });
|
||||
testTokenize("0x0._p0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.0_p0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0._0p0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.0p_0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.0p+_0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.0p-_0", &[_]Token.Id{ .Invalid, .Identifier });
|
||||
testTokenize("0x0.0p0_", &[_]Token.Id{ .Invalid, .Eof });
|
||||
}
|
||||
|
||||
fn testTokenize(source: []const u8, expected_tokens: []const Token.Id) void {
|
||||
var tokenizer = Tokenizer.init(source);
|
||||
for (expected_tokens) |expected_token_id| {
|
||||
|
||||
@ -69,9 +69,9 @@ pub const CInt = struct {
|
||||
};
|
||||
|
||||
pub fn sizeInBits(cint: CInt, self: Target) u32 {
|
||||
const arch = self.getArch();
|
||||
const arch = self.cpu.arch;
|
||||
switch (self.os.tag) {
|
||||
.freestanding, .other => switch (self.getArch()) {
|
||||
.freestanding, .other => switch (self.cpu.arch) {
|
||||
.msp430 => switch (cint.id) {
|
||||
.Short,
|
||||
.UShort,
|
||||
@ -94,7 +94,7 @@ pub const CInt = struct {
|
||||
=> return 32,
|
||||
.Long,
|
||||
.ULong,
|
||||
=> return self.getArchPtrBitWidth(),
|
||||
=> return self.cpu.arch.ptrBitWidth(),
|
||||
.LongLong,
|
||||
.ULongLong,
|
||||
=> return 64,
|
||||
@ -114,7 +114,7 @@ pub const CInt = struct {
|
||||
=> return 32,
|
||||
.Long,
|
||||
.ULong,
|
||||
=> return self.getArchPtrBitWidth(),
|
||||
=> return self.cpu.arch.ptrBitWidth(),
|
||||
.LongLong,
|
||||
.ULongLong,
|
||||
=> return 64,
|
||||
|
||||
126
src-self-hosted/clang_options.zig
Normal file
126
src-self-hosted/clang_options.zig
Normal file
@ -0,0 +1,126 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
|
||||
pub const list = @import("clang_options_data.zig").data;
|
||||
|
||||
pub const CliArg = struct {
|
||||
name: []const u8,
|
||||
syntax: Syntax,
|
||||
|
||||
/// TODO we're going to want to change this when we start shipping self-hosted because this causes
|
||||
/// all the functions in stage2.zig to get exported.
|
||||
zig_equivalent: @import("stage2.zig").ClangArgIterator.ZigEquivalent,
|
||||
|
||||
/// Prefixed by "-"
|
||||
pd1: bool = false,
|
||||
|
||||
/// Prefixed by "--"
|
||||
pd2: bool = false,
|
||||
|
||||
/// Prefixed by "/"
|
||||
psl: bool = false,
|
||||
|
||||
pub const Syntax = union(enum) {
|
||||
/// A flag with no values.
|
||||
flag,
|
||||
|
||||
/// An option which prefixes its (single) value.
|
||||
joined,
|
||||
|
||||
/// An option which is followed by its value.
|
||||
separate,
|
||||
|
||||
/// An option which is either joined to its (non-empty) value, or followed by its value.
|
||||
joined_or_separate,
|
||||
|
||||
/// An option which is both joined to its (first) value, and followed by its (second) value.
|
||||
joined_and_separate,
|
||||
|
||||
/// An option followed by its values, which are separated by commas.
|
||||
comma_joined,
|
||||
|
||||
/// An option which consumes an optional joined argument and any other remaining arguments.
|
||||
remaining_args_joined,
|
||||
|
||||
/// An option which is which takes multiple (separate) arguments.
|
||||
multi_arg: u8,
|
||||
};
|
||||
|
||||
pub fn matchEql(self: CliArg, arg: []const u8) u2 {
|
||||
if (self.pd1 and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "-") and mem.eql(u8, arg[1..], self.name))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (self.pd2 and arg.len >= self.name.len + 2 and
|
||||
mem.startsWith(u8, arg, "--") and mem.eql(u8, arg[2..], self.name))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
if (self.psl and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "/") and mem.eql(u8, arg[1..], self.name))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
pub fn matchStartsWith(self: CliArg, arg: []const u8) usize {
|
||||
if (self.pd1 and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "-") and mem.startsWith(u8, arg[1..], self.name))
|
||||
{
|
||||
return self.name.len + 1;
|
||||
}
|
||||
if (self.pd2 and arg.len >= self.name.len + 2 and
|
||||
mem.startsWith(u8, arg, "--") and mem.startsWith(u8, arg[2..], self.name))
|
||||
{
|
||||
return self.name.len + 2;
|
||||
}
|
||||
if (self.psl and arg.len >= self.name.len + 1 and
|
||||
mem.startsWith(u8, arg, "/") and mem.startsWith(u8, arg[1..], self.name))
|
||||
{
|
||||
return self.name.len + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn flagpd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .flag,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn joinpd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .joined,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn jspd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .joined_or_separate,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
|
||||
/// Shortcut function for initializing a `CliArg`
|
||||
pub fn sepd1(name: []const u8) CliArg {
|
||||
return .{
|
||||
.name = name,
|
||||
.syntax = .separate,
|
||||
.zig_equivalent = .other,
|
||||
.pd1 = true,
|
||||
};
|
||||
}
|
||||
5702
src-self-hosted/clang_options_data.zig
Normal file
5702
src-self-hosted/clang_options_data.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -95,7 +95,7 @@ pub const ZigCompiler = struct {
|
||||
|
||||
pub fn getNativeLibC(self: *ZigCompiler) !*LibCInstallation {
|
||||
if (self.native_libc.start()) |ptr| return ptr;
|
||||
try self.native_libc.data.findNative(self.allocator);
|
||||
self.native_libc.data = try LibCInstallation.findNative(.{ .allocator = self.allocator });
|
||||
self.native_libc.resolve();
|
||||
return &self.native_libc.data;
|
||||
}
|
||||
@ -126,7 +126,7 @@ pub const Compilation = struct {
|
||||
name: Buffer,
|
||||
llvm_triple: Buffer,
|
||||
root_src_path: ?[]const u8,
|
||||
target: Target,
|
||||
target: std.Target,
|
||||
llvm_target: *llvm.Target,
|
||||
build_mode: builtin.Mode,
|
||||
zig_lib_dir: []const u8,
|
||||
@ -338,7 +338,7 @@ pub const Compilation = struct {
|
||||
zig_compiler: *ZigCompiler,
|
||||
name: []const u8,
|
||||
root_src_path: ?[]const u8,
|
||||
target: Target,
|
||||
target: std.zig.CrossTarget,
|
||||
kind: Kind,
|
||||
build_mode: builtin.Mode,
|
||||
is_static: bool,
|
||||
@ -370,13 +370,18 @@ pub const Compilation = struct {
|
||||
zig_compiler: *ZigCompiler,
|
||||
name: []const u8,
|
||||
root_src_path: ?[]const u8,
|
||||
target: Target,
|
||||
cross_target: std.zig.CrossTarget,
|
||||
kind: Kind,
|
||||
build_mode: builtin.Mode,
|
||||
is_static: bool,
|
||||
zig_lib_dir: []const u8,
|
||||
) !void {
|
||||
const allocator = zig_compiler.allocator;
|
||||
|
||||
// TODO merge this line with stage2.zig crossTargetToTarget
|
||||
const target_info = try std.zig.system.NativeTargetInfo.detect(std.heap.c_allocator, cross_target);
|
||||
const target = target_info.target;
|
||||
|
||||
var comp = Compilation{
|
||||
.arena_allocator = std.heap.ArenaAllocator.init(allocator),
|
||||
.zig_compiler = zig_compiler,
|
||||
@ -419,7 +424,7 @@ pub const Compilation = struct {
|
||||
.target_machine = undefined,
|
||||
.target_data_ref = undefined,
|
||||
.target_layout_str = undefined,
|
||||
.target_ptr_bits = target.getArchPtrBitWidth(),
|
||||
.target_ptr_bits = target.cpu.arch.ptrBitWidth(),
|
||||
|
||||
.root_package = undefined,
|
||||
.std_package = undefined,
|
||||
@ -440,7 +445,7 @@ pub const Compilation = struct {
|
||||
}
|
||||
|
||||
comp.name = try Buffer.init(comp.arena(), name);
|
||||
comp.llvm_triple = try util.getTriple(comp.arena(), target);
|
||||
comp.llvm_triple = try util.getLLVMTriple(comp.arena(), target);
|
||||
comp.llvm_target = try util.llvmTargetFromTriple(comp.llvm_triple);
|
||||
comp.zig_std_dir = try fs.path.join(comp.arena(), &[_][]const u8{ zig_lib_dir, "std" });
|
||||
|
||||
@ -451,17 +456,12 @@ pub const Compilation = struct {
|
||||
|
||||
const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC;
|
||||
|
||||
// LLVM creates invalid binaries on Windows sometimes.
|
||||
// See https://github.com/ziglang/zig/issues/508
|
||||
// As a workaround we do not use target native features on Windows.
|
||||
var target_specific_cpu_args: ?[*:0]u8 = null;
|
||||
var target_specific_cpu_features: ?[*:0]u8 = null;
|
||||
defer llvm.DisposeMessage(target_specific_cpu_args);
|
||||
defer llvm.DisposeMessage(target_specific_cpu_features);
|
||||
if (target == Target.Native and !target.isWindows()) {
|
||||
target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory;
|
||||
target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory;
|
||||
}
|
||||
|
||||
// TODO detect native CPU & features here
|
||||
|
||||
comp.target_machine = llvm.CreateTargetMachine(
|
||||
comp.llvm_target,
|
||||
@ -520,8 +520,7 @@ pub const Compilation = struct {
|
||||
|
||||
if (comp.tmp_dir.getOrNull()) |tmp_dir_result|
|
||||
if (tmp_dir_result.*) |tmp_dir| {
|
||||
// TODO evented I/O?
|
||||
fs.deleteTree(tmp_dir) catch {};
|
||||
fs.cwd().deleteTree(tmp_dir) catch {};
|
||||
} else |_| {};
|
||||
}
|
||||
|
||||
@ -1125,7 +1124,9 @@ pub const Compilation = struct {
|
||||
self.libc_link_lib = link_lib;
|
||||
|
||||
// get a head start on looking for the native libc
|
||||
if (self.target == Target.Native and self.override_libc == null) {
|
||||
// TODO this is missing a bunch of logic related to whether the target is native
|
||||
// and whether we can build libc
|
||||
if (self.override_libc == null) {
|
||||
try self.deinit_group.call(startFindingNativeLibC, .{self});
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,8 +164,7 @@ pub const Msg = struct {
|
||||
const realpath_copy = try mem.dupe(comp.gpa(), u8, tree_scope.root().realpath);
|
||||
errdefer comp.gpa().free(realpath_copy);
|
||||
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&tree_scope.tree.tokens, out_stream);
|
||||
try parse_error.render(&tree_scope.tree.tokens, text_buf.outStream());
|
||||
|
||||
const msg = try comp.gpa().create(Msg);
|
||||
msg.* = Msg{
|
||||
@ -204,8 +203,7 @@ pub const Msg = struct {
|
||||
const realpath_copy = try mem.dupe(allocator, u8, realpath);
|
||||
errdefer allocator.free(realpath_copy);
|
||||
|
||||
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
|
||||
try parse_error.render(&tree.tokens, out_stream);
|
||||
try parse_error.render(&tree.tokens, text_buf.outStream());
|
||||
|
||||
const msg = try allocator.create(Msg);
|
||||
msg.* = Msg{
|
||||
@ -272,7 +270,7 @@ pub const Msg = struct {
|
||||
});
|
||||
try stream.writeByteNTimes(' ', start_loc.column);
|
||||
try stream.writeByteNTimes('~', last_token.end - first_token.start);
|
||||
try stream.write("\n");
|
||||
try stream.writeAll("\n");
|
||||
}
|
||||
|
||||
pub fn printToFile(msg: *const Msg, file: fs.File, color: Color) !void {
|
||||
@ -281,7 +279,6 @@ pub const Msg = struct {
|
||||
.On => true,
|
||||
.Off => false,
|
||||
};
|
||||
var stream = &file.outStream().stream;
|
||||
return msg.printToStream(stream, color_on);
|
||||
return msg.printToStream(file.outStream(), color_on);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1099,7 +1099,6 @@ pub const Builder = struct {
|
||||
.Await => return error.Unimplemented,
|
||||
.BitNot => return error.Unimplemented,
|
||||
.BoolNot => return error.Unimplemented,
|
||||
.Cancel => return error.Unimplemented,
|
||||
.OptionalType => return error.Unimplemented,
|
||||
.Negation => return error.Unimplemented,
|
||||
.NegationWrap => return error.Unimplemented,
|
||||
@ -1188,6 +1187,7 @@ pub const Builder = struct {
|
||||
.ParamDecl => return error.Unimplemented,
|
||||
.FieldInitializer => return error.Unimplemented,
|
||||
.EnumLiteral => return error.Unimplemented,
|
||||
.Noasync => return error.Unimplemented,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1311,13 +1311,16 @@ pub const Builder = struct {
|
||||
var base: u8 = undefined;
|
||||
var rest: []const u8 = undefined;
|
||||
if (int_token.len >= 3 and int_token[0] == '0') {
|
||||
base = switch (int_token[1]) {
|
||||
'b' => 2,
|
||||
'o' => 8,
|
||||
'x' => 16,
|
||||
else => unreachable,
|
||||
};
|
||||
rest = int_token[2..];
|
||||
switch (int_token[1]) {
|
||||
'b' => base = 2,
|
||||
'o' => base = 8,
|
||||
'x' => base = 16,
|
||||
else => {
|
||||
base = 10;
|
||||
rest = int_token;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
base = 10;
|
||||
rest = int_token;
|
||||
|
||||
@ -280,7 +280,7 @@ pub const LibCInstallation = struct {
|
||||
// search in reverse order
|
||||
const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
|
||||
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
|
||||
var search_dir = fs.cwd().openDirList(search_path) catch |err| switch (err) {
|
||||
var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
@ -335,7 +335,7 @@ pub const LibCInstallation = struct {
|
||||
const stream = result_buf.outStream();
|
||||
try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
@ -382,7 +382,7 @@ pub const LibCInstallation = struct {
|
||||
const stream = result_buf.outStream();
|
||||
try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
@ -437,7 +437,7 @@ pub const LibCInstallation = struct {
|
||||
const stream = result_buf.outStream();
|
||||
try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
@ -475,7 +475,7 @@ pub const LibCInstallation = struct {
|
||||
|
||||
try result_buf.append("\\include");
|
||||
|
||||
var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
|
||||
var dir = fs.cwd().openDir(result_buf.toSliceConst(), .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
|
||||
@ -56,12 +56,13 @@ pub fn link(comp: *Compilation) !void {
|
||||
if (comp.haveLibC()) {
|
||||
// TODO https://github.com/ziglang/zig/issues/3190
|
||||
var libc = ctx.comp.override_libc orelse blk: {
|
||||
switch (comp.target) {
|
||||
Target.Native => {
|
||||
break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
|
||||
},
|
||||
else => return error.LibCRequiredButNotProvidedOrFound,
|
||||
}
|
||||
@panic("this code has bitrotted");
|
||||
//switch (comp.target) {
|
||||
// Target.Native => {
|
||||
// break :blk comp.zig_compiler.getNativeLibC() catch return error.LibCRequiredButNotProvidedOrFound;
|
||||
// },
|
||||
// else => return error.LibCRequiredButNotProvidedOrFound,
|
||||
//}
|
||||
};
|
||||
ctx.libc = libc;
|
||||
}
|
||||
@ -155,11 +156,11 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
//bool shared = !g->is_static && is_lib;
|
||||
//Buf *soname = nullptr;
|
||||
if (ctx.comp.is_static) {
|
||||
if (util.isArmOrThumb(ctx.comp.target)) {
|
||||
try ctx.args.append("-Bstatic");
|
||||
} else {
|
||||
try ctx.args.append("-static");
|
||||
}
|
||||
//if (util.isArmOrThumb(ctx.comp.target)) {
|
||||
// try ctx.args.append("-Bstatic");
|
||||
//} else {
|
||||
// try ctx.args.append("-static");
|
||||
//}
|
||||
}
|
||||
//} else if (shared) {
|
||||
// lj->args.append("-shared");
|
||||
@ -176,29 +177,24 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
|
||||
if (ctx.link_in_crt) {
|
||||
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
|
||||
const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
|
||||
try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
|
||||
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
|
||||
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
|
||||
try addPathJoin(ctx, ctx.libc.crt_dir.?, crt1o);
|
||||
try addPathJoin(ctx, ctx.libc.crt_dir.?, "crti.o");
|
||||
}
|
||||
|
||||
if (ctx.comp.haveLibC()) {
|
||||
try ctx.args.append("-L");
|
||||
// TODO addNullByte should probably return [:0]u8
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr));
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.crt_dir.?)).ptr));
|
||||
|
||||
try ctx.args.append("-L");
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr));
|
||||
|
||||
if (!ctx.comp.is_static) {
|
||||
const dl = blk: {
|
||||
if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
|
||||
if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;
|
||||
return error.LibCMissingDynamicLinker;
|
||||
};
|
||||
try ctx.args.append("-dynamic-linker");
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
|
||||
}
|
||||
//if (!ctx.comp.is_static) {
|
||||
// const dl = blk: {
|
||||
// //if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
|
||||
// //if (util.getDynamicLinkerPath(ctx.comp.target)) |dl| break :blk dl;
|
||||
// return error.LibCMissingDynamicLinker;
|
||||
// };
|
||||
// try ctx.args.append("-dynamic-linker");
|
||||
// try ctx.args.append(@ptrCast([*:0]const u8, (try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr));
|
||||
//}
|
||||
}
|
||||
|
||||
//if (shared) {
|
||||
@ -265,13 +261,12 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
|
||||
|
||||
// crt end
|
||||
if (ctx.link_in_crt) {
|
||||
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
|
||||
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
|
||||
try addPathJoin(ctx, ctx.libc.crt_dir.?, "crtn.o");
|
||||
}
|
||||
|
||||
if (ctx.comp.target != Target.Native) {
|
||||
try ctx.args.append("--allow-shlib-undefined");
|
||||
}
|
||||
//if (ctx.comp.target != Target.Native) {
|
||||
// try ctx.args.append("--allow-shlib-undefined");
|
||||
//}
|
||||
}
|
||||
|
||||
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
|
||||
@ -287,7 +282,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
try ctx.args.append("-DEBUG");
|
||||
}
|
||||
|
||||
switch (ctx.comp.target.getArch()) {
|
||||
switch (ctx.comp.target.cpu.arch) {
|
||||
.i386 => try ctx.args.append("-MACHINE:X86"),
|
||||
.x86_64 => try ctx.args.append("-MACHINE:X64"),
|
||||
.aarch64 => try ctx.args.append("-MACHINE:ARM"),
|
||||
@ -302,7 +297,7 @@ fn constructLinkerArgsCoff(ctx: *Context) !void {
|
||||
if (ctx.comp.haveLibC()) {
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.msvc_lib_dir.?})).ptr));
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.kernel32_lib_dir.?})).ptr));
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.lib_dir.?})).ptr));
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, (try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", .{ctx.libc.crt_dir.?})).ptr));
|
||||
}
|
||||
|
||||
if (ctx.link_in_crt) {
|
||||
@ -417,7 +412,7 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
}
|
||||
},
|
||||
.IPhoneOS => {
|
||||
if (ctx.comp.target.getArch() == .aarch64) {
|
||||
if (ctx.comp.target.cpu.arch == .aarch64) {
|
||||
// iOS does not need any crt1 files for arm64
|
||||
} else if (platform.versionLessThan(3, 1)) {
|
||||
try ctx.args.append("-lcrt1.o");
|
||||
@ -435,28 +430,29 @@ fn constructLinkerArgsMachO(ctx: *Context) !void {
|
||||
}
|
||||
try addFnObjects(ctx);
|
||||
|
||||
if (ctx.comp.target == Target.Native) {
|
||||
for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
|
||||
if (mem.eql(u8, lib.name, "c")) {
|
||||
// on Darwin, libSystem has libc in it, but also you have to use it
|
||||
// to make syscalls because the syscall numbers are not documented
|
||||
// and change between versions.
|
||||
// so we always link against libSystem
|
||||
try ctx.args.append("-lSystem");
|
||||
} else {
|
||||
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
|
||||
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
|
||||
} else {
|
||||
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
|
||||
try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try ctx.args.append("-undefined");
|
||||
try ctx.args.append("dynamic_lookup");
|
||||
}
|
||||
// TODO
|
||||
//if (ctx.comp.target == Target.Native) {
|
||||
// for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
|
||||
// if (mem.eql(u8, lib.name, "c")) {
|
||||
// // on Darwin, libSystem has libc in it, but also you have to use it
|
||||
// // to make syscalls because the syscall numbers are not documented
|
||||
// // and change between versions.
|
||||
// // so we always link against libSystem
|
||||
// try ctx.args.append("-lSystem");
|
||||
// } else {
|
||||
// if (mem.indexOfScalar(u8, lib.name, '/') == null) {
|
||||
// const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", .{lib.name});
|
||||
// try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
|
||||
// } else {
|
||||
// const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
|
||||
// try ctx.args.append(@ptrCast([*:0]const u8, arg.ptr));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//} else {
|
||||
// try ctx.args.append("-undefined");
|
||||
// try ctx.args.append("dynamic_lookup");
|
||||
//}
|
||||
|
||||
if (platform.kind == .MacOS) {
|
||||
if (platform.versionLessThan(10, 5)) {
|
||||
|
||||
@ -18,10 +18,6 @@ const Target = std.Target;
|
||||
const errmsg = @import("errmsg.zig");
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
|
||||
var stderr_file: fs.File = undefined;
|
||||
var stderr: *io.OutStream(fs.File.WriteError) = undefined;
|
||||
var stdout: *io.OutStream(fs.File.WriteError) = undefined;
|
||||
|
||||
pub const io_mode = .evented;
|
||||
|
||||
pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB
|
||||
@ -51,17 +47,14 @@ const Command = struct {
|
||||
pub fn main() !void {
|
||||
const allocator = std.heap.c_allocator;
|
||||
|
||||
stdout = &std.io.getStdOut().outStream().stream;
|
||||
|
||||
stderr_file = std.io.getStdErr();
|
||||
stderr = &stderr_file.outStream().stream;
|
||||
const stderr = io.getStdErr().outStream();
|
||||
|
||||
const args = try process.argsAlloc(allocator);
|
||||
defer process.argsFree(allocator, args);
|
||||
|
||||
if (args.len <= 1) {
|
||||
try stderr.write("expected command argument\n\n");
|
||||
try stderr.write(usage);
|
||||
try stderr.writeAll("expected command argument\n\n");
|
||||
try stderr.writeAll(usage);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -78,8 +71,8 @@ pub fn main() !void {
|
||||
} else if (mem.eql(u8, cmd, "libc")) {
|
||||
return cmdLibC(allocator, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "targets")) {
|
||||
const info = try std.zig.system.NativeTargetInfo.detect(allocator);
|
||||
defer info.deinit(allocator);
|
||||
const info = try std.zig.system.NativeTargetInfo.detect(allocator, .{});
|
||||
const stdout = io.getStdOut().outStream();
|
||||
return @import("print_targets.zig").cmdTargets(allocator, cmd_args, stdout, info.target);
|
||||
} else if (mem.eql(u8, cmd, "version")) {
|
||||
return cmdVersion(allocator, cmd_args);
|
||||
@ -91,7 +84,7 @@ pub fn main() !void {
|
||||
return cmdInternal(allocator, cmd_args);
|
||||
} else {
|
||||
try stderr.print("unknown command: {}\n\n", .{args[1]});
|
||||
try stderr.write(usage);
|
||||
try stderr.writeAll(usage);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
@ -156,6 +149,8 @@ const usage_build_generic =
|
||||
;
|
||||
|
||||
fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Compilation.Kind) !void {
|
||||
const stderr = io.getStdErr().outStream();
|
||||
|
||||
var color: errmsg.Color = .Auto;
|
||||
var build_mode: std.builtin.Mode = .Debug;
|
||||
var emit_bin = true;
|
||||
@ -208,11 +203,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
const arg = args[i];
|
||||
if (mem.startsWith(u8, arg, "-")) {
|
||||
if (mem.eql(u8, arg, "--help")) {
|
||||
try stdout.write(usage_build_generic);
|
||||
try io.getStdOut().writeAll(usage_build_generic);
|
||||
process.exit(0);
|
||||
} else if (mem.eql(u8, arg, "--color")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected [auto|on|off] after --color\n");
|
||||
try stderr.writeAll("expected [auto|on|off] after --color\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -229,7 +224,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--mode")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
|
||||
try stderr.writeAll("expected [Debug|ReleaseSafe|ReleaseFast|ReleaseSmall] after --mode\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -248,49 +243,49 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
}
|
||||
} else if (mem.eql(u8, arg, "--name")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after --name\n");
|
||||
try stderr.writeAll("expected parameter after --name\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
provided_name = args[i];
|
||||
} else if (mem.eql(u8, arg, "--ver-major")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after --ver-major\n");
|
||||
try stderr.writeAll("expected parameter after --ver-major\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
version.major = try std.fmt.parseInt(u32, args[i], 10);
|
||||
} else if (mem.eql(u8, arg, "--ver-minor")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after --ver-minor\n");
|
||||
try stderr.writeAll("expected parameter after --ver-minor\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
version.minor = try std.fmt.parseInt(u32, args[i], 10);
|
||||
} else if (mem.eql(u8, arg, "--ver-patch")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after --ver-patch\n");
|
||||
try stderr.writeAll("expected parameter after --ver-patch\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
version.patch = try std.fmt.parseInt(u32, args[i], 10);
|
||||
} else if (mem.eql(u8, arg, "--linker-script")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after --linker-script\n");
|
||||
try stderr.writeAll("expected parameter after --linker-script\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
linker_script = args[i];
|
||||
} else if (mem.eql(u8, arg, "--libc")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after --libc\n");
|
||||
try stderr.writeAll("expected parameter after --libc\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
libc_arg = args[i];
|
||||
} else if (mem.eql(u8, arg, "-mllvm")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after -mllvm\n");
|
||||
try stderr.writeAll("expected parameter after -mllvm\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -300,14 +295,14 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
try mllvm_flags.append(args[i]);
|
||||
} else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after -mmacosx-version-min\n");
|
||||
try stderr.writeAll("expected parameter after -mmacosx-version-min\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
macosx_version_min = args[i];
|
||||
} else if (mem.eql(u8, arg, "-mios-version-min")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected parameter after -mios-version-min\n");
|
||||
try stderr.writeAll("expected parameter after -mios-version-min\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -348,7 +343,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
linker_rdynamic = true;
|
||||
} else if (mem.eql(u8, arg, "--pkg-begin")) {
|
||||
if (i + 2 >= args.len) {
|
||||
try stderr.write("expected [name] [path] after --pkg-begin\n");
|
||||
try stderr.writeAll("expected [name] [path] after --pkg-begin\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -363,7 +358,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
if (cur_pkg.parent) |parent| {
|
||||
cur_pkg = parent;
|
||||
} else {
|
||||
try stderr.write("encountered --pkg-end with no matching --pkg-begin\n");
|
||||
try stderr.writeAll("encountered --pkg-end with no matching --pkg-begin\n");
|
||||
process.exit(1);
|
||||
}
|
||||
} else if (mem.startsWith(u8, arg, "-l")) {
|
||||
@ -411,18 +406,18 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
var it = mem.separate(basename, ".");
|
||||
break :blk it.next() orelse basename;
|
||||
} else {
|
||||
try stderr.write("--name [name] not provided and unable to infer\n");
|
||||
try stderr.writeAll("--name [name] not provided and unable to infer\n");
|
||||
process.exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
if (root_src_file == null and link_objects.len == 0 and assembly_files.len == 0) {
|
||||
try stderr.write("Expected source file argument or at least one --object or --assembly argument\n");
|
||||
try stderr.writeAll("Expected source file argument or at least one --object or --assembly argument\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (out_type == Compilation.Kind.Obj and link_objects.len != 0) {
|
||||
try stderr.write("When building an object file, --object arguments are invalid\n");
|
||||
try stderr.writeAll("When building an object file, --object arguments are invalid\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -440,7 +435,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
&zig_compiler,
|
||||
root_name,
|
||||
root_src_file,
|
||||
Target.Native,
|
||||
.{},
|
||||
out_type,
|
||||
build_mode,
|
||||
!is_dynamic,
|
||||
@ -478,7 +473,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
comp.linker_rdynamic = linker_rdynamic;
|
||||
|
||||
if (macosx_version_min != null and ios_version_min != null) {
|
||||
try stderr.write("-mmacosx-version-min and -mios-version-min options not allowed together\n");
|
||||
try stderr.writeAll("-mmacosx-version-min and -mios-version-min options not allowed together\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -501,6 +496,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
|
||||
}
|
||||
|
||||
fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
|
||||
const stderr_file = io.getStdErr();
|
||||
const stderr = stderr_file.outStream();
|
||||
var count: usize = 0;
|
||||
while (!comp.cancelled) {
|
||||
const build_event = comp.events.get();
|
||||
@ -551,7 +548,8 @@ const Fmt = struct {
|
||||
};
|
||||
|
||||
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
|
||||
libc.parse(allocator, libc_paths_file, stderr) catch |err| {
|
||||
const stderr = io.getStdErr().outStream();
|
||||
libc.* = LibCInstallation.parse(allocator, libc_paths_file, stderr) catch |err| {
|
||||
stderr.print("Unable to parse libc path file '{}': {}.\n" ++
|
||||
"Try running `zig libc` to see an example for the native target.\n", .{
|
||||
libc_paths_file,
|
||||
@ -562,6 +560,7 @@ fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_fil
|
||||
}
|
||||
|
||||
fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const stderr = io.getStdErr().outStream();
|
||||
switch (args.len) {
|
||||
0 => {},
|
||||
1 => {
|
||||
@ -582,10 +581,12 @@ fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
|
||||
stderr.print("unable to find libc: {}\n", .{@errorName(err)}) catch {};
|
||||
process.exit(1);
|
||||
};
|
||||
libc.render(stdout) catch process.exit(1);
|
||||
libc.render(io.getStdOut().outStream()) catch process.exit(1);
|
||||
}
|
||||
|
||||
fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const stderr_file = io.getStdErr();
|
||||
const stderr = stderr_file.outStream();
|
||||
var color: errmsg.Color = .Auto;
|
||||
var stdin_flag: bool = false;
|
||||
var check_flag: bool = false;
|
||||
@ -597,11 +598,12 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const arg = args[i];
|
||||
if (mem.startsWith(u8, arg, "-")) {
|
||||
if (mem.eql(u8, arg, "--help")) {
|
||||
try stdout.write(usage_fmt);
|
||||
const stdout = io.getStdOut().outStream();
|
||||
try stdout.writeAll(usage_fmt);
|
||||
process.exit(0);
|
||||
} else if (mem.eql(u8, arg, "--color")) {
|
||||
if (i + 1 >= args.len) {
|
||||
try stderr.write("expected [auto|on|off] after --color\n");
|
||||
try stderr.writeAll("expected [auto|on|off] after --color\n");
|
||||
process.exit(1);
|
||||
}
|
||||
i += 1;
|
||||
@ -632,14 +634,13 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
|
||||
if (stdin_flag) {
|
||||
if (input_files.len != 0) {
|
||||
try stderr.write("cannot use --stdin with positional arguments\n");
|
||||
try stderr.writeAll("cannot use --stdin with positional arguments\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var stdin_file = io.getStdIn();
|
||||
var stdin = stdin_file.inStream();
|
||||
const stdin = io.getStdIn().inStream();
|
||||
|
||||
const source_code = try stdin.stream.readAllAlloc(allocator, max_src_size);
|
||||
const source_code = try stdin.readAllAlloc(allocator, max_src_size);
|
||||
defer allocator.free(source_code);
|
||||
|
||||
const tree = std.zig.parse(allocator, source_code) catch |err| {
|
||||
@ -653,7 +654,7 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, tree, "<stdin>");
|
||||
defer msg.destroy();
|
||||
|
||||
try msg.printToFile(stderr_file, color);
|
||||
try msg.printToFile(io.getStdErr(), color);
|
||||
}
|
||||
if (tree.errors.len != 0) {
|
||||
process.exit(1);
|
||||
@ -664,12 +665,13 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
|
||||
process.exit(code);
|
||||
}
|
||||
|
||||
const stdout = io.getStdOut().outStream();
|
||||
_ = try std.zig.render(allocator, stdout, tree);
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_files.len == 0) {
|
||||
try stderr.write("expected at least one source file argument\n");
|
||||
try stderr.writeAll("expected at least one source file argument\n");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -713,6 +715,9 @@ const FmtError = error{
|
||||
} || fs.File.OpenError;
|
||||
|
||||
async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtError!void {
|
||||
const stderr_file = io.getStdErr();
|
||||
const stderr = stderr_file.outStream();
|
||||
|
||||
const file_path = try std.mem.dupe(fmt.allocator, u8, file_path_ref);
|
||||
defer fmt.allocator.free(file_path);
|
||||
|
||||
@ -729,7 +734,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
|
||||
max_src_size,
|
||||
) catch |err| switch (err) {
|
||||
error.IsDir, error.AccessDenied => {
|
||||
var dir = try fs.cwd().openDirList(file_path);
|
||||
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
|
||||
defer dir.close();
|
||||
|
||||
var group = event.Group(FmtError!void).init(fmt.allocator);
|
||||
@ -791,11 +796,13 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
|
||||
}
|
||||
|
||||
fn cmdVersion(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const stdout = io.getStdOut().outStream();
|
||||
try stdout.print("{}\n", .{c.ZIG_VERSION_STRING});
|
||||
}
|
||||
|
||||
fn cmdHelp(allocator: *Allocator, args: []const []const u8) !void {
|
||||
try stdout.write(usage);
|
||||
const stdout = io.getStdOut();
|
||||
try stdout.writeAll(usage);
|
||||
}
|
||||
|
||||
pub const info_zen =
|
||||
@ -816,7 +823,7 @@ pub const info_zen =
|
||||
;
|
||||
|
||||
fn cmdZen(allocator: *Allocator, args: []const []const u8) !void {
|
||||
try stdout.write(info_zen);
|
||||
try io.getStdOut().writeAll(info_zen);
|
||||
}
|
||||
|
||||
const usage_internal =
|
||||
@ -829,8 +836,9 @@ const usage_internal =
|
||||
;
|
||||
|
||||
fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const stderr = io.getStdErr().outStream();
|
||||
if (args.len == 0) {
|
||||
try stderr.write(usage_internal);
|
||||
try stderr.writeAll(usage_internal);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@ -849,10 +857,11 @@ fn cmdInternal(allocator: *Allocator, args: []const []const u8) !void {
|
||||
}
|
||||
|
||||
try stderr.print("unknown sub command: {}\n\n", .{args[0]});
|
||||
try stderr.write(usage_internal);
|
||||
try stderr.writeAll(usage_internal);
|
||||
}
|
||||
|
||||
fn cmdInternalBuildInfo(allocator: *Allocator, args: []const []const u8) !void {
|
||||
const stdout = io.getStdOut().outStream();
|
||||
try stdout.print(
|
||||
\\ZIG_CMAKE_BINARY_DIR {}
|
||||
\\ZIG_CXX_COMPILER {}
|
||||
|
||||
@ -72,7 +72,7 @@ pub fn cmdTargets(
|
||||
};
|
||||
defer allocator.free(zig_lib_dir);
|
||||
|
||||
var dir = try std.fs.cwd().openDirList(zig_lib_dir);
|
||||
var dir = try std.fs.cwd().openDir(zig_lib_dir, .{});
|
||||
defer dir.close();
|
||||
|
||||
const vers_txt = try dir.readFileAlloc(allocator, "libc/glibc/vers.txt", 10 * 1024);
|
||||
|
||||
@ -113,6 +113,10 @@ const Error = extern enum {
|
||||
TargetHasNoDynamicLinker,
|
||||
InvalidAbiVersion,
|
||||
InvalidOperatingSystemVersion,
|
||||
UnknownClangOption,
|
||||
PermissionDenied,
|
||||
FileBusy,
|
||||
Locked,
|
||||
};
|
||||
|
||||
const FILE = std.c.FILE;
|
||||
@ -128,7 +132,7 @@ export fn stage2_translate_c(
|
||||
args_end: [*]?[*]const u8,
|
||||
resources_path: [*:0]const u8,
|
||||
) Error {
|
||||
var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0];
|
||||
var errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{};
|
||||
out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) {
|
||||
error.SemanticAnalyzeFail => {
|
||||
out_errors_ptr.* = errors.ptr;
|
||||
@ -319,7 +323,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
|
||||
const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) {
|
||||
error.IsDir, error.AccessDenied => {
|
||||
// TODO make event based (and dir.next())
|
||||
var dir = try fs.cwd().openDirList(file_path);
|
||||
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
|
||||
defer dir.close();
|
||||
|
||||
var dir_it = dir.iterate();
|
||||
@ -843,6 +847,9 @@ export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [
|
||||
error.NoDevice => return .NoDevice,
|
||||
error.NotDir => return .NotDir,
|
||||
error.DeviceBusy => return .DeviceBusy,
|
||||
error.PermissionDenied => return .PermissionDenied,
|
||||
error.FileBusy => return .FileBusy,
|
||||
error.Locked => return .Locked,
|
||||
};
|
||||
stage1_libc.initFromStage2(libc);
|
||||
return .None;
|
||||
@ -909,6 +916,7 @@ const Stage2Target = extern struct {
|
||||
os_builtin_str: ?[*:0]const u8,
|
||||
|
||||
dynamic_linker: ?[*:0]const u8,
|
||||
standard_dynamic_linker_path: ?[*:0]const u8,
|
||||
|
||||
fn fromTarget(self: *Stage2Target, cross_target: CrossTarget) !void {
|
||||
const allocator = std.heap.c_allocator;
|
||||
@ -1119,6 +1127,12 @@ const Stage2Target = extern struct {
|
||||
}
|
||||
};
|
||||
|
||||
const std_dl = target.standardDynamicLinkerPath();
|
||||
const std_dl_z = if (std_dl.get()) |dl|
|
||||
(try mem.dupeZ(std.heap.c_allocator, u8, dl)).ptr
|
||||
else
|
||||
null;
|
||||
|
||||
const cache_hash_slice = cache_hash.toOwnedSlice();
|
||||
self.* = .{
|
||||
.arch = @enumToInt(target.cpu.arch) + 1, // skip over ZigLLVM_UnknownArch
|
||||
@ -1134,6 +1148,7 @@ const Stage2Target = extern struct {
|
||||
.is_native = cross_target.isNative(),
|
||||
.glibc_or_darwin_version = glibc_or_darwin_version,
|
||||
.dynamic_linker = dynamic_linker,
|
||||
.standard_dynamic_linker_path = std_dl_z,
|
||||
};
|
||||
}
|
||||
};
|
||||
@ -1207,3 +1222,173 @@ fn convertSlice(slice: [][:0]u8, ptr: *[*][*:0]u8, len: *usize) !void {
|
||||
}
|
||||
ptr.* = new_slice.ptr;
|
||||
}
|
||||
|
||||
const clang_args = @import("clang_options.zig").list;
|
||||
|
||||
// ABI warning
|
||||
pub const ClangArgIterator = extern struct {
|
||||
has_next: bool,
|
||||
zig_equivalent: ZigEquivalent,
|
||||
only_arg: [*:0]const u8,
|
||||
second_arg: [*:0]const u8,
|
||||
other_args_ptr: [*]const [*:0]const u8,
|
||||
other_args_len: usize,
|
||||
argv_ptr: [*]const [*:0]const u8,
|
||||
argv_len: usize,
|
||||
next_index: usize,
|
||||
|
||||
// ABI warning
|
||||
pub const ZigEquivalent = extern enum {
|
||||
target,
|
||||
o,
|
||||
c,
|
||||
other,
|
||||
positional,
|
||||
l,
|
||||
ignore,
|
||||
driver_punt,
|
||||
pic,
|
||||
no_pic,
|
||||
nostdlib,
|
||||
shared,
|
||||
rdynamic,
|
||||
wl,
|
||||
preprocess,
|
||||
optimize,
|
||||
debug,
|
||||
sanitize,
|
||||
};
|
||||
|
||||
fn init(argv: []const [*:0]const u8) ClangArgIterator {
|
||||
return .{
|
||||
.next_index = 2, // `zig cc foo` this points to `foo`
|
||||
.has_next = argv.len > 2,
|
||||
.zig_equivalent = undefined,
|
||||
.only_arg = undefined,
|
||||
.second_arg = undefined,
|
||||
.other_args_ptr = undefined,
|
||||
.other_args_len = undefined,
|
||||
.argv_ptr = argv.ptr,
|
||||
.argv_len = argv.len,
|
||||
};
|
||||
}
|
||||
|
||||
fn next(self: *ClangArgIterator) !void {
|
||||
assert(self.has_next);
|
||||
assert(self.next_index < self.argv_len);
|
||||
// In this state we know that the parameter we are looking at is a root parameter
|
||||
// rather than an argument to a parameter.
|
||||
self.other_args_ptr = self.argv_ptr + self.next_index;
|
||||
self.other_args_len = 1; // We adjust this value below when necessary.
|
||||
const arg = mem.span(self.argv_ptr[self.next_index]);
|
||||
self.next_index += 1;
|
||||
defer {
|
||||
if (self.next_index >= self.argv_len) self.has_next = false;
|
||||
}
|
||||
|
||||
if (!mem.startsWith(u8, arg, "-")) {
|
||||
self.zig_equivalent = .positional;
|
||||
self.only_arg = arg.ptr;
|
||||
return;
|
||||
}
|
||||
|
||||
find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) {
|
||||
.flag => {
|
||||
const prefix_len = clang_arg.matchEql(arg);
|
||||
if (prefix_len > 0) {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
self.only_arg = arg.ptr + prefix_len;
|
||||
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.joined, .comma_joined => {
|
||||
// joined example: --target=foo
|
||||
// comma_joined example: -Wl,-soname,libsoundio.so.2
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len != 0) {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
self.only_arg = arg.ptr + prefix_len; // This will skip over the "--target=" part.
|
||||
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.joined_or_separate => {
|
||||
// Examples: `-lfoo`, `-l foo`
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len == arg.len) {
|
||||
if (self.next_index >= self.argv_len) {
|
||||
std.debug.warn("Expected parameter after '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
self.only_arg = self.argv_ptr[self.next_index];
|
||||
self.next_index += 1;
|
||||
self.other_args_len += 1;
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
|
||||
break :find_clang_arg;
|
||||
} else if (prefix_len != 0) {
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
self.only_arg = arg.ptr + prefix_len;
|
||||
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.joined_and_separate => {
|
||||
// Example: `-Xopenmp-target=riscv64-linux-unknown foo`
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len != 0) {
|
||||
self.only_arg = arg.ptr + prefix_len;
|
||||
if (self.next_index >= self.argv_len) {
|
||||
std.debug.warn("Expected parameter after '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
self.second_arg = self.argv_ptr[self.next_index];
|
||||
self.next_index += 1;
|
||||
self.other_args_len += 1;
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
break :find_clang_arg;
|
||||
}
|
||||
},
|
||||
.separate => if (clang_arg.matchEql(arg) > 0) {
|
||||
if (self.next_index >= self.argv_len) {
|
||||
std.debug.warn("Expected parameter after '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
self.only_arg = self.argv_ptr[self.next_index];
|
||||
self.next_index += 1;
|
||||
self.other_args_len += 1;
|
||||
self.zig_equivalent = clang_arg.zig_equivalent;
|
||||
break :find_clang_arg;
|
||||
},
|
||||
.remaining_args_joined => {
|
||||
const prefix_len = clang_arg.matchStartsWith(arg);
|
||||
if (prefix_len != 0) {
|
||||
@panic("TODO");
|
||||
}
|
||||
},
|
||||
.multi_arg => if (clang_arg.matchEql(arg) > 0) {
|
||||
@panic("TODO");
|
||||
},
|
||||
}
|
||||
else {
|
||||
std.debug.warn("Unknown Clang option: '{}'\n", .{arg});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export fn stage2_clang_arg_iterator(
|
||||
result: *ClangArgIterator,
|
||||
argc: usize,
|
||||
argv: [*]const [*:0]const u8,
|
||||
) void {
|
||||
result.* = ClangArgIterator.init(argv[0..argc]);
|
||||
}
|
||||
|
||||
export fn stage2_clang_arg_next(it: *ClangArgIterator) Error {
|
||||
it.next() catch |err| switch (err) {
|
||||
error.UnknownClangOption => return .UnknownClangOption,
|
||||
};
|
||||
return .None;
|
||||
}
|
||||
|
||||
@ -57,11 +57,11 @@ pub const TestContext = struct {
|
||||
errdefer allocator.free(self.zig_lib_dir);
|
||||
|
||||
try std.fs.cwd().makePath(tmp_dir_name);
|
||||
errdefer std.fs.deleteTree(tmp_dir_name) catch {};
|
||||
errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {};
|
||||
}
|
||||
|
||||
fn deinit(self: *TestContext) void {
|
||||
std.fs.deleteTree(tmp_dir_name) catch {};
|
||||
std.fs.cwd().deleteTree(tmp_dir_name) catch {};
|
||||
allocator.free(self.zig_lib_dir);
|
||||
self.zig_compiler.deinit();
|
||||
}
|
||||
|
||||
@ -1744,20 +1744,18 @@ fn writeEscapedString(buf: []u8, s: []const u8) void {
|
||||
// Returns either a string literal or a slice of `buf`.
|
||||
fn escapeChar(c: u8, char_buf: *[4]u8) []const u8 {
|
||||
return switch (c) {
|
||||
'\"' => "\\\""[0..],
|
||||
'\'' => "\\'"[0..],
|
||||
'\\' => "\\\\"[0..],
|
||||
'\n' => "\\n"[0..],
|
||||
'\r' => "\\r"[0..],
|
||||
'\t' => "\\t"[0..],
|
||||
else => {
|
||||
// Handle the remaining escapes Zig doesn't support by turning them
|
||||
// into their respective hex representation
|
||||
if (std.ascii.isCntrl(c))
|
||||
return std.fmt.bufPrint(char_buf[0..], "\\x{x:0<2}", .{c}) catch unreachable
|
||||
else
|
||||
return std.fmt.bufPrint(char_buf[0..], "{c}", .{c}) catch unreachable;
|
||||
},
|
||||
'\"' => "\\\"",
|
||||
'\'' => "\\'",
|
||||
'\\' => "\\\\",
|
||||
'\n' => "\\n",
|
||||
'\r' => "\\r",
|
||||
'\t' => "\\t",
|
||||
// Handle the remaining escapes Zig doesn't support by turning them
|
||||
// into their respective hex representation
|
||||
else => if (std.ascii.isCntrl(c))
|
||||
std.fmt.bufPrint(char_buf, "\\x{x:0<2}", .{c}) catch unreachable
|
||||
else
|
||||
std.fmt.bufPrint(char_buf, "{c}", .{c}) catch unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -3,8 +3,7 @@ const Target = std.Target;
|
||||
const llvm = @import("llvm.zig");
|
||||
|
||||
pub fn getDarwinArchString(self: Target) [:0]const u8 {
|
||||
const arch = self.getArch();
|
||||
switch (arch) {
|
||||
switch (self.cpu.arch) {
|
||||
.aarch64 => return "arm64",
|
||||
.thumb,
|
||||
.arm,
|
||||
@ -34,3 +33,15 @@ pub fn initializeAllTargets() void {
|
||||
llvm.InitializeAllAsmPrinters();
|
||||
llvm.InitializeAllAsmParsers();
|
||||
}
|
||||
|
||||
pub fn getLLVMTriple(allocator: *std.mem.Allocator, target: std.Target) !std.Buffer {
|
||||
var result = try std.Buffer.initSize(allocator, 0);
|
||||
errdefer result.deinit();
|
||||
|
||||
try result.outStream().print(
|
||||
"{}-unknown-{}-{}",
|
||||
.{ @tagName(target.cpu.arch), @tagName(target.os.tag), @tagName(target.abi) },
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -231,6 +231,7 @@ enum ConstPtrSpecial {
|
||||
// The pointer is a reference to a single object.
|
||||
ConstPtrSpecialRef,
|
||||
// The pointer points to an element in an underlying array.
|
||||
// Not to be confused with ConstPtrSpecialSubArray.
|
||||
ConstPtrSpecialBaseArray,
|
||||
// The pointer points to a field in an underlying struct.
|
||||
ConstPtrSpecialBaseStruct,
|
||||
@ -257,6 +258,10 @@ enum ConstPtrSpecial {
|
||||
// types to be the same, so all optionals of pointer types use x_ptr
|
||||
// instead of x_optional.
|
||||
ConstPtrSpecialNull,
|
||||
// The pointer points to a sub-array (not an individual element).
|
||||
// Not to be confused with ConstPtrSpecialBaseArray. However, it uses the same
|
||||
// union payload struct (base_array).
|
||||
ConstPtrSpecialSubArray,
|
||||
};
|
||||
|
||||
enum ConstPtrMut {
|
||||
@ -739,6 +744,7 @@ struct AstNodeReturnExpr {
|
||||
|
||||
struct AstNodeDefer {
|
||||
ReturnKind kind;
|
||||
AstNode *err_payload;
|
||||
AstNode *expr;
|
||||
|
||||
// temporary data used in IR generation
|
||||
@ -1997,6 +2003,7 @@ enum WantCSanitize {
|
||||
struct CFile {
|
||||
ZigList<const char *> args;
|
||||
const char *source_path;
|
||||
const char *preprocessor_only_basename;
|
||||
};
|
||||
|
||||
// When adding fields, check if they should be added to the hash computation in build_with_cache
|
||||
@ -2141,6 +2148,7 @@ struct CodeGen {
|
||||
// As an input parameter, mutually exclusive with enable_cache. But it gets
|
||||
// populated in codegen_build_and_link.
|
||||
Buf *output_dir;
|
||||
Buf *c_artifact_dir;
|
||||
const char **libc_include_dir_list;
|
||||
size_t libc_include_dir_len;
|
||||
|
||||
@ -2262,6 +2270,7 @@ struct CodeGen {
|
||||
Buf *zig_lib_dir;
|
||||
Buf *zig_std_dir;
|
||||
Buf *version_script_path;
|
||||
Buf *override_soname;
|
||||
|
||||
const char **llvm_argv;
|
||||
size_t llvm_argv_len;
|
||||
@ -3706,6 +3715,7 @@ struct IrInstGenSlice {
|
||||
IrInstGen *start;
|
||||
IrInstGen *end;
|
||||
IrInstGen *result_loc;
|
||||
ZigValue *sentinel;
|
||||
bool safety_check_on;
|
||||
};
|
||||
|
||||
|
||||
@ -780,6 +780,8 @@ ZigType *get_error_union_type(CodeGen *g, ZigType *err_set_type, ZigType *payloa
|
||||
}
|
||||
|
||||
ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, ZigValue *sentinel) {
|
||||
Error err;
|
||||
|
||||
TypeId type_id = {};
|
||||
type_id.id = ZigTypeIdArray;
|
||||
type_id.data.array.codegen = g;
|
||||
@ -791,7 +793,11 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi
|
||||
return existing_entry->value;
|
||||
}
|
||||
|
||||
assert(type_is_resolved(child_type, ResolveStatusSizeKnown));
|
||||
size_t full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0);
|
||||
|
||||
if (full_array_size != 0 && (err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
|
||||
codegen_report_errors_and_exit(g);
|
||||
}
|
||||
|
||||
ZigType *entry = new_type_table_entry(ZigTypeIdArray);
|
||||
|
||||
@ -803,15 +809,8 @@ ZigType *get_array_type(CodeGen *g, ZigType *child_type, uint64_t array_size, Zi
|
||||
}
|
||||
buf_appendf(&entry->name, "]%s", buf_ptr(&child_type->name));
|
||||
|
||||
size_t full_array_size;
|
||||
if (array_size == 0) {
|
||||
full_array_size = 0;
|
||||
} else {
|
||||
full_array_size = array_size + ((sentinel != nullptr) ? 1 : 0);
|
||||
}
|
||||
|
||||
entry->size_in_bits = child_type->size_in_bits * full_array_size;
|
||||
entry->abi_align = child_type->abi_align;
|
||||
entry->abi_align = (full_array_size == 0) ? 0 : child_type->abi_align;
|
||||
entry->abi_size = child_type->abi_size * full_array_size;
|
||||
|
||||
entry->data.array.child_type = child_type;
|
||||
@ -1197,7 +1196,8 @@ Error type_val_resolve_zero_bits(CodeGen *g, ZigValue *type_val, ZigType *parent
|
||||
LazyValueArrayType *lazy_array_type =
|
||||
reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
|
||||
|
||||
if (lazy_array_type->length < 1) {
|
||||
// The sentinel counts as an extra element
|
||||
if (lazy_array_type->length == 0 && lazy_array_type->sentinel == nullptr) {
|
||||
*is_zero_bits = true;
|
||||
return ErrorNone;
|
||||
}
|
||||
@ -1452,7 +1452,7 @@ static OnePossibleValue type_val_resolve_has_one_possible_value(CodeGen *g, ZigV
|
||||
case LazyValueIdArrayType: {
|
||||
LazyValueArrayType *lazy_array_type =
|
||||
reinterpret_cast<LazyValueArrayType *>(type_val->data.x_lazy);
|
||||
if (lazy_array_type->length < 1)
|
||||
if (lazy_array_type->length == 0)
|
||||
return OnePossibleValueYes;
|
||||
return type_val_resolve_has_one_possible_value(g, lazy_array_type->elem_type->value);
|
||||
}
|
||||
@ -4488,7 +4488,14 @@ static uint32_t get_async_frame_align_bytes(CodeGen *g) {
|
||||
}
|
||||
|
||||
uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
|
||||
ZigType *ptr_type = get_src_ptr_type(type);
|
||||
ZigType *ptr_type;
|
||||
if (type->id == ZigTypeIdStruct) {
|
||||
assert(type->data.structure.special == StructSpecialSlice);
|
||||
TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
|
||||
ptr_type = resolve_struct_field_type(g, ptr_field);
|
||||
} else {
|
||||
ptr_type = get_src_ptr_type(type);
|
||||
}
|
||||
if (ptr_type->id == ZigTypeIdPointer) {
|
||||
return (ptr_type->data.pointer.explicit_alignment == 0) ?
|
||||
get_abi_alignment(g, ptr_type->data.pointer.child_type) : ptr_type->data.pointer.explicit_alignment;
|
||||
@ -4505,8 +4512,15 @@ uint32_t get_ptr_align(CodeGen *g, ZigType *type) {
|
||||
}
|
||||
}
|
||||
|
||||
bool get_ptr_const(ZigType *type) {
|
||||
ZigType *ptr_type = get_src_ptr_type(type);
|
||||
bool get_ptr_const(CodeGen *g, ZigType *type) {
|
||||
ZigType *ptr_type;
|
||||
if (type->id == ZigTypeIdStruct) {
|
||||
assert(type->data.structure.special == StructSpecialSlice);
|
||||
TypeStructField *ptr_field = type->data.structure.fields[slice_ptr_index];
|
||||
ptr_type = resolve_struct_field_type(g, ptr_field);
|
||||
} else {
|
||||
ptr_type = get_src_ptr_type(type);
|
||||
}
|
||||
if (ptr_type->id == ZigTypeIdPointer) {
|
||||
return ptr_type->data.pointer.is_const;
|
||||
} else if (ptr_type->id == ZigTypeIdFn) {
|
||||
@ -5282,6 +5296,11 @@ static uint32_t hash_const_val_ptr(ZigValue *const_val) {
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
|
||||
hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialSubArray:
|
||||
hash_val += (uint32_t)2643358777;
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_array.array_val);
|
||||
hash_val += hash_size(const_val->data.x_ptr.data.base_array.elem_index);
|
||||
return hash_val;
|
||||
case ConstPtrSpecialBaseStruct:
|
||||
hash_val += (uint32_t)3518317043;
|
||||
hash_val += hash_ptr(const_val->data.x_ptr.data.base_struct.struct_val);
|
||||
@ -5811,18 +5830,13 @@ ZigValue *get_the_one_possible_value(CodeGen *g, ZigType *type_entry) {
|
||||
// The elements array cannot be left unpopulated
|
||||
ZigType *array_type = result->type;
|
||||
ZigType *elem_type = array_type->data.array.child_type;
|
||||
ZigValue *sentinel_value = array_type->data.array.sentinel;
|
||||
const size_t elem_count = array_type->data.array.len + (sentinel_value != nullptr);
|
||||
const size_t elem_count = array_type->data.array.len;
|
||||
|
||||
result->data.x_array.data.s_none.elements = g->pass1_arena->allocate<ZigValue>(elem_count);
|
||||
for (size_t i = 0; i < elem_count; i += 1) {
|
||||
ZigValue *elem_val = &result->data.x_array.data.s_none.elements[i];
|
||||
copy_const_val(g, elem_val, get_the_one_possible_value(g, elem_type));
|
||||
}
|
||||
if (sentinel_value != nullptr) {
|
||||
ZigValue *last_elem_val = &result->data.x_array.data.s_none.elements[elem_count - 1];
|
||||
copy_const_val(g, last_elem_val, sentinel_value);
|
||||
}
|
||||
} else if (result->type->id == ZigTypeIdPointer) {
|
||||
result->data.x_ptr.special = ConstPtrSpecialRef;
|
||||
result->data.x_ptr.data.ref.pointee = get_the_one_possible_value(g, result->type->data.pointer.child_type);
|
||||
@ -6753,6 +6767,7 @@ bool const_values_equal_ptr(ZigValue *a, ZigValue *b) {
|
||||
return false;
|
||||
return true;
|
||||
case ConstPtrSpecialBaseArray:
|
||||
case ConstPtrSpecialSubArray:
|
||||
if (a->data.x_ptr.data.base_array.array_val != b->data.x_ptr.data.base_array.array_val) {
|
||||
return false;
|
||||
}
|
||||
@ -7010,6 +7025,7 @@ static void render_const_val_ptr(CodeGen *g, Buf *buf, ZigValue *const_val, ZigT
|
||||
render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
|
||||
return;
|
||||
case ConstPtrSpecialBaseArray:
|
||||
case ConstPtrSpecialSubArray:
|
||||
buf_appendf(buf, "*");
|
||||
// TODO we need a source node for const_ptr_pointee because it can generate compile errors
|
||||
render_const_value(g, buf, const_ptr_pointee(nullptr, g, const_val, nullptr));
|
||||
|
||||
@ -76,7 +76,7 @@ void resolve_top_level_decl(CodeGen *g, Tld *tld, AstNode *source_node, bool all
|
||||
|
||||
ZigType *get_src_ptr_type(ZigType *type);
|
||||
uint32_t get_ptr_align(CodeGen *g, ZigType *type);
|
||||
bool get_ptr_const(ZigType *type);
|
||||
bool get_ptr_const(CodeGen *g, ZigType *type);
|
||||
ZigType *validate_var_type(CodeGen *g, AstNode *source_node, ZigType *type_entry);
|
||||
ZigType *container_ref_type(ZigType *type_entry);
|
||||
bool type_is_complete(ZigType *type_entry);
|
||||
|
||||
183
src/codegen.cpp
183
src/codegen.cpp
@ -5418,12 +5418,16 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
|
||||
ZigType *array_type = array_ptr_type->data.pointer.child_type;
|
||||
LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type);
|
||||
|
||||
LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
|
||||
|
||||
bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
|
||||
|
||||
ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry;
|
||||
ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel;
|
||||
ZigType *result_type = instruction->base.value->type;
|
||||
if (!type_has_bits(g, result_type)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// This is not whether the result type has a sentinel, but whether there should be a sentinel check,
|
||||
// e.g. if they used [a..b :s] syntax.
|
||||
ZigValue *sentinel = instruction->sentinel;
|
||||
|
||||
if (array_type->id == ZigTypeIdArray ||
|
||||
(array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))
|
||||
@ -5458,6 +5462,8 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
|
||||
}
|
||||
}
|
||||
if (!type_has_bits(g, array_type)) {
|
||||
LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
|
||||
|
||||
// TODO if runtime safety is on, store 0xaaaaaaa in ptr field
|
||||
@ -5466,20 +5472,26 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
|
||||
return tmp_struct_ptr;
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
|
||||
LLVMValueRef indices[] = {
|
||||
LLVMConstNull(g->builtin_types.entry_usize->llvm_type),
|
||||
start_val,
|
||||
};
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
if (result_type->id == ZigTypeIdPointer) {
|
||||
ir_assert(instruction->result_loc == nullptr, &instruction->base);
|
||||
LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
|
||||
return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
|
||||
} else {
|
||||
LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
|
||||
return tmp_struct_ptr;
|
||||
return tmp_struct_ptr;
|
||||
}
|
||||
} else if (array_type->id == ZigTypeIdPointer) {
|
||||
assert(array_type->data.pointer.ptr_len != PtrLenSingle);
|
||||
LLVMValueRef start_val = ir_llvm_value(g, instruction->start);
|
||||
@ -5493,24 +5505,39 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
|
||||
}
|
||||
}
|
||||
|
||||
if (type_has_bits(g, array_type)) {
|
||||
size_t gen_ptr_index = instruction->base.value->type->data.structure.fields[slice_ptr_index]->gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
if (!type_has_bits(g, array_type)) {
|
||||
LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
|
||||
size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
return tmp_struct_ptr;
|
||||
}
|
||||
|
||||
size_t gen_len_index = instruction->base.value->type->data.structure.fields[slice_len_index]->gen_index;
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, "");
|
||||
if (result_type->id == ZigTypeIdPointer) {
|
||||
ir_assert(instruction->result_loc == nullptr, &instruction->base);
|
||||
LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
|
||||
return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
|
||||
}
|
||||
|
||||
LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
|
||||
|
||||
size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index;
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
|
||||
size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index;
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
|
||||
return tmp_struct_ptr;
|
||||
|
||||
} else if (array_type->id == ZigTypeIdStruct) {
|
||||
assert(array_type->data.structure.special == StructSpecialSlice);
|
||||
assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind);
|
||||
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind);
|
||||
assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(tmp_struct_ptr))) == LLVMStructTypeKind);
|
||||
|
||||
size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index;
|
||||
assert(ptr_index != SIZE_MAX);
|
||||
@ -5547,15 +5574,22 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrI
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");
|
||||
LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
if (result_type->id == ZigTypeIdPointer) {
|
||||
ir_assert(instruction->result_loc == nullptr, &instruction->base);
|
||||
LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type);
|
||||
return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, "");
|
||||
} else {
|
||||
LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc);
|
||||
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");
|
||||
gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
|
||||
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");
|
||||
LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
|
||||
gen_store_untyped(g, len_value, len_field_ptr, 0, false);
|
||||
|
||||
return tmp_struct_ptr;
|
||||
return tmp_struct_ptr;
|
||||
}
|
||||
} else {
|
||||
zig_unreachable();
|
||||
}
|
||||
@ -6640,7 +6674,6 @@ static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_co
|
||||
};
|
||||
return LLVMConstInBoundsGEP(base_ptr, indices, 2);
|
||||
} else {
|
||||
assert(parent->id == ConstParentIdScalar);
|
||||
return base_ptr;
|
||||
}
|
||||
}
|
||||
@ -6790,6 +6823,22 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Zig
|
||||
used_bits += packed_bits_size;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_entry->data.array.sentinel != nullptr) {
|
||||
ZigValue *elem_val = type_entry->data.array.sentinel;
|
||||
LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val);
|
||||
|
||||
if (is_big_endian) {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false);
|
||||
val = LLVMConstShl(val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val);
|
||||
} else {
|
||||
LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
|
||||
LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
|
||||
val = LLVMConstOr(val, child_val_shifted);
|
||||
used_bits += packed_bits_size;
|
||||
}
|
||||
}
|
||||
return val;
|
||||
}
|
||||
case ZigTypeIdVector:
|
||||
@ -6852,24 +6901,16 @@ static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const cha
|
||||
return const_val->llvm_value;
|
||||
}
|
||||
case ConstPtrSpecialBaseArray:
|
||||
case ConstPtrSpecialSubArray:
|
||||
{
|
||||
ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val;
|
||||
assert(array_const_val->type->id == ZigTypeIdArray);
|
||||
if (!type_has_bits(g, array_const_val->type)) {
|
||||
if (array_const_val->type->data.array.sentinel != nullptr) {
|
||||
ZigValue *pointee = array_const_val->type->data.array.sentinel;
|
||||
render_const_val(g, pointee, "");
|
||||
render_const_val_global(g, pointee, "");
|
||||
const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global,
|
||||
get_llvm_type(g, const_val->type));
|
||||
return const_val->llvm_value;
|
||||
} else {
|
||||
// make this a null pointer
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type),
|
||||
get_llvm_type(g, const_val->type));
|
||||
return const_val->llvm_value;
|
||||
}
|
||||
// make this a null pointer
|
||||
ZigType *usize = g->builtin_types.entry_usize;
|
||||
const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type),
|
||||
get_llvm_type(g, const_val->type));
|
||||
return const_val->llvm_value;
|
||||
}
|
||||
size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
|
||||
LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index);
|
||||
@ -9228,6 +9269,7 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
|
||||
case BuildModeDebug:
|
||||
// windows c runtime requires -D_DEBUG if using debug libraries
|
||||
args.append("-D_DEBUG");
|
||||
args.append("-Og");
|
||||
|
||||
if (g->libc_link_lib != nullptr) {
|
||||
args.append("-fstack-protector-strong");
|
||||
@ -9650,6 +9692,21 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static bool need_llvm_module(CodeGen *g) {
|
||||
return buf_len(&g->main_pkg->root_src_path) != 0;
|
||||
}
|
||||
|
||||
// before gen_c_objects
|
||||
static bool main_output_dir_is_just_one_c_object_pre(CodeGen *g) {
|
||||
return g->enable_cache && g->c_source_files.length == 1 && !need_llvm_module(g) &&
|
||||
g->out_type == OutTypeObj && g->link_objects.length == 0;
|
||||
}
|
||||
|
||||
// after gen_c_objects
|
||||
static bool main_output_dir_is_just_one_c_object_post(CodeGen *g) {
|
||||
return g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g) && g->out_type == OutTypeObj;
|
||||
}
|
||||
|
||||
// returns true if it was a cache miss
|
||||
static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
Error err;
|
||||
@ -9667,8 +9724,17 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
buf_len(c_source_basename), 0);
|
||||
|
||||
Buf *final_o_basename = buf_alloc();
|
||||
os_path_extname(c_source_basename, final_o_basename, nullptr);
|
||||
buf_append_str(final_o_basename, target_o_file_ext(g->zig_target));
|
||||
if (c_file->preprocessor_only_basename == nullptr) {
|
||||
// We special case when doing build-obj for just one C file
|
||||
if (main_output_dir_is_just_one_c_object_pre(g)) {
|
||||
buf_init_from_buf(final_o_basename, g->root_out_name);
|
||||
} else {
|
||||
os_path_extname(c_source_basename, final_o_basename, nullptr);
|
||||
}
|
||||
buf_append_str(final_o_basename, target_o_file_ext(g->zig_target));
|
||||
} else {
|
||||
buf_init_from_str(final_o_basename, c_file->preprocessor_only_basename);
|
||||
}
|
||||
|
||||
CacheHash *cache_hash;
|
||||
if ((err = create_c_object_cache(g, &cache_hash, true))) {
|
||||
@ -9717,7 +9783,13 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
Termination term;
|
||||
ZigList<const char *> args = {};
|
||||
args.append(buf_ptr(self_exe_path));
|
||||
args.append("cc");
|
||||
args.append("clang");
|
||||
|
||||
if (c_file->preprocessor_only_basename != nullptr) {
|
||||
args.append("-E");
|
||||
} else {
|
||||
args.append("-c");
|
||||
}
|
||||
|
||||
Buf *out_dep_path = buf_sprintf("%s.d", buf_ptr(out_obj_path));
|
||||
add_cc_args(g, args, buf_ptr(out_dep_path), false);
|
||||
@ -9725,7 +9797,6 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
args.append("-o");
|
||||
args.append(buf_ptr(out_obj_path));
|
||||
|
||||
args.append("-c");
|
||||
args.append(buf_ptr(c_source_file));
|
||||
|
||||
for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) {
|
||||
@ -9780,6 +9851,7 @@ static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) {
|
||||
os_path_join(artifact_dir, final_o_basename, o_final_path);
|
||||
}
|
||||
|
||||
g->c_artifact_dir = artifact_dir;
|
||||
g->link_objects.append(o_final_path);
|
||||
g->caches_to_release.append(cache_hash);
|
||||
|
||||
@ -10449,6 +10521,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
|
||||
cache_str(ch, g->libc->kernel32_lib_dir);
|
||||
}
|
||||
cache_buf_opt(ch, g->version_script_path);
|
||||
cache_buf_opt(ch, g->override_soname);
|
||||
|
||||
// gen_c_objects appends objects to g->link_objects which we want to include in the hash
|
||||
gen_c_objects(g);
|
||||
@ -10467,10 +10540,6 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
static bool need_llvm_module(CodeGen *g) {
|
||||
return buf_len(&g->main_pkg->root_src_path) != 0;
|
||||
}
|
||||
|
||||
static void resolve_out_paths(CodeGen *g) {
|
||||
assert(g->output_dir != nullptr);
|
||||
assert(g->root_out_name != nullptr);
|
||||
@ -10482,10 +10551,6 @@ static void resolve_out_paths(CodeGen *g) {
|
||||
case OutTypeUnknown:
|
||||
zig_unreachable();
|
||||
case OutTypeObj:
|
||||
if (g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g)) {
|
||||
buf_init_from_buf(&g->bin_file_output_path, g->link_objects.at(0));
|
||||
return;
|
||||
}
|
||||
if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache &&
|
||||
buf_eql_buf(o_basename, out_basename))
|
||||
{
|
||||
@ -10580,6 +10645,16 @@ static void output_type_information(CodeGen *g) {
|
||||
}
|
||||
}
|
||||
|
||||
static void init_output_dir(CodeGen *g, Buf *digest) {
|
||||
if (main_output_dir_is_just_one_c_object_post(g)) {
|
||||
g->output_dir = buf_alloc();
|
||||
os_path_dirname(g->link_objects.at(0), g->output_dir);
|
||||
} else {
|
||||
g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
|
||||
buf_ptr(g->cache_dir), buf_ptr(digest));
|
||||
}
|
||||
}
|
||||
|
||||
void codegen_build_and_link(CodeGen *g) {
|
||||
Error err;
|
||||
assert(g->out_type != OutTypeUnknown);
|
||||
@ -10622,8 +10697,7 @@ void codegen_build_and_link(CodeGen *g) {
|
||||
}
|
||||
|
||||
if (g->enable_cache && buf_len(&digest) != 0) {
|
||||
g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
|
||||
buf_ptr(g->cache_dir), buf_ptr(&digest));
|
||||
init_output_dir(g, &digest);
|
||||
resolve_out_paths(g);
|
||||
} else {
|
||||
if (need_llvm_module(g)) {
|
||||
@ -10644,8 +10718,7 @@ void codegen_build_and_link(CodeGen *g) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s",
|
||||
buf_ptr(g->cache_dir), buf_ptr(&digest));
|
||||
init_output_dir(g, &digest);
|
||||
|
||||
if ((err = os_make_path(g->output_dir))) {
|
||||
fprintf(stderr, "Unable to create output directory: %s\n", err_str(err));
|
||||
|
||||
@ -83,6 +83,10 @@ const char *err_str(Error err) {
|
||||
case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker";
|
||||
case ErrorInvalidAbiVersion: return "invalid C ABI version";
|
||||
case ErrorInvalidOperatingSystemVersion: return "invalid operating system version";
|
||||
case ErrorUnknownClangOption: return "unknown Clang option";
|
||||
case ErrorPermissionDenied: return "permission is denied";
|
||||
case ErrorFileBusy: return "file is busy";
|
||||
case ErrorLocked: return "file is locked by another process";
|
||||
}
|
||||
return "(invalid error)";
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ static const ZigGLibCLib glibc_libs[] = {
|
||||
{"pthread", 0},
|
||||
{"dl", 2},
|
||||
{"rt", 1},
|
||||
{"ld", 2},
|
||||
};
|
||||
|
||||
Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) {
|
||||
@ -330,6 +331,8 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
|
||||
return err;
|
||||
}
|
||||
|
||||
bool is_ld = (strcmp(lib->name, "ld") == 0);
|
||||
|
||||
CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node);
|
||||
codegen_set_lib_version(child_gen, lib->sover, 0, 0);
|
||||
child_gen->is_dynamic = true;
|
||||
@ -337,6 +340,13 @@ Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, con
|
||||
child_gen->version_script_path = map_file_path;
|
||||
child_gen->enable_cache = false;
|
||||
child_gen->output_dir = dummy_dir;
|
||||
if (is_ld) {
|
||||
assert(g->zig_target->standard_dynamic_linker_path != nullptr);
|
||||
Buf *ld_basename = buf_alloc();
|
||||
os_path_split(buf_create_from_str(g->zig_target->standard_dynamic_linker_path),
|
||||
nullptr, ld_basename);
|
||||
child_gen->override_soname = ld_basename;
|
||||
}
|
||||
codegen_build_and_link(child_gen);
|
||||
}
|
||||
|
||||
|
||||
628
src/ir.cpp
628
src/ir.cpp
File diff suppressed because it is too large
Load Diff
11
src/link.cpp
11
src/link.cpp
@ -566,6 +566,7 @@ static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFil
|
||||
Stage2ProgressNode *progress_node)
|
||||
{
|
||||
CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node);
|
||||
child_gen->root_out_name = buf_create_from_str(name);
|
||||
ZigList<CFile *> c_source_files = {0};
|
||||
c_source_files.append(c_file);
|
||||
child_gen->c_source_files = c_source_files;
|
||||
@ -1650,7 +1651,6 @@ static void construct_linker_job_elf(LinkJob *lj) {
|
||||
|
||||
bool is_lib = g->out_type == OutTypeLib;
|
||||
bool is_dyn_lib = g->is_dynamic && is_lib;
|
||||
Buf *soname = nullptr;
|
||||
if (!g->have_dynamic_link) {
|
||||
if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb ||
|
||||
g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb)
|
||||
@ -1661,15 +1661,13 @@ static void construct_linker_job_elf(LinkJob *lj) {
|
||||
}
|
||||
} else if (is_dyn_lib) {
|
||||
lj->args.append("-shared");
|
||||
|
||||
assert(buf_len(&g->bin_file_output_path) != 0);
|
||||
soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major);
|
||||
}
|
||||
|
||||
if (target_requires_pie(g->zig_target) && g->out_type == OutTypeExe) {
|
||||
lj->args.append("-pie");
|
||||
}
|
||||
|
||||
assert(buf_len(&g->bin_file_output_path) != 0);
|
||||
lj->args.append("-o");
|
||||
lj->args.append(buf_ptr(&g->bin_file_output_path));
|
||||
|
||||
@ -1739,6 +1737,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
|
||||
}
|
||||
|
||||
if (is_dyn_lib) {
|
||||
Buf *soname = (g->override_soname == nullptr) ?
|
||||
buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major) :
|
||||
g->override_soname;
|
||||
lj->args.append("-soname");
|
||||
lj->args.append(buf_ptr(soname));
|
||||
|
||||
@ -2007,7 +2008,7 @@ static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_fi
|
||||
|
||||
ZigList<const char *> args = {};
|
||||
args.append(buf_ptr(self_exe_path));
|
||||
args.append("cc");
|
||||
args.append("clang");
|
||||
args.append("-x");
|
||||
args.append("c");
|
||||
args.append(buf_ptr(def_in_file));
|
||||
|
||||
315
src/main.cpp
315
src/main.cpp
@ -36,7 +36,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
|
||||
" build-lib [source] create library from source or object files\n"
|
||||
" build-obj [source] create object from source or assembly\n"
|
||||
" builtin show the source code of @import(\"builtin\")\n"
|
||||
" cc C compiler\n"
|
||||
" cc use Zig as a drop-in C compiler\n"
|
||||
" fmt parse files and render in canonical zig format\n"
|
||||
" id print the base64-encoded compiler id\n"
|
||||
" init-exe initialize a `zig build` application in the cwd\n"
|
||||
@ -54,7 +54,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
|
||||
" --cache-dir [path] override the local cache directory\n"
|
||||
" --cache [auto|off|on] build in cache, print output path to stdout\n"
|
||||
" --color [auto|off|on] enable or disable colored error messages\n"
|
||||
" --disable-gen-h do not generate a C header file (.h)\n"
|
||||
" --disable-valgrind omit valgrind client requests in debug builds\n"
|
||||
" --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n"
|
||||
" --enable-valgrind include valgrind client requests release builds\n"
|
||||
@ -77,6 +76,8 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
|
||||
" -fno-emit-asm (default) do not output .s (assembly code)\n"
|
||||
" -femit-llvm-ir produce a .ll file with LLVM IR\n"
|
||||
" -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n"
|
||||
" -femit-h generate a C header file (.h)\n"
|
||||
" -fno-emit-h (default) do not generate a C header file (.h)\n"
|
||||
" --libc [file] Provide a file which specifies libc paths\n"
|
||||
" --name [name] override output name\n"
|
||||
" --output-dir [dir] override output directory (defaults to cwd)\n"
|
||||
@ -270,7 +271,7 @@ static int main0(int argc, char **argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc >= 2 && (strcmp(argv[1], "cc") == 0 ||
|
||||
if (argc >= 2 && (strcmp(argv[1], "clang") == 0 ||
|
||||
strcmp(argv[1], "-cc1") == 0 || strcmp(argv[1], "-cc1as") == 0))
|
||||
{
|
||||
return ZigClang_main(argc, argv);
|
||||
@ -429,8 +430,10 @@ static int main0(int argc, char **argv) {
|
||||
bool enable_dump_analysis = false;
|
||||
bool enable_doc_generation = false;
|
||||
bool emit_bin = true;
|
||||
const char *emit_bin_override_path = nullptr;
|
||||
bool emit_asm = false;
|
||||
bool emit_llvm_ir = false;
|
||||
bool emit_h = false;
|
||||
const char *cache_dir = nullptr;
|
||||
CliPkg *cur_pkg = heap::c_allocator.create<CliPkg>();
|
||||
BuildMode build_mode = BuildModeDebug;
|
||||
@ -439,7 +442,6 @@ static int main0(int argc, char **argv) {
|
||||
bool system_linker_hack = false;
|
||||
TargetSubsystem subsystem = TargetSubsystemAuto;
|
||||
bool want_single_threaded = false;
|
||||
bool disable_gen_h = false;
|
||||
bool bundle_compiler_rt = false;
|
||||
Buf *override_lib_dir = nullptr;
|
||||
Buf *main_pkg_path = nullptr;
|
||||
@ -450,6 +452,8 @@ static int main0(int argc, char **argv) {
|
||||
bool function_sections = false;
|
||||
const char *mcpu = nullptr;
|
||||
CodeModel code_model = CodeModelDefault;
|
||||
const char *override_soname = nullptr;
|
||||
bool only_preprocess = false;
|
||||
|
||||
ZigList<const char *> llvm_argv = {0};
|
||||
llvm_argv.append("zig (LLVM option parsing)");
|
||||
@ -574,9 +578,240 @@ static int main0(int argc, char **argv) {
|
||||
return (term.how == TerminationIdClean) ? term.code : -1;
|
||||
} else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) {
|
||||
return stage2_fmt(argc, argv);
|
||||
}
|
||||
} else if (argc >= 2 && strcmp(argv[1], "cc") == 0) {
|
||||
emit_h = false;
|
||||
strip = true;
|
||||
|
||||
for (int i = 1; i < argc; i += 1) {
|
||||
bool c_arg = false;
|
||||
Stage2ClangArgIterator it;
|
||||
stage2_clang_arg_iterator(&it, argc, argv);
|
||||
bool nostdlib = false;
|
||||
bool is_shared_lib = false;
|
||||
ZigList<Buf *> linker_args = {};
|
||||
while (it.has_next) {
|
||||
if ((err = stage2_clang_arg_next(&it))) {
|
||||
fprintf(stderr, "unable to parse command line parameters: %s\n", err_str(err));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
switch (it.kind) {
|
||||
case Stage2ClangArgTarget: // example: -target riscv64-linux-unknown
|
||||
target_string = it.only_arg;
|
||||
break;
|
||||
case Stage2ClangArgO: // -o
|
||||
emit_bin_override_path = it.only_arg;
|
||||
enable_cache = CacheOptOn;
|
||||
break;
|
||||
case Stage2ClangArgC: // -c
|
||||
c_arg = true;
|
||||
break;
|
||||
case Stage2ClangArgOther:
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
break;
|
||||
case Stage2ClangArgPositional: {
|
||||
Buf *arg_buf = buf_create_from_str(it.only_arg);
|
||||
if (buf_ends_with_str(arg_buf, ".c") ||
|
||||
buf_ends_with_str(arg_buf, ".C") ||
|
||||
buf_ends_with_str(arg_buf, ".cc") ||
|
||||
buf_ends_with_str(arg_buf, ".cpp") ||
|
||||
buf_ends_with_str(arg_buf, ".cxx") ||
|
||||
buf_ends_with_str(arg_buf, ".s") ||
|
||||
buf_ends_with_str(arg_buf, ".S"))
|
||||
{
|
||||
CFile *c_file = heap::c_allocator.create<CFile>();
|
||||
c_file->source_path = it.only_arg;
|
||||
c_source_files.append(c_file);
|
||||
} else {
|
||||
objects.append(it.only_arg);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Stage2ClangArgL: // -l
|
||||
if (strcmp(it.only_arg, "c") == 0)
|
||||
have_libc = true;
|
||||
link_libs.append(it.only_arg);
|
||||
break;
|
||||
case Stage2ClangArgIgnore:
|
||||
break;
|
||||
case Stage2ClangArgDriverPunt:
|
||||
// Never mind what we're doing, just pass the args directly. For example --help.
|
||||
return ZigClang_main(argc, argv);
|
||||
case Stage2ClangArgPIC:
|
||||
want_pic = WantPICEnabled;
|
||||
break;
|
||||
case Stage2ClangArgNoPIC:
|
||||
want_pic = WantPICDisabled;
|
||||
break;
|
||||
case Stage2ClangArgNoStdLib:
|
||||
nostdlib = true;
|
||||
break;
|
||||
case Stage2ClangArgShared:
|
||||
is_dynamic = true;
|
||||
is_shared_lib = true;
|
||||
break;
|
||||
case Stage2ClangArgRDynamic:
|
||||
rdynamic = true;
|
||||
break;
|
||||
case Stage2ClangArgWL: {
|
||||
const char *arg = it.only_arg;
|
||||
for (;;) {
|
||||
size_t pos = 0;
|
||||
while (arg[pos] != ',' && arg[pos] != 0) pos += 1;
|
||||
linker_args.append(buf_create_from_mem(arg, pos));
|
||||
if (arg[pos] == 0) break;
|
||||
arg += pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Stage2ClangArgPreprocess:
|
||||
only_preprocess = true;
|
||||
break;
|
||||
case Stage2ClangArgOptimize:
|
||||
// alright what release mode do they want?
|
||||
if (strcmp(it.only_arg, "Os") == 0) {
|
||||
build_mode = BuildModeSmallRelease;
|
||||
} else if (strcmp(it.only_arg, "O2") == 0 ||
|
||||
strcmp(it.only_arg, "O3") == 0 ||
|
||||
strcmp(it.only_arg, "O4") == 0)
|
||||
{
|
||||
build_mode = BuildModeFastRelease;
|
||||
} else if (strcmp(it.only_arg, "Og") == 0) {
|
||||
build_mode = BuildModeDebug;
|
||||
} else {
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Stage2ClangArgDebug:
|
||||
strip = false;
|
||||
if (strcmp(it.only_arg, "-g") == 0) {
|
||||
// we handled with strip = false above
|
||||
} else {
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Stage2ClangArgSanitize:
|
||||
if (strcmp(it.only_arg, "undefined") == 0) {
|
||||
want_sanitize_c = WantCSanitizeEnabled;
|
||||
} else {
|
||||
for (size_t i = 0; i < it.other_args_len; i += 1) {
|
||||
clang_argv.append(it.other_args_ptr[i]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Parse linker args
|
||||
for (size_t i = 0; i < linker_args.length; i += 1) {
|
||||
Buf *arg = linker_args.at(i);
|
||||
if (buf_eql_str(arg, "-soname")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.length) {
|
||||
fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Buf *soname_buf = linker_args.at(i);
|
||||
override_soname = buf_ptr(soname_buf);
|
||||
// use it as --name
|
||||
// example: libsoundio.so.2
|
||||
size_t prefix = 0;
|
||||
if (buf_starts_with_str(soname_buf, "lib")) {
|
||||
prefix = 3;
|
||||
}
|
||||
size_t end = buf_len(soname_buf);
|
||||
if (buf_ends_with_str(soname_buf, ".so")) {
|
||||
end -= 3;
|
||||
} else {
|
||||
bool found_digit = false;
|
||||
while (end > 0 && isdigit(buf_ptr(soname_buf)[end - 1])) {
|
||||
found_digit = true;
|
||||
end -= 1;
|
||||
}
|
||||
if (found_digit && end > 0 && buf_ptr(soname_buf)[end - 1] == '.') {
|
||||
end -= 1;
|
||||
} else {
|
||||
end = buf_len(soname_buf);
|
||||
}
|
||||
if (buf_ends_with_str(buf_slice(soname_buf, prefix, end), ".so")) {
|
||||
end -= 3;
|
||||
}
|
||||
}
|
||||
out_name = buf_ptr(buf_slice(soname_buf, prefix, end));
|
||||
} else if (buf_eql_str(arg, "-rpath")) {
|
||||
i += 1;
|
||||
if (i >= linker_args.length) {
|
||||
fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Buf *rpath = linker_args.at(i);
|
||||
rpath_list.append(buf_ptr(rpath));
|
||||
} else if (buf_eql_str(arg, "-I") ||
|
||||
buf_eql_str(arg, "--dynamic-linker") ||
|
||||
buf_eql_str(arg, "-dynamic-linker"))
|
||||
{
|
||||
i += 1;
|
||||
if (i >= linker_args.length) {
|
||||
fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
dynamic_linker = buf_ptr(linker_args.at(i));
|
||||
} else {
|
||||
fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg));
|
||||
}
|
||||
}
|
||||
|
||||
if (want_sanitize_c == WantCSanitizeEnabled && build_mode == BuildModeFastRelease) {
|
||||
build_mode = BuildModeSafeRelease;
|
||||
}
|
||||
|
||||
if (!nostdlib && !have_libc) {
|
||||
have_libc = true;
|
||||
link_libs.append("c");
|
||||
}
|
||||
if (only_preprocess) {
|
||||
cmd = CmdBuild;
|
||||
out_type = OutTypeObj;
|
||||
emit_bin = false;
|
||||
// Transfer "objects" into c_source_files
|
||||
for (size_t i = 0; i < objects.length; i += 1) {
|
||||
CFile *c_file = heap::c_allocator.create<CFile>();
|
||||
c_file->source_path = objects.at(i);
|
||||
c_source_files.append(c_file);
|
||||
}
|
||||
for (size_t i = 0; i < c_source_files.length; i += 1) {
|
||||
Buf *src_path;
|
||||
if (emit_bin_override_path != nullptr) {
|
||||
src_path = buf_create_from_str(emit_bin_override_path);
|
||||
} else {
|
||||
src_path = buf_create_from_str(c_source_files.at(i)->source_path);
|
||||
}
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(src_path, nullptr, &basename);
|
||||
c_source_files.at(i)->preprocessor_only_basename = buf_ptr(&basename);
|
||||
}
|
||||
} else if (!c_arg) {
|
||||
cmd = CmdBuild;
|
||||
if (is_shared_lib) {
|
||||
out_type = OutTypeLib;
|
||||
} else {
|
||||
out_type = OutTypeExe;
|
||||
}
|
||||
if (emit_bin_override_path == nullptr) {
|
||||
emit_bin_override_path = "a.out";
|
||||
}
|
||||
} else {
|
||||
cmd = CmdBuild;
|
||||
out_type = OutTypeObj;
|
||||
}
|
||||
if (c_source_files.length == 0 && objects.length == 0) {
|
||||
// For example `zig cc` and no args should print the "no input files" message.
|
||||
return ZigClang_main(argc, argv);
|
||||
}
|
||||
} else for (int i = 1; i < argc; i += 1) {
|
||||
char *arg = argv[i];
|
||||
|
||||
if (arg[0] == '-') {
|
||||
@ -660,9 +895,7 @@ static int main0(int argc, char **argv) {
|
||||
} else if (strcmp(arg, "--system-linker-hack") == 0) {
|
||||
system_linker_hack = true;
|
||||
} else if (strcmp(arg, "--single-threaded") == 0) {
|
||||
want_single_threaded = true;
|
||||
} else if (strcmp(arg, "--disable-gen-h") == 0) {
|
||||
disable_gen_h = true;
|
||||
want_single_threaded = true;;
|
||||
} else if (strcmp(arg, "--bundle-compiler-rt") == 0) {
|
||||
bundle_compiler_rt = true;
|
||||
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
|
||||
@ -719,6 +952,11 @@ static int main0(int argc, char **argv) {
|
||||
emit_llvm_ir = true;
|
||||
} else if (strcmp(arg, "-fno-emit-llvm-ir") == 0) {
|
||||
emit_llvm_ir = false;
|
||||
} else if (strcmp(arg, "-femit-h") == 0) {
|
||||
emit_h = true;
|
||||
} else if (strcmp(arg, "-fno-emit-h") == 0 || strcmp(arg, "--disable-gen-h") == 0) {
|
||||
// the --disable-gen-h is there to support godbolt. once they upgrade to -fno-emit-h then we can remove this
|
||||
emit_h = false;
|
||||
} else if (str_starts_with(arg, "-mcpu=")) {
|
||||
mcpu = arg + strlen("-mcpu=");
|
||||
} else if (i + 1 >= argc) {
|
||||
@ -1134,6 +1372,18 @@ static int main0(int argc, char **argv) {
|
||||
buf_out_name = buf_alloc();
|
||||
os_path_extname(&basename, buf_out_name, nullptr);
|
||||
}
|
||||
if (need_name && buf_out_name == nullptr && objects.length == 1) {
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(buf_create_from_str(objects.at(0)), nullptr, &basename);
|
||||
buf_out_name = buf_alloc();
|
||||
os_path_extname(&basename, buf_out_name, nullptr);
|
||||
}
|
||||
if (need_name && buf_out_name == nullptr && emit_bin_override_path != nullptr) {
|
||||
Buf basename = BUF_INIT;
|
||||
os_path_split(buf_create_from_str(emit_bin_override_path), nullptr, &basename);
|
||||
buf_out_name = buf_alloc();
|
||||
os_path_extname(&basename, buf_out_name, nullptr);
|
||||
}
|
||||
|
||||
if (need_name && buf_out_name == nullptr) {
|
||||
fprintf(stderr, "--name [name] not provided and unable to infer\n\n");
|
||||
@ -1202,13 +1452,17 @@ static int main0(int argc, char **argv) {
|
||||
g->verbose_cc = verbose_cc;
|
||||
g->verbose_llvm_cpu_features = verbose_llvm_cpu_features;
|
||||
g->output_dir = output_dir;
|
||||
g->disable_gen_h = disable_gen_h;
|
||||
g->disable_gen_h = !emit_h;
|
||||
g->bundle_compiler_rt = bundle_compiler_rt;
|
||||
codegen_set_errmsg_color(g, color);
|
||||
g->system_linker_hack = system_linker_hack;
|
||||
g->function_sections = function_sections;
|
||||
g->code_model = code_model;
|
||||
|
||||
if (override_soname) {
|
||||
g->override_soname = buf_create_from_str(override_soname);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < lib_dirs.length; i += 1) {
|
||||
codegen_add_lib_dir(g, lib_dirs.at(i));
|
||||
}
|
||||
@ -1287,9 +1541,46 @@ static int main0(int argc, char **argv) {
|
||||
os_spawn_process(args, &term);
|
||||
return term.code;
|
||||
} else if (cmd == CmdBuild) {
|
||||
if (g->enable_cache) {
|
||||
if (emit_bin_override_path != nullptr) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_replace(g->output_dir, '/', '\\');
|
||||
#endif
|
||||
Buf *dest_path = buf_create_from_str(emit_bin_override_path);
|
||||
Buf *source_path;
|
||||
if (only_preprocess) {
|
||||
source_path = buf_alloc();
|
||||
Buf *pp_only_basename = buf_create_from_str(
|
||||
c_source_files.at(0)->preprocessor_only_basename);
|
||||
os_path_join(g->output_dir, pp_only_basename, source_path);
|
||||
|
||||
} else {
|
||||
source_path = &g->bin_file_output_path;
|
||||
}
|
||||
if ((err = os_update_file(source_path, dest_path))) {
|
||||
fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(source_path),
|
||||
buf_ptr(dest_path), err_str(err));
|
||||
return main_exit(root_progress_node, EXIT_FAILURE);
|
||||
}
|
||||
} else if (only_preprocess) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_replace(g->c_artifact_dir, '/', '\\');
|
||||
#endif
|
||||
// dump the preprocessed output to stdout
|
||||
for (size_t i = 0; i < c_source_files.length; i += 1) {
|
||||
Buf *source_path = buf_alloc();
|
||||
Buf *pp_only_basename = buf_create_from_str(
|
||||
c_source_files.at(i)->preprocessor_only_basename);
|
||||
os_path_join(g->c_artifact_dir, pp_only_basename, source_path);
|
||||
if ((err = os_dump_file(source_path, stdout))) {
|
||||
fprintf(stderr, "unable to read %s: %s\n", buf_ptr(source_path),
|
||||
err_str(err));
|
||||
return main_exit(root_progress_node, EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
} else if (g->enable_cache) {
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
buf_replace(&g->bin_file_output_path, '/', '\\');
|
||||
buf_replace(g->output_dir, '/', '\\');
|
||||
#endif
|
||||
if (final_output_dir_step != nullptr) {
|
||||
Buf *dest_basename = buf_alloc();
|
||||
@ -1303,7 +1594,7 @@ static int main0(int argc, char **argv) {
|
||||
return main_exit(root_progress_node, EXIT_FAILURE);
|
||||
}
|
||||
} else {
|
||||
if (g->emit_bin && printf("%s\n", buf_ptr(&g->bin_file_output_path)) < 0)
|
||||
if (printf("%s\n", buf_ptr(g->output_dir)) < 0)
|
||||
return main_exit(root_progress_node, EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
24
src/os.cpp
24
src/os.cpp
@ -1051,6 +1051,30 @@ static Error copy_open_files(FILE *src_f, FILE *dest_f) {
|
||||
}
|
||||
}
|
||||
|
||||
Error os_dump_file(Buf *src_path, FILE *dest_file) {
|
||||
Error err;
|
||||
|
||||
FILE *src_f = fopen(buf_ptr(src_path), "rb");
|
||||
if (!src_f) {
|
||||
int err = errno;
|
||||
if (err == ENOENT) {
|
||||
return ErrorFileNotFound;
|
||||
} else if (err == EACCES || err == EPERM) {
|
||||
return ErrorAccess;
|
||||
} else {
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
copy_open_files(src_f, dest_file);
|
||||
if ((err = copy_open_files(src_f, dest_file))) {
|
||||
fclose(src_f);
|
||||
return err;
|
||||
}
|
||||
|
||||
fclose(src_f);
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
#if defined(ZIG_OS_WINDOWS)
|
||||
static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) {
|
||||
mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime;
|
||||
|
||||
@ -129,6 +129,7 @@ void os_file_close(OsFile *file);
|
||||
Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents);
|
||||
Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path);
|
||||
Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path);
|
||||
Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file);
|
||||
|
||||
Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents);
|
||||
Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents);
|
||||
|
||||
@ -165,22 +165,36 @@ static long long scanexp(struct MuslFILE *f, int pok)
|
||||
int x;
|
||||
long long y;
|
||||
int neg = 0;
|
||||
|
||||
|
||||
c = shgetc(f);
|
||||
if (c=='+' || c=='-') {
|
||||
neg = (c=='-');
|
||||
c = shgetc(f);
|
||||
if (c-'0'>=10U && pok) shunget(f);
|
||||
}
|
||||
if (c-'0'>=10U) {
|
||||
if (c-'0'>=10U && c!='_') {
|
||||
shunget(f);
|
||||
return LLONG_MIN;
|
||||
}
|
||||
for (x=0; c-'0'<10U && x<INT_MAX/10; c = shgetc(f))
|
||||
x = 10*x + c-'0';
|
||||
for (y=x; c-'0'<10U && y<LLONG_MAX/100; c = shgetc(f))
|
||||
y = 10*y + c-'0';
|
||||
for (; c-'0'<10U; c = shgetc(f));
|
||||
for (x=0; ; c = shgetc(f)) {
|
||||
if (c=='_') {
|
||||
continue;
|
||||
} else if (c-'0'<10U && x<INT_MAX/10) {
|
||||
x = 10*x + c-'0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (y=x; ; c = shgetc(f)) {
|
||||
if (c=='_') {
|
||||
continue;
|
||||
} else if (c-'0'<10U && y<LLONG_MAX/100) {
|
||||
y = 10*y + c-'0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (; c-'0'<10U || c=='_'; c = shgetc(f));
|
||||
shunget(f);
|
||||
return neg ? -y : y;
|
||||
}
|
||||
@ -450,16 +464,36 @@ static float128_t decfloat(struct MuslFILE *f, int c, int bits, int emin, int si
|
||||
j=0;
|
||||
k=0;
|
||||
|
||||
/* Don't let leading zeros consume buffer space */
|
||||
for (; c=='0'; c = shgetc(f)) gotdig=1;
|
||||
/* Don't let leading zeros/underscores consume buffer space */
|
||||
for (; ; c = shgetc(f)) {
|
||||
if (c=='_') {
|
||||
continue;
|
||||
} else if (c=='0') {
|
||||
gotdig=1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (c=='.') {
|
||||
gotrad = 1;
|
||||
for (c = shgetc(f); c=='0'; c = shgetc(f)) gotdig=1, lrp--;
|
||||
for (c = shgetc(f); ; c = shgetc(f)) {
|
||||
if (c == '_') {
|
||||
continue;
|
||||
} else if (c=='0') {
|
||||
gotdig=1;
|
||||
lrp--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
x[0] = 0;
|
||||
for (; c-'0'<10U || c=='.'; c = shgetc(f)) {
|
||||
if (c == '.') {
|
||||
for (; c-'0'<10U || c=='.' || c=='_'; c = shgetc(f)) {
|
||||
if (c == '_') {
|
||||
continue;
|
||||
} else if (c == '.') {
|
||||
if (gotrad) break;
|
||||
gotrad = 1;
|
||||
lrp = dc;
|
||||
@ -773,18 +807,29 @@ static float128_t hexfloat(struct MuslFILE *f, int bits, int emin, int sign, int
|
||||
|
||||
c = shgetc(f);
|
||||
|
||||
/* Skip leading zeros */
|
||||
for (; c=='0'; c = shgetc(f)) gotdig = 1;
|
||||
/* Skip leading zeros/underscores */
|
||||
for (; c=='0' || c=='_'; c = shgetc(f)) gotdig = 1;
|
||||
|
||||
if (c=='.') {
|
||||
gotrad = 1;
|
||||
c = shgetc(f);
|
||||
/* Count zeros after the radix point before significand */
|
||||
for (rp=0; c=='0'; c = shgetc(f), rp--) gotdig = 1;
|
||||
for (rp=0; ; c = shgetc(f)) {
|
||||
if (c == '_') {
|
||||
continue;
|
||||
} else if (c == '0') {
|
||||
gotdig = 1;
|
||||
rp--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; c-'0'<10U || (c|32)-'a'<6U || c=='.'; c = shgetc(f)) {
|
||||
if (c=='.') {
|
||||
for (; c-'0'<10U || (c|32)-'a'<6U || c=='.' || c=='_'; c = shgetc(f)) {
|
||||
if (c=='_') {
|
||||
continue;
|
||||
} else if (c=='.') {
|
||||
if (gotrad) break;
|
||||
rp = dc;
|
||||
gotrad = 1;
|
||||
|
||||
@ -879,7 +879,7 @@ static AstNode *ast_parse_container_field(ParseContext *pc) {
|
||||
// / KEYWORD_noasync BlockExprStatement
|
||||
// / KEYWORD_suspend (SEMICOLON / BlockExprStatement)
|
||||
// / KEYWORD_defer BlockExprStatement
|
||||
// / KEYWORD_errdefer BlockExprStatement
|
||||
// / KEYWORD_errdefer Payload? BlockExprStatement
|
||||
// / IfStatement
|
||||
// / LabeledStatement
|
||||
// / SwitchExpr
|
||||
@ -923,12 +923,18 @@ static AstNode *ast_parse_statement(ParseContext *pc) {
|
||||
if (defer == nullptr)
|
||||
defer = eat_token_if(pc, TokenIdKeywordErrdefer);
|
||||
if (defer != nullptr) {
|
||||
Token *payload = (defer->id == TokenIdKeywordErrdefer) ?
|
||||
ast_parse_payload(pc) : nullptr;
|
||||
AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement);
|
||||
AstNode *res = ast_create_node(pc, NodeTypeDefer, defer);
|
||||
|
||||
res->data.defer.kind = ReturnKindUnconditional;
|
||||
res->data.defer.expr = statement;
|
||||
if (defer->id == TokenIdKeywordErrdefer)
|
||||
if (defer->id == TokenIdKeywordErrdefer) {
|
||||
res->data.defer.kind = ReturnKindError;
|
||||
if (payload != nullptr)
|
||||
res->data.defer.err_payload = token_symbol(pc, payload);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -3032,6 +3038,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
||||
break;
|
||||
case NodeTypeDefer:
|
||||
visit_field(&node->data.defer.expr, visit, context);
|
||||
visit_field(&node->data.defer.err_payload, visit, context);
|
||||
break;
|
||||
case NodeTypeVariableDeclaration:
|
||||
visit_field(&node->data.variable_declaration.type, visit, context);
|
||||
|
||||
@ -304,3 +304,15 @@ enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) {
|
||||
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
|
||||
size_t argc, char **argv)
|
||||
{
|
||||
const char *msg = "stage0 called stage2_clang_arg_iterator";
|
||||
stage2_panic(msg, strlen(msg));
|
||||
}
|
||||
|
||||
enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) {
|
||||
const char *msg = "stage0 called stage2_clang_arg_next";
|
||||
stage2_panic(msg, strlen(msg));
|
||||
}
|
||||
|
||||
47
src/stage2.h
47
src/stage2.h
@ -105,6 +105,10 @@ enum Error {
|
||||
ErrorTargetHasNoDynamicLinker,
|
||||
ErrorInvalidAbiVersion,
|
||||
ErrorInvalidOperatingSystemVersion,
|
||||
ErrorUnknownClangOption,
|
||||
ErrorPermissionDenied,
|
||||
ErrorFileBusy,
|
||||
ErrorLocked,
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
@ -291,6 +295,7 @@ struct ZigTarget {
|
||||
size_t cache_hash_len;
|
||||
const char *os_builtin_str;
|
||||
const char *dynamic_linker;
|
||||
const char *standard_dynamic_linker_path;
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
@ -315,4 +320,46 @@ struct Stage2NativePaths {
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths);
|
||||
|
||||
// ABI warning
|
||||
enum Stage2ClangArg {
|
||||
Stage2ClangArgTarget,
|
||||
Stage2ClangArgO,
|
||||
Stage2ClangArgC,
|
||||
Stage2ClangArgOther,
|
||||
Stage2ClangArgPositional,
|
||||
Stage2ClangArgL,
|
||||
Stage2ClangArgIgnore,
|
||||
Stage2ClangArgDriverPunt,
|
||||
Stage2ClangArgPIC,
|
||||
Stage2ClangArgNoPIC,
|
||||
Stage2ClangArgNoStdLib,
|
||||
Stage2ClangArgShared,
|
||||
Stage2ClangArgRDynamic,
|
||||
Stage2ClangArgWL,
|
||||
Stage2ClangArgPreprocess,
|
||||
Stage2ClangArgOptimize,
|
||||
Stage2ClangArgDebug,
|
||||
Stage2ClangArgSanitize,
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
struct Stage2ClangArgIterator {
|
||||
bool has_next;
|
||||
enum Stage2ClangArg kind;
|
||||
const char *only_arg;
|
||||
const char *second_arg;
|
||||
const char **other_args_ptr;
|
||||
size_t other_args_len;
|
||||
const char **argv_ptr;
|
||||
size_t argv_len;
|
||||
size_t next_index;
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it,
|
||||
size_t argc, char **argv);
|
||||
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it);
|
||||
|
||||
#endif
|
||||
|
||||
@ -177,10 +177,13 @@ enum TokenizeState {
|
||||
TokenizeStateSymbol,
|
||||
TokenizeStateZero, // "0", which might lead to "0x"
|
||||
TokenizeStateNumber, // "123", "0x123"
|
||||
TokenizeStateNumberNoUnderscore, // "12_", "0x12_" next char must be digit
|
||||
TokenizeStateNumberDot,
|
||||
TokenizeStateFloatFraction, // "123.456", "0x123.456"
|
||||
TokenizeStateFloatFractionNoUnderscore, // "123.45_", "0x123.45_"
|
||||
TokenizeStateFloatExponentUnsigned, // "123.456e", "123e", "0x123p"
|
||||
TokenizeStateFloatExponentNumber, // "123.456e-", "123.456e5", "123.456e5e-5"
|
||||
TokenizeStateFloatExponentNumber, // "123.456e7", "123.456e+7", "123.456e-7"
|
||||
TokenizeStateFloatExponentNumberNoUnderscore, // "123.456e7_", "123.456e+7_", "123.456e-7_"
|
||||
TokenizeStateString,
|
||||
TokenizeStateStringEscape,
|
||||
TokenizeStateStringEscapeUnicodeStart,
|
||||
@ -233,14 +236,10 @@ struct Tokenize {
|
||||
Token *cur_tok;
|
||||
Tokenization *out;
|
||||
uint32_t radix;
|
||||
int32_t exp_add_amt;
|
||||
bool is_exp_negative;
|
||||
bool is_trailing_underscore;
|
||||
size_t char_code_index;
|
||||
bool unicode;
|
||||
uint32_t char_code;
|
||||
int exponent_in_bin_or_dec;
|
||||
BigInt specified_exponent;
|
||||
BigInt significand;
|
||||
size_t remaining_code_units;
|
||||
};
|
||||
|
||||
@ -426,20 +425,16 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
case '0':
|
||||
t.state = TokenizeStateZero;
|
||||
begin_token(&t, TokenIdIntLiteral);
|
||||
t.is_trailing_underscore = false;
|
||||
t.radix = 10;
|
||||
t.exp_add_amt = 1;
|
||||
t.exponent_in_bin_or_dec = 0;
|
||||
bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0);
|
||||
bigint_init_unsigned(&t.specified_exponent, 0);
|
||||
break;
|
||||
case DIGIT_NON_ZERO:
|
||||
t.state = TokenizeStateNumber;
|
||||
begin_token(&t, TokenIdIntLiteral);
|
||||
t.is_trailing_underscore = false;
|
||||
t.radix = 10;
|
||||
t.exp_add_amt = 1;
|
||||
t.exponent_in_bin_or_dec = 0;
|
||||
bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c));
|
||||
bigint_init_unsigned(&t.specified_exponent, 0);
|
||||
break;
|
||||
case '"':
|
||||
begin_token(&t, TokenIdStringLiteral);
|
||||
@ -1189,17 +1184,15 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
switch (c) {
|
||||
case 'b':
|
||||
t.radix = 2;
|
||||
t.state = TokenizeStateNumber;
|
||||
t.state = TokenizeStateNumberNoUnderscore;
|
||||
break;
|
||||
case 'o':
|
||||
t.radix = 8;
|
||||
t.exp_add_amt = 3;
|
||||
t.state = TokenizeStateNumber;
|
||||
t.state = TokenizeStateNumberNoUnderscore;
|
||||
break;
|
||||
case 'x':
|
||||
t.radix = 16;
|
||||
t.exp_add_amt = 4;
|
||||
t.state = TokenizeStateNumber;
|
||||
t.state = TokenizeStateNumberNoUnderscore;
|
||||
break;
|
||||
default:
|
||||
// reinterpret as normal number
|
||||
@ -1208,9 +1201,27 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateNumberNoUnderscore:
|
||||
if (c == '_') {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
} else if (get_digit_value(c) < t.radix) {
|
||||
t.is_trailing_underscore = false;
|
||||
t.state = TokenizeStateNumber;
|
||||
}
|
||||
// fall through
|
||||
case TokenizeStateNumber:
|
||||
{
|
||||
if (c == '_') {
|
||||
t.is_trailing_underscore = true;
|
||||
t.state = TokenizeStateNumberNoUnderscore;
|
||||
break;
|
||||
}
|
||||
if (c == '.') {
|
||||
if (t.is_trailing_underscore) {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
}
|
||||
if (t.radix != 16 && t.radix != 10) {
|
||||
invalid_char_error(&t, c);
|
||||
}
|
||||
@ -1218,17 +1229,26 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
break;
|
||||
}
|
||||
if (is_exponent_signifier(c, t.radix)) {
|
||||
if (t.is_trailing_underscore) {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
}
|
||||
if (t.radix != 16 && t.radix != 10) {
|
||||
invalid_char_error(&t, c);
|
||||
}
|
||||
t.state = TokenizeStateFloatExponentUnsigned;
|
||||
t.radix = 10; // exponent is always base 10
|
||||
assert(t.cur_tok->id == TokenIdIntLiteral);
|
||||
bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint);
|
||||
set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
|
||||
break;
|
||||
}
|
||||
uint32_t digit_value = get_digit_value(c);
|
||||
if (digit_value >= t.radix) {
|
||||
if (t.is_trailing_underscore) {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
}
|
||||
|
||||
if (is_symbol_char(c)) {
|
||||
invalid_char_error(&t, c);
|
||||
}
|
||||
@ -1259,20 +1279,41 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
continue;
|
||||
}
|
||||
t.pos -= 1;
|
||||
t.state = TokenizeStateFloatFraction;
|
||||
t.state = TokenizeStateFloatFractionNoUnderscore;
|
||||
assert(t.cur_tok->id == TokenIdIntLiteral);
|
||||
bigint_init_bigint(&t.significand, &t.cur_tok->data.int_lit.bigint);
|
||||
set_token_id(&t, t.cur_tok, TokenIdFloatLiteral);
|
||||
continue;
|
||||
}
|
||||
case TokenizeStateFloatFractionNoUnderscore:
|
||||
if (c == '_') {
|
||||
invalid_char_error(&t, c);
|
||||
} else if (get_digit_value(c) < t.radix) {
|
||||
t.is_trailing_underscore = false;
|
||||
t.state = TokenizeStateFloatFraction;
|
||||
}
|
||||
// fall through
|
||||
case TokenizeStateFloatFraction:
|
||||
{
|
||||
if (c == '_') {
|
||||
t.is_trailing_underscore = true;
|
||||
t.state = TokenizeStateFloatFractionNoUnderscore;
|
||||
break;
|
||||
}
|
||||
if (is_exponent_signifier(c, t.radix)) {
|
||||
if (t.is_trailing_underscore) {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
}
|
||||
t.state = TokenizeStateFloatExponentUnsigned;
|
||||
t.radix = 10; // exponent is always base 10
|
||||
break;
|
||||
}
|
||||
uint32_t digit_value = get_digit_value(c);
|
||||
if (digit_value >= t.radix) {
|
||||
if (t.is_trailing_underscore) {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
}
|
||||
if (is_symbol_char(c)) {
|
||||
invalid_char_error(&t, c);
|
||||
}
|
||||
@ -1282,46 +1323,47 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
t.exponent_in_bin_or_dec -= t.exp_add_amt;
|
||||
if (t.radix == 10) {
|
||||
// For now we use strtod to parse decimal floats, so we just have to get to the
|
||||
// end of the token.
|
||||
break;
|
||||
}
|
||||
BigInt digit_value_bi;
|
||||
bigint_init_unsigned(&digit_value_bi, digit_value);
|
||||
|
||||
BigInt radix_bi;
|
||||
bigint_init_unsigned(&radix_bi, t.radix);
|
||||
|
||||
BigInt multiplied;
|
||||
bigint_mul(&multiplied, &t.significand, &radix_bi);
|
||||
|
||||
bigint_add(&t.significand, &multiplied, &digit_value_bi);
|
||||
break;
|
||||
// we use parse_f128 to generate the float literal, so just
|
||||
// need to get to the end of the token
|
||||
}
|
||||
break;
|
||||
case TokenizeStateFloatExponentUnsigned:
|
||||
switch (c) {
|
||||
case '+':
|
||||
t.is_exp_negative = false;
|
||||
t.state = TokenizeStateFloatExponentNumber;
|
||||
t.state = TokenizeStateFloatExponentNumberNoUnderscore;
|
||||
break;
|
||||
case '-':
|
||||
t.is_exp_negative = true;
|
||||
t.state = TokenizeStateFloatExponentNumber;
|
||||
t.state = TokenizeStateFloatExponentNumberNoUnderscore;
|
||||
break;
|
||||
default:
|
||||
// reinterpret as normal exponent number
|
||||
t.pos -= 1;
|
||||
t.is_exp_negative = false;
|
||||
t.state = TokenizeStateFloatExponentNumber;
|
||||
t.state = TokenizeStateFloatExponentNumberNoUnderscore;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case TokenizeStateFloatExponentNumberNoUnderscore:
|
||||
if (c == '_') {
|
||||
invalid_char_error(&t, c);
|
||||
} else if (get_digit_value(c) < t.radix) {
|
||||
t.is_trailing_underscore = false;
|
||||
t.state = TokenizeStateFloatExponentNumber;
|
||||
}
|
||||
// fall through
|
||||
case TokenizeStateFloatExponentNumber:
|
||||
{
|
||||
if (c == '_') {
|
||||
t.is_trailing_underscore = true;
|
||||
t.state = TokenizeStateFloatExponentNumberNoUnderscore;
|
||||
break;
|
||||
}
|
||||
uint32_t digit_value = get_digit_value(c);
|
||||
if (digit_value >= t.radix) {
|
||||
if (t.is_trailing_underscore) {
|
||||
invalid_char_error(&t, c);
|
||||
break;
|
||||
}
|
||||
if (is_symbol_char(c)) {
|
||||
invalid_char_error(&t, c);
|
||||
}
|
||||
@ -1331,21 +1373,9 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
t.state = TokenizeStateStart;
|
||||
continue;
|
||||
}
|
||||
if (t.radix == 10) {
|
||||
// For now we use strtod to parse decimal floats, so we just have to get to the
|
||||
// end of the token.
|
||||
break;
|
||||
}
|
||||
BigInt digit_value_bi;
|
||||
bigint_init_unsigned(&digit_value_bi, digit_value);
|
||||
|
||||
BigInt radix_bi;
|
||||
bigint_init_unsigned(&radix_bi, 10);
|
||||
|
||||
BigInt multiplied;
|
||||
bigint_mul(&multiplied, &t.specified_exponent, &radix_bi);
|
||||
|
||||
bigint_add(&t.specified_exponent, &multiplied, &digit_value_bi);
|
||||
// we use parse_f128 to generate the float literal, so just
|
||||
// need to get to the end of the token
|
||||
}
|
||||
break;
|
||||
case TokenizeStateSawDash:
|
||||
@ -1399,6 +1429,9 @@ void tokenize(Buf *buf, Tokenization *out) {
|
||||
case TokenizeStateStart:
|
||||
case TokenizeStateError:
|
||||
break;
|
||||
case TokenizeStateNumberNoUnderscore:
|
||||
case TokenizeStateFloatFractionNoUnderscore:
|
||||
case TokenizeStateFloatExponentNumberNoUnderscore:
|
||||
case TokenizeStateNumberDot:
|
||||
tokenize_error(&t, "unterminated number literal");
|
||||
break;
|
||||
|
||||
@ -36,7 +36,7 @@ pub fn main() !void {
|
||||
testMissingOutputPath,
|
||||
};
|
||||
for (test_fns) |testFn| {
|
||||
try fs.deleteTree(dir_path);
|
||||
try fs.cwd().deleteTree(dir_path);
|
||||
try fs.cwd().makeDir(dir_path);
|
||||
try testFn(zig_exe, dir_path);
|
||||
}
|
||||
|
||||
@ -292,7 +292,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\pub export fn main() c_int {
|
||||
\\ var array = [_]u32{ 1, 7, 3, 2, 0, 9, 4, 8, 6, 5 };
|
||||
\\
|
||||
\\ c.qsort(@ptrCast(?*c_void, array[0..].ptr), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn);
|
||||
\\ c.qsort(@ptrCast(?*c_void, &array), @intCast(c_ulong, array.len), @sizeOf(i32), compare_fn);
|
||||
\\
|
||||
\\ for (array) |item, i| {
|
||||
\\ if (item != i) {
|
||||
|
||||
@ -2,6 +2,29 @@ const tests = @import("tests.zig");
|
||||
const std = @import("std");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.addTest("unused variable error on errdefer",
|
||||
\\fn foo() !void {
|
||||
\\ errdefer |a| unreachable;
|
||||
\\ return error.A;
|
||||
\\}
|
||||
\\export fn entry() void {
|
||||
\\ foo() catch unreachable;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:15: error: unused variable: 'a'",
|
||||
});
|
||||
|
||||
cases.addTest("comparison of non-tagged union and enum literal",
|
||||
\\export fn entry() void {
|
||||
\\ const U = union { A: u32, B: u64 };
|
||||
\\ var u = U{ .A = 42 };
|
||||
\\ var ok = u == .A;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:4:16: error: comparison of union and enum literal is only valid for tagged union types",
|
||||
"tmp.zig:2:15: note: type U is not a tagged union",
|
||||
});
|
||||
|
||||
cases.addTest("shift on type with non-power-of-two size",
|
||||
\\export fn entry() void {
|
||||
\\ const S = struct {
|
||||
@ -103,18 +126,6 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
"tmp.zig:3:23: error: pointer to size 0 type has no address",
|
||||
});
|
||||
|
||||
cases.addTest("slice to pointer conversion mismatch",
|
||||
\\pub fn bytesAsSlice(bytes: var) [*]align(1) const u16 {
|
||||
\\ return @ptrCast([*]align(1) const u16, bytes.ptr)[0..1];
|
||||
\\}
|
||||
\\test "bytesAsSlice" {
|
||||
\\ const bytes = [_]u8{ 0xDE, 0xAD, 0xBE, 0xEF };
|
||||
\\ const slice = bytesAsSlice(bytes[0..]);
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:54: error: expected type '[*]align(1) const u16', found '[]align(1) const u16'",
|
||||
});
|
||||
|
||||
cases.addTest("access invalid @typeInfo decl",
|
||||
\\const A = B;
|
||||
\\test "Crash" {
|
||||
@ -384,11 +395,163 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\ var bad_float :f32 = 0.0;
|
||||
\\ bad_float = bad_float + .20;
|
||||
\\ std.debug.assert(bad_float < 1.0);
|
||||
\\})
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:5:29: error: invalid token: '.'",
|
||||
});
|
||||
|
||||
cases.add("invalid exponent in float literal - 1",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0x1.0p1ab1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:28: error: invalid character: 'a'",
|
||||
});
|
||||
|
||||
cases.add("invalid exponent in float literal - 2",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0x1.0p50F;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:29: error: invalid character: 'F'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 1",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0._0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:23: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 2",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0_.0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:23: error: invalid character: '.'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 3",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0.0_;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:25: error: invalid character: ';'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 4",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1.0e_1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:25: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 5",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1.0e+_1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:26: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 6",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1.0e-_1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:26: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 7",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1.0e-1_;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:28: error: invalid character: ';'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 9",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1__0.0e-1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:23: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 10",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1.0__0e-1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:25: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 11",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 1.0e-1__0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:28: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 12",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0_x0.0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:23: error: invalid character: 'x'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 13",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0x_0.0;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:23: error: invalid character: '_'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in float literal - 14",
|
||||
\\fn main() void {
|
||||
\\ var bad: f128 = 0x0.0_p1;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:27: error: invalid character: 'p'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in int literal - 1",
|
||||
\\fn main() void {
|
||||
\\ var bad: u128 = 0010_;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:26: error: invalid character: ';'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in int literal - 2",
|
||||
\\fn main() void {
|
||||
\\ var bad: u128 = 0b0010_;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:28: error: invalid character: ';'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in int literal - 3",
|
||||
\\fn main() void {
|
||||
\\ var bad: u128 = 0o0010_;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:28: error: invalid character: ';'",
|
||||
});
|
||||
|
||||
cases.add("invalid underscore placement in int literal - 4",
|
||||
\\fn main() void {
|
||||
\\ var bad: u128 = 0x0010_;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
"tmp.zig:2:28: error: invalid character: ';'",
|
||||
});
|
||||
|
||||
cases.add("var args without c calling conv",
|
||||
\\fn foo(args: ...) void {}
|
||||
\\comptime {
|
||||
@ -1918,8 +2081,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add("reading past end of pointer casted array",
|
||||
\\comptime {
|
||||
\\ const array: [4]u8 = "aoeu".*;
|
||||
\\ const slice = array[1..];
|
||||
\\ const int_ptr = @ptrCast(*const u24, slice.ptr);
|
||||
\\ const sub_array = array[1..];
|
||||
\\ const int_ptr = @ptrCast(*const u24, sub_array);
|
||||
\\ const deref = int_ptr.*;
|
||||
\\}
|
||||
, &[_][]const u8{
|
||||
|
||||
@ -69,7 +69,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
|
||||
\\}
|
||||
\\pub fn main() void {
|
||||
\\ var buf: [4]u8 = undefined;
|
||||
\\ const ptr = buf[0..].ptr;
|
||||
\\ const ptr: [*]u8 = &buf;
|
||||
\\ const slice = ptr[0..3 :0];
|
||||
\\}
|
||||
);
|
||||
|
||||
@ -5,10 +5,17 @@ const builtin = @import("builtin");
|
||||
var foo: u8 align(4) = 100;
|
||||
|
||||
test "global variable alignment" {
|
||||
expect(@TypeOf(&foo).alignment == 4);
|
||||
expect(@TypeOf(&foo) == *align(4) u8);
|
||||
const slice = @as(*[1]u8, &foo)[0..];
|
||||
expect(@TypeOf(slice) == []align(4) u8);
|
||||
comptime expect(@TypeOf(&foo).alignment == 4);
|
||||
comptime expect(@TypeOf(&foo) == *align(4) u8);
|
||||
{
|
||||
const slice = @as(*[1]u8, &foo)[0..];
|
||||
comptime expect(@TypeOf(slice) == *align(4) [1]u8);
|
||||
}
|
||||
{
|
||||
var runtime_zero: usize = 0;
|
||||
const slice = @as(*[1]u8, &foo)[runtime_zero..];
|
||||
comptime expect(@TypeOf(slice) == []align(4) u8);
|
||||
}
|
||||
}
|
||||
|
||||
fn derp() align(@sizeOf(usize) * 2) i32 {
|
||||
@ -171,18 +178,19 @@ test "runtime known array index has best alignment possible" {
|
||||
|
||||
// because pointer is align 2 and u32 align % 2 == 0 we can assume align 2
|
||||
var smaller align(2) = [_]u32{ 1, 2, 3, 4 };
|
||||
comptime expect(@TypeOf(smaller[0..]) == []align(2) u32);
|
||||
comptime expect(@TypeOf(smaller[0..].ptr) == [*]align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 0, *align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 1, *align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 2, *align(2) u32);
|
||||
testIndex(smaller[0..].ptr, 3, *align(2) u32);
|
||||
var runtime_zero: usize = 0;
|
||||
comptime expect(@TypeOf(smaller[runtime_zero..]) == []align(2) u32);
|
||||
comptime expect(@TypeOf(smaller[runtime_zero..].ptr) == [*]align(2) u32);
|
||||
testIndex(smaller[runtime_zero..].ptr, 0, *align(2) u32);
|
||||
testIndex(smaller[runtime_zero..].ptr, 1, *align(2) u32);
|
||||
testIndex(smaller[runtime_zero..].ptr, 2, *align(2) u32);
|
||||
testIndex(smaller[runtime_zero..].ptr, 3, *align(2) u32);
|
||||
|
||||
// has to use ABI alignment because index known at runtime only
|
||||
testIndex2(array[0..].ptr, 0, *u8);
|
||||
testIndex2(array[0..].ptr, 1, *u8);
|
||||
testIndex2(array[0..].ptr, 2, *u8);
|
||||
testIndex2(array[0..].ptr, 3, *u8);
|
||||
testIndex2(array[runtime_zero..].ptr, 0, *u8);
|
||||
testIndex2(array[runtime_zero..].ptr, 1, *u8);
|
||||
testIndex2(array[runtime_zero..].ptr, 2, *u8);
|
||||
testIndex2(array[runtime_zero..].ptr, 3, *u8);
|
||||
}
|
||||
fn testIndex(smaller: [*]align(2) u32, index: usize, comptime T: type) void {
|
||||
comptime expect(@TypeOf(&smaller[index]) == T);
|
||||
|
||||
@ -28,6 +28,24 @@ fn getArrayLen(a: []const u32) usize {
|
||||
return a.len;
|
||||
}
|
||||
|
||||
test "array with sentinels" {
|
||||
const S = struct {
|
||||
fn doTheTest(is_ct: bool) void {
|
||||
var zero_sized: [0:0xde]u8 = [_:0xde]u8{};
|
||||
expectEqual(@as(u8, 0xde), zero_sized[0]);
|
||||
// Disabled at runtime because of
|
||||
// https://github.com/ziglang/zig/issues/4372
|
||||
if (is_ct) {
|
||||
var reinterpreted = @ptrCast(*[1]u8, &zero_sized);
|
||||
expectEqual(@as(u8, 0xde), reinterpreted[0]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
S.doTheTest(false);
|
||||
comptime S.doTheTest(true);
|
||||
}
|
||||
|
||||
test "void arrays" {
|
||||
var array: [4]void = undefined;
|
||||
array[0] = void{};
|
||||
@ -376,3 +394,23 @@ test "type deduction for array subscript expression" {
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "sentinel element count towards the ABI size calculation" {
|
||||
const S = struct {
|
||||
fn doTheTest() void {
|
||||
const T = packed struct {
|
||||
fill_pre: u8 = 0x55,
|
||||
data: [0:0]u8 = undefined,
|
||||
fill_post: u8 = 0xAA,
|
||||
};
|
||||
var x = T{};
|
||||
var as_slice = mem.asBytes(&x);
|
||||
expectEqual(@as(usize, 3), as_slice.len);
|
||||
expectEqual(@as(u8, 0x55), as_slice[0]);
|
||||
expectEqual(@as(u8, 0xAA), as_slice[2]);
|
||||
}
|
||||
};
|
||||
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
@ -435,7 +435,8 @@ fn incrementVoidPtrValue(value: ?*c_void) void {
|
||||
|
||||
test "implicit cast from [*]T to ?*c_void" {
|
||||
var a = [_]u8{ 3, 2, 1 };
|
||||
incrementVoidPtrArray(a[0..].ptr, 3);
|
||||
var runtime_zero: usize = 0;
|
||||
incrementVoidPtrArray(a[runtime_zero..].ptr, 3);
|
||||
expect(std.mem.eql(u8, &a, &[_]u8{ 4, 3, 2 }));
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
const expect = @import("std").testing.expect;
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const expectEqual = std.testing.expectEqual;
|
||||
const expectError = std.testing.expectError;
|
||||
|
||||
var result: [3]u8 = undefined;
|
||||
var index: usize = undefined;
|
||||
@ -93,3 +96,19 @@ test "return variable while defer expression in scope to modify it" {
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "errdefer with payload" {
|
||||
const S = struct {
|
||||
fn foo() !i32 {
|
||||
errdefer |a| {
|
||||
expectEqual(error.One, a);
|
||||
}
|
||||
return error.One;
|
||||
}
|
||||
fn doTheTest() void {
|
||||
expectError(error.One, foo());
|
||||
}
|
||||
};
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
@ -524,7 +524,7 @@ test "comptime slice of slice preserves comptime var" {
|
||||
test "comptime slice of pointer preserves comptime var" {
|
||||
comptime {
|
||||
var buff: [10]u8 = undefined;
|
||||
var a = buff[0..].ptr;
|
||||
var a = @ptrCast([*]u8, &buff);
|
||||
a[0..1][0] = 1;
|
||||
expect(buff[0..][0..][0] == 1);
|
||||
}
|
||||
|
||||
@ -411,6 +411,34 @@ test "quad hex float literal parsing accurate" {
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "underscore separator parsing" {
|
||||
expect(0_0_0_0 == 0);
|
||||
expect(1_234_567 == 1234567);
|
||||
expect(001_234_567 == 1234567);
|
||||
expect(0_0_1_2_3_4_5_6_7 == 1234567);
|
||||
|
||||
expect(0b0_0_0_0 == 0);
|
||||
expect(0b1010_1010 == 0b10101010);
|
||||
expect(0b0000_1010_1010 == 0b10101010);
|
||||
expect(0b1_0_1_0_1_0_1_0 == 0b10101010);
|
||||
|
||||
expect(0o0_0_0_0 == 0);
|
||||
expect(0o1010_1010 == 0o10101010);
|
||||
expect(0o0000_1010_1010 == 0o10101010);
|
||||
expect(0o1_0_1_0_1_0_1_0 == 0o10101010);
|
||||
|
||||
expect(0x0_0_0_0 == 0);
|
||||
expect(0x1010_1010 == 0x10101010);
|
||||
expect(0x0000_1010_1010 == 0x10101010);
|
||||
expect(0x1_0_1_0_1_0_1_0 == 0x10101010);
|
||||
|
||||
expect(123_456.789_000e1_0 == 123456.789000e10);
|
||||
expect(0_1_2_3_4_5_6.7_8_9_0_0_0e0_0_1_0 == 123456.789000e10);
|
||||
|
||||
expect(0x1234_5678.9ABC_DEF0p-1_0 == 0x12345678.9ABCDEF0p-10);
|
||||
expect(0x1_2_3_4_5_6_7_8.9_A_B_C_D_E_F_0p-0_0_0_1_0 == 0x12345678.9ABCDEF0p-10);
|
||||
}
|
||||
|
||||
test "hex float literal within range" {
|
||||
const a = 0x1.0p16383;
|
||||
const b = 0x0.1p16387;
|
||||
|
||||
@ -102,8 +102,8 @@ test "memcpy and memset intrinsics" {
|
||||
var foo: [20]u8 = undefined;
|
||||
var bar: [20]u8 = undefined;
|
||||
|
||||
@memset(foo[0..].ptr, 'A', foo.len);
|
||||
@memcpy(bar[0..].ptr, foo[0..].ptr, bar.len);
|
||||
@memset(&foo, 'A', foo.len);
|
||||
@memcpy(&bar, &foo, bar.len);
|
||||
|
||||
if (bar[11] != 'A') unreachable;
|
||||
}
|
||||
@ -565,12 +565,16 @@ test "volatile load and store" {
|
||||
expect(ptr.* == 1235);
|
||||
}
|
||||
|
||||
test "slice string literal has type []const u8" {
|
||||
test "slice string literal has correct type" {
|
||||
comptime {
|
||||
expect(@TypeOf("aoeu"[0..]) == []const u8);
|
||||
expect(@TypeOf("aoeu"[0..]) == *const [4:0]u8);
|
||||
const array = [_]i32{ 1, 2, 3, 4 };
|
||||
expect(@TypeOf(array[0..]) == []const i32);
|
||||
expect(@TypeOf(array[0..]) == *const [4]i32);
|
||||
}
|
||||
var runtime_zero: usize = 0;
|
||||
comptime expect(@TypeOf("aoeu"[runtime_zero..]) == [:0]const u8);
|
||||
const array = [_]i32{ 1, 2, 3, 4 };
|
||||
comptime expect(@TypeOf(array[runtime_zero..]) == []const i32);
|
||||
}
|
||||
|
||||
test "pointer child field" {
|
||||
|
||||
@ -159,12 +159,13 @@ test "allowzero pointer and slice" {
|
||||
var opt_ptr: ?[*]allowzero i32 = ptr;
|
||||
expect(opt_ptr != null);
|
||||
expect(@ptrToInt(ptr) == 0);
|
||||
var slice = ptr[0..10];
|
||||
expect(@TypeOf(slice) == []allowzero i32);
|
||||
var runtime_zero: usize = 0;
|
||||
var slice = ptr[runtime_zero..10];
|
||||
comptime expect(@TypeOf(slice) == []allowzero i32);
|
||||
expect(@ptrToInt(&slice[5]) == 20);
|
||||
|
||||
expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
|
||||
expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
|
||||
comptime expect(@typeInfo(@TypeOf(ptr)).Pointer.is_allowzero);
|
||||
comptime expect(@typeInfo(@TypeOf(slice)).Pointer.is_allowzero);
|
||||
}
|
||||
|
||||
test "assign null directly to C pointer and test null equality" {
|
||||
|
||||
@ -13,7 +13,7 @@ fn testReinterpretBytesAsInteger() void {
|
||||
builtin.Endian.Little => 0xab785634,
|
||||
builtin.Endian.Big => 0x345678ab,
|
||||
};
|
||||
expect(@ptrCast(*align(1) const u32, bytes[1..5].ptr).* == expected);
|
||||
expect(@ptrCast(*align(1) const u32, bytes[1..5]).* == expected);
|
||||
}
|
||||
|
||||
test "reinterpret bytes of an array into an extern struct" {
|
||||
|
||||
@ -7,10 +7,10 @@ const mem = std.mem;
|
||||
const x = @intToPtr([*]i32, 0x1000)[0..0x500];
|
||||
const y = x[0x100..];
|
||||
test "compile time slice of pointer to hard coded address" {
|
||||
expect(@ptrToInt(x.ptr) == 0x1000);
|
||||
expect(@ptrToInt(x) == 0x1000);
|
||||
expect(x.len == 0x500);
|
||||
|
||||
expect(@ptrToInt(y.ptr) == 0x1100);
|
||||
expect(@ptrToInt(y) == 0x1100);
|
||||
expect(y.len == 0x400);
|
||||
}
|
||||
|
||||
@ -47,7 +47,9 @@ test "C pointer slice access" {
|
||||
var buf: [10]u32 = [1]u32{42} ** 10;
|
||||
const c_ptr = @ptrCast([*c]const u32, &buf);
|
||||
|
||||
comptime expectEqual([]const u32, @TypeOf(c_ptr[0..1]));
|
||||
var runtime_zero: usize = 0;
|
||||
comptime expectEqual([]const u32, @TypeOf(c_ptr[runtime_zero..1]));
|
||||
comptime expectEqual(*const [1]u32, @TypeOf(c_ptr[0..1]));
|
||||
|
||||
for (c_ptr[0..5]) |*cl| {
|
||||
expectEqual(@as(u32, 42), cl.*);
|
||||
@ -107,7 +109,9 @@ test "obtaining a null terminated slice" {
|
||||
const ptr2 = buf[0..runtime_len :0];
|
||||
// ptr2 is a null-terminated slice
|
||||
comptime expect(@TypeOf(ptr2) == [:0]u8);
|
||||
comptime expect(@TypeOf(ptr2[0..2]) == []u8);
|
||||
comptime expect(@TypeOf(ptr2[0..2]) == *[2]u8);
|
||||
var runtime_zero: usize = 0;
|
||||
comptime expect(@TypeOf(ptr2[runtime_zero..2]) == []u8);
|
||||
}
|
||||
|
||||
test "empty array to slice" {
|
||||
@ -126,3 +130,172 @@ test "empty array to slice" {
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "@ptrCast slice to pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() void {
|
||||
var array align(@alignOf(u16)) = [5]u8{ 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
var slice: []u8 = &array;
|
||||
var ptr = @ptrCast(*u16, slice);
|
||||
expect(ptr.* == 65535);
|
||||
}
|
||||
};
|
||||
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "slice syntax resulting in pointer-to-array" {
|
||||
const S = struct {
|
||||
fn doTheTest() void {
|
||||
testArray();
|
||||
testArrayZ();
|
||||
testArray0();
|
||||
testArrayAlign();
|
||||
testPointer();
|
||||
testPointerZ();
|
||||
testPointer0();
|
||||
testPointerAlign();
|
||||
testSlice();
|
||||
testSliceZ();
|
||||
testSlice0();
|
||||
testSliceOpt();
|
||||
testSliceAlign();
|
||||
}
|
||||
|
||||
fn testArray() void {
|
||||
var array = [5]u8{ 1, 2, 3, 4, 5 };
|
||||
var slice = array[1..3];
|
||||
comptime expect(@TypeOf(slice) == *[2]u8);
|
||||
expect(slice[0] == 2);
|
||||
expect(slice[1] == 3);
|
||||
}
|
||||
|
||||
fn testArrayZ() void {
|
||||
var array = [5:0]u8{ 1, 2, 3, 4, 5 };
|
||||
comptime expect(@TypeOf(array[1..3]) == *[2]u8);
|
||||
comptime expect(@TypeOf(array[1..5]) == *[4:0]u8);
|
||||
comptime expect(@TypeOf(array[1..]) == *[4:0]u8);
|
||||
comptime expect(@TypeOf(array[1..3 :4]) == *[2:4]u8);
|
||||
}
|
||||
|
||||
fn testArray0() void {
|
||||
{
|
||||
var array = [0]u8{};
|
||||
var slice = array[0..0];
|
||||
comptime expect(@TypeOf(slice) == *[0]u8);
|
||||
}
|
||||
{
|
||||
var array = [0:0]u8{};
|
||||
var slice = array[0..0];
|
||||
comptime expect(@TypeOf(slice) == *[0:0]u8);
|
||||
expect(slice[0] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn testArrayAlign() void {
|
||||
var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
|
||||
var slice = array[4..5];
|
||||
comptime expect(@TypeOf(slice) == *align(4) [1]u8);
|
||||
expect(slice[0] == 5);
|
||||
comptime expect(@TypeOf(array[0..2]) == *align(4) [2]u8);
|
||||
}
|
||||
|
||||
fn testPointer() void {
|
||||
var array = [5]u8{ 1, 2, 3, 4, 5 };
|
||||
var pointer: [*]u8 = &array;
|
||||
var slice = pointer[1..3];
|
||||
comptime expect(@TypeOf(slice) == *[2]u8);
|
||||
expect(slice[0] == 2);
|
||||
expect(slice[1] == 3);
|
||||
}
|
||||
|
||||
fn testPointerZ() void {
|
||||
var array = [5:0]u8{ 1, 2, 3, 4, 5 };
|
||||
var pointer: [*:0]u8 = &array;
|
||||
comptime expect(@TypeOf(pointer[1..3]) == *[2]u8);
|
||||
comptime expect(@TypeOf(pointer[1..3 :4]) == *[2:4]u8);
|
||||
}
|
||||
|
||||
fn testPointer0() void {
|
||||
var pointer: [*]u0 = &[1]u0{0};
|
||||
var slice = pointer[0..1];
|
||||
comptime expect(@TypeOf(slice) == *[1]u0);
|
||||
expect(slice[0] == 0);
|
||||
}
|
||||
|
||||
fn testPointerAlign() void {
|
||||
var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
|
||||
var pointer: [*]align(4) u8 = &array;
|
||||
var slice = pointer[4..5];
|
||||
comptime expect(@TypeOf(slice) == *align(4) [1]u8);
|
||||
expect(slice[0] == 5);
|
||||
comptime expect(@TypeOf(pointer[0..2]) == *align(4) [2]u8);
|
||||
}
|
||||
|
||||
fn testSlice() void {
|
||||
var array = [5]u8{ 1, 2, 3, 4, 5 };
|
||||
var src_slice: []u8 = &array;
|
||||
var slice = src_slice[1..3];
|
||||
comptime expect(@TypeOf(slice) == *[2]u8);
|
||||
expect(slice[0] == 2);
|
||||
expect(slice[1] == 3);
|
||||
}
|
||||
|
||||
fn testSliceZ() void {
|
||||
var array = [5:0]u8{ 1, 2, 3, 4, 5 };
|
||||
var slice: [:0]u8 = &array;
|
||||
comptime expect(@TypeOf(slice[1..3]) == *[2]u8);
|
||||
comptime expect(@TypeOf(slice[1..]) == [:0]u8);
|
||||
comptime expect(@TypeOf(slice[1..3 :4]) == *[2:4]u8);
|
||||
}
|
||||
|
||||
fn testSliceOpt() void {
|
||||
var array: [2]u8 = [2]u8{ 1, 2 };
|
||||
var slice: ?[]u8 = &array;
|
||||
comptime expect(@TypeOf(&array, slice) == ?[]u8);
|
||||
comptime expect(@TypeOf(slice.?[0..2]) == *[2]u8);
|
||||
}
|
||||
|
||||
fn testSlice0() void {
|
||||
{
|
||||
var array = [0]u8{};
|
||||
var src_slice: []u8 = &array;
|
||||
var slice = src_slice[0..0];
|
||||
comptime expect(@TypeOf(slice) == *[0]u8);
|
||||
}
|
||||
{
|
||||
var array = [0:0]u8{};
|
||||
var src_slice: [:0]u8 = &array;
|
||||
var slice = src_slice[0..0];
|
||||
comptime expect(@TypeOf(slice) == *[0]u8);
|
||||
}
|
||||
}
|
||||
|
||||
fn testSliceAlign() void {
|
||||
var array align(4) = [5]u8{ 1, 2, 3, 4, 5 };
|
||||
var src_slice: []align(4) u8 = &array;
|
||||
var slice = src_slice[4..5];
|
||||
comptime expect(@TypeOf(slice) == *align(4) [1]u8);
|
||||
expect(slice[0] == 5);
|
||||
comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);
|
||||
}
|
||||
};
|
||||
|
||||
S.doTheTest();
|
||||
comptime S.doTheTest();
|
||||
}
|
||||
|
||||
test "slice of hardcoded address to pointer" {
|
||||
const S = struct {
|
||||
fn doTheTest() void {
|
||||
const pointer = @intToPtr([*]u8, 0x04)[0..2];
|
||||
comptime expect(@TypeOf(pointer) == *[2]u8);
|
||||
const slice: []const u8 = pointer;
|
||||
expect(@ptrToInt(slice.ptr) == 4);
|
||||
expect(slice.len == 2);
|
||||
}
|
||||
};
|
||||
|
||||
S.doTheTest();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user