Merge remote-tracking branch 'origin/master' into zig-ast-to-zir

This commit is contained in:
Andrew Kelley
2020-06-22 23:22:17 -04:00
47 changed files with 1629 additions and 645 deletions

View File

@@ -288,6 +288,7 @@ set(ZIG_SOURCES
"${CMAKE_SOURCE_DIR}/src/target.cpp"
"${CMAKE_SOURCE_DIR}/src/tokenizer.cpp"
"${CMAKE_SOURCE_DIR}/src/util.cpp"
"${CMAKE_SOURCE_DIR}/src/softfloat_ext.cpp"
"${ZIG_SOURCES_MEM_PROFILE}"
)
set(OPTIMIZED_C_SOURCES

View File

@@ -139,7 +139,10 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes));
test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes));
test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes));
test_step.dependOn(tests.addCliTests(b, test_filter, modes));
const test_cli = tests.addCliTests(b, test_filter, modes);
const test_cli_step = b.step("test-cli", "Run zig cli tests");
test_cli_step.dependOn(test_cli);
test_step.dependOn(test_cli);
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));

View File

@@ -236,19 +236,18 @@ pub fn main() !void {
}
{#code_end#}
<p>
Usually you don't want to write to stdout. You want to write to stderr. And you
don't care if it fails. It's more like a <em>warning message</em> that you want
to emit. For that you can use a simpler API:
Usually you don't want to write to stdout. You want to write to stderr, and you
don't care if it fails. For that you can use a simpler API:
</p>
{#code_begin|exe|hello#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
pub fn main() void {
warn("Hello, world!\n", .{});
print("Hello, world!\n", .{});
}
{#code_end#}
<p>
Note that you can leave off the {#syntax#}!{#endsyntax#} from the return type because {#syntax#}warn{#endsyntax#} cannot fail.
Note that you can leave off the {#syntax#}!{#endsyntax#} from the return type because {#syntax#}print{#endsyntax#} cannot fail.
</p>
{#see_also|Values|@import|Errors|Root Source File#}
{#header_close#}
@@ -307,7 +306,7 @@ const Timestamp = struct {
{#header_open|Values#}
{#code_begin|exe|values#}
// Top-level declarations are order-independent:
const warn = std.debug.warn;
const print = std.debug.print;
const std = @import("std");
const os = std.os;
const assert = std.debug.assert;
@@ -315,14 +314,14 @@ const assert = std.debug.assert;
pub fn main() void {
// integers
const one_plus_one: i32 = 1 + 1;
warn("1 + 1 = {}\n", .{one_plus_one});
print("1 + 1 = {}\n", .{one_plus_one});
// floats
const seven_div_three: f32 = 7.0 / 3.0;
warn("7.0 / 3.0 = {}\n", .{seven_div_three});
print("7.0 / 3.0 = {}\n", .{seven_div_three});
// boolean
warn("{}\n{}\n{}\n", .{
print("{}\n{}\n{}\n", .{
true and false,
true or false,
!true,
@@ -332,7 +331,7 @@ pub fn main() void {
var optional_value: ?[]const u8 = null;
assert(optional_value == null);
warn("\noptional 1\ntype: {}\nvalue: {}\n", .{
print("\noptional 1\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(optional_value)),
optional_value,
});
@@ -340,7 +339,7 @@ pub fn main() void {
optional_value = "hi";
assert(optional_value != null);
warn("\noptional 2\ntype: {}\nvalue: {}\n", .{
print("\noptional 2\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(optional_value)),
optional_value,
});
@@ -348,14 +347,14 @@ pub fn main() void {
// error union
var number_or_error: anyerror!i32 = error.ArgNotFound;
warn("\nerror union 1\ntype: {}\nvalue: {}\n", .{
print("\nerror union 1\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(number_or_error)),
number_or_error,
});
number_or_error = 1234;
warn("\nerror union 2\ntype: {}\nvalue: {}\n", .{
print("\nerror union 2\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(number_or_error)),
number_or_error,
});
@@ -994,15 +993,15 @@ export fn foo_optimized(x: f64) f64 {
which operates in strict mode.</p>
{#code_begin|exe|float_mode#}
{#code_link_object|foo#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
extern fn foo_strict(x: f64) f64;
extern fn foo_optimized(x: f64) f64;
pub fn main() void {
const x = 0.001;
warn("optimized = {}\n", .{foo_optimized(x)});
warn("strict = {}\n", .{foo_strict(x)});
print("optimized = {}\n", .{foo_optimized(x)});
print("strict = {}\n", .{foo_strict(x)});
}
{#code_end#}
{#see_also|@setFloatMode|Division by Zero#}
@@ -2668,9 +2667,9 @@ const std = @import("std");
pub fn main() void {
const Foo = struct {};
std.debug.warn("variable: {}\n", .{@typeName(Foo)});
std.debug.warn("anonymous: {}\n", .{@typeName(struct {})});
std.debug.warn("function: {}\n", .{@typeName(List(i32))});
std.debug.print("variable: {}\n", .{@typeName(Foo)});
std.debug.print("anonymous: {}\n", .{@typeName(struct {})});
std.debug.print("function: {}\n", .{@typeName(List(i32))});
}
fn List(comptime T: type) type {
@@ -3869,7 +3868,7 @@ test "if error union" {
{#code_begin|test|defer#}
const std = @import("std");
const assert = std.debug.assert;
const warn = std.debug.warn;
const print = std.debug.print;
// defer will execute an expression at the end of the current scope.
fn deferExample() usize {
@@ -3892,18 +3891,18 @@ test "defer basics" {
// If multiple defer statements are specified, they will be executed in
// the reverse order they were run.
fn deferUnwindExample() void {
warn("\n", .{});
print("\n", .{});
defer {
warn("1 ", .{});
print("1 ", .{});
}
defer {
warn("2 ", .{});
print("2 ", .{});
}
if (false) {
// defers are not run if they are never executed.
defer {
warn("3 ", .{});
print("3 ", .{});
}
}
}
@@ -3918,15 +3917,15 @@ test "defer unwinding" {
// This is especially useful in allowing a function to clean up properly
// on error, and replaces goto error handling tactics as seen in c.
fn deferErrorExample(is_error: bool) !void {
warn("\nstart of function\n", .{});
print("\nstart of function\n", .{});
// This will always be executed on exit
defer {
warn("end of function\n", .{});
print("end of function\n", .{});
}
errdefer {
warn("encountered an error!\n", .{});
print("encountered an error!\n", .{});
}
if (is_error) {
@@ -5925,13 +5924,13 @@ const Node = struct {
Putting all of this together, let's see how {#syntax#}printf{#endsyntax#} works in Zig.
</p>
{#code_begin|exe|printf#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
const a_number: i32 = 1234;
const a_string = "foobar";
pub fn main() void {
warn("here is a string: '{}' here is a number: {}\n", .{a_string, a_number});
print("here is a string: '{}' here is a number: {}\n", .{a_string, a_number});
}
{#code_end#}
@@ -6045,13 +6044,13 @@ pub fn printValue(self: *OutStream, value: var) !void {
And now, what happens if we give too many arguments to {#syntax#}printf{#endsyntax#}?
</p>
{#code_begin|test_err|Unused arguments#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
const a_number: i32 = 1234;
const a_string = "foobar";
test "printf too many arguments" {
warn("here is a string: '{}' here is a number: {}\n", .{
print("here is a string: '{}' here is a number: {}\n", .{
a_string,
a_number,
a_number,
@@ -6066,14 +6065,14 @@ test "printf too many arguments" {
only that it is a compile-time known value that can be coerced to a {#syntax#}[]const u8{#endsyntax#}:
</p>
{#code_begin|exe|printf#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
const a_number: i32 = 1234;
const a_string = "foobar";
const fmt = "here is a string: '{}' here is a number: {}\n";
pub fn main() void {
warn(fmt, .{a_string, a_number});
print(fmt, .{a_string, a_number});
}
{#code_end#}
<p>
@@ -6511,7 +6510,7 @@ pub fn main() void {
fn amainWrap() void {
amain() catch |e| {
std.debug.warn("{}\n", .{e});
std.debug.print("{}\n", .{e});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
@@ -6541,8 +6540,8 @@ fn amain() !void {
const download_text = try await download_frame;
defer allocator.free(download_text);
std.debug.warn("download_text: {}\n", .{download_text});
std.debug.warn("file_text: {}\n", .{file_text});
std.debug.print("download_text: {}\n", .{download_text});
std.debug.print("file_text: {}\n", .{file_text});
}
var global_download_frame: anyframe = undefined;
@@ -6552,7 +6551,7 @@ fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
suspend {
global_download_frame = @frame();
}
std.debug.warn("fetchUrl returning\n", .{});
std.debug.print("fetchUrl returning\n", .{});
return result;
}
@@ -6563,7 +6562,7 @@ fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
suspend {
global_file_frame = @frame();
}
std.debug.warn("readFile returning\n", .{});
std.debug.print("readFile returning\n", .{});
return result;
}
{#code_end#}
@@ -6581,7 +6580,7 @@ pub fn main() void {
fn amainWrap() void {
amain() catch |e| {
std.debug.warn("{}\n", .{e});
std.debug.print("{}\n", .{e});
if (@errorReturnTrace()) |trace| {
std.debug.dumpStackTrace(trace.*);
}
@@ -6611,21 +6610,21 @@ fn amain() !void {
const download_text = try await download_frame;
defer allocator.free(download_text);
std.debug.warn("download_text: {}\n", .{download_text});
std.debug.warn("file_text: {}\n", .{file_text});
std.debug.print("download_text: {}\n", .{download_text});
std.debug.print("file_text: {}\n", .{file_text});
}
fn fetchUrl(allocator: *Allocator, url: []const u8) ![]u8 {
const result = try std.mem.dupe(allocator, u8, "this is the downloaded url contents");
errdefer allocator.free(result);
std.debug.warn("fetchUrl returning\n", .{});
std.debug.print("fetchUrl returning\n", .{});
return result;
}
fn readFile(allocator: *Allocator, filename: []const u8) ![]u8 {
const result = try std.mem.dupe(allocator, u8, "this is the file contents");
errdefer allocator.free(result);
std.debug.warn("readFile returning\n", .{});
std.debug.print("readFile returning\n", .{});
return result;
}
{#code_end#}
@@ -7121,7 +7120,7 @@ fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: *T, expected_value: T, new_val
compile-time executing code.
</p>
{#code_begin|test_err|found compile log statement#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
const num1 = blk: {
var val1: i32 = 99;
@@ -7133,7 +7132,7 @@ const num1 = blk: {
test "main" {
@compileLog("comptime in main");
warn("Runtime in main, num1 = {}.\n", .{num1});
print("Runtime in main, num1 = {}.\n", .{num1});
}
{#code_end#}
<p>
@@ -7145,7 +7144,7 @@ test "main" {
program compiles successfully and the generated executable prints:
</p>
{#code_begin|test#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
const num1 = blk: {
var val1: i32 = 99;
@@ -7154,7 +7153,7 @@ const num1 = blk: {
};
test "main" {
warn("Runtime in main, num1 = {}.\n", .{num1});
print("Runtime in main, num1 = {}.\n", .{num1});
}
{#code_end#}
{#header_close#}
@@ -8205,7 +8204,7 @@ test "vector @splat" {
{#header_open|@This#}
<pre>{#syntax#}@This() type{#endsyntax#}</pre>
<p>
Returns the innermost struct or union that this function call is inside.
Returns the innermost struct, enum, or union that this function call is inside.
This can be useful for an anonymous struct that needs to refer to itself:
</p>
{#code_begin|test#}
@@ -8555,7 +8554,7 @@ const std = @import("std");
pub fn main() void {
var value: i32 = -1;
var unsigned = @intCast(u32, value);
std.debug.warn("value: {}\n", .{unsigned});
std.debug.print("value: {}\n", .{unsigned});
}
{#code_end#}
<p>
@@ -8577,7 +8576,7 @@ const std = @import("std");
pub fn main() void {
var spartan_count: u16 = 300;
const byte = @intCast(u8, spartan_count);
std.debug.warn("value: {}\n", .{byte});
std.debug.print("value: {}\n", .{byte});
}
{#code_end#}
<p>
@@ -8611,7 +8610,7 @@ const std = @import("std");
pub fn main() void {
var byte: u8 = 255;
byte += 1;
std.debug.warn("value: {}\n", .{byte});
std.debug.print("value: {}\n", .{byte});
}
{#code_end#}
{#header_close#}
@@ -8629,16 +8628,16 @@ pub fn main() void {
<p>Example of catching an overflow for addition:</p>
{#code_begin|exe_err#}
const math = @import("std").math;
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
pub fn main() !void {
var byte: u8 = 255;
byte = if (math.add(u8, byte, 1)) |result| result else |err| {
warn("unable to add one: {}\n", .{@errorName(err)});
print("unable to add one: {}\n", .{@errorName(err)});
return err;
};
warn("result: {}\n", .{byte});
print("result: {}\n", .{byte});
}
{#code_end#}
{#header_close#}
@@ -8657,15 +8656,15 @@ pub fn main() !void {
Example of {#link|@addWithOverflow#}:
</p>
{#code_begin|exe#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
pub fn main() void {
var byte: u8 = 255;
var result: u8 = undefined;
if (@addWithOverflow(u8, byte, 10, &result)) {
warn("overflowed result: {}\n", .{result});
print("overflowed result: {}\n", .{result});
} else {
warn("result: {}\n", .{result});
print("result: {}\n", .{result});
}
}
{#code_end#}
@@ -8710,7 +8709,7 @@ const std = @import("std");
pub fn main() void {
var x: u8 = 0b01010101;
var y = @shlExact(x, 2);
std.debug.warn("value: {}\n", .{y});
std.debug.print("value: {}\n", .{y});
}
{#code_end#}
{#header_close#}
@@ -8728,7 +8727,7 @@ const std = @import("std");
pub fn main() void {
var x: u8 = 0b10101010;
var y = @shrExact(x, 2);
std.debug.warn("value: {}\n", .{y});
std.debug.print("value: {}\n", .{y});
}
{#code_end#}
{#header_close#}
@@ -8749,7 +8748,7 @@ pub fn main() void {
var a: u32 = 1;
var b: u32 = 0;
var c = a / b;
std.debug.warn("value: {}\n", .{c});
std.debug.print("value: {}\n", .{c});
}
{#code_end#}
{#header_close#}
@@ -8770,7 +8769,7 @@ pub fn main() void {
var a: u32 = 10;
var b: u32 = 0;
var c = a % b;
std.debug.warn("value: {}\n", .{c});
std.debug.print("value: {}\n", .{c});
}
{#code_end#}
{#header_close#}
@@ -8791,7 +8790,7 @@ pub fn main() void {
var a: u32 = 10;
var b: u32 = 3;
var c = @divExact(a, b);
std.debug.warn("value: {}\n", .{c});
std.debug.print("value: {}\n", .{c});
}
{#code_end#}
{#header_close#}
@@ -8810,20 +8809,20 @@ const std = @import("std");
pub fn main() void {
var optional_number: ?i32 = null;
var number = optional_number.?;
std.debug.warn("value: {}\n", .{number});
std.debug.print("value: {}\n", .{number});
}
{#code_end#}
<p>One way to avoid this crash is to test for null instead of assuming non-null, with
the {#syntax#}if{#endsyntax#} expression:</p>
{#code_begin|exe|test#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
pub fn main() void {
const optional_number: ?i32 = null;
if (optional_number) |number| {
warn("got number: {}\n", .{number});
print("got number: {}\n", .{number});
} else {
warn("it's null\n", .{});
print("it's null\n", .{});
}
}
{#code_end#}
@@ -8846,7 +8845,7 @@ const std = @import("std");
pub fn main() void {
const number = getNumberOrFail() catch unreachable;
std.debug.warn("value: {}\n", .{number});
std.debug.print("value: {}\n", .{number});
}
fn getNumberOrFail() !i32 {
@@ -8856,15 +8855,15 @@ fn getNumberOrFail() !i32 {
<p>One way to avoid this crash is to test for an error instead of assuming a successful result, with
the {#syntax#}if{#endsyntax#} expression:</p>
{#code_begin|exe#}
const warn = @import("std").debug.warn;
const print = @import("std").debug.print;
pub fn main() void {
const result = getNumberOrFail();
if (result) |number| {
warn("got number: {}\n", .{number});
print("got number: {}\n", .{number});
} else |err| {
warn("got error: {}\n", .{@errorName(err)});
print("got error: {}\n", .{@errorName(err)});
}
}
@@ -8891,7 +8890,7 @@ pub fn main() void {
var err = error.AnError;
var number = @errorToInt(err) + 500;
var invalid_err = @intToError(number);
std.debug.warn("value: {}\n", .{number});
std.debug.print("value: {}\n", .{number});
}
{#code_end#}
{#header_close#}
@@ -8921,7 +8920,7 @@ const Foo = enum {
pub fn main() void {
var a: u2 = 3;
var b = @intToEnum(Foo, a);
std.debug.warn("value: {}\n", .{@tagName(b)});
std.debug.print("value: {}\n", .{@tagName(b)});
}
{#code_end#}
{#header_close#}
@@ -8958,7 +8957,7 @@ pub fn main() void {
}
fn foo(set1: Set1) void {
const x = @errSetCast(Set2, set1);
std.debug.warn("value: {}\n", .{x});
std.debug.print("value: {}\n", .{x});
}
{#code_end#}
{#header_close#}
@@ -9015,7 +9014,7 @@ pub fn main() void {
fn bar(f: *Foo) void {
f.float = 12.34;
std.debug.warn("value: {}\n", .{f.float});
std.debug.print("value: {}\n", .{f.float});
}
{#code_end#}
<p>
@@ -9039,7 +9038,7 @@ pub fn main() void {
fn bar(f: *Foo) void {
f.* = Foo{ .float = 12.34 };
std.debug.warn("value: {}\n", .{f.float});
std.debug.print("value: {}\n", .{f.float});
}
{#code_end#}
<p>
@@ -9058,7 +9057,7 @@ pub fn main() void {
var f = Foo{ .int = 42 };
f = Foo{ .float = undefined };
bar(&f);
std.debug.warn("value: {}\n", .{f.float});
std.debug.print("value: {}\n", .{f.float});
}
fn bar(f: *Foo) void {
@@ -9178,7 +9177,7 @@ pub fn main() !void {
const allocator = &arena.allocator;
const ptr = try allocator.create(i32);
std.debug.warn("ptr={*}\n", .{ptr});
std.debug.print("ptr={*}\n", .{ptr});
}
{#code_end#}
When using this kind of allocator, there is no need to free anything manually. Everything
@@ -9712,7 +9711,7 @@ pub fn main() !void {
defer std.process.argsFree(std.heap.page_allocator, args);
for (args) |arg, i| {
std.debug.warn("{}: {}\n", .{i, arg});
std.debug.print("{}: {}\n", .{i, arg});
}
}
{#code_end#}
@@ -9734,7 +9733,7 @@ pub fn main() !void {
try preopens.populate();
for (preopens.asSlice()) |preopen, i| {
std.debug.warn("{}: {}\n", .{ i, preopen });
std.debug.print("{}: {}\n", .{ i, preopen });
}
}
{#code_end#}

View File

@@ -162,7 +162,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
mem.copy(T, self.items[oldlen..], items);
}
pub usingnamespace if (T != u8) struct { } else struct {
pub usingnamespace if (T != u8) struct {} else struct {
pub const Writer = std.io.Writer(*Self, error{OutOfMemory}, appendWrite);
/// Initializes a Writer which will append to the list.

View File

@@ -2559,3 +2559,10 @@ pub const InstalledFile = struct {
dir: InstallDir,
path: []const u8,
};
test "" {
// The only purpose of this test is to get all these untested functions
// to be referenced to avoid regression so it is okay to skip some targets.
if (comptime std.Target.current.cpu.arch.ptrBitWidth() == 64)
std.meta.refAllDecls(@This());
}

View File

@@ -215,3 +215,7 @@ pub const InstallRawStep = struct {
try emitRaw(builder.allocator, full_src_path, full_dest_path);
}
};
test "" {
std.meta.refAllDecls(InstallRawStep);
}

View File

@@ -166,7 +166,7 @@ pub const TypeInfo = union(enum) {
Fn: Fn,
BoundFn: Fn,
Opaque: void,
Frame: void,
Frame: Frame,
AnyFrame: AnyFrame,
Vector: Vector,
EnumLiteral: void,
@@ -244,8 +244,8 @@ pub const TypeInfo = union(enum) {
/// therefore must be kept in sync with the compiler implementation.
pub const Struct = struct {
layout: ContainerLayout,
fields: []StructField,
decls: []Declaration,
fields: []const StructField,
decls: []const Declaration,
};
/// This data structure is used by the Zig language code generation and
@@ -265,12 +265,13 @@ pub const TypeInfo = union(enum) {
/// therefore must be kept in sync with the compiler implementation.
pub const Error = struct {
name: []const u8,
/// This field is ignored when using @Type().
value: comptime_int,
};
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const ErrorSet = ?[]Error;
pub const ErrorSet = ?[]const Error;
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
@@ -284,8 +285,8 @@ pub const TypeInfo = union(enum) {
pub const Enum = struct {
layout: ContainerLayout,
tag_type: type,
fields: []EnumField,
decls: []Declaration,
fields: []const EnumField,
decls: []const Declaration,
is_exhaustive: bool,
};
@@ -302,8 +303,8 @@ pub const TypeInfo = union(enum) {
pub const Union = struct {
layout: ContainerLayout,
tag_type: ?type,
fields: []UnionField,
decls: []Declaration,
fields: []const UnionField,
decls: []const Declaration,
};
/// This data structure is used by the Zig language code generation and
@@ -321,7 +322,13 @@ pub const TypeInfo = union(enum) {
is_generic: bool,
is_var_args: bool,
return_type: ?type,
args: []FnArg,
args: []const FnArg,
};
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const Frame = struct {
function: var,
};
/// This data structure is used by the Zig language code generation and
@@ -361,7 +368,7 @@ pub const TypeInfo = union(enum) {
is_export: bool,
lib_name: ?[]const u8,
return_type: type,
arg_names: [][]const u8,
arg_names: []const []const u8,
/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.

View File

@@ -102,6 +102,7 @@ pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
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 symlinkat(oldpath: [*:0]const u8, newdirfd: fd_t, newpath: [*: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;

View File

@@ -278,62 +278,62 @@ pub const Token = struct {
// TODO extensions
pub const keywords = std.ComptimeStringMap(Id, .{
.{"auto", .Keyword_auto},
.{"break", .Keyword_break},
.{"case", .Keyword_case},
.{"char", .Keyword_char},
.{"const", .Keyword_const},
.{"continue", .Keyword_continue},
.{"default", .Keyword_default},
.{"do", .Keyword_do},
.{"double", .Keyword_double},
.{"else", .Keyword_else},
.{"enum", .Keyword_enum},
.{"extern", .Keyword_extern},
.{"float", .Keyword_float},
.{"for", .Keyword_for},
.{"goto", .Keyword_goto},
.{"if", .Keyword_if},
.{"int", .Keyword_int},
.{"long", .Keyword_long},
.{"register", .Keyword_register},
.{"return", .Keyword_return},
.{"short", .Keyword_short},
.{"signed", .Keyword_signed},
.{"sizeof", .Keyword_sizeof},
.{"static", .Keyword_static},
.{"struct", .Keyword_struct},
.{"switch", .Keyword_switch},
.{"typedef", .Keyword_typedef},
.{"union", .Keyword_union},
.{"unsigned", .Keyword_unsigned},
.{"void", .Keyword_void},
.{"volatile", .Keyword_volatile},
.{"while", .Keyword_while},
.{ "auto", .Keyword_auto },
.{ "break", .Keyword_break },
.{ "case", .Keyword_case },
.{ "char", .Keyword_char },
.{ "const", .Keyword_const },
.{ "continue", .Keyword_continue },
.{ "default", .Keyword_default },
.{ "do", .Keyword_do },
.{ "double", .Keyword_double },
.{ "else", .Keyword_else },
.{ "enum", .Keyword_enum },
.{ "extern", .Keyword_extern },
.{ "float", .Keyword_float },
.{ "for", .Keyword_for },
.{ "goto", .Keyword_goto },
.{ "if", .Keyword_if },
.{ "int", .Keyword_int },
.{ "long", .Keyword_long },
.{ "register", .Keyword_register },
.{ "return", .Keyword_return },
.{ "short", .Keyword_short },
.{ "signed", .Keyword_signed },
.{ "sizeof", .Keyword_sizeof },
.{ "static", .Keyword_static },
.{ "struct", .Keyword_struct },
.{ "switch", .Keyword_switch },
.{ "typedef", .Keyword_typedef },
.{ "union", .Keyword_union },
.{ "unsigned", .Keyword_unsigned },
.{ "void", .Keyword_void },
.{ "volatile", .Keyword_volatile },
.{ "while", .Keyword_while },
// ISO C99
.{"_Bool", .Keyword_bool},
.{"_Complex", .Keyword_complex},
.{"_Imaginary", .Keyword_imaginary},
.{"inline", .Keyword_inline},
.{"restrict", .Keyword_restrict},
.{ "_Bool", .Keyword_bool },
.{ "_Complex", .Keyword_complex },
.{ "_Imaginary", .Keyword_imaginary },
.{ "inline", .Keyword_inline },
.{ "restrict", .Keyword_restrict },
// ISO C11
.{"_Alignas", .Keyword_alignas},
.{"_Alignof", .Keyword_alignof},
.{"_Atomic", .Keyword_atomic},
.{"_Generic", .Keyword_generic},
.{"_Noreturn", .Keyword_noreturn},
.{"_Static_assert", .Keyword_static_assert},
.{"_Thread_local", .Keyword_thread_local},
.{ "_Alignas", .Keyword_alignas },
.{ "_Alignof", .Keyword_alignof },
.{ "_Atomic", .Keyword_atomic },
.{ "_Generic", .Keyword_generic },
.{ "_Noreturn", .Keyword_noreturn },
.{ "_Static_assert", .Keyword_static_assert },
.{ "_Thread_local", .Keyword_thread_local },
// Preprocessor directives
.{"include", .Keyword_include},
.{"define", .Keyword_define},
.{"ifdef", .Keyword_ifdef},
.{"ifndef", .Keyword_ifndef},
.{"error", .Keyword_error},
.{"pragma", .Keyword_pragma},
.{ "include", .Keyword_include },
.{ "define", .Keyword_define },
.{ "ifdef", .Keyword_ifdef },
.{ "ifndef", .Keyword_ifndef },
.{ "error", .Keyword_error },
.{ "pragma", .Keyword_pragma },
});
// TODO do this in the preprocessor

View File

@@ -52,9 +52,13 @@ pub const LineInfo = struct {
var stderr_mutex = std.Mutex.init();
/// Tries to write to stderr, unbuffered, and ignores any error returned.
/// Does not append a newline.
pub fn warn(comptime fmt: []const u8, args: var) void {
/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
/// "printf debugging".
pub const warn = print;
/// Print to stderr, unbuffered, and silently returning on failure. Intended
/// for use in "printf debugging." Use `std.log` functions for proper logging.
pub fn print(comptime fmt: []const u8, args: var) void {
const held = stderr_mutex.acquire();
defer held.release();
const stderr = io.getStdErr().writer();

View File

@@ -69,14 +69,14 @@ fn peekIsAlign(comptime fmt: []const u8) bool {
///
/// If a formatted user type contains a function of the type
/// ```
/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, out_stream: var) !void
/// pub fn format(value: ?, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: var) !void
/// ```
/// with `?` being the type formatted, this function will be called instead of the default implementation.
/// This allows user types to be formatted in a logical manner instead of dumping all fields of the type.
///
/// A user type may be a `struct`, `vector`, `union` or `enum` type.
pub fn format(
out_stream: var,
writer: var,
comptime fmt: []const u8,
args: var,
) !void {
@@ -136,7 +136,7 @@ pub fn format(
.Start => switch (c) {
'{' => {
if (start_index < i) {
try out_stream.writeAll(fmt[start_index..i]);
try writer.writeAll(fmt[start_index..i]);
}
start_index = i;
@@ -148,7 +148,7 @@ pub fn format(
},
'}' => {
if (start_index < i) {
try out_stream.writeAll(fmt[start_index..i]);
try writer.writeAll(fmt[start_index..i]);
}
state = .CloseBrace;
},
@@ -183,7 +183,7 @@ pub fn format(
args[arg_to_print],
fmt[0..0],
options,
out_stream,
writer,
default_max_depth,
);
@@ -214,7 +214,7 @@ pub fn format(
args[arg_to_print],
fmt[specifier_start..i],
options,
out_stream,
writer,
default_max_depth,
);
state = .Start;
@@ -259,7 +259,7 @@ pub fn format(
args[arg_to_print],
fmt[specifier_start..specifier_end],
options,
out_stream,
writer,
default_max_depth,
);
state = .Start;
@@ -285,7 +285,7 @@ pub fn format(
args[arg_to_print],
fmt[specifier_start..specifier_end],
options,
out_stream,
writer,
default_max_depth,
);
state = .Start;
@@ -306,7 +306,7 @@ pub fn format(
}
}
if (start_index < fmt.len) {
try out_stream.writeAll(fmt[start_index..]);
try writer.writeAll(fmt[start_index..]);
}
}
@@ -314,140 +314,140 @@ pub fn formatType(
value: var,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
max_depth: usize,
) @TypeOf(out_stream).Error!void {
) @TypeOf(writer).Error!void {
if (comptime std.mem.eql(u8, fmt, "*")) {
try out_stream.writeAll(@typeName(@TypeOf(value).Child));
try out_stream.writeAll("@");
try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, out_stream);
try writer.writeAll(@typeName(@TypeOf(value).Child));
try writer.writeAll("@");
try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, writer);
return;
}
const T = @TypeOf(value);
if (comptime std.meta.trait.hasFn("format")(T)) {
return try value.format(fmt, options, out_stream);
return try value.format(fmt, options, writer);
}
switch (@typeInfo(T)) {
.ComptimeInt, .Int, .ComptimeFloat, .Float => {
return formatValue(value, fmt, options, out_stream);
return formatValue(value, fmt, options, writer);
},
.Void => {
return formatBuf("void", options, out_stream);
return formatBuf("void", options, writer);
},
.Bool => {
return formatBuf(if (value) "true" else "false", options, out_stream);
return formatBuf(if (value) "true" else "false", options, writer);
},
.Optional => {
if (value) |payload| {
return formatType(payload, fmt, options, out_stream, max_depth);
return formatType(payload, fmt, options, writer, max_depth);
} else {
return formatBuf("null", options, out_stream);
return formatBuf("null", options, writer);
}
},
.ErrorUnion => {
if (value) |payload| {
return formatType(payload, fmt, options, out_stream, max_depth);
return formatType(payload, fmt, options, writer, max_depth);
} else |err| {
return formatType(err, fmt, options, out_stream, max_depth);
return formatType(err, fmt, options, writer, max_depth);
}
},
.ErrorSet => {
try out_stream.writeAll("error.");
return out_stream.writeAll(@errorName(value));
try writer.writeAll("error.");
return writer.writeAll(@errorName(value));
},
.Enum => |enumInfo| {
try out_stream.writeAll(@typeName(T));
try writer.writeAll(@typeName(T));
if (enumInfo.is_exhaustive) {
try out_stream.writeAll(".");
try out_stream.writeAll(@tagName(value));
try writer.writeAll(".");
try writer.writeAll(@tagName(value));
return;
}
// Use @tagName only if value is one of known fields
inline for (enumInfo.fields) |enumField| {
if (@enumToInt(value) == enumField.value) {
try out_stream.writeAll(".");
try out_stream.writeAll(@tagName(value));
try writer.writeAll(".");
try writer.writeAll(@tagName(value));
return;
}
}
try out_stream.writeAll("(");
try formatType(@enumToInt(value), fmt, options, out_stream, max_depth);
try out_stream.writeAll(")");
try writer.writeAll("(");
try formatType(@enumToInt(value), fmt, options, writer, max_depth);
try writer.writeAll(")");
},
.Union => {
try out_stream.writeAll(@typeName(T));
try writer.writeAll(@typeName(T));
if (max_depth == 0) {
return out_stream.writeAll("{ ... }");
return writer.writeAll("{ ... }");
}
const info = @typeInfo(T).Union;
if (info.tag_type) |UnionTagType| {
try out_stream.writeAll("{ .");
try out_stream.writeAll(@tagName(@as(UnionTagType, value)));
try out_stream.writeAll(" = ");
try writer.writeAll("{ .");
try writer.writeAll(@tagName(@as(UnionTagType, value)));
try writer.writeAll(" = ");
inline for (info.fields) |u_field| {
if (@enumToInt(@as(UnionTagType, value)) == u_field.enum_field.?.value) {
try formatType(@field(value, u_field.name), fmt, options, out_stream, max_depth - 1);
try formatType(@field(value, u_field.name), fmt, options, writer, max_depth - 1);
}
}
try out_stream.writeAll(" }");
try writer.writeAll(" }");
} else {
try format(out_stream, "@{x}", .{@ptrToInt(&value)});
try format(writer, "@{x}", .{@ptrToInt(&value)});
}
},
.Struct => |StructT| {
try out_stream.writeAll(@typeName(T));
try writer.writeAll(@typeName(T));
if (max_depth == 0) {
return out_stream.writeAll("{ ... }");
return writer.writeAll("{ ... }");
}
try out_stream.writeAll("{");
try writer.writeAll("{");
inline for (StructT.fields) |f, i| {
if (i == 0) {
try out_stream.writeAll(" .");
try writer.writeAll(" .");
} else {
try out_stream.writeAll(", .");
try writer.writeAll(", .");
}
try out_stream.writeAll(f.name);
try out_stream.writeAll(" = ");
try formatType(@field(value, f.name), fmt, options, out_stream, max_depth - 1);
try writer.writeAll(f.name);
try writer.writeAll(" = ");
try formatType(@field(value, f.name), fmt, options, writer, max_depth - 1);
}
try out_stream.writeAll(" }");
try writer.writeAll(" }");
},
.Pointer => |ptr_info| switch (ptr_info.size) {
.One => switch (@typeInfo(ptr_info.child)) {
.Array => |info| {
if (info.child == u8) {
return formatText(value, fmt, options, out_stream);
return formatText(value, fmt, options, writer);
}
return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
},
.Enum, .Union, .Struct => {
return formatType(value.*, fmt, options, out_stream, max_depth);
return formatType(value.*, fmt, options, writer, max_depth);
},
else => return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
else => return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) }),
},
.Many, .C => {
if (ptr_info.sentinel) |sentinel| {
return formatType(mem.span(value), fmt, options, out_stream, max_depth);
return formatType(mem.span(value), fmt, options, writer, max_depth);
}
if (ptr_info.child == u8) {
if (fmt.len > 0 and fmt[0] == 's') {
return formatText(mem.span(value), fmt, options, out_stream);
return formatText(mem.span(value), fmt, options, writer);
}
}
return format(out_stream, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
return format(writer, "{}@{x}", .{ @typeName(T.Child), @ptrToInt(value) });
},
.Slice => {
if (fmt.len > 0 and ((fmt[0] == 'x') or (fmt[0] == 'X'))) {
return formatText(value, fmt, options, out_stream);
return formatText(value, fmt, options, writer);
}
if (ptr_info.child == u8) {
return formatText(value, fmt, options, out_stream);
return formatText(value, fmt, options, writer);
}
return format(out_stream, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
return format(writer, "{}@{x}", .{ @typeName(ptr_info.child), @ptrToInt(value.ptr) });
},
},
.Array => |info| {
@@ -462,27 +462,27 @@ pub fn formatType(
.sentinel = null,
},
});
return formatType(@as(Slice, &value), fmt, options, out_stream, max_depth);
return formatType(@as(Slice, &value), fmt, options, writer, max_depth);
},
.Vector => {
const len = @typeInfo(T).Vector.len;
try out_stream.writeAll("{ ");
try writer.writeAll("{ ");
var i: usize = 0;
while (i < len) : (i += 1) {
try formatValue(value[i], fmt, options, out_stream);
try formatValue(value[i], fmt, options, writer);
if (i < len - 1) {
try out_stream.writeAll(", ");
try writer.writeAll(", ");
}
}
try out_stream.writeAll(" }");
try writer.writeAll(" }");
},
.Fn => {
return format(out_stream, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
return format(writer, "{}@{x}", .{ @typeName(T), @ptrToInt(value) });
},
.Type => return out_stream.writeAll(@typeName(T)),
.Type => return writer.writeAll(@typeName(T)),
.EnumLiteral => {
const buffer = [_]u8{'.'} ++ @tagName(value);
return formatType(buffer, fmt, options, out_stream, max_depth);
return formatType(buffer, fmt, options, writer, max_depth);
},
else => @compileError("Unable to format type '" ++ @typeName(T) ++ "'"),
}
@@ -492,19 +492,19 @@ fn formatValue(
value: var,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
if (comptime std.mem.eql(u8, fmt, "B")) {
return formatBytes(value, options, 1000, out_stream);
return formatBytes(value, options, 1000, writer);
} else if (comptime std.mem.eql(u8, fmt, "Bi")) {
return formatBytes(value, options, 1024, out_stream);
return formatBytes(value, options, 1024, writer);
}
const T = @TypeOf(value);
switch (@typeInfo(T)) {
.Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, out_stream),
.Int, .ComptimeInt => return formatIntValue(value, fmt, options, out_stream),
.Bool => return formatBuf(if (value) "true" else "false", options, out_stream),
.Float, .ComptimeFloat => return formatFloatValue(value, fmt, options, writer),
.Int, .ComptimeInt => return formatIntValue(value, fmt, options, writer),
.Bool => return formatBuf(if (value) "true" else "false", options, writer),
else => comptime unreachable,
}
}
@@ -513,7 +513,7 @@ pub fn formatIntValue(
value: var,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
comptime var radix = 10;
comptime var uppercase = false;
@@ -529,7 +529,7 @@ pub fn formatIntValue(
uppercase = false;
} else if (comptime std.mem.eql(u8, fmt, "c")) {
if (@TypeOf(int_value).bit_count <= 8) {
return formatAsciiChar(@as(u8, int_value), options, out_stream);
return formatAsciiChar(@as(u8, int_value), options, writer);
} else {
@compileError("Cannot print integer that is larger than 8 bits as a ascii");
}
@@ -546,19 +546,19 @@ pub fn formatIntValue(
@compileError("Unknown format string: '" ++ fmt ++ "'");
}
return formatInt(int_value, radix, uppercase, options, out_stream);
return formatInt(int_value, radix, uppercase, options, writer);
}
fn formatFloatValue(
value: var,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
return formatFloatScientific(value, options, out_stream);
return formatFloatScientific(value, options, writer);
} else if (comptime std.mem.eql(u8, fmt, "d")) {
return formatFloatDecimal(value, options, out_stream);
return formatFloatDecimal(value, options, writer);
} else {
@compileError("Unknown format string: '" ++ fmt ++ "'");
}
@@ -568,13 +568,13 @@ pub fn formatText(
bytes: []const u8,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
if (comptime std.mem.eql(u8, fmt, "s") or (fmt.len == 0)) {
return formatBuf(bytes, options, out_stream);
return formatBuf(bytes, options, writer);
} else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
for (bytes) |c| {
try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, out_stream);
try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, writer);
}
return;
} else {
@@ -585,38 +585,38 @@ pub fn formatText(
pub fn formatAsciiChar(
c: u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
return out_stream.writeAll(@as(*const [1]u8, &c));
return writer.writeAll(@as(*const [1]u8, &c));
}
pub fn formatBuf(
buf: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
const width = options.width orelse buf.len;
var padding = if (width > buf.len) (width - buf.len) else 0;
const pad_byte = [1]u8{options.fill};
switch (options.alignment) {
.Left => {
try out_stream.writeAll(buf);
try writer.writeAll(buf);
while (padding > 0) : (padding -= 1) {
try out_stream.writeAll(&pad_byte);
try writer.writeAll(&pad_byte);
}
},
.Center => {
const padl = padding / 2;
var i: usize = 0;
while (i < padl) : (i += 1) try out_stream.writeAll(&pad_byte);
try out_stream.writeAll(buf);
while (i < padding) : (i += 1) try out_stream.writeAll(&pad_byte);
while (i < padl) : (i += 1) try writer.writeAll(&pad_byte);
try writer.writeAll(buf);
while (i < padding) : (i += 1) try writer.writeAll(&pad_byte);
},
.Right => {
while (padding > 0) : (padding -= 1) {
try out_stream.writeAll(&pad_byte);
try writer.writeAll(&pad_byte);
}
try out_stream.writeAll(buf);
try writer.writeAll(buf);
},
}
}
@@ -627,38 +627,38 @@ pub fn formatBuf(
pub fn formatFloatScientific(
value: var,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
var x = @floatCast(f64, value);
// Errol doesn't handle these special cases.
if (math.signbit(x)) {
try out_stream.writeAll("-");
try writer.writeAll("-");
x = -x;
}
if (math.isNan(x)) {
return out_stream.writeAll("nan");
return writer.writeAll("nan");
}
if (math.isPositiveInf(x)) {
return out_stream.writeAll("inf");
return writer.writeAll("inf");
}
if (x == 0.0) {
try out_stream.writeAll("0");
try writer.writeAll("0");
if (options.precision) |precision| {
if (precision != 0) {
try out_stream.writeAll(".");
try writer.writeAll(".");
var i: usize = 0;
while (i < precision) : (i += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
}
} else {
try out_stream.writeAll(".0");
try writer.writeAll(".0");
}
try out_stream.writeAll("e+00");
try writer.writeAll("e+00");
return;
}
@@ -668,50 +668,50 @@ pub fn formatFloatScientific(
if (options.precision) |precision| {
errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
try out_stream.writeAll(float_decimal.digits[0..1]);
try writer.writeAll(float_decimal.digits[0..1]);
// {e0} case prints no `.`
if (precision != 0) {
try out_stream.writeAll(".");
try writer.writeAll(".");
var printed: usize = 0;
if (float_decimal.digits.len > 1) {
const num_digits = math.min(float_decimal.digits.len, precision + 1);
try out_stream.writeAll(float_decimal.digits[1..num_digits]);
try writer.writeAll(float_decimal.digits[1..num_digits]);
printed += num_digits - 1;
}
while (printed < precision) : (printed += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
}
} else {
try out_stream.writeAll(float_decimal.digits[0..1]);
try out_stream.writeAll(".");
try writer.writeAll(float_decimal.digits[0..1]);
try writer.writeAll(".");
if (float_decimal.digits.len > 1) {
const num_digits = if (@TypeOf(value) == f32) math.min(@as(usize, 9), float_decimal.digits.len) else float_decimal.digits.len;
try out_stream.writeAll(float_decimal.digits[1..num_digits]);
try writer.writeAll(float_decimal.digits[1..num_digits]);
} else {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
}
try out_stream.writeAll("e");
try writer.writeAll("e");
const exp = float_decimal.exp - 1;
if (exp >= 0) {
try out_stream.writeAll("+");
try writer.writeAll("+");
if (exp > -10 and exp < 10) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, out_stream);
try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, writer);
} else {
try out_stream.writeAll("-");
try writer.writeAll("-");
if (exp > -10 and exp < 10) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, out_stream);
try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, writer);
}
}
@@ -720,34 +720,34 @@ pub fn formatFloatScientific(
pub fn formatFloatDecimal(
value: var,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
var x = @as(f64, value);
// Errol doesn't handle these special cases.
if (math.signbit(x)) {
try out_stream.writeAll("-");
try writer.writeAll("-");
x = -x;
}
if (math.isNan(x)) {
return out_stream.writeAll("nan");
return writer.writeAll("nan");
}
if (math.isPositiveInf(x)) {
return out_stream.writeAll("inf");
return writer.writeAll("inf");
}
if (x == 0.0) {
try out_stream.writeAll("0");
try writer.writeAll("0");
if (options.precision) |precision| {
if (precision != 0) {
try out_stream.writeAll(".");
try writer.writeAll(".");
var i: usize = 0;
while (i < precision) : (i += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
} else {
try out_stream.writeAll(".0");
try writer.writeAll(".0");
}
}
@@ -769,14 +769,14 @@ pub fn formatFloatDecimal(
if (num_digits_whole > 0) {
// We may have to zero pad, for instance 1e4 requires zero padding.
try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
var i = num_digits_whole_no_pad;
while (i < num_digits_whole) : (i += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
} else {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
// {.0} special case doesn't want a trailing '.'
@@ -784,7 +784,7 @@ pub fn formatFloatDecimal(
return;
}
try out_stream.writeAll(".");
try writer.writeAll(".");
// Keep track of fractional count printed for case where we pre-pad then post-pad with 0's.
var printed: usize = 0;
@@ -796,7 +796,7 @@ pub fn formatFloatDecimal(
var i: usize = 0;
while (i < zeros_to_print) : (i += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
printed += 1;
}
@@ -808,14 +808,14 @@ pub fn formatFloatDecimal(
// Remaining fractional portion, zero-padding if insufficient.
assert(precision >= printed);
if (num_digits_whole_no_pad + precision - printed < float_decimal.digits.len) {
try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad .. num_digits_whole_no_pad + precision - printed]);
return;
} else {
try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
printed += float_decimal.digits.len - num_digits_whole_no_pad;
while (printed < precision) : (printed += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
}
} else {
@@ -827,14 +827,14 @@ pub fn formatFloatDecimal(
if (num_digits_whole > 0) {
// We may have to zero pad, for instance 1e4 requires zero padding.
try out_stream.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
try writer.writeAll(float_decimal.digits[0..num_digits_whole_no_pad]);
var i = num_digits_whole_no_pad;
while (i < num_digits_whole) : (i += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
} else {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
// Omit `.` if no fractional portion
@@ -842,7 +842,7 @@ pub fn formatFloatDecimal(
return;
}
try out_stream.writeAll(".");
try writer.writeAll(".");
// Zero-fill until we reach significant digits or run out of precision.
if (float_decimal.exp < 0) {
@@ -850,11 +850,11 @@ pub fn formatFloatDecimal(
var i: usize = 0;
while (i < zero_digit_count) : (i += 1) {
try out_stream.writeAll("0");
try writer.writeAll("0");
}
}
try out_stream.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
try writer.writeAll(float_decimal.digits[num_digits_whole_no_pad..]);
}
}
@@ -862,10 +862,10 @@ pub fn formatBytes(
value: var,
options: FormatOptions,
comptime radix: usize,
out_stream: var,
writer: var,
) !void {
if (value == 0) {
return out_stream.writeAll("0B");
return writer.writeAll("0B");
}
const is_float = comptime std.meta.trait.is(.Float)(@TypeOf(value));
@@ -885,10 +885,10 @@ pub fn formatBytes(
else => unreachable,
};
try formatFloatDecimal(new_value, options, out_stream);
try formatFloatDecimal(new_value, options, writer);
if (suffix == ' ') {
return out_stream.writeAll("B");
return writer.writeAll("B");
}
const buf = switch (radix) {
@@ -896,7 +896,7 @@ pub fn formatBytes(
1024 => &[_]u8{ suffix, 'i', 'B' },
else => unreachable,
};
return out_stream.writeAll(buf);
return writer.writeAll(buf);
}
pub fn formatInt(
@@ -904,7 +904,7 @@ pub fn formatInt(
base: u8,
uppercase: bool,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
const int_value = if (@TypeOf(value) == comptime_int) blk: {
const Int = math.IntFittingRange(value, value);
@@ -913,9 +913,9 @@ pub fn formatInt(
value;
if (@TypeOf(int_value).is_signed) {
return formatIntSigned(int_value, base, uppercase, options, out_stream);
return formatIntSigned(int_value, base, uppercase, options, writer);
} else {
return formatIntUnsigned(int_value, base, uppercase, options, out_stream);
return formatIntUnsigned(int_value, base, uppercase, options, writer);
}
}
@@ -924,7 +924,7 @@ fn formatIntSigned(
base: u8,
uppercase: bool,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
const new_options = FormatOptions{
.width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
@@ -934,15 +934,15 @@ fn formatIntSigned(
const bit_count = @typeInfo(@TypeOf(value)).Int.bits;
const Uint = std.meta.Int(false, bit_count);
if (value < 0) {
try out_stream.writeAll("-");
try writer.writeAll("-");
const new_value = math.absCast(value);
return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);
return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
} else if (options.width == null or options.width.? == 0) {
return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, out_stream);
return formatIntUnsigned(@intCast(Uint, value), base, uppercase, options, writer);
} else {
try out_stream.writeAll("+");
try writer.writeAll("+");
const new_value = @intCast(Uint, value);
return formatIntUnsigned(new_value, base, uppercase, new_options, out_stream);
return formatIntUnsigned(new_value, base, uppercase, new_options, writer);
}
}
@@ -951,7 +951,7 @@ fn formatIntUnsigned(
base: u8,
uppercase: bool,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
assert(base >= 2);
var buf: [math.max(@TypeOf(value).bit_count, 1)]u8 = undefined;
@@ -976,22 +976,22 @@ fn formatIntUnsigned(
const zero_byte: u8 = options.fill;
var leftover_padding = padding - index;
while (true) {
try out_stream.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
try writer.writeAll(@as(*const [1]u8, &zero_byte)[0..]);
leftover_padding -= 1;
if (leftover_padding == 0) break;
}
mem.set(u8, buf[0..index], options.fill);
return out_stream.writeAll(&buf);
return writer.writeAll(&buf);
} else {
const padded_buf = buf[index - padding ..];
mem.set(u8, padded_buf[0..padding], options.fill);
return out_stream.writeAll(padded_buf);
return writer.writeAll(padded_buf);
}
}
pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize {
var fbs = std.io.fixedBufferStream(out_buf);
formatInt(value, base, uppercase, options, fbs.outStream()) catch unreachable;
formatInt(value, base, uppercase, options, fbs.writer()) catch unreachable;
return fbs.pos;
}
@@ -1098,15 +1098,15 @@ pub const BufPrintError = error{
};
pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: var) BufPrintError![]u8 {
var fbs = std.io.fixedBufferStream(buf);
try format(fbs.outStream(), fmt, args);
try format(fbs.writer(), fmt, args);
return fbs.getWritten();
}
// Count the characters needed for format. Useful for preallocating memory
pub fn count(comptime fmt: []const u8, args: var) u64 {
var counting_stream = std.io.countingOutStream(std.io.null_out_stream);
format(counting_stream.outStream(), fmt, args) catch |err| switch (err) {};
return counting_stream.bytes_written;
var counting_writer = std.io.countingWriter(std.io.null_writer);
format(counting_writer.writer(), fmt, args) catch |err| switch (err) {};
return counting_writer.bytes_written;
}
pub const AllocPrintError = error{OutOfMemory};
@@ -1215,15 +1215,15 @@ test "buffer" {
{
var buf1: [32]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf1);
try formatType(1234, "", FormatOptions{}, fbs.outStream(), default_max_depth);
try formatType(1234, "", FormatOptions{}, fbs.writer(), default_max_depth);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1234"));
fbs.reset();
try formatType('a', "c", FormatOptions{}, fbs.outStream(), default_max_depth);
try formatType('a', "c", FormatOptions{}, fbs.writer(), default_max_depth);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "a"));
fbs.reset();
try formatType(0b1100, "b", FormatOptions{}, fbs.outStream(), default_max_depth);
try formatType(0b1100, "b", FormatOptions{}, fbs.writer(), default_max_depth);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1100"));
}
}
@@ -1413,12 +1413,12 @@ test "custom" {
self: SelfType,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "p")) {
return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });
return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
} else if (comptime std.mem.eql(u8, fmt, "d")) {
return std.fmt.format(out_stream, "{d:.3}x{d:.3}", .{ self.x, self.y });
return std.fmt.format(writer, "{d:.3}x{d:.3}", .{ self.x, self.y });
} else {
@compileError("Unknown format character: '" ++ fmt ++ "'");
}
@@ -1604,7 +1604,7 @@ test "formatIntValue with comptime_int" {
var buf: [20]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
try formatIntValue(value, "", FormatOptions{}, fbs.outStream());
try formatIntValue(value, "", FormatOptions{}, fbs.writer());
std.testing.expect(mem.eql(u8, fbs.getWritten(), "123456789123456789"));
}
@@ -1613,7 +1613,7 @@ test "formatFloatValue with comptime_float" {
var buf: [20]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
try formatFloatValue(value, "", FormatOptions{}, fbs.outStream());
try formatFloatValue(value, "", FormatOptions{}, fbs.writer());
std.testing.expect(mem.eql(u8, fbs.getWritten(), "1.0e+00"));
try testFmt("1.0e+00", "{}", .{value});
@@ -1630,10 +1630,10 @@ test "formatType max_depth" {
self: SelfType,
comptime fmt: []const u8,
options: FormatOptions,
out_stream: var,
writer: var,
) !void {
if (fmt.len == 0) {
return std.fmt.format(out_stream, "({d:.3},{d:.3})", .{ self.x, self.y });
return std.fmt.format(writer, "({d:.3},{d:.3})", .{ self.x, self.y });
} else {
@compileError("Unknown format string: '" ++ fmt ++ "'");
}
@@ -1669,19 +1669,19 @@ test "formatType max_depth" {
var buf: [1000]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 0);
try formatType(inst, "", FormatOptions{}, fbs.writer(), 0);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ ... }"));
fbs.reset();
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 1);
try formatType(inst, "", FormatOptions{}, fbs.writer(), 1);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }"));
fbs.reset();
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 2);
try formatType(inst, "", FormatOptions{}, fbs.writer(), 2);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }"));
fbs.reset();
try formatType(inst, "", FormatOptions{}, fbs.outStream(), 3);
try formatType(inst, "", FormatOptions{}, fbs.writer(), 3);
std.testing.expect(mem.eql(u8, fbs.getWritten(), "S{ .a = S{ .a = S{ .a = S{ ... }, .tu = TU{ ... }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ ... } }, .e = E.Two, .vec = (10.200,2.220) }, .tu = TU{ .ptr = TU{ .ptr = TU{ ... } } }, .e = E.Two, .vec = (10.200,2.220) }"));
}

View File

@@ -261,17 +261,7 @@ pub const Dir = struct {
name: []const u8,
kind: Kind,
pub const Kind = enum {
BlockDevice,
CharacterDevice,
Directory,
NamedPipe,
SymLink,
File,
UnixDomainSocket,
Whiteout,
Unknown,
};
pub const Kind = File.Kind;
};
const IteratorError = error{AccessDenied} || os.UnexpectedError;
@@ -1229,14 +1219,9 @@ pub const Dir = struct {
var file = try self.openFile(file_path, .{});
defer file.close();
const size = math.cast(usize, try file.getEndPos()) catch math.maxInt(usize);
if (size > max_bytes) return error.FileTooBig;
const stat_size = try file.getEndPos();
const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
errdefer allocator.free(buf);
try file.inStream().readNoEof(buf);
return buf;
return file.readAllAllocOptions(allocator, stat_size, max_bytes, alignment, optional_sentinel);
}
pub const DeleteTreeError = error{
@@ -1532,9 +1517,9 @@ pub const Dir = struct {
var size: ?u64 = null;
const mode = options.override_mode orelse blk: {
const stat = try in_file.stat();
size = stat.size;
break :blk stat.mode;
const st = try in_file.stat();
size = st.size;
break :blk st.mode;
};
var atomic_file = try dest_dir.atomicFile(dest_path, .{ .mode = mode });
@@ -1560,6 +1545,17 @@ pub const Dir = struct {
return AtomicFile.init(dest_path, options.mode, self, false);
}
}
pub const Stat = File.Stat;
pub const StatError = File.StatError;
pub fn stat(self: Dir) StatError!Stat {
const file: File = .{
.handle = self.fd,
.capable_io_mode = .blocking,
};
return file.stat();
}
};
/// Returns an handle to the current working directory. It is not opened with iteration capability.

View File

@@ -29,6 +29,18 @@ pub const File = struct {
pub const Mode = os.mode_t;
pub const INode = os.ino_t;
pub const Kind = enum {
BlockDevice,
CharacterDevice,
Directory,
NamedPipe,
SymLink,
File,
UnixDomainSocket,
Whiteout,
Unknown,
};
pub const default_mode = switch (builtin.os.tag) {
.windows => 0,
.wasi => 0,
@@ -209,7 +221,7 @@ pub const File = struct {
/// TODO: integrate with async I/O
pub fn mode(self: File) ModeError!Mode {
if (builtin.os.tag == .windows) {
return {};
return 0;
}
return (try self.stat()).mode;
}
@@ -219,13 +231,14 @@ pub const File = struct {
/// unique across time, as some file systems may reuse an inode after its file has been deleted.
/// Some systems may change the inode of a file over time.
///
/// On Linux, the inode _is_ structure that stores the metadata, and the inode _number_ is what
/// On Linux, the inode is a structure that stores the metadata, and the inode _number_ is what
/// you see here: the index number of the inode.
///
/// The FileIndex on Windows is similar. It is a number for a file that is unique to each filesystem.
inode: INode,
size: u64,
mode: Mode,
kind: Kind,
/// Access time in nanoseconds, relative to UTC 1970-01-01.
atime: i128,
@@ -254,6 +267,7 @@ pub const File = struct {
.inode = info.InternalInformation.IndexNumber,
.size = @bitCast(u64, info.StandardInformation.EndOfFile),
.mode = 0,
.kind = if (info.StandardInformation.Directory == 0) .File else .Directory,
.atime = windows.fromSysTime(info.BasicInformation.LastAccessTime),
.mtime = windows.fromSysTime(info.BasicInformation.LastWriteTime),
.ctime = windows.fromSysTime(info.BasicInformation.CreationTime),
@@ -268,6 +282,27 @@ pub const File = struct {
.inode = st.ino,
.size = @bitCast(u64, st.size),
.mode = st.mode,
.kind = switch (builtin.os.tag) {
.wasi => switch (st.filetype) {
os.FILETYPE_BLOCK_DEVICE => Kind.BlockDevice,
os.FILETYPE_CHARACTER_DEVICE => Kind.CharacterDevice,
os.FILETYPE_DIRECTORY => Kind.Directory,
os.FILETYPE_SYMBOLIC_LINK => Kind.SymLink,
os.FILETYPE_REGULAR_FILE => Kind.File,
os.FILETYPE_SOCKET_STREAM, os.FILETYPE_SOCKET_DGRAM => Kind.UnixDomainSocket,
else => Kind.Unknown,
},
else => switch (st.mode & os.S_IFMT) {
os.S_IFBLK => Kind.BlockDevice,
os.S_IFCHR => Kind.CharacterDevice,
os.S_IFDIR => Kind.Directory,
os.S_IFIFO => Kind.NamedPipe,
os.S_IFLNK => Kind.SymLink,
os.S_IFREG => Kind.File,
os.S_IFSOCK => Kind.UnixDomainSocket,
else => Kind.Unknown,
},
},
.atime = @as(i128, atime.tv_sec) * std.time.ns_per_s + atime.tv_nsec,
.mtime = @as(i128, mtime.tv_sec) * std.time.ns_per_s + mtime.tv_nsec,
.ctime = @as(i128, ctime.tv_sec) * std.time.ns_per_s + ctime.tv_nsec,
@@ -306,6 +341,33 @@ pub const File = struct {
try os.futimens(self.handle, &times);
}
/// On success, caller owns returned buffer.
/// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
pub fn readAllAlloc(self: File, allocator: *mem.Allocator, stat_size: u64, max_bytes: usize) ![]u8 {
return self.readAllAllocOptions(allocator, stat_size, max_bytes, @alignOf(u8), null);
}
/// On success, caller owns returned buffer.
/// If the file is larger than `max_bytes`, returns `error.FileTooBig`.
/// Allows specifying alignment and a sentinel value.
pub fn readAllAllocOptions(
self: File,
allocator: *mem.Allocator,
stat_size: u64,
max_bytes: usize,
comptime alignment: u29,
comptime optional_sentinel: ?u8,
) !(if (optional_sentinel) |s| [:s]align(alignment) u8 else []align(alignment) u8) {
const size = math.cast(usize, stat_size) catch math.maxInt(usize);
if (size > max_bytes) return error.FileTooBig;
const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
errdefer allocator.free(buf);
try self.reader().readNoEof(buf);
return buf;
}
pub const ReadError = os.ReadError;
pub const PReadError = os.PReadError;

View File

@@ -1,7 +1,44 @@
const std = @import("../std.zig");
const testing = std.testing;
const builtin = std.builtin;
const fs = std.fs;
const mem = std.mem;
const File = std.fs.File;
const tmpDir = testing.tmpDir;
test "readAllAlloc" {
var tmp_dir = tmpDir(.{});
defer tmp_dir.cleanup();
var file = try tmp_dir.dir.createFile("test_file", .{ .read = true });
defer file.close();
const buf1 = try file.readAllAlloc(testing.allocator, 0, 1024);
defer testing.allocator.free(buf1);
testing.expect(buf1.len == 0);
const write_buf: []const u8 = "this is a test.\nthis is a test.\nthis is a test.\nthis is a test.\n";
try file.writeAll(write_buf);
try file.seekTo(0);
const file_size = try file.getEndPos();
// max_bytes > file_size
const buf2 = try file.readAllAlloc(testing.allocator, file_size, 1024);
defer testing.allocator.free(buf2);
testing.expectEqual(write_buf.len, buf2.len);
testing.expect(std.mem.eql(u8, write_buf, buf2));
try file.seekTo(0);
// max_bytes == file_size
const buf3 = try file.readAllAlloc(testing.allocator, file_size, write_buf.len);
defer testing.allocator.free(buf3);
testing.expectEqual(write_buf.len, buf3.len);
testing.expect(std.mem.eql(u8, write_buf, buf3));
// max_bytes < file_size
testing.expectError(error.FileTooBig, file.readAllAlloc(testing.allocator, file_size, write_buf.len - 1));
}
test "openSelfExe" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
@@ -116,7 +153,7 @@ test "create file, lock and read from multiple process at once" {
test "open file with exclusive nonblocking lock twice (absolute paths)" {
if (builtin.os.tag == .wasi) return error.SkipZigTest;
const allocator = std.testing.allocator;
const allocator = testing.allocator;
const file_paths: [1][]const u8 = .{"zig-test-absolute-paths.txt"};
const filename = try fs.path.resolve(allocator, &file_paths);
@@ -126,7 +163,7 @@ test "open file with exclusive nonblocking lock twice (absolute paths)" {
const file2 = fs.createFileAbsolute(filename, .{ .lock = .Exclusive, .lock_nonblocking = true });
file1.close();
std.testing.expectError(error.WouldBlock, file2);
testing.expectError(error.WouldBlock, file2);
try fs.deleteFileAbsolute(filename);
}
@@ -187,7 +224,7 @@ const FileLockTestContext = struct {
};
fn run_lock_file_test(contexts: []FileLockTestContext) !void {
var threads = std.ArrayList(*std.Thread).init(std.testing.allocator);
var threads = std.ArrayList(*std.Thread).init(testing.allocator);
defer {
for (threads.items) |thread| {
thread.wait();

View File

@@ -2,4 +2,4 @@
pub const BufferedOutStream = @import("./buffered_writer.zig").BufferedWriter;
/// Deprecated: use `std.io.buffered_writer.bufferedWriter`
pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter
pub const bufferedOutStream = @import("./buffered_writer.zig").bufferedWriter;

View File

@@ -40,8 +40,7 @@ pub fn Reader(
return index;
}
/// Returns the number of bytes read. If the number read would be smaller than buf.len,
/// error.EndOfStream is returned instead.
/// If the number read would be smaller than `buf.len`, `error.EndOfStream` is returned instead.
pub fn readNoEof(self: Self, buf: []u8) !void {
const amt_read = try self.readAll(buf);
if (amt_read < buf.len) return error.EndOfStream;

View File

@@ -1535,7 +1535,7 @@ fn parseInternal(comptime T: type, token: Token, tokens: *TokenStream, options:
const allocator = options.allocator orelse return error.AllocatorRequired;
switch (ptrInfo.size) {
.One => {
const r: T = allocator.create(ptrInfo.child);
const r: T = try allocator.create(ptrInfo.child);
r.* = try parseInternal(ptrInfo.child, token, tokens, options);
return r;
},
@@ -1629,7 +1629,7 @@ pub fn parseFree(comptime T: type, value: T, options: ParseOptions) void {
switch (ptrInfo.size) {
.One => {
parseFree(ptrInfo.child, value.*, options);
allocator.destroy(v);
allocator.destroy(value);
},
.Slice => {
for (value) |v| {
@@ -2576,8 +2576,8 @@ pub fn stringify(
},
.Array => return stringify(&value, options, out_stream),
.Vector => |info| {
const array: [info.len]info.child = value;
return stringify(&array, options, out_stream);
const array: [info.len]info.child = value;
return stringify(&array, options, out_stream);
},
else => @compileError("Unable to stringify type '" ++ @typeName(T) ++ "'"),
}
@@ -2770,4 +2770,3 @@ test "stringify struct with custom stringifier" {
test "stringify vector" {
try teststringify("[1,1]", @splat(2, @as(u32, 1)), StringifyOptions{});
}

202
lib/std/log.zig Normal file
View File

@@ -0,0 +1,202 @@
const std = @import("std.zig");
const builtin = std.builtin;
const root = @import("root");
//! std.log is standardized interface for logging which allows for the logging
//! of programs and libraries using this interface to be formatted and filtered
//! by the implementer of the root.log function.
//!
//! The scope parameter should be used to give context to the logging. For
//! example, a library called 'libfoo' might use .libfoo as its scope.
//!
//! An example root.log might look something like this:
//!
//! ```
//! const std = @import("std");
//!
//! // Set the log level to warning
//! pub const log_level: std.log.Level = .warn;
//!
//! // Define root.log to override the std implementation
//! pub fn log(
//! comptime level: std.log.Level,
//! comptime scope: @TypeOf(.EnumLiteral),
//! comptime format: []const u8,
//! args: var,
//! ) void {
//! // Ignore all non-critical logging from sources other than
//! // .my_project and .nice_library
//! const scope_prefix = "(" ++ switch (scope) {
//! .my_project, .nice_library => @tagName(scope),
//! else => if (@enumToInt(level) <= @enumToInt(std.log.Level.crit))
//! @tagName(scope)
//! else
//! return,
//! } ++ "): ";
//!
//! const prefix = "[" ++ @tagName(level) ++ "] " ++ scope_prefix;
//!
//! // Print the message to stderr, silently ignoring any errors
//! const held = std.debug.getStderrMutex().acquire();
//! defer held.release();
//! const stderr = std.debug.getStderrStream();
//! nosuspend stderr.print(prefix ++ format, args) catch return;
//! }
//!
//! pub fn main() void {
//! // Won't be printed as log_level is .warn
//! std.log.info(.my_project, "Starting up.\n", .{});
//! std.log.err(.nice_library, "Something went very wrong, sorry.\n", .{});
//! // Won't be printed as it gets filtered out by our log function
//! std.log.err(.lib_that_logs_too_much, "Added 1 + 1\n", .{});
//! }
//! ```
//! Which produces the following output:
//! ```
//! [err] (nice_library): Something went very wrong, sorry.
//! ```
pub const Level = enum {
/// Emergency: a condition that cannot be handled, usually followed by a
/// panic.
emerg,
/// Alert: a condition that should be corrected immediately (e.g. database
/// corruption).
alert,
/// Critical: A bug has been detected or something has gone wrong and it
/// will have an effect on the operation of the program.
crit,
/// Error: A bug has been detected or something has gone wrong but it is
/// recoverable.
err,
/// Warning: it is uncertain if something has gone wrong or not, but the
/// circumstances would be worth investigating.
warn,
/// Notice: non-error but significant conditions.
notice,
/// Informational: general messages about the state of the program.
info,
/// Debug: messages only useful for debugging.
debug,
};
/// The default log level is based on build mode. Note that in ReleaseSmall
/// builds the default level is emerg but no messages will be stored/logged
/// by the default logger to save space.
pub const default_level: Level = switch (builtin.mode) {
.Debug => .debug,
.ReleaseSafe => .notice,
.ReleaseFast => .err,
.ReleaseSmall => .emerg,
};
/// The current log level. This is set to root.log_level if present, otherwise
/// log.default_level.
pub const level: Level = if (@hasDecl(root, "log_level"))
root.log_level
else
default_level;
fn log(
comptime message_level: Level,
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
if (@enumToInt(message_level) <= @enumToInt(level)) {
if (@hasDecl(root, "log")) {
root.log(message_level, scope, format, args);
} else if (builtin.mode != .ReleaseSmall) {
const held = std.debug.getStderrMutex().acquire();
defer held.release();
const stderr = std.io.getStdErr().writer();
nosuspend stderr.print(format, args) catch return;
}
}
}
/// Log an emergency message to stderr. This log level is intended to be used
/// for conditions that cannot be handled and is usually followed by a panic.
pub fn emerg(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.emerg, scope, format, args);
}
/// Log an alert message to stderr. This log level is intended to be used for
/// conditions that should be corrected immediately (e.g. database corruption).
pub fn alert(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.alert, scope, format, args);
}
/// Log a critical message to stderr. This log level is intended to be used
/// when a bug has been detected or something has gone wrong and it will have
/// an effect on the operation of the program.
pub fn crit(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.crit, scope, format, args);
}
/// Log an error message to stderr. This log level is intended to be used when
/// a bug has been detected or something has gone wrong but it is recoverable.
pub fn err(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
@setCold(true);
log(.err, scope, format, args);
}
/// Log a warning message to stderr. This log level is intended to be used if
/// it is uncertain whether something has gone wrong or not, but the
/// circumstances would be worth investigating.
pub fn warn(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
log(.warn, scope, format, args);
}
/// Log a notice message to stderr. This log level is intended to be used for
/// non-error but significant conditions.
pub fn notice(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
log(.notice, scope, format, args);
}
/// Log an info message to stderr. This log level is intended to be used for
/// general messages about the state of the program.
pub fn info(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
log(.info, scope, format, args);
}
/// Log a debug message to stderr. This log level is intended to be used for
/// messages which are only useful for debugging.
pub fn debug(
comptime scope: @Type(.EnumLiteral),
comptime format: []const u8,
args: var,
) void {
log(.debug, scope, format, args);
}

View File

@@ -122,6 +122,11 @@ pub fn forceEval(value: var) void {
const p = @ptrCast(*volatile f64, &x);
p.* = x;
},
f128 => {
var x: f128 = undefined;
const p = @ptrCast(*volatile f128, &x);
p.* = x;
},
else => {
@compileError("forceEval not implemented for " ++ @typeName(T));
},

View File

@@ -20,6 +20,7 @@ pub fn ceil(x: var) @TypeOf(x) {
return switch (T) {
f32 => ceil32(x),
f64 => ceil64(x),
f128 => ceil128(x),
else => @compileError("ceil not implemented for " ++ @typeName(T)),
};
}
@@ -86,9 +87,37 @@ fn ceil64(x: f64) f64 {
}
}
fn ceil128(x: f128) f128 {
const u = @bitCast(u128, x);
const e = (u >> 112) & 0x7FFF;
var y: f128 = undefined;
if (e >= 0x3FFF + 112 or x == 0) return x;
if (u >> 127 != 0) {
y = x - math.f128_toint + math.f128_toint - x;
} else {
y = x + math.f128_toint - math.f128_toint - x;
}
if (e <= 0x3FFF - 1) {
math.forceEval(y);
if (u >> 127 != 0) {
return -0.0;
} else {
return 1.0;
}
} else if (y < 0) {
return x + y + 1;
} else {
return x + y;
}
}
test "math.ceil" {
expect(ceil(@as(f32, 0.0)) == ceil32(0.0));
expect(ceil(@as(f64, 0.0)) == ceil64(0.0));
expect(ceil(@as(f128, 0.0)) == ceil128(0.0));
}
test "math.ceil32" {
@@ -103,6 +132,12 @@ test "math.ceil64" {
expect(ceil64(0.2) == 1.0);
}
test "math.ceil128" {
expect(ceil128(1.3) == 2.0);
expect(ceil128(-1.3) == -1.0);
expect(ceil128(0.2) == 1.0);
}
test "math.ceil32.special" {
expect(ceil32(0.0) == 0.0);
expect(ceil32(-0.0) == -0.0);
@@ -118,3 +153,11 @@ test "math.ceil64.special" {
expect(math.isNegativeInf(ceil64(-math.inf(f64))));
expect(math.isNan(ceil64(math.nan(f64))));
}
test "math.ceil128.special" {
expect(ceil128(0.0) == 0.0);
expect(ceil128(-0.0) == -0.0);
expect(math.isPositiveInf(ceil128(math.inf(f128))));
expect(math.isNegativeInf(ceil128(-math.inf(f128))));
expect(math.isNan(ceil128(math.nan(f128))));
}

View File

@@ -21,6 +21,7 @@ pub fn floor(x: var) @TypeOf(x) {
f16 => floor16(x),
f32 => floor32(x),
f64 => floor64(x),
f128 => floor128(x),
else => @compileError("floor not implemented for " ++ @typeName(T)),
};
}
@@ -122,10 +123,38 @@ fn floor64(x: f64) f64 {
}
}
fn floor128(x: f128) f128 {
const u = @bitCast(u128, x);
const e = (u >> 112) & 0x7FFF;
var y: f128 = undefined;
if (e >= 0x3FFF + 112 or x == 0) return x;
if (u >> 127 != 0) {
y = x - math.f128_toint + math.f128_toint - x;
} else {
y = x + math.f128_toint - math.f128_toint - x;
}
if (e <= 0x3FFF - 1) {
math.forceEval(y);
if (u >> 127 != 0) {
return -1.0;
} else {
return 0.0;
}
} else if (y > 0) {
return x + y - 1;
} else {
return x + y;
}
}
test "math.floor" {
expect(floor(@as(f16, 1.3)) == floor16(1.3));
expect(floor(@as(f32, 1.3)) == floor32(1.3));
expect(floor(@as(f64, 1.3)) == floor64(1.3));
expect(floor(@as(f128, 1.3)) == floor128(1.3));
}
test "math.floor16" {
@@ -146,6 +175,12 @@ test "math.floor64" {
expect(floor64(0.2) == 0.0);
}
test "math.floor128" {
expect(floor128(1.3) == 1.0);
expect(floor128(-1.3) == -2.0);
expect(floor128(0.2) == 0.0);
}
test "math.floor16.special" {
expect(floor16(0.0) == 0.0);
expect(floor16(-0.0) == -0.0);
@@ -169,3 +204,11 @@ test "math.floor64.special" {
expect(math.isNegativeInf(floor64(-math.inf(f64))));
expect(math.isNan(floor64(math.nan(f64))));
}
test "math.floor128.special" {
expect(floor128(0.0) == 0.0);
expect(floor128(-0.0) == -0.0);
expect(math.isPositiveInf(floor128(math.inf(f128))));
expect(math.isNegativeInf(floor128(-math.inf(f128))));
expect(math.isNan(floor128(math.nan(f128))));
}

View File

@@ -20,6 +20,7 @@ pub fn round(x: var) @TypeOf(x) {
return switch (T) {
f32 => round32(x),
f64 => round64(x),
f128 => round128(x),
else => @compileError("round not implemented for " ++ @typeName(T)),
};
}
@@ -90,9 +91,43 @@ fn round64(x_: f64) f64 {
}
}
fn round128(x_: f128) f128 {
var x = x_;
const u = @bitCast(u128, x);
const e = (u >> 112) & 0x7FFF;
var y: f128 = undefined;
if (e >= 0x3FFF + 112) {
return x;
}
if (u >> 127 != 0) {
x = -x;
}
if (e < 0x3FFF - 1) {
math.forceEval(x + math.f64_toint);
return 0 * @bitCast(f128, u);
}
y = x + math.f128_toint - math.f128_toint - x;
if (y > 0.5) {
y = y + x - 1;
} else if (y <= -0.5) {
y = y + x + 1;
} else {
y = y + x;
}
if (u >> 127 != 0) {
return -y;
} else {
return y;
}
}
test "math.round" {
expect(round(@as(f32, 1.3)) == round32(1.3));
expect(round(@as(f64, 1.3)) == round64(1.3));
expect(round(@as(f128, 1.3)) == round128(1.3));
}
test "math.round32" {
@@ -109,6 +144,13 @@ test "math.round64" {
expect(round64(1.8) == 2.0);
}
test "math.round128" {
expect(round128(1.3) == 1.0);
expect(round128(-1.3) == -1.0);
expect(round128(0.2) == 0.0);
expect(round128(1.8) == 2.0);
}
test "math.round32.special" {
expect(round32(0.0) == 0.0);
expect(round32(-0.0) == -0.0);
@@ -124,3 +166,11 @@ test "math.round64.special" {
expect(math.isNegativeInf(round64(-math.inf(f64))));
expect(math.isNan(round64(math.nan(f64))));
}
test "math.round128.special" {
expect(round128(0.0) == 0.0);
expect(round128(-0.0) == -0.0);
expect(math.isPositiveInf(round128(math.inf(f128))));
expect(math.isNegativeInf(round128(-math.inf(f128))));
expect(math.isNan(round128(math.nan(f128))));
}

View File

@@ -20,6 +20,7 @@ pub fn trunc(x: var) @TypeOf(x) {
return switch (T) {
f32 => trunc32(x),
f64 => trunc64(x),
f128 => trunc128(x),
else => @compileError("trunc not implemented for " ++ @typeName(T)),
};
}
@@ -66,9 +67,31 @@ fn trunc64(x: f64) f64 {
}
}
fn trunc128(x: f128) f128 {
const u = @bitCast(u128, x);
var e = @intCast(i32, ((u >> 112) & 0x7FFF)) - 0x3FFF + 16;
var m: u128 = undefined;
if (e >= 112 + 16) {
return x;
}
if (e < 16) {
e = 1;
}
m = @as(u128, maxInt(u128)) >> @intCast(u7, e);
if (u & m == 0) {
return x;
} else {
math.forceEval(x + 0x1p120);
return @bitCast(f128, u & ~m);
}
}
test "math.trunc" {
expect(trunc(@as(f32, 1.3)) == trunc32(1.3));
expect(trunc(@as(f64, 1.3)) == trunc64(1.3));
expect(trunc(@as(f128, 1.3)) == trunc128(1.3));
}
test "math.trunc32" {
@@ -83,6 +106,12 @@ test "math.trunc64" {
expect(trunc64(0.2) == 0.0);
}
test "math.trunc128" {
expect(trunc128(1.3) == 1.0);
expect(trunc128(-1.3) == -1.0);
expect(trunc128(0.2) == 0.0);
}
test "math.trunc32.special" {
expect(trunc32(0.0) == 0.0); // 0x3F800000
expect(trunc32(-0.0) == -0.0);
@@ -98,3 +127,11 @@ test "math.trunc64.special" {
expect(math.isNegativeInf(trunc64(-math.inf(f64))));
expect(math.isNan(trunc64(math.nan(f64))));
}
test "math.trunc128.special" {
expect(trunc128(0.0) == 0.0);
expect(trunc128(-0.0) == -0.0);
expect(math.isPositiveInf(trunc128(math.inf(f128))));
expect(math.isNegativeInf(trunc128(-math.inf(f128))));
expect(math.isNan(trunc128(math.nan(f128))));
}

View File

@@ -250,7 +250,7 @@ test "std.meta.containerLayout" {
testing.expect(containerLayout(U3) == .Extern);
}
pub fn declarations(comptime T: type) []TypeInfo.Declaration {
pub fn declarations(comptime T: type) []const TypeInfo.Declaration {
return switch (@typeInfo(T)) {
.Struct => |info| info.decls,
.Enum => |info| info.decls,
@@ -274,7 +274,7 @@ test "std.meta.declarations" {
fn a() void {}
};
const decls = comptime [_][]TypeInfo.Declaration{
const decls = comptime [_][]const TypeInfo.Declaration{
declarations(E1),
declarations(S1),
declarations(U1),
@@ -323,10 +323,10 @@ test "std.meta.declarationInfo" {
}
pub fn fields(comptime T: type) switch (@typeInfo(T)) {
.Struct => []TypeInfo.StructField,
.Union => []TypeInfo.UnionField,
.ErrorSet => []TypeInfo.Error,
.Enum => []TypeInfo.EnumField,
.Struct => []const TypeInfo.StructField,
.Union => []const TypeInfo.UnionField,
.ErrorSet => []const TypeInfo.Error,
.Enum => []const TypeInfo.EnumField,
else => @compileError("Expected struct, union, error set or enum type, found '" ++ @typeName(T) ++ "'"),
} {
return switch (@typeInfo(T)) {
@@ -693,3 +693,85 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
},
});
}
/// Given a type and value, cast the value to the type as c would.
/// This is for translate-c and is not intended for general use.
pub fn cast(comptime DestType: type, target: var) DestType {
const TargetType = @TypeOf(target);
switch (@typeInfo(DestType)) {
.Pointer => {
switch (@typeInfo(TargetType)) {
.Int, .ComptimeInt => {
return @intToPtr(DestType, target);
},
.Pointer => |ptr| {
return @ptrCast(DestType, @alignCast(ptr.alignment, target));
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
return @ptrCast(DestType, @alignCast(@alignOf(opt.child.Child), target));
}
},
else => {},
}
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
switch (@typeInfo(TargetType)) {
.Int, .ComptimeInt => {
return @intToPtr(DestType, target);
},
.Pointer => |ptr| {
return @ptrCast(DestType, @alignCast(ptr.alignment, target));
},
.Optional => |target_opt| {
if (@typeInfo(target_opt.child) == .Pointer) {
return @ptrCast(DestType, @alignCast(@alignOf(target_opt.child.Child), target));
}
},
else => {},
}
}
},
.Enum, .EnumLiteral => {
if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
return @intToEnum(DestType, target);
}
},
.Int, .ComptimeInt => {
switch (@typeInfo(TargetType)) {
.Pointer => {
return @as(DestType, @ptrToInt(target));
},
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
return @as(DestType, @ptrToInt(target));
}
},
.Enum, .EnumLiteral => {
return @as(DestType, @enumToInt(target));
},
else => {},
}
},
else => {},
}
return @as(DestType, target);
}
test "std.meta.cast" {
const E = enum(u2) {
Zero,
One,
Two,
};
var i = @as(i64, 10);
testing.expect(cast(?*c_void, 0) == @intToPtr(?*c_void, 0));
testing.expect(cast(*u8, 16) == @intToPtr(*u8, 16));
testing.expect(cast(u64, @as(u32, 10)) == @as(u64, 10));
testing.expect(cast(E, 1) == .One);
testing.expect(cast(u8, E.Two) == 2);
testing.expect(cast(*u64, &i).* == @as(u64, 10));
}

View File

@@ -1520,15 +1520,17 @@ pub const SymLinkError = error{
/// If `sym_link_path` exists, it will not be overwritten.
/// See also `symlinkC` and `symlinkW`.
pub fn symlink(target_path: []const u8, sym_link_path: []const u8) SymLinkError!void {
if (builtin.os.tag == .wasi) {
@compileError("symlink is not supported in WASI; use symlinkat instead");
}
if (builtin.os.tag == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
return windows.CreateSymbolicLinkW(sym_link_path_w.span().ptr, target_path_w.span().ptr, 0);
} else {
const target_path_c = try toPosixPath(target_path);
const sym_link_path_c = try toPosixPath(sym_link_path);
return symlinkZ(&target_path_c, &sym_link_path_c);
}
const target_path_c = try toPosixPath(target_path);
const sym_link_path_c = try toPosixPath(sym_link_path);
return symlinkZ(&target_path_c, &sym_link_path_c);
}
pub const symlinkC = @compileError("deprecated: renamed to symlinkZ");
@@ -1561,15 +1563,65 @@ pub fn symlinkZ(target_path: [*:0]const u8, sym_link_path: [*:0]const u8) SymLin
}
}
/// Similar to `symlink`, however, creates a symbolic link named `sym_link_path` which contains the string
/// `target_path` **relative** to `newdirfd` directory handle.
/// A symbolic link (also known as a soft link) may point to an existing file or to a nonexistent
/// one; the latter case is known as a dangling link.
/// If `sym_link_path` exists, it will not be overwritten.
/// See also `symlinkatWasi`, `symlinkatZ` and `symlinkatW`.
pub fn symlinkat(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
if (builtin.os.tag == .wasi) {
return symlinkatWasi(target_path, newdirfd, sym_link_path);
}
if (builtin.os.tag == .windows) {
const target_path_w = try windows.sliceToPrefixedFileW(target_path);
const sym_link_path_w = try windows.sliceToPrefixedFileW(sym_link_path);
return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path_w.span().ptr);
}
const target_path_c = try toPosixPath(target_path);
const sym_link_path_c = try toPosixPath(sym_link_path);
return symlinkatZ(target_path_c, newdirfd, sym_link_path_c);
return symlinkatZ(&target_path_c, newdirfd, &sym_link_path_c);
}
pub const symlinkatC = @compileError("deprecated: renamed to symlinkatZ");
/// WASI-only. The same as `symlinkat` but targeting WASI.
/// See also `symlinkat`.
pub fn symlinkatWasi(target_path: []const u8, newdirfd: fd_t, sym_link_path: []const u8) SymLinkError!void {
switch (wasi.path_symlink(target_path.ptr, target_path.len, newdirfd, sym_link_path.ptr, sym_link_path.len)) {
wasi.ESUCCESS => {},
wasi.EFAULT => unreachable,
wasi.EINVAL => unreachable,
wasi.EACCES => return error.AccessDenied,
wasi.EPERM => return error.AccessDenied,
wasi.EDQUOT => return error.DiskQuota,
wasi.EEXIST => return error.PathAlreadyExists,
wasi.EIO => return error.FileSystem,
wasi.ELOOP => return error.SymLinkLoop,
wasi.ENAMETOOLONG => return error.NameTooLong,
wasi.ENOENT => return error.FileNotFound,
wasi.ENOTDIR => return error.NotDir,
wasi.ENOMEM => return error.SystemResources,
wasi.ENOSPC => return error.NoSpaceLeft,
wasi.EROFS => return error.ReadOnlyFileSystem,
else => |err| return unexpectedErrno(err),
}
}
/// Windows-only. The same as `symlinkat` except the paths are null-terminated, WTF-16 encoded.
/// See also `symlinkat`.
pub fn symlinkatW(target_path: [*:0]const u16, newdirfd: fd_t, sym_link_path: [*:0]const u16) SymlinkError!void {
@compileError("TODO implement on Windows");
}
/// The same as `symlinkat` except the parameters are null-terminated pointers.
/// See also `symlinkat`.
pub fn symlinkatZ(target_path: [*:0]const u8, newdirfd: fd_t, sym_link_path: [*:0]const u8) SymLinkError!void {
if (builtin.os.tag == .windows) {
const target_path_w = try windows.cStrToPrefixedFileW(target_path);
const sym_link_path_w = try windows.cStrToPrefixedFileW(sym_link_path);
return symlinkatW(target_path_w.span().ptr, newdirfd, sym_link_path.span().ptr);
}
switch (errno(system.symlinkat(target_path, newdirfd, sym_link_path))) {
0 => return,
EFAULT => unreachable,
@@ -2291,12 +2343,54 @@ pub fn readlinkZ(file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8
}
}
/// Similar to `readlink` except reads value of a symbolink link **relative** to `dirfd` directory handle.
/// The return value is a slice of `out_buffer` from index 0.
/// See also `readlinkatWasi`, `realinkatZ` and `realinkatW`.
pub fn readlinkat(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os.tag == .wasi) {
return readlinkatWasi(dirfd, file_path, out_buffer);
}
if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
return readlinkatW(dirfd, file_path.span().ptr, out_buffer);
}
const file_path_c = try toPosixPath(file_path);
return readlinkatZ(dirfd, &file_path_c, out_buffer);
}
pub const readlinkatC = @compileError("deprecated: renamed to readlinkatZ");
/// WASI-only. Same as `readlinkat` but targets WASI.
/// See also `readlinkat`.
pub fn readlinkatWasi(dirfd: fd_t, file_path: []const u8, out_buffer: []u8) ReadLinkError![]u8 {
var bufused: usize = undefined;
switch (wasi.path_readlink(dirfd, file_path.ptr, file_path.len, out_buffer.ptr, out_buffer.len, &bufused)) {
wasi.ESUCCESS => return out_buffer[0..bufused],
wasi.EACCES => return error.AccessDenied,
wasi.EFAULT => unreachable,
wasi.EINVAL => unreachable,
wasi.EIO => return error.FileSystem,
wasi.ELOOP => return error.SymLinkLoop,
wasi.ENAMETOOLONG => return error.NameTooLong,
wasi.ENOENT => return error.FileNotFound,
wasi.ENOMEM => return error.SystemResources,
wasi.ENOTDIR => return error.NotDir,
else => |err| return unexpectedErrno(err),
}
}
/// Windows-only. Same as `readlinkat` except `file_path` is null-terminated, WTF16 encoded.
/// See also `readlinkat`.
pub fn readlinkatW(dirfd: fd_t, file_path: [*:0]const u16, out_buffer: []u8) ReadLinkError![]u8 {
@compileError("TODO implement on Windows");
}
/// Same as `readlinkat` except `file_path` is null-terminated.
/// See also `readlinkat`.
pub fn readlinkatZ(dirfd: fd_t, file_path: [*:0]const u8, out_buffer: []u8) ReadLinkError![]u8 {
if (builtin.os.tag == .windows) {
const file_path_w = try windows.cStrToPrefixedFileW(file_path);
@compileError("TODO implement readlink for Windows");
return readlinkatW(dirfd, file_path_w.span().ptr, out_buffer);
}
const rc = system.readlinkat(dirfd, file_path, out_buffer.ptr, out_buffer.len);
switch (errno(rc)) {

View File

@@ -18,6 +18,25 @@ const AtomicOrder = builtin.AtomicOrder;
const tmpDir = std.testing.tmpDir;
const Dir = std.fs.Dir;
test "readlinkat" {
// enable when `readlinkat` and `symlinkat` are implemented on Windows
if (builtin.os.tag == .windows) return error.SkipZigTest;
var tmp = tmpDir(.{});
defer tmp.cleanup();
// create file
try tmp.dir.writeFile("file.txt", "nonsense");
// create a symbolic link
try os.symlinkat("file.txt", tmp.dir.fd, "link");
// read the link
var buffer: [fs.MAX_PATH_BYTES]u8 = undefined;
const read_link = try os.readlinkat(tmp.dir.fd, "link", buffer[0..]);
expect(mem.eql(u8, "file.txt", read_link));
}
test "makePath, put some files in it, deleteTree" {
var tmp = tmpDir(.{});
defer tmp.cleanup();

View File

@@ -901,7 +901,13 @@ pub fn WSAStartup(majorVersion: u8, minorVersion: u8) !ws2_32.WSADATA {
var wsadata: ws2_32.WSADATA = undefined;
return switch (ws2_32.WSAStartup((@as(WORD, minorVersion) << 8) | majorVersion, &wsadata)) {
0 => wsadata,
else => |err| unexpectedWSAError(@intToEnum(ws2_32.WinsockError, @intCast(u16, err))),
else => |err_int| switch (@intToEnum(ws2_32.WinsockError, @intCast(u16, err_int))) {
.WSASYSNOTREADY => return error.SystemNotAvailable,
.WSAVERNOTSUPPORTED => return error.VersionNotSupported,
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
.WSAEPROCLIM => return error.SystemResources,
else => |err| return unexpectedWSAError(err),
},
};
}
@@ -909,6 +915,9 @@ pub fn WSACleanup() !void {
return switch (ws2_32.WSACleanup()) {
0 => {},
ws2_32.SOCKET_ERROR => switch (ws2_32.WSAGetLastError()) {
.WSANOTINITIALISED => return error.NotInitialized,
.WSAENETDOWN => return error.NetworkNotAvailable,
.WSAEINPROGRESS => return error.BlockingOperationInProgress,
else => |err| return unexpectedWSAError(err),
},
else => unreachable,

View File

@@ -163,16 +163,16 @@ pub const IPPROTO_UDP = 17;
pub const IPPROTO_ICMPV6 = 58;
pub const IPPROTO_RM = 113;
pub const AI_PASSIVE = 0x00001;
pub const AI_CANONNAME = 0x00002;
pub const AI_NUMERICHOST = 0x00004;
pub const AI_NUMERICSERV = 0x00008;
pub const AI_ADDRCONFIG = 0x00400;
pub const AI_V4MAPPED = 0x00800;
pub const AI_NON_AUTHORITATIVE = 0x04000;
pub const AI_SECURE = 0x08000;
pub const AI_PASSIVE = 0x00001;
pub const AI_CANONNAME = 0x00002;
pub const AI_NUMERICHOST = 0x00004;
pub const AI_NUMERICSERV = 0x00008;
pub const AI_ADDRCONFIG = 0x00400;
pub const AI_V4MAPPED = 0x00800;
pub const AI_NON_AUTHORITATIVE = 0x04000;
pub const AI_SECURE = 0x08000;
pub const AI_RETURN_PREFERRED_NAMES = 0x10000;
pub const AI_DISABLE_IDN_ENCODING = 0x80000;
pub const AI_DISABLE_IDN_ENCODING = 0x80000;
pub const FIONBIO = -2147195266;

View File

@@ -281,9 +281,6 @@ pub const ArgIteratorWasi = struct {
pub const ArgIteratorWindows = struct {
index: usize,
cmd_line: [*]const u8,
in_quote: bool,
quote_count: usize,
seen_quote_count: usize,
pub const NextError = error{OutOfMemory};
@@ -295,9 +292,6 @@ pub const ArgIteratorWindows = struct {
return ArgIteratorWindows{
.index = 0,
.cmd_line = cmd_line,
.in_quote = false,
.quote_count = countQuotes(cmd_line),
.seen_quote_count = 0,
};
}
@@ -328,6 +322,7 @@ pub const ArgIteratorWindows = struct {
}
var backslash_count: usize = 0;
var in_quote = false;
while (true) : (self.index += 1) {
const byte = self.cmd_line[self.index];
switch (byte) {
@@ -335,14 +330,14 @@ pub const ArgIteratorWindows = struct {
'"' => {
const quote_is_real = backslash_count % 2 == 0;
if (quote_is_real) {
self.seen_quote_count += 1;
in_quote = !in_quote;
}
},
'\\' => {
backslash_count += 1;
},
' ', '\t' => {
if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
if (!in_quote) {
return true;
}
backslash_count = 0;
@@ -360,6 +355,7 @@ pub const ArgIteratorWindows = struct {
defer buf.deinit();
var backslash_count: usize = 0;
var in_quote = false;
while (true) : (self.index += 1) {
const byte = self.cmd_line[self.index];
switch (byte) {
@@ -370,10 +366,7 @@ pub const ArgIteratorWindows = struct {
backslash_count = 0;
if (quote_is_real) {
self.seen_quote_count += 1;
if (self.seen_quote_count == self.quote_count and self.seen_quote_count % 2 == 1) {
try buf.append('"');
}
in_quote = !in_quote;
} else {
try buf.append('"');
}
@@ -384,7 +377,7 @@ pub const ArgIteratorWindows = struct {
' ', '\t' => {
try self.emitBackslashes(&buf, backslash_count);
backslash_count = 0;
if (self.seen_quote_count % 2 == 1 and self.seen_quote_count != self.quote_count) {
if (in_quote) {
try buf.append(byte);
} else {
return buf.toOwnedSlice();
@@ -405,26 +398,6 @@ pub const ArgIteratorWindows = struct {
try buf.append('\\');
}
}
fn countQuotes(cmd_line: [*]const u8) usize {
var result: usize = 0;
var backslash_count: usize = 0;
var index: usize = 0;
while (true) : (index += 1) {
const byte = cmd_line[index];
switch (byte) {
0 => return result,
'\\' => backslash_count += 1,
'"' => {
result += 1 - (backslash_count % 2);
backslash_count = 0;
},
else => {
backslash_count = 0;
},
}
}
}
};
pub const ArgIterator = struct {
@@ -578,7 +551,7 @@ test "windows arg parsing" {
testWindowsCmdLine("a\\\\\\b d\"e f\"g h", &[_][]const u8{ "a\\\\\\b", "de fg", "h" });
testWindowsCmdLine("a\\\\\\\"b c d", &[_][]const u8{ "a\\\"b", "c", "d" });
testWindowsCmdLine("a\\\\\\\\\"b c\" d e", &[_][]const u8{ "a\\\\b c", "d", "e" });
testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "\"d", "f" });
testWindowsCmdLine("a b\tc \"d f", &[_][]const u8{ "a", "b", "c", "d f" });
testWindowsCmdLine("\".\\..\\zig-cache\\build\" \"bin\\zig.exe\" \".\\..\" \".\\..\\zig-cache\" \"--help\"", &[_][]const u8{
".\\..\\zig-cache\\build",

View File

@@ -49,6 +49,7 @@ pub const heap = @import("heap.zig");
pub const http = @import("http.zig");
pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const log = @import("log.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math.zig");
pub const mem = @import("mem.zig");

View File

@@ -235,6 +235,22 @@ pub const Utf8Iterator = struct {
else => unreachable,
}
}
/// Look ahead at the next n codepoints without advancing the iterator.
/// If fewer than n codepoints are available, then return the remainder of the string.
pub fn peek(it: *Utf8Iterator, n: usize) []const u8 {
const original_i = it.i;
defer it.i = original_i;
var end_ix = original_i;
var found: usize = 0;
while (found < n) : (found += 1) {
const next_codepoint = it.nextCodepointSlice() orelse return it.bytes[original_i..];
end_ix += next_codepoint.len;
}
return it.bytes[original_i..end_ix];
}
};
pub const Utf16LeIterator = struct {
@@ -451,6 +467,31 @@ fn testMiscInvalidUtf8() void {
testValid("\xee\x80\x80", 0xe000);
}
test "utf8 iterator peeking" {
comptime testUtf8Peeking();
testUtf8Peeking();
}
fn testUtf8Peeking() void {
const s = Utf8View.initComptime("noël");
var it = s.iterator();
testing.expect(std.mem.eql(u8, "n", it.nextCodepointSlice().?));
testing.expect(std.mem.eql(u8, "o", it.peek(1)));
testing.expect(std.mem.eql(u8, "", it.peek(2)));
testing.expect(std.mem.eql(u8, "oël", it.peek(3)));
testing.expect(std.mem.eql(u8, "oël", it.peek(4)));
testing.expect(std.mem.eql(u8, "oël", it.peek(10)));
testing.expect(std.mem.eql(u8, "o", it.nextCodepointSlice().?));
testing.expect(std.mem.eql(u8, "ë", it.nextCodepointSlice().?));
testing.expect(std.mem.eql(u8, "l", it.nextCodepointSlice().?));
testing.expect(it.nextCodepointSlice() == null);
testing.expect(std.mem.eql(u8, &[_]u8{}, it.peek(1)));
}
fn testError(bytes: []const u8, expected_err: anyerror) void {
testing.expectError(expected_err, testDecode(bytes));
}

View File

@@ -937,7 +937,6 @@ const Parser = struct {
return node;
}
while_prefix.body = try p.expectNode(parseAssignExpr, .{
.ExpectedBlockOrAssignment = .{ .token = p.tok_i },
});

View File

@@ -546,8 +546,9 @@ const Fmt = struct {
any_error: bool,
color: Color,
gpa: *Allocator,
out_buffer: std.ArrayList(u8),
const SeenMap = std.BufSet;
const SeenMap = std.AutoHashMap(fs.File.INode, void);
};
pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
@@ -641,10 +642,20 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
.seen = Fmt.SeenMap.init(gpa),
.any_error = false,
.color = color,
.out_buffer = std.ArrayList(u8).init(gpa),
};
defer fmt.seen.deinit();
defer fmt.out_buffer.deinit();
for (input_files.span()) |file_path| {
try fmtPath(&fmt, file_path, check_flag);
// Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
const real_path = fs.realpathAlloc(gpa, file_path) catch |err| {
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
process.exit(1);
};
defer gpa.free(real_path);
try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path);
}
if (fmt.any_error) {
process.exit(1);
@@ -670,48 +681,82 @@ const FmtError = error{
ReadOnlyFileSystem,
LinkQuotaExceeded,
FileBusy,
EndOfStream,
} || fs.File.OpenError;
fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
// get the real path here to avoid Windows failing on relative file paths with . or .. in them
var real_path = fs.realpathAlloc(fmt.gpa, file_path) catch |err| {
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
};
defer fmt.gpa.free(real_path);
if (fmt.seen.exists(real_path)) return;
try fmt.seen.put(real_path);
const source_code = fs.cwd().readFileAlloc(fmt.gpa, real_path, max_src_size) catch |err| switch (err) {
error.IsDir, error.AccessDenied => {
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
defer dir.close();
var dir_it = dir.iterate();
while (try dir_it.next()) |entry| {
if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) {
const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
try fmtPath(fmt, full_path, check_mode);
}
}
return;
},
fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void {
fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) {
error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path),
else => {
std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
std.debug.warn("unable to format '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
},
};
}
fn fmtPathDir(
fmt: *Fmt,
file_path: []const u8,
check_mode: bool,
parent_dir: fs.Dir,
parent_sub_path: []const u8,
) FmtError!void {
var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true });
defer dir.close();
const stat = try dir.stat();
if (try fmt.seen.put(stat.inode, {})) |_| return;
var dir_it = dir.iterate();
while (try dir_it.next()) |entry| {
const is_dir = entry.kind == .Directory;
if (is_dir or mem.endsWith(u8, entry.name, ".zig")) {
const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name });
defer fmt.gpa.free(full_path);
if (is_dir) {
try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
} else {
fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| {
std.debug.warn("unable to format '{}': {}\n", .{ full_path, err });
fmt.any_error = true;
return;
};
}
}
}
}
fn fmtPathFile(
fmt: *Fmt,
file_path: []const u8,
check_mode: bool,
dir: fs.Dir,
sub_path: []const u8,
) FmtError!void {
const source_file = try dir.openFile(sub_path, .{});
var file_closed = false;
errdefer if (!file_closed) source_file.close();
const stat = try source_file.stat();
if (stat.kind == .Directory)
return error.IsDir;
const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
error.ConnectionResetByPeer => unreachable,
error.ConnectionTimedOut => unreachable,
else => |e| return e,
};
source_file.close();
file_closed = true;
defer fmt.gpa.free(source_code);
const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
std.debug.warn("error parsing file '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
};
// Add to set after no longer possible to get error.IsDir.
if (try fmt.seen.put(stat.inode, {})) |_| return;
const tree = try std.zig.parse(fmt.gpa, source_code);
defer tree.deinit();
for (tree.errors) |parse_error| {
@@ -729,14 +774,19 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
fmt.any_error = true;
}
} else {
const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{});
defer baf.destroy();
// As a heuristic, we make enough capacity for the same as the input source.
try fmt.out_buffer.ensureCapacity(source_code.len);
fmt.out_buffer.items.len = 0;
const anything_changed = try std.zig.render(fmt.gpa, fmt.out_buffer.writer(), tree);
if (!anything_changed)
return; // Good thing we didn't waste any file system access on this.
const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
if (anything_changed) {
std.debug.warn("{}\n", .{file_path});
try baf.finish();
}
var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode });
defer af.deinit();
try af.file.writeAll(fmt.out_buffer.items);
try af.finish();
std.debug.warn("{}\n", .{file_path});
}
}

View File

@@ -5668,161 +5668,23 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
const lparen = try appendToken(c, .LParen, "(");
if (saw_integer_literal) {
//( if (@typeInfo(dest) == .Pointer))
// @intToPtr(dest, x)
//else
// @as(dest, x) )
const if_node = try transCreateNodeIf(c);
const type_info_node = try c.createBuiltinCall("@typeInfo", 1);
type_info_node.params()[0] = inner_node;
type_info_node.rparen_token = try appendToken(c, .LParen, ")");
const cmp_node = try c.arena.create(ast.Node.InfixOp);
cmp_node.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_node.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
};
if_node.condition = &cmp_node.base;
_ = try appendToken(c, .RParen, ")");
//(@import("std").meta.cast(dest, x))
const import_fn_call = try c.createBuiltinCall("@import", 1);
const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
import_fn_call.params()[0] = std_node;
import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "meta");
const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "cast");
const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
int_to_ptr.params()[0] = inner_node;
int_to_ptr.params()[1] = node_to_cast;
int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
if_node.body = &int_to_ptr.base;
const else_node = try transCreateNodeElse(c);
if_node.@"else" = else_node;
const as_node = try c.createBuiltinCall("@as", 2);
as_node.params()[0] = inner_node;
as_node.params()[1] = node_to_cast;
as_node.rparen_token = try appendToken(c, .RParen, ")");
else_node.body = &as_node.base;
const group_node = try c.arena.create(ast.Node.GroupedExpression);
group_node.* = .{
.lparen = lparen,
.expr = &if_node.base,
.rparen = try appendToken(c, .RParen, ")"),
};
return &group_node.base;
}
//( if (@typeInfo(@TypeOf(x)) == .Pointer)
// @ptrCast(dest, @alignCast(@alignOf(dest.Child), x))
//else if (@typeInfo(@TypeOf(x)) == .Int and @typeInfo(dest) == .Pointer))
// @intToPtr(dest, x)
//else
// @as(dest, x) )
const if_1 = try transCreateNodeIf(c);
const type_info_1 = try c.createBuiltinCall("@typeInfo", 1);
const type_of_1 = try c.createBuiltinCall("@TypeOf", 1);
type_info_1.params()[0] = &type_of_1.base;
type_of_1.params()[0] = node_to_cast;
type_of_1.rparen_token = try appendToken(c, .RParen, ")");
type_info_1.rparen_token = try appendToken(c, .RParen, ")");
const cmp_1 = try c.arena.create(ast.Node.InfixOp);
cmp_1.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_1.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
};
if_1.condition = &cmp_1.base;
_ = try appendToken(c, .RParen, ")");
const period_tok = try appendToken(c, .Period, ".");
const child_ident = try transCreateNodeIdentifier(c, "Child");
const inner_node_child = try c.arena.create(ast.Node.InfixOp);
inner_node_child.* = .{
.op_token = period_tok,
.lhs = inner_node,
.op = .Period,
.rhs = child_ident,
};
const align_of = try c.createBuiltinCall("@alignOf", 1);
align_of.params()[0] = &inner_node_child.base;
align_of.rparen_token = try appendToken(c, .RParen, ")");
// hack to get zig fmt to render a comma in builtin calls
_ = try appendToken(c, .Comma, ",");
const align_cast = try c.createBuiltinCall("@alignCast", 2);
align_cast.params()[0] = &align_of.base;
align_cast.params()[1] = node_to_cast;
align_cast.rparen_token = try appendToken(c, .RParen, ")");
const ptr_cast = try c.createBuiltinCall("@ptrCast", 2);
ptr_cast.params()[0] = inner_node;
ptr_cast.params()[1] = &align_cast.base;
ptr_cast.rparen_token = try appendToken(c, .RParen, ")");
if_1.body = &ptr_cast.base;
const else_1 = try transCreateNodeElse(c);
if_1.@"else" = else_1;
const if_2 = try transCreateNodeIf(c);
const type_info_2 = try c.createBuiltinCall("@typeInfo", 1);
const type_of_2 = try c.createBuiltinCall("@TypeOf", 1);
type_info_2.params()[0] = &type_of_2.base;
type_of_2.params()[0] = node_to_cast;
type_of_2.rparen_token = try appendToken(c, .RParen, ")");
type_info_2.rparen_token = try appendToken(c, .RParen, ")");
const cmp_2 = try c.arena.create(ast.Node.InfixOp);
cmp_2.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_2.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Int"),
};
if_2.condition = &cmp_2.base;
const cmp_4 = try c.arena.create(ast.Node.InfixOp);
cmp_4.* = .{
.op_token = try appendToken(c, .Keyword_and, "and"),
.lhs = &cmp_2.base,
.op = .BoolAnd,
.rhs = undefined,
};
const type_info_3 = try c.createBuiltinCall("@typeInfo", 1);
type_info_3.params()[0] = inner_node;
type_info_3.rparen_token = try appendToken(c, .LParen, ")");
const cmp_3 = try c.arena.create(ast.Node.InfixOp);
cmp_3.* = .{
.op_token = try appendToken(c, .EqualEqual, "=="),
.lhs = &type_info_3.base,
.op = .EqualEqual,
.rhs = try transCreateNodeEnumLiteral(c, "Pointer"),
};
cmp_4.rhs = &cmp_3.base;
if_2.condition = &cmp_4.base;
else_1.body = &if_2.base;
_ = try appendToken(c, .RParen, ")");
const int_to_ptr = try c.createBuiltinCall("@intToPtr", 2);
int_to_ptr.params()[0] = inner_node;
int_to_ptr.params()[1] = node_to_cast;
int_to_ptr.rparen_token = try appendToken(c, .RParen, ")");
if_2.body = &int_to_ptr.base;
const else_2 = try transCreateNodeElse(c);
if_2.@"else" = else_2;
const as = try c.createBuiltinCall("@as", 2);
as.params()[0] = inner_node;
as.params()[1] = node_to_cast;
as.rparen_token = try appendToken(c, .RParen, ")");
else_2.body = &as.base;
const cast_fn_call = try c.createCall(outer_field_access, 2);
cast_fn_call.params()[0] = inner_node;
cast_fn_call.params()[1] = node_to_cast;
cast_fn_call.rtoken = try appendToken(c, .RParen, ")");
const group_node = try c.arena.create(ast.Node.GroupedExpression);
group_node.* = .{
.lparen = lparen,
.expr = &if_1.base,
.expr = &cast_fn_call.base,
.rparen = try appendToken(c, .RParen, ")"),
};
return &group_node.base;

View File

@@ -6012,6 +6012,19 @@ ZigValue *create_const_null(CodeGen *g, ZigType *type) {
return const_val;
}
void init_const_fn(ZigValue *const_val, ZigFn *fn) {
const_val->special = ConstValSpecialStatic;
const_val->type = fn->type_entry;
const_val->data.x_ptr.special = ConstPtrSpecialFunction;
const_val->data.x_ptr.data.fn.fn_entry = fn;
}
ZigValue *create_const_fn(CodeGen *g, ZigFn *fn) {
ZigValue *const_val = g->pass1_arena->create<ZigValue>();
init_const_fn(const_val, fn);
return const_val;
}
void init_const_float(ZigValue *const_val, ZigType *type, double value) {
const_val->special = ConstValSpecialStatic;
const_val->type = type;

View File

@@ -180,6 +180,9 @@ ZigValue *create_const_slice(CodeGen *g, ZigValue *array_val, size_t start, size
void init_const_null(ZigValue *const_val, ZigType *type);
ZigValue *create_const_null(CodeGen *g, ZigType *type);
void init_const_fn(ZigValue *const_val, ZigFn *fn);
ZigValue *create_const_fn(CodeGen *g, ZigFn *fn);
ZigValue **alloc_const_vals_ptrs(CodeGen *g, size_t count);
ZigValue **realloc_const_vals_ptrs(CodeGen *g, ZigValue **ptr, size_t old_count, size_t new_count);

View File

@@ -3540,7 +3540,7 @@ static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executabl
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i];
Buf *name = type_enum_field->name;
auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
if (entry != nullptr) {
@@ -3654,7 +3654,7 @@ static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *oper
} else if (scalar_type->data.integral.is_signed) {
return LLVMBuildNSWNeg(g->builder, llvm_operand, "");
} else {
return LLVMBuildNUWNeg(g->builder, llvm_operand, "");
zig_unreachable();
}
} else {
zig_unreachable();
@@ -3984,7 +3984,7 @@ static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable,
assert(array_type->data.pointer.child_type->id == ZigTypeIdArray);
array_type = array_type->data.pointer.child_type;
}
assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr);
if (safety_check_on) {
@@ -5258,7 +5258,7 @@ static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) {
for (size_t field_i = 0; field_i < field_count; field_i += 1) {
TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i];
Buf *name = type_enum_field->name;
auto entry = occupied_tag_values.put_unique(type_enum_field->value, name);
if (entry != nullptr) {
@@ -5471,7 +5471,7 @@ static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) {
}
auto bit_count = operand_type->data.integral.bit_count;
bool is_signed = operand_type->data.integral.is_signed;
ir_assert(bit_count != 0, instruction);
if (bit_count == 1 || !is_power_of_2(bit_count)) {
return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8));
@@ -9275,7 +9275,7 @@ static void init(CodeGen *g) {
abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64";
}
}
g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str),
target_specific_cpu_args, target_specific_features, opt_level, reloc_mode,
to_llvm_code_model(g), g->function_sections, float_abi, abi_name);

View File

@@ -13,6 +13,7 @@
#include "os.hpp"
#include "range_set.hpp"
#include "softfloat.hpp"
#include "softfloat_ext.hpp"
#include "util.hpp"
#include "mem_list.hpp"
#include "all_types.hpp"
@@ -825,12 +826,11 @@ static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_
ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val;
size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index;
// TODO handle sentinel terminated arrays
expand_undef_array(g, array_val);
result = g->pass1_arena->create<ZigValue>();
result->special = array_val->special;
result->type = get_array_type(g, array_val->type->data.array.child_type,
array_val->type->data.array.len - elem_index, nullptr);
array_val->type->data.array.len - elem_index, array_val->type->data.array.sentinel);
result->data.x_array.special = ConstArraySpecialNone;
result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index];
result->parent.id = ConstParentIdArray;
@@ -12601,28 +12601,28 @@ static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigT
if (prev_type->id == ZigTypeIdPointer &&
prev_type->data.pointer.ptr_len == PtrLenSingle &&
prev_type->data.pointer.child_type->id == ZigTypeIdArray &&
((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown)))
{
prev_inst = cur_inst;
prev_inst = cur_inst;
if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) {
// const array pointer and non-const unknown pointer
make_the_pointer_const = true;
}
continue;
continue;
}
// *[N]T to [*]T
if (cur_type->id == ZigTypeIdPointer &&
cur_type->data.pointer.ptr_len == PtrLenSingle &&
cur_type->data.pointer.child_type->id == ZigTypeIdArray &&
((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown)))
{
if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) {
// const array pointer and non-const unknown pointer
make_the_pointer_const = true;
}
continue;
continue;
}
// *[N]T to []T
@@ -20986,17 +20986,24 @@ static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction
if (type_is_invalid(expr_type))
return ira->codegen->invalid_inst_gen;
if (!(expr_type->id == ZigTypeIdInt || expr_type->id == ZigTypeIdComptimeInt ||
expr_type->id == ZigTypeIdFloat || expr_type->id == ZigTypeIdComptimeFloat ||
expr_type->id == ZigTypeIdVector))
{
ir_add_error(ira, &instruction->base.base,
buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
return ira->codegen->invalid_inst_gen;
}
bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap);
switch (expr_type->id) {
case ZigTypeIdComptimeInt:
case ZigTypeIdFloat:
case ZigTypeIdComptimeFloat:
case ZigTypeIdVector:
break;
case ZigTypeIdInt:
if (is_wrap_op || expr_type->data.integral.is_signed)
break;
ZIG_FALLTHROUGH;
default:
ir_add_error(ira, &instruction->base.base,
buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name)));
return ira->codegen->invalid_inst_gen;
}
ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type;
if (instr_is_comptime(value)) {
@@ -25609,9 +25616,18 @@ static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigTy
break;
}
case ZigTypeIdFnFrame:
ir_add_error(ira, source_instr,
buf_sprintf("compiler bug: TODO @typeInfo for async function frames. https://github.com/ziglang/zig/issues/3066"));
return ErrorSemanticAnalyzeFail;
{
result = ira->codegen->pass1_arena->create<ZigValue>();
result->special = ConstValSpecialStatic;
result->type = ir_type_info_get_type(ira, "Frame", nullptr);
ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1);
result->data.x_struct.fields = fields;
ZigFn *fn = type_entry->data.frame.fn;
// function: var
ensure_field_index(result->type, "function", 0);
fields[0] = create_const_fn(ira->codegen, fn);
break;
}
}
assert(result != nullptr);
@@ -25880,10 +25896,90 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0);
return get_any_frame_type(ira->codegen, child_type);
}
case ZigTypeIdErrorSet:
case ZigTypeIdEnum:
case ZigTypeIdFnFrame:
case ZigTypeIdEnumLiteral:
return ira->codegen->builtin_types.entry_enum_literal;
case ZigTypeIdFnFrame: {
assert(payload->special == ConstValSpecialStatic);
assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr));
ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0);
assert(function->type->id == ZigTypeIdFn);
ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;
return get_fn_frame_type(ira->codegen, fn);
}
case ZigTypeIdErrorSet: {
assert(payload->special == ConstValSpecialStatic);
assert(payload->type->id == ZigTypeIdOptional);
ZigValue *slice = payload->data.x_optional;
if (slice == nullptr)
return ira->codegen->builtin_types.entry_global_error_set;
assert(slice->special == ConstValSpecialStatic);
assert(is_slice(slice->type));
ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet);
Buf bare_name = BUF_INIT;
buf_init_from_buf(&err_set_type->name, get_anon_type_name(ira->codegen, ira->old_irb.exec, "error", source_instr->scope, source_instr->source_node, &bare_name));
err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits;
err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align;
err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size;
ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index];
assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);;
assert(ptr->data.x_ptr.data.base_array.elem_index == 0);
ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val;
assert(arr->special == ConstValSpecialStatic);
assert(arr->data.x_array.special == ConstArraySpecialNone);
ZigValue *len = slice->data.x_struct.fields[slice_len_index];
size_t count = bigint_as_usize(&len->data.x_bigint);
err_set_type->data.error_set.err_count = count;
err_set_type->data.error_set.errors = heap::c_allocator.allocate<ErrorTableEntry *>(count);
bool *already_set = heap::c_allocator.allocate<bool>(ira->codegen->errors_by_index.length + count);
for (size_t i = 0; i < count; i++) {
ZigValue *error = &arr->data.x_array.data.s_none.elements[i];
assert(error->type == ir_type_info_get_type(ira, "Error", nullptr));
ErrorTableEntry *err_entry = heap::c_allocator.create<ErrorTableEntry>();
err_entry->decl_node = source_instr->source_node;
ZigValue *name_slice = get_const_field(ira, source_instr->source_node, error, "name", 0);
ZigValue *name_ptr = name_slice->data.x_struct.fields[slice_ptr_index];
ZigValue *name_len = name_slice->data.x_struct.fields[slice_len_index];
assert(name_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);
assert(name_ptr->data.x_ptr.data.base_array.elem_index == 0);
ZigValue *name_arr = name_ptr->data.x_ptr.data.base_array.array_val;
assert(name_arr->special == ConstValSpecialStatic);
switch (name_arr->data.x_array.special) {
case ConstArraySpecialUndef:
return ira->codegen->invalid_inst_gen->value->type;
case ConstArraySpecialNone: {
buf_resize(&err_entry->name, 0);
size_t name_count = bigint_as_usize(&name_len->data.x_bigint);
for (size_t j = 0; j < name_count; j++) {
ZigValue *ch_val = &name_arr->data.x_array.data.s_none.elements[j];
unsigned ch = bigint_as_u32(&ch_val->data.x_bigint);
buf_append_char(&err_entry->name, ch);
}
break;
}
case ConstArraySpecialBuf:
buf_init_from_buf(&err_entry->name, name_arr->data.x_array.data.s_buf);
break;
}
auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry);
if (existing_entry) {
err_entry->value = existing_entry->value->value;
} else {
size_t error_value_count = ira->codegen->errors_by_index.length;
assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count));
err_entry->value = error_value_count;
ira->codegen->errors_by_index.append(err_entry);
}
if (already_set[err_entry->value]) {
ir_add_error(ira, source_instr, buf_sprintf("duplicate error: %s", buf_ptr(&err_entry->name)));
return ira->codegen->invalid_inst_gen->value->type;
} else {
already_set[err_entry->value] = true;
}
err_set_type->data.error_set.errors[i] = err_entry;
}
return err_set_type;
}
case ZigTypeIdEnum:
ir_add_error(ira, source_instr, buf_sprintf(
"TODO implement @Type for 'TypeInfo.%s': see https://github.com/ziglang/zig/issues/2907", type_id_name(tagTypeId)));
return ira->codegen->invalid_inst_gen->value->type;
@@ -30278,6 +30374,21 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
case BuiltinFnIdSqrt:
f128M_sqrt(in, out);
break;
case BuiltinFnIdFabs:
f128M_abs(in, out);
break;
case BuiltinFnIdFloor:
f128M_roundToInt(in, softfloat_round_min, false, out);
break;
case BuiltinFnIdCeil:
f128M_roundToInt(in, softfloat_round_max, false, out);
break;
case BuiltinFnIdTrunc:
f128M_trunc(in, out);
break;
case BuiltinFnIdRound:
f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
break;
case BuiltinFnIdNearbyInt:
case BuiltinFnIdSin:
case BuiltinFnIdCos:
@@ -30286,11 +30397,6 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
case BuiltinFnIdLog:
case BuiltinFnIdLog10:
case BuiltinFnIdLog2:
case BuiltinFnIdFabs:
case BuiltinFnIdFloor:
case BuiltinFnIdCeil:
case BuiltinFnIdTrunc:
case BuiltinFnIdRound:
return ir_add_error(ira, source_instr,
buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026",
float_op_to_name(fop), buf_ptr(&float_type->name)));

25
src/softfloat_ext.cpp Normal file
View File

@@ -0,0 +1,25 @@
#include "softfloat_ext.hpp"
extern "C" {
#include "softfloat.h"
}
void f128M_abs(const float128_t *aPtr, float128_t *zPtr) {
float128_t zero_float;
ui32_to_f128M(0, &zero_float);
if (f128M_lt(aPtr, &zero_float)) {
f128M_sub(&zero_float, aPtr, zPtr);
} else {
*zPtr = *aPtr;
}
}
void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) {
float128_t zero_float;
ui32_to_f128M(0, &zero_float);
if (f128M_lt(aPtr, &zero_float)) {
f128M_roundToInt(aPtr, softfloat_round_max, false, zPtr);
} else {
f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr);
}
}

9
src/softfloat_ext.hpp Normal file
View File

@@ -0,0 +1,9 @@
#ifndef ZIG_SOFTFLOAT_EXT_HPP
#define ZIG_SOFTFLOAT_EXT_HPP
#include "softfloat_types.h"
void f128M_abs(const float128_t *aPtr, float128_t *zPtr);
void f128M_trunc(const float128_t *aPtr, float128_t *zPtr);
#endif

View File

@@ -34,6 +34,7 @@ pub fn main() !void {
testZigInitExe,
testGodboltApi,
testMissingOutputPath,
testZigFmt,
};
for (test_fns) |testFn| {
try fs.cwd().deleteTree(dir_path);
@@ -143,3 +144,29 @@ fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
zig_exe, "build-exe", source_path, "--output-dir", output_path,
});
}
fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
const unformatted_code = " // no reason for indent";
const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" });
try fs.cwd().writeFile(fmt1_zig_path, unformatted_code);
const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path });
// stderr should be file path + \n
testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path));
testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n');
const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" });
try fs.cwd().writeFile(fmt2_zig_path, unformatted_code);
const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
// running it on the dir, only the new file should be changed
testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path));
testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n');
const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path });
// both files have been formatted, nothing should change now
testing.expect(run_result3.stderr.len == 0);
}

View File

@@ -7530,4 +7530,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
, &[_][]const u8{
"tmp.zig:2:9: error: @wasmMemoryGrow is a wasm32 feature only",
});
cases.add("Issue #5586: Make unary minus for unsigned types a compile error",
\\export fn f(x: u32) u32 {
\\ const y = -%x;
\\ return -y;
\\}
, &[_][]const u8{
"tmp.zig:3:12: error: negation of type 'u32'"
});
}

View File

@@ -634,6 +634,128 @@ fn testSqrt(comptime T: type, x: T) void {
expect(@sqrt(x * x) == x);
}
test "@fabs" {
testFabs(f128, 12.0);
comptime testFabs(f128, 12.0);
testFabs(f64, 12.0);
comptime testFabs(f64, 12.0);
testFabs(f32, 12.0);
comptime testFabs(f32, 12.0);
testFabs(f16, 12.0);
comptime testFabs(f16, 12.0);
const x = 14.0;
const y = -x;
const z = @fabs(y);
comptime expectEqual(x, z);
}
fn testFabs(comptime T: type, x: T) void {
const y = -x;
const z = @fabs(y);
expectEqual(x, z);
}
test "@floor" {
// FIXME: Generates a floorl function call
// testFloor(f128, 12.0);
comptime testFloor(f128, 12.0);
testFloor(f64, 12.0);
comptime testFloor(f64, 12.0);
testFloor(f32, 12.0);
comptime testFloor(f32, 12.0);
testFloor(f16, 12.0);
comptime testFloor(f16, 12.0);
const x = 14.0;
const y = x + 0.7;
const z = @floor(y);
comptime expectEqual(x, z);
}
fn testFloor(comptime T: type, x: T) void {
const y = x + 0.6;
const z = @floor(y);
expectEqual(x, z);
}
test "@ceil" {
// FIXME: Generates a ceill function call
//testCeil(f128, 12.0);
comptime testCeil(f128, 12.0);
testCeil(f64, 12.0);
comptime testCeil(f64, 12.0);
testCeil(f32, 12.0);
comptime testCeil(f32, 12.0);
testCeil(f16, 12.0);
comptime testCeil(f16, 12.0);
const x = 14.0;
const y = x - 0.7;
const z = @ceil(y);
comptime expectEqual(x, z);
}
fn testCeil(comptime T: type, x: T) void {
const y = x - 0.8;
const z = @ceil(y);
expectEqual(x, z);
}
test "@trunc" {
// FIXME: Generates a truncl function call
//testTrunc(f128, 12.0);
comptime testTrunc(f128, 12.0);
testTrunc(f64, 12.0);
comptime testTrunc(f64, 12.0);
testTrunc(f32, 12.0);
comptime testTrunc(f32, 12.0);
testTrunc(f16, 12.0);
comptime testTrunc(f16, 12.0);
const x = 14.0;
const y = x + 0.7;
const z = @trunc(y);
comptime expectEqual(x, z);
}
fn testTrunc(comptime T: type, x: T) void {
{
const y = x + 0.8;
const z = @trunc(y);
expectEqual(x, z);
}
{
const y = -x - 0.8;
const z = @trunc(y);
expectEqual(-x, z);
}
}
test "@round" {
// FIXME: Generates a roundl function call
//testRound(f128, 12.0);
comptime testRound(f128, 12.0);
testRound(f64, 12.0);
comptime testRound(f64, 12.0);
testRound(f32, 12.0);
comptime testRound(f32, 12.0);
testRound(f16, 12.0);
comptime testRound(f16, 12.0);
const x = 14.0;
const y = x + 0.4;
const z = @round(y);
comptime expectEqual(x, z);
}
fn testRound(comptime T: type, x: T) void {
const y = x - 0.5;
const z = @round(y);
expectEqual(x, z);
}
test "comptime_int param and return" {
const a = comptimeAdd(35361831660712422535336160538497375248, 101752735581729509668353361206450473702);
expect(a == 137114567242441932203689521744947848950);

View File

@@ -280,6 +280,11 @@ test "slice syntax resulting in pointer-to-array" {
expect(slice[0] == 5);
comptime expect(@TypeOf(src_slice[0..2]) == *align(4) [2]u8);
}
fn testConcatStrLiterals() void {
expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
expectEqualSlices("a"[0..:0] ++ "b"[0..:0], "ab");
}
};
S.doTheTest();

View File

@@ -213,3 +213,26 @@ test "Type.AnyFrame" {
anyframe->anyframe->u8,
});
}
test "Type.EnumLiteral" {
testTypes(&[_]type{
@TypeOf(.Dummy),
});
}
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "Type.Frame" {
testTypes(&[_]type{
@Frame(add),
});
}
test "Type.ErrorSet" {
// error sets don't compare equal so just check if they compile
_ = @Type(@typeInfo(error{}));
_ = @Type(@typeInfo(error{A}));
_ = @Type(@typeInfo(error{ A, B, C }));
}

View File

@@ -202,7 +202,7 @@ fn testUnion() void {
expect(typeinfo_info.Union.fields[4].enum_field != null);
expect(typeinfo_info.Union.fields[4].enum_field.?.value == 4);
expect(typeinfo_info.Union.fields[4].field_type == @TypeOf(@typeInfo(u8).Int));
expect(typeinfo_info.Union.decls.len == 20);
expect(typeinfo_info.Union.decls.len == 21);
const TestNoTagUnion = union {
Foo: void,
@@ -389,3 +389,16 @@ test "defaut value for a var-typed field" {
const S = struct { x: var };
expect(@typeInfo(S).Struct.fields[0].default_value == null);
}
fn add(a: i32, b: i32) i32 {
return a + b;
}
test "type info for async frames" {
switch (@typeInfo(@Frame(add))) {
.Frame => |frame| {
expect(frame.function == add);
},
else => unreachable,
}
}

View File

@@ -1473,7 +1473,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
cases.add("macro pointer cast",
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
, &[_][]const u8{
\\pub const NRF_GPIO = (if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Pointer) @ptrCast([*c]NRF_GPIO_Type, @alignCast(@alignOf([*c]NRF_GPIO_Type.Child), NRF_GPIO_BASE)) else if (@typeInfo(@TypeOf(NRF_GPIO_BASE)) == .Int and @typeInfo([*c]NRF_GPIO_Type) == .Pointer) @intToPtr([*c]NRF_GPIO_Type, NRF_GPIO_BASE) else @as([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
\\pub const NRF_GPIO = (@import("std").meta.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE));
});
cases.add("basic macro function",
@@ -2683,11 +2683,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define FOO(bar) baz((void *)(baz))
\\#define BAR (void*) a
, &[_][]const u8{
\\pub inline fn FOO(bar: var) @TypeOf(baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)))) {
\\ return baz((if (@typeInfo(@TypeOf(baz)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), baz)) else if (@typeInfo(@TypeOf(baz)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, baz) else @as(?*c_void, baz)));
\\pub inline fn FOO(bar: var) @TypeOf(baz((@import("std").meta.cast(?*c_void, baz)))) {
\\ return baz((@import("std").meta.cast(?*c_void, baz)));
\\}
,
\\pub const BAR = (if (@typeInfo(@TypeOf(a)) == .Pointer) @ptrCast(?*c_void, @alignCast(@alignOf(?*c_void.Child), a)) else if (@typeInfo(@TypeOf(a)) == .Int and @typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, a) else @as(?*c_void, a));
\\pub const BAR = (@import("std").meta.cast(?*c_void, a));
});
cases.add("macro conditional operator",
@@ -2905,8 +2905,8 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
\\
, &[_][]const u8{
\\pub inline fn DefaultScreen(dpy: var) @TypeOf((if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen) {
\\ return (if (@typeInfo(@TypeOf(dpy)) == .Pointer) @ptrCast(_XPrivDisplay, @alignCast(@alignOf(_XPrivDisplay.Child), dpy)) else if (@typeInfo(@TypeOf(dpy)) == .Int and @typeInfo(_XPrivDisplay) == .Pointer) @intToPtr(_XPrivDisplay, dpy) else @as(_XPrivDisplay, dpy)).*.default_screen;
\\pub inline fn DefaultScreen(dpy: var) @TypeOf((@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen) {
\\ return (@import("std").meta.cast(_XPrivDisplay, dpy)).*.default_screen;
\\}
});
@@ -2914,9 +2914,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\#define NULL ((void*)0)
\\#define FOO ((int)0x8000)
, &[_][]const u8{
\\pub const NULL = (if (@typeInfo(?*c_void) == .Pointer) @intToPtr(?*c_void, 0) else @as(?*c_void, 0));
\\pub const NULL = (@import("std").meta.cast(?*c_void, 0));
,
\\pub const FOO = (if (@typeInfo(c_int) == .Pointer) @intToPtr(c_int, 0x8000) else @as(c_int, 0x8000));
\\pub const FOO = (@import("std").meta.cast(c_int, 0x8000));
});
if (std.Target.current.abi == .msvc) {