From 1696e943acd67119104f303467c0e26eecb94544 Mon Sep 17 00:00:00 2001
From: Tadeo Kondrak
Date: Tue, 28 Apr 2020 11:14:47 -0600
Subject: [PATCH 01/41] Implement @typeInfo for @Frame()
Closes https://github.com/ziglang/zig/issues/3066
---
src/analyze.cpp | 13 +++++++++++++
src/analyze.hpp | 3 +++
src/ir.cpp | 15 ++++++++++++---
test/stage1/behavior/type_info.zig | 15 ++++++++++++++-
4 files changed, 42 insertions(+), 4 deletions(-)
diff --git a/src/analyze.cpp b/src/analyze.cpp
index d170273808..5eb0205dfb 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -6021,6 +6021,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();
+ 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;
diff --git a/src/analyze.hpp b/src/analyze.hpp
index ae010c87e1..a92d7ec1de 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -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);
diff --git a/src/ir.cpp b/src/ir.cpp
index f37f91088a..0111a33f34 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -25166,9 +25166,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();
+ 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);
diff --git a/test/stage1/behavior/type_info.zig b/test/stage1/behavior/type_info.zig
index c897276f83..41301f290d 100644
--- a/test/stage1/behavior/type_info.zig
+++ b/test/stage1/behavior/type_info.zig
@@ -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,
+ }
+}
From ca6db2d008cf3e0e3700e84400bd3d6e259e3c0f Mon Sep 17 00:00:00 2001
From: Tadeo Kondrak
Date: Tue, 28 Apr 2020 11:16:11 -0600
Subject: [PATCH 02/41] Implement @Type() for EnumLiteral and FnFrame
---
lib/std/builtin.zig | 8 +++++++-
src/ir.cpp | 12 ++++++++++--
test/stage1/behavior/type.zig | 16 ++++++++++++++++
3 files changed, 33 insertions(+), 3 deletions(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index af8033ae91..9d419f9af2 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -157,7 +157,7 @@ pub const TypeInfo = union(enum) {
Fn: Fn,
BoundFn: Fn,
Opaque: void,
- Frame: void,
+ Frame: Frame,
AnyFrame: AnyFrame,
Vector: Vector,
EnumLiteral: void,
@@ -315,6 +315,12 @@ pub const TypeInfo = union(enum) {
args: []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
/// therefore must be kept in sync with the compiler implementation.
pub const AnyFrame = struct {
diff --git a/src/ir.cpp b/src/ir.cpp
index 0111a33f34..1a7c0c3527 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -25446,10 +25446,18 @@ 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 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:
case ZigTypeIdEnum:
- case ZigTypeIdFnFrame:
- case ZigTypeIdEnumLiteral:
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;
diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig
index 2860229cb8..d8ed633887 100644
--- a/test/stage1/behavior/type.zig
+++ b/test/stage1/behavior/type.zig
@@ -213,3 +213,19 @@ 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),
+ });
+}
From 647901b4a82dbb89656c620d4d4a89869fdf1fa0 Mon Sep 17 00:00:00 2001
From: Tadeo Kondrak
Date: Thu, 30 Apr 2020 04:30:01 -0600
Subject: [PATCH 03/41] Constify TypeInfo
---
lib/std/builtin.zig | 18 +++++++++---------
lib/std/meta.zig | 12 ++++++------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 9d419f9af2..9b0293228b 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -235,8 +235,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
@@ -261,7 +261,7 @@ pub const TypeInfo = union(enum) {
/// 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.
@@ -275,8 +275,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,
};
@@ -293,8 +293,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
@@ -312,7 +312,7 @@ 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
@@ -358,7 +358,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.
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index e38712af2b..4fc74282a5 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -219,7 +219,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,
@@ -243,7 +243,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),
@@ -292,10 +292,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)) {
From a62e9bc8e50296e2d5b201614a78b0e658887aa9 Mon Sep 17 00:00:00 2001
From: Tadeo Kondrak
Date: Thu, 30 Apr 2020 05:50:17 -0600
Subject: [PATCH 04/41] Implement @Type for ErrorSet
---
lib/std/builtin.zig | 1 +
src/ir.cpp | 74 ++++++++++++++++++++++++++++++++++-
test/stage1/behavior/type.zig | 7 ++++
3 files changed, 81 insertions(+), 1 deletion(-)
diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig
index 9b0293228b..47fba9d095 100644
--- a/lib/std/builtin.zig
+++ b/lib/std/builtin.zig
@@ -256,6 +256,7 @@ 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,
};
diff --git a/src/ir.cpp b/src/ir.cpp
index 1a7c0c3527..5ba92be3c0 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -25456,7 +25456,79 @@ static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeI
ZigFn *fn = function->data.x_ptr.data.fn.fn_entry;
return get_fn_frame_type(ira->codegen, fn);
}
- case ZigTypeIdErrorSet:
+ 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(count);
+ bool *already_set = heap::c_allocator.allocate(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();
+ 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)));
diff --git a/test/stage1/behavior/type.zig b/test/stage1/behavior/type.zig
index d8ed633887..f21e9f1ce9 100644
--- a/test/stage1/behavior/type.zig
+++ b/test/stage1/behavior/type.zig
@@ -229,3 +229,10 @@ test "Type.Frame" {
@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 }));
+}
From 0bd067d19a434a5c7fd04d816d131cd593bdc1bf Mon Sep 17 00:00:00 2001
From: Isaac Freund
Date: Fri, 15 May 2020 17:10:56 +0200
Subject: [PATCH 05/41] Introduce std.log
std.log provides 8 log levels and corresponding logging functions. It
allows the user to override the logging "backend" by defining root.log
and to override the default log level by defining root.log_level.
Logging functions accept a scope parameter which allows the implementer
of the logging "backend" to filter logging by library as well as level.
Using the standardized syslog [1] log levels ensures that std.log will
be flexible enough to work for as many use-cases as possible. If we were
to stick with only 3/4 log levels, std.log would be insufficient for
large and/or complex projects such as a kernel or display server.
[1]: https://tools.ietf.org/html/rfc5424#section-6.2.1
---
lib/std/log.zig | 202 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/std/std.zig | 1 +
2 files changed, 203 insertions(+)
create mode 100644 lib/std/log.zig
diff --git a/lib/std/log.zig b/lib/std/log.zig
new file mode 100644
index 0000000000..706c2b2378
--- /dev/null
+++ b/lib/std/log.zig
@@ -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 = 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);
+}
diff --git a/lib/std/std.zig b/lib/std/std.zig
index b1cab77109..0961991ea6 100644
--- a/lib/std/std.zig
+++ b/lib/std/std.zig
@@ -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");
From 8e5393a779ee115846821e28bdb47affdf158992 Mon Sep 17 00:00:00 2001
From: Isaac Freund
Date: Tue, 2 Jun 2020 18:43:27 +0200
Subject: [PATCH 06/41] Deprecate std.debug.warn
---
lib/std/debug.zig | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index 591f2d1a80..f3c2cf3b31 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -52,8 +52,7 @@ 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.
+/// Deprecated. Use `std.log` functions for logging.
pub fn warn(comptime fmt: []const u8, args: var) void {
const held = stderr_mutex.acquire();
defer held.release();
From 1157ee130732449811294d70021aaafa588d3048 Mon Sep 17 00:00:00 2001
From: antlilja
Date: Wed, 17 Jun 2020 17:35:45 +0200
Subject: [PATCH 07/41] Improve builtin op support for f128/comptime_float *
Add support for fabs, floor, ceil, trunc and round * Add behavior tests
---
CMakeLists.txt | 1 +
src/ir.cpp | 21 ++++--
src/softfloat_ext.cpp | 25 +++++++
src/softfloat_ext.hpp | 9 +++
test/stage1/behavior/math.zig | 122 ++++++++++++++++++++++++++++++++++
5 files changed, 173 insertions(+), 5 deletions(-)
create mode 100644 src/softfloat_ext.cpp
create mode 100644 src/softfloat_ext.hpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8599d01a5d..94219c1631 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/src/ir.cpp b/src/ir.cpp
index d28648e128..8267842671 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -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"
@@ -30303,6 +30304,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:
@@ -30311,11 +30327,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)));
diff --git a/src/softfloat_ext.cpp b/src/softfloat_ext.cpp
new file mode 100644
index 0000000000..8408a15116
--- /dev/null
+++ b/src/softfloat_ext.cpp
@@ -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);
+ }
+}
\ No newline at end of file
diff --git a/src/softfloat_ext.hpp b/src/softfloat_ext.hpp
new file mode 100644
index 0000000000..0a1f958933
--- /dev/null
+++ b/src/softfloat_ext.hpp
@@ -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
\ No newline at end of file
diff --git a/test/stage1/behavior/math.zig b/test/stage1/behavior/math.zig
index 1d361494eb..b13b1ce1e0 100644
--- a/test/stage1/behavior/math.zig
+++ b/test/stage1/behavior/math.zig
@@ -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);
From eb7fad28f8f11b985c8ee6fbeb4a68345a9b3a5e Mon Sep 17 00:00:00 2001
From: antlilja
Date: Wed, 17 Jun 2020 18:18:45 +0200
Subject: [PATCH 08/41] Improve f128 standard library support * Add functions:
floor128, ceil128, trunc128 and round128 * Add corresponding tests
---
lib/std/math.zig | 5 +++++
lib/std/math/ceil.zig | 43 ++++++++++++++++++++++++++++++++++++
lib/std/math/floor.zig | 43 ++++++++++++++++++++++++++++++++++++
lib/std/math/round.zig | 50 ++++++++++++++++++++++++++++++++++++++++++
lib/std/math/trunc.zig | 37 +++++++++++++++++++++++++++++++
5 files changed, 178 insertions(+)
diff --git a/lib/std/math.zig b/lib/std/math.zig
index 5cf6d40d8a..799c42846b 100644
--- a/lib/std/math.zig
+++ b/lib/std/math.zig
@@ -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));
},
diff --git a/lib/std/math/ceil.zig b/lib/std/math/ceil.zig
index b94e13a176..e3b5679318 100644
--- a/lib/std/math/ceil.zig
+++ b/lib/std/math/ceil.zig
@@ -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))));
+}
diff --git a/lib/std/math/floor.zig b/lib/std/math/floor.zig
index 1eda362e69..565e2911a9 100644
--- a/lib/std/math/floor.zig
+++ b/lib/std/math/floor.zig
@@ -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))));
+}
diff --git a/lib/std/math/round.zig b/lib/std/math/round.zig
index dceb3ed770..052c0f7670 100644
--- a/lib/std/math/round.zig
+++ b/lib/std/math/round.zig
@@ -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))));
+}
diff --git a/lib/std/math/trunc.zig b/lib/std/math/trunc.zig
index b70f0c6be3..cdd2fa3c6b 100644
--- a/lib/std/math/trunc.zig
+++ b/lib/std/math/trunc.zig
@@ -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))));
+}
From c3e0224792510e69763dbc6ba68794f9134925f2 Mon Sep 17 00:00:00 2001
From: Isaac Freund
Date: Tue, 2 Jun 2020 19:22:22 +0200
Subject: [PATCH 09/41] Add std.debug.print for "printf debugging"
---
lib/std/debug.zig | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/lib/std/debug.zig b/lib/std/debug.zig
index f3c2cf3b31..92b79be35c 100644
--- a/lib/std/debug.zig
+++ b/lib/std/debug.zig
@@ -52,8 +52,13 @@ pub const LineInfo = struct {
var stderr_mutex = std.Mutex.init();
-/// Deprecated. Use `std.log` functions for logging.
-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();
From caaa26c9f0db92c463a826f15c8392162be2e037 Mon Sep 17 00:00:00 2001
From: Vexu
Date: Wed, 17 Jun 2020 20:24:37 +0300
Subject: [PATCH 10/41] reference emit_raw in std lib tests
---
lib/std/build.zig | 7 +++++++
lib/std/build/emit_raw.zig | 4 ++++
2 files changed, 11 insertions(+)
diff --git a/lib/std/build.zig b/lib/std/build.zig
index d98ef71a59..a4d922f2ad 100644
--- a/lib/std/build.zig
+++ b/lib/std/build.zig
@@ -2558,3 +2558,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());
+}
diff --git a/lib/std/build/emit_raw.zig b/lib/std/build/emit_raw.zig
index 8fd27d6cfc..746b0ac91b 100644
--- a/lib/std/build/emit_raw.zig
+++ b/lib/std/build/emit_raw.zig
@@ -215,3 +215,7 @@ pub const InstallRawStep = struct {
try emitRaw(builder.allocator, full_src_path, full_dest_path);
}
};
+
+test "" {
+ std.meta.refAllDecls(InstallRawStep);
+}
From bd17a373cc20c919be9fa279a4420033e959ca92 Mon Sep 17 00:00:00 2001
From: Michael Rees
Date: Wed, 17 Jun 2020 06:04:08 -0500
Subject: [PATCH 11/41] Add std.unicode.Utf8Iterator.peek
---
lib/std/unicode.zig | 41 +++++++++++++++++++++++++++++++++++++++++
1 file changed, 41 insertions(+)
diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig
index df2e16a4bf..2c88d2ba0c 100644
--- a/lib/std/unicode.zig
+++ b/lib/std/unicode.zig
@@ -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, "oë", 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));
}
From b30642a86ae59f9cfe73c8a9ed6e79c3cb5aaa36 Mon Sep 17 00:00:00 2001
From: Cassidy Dingenskirchen
Date: Tue, 16 Jun 2020 19:07:55 +0200
Subject: [PATCH 12/41] Fix zig fmt clobbering a file's mode
---
src-self-hosted/main.zig | 11 +++++++++--
1 file changed, 9 insertions(+), 2 deletions(-)
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index eda9dcfc31..4db98c60dc 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -684,7 +684,7 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
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) {
+ const source_file = fs.cwd().openFile(real_path, .{}) catch |err| switch (err) {
error.IsDir, error.AccessDenied => {
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
defer dir.close();
@@ -705,6 +705,13 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
return;
},
};
+ defer source_file.close();
+
+ const source_code = source_file.reader().readAllAlloc(fmt.gpa, max_src_size) catch |err| {
+ std.debug.warn("unable to read '{}': {}\n", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ };
defer fmt.gpa.free(source_code);
const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
@@ -729,7 +736,7 @@ 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, .{});
+ const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{ .mode = try source_file.mode() });
defer baf.destroy();
const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
From 8b49487c3345d6e203d8c72695e639b42a93c119 Mon Sep 17 00:00:00 2001
From: Cassidy Dingenskirchen
Date: Thu, 18 Jun 2020 18:27:42 +0200
Subject: [PATCH 13/41] Fix fs.File.mode() not returning mode_t on windows
---
lib/std/fs/file.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index d950a1cfa4..ec4059b9c7 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -209,7 +209,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;
}
From f7bcc8e04087e2308582d8b6f068e2e71b65bef9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 18 Jun 2020 21:08:30 -0400
Subject: [PATCH 14/41] rework zig fmt to only make one allocation
taking advantage of the fstat size
---
lib/std/fs.zig | 9 ++-------
lib/std/fs/file.zig | 27 +++++++++++++++++++++++++++
src-self-hosted/main.zig | 10 ++++++++--
3 files changed, 37 insertions(+), 9 deletions(-)
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 262aa4872d..3ff4819ac5 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -1229,14 +1229,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{
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index ec4059b9c7..0a3c1b5ab7 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -306,6 +306,33 @@ pub const File = struct {
try os.futimens(self.handle, ×);
}
+ /// 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.inStream().readNoEof(buf);
+ return buf;
+ }
+
pub const ReadError = os.ReadError;
pub const PReadError = os.PReadError;
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 4db98c60dc..6c13f8ab00 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -707,7 +707,13 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
};
defer source_file.close();
- const source_code = source_file.reader().readAllAlloc(fmt.gpa, max_src_size) catch |err| {
+ const stat = source_file.stat() catch |err| {
+ std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ };
+
+ const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| {
std.debug.warn("unable to read '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
@@ -736,7 +742,7 @@ 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, .{ .mode = try source_file.mode() });
+ const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{ .mode = stat.mode });
defer baf.destroy();
const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
From 237c5429b0e8a089c89d13031ae6004ebac55ba9 Mon Sep 17 00:00:00 2001
From: Haze Booth
Date: Thu, 18 Jun 2020 23:02:21 -0400
Subject: [PATCH 15/41] Don't attempt to use io from thin air
---
lib/std/log.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lib/std/log.zig b/lib/std/log.zig
index 706c2b2378..63aeaecf88 100644
--- a/lib/std/log.zig
+++ b/lib/std/log.zig
@@ -109,7 +109,7 @@ fn log(
} else if (builtin.mode != .ReleaseSmall) {
const held = std.debug.getStderrMutex().acquire();
defer held.release();
- const stderr = io.getStdErr().writer();
+ const stderr = std.io.getStdErr().writer();
nosuspend stderr.print(format, args) catch return;
}
}
From 605769ec25923a50fc01d29d5837fbc48abffcf1 Mon Sep 17 00:00:00 2001
From: Eleanor NB
Date: Fri, 19 Jun 2020 19:38:22 +1000
Subject: [PATCH 16/41] Replaced all occurrences of std.debug.warn in the docs
with std.debug.print
---
doc/langref.html.in | 157 ++++++++++++++++++++++----------------------
1 file changed, 78 insertions(+), 79 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 4a56293208..e810b36be2 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -236,19 +236,18 @@ pub fn main() !void {
}
{#code_end#}
- 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 warning message 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:
{#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#}
- 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.
{#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.
{#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.
{#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#}?
{#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#}:
{#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#}
@@ -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.
{#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#}
@@ -7145,7 +7144,7 @@ test "main" {
program compiles successfully and the generated executable prints:
{#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#}
@@ -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#}
@@ -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#}
@@ -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 {
Example of catching an overflow for addition:
{#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#}:
{#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#}
One way to avoid this crash is to test for null instead of assuming non-null, with
the {#syntax#}if{#endsyntax#} expression:
{#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 {
One way to avoid this crash is to test for an error instead of assuming a successful result, with
the {#syntax#}if{#endsyntax#} expression:
{#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#}
@@ -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#}
@@ -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#}
From 85277183504148dd5c14bd153661377e76118d3b Mon Sep 17 00:00:00 2001
From: Sebastian <15335529+Sobeston@users.noreply.github.com>
Date: Sat, 20 Jun 2020 12:16:57 +0100
Subject: [PATCH 17/41] langref - document that This works on enums too
---
doc/langref.html.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index e810b36be2..aaa15e91d0 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -8204,7 +8204,7 @@ test "vector @splat" {
{#header_open|@This#}
{#syntax#}@This() type{#endsyntax#}
- 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:
{#code_begin|test#}
From 5229f6ec68708c3e66393a50ad9f9032ee7f4257 Mon Sep 17 00:00:00 2001
From: data-man
Date: Sat, 20 Jun 2020 15:02:48 +0500
Subject: [PATCH 18/41] Use writer in std.fmt
---
lib/std/fmt.zig | 350 ++++++++++++++++++++++++------------------------
1 file changed, 175 insertions(+), 175 deletions(-)
diff --git a/lib/std/fmt.zig b/lib/std/fmt.zig
index 23066a6963..cf0b21dc3a 100644
--- a/lib/std/fmt.zig
+++ b/lib/std/fmt.zig
@@ -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) }"));
}
From bc0ca73887d12ff71e12ad473bab22631a29b1aa Mon Sep 17 00:00:00 2001
From: DrDeano
Date: Sat, 20 Jun 2020 15:12:52 +0100
Subject: [PATCH 19/41] Moved the check for formatting a directory
The original check for a directory was for the `readAllAlloc` so move the check from open to read. This in turn fixes the fmt step in the build script for directories.
---
src-self-hosted/main.zig | 32 ++++++++++++++++----------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 6c13f8ab00..3c81070439 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -684,8 +684,21 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
if (fmt.seen.exists(real_path)) return;
try fmt.seen.put(real_path);
- const source_file = fs.cwd().openFile(real_path, .{}) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => {
+ const source_file = fs.cwd().openFile(real_path, .{}) catch |err| {
+ std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ };
+ defer source_file.close();
+
+ const stat = source_file.stat() catch |err| {
+ std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ };
+
+ const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
+ error.IsDir => {
var dir = try fs.cwd().openDir(file_path, .{ .iterate = true });
defer dir.close();
@@ -700,24 +713,11 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
return;
},
else => {
- std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
+ std.debug.warn("unable to read '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
return;
},
};
- defer source_file.close();
-
- const stat = source_file.stat() catch |err| {
- std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
-
- const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| {
- std.debug.warn("unable to read '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
defer fmt.gpa.free(source_code);
const tree = std.zig.parse(fmt.gpa, source_code) catch |err| {
From d87cd06296a759ce398b50a437b8a1444413c6be Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 20 Jun 2020 18:27:37 -0400
Subject: [PATCH 20/41] rework zig fmt to use less syscalls and open fds
* `std.fs.Dir.Entry.Kind` is moved to `std.fs.File.Kind`
* `std.fs.File.Stat` gains the `kind` field, so performing a stat() on
a File now tells what kind of file it is. On Windows this only will
distinguish between directories and files.
* rework zig fmt logic so that in the case of opening a file and
discovering it to be a directory, it closes the file descriptor
before re-opening it with O_DIRECTORY, using fewer simultaneous open
file descriptors when walking a directory tree.
* rework zig fmt logic so that it pays attention to the kind of
directory entries, and when it sees a sub-directory it attempts to
open it as a directory rather than a file, reducing the number of
open() syscalls when walking a directory tree.
---
lib/std/fs.zig | 12 +-----
lib/std/fs/file.zig | 26 +++++++++++-
src-self-hosted/main.zig | 89 ++++++++++++++++++++++++----------------
3 files changed, 80 insertions(+), 47 deletions(-)
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index 3ff4819ac5..fa782b14c0 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -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;
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index 0a3c1b5ab7..f0304c332a 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -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,
@@ -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,16 @@ pub const File = struct {
.inode = st.ino,
.size = @bitCast(u64, st.size),
.mode = st.mode,
+ .kind = switch (st.mode & os.S_IFMT) {
+ os.S_IFBLK => .BlockDevice,
+ os.S_IFCHR => .CharacterDevice,
+ os.S_IFDIR => .Directory,
+ os.S_IFIFO => .NamedPipe,
+ os.S_IFLNK => .SymLink,
+ os.S_IFREG => .File,
+ os.S_IFSOCK => .UnixDomainSocket,
+ else => .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,
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 3c81070439..ee4cf9bbe3 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -670,11 +670,12 @@ 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| {
+ const 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;
@@ -684,47 +685,65 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
if (fmt.seen.exists(real_path)) return;
try fmt.seen.put(real_path);
- const source_file = fs.cwd().openFile(real_path, .{}) catch |err| {
- std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
- defer source_file.close();
-
- const stat = source_file.stat() catch |err| {
- std.debug.warn("unable to stat '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
-
- const source_code = source_file.readAllAlloc(fmt.gpa, stat.size, max_src_size) catch |err| switch (err) {
- error.IsDir => {
- 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;
- },
+ fmtPathFile(fmt, file_path, check_mode, real_path) catch |err| switch (err) {
+ error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, real_path),
else => {
- std.debug.warn("unable to read '{}': {}\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_real_path: []const u8) FmtError!void {
+ var dir = try fs.cwd().openDir(parent_real_path, .{ .iterate = true });
+ defer dir.close();
+
+ 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 });
+ const sub_real_path = fs.realpathAlloc(fmt.gpa, full_path) catch |err| {
+ std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
+ fmt.any_error = true;
+ return;
+ };
+ defer fmt.gpa.free(sub_real_path);
+
+ if (fmt.seen.exists(sub_real_path)) return;
+ try fmt.seen.put(sub_real_path);
+
+ if (is_dir) {
+ try fmtPathDir(fmt, full_path, check_mode, sub_real_path);
+ } else {
+ fmtPathFile(fmt, full_path, check_mode, sub_real_path) 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, real_path: []const u8) FmtError!void {
+ const source_file = try fs.cwd().openFile(real_path, .{});
+ defer 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,
+ };
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;
- };
+ const tree = try std.zig.parse(fmt.gpa, source_code);
defer tree.deinit();
for (tree.errors) |parse_error| {
From da549a72e19d4f24520ca151bbd4cd0e74dc752c Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 20 Jun 2020 18:39:15 -0400
Subject: [PATCH 21/41] zig fmt
---
lib/std/array_list.zig | 2 +-
lib/std/c/tokenizer.zig | 100 ++++++++++++++---------------
lib/std/io/buffered_out_stream.zig | 2 +-
lib/std/json.zig | 5 +-
lib/std/os/windows/ws2_32.zig | 18 +++---
lib/std/zig/parse.zig | 1 -
6 files changed, 63 insertions(+), 65 deletions(-)
diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig
index d42a3c3d73..452ebe2124 100644
--- a/lib/std/array_list.zig
+++ b/lib/std/array_list.zig
@@ -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.
diff --git a/lib/std/c/tokenizer.zig b/lib/std/c/tokenizer.zig
index 198c69e2a7..f3fc8b8107 100644
--- a/lib/std/c/tokenizer.zig
+++ b/lib/std/c/tokenizer.zig
@@ -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
diff --git a/lib/std/io/buffered_out_stream.zig b/lib/std/io/buffered_out_stream.zig
index 6b8ede5489..6f9efa9575 100644
--- a/lib/std/io/buffered_out_stream.zig
+++ b/lib/std/io/buffered_out_stream.zig
@@ -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;
diff --git a/lib/std/json.zig b/lib/std/json.zig
index 4acdbc7d1a..ae10dc9559 100644
--- a/lib/std/json.zig
+++ b/lib/std/json.zig
@@ -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{});
}
-
diff --git a/lib/std/os/windows/ws2_32.zig b/lib/std/os/windows/ws2_32.zig
index 8b16f54361..1e36a72038 100644
--- a/lib/std/os/windows/ws2_32.zig
+++ b/lib/std/os/windows/ws2_32.zig
@@ -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;
diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig
index 2a3ff9d9de..e6cd7a8b6d 100644
--- a/lib/std/zig/parse.zig
+++ b/lib/std/zig/parse.zig
@@ -937,7 +937,6 @@ const Parser = struct {
return node;
}
-
while_prefix.body = try p.expectNode(parseAssignExpr, .{
.ExpectedBlockOrAssignment = .{ .token = p.tok_i },
});
From 0a9672fb86b84658f8780f57e769be45e41f3034 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 20 Jun 2020 19:46:14 -0400
Subject: [PATCH 22/41] rework zig fmt to avoid unnecessary realpath() calls
* add `std.fs.Dir.stat`
* zig fmt checks for sym link loops using inodes instead of using
realpath
---
lib/std/fs.zig | 11 +++++++
src-self-hosted/main.zig | 70 ++++++++++++++++++++++------------------
2 files changed, 49 insertions(+), 32 deletions(-)
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index fa782b14c0..dfd58de500 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -1545,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.
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index ee4cf9bbe3..f322e455a8 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -547,7 +547,7 @@ const Fmt = struct {
color: Color,
gpa: *Allocator,
- const SeenMap = std.BufSet;
+ const SeenMap = std.AutoHashMap(fs.File.INode, void);
};
pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
@@ -644,7 +644,14 @@ pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void {
};
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);
@@ -673,20 +680,9 @@ const FmtError = error{
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
- const 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);
-
- fmtPathFile(fmt, file_path, check_mode, real_path) catch |err| switch (err) {
- error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, real_path),
+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 format '{}': {}\n", .{ file_path, err });
fmt.any_error = true;
@@ -695,29 +691,30 @@ fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void {
};
}
-fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_path: []const u8) FmtError!void {
- var dir = try fs.cwd().openDir(parent_real_path, .{ .iterate = true });
+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 });
- const sub_real_path = fs.realpathAlloc(fmt.gpa, full_path) catch |err| {
- std.debug.warn("unable to open '{}': {}\n", .{ file_path, err });
- fmt.any_error = true;
- return;
- };
- defer fmt.gpa.free(sub_real_path);
-
- if (fmt.seen.exists(sub_real_path)) return;
- try fmt.seen.put(sub_real_path);
+ defer fmt.gpa.free(full_path);
if (is_dir) {
- try fmtPathDir(fmt, full_path, check_mode, sub_real_path);
+ try fmtPathDir(fmt, full_path, check_mode, dir, entry.name);
} else {
- fmtPathFile(fmt, full_path, check_mode, sub_real_path) catch |err| {
+ 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;
@@ -727,8 +724,14 @@ fn fmtPathDir(fmt: *Fmt, file_path: []const u8, check_mode: bool, parent_real_pa
}
}
-fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []const u8) FmtError!void {
- const source_file = try fs.cwd().openFile(real_path, .{});
+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, .{});
defer source_file.close();
const stat = try source_file.stat();
@@ -743,6 +746,9 @@ fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []
};
defer fmt.gpa.free(source_code);
+ // 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();
@@ -761,7 +767,7 @@ fn fmtPathFile(fmt: *Fmt, file_path: []const u8, check_mode: bool, real_path: []
fmt.any_error = true;
}
} else {
- const baf = try io.BufferedAtomicFile.create(fmt.gpa, fs.cwd(), real_path, .{ .mode = stat.mode });
+ const baf = try io.BufferedAtomicFile.create(fmt.gpa, dir, sub_path, .{ .mode = stat.mode });
defer baf.destroy();
const anything_changed = try std.zig.render(fmt.gpa, baf.stream(), tree);
From 64dfd1883eb2b9ab175af9b07c84bd4b53b7e904 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 20 Jun 2020 20:14:33 -0400
Subject: [PATCH 23/41] zig fmt: avoid unnecessary file system access
zig fmt previously would write a temp file, and then either rename it
into place if necessary, or unlink it if nothing was changed. Now zig
fmt renders into a memory buffer, and only writes the temp file and
renames it into place if anything changed.
Based on the performance testing I did this actually did not have much
of an impact, however it's likely that on other operating systems and
other hard drives this could make a big difference.
---
src-self-hosted/main.zig | 23 ++++++++++++++++-------
1 file changed, 16 insertions(+), 7 deletions(-)
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index f322e455a8..29930270eb 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -546,6 +546,7 @@ const Fmt = struct {
any_error: bool,
color: Color,
gpa: *Allocator,
+ out_buffer: std.ArrayList(u8),
const SeenMap = std.AutoHashMap(fs.File.INode, void);
};
@@ -641,7 +642,10 @@ 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| {
// Get the real path here to avoid Windows failing on relative file paths with . or .. in them.
@@ -767,14 +771,19 @@ fn fmtPathFile(
fmt.any_error = true;
}
} else {
- const baf = try io.BufferedAtomicFile.create(fmt.gpa, dir, sub_path, .{ .mode = stat.mode });
- 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});
}
}
From 225f1968426d333834465dae526b3d2ae242cd8f Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 20 Jun 2020 20:43:56 -0400
Subject: [PATCH 24/41] std.fs: fix shadowing `stat` with a local variable
---
lib/std/fs.zig | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/std/fs.zig b/lib/std/fs.zig
index dfd58de500..10422b9d54 100644
--- a/lib/std/fs.zig
+++ b/lib/std/fs.zig
@@ -1517,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 });
From faf783e5959a09fb2a1680f8bb7558db96dcbed9 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 20 Jun 2020 22:09:47 -0400
Subject: [PATCH 25/41] implement new stat functionality for WASI
---
lib/std/fs/file.zig | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index f0304c332a..160f8314b9 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -282,15 +282,26 @@ pub const File = struct {
.inode = st.ino,
.size = @bitCast(u64, st.size),
.mode = st.mode,
- .kind = switch (st.mode & os.S_IFMT) {
- os.S_IFBLK => .BlockDevice,
- os.S_IFCHR => .CharacterDevice,
- os.S_IFDIR => .Directory,
- os.S_IFIFO => .NamedPipe,
- os.S_IFLNK => .SymLink,
- os.S_IFREG => .File,
- os.S_IFSOCK => .UnixDomainSocket,
- else => .Unknown,
+ .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,
From 56220449abcbbb0e308e0eadeb2e0e7c8a9e4cba Mon Sep 17 00:00:00 2001
From: Nameless
Date: Fri, 19 Jun 2020 18:25:22 -0500
Subject: [PATCH 26/41] Add errors to windows.WSAStartup and WSACleanup
---
lib/std/os/windows.zig | 11 ++++++++++-
1 file changed, 10 insertions(+), 1 deletion(-)
diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig
index 953a16a2ea..1ed1ef1f54 100644
--- a/lib/std/os/windows.zig
+++ b/lib/std/os/windows.zig
@@ -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,
From ca9d8a1337554a5e117a59d96345d763ef1ad18e Mon Sep 17 00:00:00 2001
From: Ryan Liptak
Date: Sat, 20 Jun 2020 20:49:45 -0700
Subject: [PATCH 27/41] Add zig fmt test to cli tests for both files and
directories
Should catch basic `zig fmt` regressions that were previously going uncaught and breaking things
---
test/cli.zig | 27 +++++++++++++++++++++++++++
1 file changed, 27 insertions(+)
diff --git a/test/cli.zig b/test/cli.zig
index 3af78e1857..4c28827ba8 100644
--- a/test/cli.zig
+++ b/test/cli.zig
@@ -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 =
+ \\fn square(num: i32) i32 {
+ \\return num * num;
+ \\}
+ ;
+
+ 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');
+}
From b5f90244a436b74470caedcbfbb459cb2ad90203 Mon Sep 17 00:00:00 2001
From: Ryan Liptak
Date: Sat, 20 Jun 2020 20:50:28 -0700
Subject: [PATCH 28/41] temporary: Add test-cli step for only running cli tests
---
build.zig | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/build.zig b/build.zig
index 4d71b0fb36..2b5863a2b9 100644
--- a/build.zig
+++ b/build.zig
@@ -126,7 +126,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));
From b216d8de886b1d3b3cebb279864ec6bc91216d3e Mon Sep 17 00:00:00 2001
From: Ryan Liptak
Date: Sat, 20 Jun 2020 22:20:23 -0700
Subject: [PATCH 29/41] Simplify unformatted code in zig fmt cli test
---
test/cli.zig | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/test/cli.zig b/test/cli.zig
index 4c28827ba8..410fa97c8f 100644
--- a/test/cli.zig
+++ b/test/cli.zig
@@ -148,11 +148,7 @@ fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void {
fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
_ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" });
- const unformatted_code =
- \\fn square(num: i32) i32 {
- \\return num * num;
- \\}
- ;
+ 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);
From 399f6b77c40d5ebd1d54ad193a70f1935c1ebdfc Mon Sep 17 00:00:00 2001
From: Ryan Liptak
Date: Sat, 20 Jun 2020 22:21:23 -0700
Subject: [PATCH 30/41] Add 'no changes' test to zig fmt cli test
---
test/cli.zig | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/test/cli.zig b/test/cli.zig
index 410fa97c8f..77d79ed98e 100644
--- a/test/cli.zig
+++ b/test/cli.zig
@@ -165,4 +165,8 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void {
// 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);
}
From b70c38c33cd00d63783c51e440a9d30035b9eee3 Mon Sep 17 00:00:00 2001
From: Alexandros Naskos
Date: Sun, 21 Jun 2020 17:03:02 +0300
Subject: [PATCH 31/41] Close source file after reading it in zig fmt
---
src-self-hosted/main.zig | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 29930270eb..085e644d7f 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -736,7 +736,8 @@ fn fmtPathFile(
sub_path: []const u8,
) FmtError!void {
const source_file = try dir.openFile(sub_path, .{});
- defer source_file.close();
+ var file_closed = false;
+ errdefer if (!file_closed) source_file.close();
const stat = try source_file.stat();
@@ -748,6 +749,8 @@ fn fmtPathFile(
error.ConnectionTimedOut => unreachable,
else => |e| return e,
};
+ source_file.close();
+ file_closed = true;
defer fmt.gpa.free(source_code);
// Add to set after no longer possible to get error.IsDir.
From 6f475130098a5913b485828c72cb47ab8146f9ea Mon Sep 17 00:00:00 2001
From: Charlie Stanton
Date: Sun, 21 Jun 2020 18:24:59 +0100
Subject: [PATCH 32/41] Adds std.meta.cast and uses it to simplify translate-c
---
lib/std/meta.zig | 95 ++++++++++++++++++
src-self-hosted/translate_c.zig | 164 +++-----------------------------
test/translate_c.zig | 16 ++--
3 files changed, 118 insertions(+), 157 deletions(-)
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index f93f035429..f27da1a512 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -693,3 +693,98 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
},
});
}
+
+/// Given a type and value, cast the value to the type as c would
+pub fn cast(comptime DestType: type, target: var) DestType {
+ const TargetType = @TypeOf(target);
+ switch (@typeInfo(DestType)) {
+ .Pointer => |_| {
+ switch (@typeInfo(TargetType)) {
+ .Int => |_| {
+ return @intToPtr(DestType, target);
+ },
+ .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 => |_| {
+ return @intToPtr(DestType, target);
+ },
+ .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 => |_| {
+ if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
+ return @intToEnum(DestType, target);
+ }
+ },
+ .EnumLiteral => |_| {
+ if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
+ return @intToEnum(DestType, target);
+ }
+ },
+ .Int => |_| {
+ switch (@typeInfo(TargetType)) {
+ .Pointer => |_| {
+ return @as(DestType, @ptrToInt(target));
+ },
+ .Optional => |opt| {
+ if (@typeInfo(opt.child) == .Pointer) {
+ return @as(DestType, @ptrToInt(target));
+ }
+ },
+ .Enum => |_| {
+ return @as(DestType, @enumToInt(target));
+ },
+ .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));
+}
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
index 3b1a91b28c..acce0e7f1d 100644
--- a/src-self-hosted/translate_c.zig
+++ b/src-self-hosted/translate_c.zig
@@ -5668,161 +5668,27 @@ 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, ")");
-
- 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"),
+ //(@import("std").meta.cast(dest, x))
+ const import_fn_call = try c.createBuiltinCall("@import", 1);
+ const std_token = try appendToken(c, .StringLiteral, "\"std\"");
+ const std_node = try c.arena.create(ast.Node.StringLiteral);
+ std_node.* = .{
+ .token = std_token,
};
- if_1.condition = &cmp_1.base;
- _ = try appendToken(c, .RParen, ")");
+ import_fn_call.params()[0] = &std_node.base;
+ 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 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;
diff --git a/test/translate_c.zig b/test/translate_c.zig
index 1c23afdb30..a4e2a33000 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -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) {
From 8696e52a3d617ce30ec6202adc89cb10c67bcc43 Mon Sep 17 00:00:00 2001
From: Robin Voetter
Date: Sun, 21 Jun 2020 20:55:44 +0200
Subject: [PATCH 33/41] Make unary minus for unsigned types a compile error
(#5654)
* Make unary minus for unsigned types a compile error
* Add unreachable when generating unsigned negate
---
src/codegen.cpp | 12 ++++++------
src/ir.cpp | 37 ++++++++++++++++++++++---------------
test/compile_errors.zig | 9 +++++++++
3 files changed, 37 insertions(+), 21 deletions(-)
diff --git a/src/codegen.cpp b/src/codegen.cpp
index e20d6d60f5..f7c3575f86 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -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));
@@ -9265,7 +9265,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);
diff --git a/src/ir.cpp b/src/ir.cpp
index 635af397c4..bb6ca554df 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -12602,28 +12602,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
@@ -20987,17 +20987,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)) {
@@ -30380,7 +30387,7 @@ static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinF
case BuiltinFnIdTrunc:
f128M_trunc(in, out);
break;
- case BuiltinFnIdRound:
+ case BuiltinFnIdRound:
f128M_roundToInt(in, softfloat_round_near_maxMag, false, out);
break;
case BuiltinFnIdNearbyInt:
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 3f898cc337..e3dd1f0d8f 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -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'"
+ });
}
From d907f574e02aadf8196e616bcc2fb2813cf2c82c Mon Sep 17 00:00:00 2001
From: xackus <14938807+xackus@users.noreply.github.com>
Date: Sat, 20 Jun 2020 18:35:01 +0200
Subject: [PATCH 34/41] stage1: fix concat of sliced str literals
---
src/ir.cpp | 3 +--
test/stage1/behavior/slice.zig | 5 +++++
2 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/src/ir.cpp b/src/ir.cpp
index bb6ca554df..3fa138ed8e 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -826,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();
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;
diff --git a/test/stage1/behavior/slice.zig b/test/stage1/behavior/slice.zig
index 1faefe6800..8aa3bdb7c1 100644
--- a/test/stage1/behavior/slice.zig
+++ b/test/stage1/behavior/slice.zig
@@ -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();
From 8c15cfe3dabe736754b138f16de905e6487bd8b5 Mon Sep 17 00:00:00 2001
From: Charlie Stanton
Date: Sun, 21 Jun 2020 21:48:12 +0100
Subject: [PATCH 35/41] Compacts switch statements and string literal
---
lib/std/meta.zig | 31 +++++++++----------------------
src-self-hosted/translate_c.zig | 8 ++------
2 files changed, 11 insertions(+), 28 deletions(-)
diff --git a/lib/std/meta.zig b/lib/std/meta.zig
index f27da1a512..6c10941aa7 100644
--- a/lib/std/meta.zig
+++ b/lib/std/meta.zig
@@ -694,16 +694,14 @@ pub fn Vector(comptime len: u32, comptime child: type) type {
});
}
-/// Given a type and value, cast the value to the type as c would
+/// 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 => |_| {
+ .Pointer => {
switch (@typeInfo(TargetType)) {
- .Int => |_| {
- return @intToPtr(DestType, target);
- },
- .ComptimeInt => |_| {
+ .Int, .ComptimeInt => {
return @intToPtr(DestType, target);
},
.Pointer => |ptr| {
@@ -720,10 +718,7 @@ pub fn cast(comptime DestType: type, target: var) DestType {
.Optional => |opt| {
if (@typeInfo(opt.child) == .Pointer) {
switch (@typeInfo(TargetType)) {
- .Int => |_| {
- return @intToPtr(DestType, target);
- },
- .ComptimeInt => |_| {
+ .Int, .ComptimeInt => {
return @intToPtr(DestType, target);
},
.Pointer => |ptr| {
@@ -738,19 +733,14 @@ pub fn cast(comptime DestType: type, target: var) DestType {
}
}
},
- .Enum => |_| {
+ .Enum, .EnumLiteral => {
if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
return @intToEnum(DestType, target);
}
},
- .EnumLiteral => |_| {
- if (@typeInfo(TargetType) == .Int or @typeInfo(TargetType) == .ComptimeInt) {
- return @intToEnum(DestType, target);
- }
- },
- .Int => |_| {
+ .Int, .ComptimeInt => {
switch (@typeInfo(TargetType)) {
- .Pointer => |_| {
+ .Pointer => {
return @as(DestType, @ptrToInt(target));
},
.Optional => |opt| {
@@ -758,10 +748,7 @@ pub fn cast(comptime DestType: type, target: var) DestType {
return @as(DestType, @ptrToInt(target));
}
},
- .Enum => |_| {
- return @as(DestType, @enumToInt(target));
- },
- .EnumLiteral => |_| {
+ .Enum, .EnumLiteral => {
return @as(DestType, @enumToInt(target));
},
else => {},
diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig
index acce0e7f1d..e492edb379 100644
--- a/src-self-hosted/translate_c.zig
+++ b/src-self-hosted/translate_c.zig
@@ -5670,12 +5670,8 @@ fn parseCPrimaryExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
//(@import("std").meta.cast(dest, x))
const import_fn_call = try c.createBuiltinCall("@import", 1);
- const std_token = try appendToken(c, .StringLiteral, "\"std\"");
- const std_node = try c.arena.create(ast.Node.StringLiteral);
- std_node.* = .{
- .token = std_token,
- };
- import_fn_call.params()[0] = &std_node.base;
+ 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");
From 64078ca92439d4f01d4a6e60a6ad33025da5a36a Mon Sep 17 00:00:00 2001
From: Jakub Konka
Date: Mon, 22 Jun 2020 09:14:51 +0200
Subject: [PATCH 36/41] Enhance std.os.symlinkat coverage
Fixes `std.os.symlinkat` compile errors, adds Windows stub (still
needs to be implemented), adds WASI implementation.
---
lib/std/c.zig | 1 +
lib/std/os.zig | 62 +++++++++++++++++++++++++++++++++++++++++----
lib/std/os/test.zig | 13 ++++++++++
3 files changed, 71 insertions(+), 5 deletions(-)
diff --git a/lib/std/c.zig b/lib/std/c.zig
index fe9fc7ac40..97d6bf5215 100644
--- a/lib/std/c.zig
+++ b/lib/std/c.zig
@@ -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;
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 0558390b9e..16ab314c14 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -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(sym_link_path.ptr, sym_link_path.len, newdirfd, target_path.ptr, target_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,
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index cc3b4f5741..d4624929ee 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -18,6 +18,19 @@ const AtomicOrder = builtin.AtomicOrder;
const tmpDir = std.testing.tmpDir;
const Dir = std.fs.Dir;
+test "readlinkat" {
+ 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");
+
+ // TODO read the link
+}
+
test "makePath, put some files in it, deleteTree" {
var tmp = tmpDir(.{});
defer tmp.cleanup();
From c950f0c6c3d5472a635ba8b971f46200d41221fd Mon Sep 17 00:00:00 2001
From: Jakub Konka
Date: Mon, 22 Jun 2020 09:40:06 +0200
Subject: [PATCH 37/41] Enhance std.os.readlinkat coverage
Adds Windows stub (still needs to be implemented on Windows),
adds WASI implementation, adds unit test testing basic chain of
ops: create file -> symlink -> readlink.
---
lib/std/os.zig | 46 +++++++++++++++++++++++++++++++++++++++++++--
lib/std/os/test.zig | 8 +++++++-
2 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/lib/std/os.zig b/lib/std/os.zig
index 16ab314c14..59cde5d606 100644
--- a/lib/std/os.zig
+++ b/lib/std/os.zig
@@ -1588,7 +1588,7 @@ 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(sym_link_path.ptr, sym_link_path.len, newdirfd, target_path.ptr, target_path.len)) {
+ 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,
@@ -2343,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)) {
diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig
index d4624929ee..c298a0a203 100644
--- a/lib/std/os/test.zig
+++ b/lib/std/os/test.zig
@@ -19,6 +19,9 @@ 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();
@@ -28,7 +31,10 @@ test "readlinkat" {
// create a symbolic link
try os.symlinkat("file.txt", tmp.dir.fd, "link");
- // TODO read the 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" {
From 8faa85ac19e1bbbe962d69b283c94526e35f5338 Mon Sep 17 00:00:00 2001
From: Carter Sande
Date: Mon, 22 Jun 2020 02:45:23 -0700
Subject: [PATCH 38/41] ArgIteratorWindows: don't treat unclosed quotes like
they're escaped
---
lib/std/process.zig | 31 +++----------------------------
1 file changed, 3 insertions(+), 28 deletions(-)
diff --git a/lib/std/process.zig b/lib/std/process.zig
index a65f6da3af..57b7888e19 100644
--- a/lib/std/process.zig
+++ b/lib/std/process.zig
@@ -282,7 +282,6 @@ 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};
@@ -296,7 +295,6 @@ pub const ArgIteratorWindows = struct {
.index = 0,
.cmd_line = cmd_line,
.in_quote = false,
- .quote_count = countQuotes(cmd_line),
.seen_quote_count = 0,
};
}
@@ -342,7 +340,7 @@ pub const ArgIteratorWindows = struct {
backslash_count += 1;
},
' ', '\t' => {
- if (self.seen_quote_count % 2 == 0 or self.seen_quote_count == self.quote_count) {
+ if (self.seen_quote_count % 2 == 0) {
return true;
}
backslash_count = 0;
@@ -371,9 +369,6 @@ pub const ArgIteratorWindows = struct {
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('"');
- }
} else {
try buf.append('"');
}
@@ -384,7 +379,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 (self.seen_quote_count % 2 == 1) {
try buf.append(byte);
} else {
return buf.toOwnedSlice();
@@ -405,26 +400,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 +553,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",
From 7cb41a415a6eca7d13980a62ee5727b4adcad8b0 Mon Sep 17 00:00:00 2001
From: Carter Sande
Date: Mon, 22 Jun 2020 02:53:14 -0700
Subject: [PATCH 39/41] ArgIteratorWindows: simplify quote state tracking
---
lib/std/process.zig | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/lib/std/process.zig b/lib/std/process.zig
index 57b7888e19..520b219f97 100644
--- a/lib/std/process.zig
+++ b/lib/std/process.zig
@@ -281,8 +281,6 @@ pub const ArgIteratorWasi = struct {
pub const ArgIteratorWindows = struct {
index: usize,
cmd_line: [*]const u8,
- in_quote: bool,
- seen_quote_count: usize,
pub const NextError = error{OutOfMemory};
@@ -294,8 +292,6 @@ pub const ArgIteratorWindows = struct {
return ArgIteratorWindows{
.index = 0,
.cmd_line = cmd_line,
- .in_quote = false,
- .seen_quote_count = 0,
};
}
@@ -326,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) {
@@ -333,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) {
+ if (!in_quote) {
return true;
}
backslash_count = 0;
@@ -358,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) {
@@ -368,7 +366,7 @@ pub const ArgIteratorWindows = struct {
backslash_count = 0;
if (quote_is_real) {
- self.seen_quote_count += 1;
+ in_quote = !in_quote;
} else {
try buf.append('"');
}
@@ -379,7 +377,7 @@ pub const ArgIteratorWindows = struct {
' ', '\t' => {
try self.emitBackslashes(&buf, backslash_count);
backslash_count = 0;
- if (self.seen_quote_count % 2 == 1) {
+ if (in_quote) {
try buf.append(byte);
} else {
return buf.toOwnedSlice();
From 65ef74e2cdc87888d16ad207371cfd3ff8c0dce1 Mon Sep 17 00:00:00 2001
From: prime31
Date: Mon, 22 Jun 2020 07:30:02 -0700
Subject: [PATCH 40/41] `try` allocation of pointer type when parsing (#5665)
* `try` allocation of pointer type when parsing
* fixes pointer destroy compile error
---
lib/std/json.zig | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/std/json.zig b/lib/std/json.zig
index ae10dc9559..6377b69a80 100644
--- a/lib/std/json.zig
+++ b/lib/std/json.zig
@@ -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| {
From 923c0feda1e42dcaf65ab80bafd7400996b39861 Mon Sep 17 00:00:00 2001
From: Jakub Konka
Date: Sun, 21 Jun 2020 15:18:50 +0200
Subject: [PATCH 41/41] Add std.fs.File.readAllAlloc tests
This commit adds some unit tests for `std.fs.File.readAllAlloc`
function. It also updates the docs of `Reader.readNoEof`
which were outdated, and swaps `inStream()` for `reader()` in
`File.readAllAlloc` with the former being deprecated.
---
lib/std/fs/file.zig | 2 +-
lib/std/fs/test.zig | 43 ++++++++++++++++++++++++++++++++++++++++---
lib/std/io/reader.zig | 3 +--
3 files changed, 42 insertions(+), 6 deletions(-)
diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig
index 160f8314b9..cffc8cf87e 100644
--- a/lib/std/fs/file.zig
+++ b/lib/std/fs/file.zig
@@ -364,7 +364,7 @@ pub const File = struct {
const buf = try allocator.allocWithOptions(u8, size, alignment, optional_sentinel);
errdefer allocator.free(buf);
- try self.inStream().readNoEof(buf);
+ try self.reader().readNoEof(buf);
return buf;
}
diff --git a/lib/std/fs/test.zig b/lib/std/fs/test.zig
index 875565fc54..23a9c96ff5 100644
--- a/lib/std/fs/test.zig
+++ b/lib/std/fs/test.zig
@@ -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();
diff --git a/lib/std/io/reader.zig b/lib/std/io/reader.zig
index 03744e4da4..4c682e8aba 100644
--- a/lib/std/io/reader.zig
+++ b/lib/std/io/reader.zig
@@ -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;