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

master
Andrew Kelley 2019-02-07 12:21:20 -05:00
commit a94304d3e4
No known key found for this signature in database
GPG Key ID: 7C5F548F728501A9
45 changed files with 779 additions and 345 deletions

View File

@ -596,6 +596,7 @@ set(ZIG_STD_FILES
"os/windows/ntdll.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
"os/windows/tls.zig"
"os/windows/util.zig"
"os/zen.zig"
"pdb.zig"

View File

@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void {
var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe);
const langref_out_path = os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable;
const langref_out_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, "langref.html" },
) catch unreachable;
var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{
docgen_exe.getOutputPath(),
rel_zig_exe,
@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
for (dep.libdirs.toSliceConst()) |lib_dir| {
lib_exe_obj.addLibPath(lib_dir);
}
const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable;
const lib_dir = os.path.join(
b.allocator,
[][]const u8{ dep.prefix, "lib" },
) catch unreachable;
for (dep.system_libs.toSliceConst()) |lib| {
const static_bare_name = if (mem.eql(u8, lib, "curses"))
([]const u8)("libncurses.a")
else
b.fmt("lib{}.a", lib);
const static_lib_name = os.path.join(b.allocator, lib_dir, static_bare_name) catch unreachable;
const static_lib_name = os.path.join(
b.allocator,
[][]const u8{ lib_dir, static_bare_name },
) catch unreachable;
const have_static = fileExists(static_lib_name) catch unreachable;
if (have_static) {
lib_exe_obj.addObjectFile(static_lib_name);
@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool {
fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
lib_exe_obj.addObjectFile(os.path.join(b.allocator, cmake_binary_dir, "zig_cpp", b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt())) catch unreachable);
lib_exe_obj.addObjectFile(os.path.join(b.allocator, [][]const u8{
cmake_binary_dir,
"zig_cpp",
b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt()),
}) catch unreachable);
}
const LibraryDep = struct {
@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
var it = mem.tokenize(stdlib_files, ";");
while (it.next()) |stdlib_file| {
const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable;
const dest_path = os.path.join(b.allocator, "lib", "zig", "std", stdlib_file) catch unreachable;
const src_path = os.path.join(b.allocator, [][]const u8{ "std", stdlib_file }) catch unreachable;
const dest_path = os.path.join(
b.allocator,
[][]const u8{ "lib", "zig", "std", stdlib_file },
) catch unreachable;
b.installFile(src_path, dest_path);
}
}
@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
var it = mem.tokenize(c_header_files, ";");
while (it.next()) |c_header_file| {
const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable;
const dest_path = os.path.join(b.allocator, "lib", "zig", "include", c_header_file) catch unreachable;
const src_path = os.path.join(b.allocator, [][]const u8{ "c_headers", c_header_file }) catch unreachable;
const dest_path = os.path.join(
b.allocator,
[][]const u8{ "lib", "zig", "include", c_header_file },
) catch unreachable;
b.installFile(src_path, dest_path);
}
}

View File

@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
try tokenizeAndPrint(tokenizer, out, code.source_token);
try out.write("</pre>");
const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
const tmp_source_file_name = try os.path.join(
allocator,
[][]const u8{ tmp_dir_name, name_plus_ext },
);
try io.writeFile(tmp_source_file_name, trimmed_raw_source);
switch (code.id) {
Code.Id.Exe => |expected_outcome| {
const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);
const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext);
const tmp_bin_file_name = try os.path.join(
allocator,
[][]const u8{ tmp_dir_name, name_plus_bin_ext },
);
var build_args = std.ArrayList([]const u8).init(allocator);
defer build_args.deinit();
try build_args.appendSlice([][]const u8{
@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
}
for (code.link_objects) |link_object| {
const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);
const full_path_object = try os.path.join(allocator, tmp_dir_name, name_with_ext);
const full_path_object = try os.path.join(
allocator,
[][]const u8{ tmp_dir_name, name_with_ext },
);
try build_args.append("--object");
try build_args.append(full_path_object);
try out.print(" --object {}", name_with_ext);
@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
},
Code.Id.Obj => |maybe_error_match| {
const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);
const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);
const tmp_obj_file_name = try os.path.join(
allocator,
[][]const u8{ tmp_dir_name, name_plus_obj_ext },
);
var build_args = std.ArrayList([]const u8).init(allocator);
defer build_args.deinit();
const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name);
const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext);
const output_h_file_name = try os.path.join(
allocator,
[][]const u8{ tmp_dir_name, name_plus_h_ext },
);
try build_args.appendSlice([][]const u8{
zig_exe,

View File

@ -3192,7 +3192,16 @@ fn foo() void { }
{#code_end#}
{#header_open|Pass-by-value Parameters#}
<p>
In Zig, structs, unions, and enums with payloads can be passed directly to a function:
Primitive types such as {#link|Integers#} and {#link|Floats#} passed as parameters
are copied, and then the copy is available in the function body. This is called "passing by value".
Copying a primitive type is essentially free and typically involves nothing more than
setting a register.
</p>
<p>
Structs, unions, and arrays can sometimes be more efficiently passed as a reference, since a copy
could be arbitrarily expensive depending on the size. When these types are passed
as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way
Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.
</p>
{#code_begin|test#}
const Point = struct {
@ -3201,20 +3210,20 @@ const Point = struct {
};
fn foo(point: Point) i32 {
// Here, `point` could be a reference, or a copy. The function body
// can ignore the difference and treat it as a value. Be very careful
// taking the address of the parameter - it should be treated as if
// the address will become invalid when the function returns.
return point.x + point.y;
}
const assert = @import("std").debug.assert;
test "pass aggregate type by non-copy value to function" {
test "pass struct to function" {
assert(foo(Point{ .x = 1, .y = 2 }) == 3);
}
{#code_end#}
<p>
In this case, the value may be passed by reference, or by value, whichever way
Zig decides will be faster.
</p>
<p>
For extern functions, Zig follows the C ABI for passing structs and unions by value.
</p>
{#header_close#}

View File

@ -1,15 +0,0 @@
# How to Add Support For More Targets
Create bootstrap code in std/bootstrap.zig and add conditional compilation
logic. This code is responsible for the real executable entry point, calling
main() and making the exit syscall when main returns.
How to pass a byvalue struct parameter in the C calling convention is
target-specific. Add logic for how to do function prototypes and function calls
for the target when an exported or external function has a byvalue struct.
Write the target-specific code in the standard library.
Update the C integer types to be the correct size for the target.
Make sure that `c_longdouble` codegens the correct floating point value.

View File

@ -487,7 +487,7 @@ pub const Compilation = struct {
comp.name = try Buffer.init(comp.arena(), name);
comp.llvm_triple = try target.getTriple(comp.arena());
comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
comp.zig_std_dir = try std.os.path.join(comp.arena(), [][]const u8{ zig_lib_dir, "std" });
const opt_level = switch (build_mode) {
builtin.Mode.Debug => llvm.CodeGenLevelNone,
@ -1198,7 +1198,7 @@ pub const Compilation = struct {
const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
defer self.gpa().free(file_name);
const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]);
const full_path = try os.path.join(self.gpa(), [][]const u8{ tmp_dir, file_name[0..] });
errdefer self.gpa().free(full_path);
return Buffer.fromOwnedSlice(self.gpa(), full_path);
@ -1219,7 +1219,7 @@ pub const Compilation = struct {
const zig_dir_path = try getZigDir(self.gpa());
defer self.gpa().free(zig_dir_path);
const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]);
const tmp_dir = try os.path.join(self.arena(), [][]const u8{ zig_dir_path, comp_dir_name[0..] });
try os.makePath(self.gpa(), tmp_dir);
return tmp_dir;
}

View File

@ -8,10 +8,10 @@ const warn = std.debug.warn;
/// Caller must free result
pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" });
errdefer allocator.free(test_zig_dir);
const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "index.zig" });
defer allocator.free(test_index_file);
var file = try os.File.openRead(test_index_file);

View File

@ -230,7 +230,7 @@ pub const LibCInstallation = struct {
while (path_i < search_paths.len) : (path_i += 1) {
const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
const stdlib_path = try std.os.path.join(loop.allocator, [][]const u8{ search_path, "stdlib.h" });
defer loop.allocator.free(stdlib_path);
if (try fileExists(stdlib_path)) {
@ -254,7 +254,10 @@ pub const LibCInstallation = struct {
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
const stdlib_path = try std.os.path.join(
loop.allocator,
[][]const u8{ result_buf.toSliceConst(), "stdlib.h" },
);
defer loop.allocator.free(stdlib_path);
if (try fileExists(stdlib_path)) {
@ -283,7 +286,10 @@ pub const LibCInstallation = struct {
builtin.Arch.aarch64v8 => try stream.write("arm"),
else => return error.UnsupportedArchitecture,
}
const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
const ucrt_lib_path = try std.os.path.join(
loop.allocator,
[][]const u8{ result_buf.toSliceConst(), "ucrt.lib" },
);
defer loop.allocator.free(ucrt_lib_path);
if (try fileExists(ucrt_lib_path)) {
self.lib_dir = result_buf.toOwnedSlice();
@ -358,7 +364,10 @@ pub const LibCInstallation = struct {
builtin.Arch.aarch64v8 => try stream.write("arm\\"),
else => return error.UnsupportedArchitecture,
}
const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
const kernel32_path = try std.os.path.join(
loop.allocator,
[][]const u8{ result_buf.toSliceConst(), "kernel32.lib" },
);
defer loop.allocator.free(kernel32_path);
if (try fileExists(kernel32_path)) {
self.kernel32_lib_dir = result_buf.toOwnedSlice();

View File

@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
}
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
const full_path = try std.os.path.join(&ctx.arena.allocator, [][]const u8{ dirname, basename });
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
try ctx.args.append(full_path_with_null.ptr);
}

View File

@ -757,7 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
var group = event.Group(FmtError!void).init(fmt.loop);
while (try dir.next()) |entry| {
if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
const full_path = try os.path.join(fmt.loop.allocator, [][]const u8{ file_path, entry.name });
try group.call(fmtPath, fmt, full_path, check_mode);
}
}

View File

@ -87,7 +87,7 @@ pub const TestContext = struct {
) !void {
var file_index_buf: [20]u8 = undefined;
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 });
if (std.os.path.dirname(file1_path)) |dirname| {
try std.os.makePath(allocator, dirname);
@ -120,7 +120,7 @@ pub const TestContext = struct {
) !void {
var file_index_buf: [20]u8 = undefined;
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 });
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
if (std.os.path.dirname(file1_path)) |dirname| {

View File

@ -544,12 +544,7 @@ struct AstNodeDefer {
};
struct AstNodeVariableDeclaration {
VisibMod visib_mod;
Buf *symbol;
bool is_const;
bool is_comptime;
bool is_export;
bool is_extern;
// one or both of type and expr will be non null
AstNode *type;
AstNode *expr;
@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
AstNode *align_expr;
// populated if the "section(S)" is present
AstNode *section_expr;
Token *threadlocal_tok;
VisibMod visib_mod;
bool is_const;
bool is_comptime;
bool is_export;
bool is_extern;
};
struct AstNodeTestDecl {
@ -1873,6 +1875,7 @@ struct ZigVar {
bool shadowable;
bool src_is_const;
bool gen_is_const;
bool is_thread_local;
};
struct ErrorTableEntry {

View File

@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
if (node->owner->c_import_node != nullptr) {
// if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
buf_sprintf("compiler bug: @cImport generated invalid zig code"));
add_error_note(g, err, node, msg);
g->errors.append(err);
return err;
}
ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
node->owner->source_code, node->owner->line_offsets, msg);
g->errors.append(err);
return err;
}
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
if (node->owner->c_import_node != nullptr) {
static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
Buf *msg)
{
if (owner->c_import_node != nullptr) {
// if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
return note;
}
ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
node->owner->source_code, node->owner->line_offsets, msg);
ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
owner->source_code, owner->line_offsets, msg);
err_msg_add_note(parent_msg, err);
return err;
}
ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) {
if (owner->c_import_node != nullptr) {
// if this happens, then translate_c generated code that
// failed semantic analysis, which isn't supposed to happen
ErrorMsg *err = add_node_error(g, owner->c_import_node,
buf_sprintf("compiler bug: @cImport generated invalid zig code"));
add_error_note_token(g, err, owner, token, msg);
g->errors.append(err);
return err;
}
ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
owner->source_code, owner->line_offsets, msg);
g->errors.append(err);
return err;
}
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
Token fake_token;
fake_token.start_line = node->line;
fake_token.start_column = node->column;
return add_token_error(g, node->owner, &fake_token, msg);
}
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
Token fake_token;
fake_token.start_line = node->line;
fake_token.start_column = node->column;
return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
}
ZigType *new_type_table_entry(ZigTypeId id) {
ZigType *entry = allocate<ZigType>(1);
entry->id = id;
@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
bool is_const = var_decl->is_const;
bool is_extern = var_decl->is_extern;
bool is_export = var_decl->is_export;
bool is_thread_local = var_decl->threadlocal_tok != nullptr;
ZigType *explicit_type = nullptr;
if (var_decl->type) {
@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
is_const, init_val, &tld_var->base, type);
tld_var->var->linkage = linkage;
tld_var->var->is_thread_local = is_thread_local;
if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
tld_var->var->var_type = g->builtin_types.entry_invalid;
@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
}
}
if (is_thread_local && is_const) {
add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
}
g->global_vars.append(tld_var);
}

View File

@ -12,6 +12,7 @@
void semantic_analyze(CodeGen *g);
ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
ZigType *new_type_table_entry(ZigTypeId id);
ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);

View File

@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {
return is_const ? "const" : "var";
}
static const char *thread_local_string(Token *tok) {
return (tok == nullptr) ? "" : "threadlocal ";
}
const char *container_string(ContainerKind kind) {
switch (kind) {
case ContainerKindEnum: return "enum";
@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
{
const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var);
fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
print_symbol(ar, node->data.variable_declaration.symbol);
if (node->data.variable_declaration.type) {

View File

@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {
};
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
Buf *zig_lib_dir)
Buf *zig_lib_dir, Buf *override_std_dir)
{
CodeGen *g = allocate<CodeGen>(1);
@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
g->zig_lib_dir = zig_lib_dir;
g->zig_std_dir = buf_alloc();
os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
if (override_std_dir == nullptr) {
g->zig_std_dir = buf_alloc();
os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
} else {
g->zig_std_dir = override_std_dir;
}
g->zig_c_headers_dir = buf_alloc();
os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);
@ -2582,6 +2586,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
}
typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
IrInstructionBinOp *bin_op_instruction)
{
@ -2640,50 +2646,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
} else {
zig_unreachable();
}
case IrBinOpMult:
case IrBinOpMultWrap:
case IrBinOpAdd:
case IrBinOpAddWrap:
case IrBinOpSub:
case IrBinOpSubWrap: {
// These are lookup table using the AddSubMul enum as the lookup.
// If AddSubMul ever changes, then these tables will be out of
// date.
static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
bool is_vector = type_entry->id == ZigTypeIdVector;
bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
AddSubMul add_sub_mul =
op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
AddSubMulMul;
// The code that is generated for vectors and scalars are the same,
// so we can just set type_entry to the vectors elem_type an avoid
// a lot of repeated code.
if (is_vector)
type_entry = type_entry->data.vector.elem_type;
if (type_entry->id == ZigTypeIdPointer) {
assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
LLVMValueRef subscript_value;
if (is_vector)
zig_panic("TODO: Implement vector operations on pointers.");
switch (add_sub_mul) {
case AddSubMulAdd:
subscript_value = op2_value;
break;
case AddSubMulSub:
subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
break;
case AddSubMulMul:
zig_unreachable();
}
// TODO runtime safety
return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, "");
return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
} else if (type_entry->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else if (type_entry->id == ZigTypeIdInt) {
bool is_wrapping = (op_id == IrBinOpAddWrap);
if (is_wrapping) {
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value);
if (is_vector)
zig_panic("TODO: Implement runtime safety vector operations.");
return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
} else if (type_entry->data.integral.is_signed) {
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
} else {
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
}
} else if (type_entry->id == ZigTypeIdVector) {
ZigType *elem_type = type_entry->data.vector.elem_type;
if (elem_type->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
} else if (elem_type->id == ZigTypeIdPointer) {
zig_panic("TODO codegen for pointers in vectors");
} else if (elem_type->id == ZigTypeIdInt) {
bool is_wrapping = (op_id == IrBinOpAddWrap);
if (is_wrapping) {
return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
zig_panic("TODO runtime safety for vector integer addition");
} else if (elem_type->data.integral.is_signed) {
return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
} else {
return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
}
} else {
zig_unreachable();
return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
}
} else {
zig_unreachable();
}
}
case IrBinOpBinOr:
return LLVMBuildOr(g->builder, op1_value, op2_value, "");
case IrBinOpBinXor:
@ -2728,49 +2755,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
}
}
case IrBinOpSub:
case IrBinOpSubWrap:
if (type_entry->id == ZigTypeIdPointer) {
assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
// TODO runtime safety
LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
} else if (type_entry->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
} else if (type_entry->id == ZigTypeIdInt) {
bool is_wrapping = (op_id == IrBinOpSubWrap);
if (is_wrapping) {
return LLVMBuildSub(g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value);
} else if (type_entry->data.integral.is_signed) {
return LLVMBuildNSWSub(g->builder, op1_value, op2_value, "");
} else {
return LLVMBuildNUWSub(g->builder, op1_value, op2_value, "");
}
} else {
zig_unreachable();
}
case IrBinOpMult:
case IrBinOpMultWrap:
if (type_entry->id == ZigTypeIdFloat) {
ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
} else if (type_entry->id == ZigTypeIdInt) {
bool is_wrapping = (op_id == IrBinOpMultWrap);
if (is_wrapping) {
return LLVMBuildMul(g->builder, op1_value, op2_value, "");
} else if (want_runtime_safety) {
return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value);
} else if (type_entry->data.integral.is_signed) {
return LLVMBuildNSWMul(g->builder, op1_value, op2_value, "");
} else {
return LLVMBuildNUWMul(g->builder, op1_value, op2_value, "");
}
} else {
zig_unreachable();
}
case IrBinOpDivUnspecified:
return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
op1_value, op2_value, type_entry, DivKindFloat);
@ -6361,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) {
report_errors_and_maybe_exit(g);
}
static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
if (var->is_thread_local && !g->is_single_threaded) {
LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
}
}
static void do_code_gen(CodeGen *g) {
assert(!g->errors.length);
@ -6445,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {
maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
LLVMSetAlignment(global_value, var->align_bytes);
LLVMSetGlobalConstant(global_value, var->gen_is_const);
set_global_tls(g, var, global_value);
}
} else {
bool exported = (var->linkage == VarLinkageExport);
@ -6470,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {
}
LLVMSetGlobalConstant(global_value, var->gen_is_const);
set_global_tls(g, var, global_value);
}
var->value_ref = global_value;
@ -7520,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
scan_import(g, g->compile_var_import);
@ -7560,7 +7553,13 @@ static void init(CodeGen *g) {
LLVMTargetRef target_ref;
char *err_msg = nullptr;
if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) {
zig_panic("unable to create target based on: %s", buf_ptr(&g->triple_str));
fprintf(stderr,
"Zig is expecting LLVM to understand this target: '%s'\n"
"However LLVM responded with: \"%s\"\n"
"Zig is unable to continue. This is a bug in Zig:\n"
"https://github.com/ziglang/zig/issues/438\n"
, buf_ptr(&g->triple_str), err_msg);
exit(1);
}
bool is_optimized = g->build_mode != BuildModeDebug;
@ -8349,8 +8348,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {
if (!entry)
break;
cache_buf(ch, entry->key);
add_cache_pkg(g, ch, entry->value);
// TODO: I think we need a more sophisticated detection of
// packages we have already seen
if (entry->value != pkg) {
cache_buf(ch, entry->key);
add_cache_pkg(g, ch, entry->value);
}
}
}

View File

@ -15,7 +15,7 @@
#include <stdio.h>
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
Buf *zig_lib_dir);
Buf *zig_lib_dir, Buf *override_std_dir);
void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);

View File

@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
add_node_error(irb->codegen, variable_declaration->section_expr,
buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
}
if (variable_declaration->threadlocal_tok != nullptr) {
add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
}
// Temporarily set the name of the IrExecutable to the VariableDeclaration
// so that the struct or enum from the init expression inherits the name.

View File

@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
}
CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,
parent_gen->build_mode, parent_gen->zig_lib_dir);
parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir);
child_gen->out_h_path = nullptr;
child_gen->verbose_tokenize = parent_gen->verbose_tokenize;

View File

@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) {
" -dirafter [dir] same as -isystem but do it last\n"
" -isystem [dir] add additional search path for other .h files\n"
" -mllvm [arg] forward an arg to LLVM's option processing\n"
" --override-std-dir [arg] use an alternate Zig standard library\n"
"\n"
"Link Options:\n"
" --dynamic-linker [path] set the path to ld.so\n"
@ -395,6 +396,7 @@ int main(int argc, char **argv) {
bool system_linker_hack = false;
TargetSubsystem subsystem = TargetSubsystemAuto;
bool is_single_threaded = false;
Buf *override_std_dir = nullptr;
if (argc >= 2 && strcmp(argv[1], "build") == 0) {
Buf zig_exe_path_buf = BUF_INIT;
@ -430,7 +432,8 @@ int main(int argc, char **argv) {
Buf *build_runner_path = buf_alloc();
os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);
CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir());
CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
override_std_dir);
g->enable_time_report = timing_info;
buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
codegen_set_out_name(g, buf_create_from_str("build"));
@ -645,6 +648,8 @@ int main(int argc, char **argv) {
clang_argv.append(argv[i]);
llvm_argv.append(argv[i]);
} else if (strcmp(arg, "--override-std-dir") == 0) {
override_std_dir = buf_create_from_str(argv[i]);
} else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
lib_dirs.append(argv[i]);
} else if (strcmp(arg, "--library") == 0) {
@ -819,7 +824,7 @@ int main(int argc, char **argv) {
switch (cmd) {
case CmdBuiltin: {
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir);
g->is_single_threaded = is_single_threaded;
Buf *builtin_source = codegen_generate_builtin_source(g);
if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {
@ -878,7 +883,8 @@ int main(int argc, char **argv) {
if (cmd == CmdRun && buf_out_name == nullptr) {
buf_out_name = buf_create_from_str("run");
}
CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir());
CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(),
override_std_dir);
g->subsystem = subsystem;
if (disable_pic) {

View File

@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
static AstNode *ast_parse_var_decl(ParseContext *pc) {
Token *first = eat_token_if(pc, TokenIdKeywordConst);
if (first == nullptr)
first = eat_token_if(pc, TokenIdKeywordVar);
if (first == nullptr)
return nullptr;
Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
if (mut_kw == nullptr)
mut_kw = eat_token_if(pc, TokenIdKeywordVar);
if (mut_kw == nullptr) {
if (thread_local_kw == nullptr) {
return nullptr;
} else {
ast_invalid_token_error(pc, peek_token(pc));
}
}
Token *identifier = expect_token(pc, TokenIdSymbol);
AstNode *type_expr = nullptr;
if (eat_token_if(pc, TokenIdColon) != nullptr)
@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
expect_token(pc, TokenIdSemicolon);
AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);
res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;
AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
res->data.variable_declaration.threadlocal_tok = thread_local_kw;
res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
res->data.variable_declaration.symbol = token_buf(identifier);
res->data.variable_declaration.type = type_expr;
res->data.variable_declaration.align_expr = align_expr;

View File

@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"suspend", TokenIdKeywordSuspend},
{"switch", TokenIdKeywordSwitch},
{"test", TokenIdKeywordTest},
{"threadlocal", TokenIdKeywordThreadLocal},
{"true", TokenIdKeywordTrue},
{"try", TokenIdKeywordTry},
{"undefined", TokenIdKeywordUndefined},
@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordStruct: return "struct";
case TokenIdKeywordSwitch: return "switch";
case TokenIdKeywordTest: return "test";
case TokenIdKeywordThreadLocal: return "threadlocal";
case TokenIdKeywordTrue: return "true";
case TokenIdKeywordTry: return "try";
case TokenIdKeywordUndefined: return "undefined";

View File

@ -88,6 +88,7 @@ enum TokenId {
TokenIdKeywordSuspend,
TokenIdKeywordSwitch,
TokenIdKeywordTest,
TokenIdKeywordThreadLocal,
TokenIdKeywordTrue,
TokenIdKeywordTry,
TokenIdKeywordUndefined,

View File

@ -145,8 +145,8 @@ pub const Builder = struct {
pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;
self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;
self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable;
self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable;
}
pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
@ -618,7 +618,10 @@ pub const Builder = struct {
///::dest_rel_path is relative to prefix path or it can be an absolute path
pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable;
const full_dest_path = os.path.resolve(
self.allocator,
[][]const u8{ self.prefix, dest_rel_path },
) catch unreachable;
self.pushInstalledFile(full_dest_path);
const install_step = self.allocator.create(InstallFileStep) catch unreachable;
@ -653,7 +656,7 @@ pub const Builder = struct {
}
fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable;
return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable;
}
pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 {
@ -676,7 +679,7 @@ pub const Builder = struct {
if (os.path.isAbsolute(name)) {
return name;
}
const full_path = try os.path.join(self.allocator, search_prefix, "bin", self.fmt("{}{}", name, exe_extension));
const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });
if (os.path.real(self.allocator, full_path)) |real_path| {
return real_path;
} else |_| {
@ -691,7 +694,7 @@ pub const Builder = struct {
}
var it = mem.tokenize(PATH, []u8{os.path.delimiter});
while (it.next()) |path| {
const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
if (os.path.real(self.allocator, full_path)) |real_path| {
return real_path;
} else |_| {
@ -705,7 +708,7 @@ pub const Builder = struct {
return name;
}
for (paths) |path| {
const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
if (os.path.real(self.allocator, full_path)) |real_path| {
return real_path;
} else |_| {
@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct {
}
pub fn getOutputPath(self: *LibExeObjStep) []const u8 {
return if (self.output_path) |output_path| output_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename) catch unreachable;
return if (self.output_path) |output_path| output_path else os.path.join(
self.builder.allocator,
[][]const u8{ self.builder.cache_root, self.out_filename },
) catch unreachable;
}
pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void {
@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct {
}
pub fn getOutputHPath(self: *LibExeObjStep) []const u8 {
return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename) catch unreachable;
return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(
self.builder.allocator,
[][]const u8{ self.builder.cache_root, self.out_h_filename },
) catch unreachable;
}
pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct {
}
if (self.build_options_contents.len() > 0) {
const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));
const build_options_file = try os.path.join(
builder.allocator,
[][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) },
);
try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst());
try zig_args.append("--pkg-begin");
try zig_args.append("build_options");
@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct {
cc_args.append("-c") catch unreachable;
cc_args.append(abs_source_file) catch unreachable;
const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
const cache_o_src = os.path.join(
builder.allocator,
[][]const u8{ builder.cache_root, source_file },
) catch unreachable;
if (os.path.dirname(cache_o_src)) |cache_o_dir| {
try builder.makePath(cache_o_dir);
}
@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct {
cc_args.append("-current_version") catch unreachable;
cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable;
const install_name = builder.pathFromRoot(os.path.join(builder.allocator, builder.cache_root, self.major_only_filename) catch unreachable);
const install_name = builder.pathFromRoot(os.path.join(
builder.allocator,
[][]const u8{ builder.cache_root, self.major_only_filename },
) catch unreachable);
cc_args.append("-install_name") catch unreachable;
cc_args.append(install_name) catch unreachable;
} else {
@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct {
cc_args.append("-c") catch unreachable;
cc_args.append(abs_source_file) catch unreachable;
const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
const cache_o_src = os.path.join(
builder.allocator,
[][]const u8{ builder.cache_root, source_file },
) catch unreachable;
if (os.path.dirname(cache_o_src)) |cache_o_dir| {
try builder.makePath(cache_o_dir);
}
@ -1686,6 +1707,7 @@ pub const TestStep = struct {
no_rosegment: bool,
output_path: ?[]const u8,
system_linker_hack: bool,
override_std_dir: ?[]const u8,
pub fn init(builder: *Builder, root_src: []const u8) TestStep {
const step_name = builder.fmt("test {}", root_src);
@ -1707,6 +1729,7 @@ pub const TestStep = struct {
.no_rosegment = false,
.output_path = null,
.system_linker_hack = false,
.override_std_dir = null,
};
}
@ -1737,6 +1760,10 @@ pub const TestStep = struct {
self.build_mode = mode;
}
pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
self.override_std_dir = dir_path;
}
pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
self.output_path = file_path;
@ -1751,7 +1778,10 @@ pub const TestStep = struct {
return output_path;
} else {
const basename = self.builder.fmt("test{}", self.target.exeFileExt());
return os.path.join(self.builder.allocator, self.builder.cache_root, basename) catch unreachable;
return os.path.join(
self.builder.allocator,
[][]const u8{ self.builder.cache_root, basename },
) catch unreachable;
}
}
@ -1914,6 +1944,10 @@ pub const TestStep = struct {
if (self.system_linker_hack) {
try zig_args.append("--system-linker-hack");
}
if (self.override_std_dir) |dir| {
try zig_args.append("--override-std-dir");
try zig_args.append(builder.pathFromRoot(dir));
}
try builder.spawnChild(zig_args.toSliceConst());
}
@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct {
.builder = builder,
.step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),
.artifact = artifact,
.dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable,
.dest_file = os.path.join(
builder.allocator,
[][]const u8{ dest_dir, artifact.out_filename },
) catch unreachable,
};
self.step.dependOn(&artifact.step);
builder.pushInstalledFile(self.dest_file);
if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename) catch unreachable);
builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable);
builder.pushInstalledFile(os.path.join(
builder.allocator,
[][]const u8{ builder.lib_dir, artifact.major_only_filename },
) catch unreachable);
builder.pushInstalledFile(os.path.join(
builder.allocator,
[][]const u8{ builder.lib_dir, artifact.name_only_filename },
) catch unreachable);
}
return self;
}
@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj
const out_dir = os.path.dirname(output_path) orelse ".";
const out_basename = os.path.basename(output_path);
// sym link for libfoo.so.1 to libfoo.so.1.2.3
const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable;
const major_only_path = os.path.join(
allocator,
[][]const u8{ out_dir, filename_major_only },
) catch unreachable;
os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);
return err;
};
// sym link for libfoo.so to libfoo.so.1
const name_only_path = os.path.join(allocator, out_dir, filename_name_only) catch unreachable;
const name_only_path = os.path.join(
allocator,
[][]const u8{ out_dir, filename_name_only },
) catch unreachable;
os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
return err;

View File

@ -37,7 +37,6 @@ const Module = struct {
var stderr_file: os.File = undefined;
var stderr_file_out_stream: os.File.OutStream = undefined;
/// TODO multithreaded awareness
var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
var stderr_mutex = std.Mutex.init();
pub fn warn(comptime fmt: []const u8, args: ...) void {
@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
const len = try di.coff.getPdbPath(path_buf[0..]);
const raw_path = path_buf[0..len];
const path = try os.path.resolve(allocator, raw_path);
const path = try os.path.resolve(allocator, [][]const u8{raw_path});
try di.pdb.openFile(di.coff, path);
@ -1353,7 +1352,7 @@ const LineNumberProgram = struct {
return error.InvalidDebugInfo;
} else
self.include_dirs[file_entry.dir_index];
const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name);
const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name });
errdefer self.file_entries.allocator.free(file_name);
return LineInfo{
.line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0,

View File

@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type {
}
async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path);
const resolved_path = try os.path.resolve(self.channel.loop.allocator, [][]const u8{file_path});
var resolved_path_consumed = false;
defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
}
async fn testFsWatch(loop: *Loop) !void {
const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
defer loop.allocator.free(file_path);
const contents =

View File

@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
};
const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
const root_addr = @ptrToInt(ptr);
const rem = @rem(root_addr, alignment);
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
const adjusted_addr = root_addr + march_forward_bytes;
const adjusted_addr = mem.alignForward(root_addr, alignment);
const record_addr = adjusted_addr + n;
@intToPtr(*align(1) usize, record_addr).* = root_addr;
return @intToPtr([*]u8, adjusted_addr)[0..n];
@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
const base_addr = @ptrToInt(old_mem.ptr);
const old_addr_end = base_addr + old_mem.len;
const new_addr_end = base_addr + new_size;
const rem = @rem(new_addr_end, os.page_size);
const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
if (old_addr_end > new_addr_end_rounded) {
_ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
}

View File

@ -33,8 +33,8 @@ pub const io = @import("io.zig");
pub const json = @import("json.zig");
pub const macho = @import("macho.zig");
pub const math = @import("math/index.zig");
pub const meta = @import("meta/index.zig");
pub const mem = @import("mem.zig");
pub const meta = @import("meta/index.zig");
pub const net = @import("net.zig");
pub const os = @import("os/index.zig");
pub const pdb = @import("pdb.zig");

View File

@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
}
/// Flush any remaining bits to the stream.
pub fn flushBits(self: *Self) !void {
pub fn flushBits(self: *Self) Error!void {
if (self.bit_count == 0) return;
try self.out_stream.writeByte(self.bit_buffer);
self.bit_buffer = 0;
@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
}
//@BUG: inferred error issue. See: #1386
fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T {
fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
const u8_bit_count = 8;
@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
}
/// Flushes any unwritten bits to the stream
pub fn flush(self: *Self) Stream.Error!void {
pub fn flush(self: *Self) Error!void {
if (is_packed) return self.out_stream.flushBits();
}
fn serializeInt(self: *Self, value: var) !void {
fn serializeInt(self: *Self, value: var) Error!void {
const T = @typeOf(value);
comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
@ -1323,7 +1323,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
}
/// Serializes the passed value into the stream
pub fn serialize(self: *Self, value: var) !void {
pub fn serialize(self: *Self, value: var) Error!void {
const T = comptime @typeOf(value);
if (comptime trait.isIndexable(T)) {

View File

@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
assert(in.pos == if (is_packed) total_packed_bytes else total_bytes);
//Verify that empty error set works with serializer.
//deserializer is covered by SliceInStream
const NullError = io.NullOutStream.Error;
var null_out = io.NullOutStream.init();
var null_out_stream = &null_out.stream;
var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream);
try null_serializer.serialize(data_mem[0..]);
try null_serializer.flush();
}
test "Serializer/Deserializer Int" {
@ -568,4 +577,4 @@ test "Deserializer bad data" {
try testBadData(builtin.Endian.Little, false);
try testBadData(builtin.Endian.Big, true);
try testBadData(builtin.Endian.Little, true);
}
}

View File

@ -882,42 +882,40 @@ pub const SplitIterator = struct {
}
};
/// Naively combines a series of strings with a separator.
/// Naively combines a series of slices with a separator.
/// Allocates memory for the result, which must be freed by the caller.
pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
comptime assert(strings.len >= 1);
var total_strings_len: usize = strings.len; // 1 sep per string
{
comptime var string_i = 0;
inline while (string_i < strings.len) : (string_i += 1) {
const arg = ([]const u8)(strings[string_i]);
total_strings_len += arg.len;
}
}
pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
if (slices.len == 0) return (([*]u8)(undefined))[0..0];
const buf = try allocator.alloc(u8, total_strings_len);
const total_len = blk: {
var sum: usize = separator.len * (slices.len - 1);
for (slices) |slice|
sum += slice.len;
break :blk sum;
};
const buf = try allocator.alloc(u8, total_len);
errdefer allocator.free(buf);
var buf_index: usize = 0;
comptime var string_i = 0;
inline while (true) {
const arg = ([]const u8)(strings[string_i]);
string_i += 1;
copy(u8, buf[buf_index..], arg);
buf_index += arg.len;
if (string_i >= strings.len) break;
if (buf[buf_index - 1] != sep) {
buf[buf_index] = sep;
buf_index += 1;
}
copy(u8, buf, slices[0]);
var buf_index: usize = slices[0].len;
for (slices[1..]) |slice| {
copy(u8, buf[buf_index..], separator);
buf_index += separator.len;
copy(u8, buf[buf_index..], slice);
buf_index += slice.len;
}
return allocator.shrink(u8, buf, buf_index);
// No need for shrink since buf is exactly the correct size.
return buf;
}
test "mem.join" {
assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c"));
assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a"));
var buf: [1024]u8 = undefined;
const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
assert(eql(u8, try join(a, ",", [][]const u8{ "a", "b", "c" }), "a,b,c"));
assert(eql(u8, try join(a, ",", [][]const u8{"a"}), "a"));
assert(eql(u8, try join(a, ",", [][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c"));
}
test "testStringEquality" {
@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" {
sub2[1] = 'X';
debug.assert(std.mem.eql(u8, a2, "abcXef"));
}
/// Round an address up to the nearest aligned address
pub fn alignForward(addr: usize, alignment: usize) usize {
return (addr + alignment - 1) & ~(alignment - 1);
}
test "std.mem.alignForward" {
debug.assertOrPanic(alignForward(1, 1) == 1);
debug.assertOrPanic(alignForward(2, 1) == 2);
debug.assertOrPanic(alignForward(1, 2) == 2);
debug.assertOrPanic(alignForward(2, 2) == 2);
debug.assertOrPanic(alignForward(3, 2) == 4);
debug.assertOrPanic(alignForward(4, 2) == 4);
debug.assertOrPanic(alignForward(7, 8) == 8);
debug.assertOrPanic(alignForward(8, 8) == 8);
debug.assertOrPanic(alignForward(9, 8) == 16);
debug.assertOrPanic(alignForward(15, 8) == 16);
debug.assertOrPanic(alignForward(16, 8) == 16);
debug.assertOrPanic(alignForward(17, 8) == 24);
}

View File

@ -574,7 +574,7 @@ pub const ChildProcess = struct {
// to match posix semantics
const app_name = x: {
if (self.cwd) |cwd| {
const resolved = try os.path.resolve(self.allocator, cwd, self.argv[0]);
const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] });
defer self.allocator.free(resolved);
break :x try cstr.addNullByte(self.allocator, resolved);
} else {
@ -597,10 +597,10 @@ pub const ChildProcess = struct {
var it = mem.tokenize(PATH, ";");
while (it.next()) |search_path| {
const joined_path = try os.path.join(self.allocator, search_path, app_name);
const joined_path = try os.path.join(self.allocator, [][]const u8{ search_path, app_name });
defer self.allocator.free(joined_path);
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
defer self.allocator.free(joined_path_w);
if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {
@ -610,6 +610,9 @@ pub const ChildProcess = struct {
} else {
return err;
}
} else {
// Every other error would have been returned earlier.
return error.FileNotFound;
}
};

View File

@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
error.OutOfMemory => return error.OutOfMemory,
};
defer allocator.free(global_dir);
return os.path.join(allocator, global_dir, appname);
return os.path.join(allocator, [][]const u8{ global_dir, appname });
},
os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
else => return error.AppDataDirUnavailable,
@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname });
},
builtin.Os.linux, builtin.Os.freebsd => {
const home_dir = os.getEnvPosix("HOME") orelse {
// TODO look in /etc/passwd
return error.AppDataDirUnavailable;
};
return os.path.join(allocator, home_dir, ".local", "share", appname);
return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname });
},
else => @compileError("Unsupported OS"),
}
@ -67,4 +67,3 @@ test "std.os.getAppDataDir" {
// We can't actually validate the result
_ = getAppDataDir(allocator, "zig") catch return;
}

View File

@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {
};
const os = @This();
comptime {
assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
}
test "std.os" {
_ = @import("child_process.zig");
_ = @import("darwin.zig");
@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {
return base;
}
const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
const ElfHeader = switch (@sizeOf(usize)) {
4 => std.elf.Elf32_Ehdr,
8 => std.elf.Elf64_Ehdr,
else => @compileError("Unsupported architecture"),
};
return phdr - @sizeOf(ElfHeader);
return phdr - @sizeOf(std.elf.Ehdr);
},
builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void {
/// already exists and is a directory.
/// TODO determine if we can remove the allocator requirement from this function
pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
const resolved_path = try path.resolve(allocator, full_path);
const resolved_path = try path.resolve(allocator, [][]const u8{full_path});
defer allocator.free(resolved_path);
var end_index: usize = resolved_path.len;
@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
switch (builtin.os) {
Os.linux => return readLink(out_buffer, "/proc/self/exe"),
Os.freebsd => {
var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1};
var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 };
var out_len: usize = out_buffer.len;
const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0));
if (err == 0 ) return mem.toSlice(u8, out_buffer);
if (err == 0) return mem.toSlice(u8, out_buffer);
return switch (err) {
posix.EFAULT => error.BadAdress,
posix.EPERM => error.PermissionDenied,
else => unexpectedErrorPosix(err),
};
},
Os.windows => {
var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
@ -2908,14 +2906,15 @@ pub const Thread = struct {
pub const Data = if (use_pthreads)
struct {
handle: Thread.Handle,
stack_addr: usize,
stack_len: usize,
mmap_addr: usize,
mmap_len: usize,
}
else switch (builtin.os) {
builtin.Os.linux => struct {
handle: Thread.Handle,
stack_addr: usize,
stack_len: usize,
mmap_addr: usize,
mmap_len: usize,
tls_end_addr: usize,
},
builtin.Os.windows => struct {
handle: Thread.Handle,
@ -2955,7 +2954,7 @@ pub const Thread = struct {
posix.EDEADLK => unreachable,
else => unreachable,
}
assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
} else switch (builtin.os) {
builtin.Os.linux => {
while (true) {
@ -2969,7 +2968,7 @@ pub const Thread = struct {
else => unreachable,
}
}
assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
},
builtin.Os.windows => {
assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{
Unexpected,
};
pub var linux_tls_phdr: ?*std.elf.Phdr = null;
pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is
/// caller must call wait on the returned thread
/// fn startFn(@typeOf(context)) T
/// where T is u8, noreturn, void, or !void
@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
const mmap_len = default_stack_size;
const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
var stack_end_offset: usize = undefined;
var thread_start_offset: usize = undefined;
var context_start_offset: usize = undefined;
var tls_start_offset: usize = undefined;
const mmap_len = blk: {
// First in memory will be the stack, which grows downwards.
var l: usize = mem.alignForward(default_stack_size, os.page_size);
stack_end_offset = l;
// Above the stack, so that it can be in the same mmap call, put the Thread object.
l = mem.alignForward(l, @alignOf(Thread));
thread_start_offset = l;
l += @sizeOf(Thread);
// Next, the Context object.
if (@sizeOf(Context) != 0) {
l = mem.alignForward(l, @alignOf(Context));
context_start_offset = l;
l += @sizeOf(Context);
}
// Finally, the Thread Local Storage, if any.
if (!Thread.use_pthreads) {
if (linux_tls_phdr) |tls_phdr| {
l = mem.alignForward(l, tls_phdr.p_align);
tls_start_offset = l;
l += tls_phdr.p_memsz;
}
}
break :blk l;
};
const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
thread_ptr.data.mmap_addr = mmap_addr;
thread_ptr.data.mmap_len = mmap_len;
var stack_end: usize = stack_addr + mmap_len;
var arg: usize = undefined;
if (@sizeOf(Context) != 0) {
stack_end -= @sizeOf(Context);
stack_end -= stack_end % @alignOf(Context);
assert(stack_end >= stack_addr);
const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
arg = mmap_addr + context_start_offset;
const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
context_ptr.* = context;
arg = stack_end;
}
stack_end -= @sizeOf(Thread);
stack_end -= stack_end % @alignOf(Thread);
assert(stack_end >= stack_addr);
const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end));
thread_ptr.data.stack_addr = stack_addr;
thread_ptr.data.stack_len = mmap_len;
if (builtin.os == builtin.Os.windows) {
// use windows API directly
@compileError("TODO support spawnThread for Windows");
} else if (Thread.use_pthreads) {
if (Thread.use_pthreads) {
// use pthreads
var attr: c.pthread_attr_t = undefined;
if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
defer assert(c.pthread_attr_destroy(&attr) == 0);
// align to page
stack_end -= stack_end % os.page_size;
assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
switch (err) {
@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
else => return unexpectedErrorPosix(@intCast(usize, err)),
}
} else if (builtin.os == builtin.Os.linux) {
// use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
const flags = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND | posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID | posix.CLONE_DETACHED;
const newtls: usize = 0;
const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
posix.CLONE_DETACHED;
var newtls: usize = undefined;
if (linux_tls_phdr) |tls_phdr| {
@memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz);
thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
flags |= posix.CLONE_SETTLS;
}
const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
const err = posix.getErrno(rc);
switch (err) {
0 => return thread_ptr,

View File

@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool {
}
}
/// This is different from mem.join in that the separator will not be repeated if
/// it is found at the end or beginning of a pair of consecutive paths.
fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 {
if (paths.len == 0) return (([*]u8)(undefined))[0..0];
const total_len = blk: {
var sum: usize = paths[0].len;
var i: usize = 1;
while (i < paths.len) : (i += 1) {
const prev_path = paths[i - 1];
const this_path = paths[i];
const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
const this_sep = (this_path.len != 0 and this_path[0] == separator);
sum += @boolToInt(!prev_sep and !this_sep);
sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len;
}
break :blk sum;
};
const buf = try allocator.alloc(u8, total_len);
errdefer allocator.free(buf);
mem.copy(u8, buf, paths[0]);
var buf_index: usize = paths[0].len;
var i: usize = 1;
while (i < paths.len) : (i += 1) {
const prev_path = paths[i - 1];
const this_path = paths[i];
const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
const this_sep = (this_path.len != 0 and this_path[0] == separator);
if (!prev_sep and !this_sep) {
buf[buf_index] = separator;
buf_index += 1;
}
const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path;
mem.copy(u8, buf[buf_index..], adjusted_path);
buf_index += adjusted_path.len;
}
// No need for shrink since buf is exactly the correct size.
return buf;
}
pub const join = if (is_windows) joinWindows else joinPosix;
/// Naively combines a series of paths with the native path seperator.
/// Allocates memory for the result, which must be freed by the caller.
pub fn join(allocator: *Allocator, paths: ...) ![]u8 {
if (is_windows) {
return joinWindows(allocator, paths);
} else {
return joinPosix(allocator, paths);
}
pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
return joinSep(allocator, sep_windows, paths);
}
pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 {
return mem.join(allocator, sep_windows, paths);
/// Naively combines a series of paths with the native path seperator.
/// Allocates memory for the result, which must be freed by the caller.
pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
return joinSep(allocator, sep_posix, paths);
}
pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 {
return mem.join(allocator, sep_posix, paths);
fn testJoinWindows(paths: []const []const u8, expected: []const u8) void {
var buf: [1024]u8 = undefined;
const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
const actual = joinWindows(a, paths) catch @panic("fail");
debug.assertOrPanic(mem.eql(u8, actual, expected));
}
fn testJoinPosix(paths: []const []const u8, expected: []const u8) void {
var buf: [1024]u8 = undefined;
const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
const actual = joinPosix(a, paths) catch @panic("fail");
debug.assertOrPanic(mem.eql(u8, actual, expected));
}
test "os.path.join" {
assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b", "c"), "c:\\a\\b\\c"));
assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b\\", "c"), "c:\\a\\b\\c"));
testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c");
assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c"));
assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c"));
testJoinWindows([][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c");
testJoinWindows([][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c");
assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig"), "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig"));
testJoinWindows(
[][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" },
"c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig",
);
assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b", "c"), "/a/b/c"));
assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b/", "c"), "/a/b/c"));
testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c");
testJoinPosix([][]const u8{ "/a/b/", "c" }, "/a/b/c");
assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c"));
assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/", "b/", "c"), "/a/b/c"));
testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c");
testJoinPosix([][]const u8{ "/a/", "b/", "c" }, "/a/b/c");
assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/home/andy/dev/zig/build/lib/zig/std", "io.zig"), "/home/andy/dev/zig/build/lib/zig/std/io.zig"));
testJoinPosix(
[][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" },
"/home/andy/dev/zig/build/lib/zig/std/io.zig",
);
testJoinPosix([][]const u8{ "a", "/c" }, "a/c");
testJoinPosix([][]const u8{ "a/", "/c" }, "a/c");
}
pub fn isAbsolute(path: []const u8) bool {
@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
return true;
}
/// Converts the command line arguments into a slice and calls `resolveSlice`.
pub fn resolve(allocator: *Allocator, args: ...) ![]u8 {
var paths: [args.len][]const u8 = undefined;
comptime var arg_i = 0;
inline while (arg_i < args.len) : (arg_i += 1) {
paths[arg_i] = args[arg_i];
}
return resolveSlice(allocator, paths);
}
/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
if (is_windows) {
return resolveWindows(allocator, paths);
} else {
@ -602,7 +655,10 @@ test "os.path.resolveWindows" {
const parsed_cwd = windowsParsePath(cwd);
{
const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });
const expected = try join(debug.global_allocator, parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig");
const expected = try join(debug.global_allocator, [][]const u8{
parsed_cwd.disk_designator,
"usr\\local\\lib\\zig\\std\\array_list.zig",
});
if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
}
@ -610,7 +666,10 @@ test "os.path.resolveWindows" {
}
{
const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" });
const expected = try join(debug.global_allocator, cwd, "usr\\local\\lib\\zig");
const expected = try join(debug.global_allocator, [][]const u8{
cwd,
"usr\\local\\lib\\zig",
});
if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
}

View File

@ -105,3 +105,19 @@ test "AtomicFile" {
try os.deleteFile(test_out_file);
}
test "thread local storage" {
if (builtin.single_threaded) return error.SkipZigTest;
const thread1 = try std.os.spawnThread({}, testTls);
const thread2 = try std.os.spawnThread({}, testTls);
testTls({});
thread1.wait();
thread2.wait();
}
threadlocal var x: i32 = 1234;
fn testTls(context: void) void {
if (x != 1234) @panic("bad start value");
x += 1;
if (x != 1235) @panic("bad end value");
}

View File

@ -49,7 +49,10 @@ pub const UNICODE = false;
pub const WCHAR = u16;
pub const WORD = u16;
pub const LARGE_INTEGER = i64;
pub const LONG = c_long;
pub const ULONG = u32;
pub const LONG = i32;
pub const ULONGLONG = u64;
pub const LONGLONG = i64;
pub const TRUE = 1;
pub const FALSE = 0;
@ -380,3 +383,17 @@ pub const COORD = extern struct {
};
pub const CREATE_UNICODE_ENVIRONMENT = 1024;
pub const TLS_OUT_OF_INDEXES = 4294967295;
pub const IMAGE_TLS_DIRECTORY = extern struct {
StartAddressOfRawData: usize,
EndAddressOfRawData: usize,
AddressOfIndex: usize,
AddressOfCallBacks: usize,
SizeOfZeroFill: u32,
Characteristics: u32,
};
pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void;

View File

@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(

36
std/os/windows/tls.zig Normal file
View File

@ -0,0 +1,36 @@
const std = @import("../../index.zig");
export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
export var _tls_start: u8 linksection(".tls") = 0;
export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
// TODO this is how I would like it to be expressed
// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
// why they do that.
//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
// .StartAddressOfRawData = @ptrToInt(&_tls_start),
// .EndAddressOfRawData = @ptrToInt(&_tls_end),
// .AddressOfIndex = @ptrToInt(&_tls_index),
// .AddressOfCallBacks = @ptrToInt(__xl_a),
// .SizeOfZeroFill = 0,
// .Characteristics = 0,
//};
// This is the workaround because we can't do @ptrToInt at comptime like that.
pub const IMAGE_TLS_DIRECTORY = extern struct {
StartAddressOfRawData: *c_void,
EndAddressOfRawData: *c_void,
AddressOfIndex: *c_void,
AddressOfCallBacks: *c_void,
SizeOfZeroFill: u32,
Characteristics: u32,
};
export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY {
.StartAddressOfRawData = &_tls_start,
.EndAddressOfRawData = &_tls_end,
.AddressOfIndex = &_tls_index,
.AddressOfCallBacks = &__xl_a,
.SizeOfZeroFill = 0,
.Characteristics = 0,
};

View File

@ -4,6 +4,7 @@
const root = @import("@root");
const std = @import("std");
const builtin = @import("builtin");
const assert = std.debug.assert;
var argc_ptr: [*]usize = undefined;
@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {
extern fn WinMainCRTStartup() noreturn {
@setAlignStack(16);
if (!builtin.single_threaded) {
_ = @import("../os/windows/tls.zig");
}
std.os.windows.ExitProcess(callMain());
}
@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {
while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
if (builtin.os == builtin.Os.linux) {
const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);
std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);
// Scan auxiliary vector.
const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
std.os.linux_elf_aux_maybe = auxv;
var i: usize = 0;
var at_phdr: usize = 0;
var at_phnum: usize = 0;
var at_phent: usize = 0;
while (auxv[i].a_un.a_val != 0) : (i += 1) {
switch (auxv[i].a_type) {
std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
else => {},
}
}
if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
}
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
@ -116,3 +133,41 @@ inline fn callMain() u8 {
else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
}
}
var tls_end_addr: usize = undefined;
const main_thread_tls_align = 32;
var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
var phdr_addr = at_phdr;
var n = at_phnum;
var base: usize = 0;
while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
// TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
switch (phdr.p_type) {
std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
std.elf.PT_TLS => std.os.linux_tls_phdr = phdr,
else => continue,
}
}
const tls_phdr = std.os.linux_tls_phdr orelse return;
std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
@memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz);
tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
linuxSetThreadArea(@ptrToInt(&tls_end_addr));
}
fn linuxSetThreadArea(addr: usize) void {
switch (builtin.arch) {
builtin.Arch.x86_64 => {
const ARCH_SET_FS = 0x1002;
const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
// acrh_prctl is documented to never fail
assert(rc == 0);
},
else => @compileError("Unsupported architecture"),
}
}

View File

@ -27,9 +27,9 @@ pub fn main() !void {
std.debug.warn("Expected second argument to be cache root directory path\n");
return error.InvalidArgs;
});
const zig_exe = try os.path.resolve(a, zig_exe_rel);
const zig_exe = try os.path.resolve(a, [][]const u8{zig_exe_rel});
const dir_path = try os.path.join(a, cache_root, "clitest");
const dir_path = try os.path.join(a, [][]const u8{ cache_root, "clitest" });
const TestFn = fn ([]const u8, []const u8) anyerror!void;
const test_fns = []TestFn{
testZigInitLib,
@ -99,8 +99,8 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
if (builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64) return;
const example_zig_path = try os.path.join(a, dir_path, "example.zig");
const example_s_path = try os.path.join(a, dir_path, "example.s");
const example_zig_path = try os.path.join(a, [][]const u8{ dir_path, "example.zig" });
const example_s_path = try os.path.join(a, [][]const u8{ dir_path, "example.s" });
try std.io.writeFile(example_zig_path,
\\// Type your code here, or load an example.

View File

@ -1,6 +1,25 @@
const tests = @import("tests.zig");
pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add(
"threadlocal qualifier on const",
\\threadlocal const x: i32 = 1234;
\\export fn entry() i32 {
\\ return x;
\\}
,
".tmp_source.zig:1:13: error: threadlocal variable cannot be constant",
);
cases.add(
"threadlocal qualifier on local variable",
\\export fn entry() void {
\\ threadlocal var x: i32 = 1234;
\\}
,
".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal",
);
cases.add(
"@bitCast same size but bit count mismatch",
\\export fn entry(byte: u8) void {

View File

@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {
fn getNull() ?*i32 {
return null;
}
test "thread local variable" {
const S = struct {
threadlocal var t: i32 = 1234;
};
S.t += 1;
assertOrPanic(S.t == 1235);
}

View File

@ -1,20 +1,17 @@
const std = @import("std");
const mem = std.mem;
const assertOrPanic = std.debug.assertOrPanic;
test "implicit array to vector and vector to array" {
test "vector wrap operators" {
const S = struct {
fn doTheTest() void {
var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
v +%= x;
const result: [4]i32 = v;
assertOrPanic(result[0] == 11);
assertOrPanic(result[1] == 22);
assertOrPanic(result[2] == 33);
assertOrPanic(result[3] == 44);
const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));
assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));
assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));
}
};
S.doTheTest();
comptime S.doTheTest();
}

View File

@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
if (link_libc) {
these_tests.linkSystemLibrary("c");
}
if (mem.eql(u8, name, "std")) {
these_tests.overrideStdDir("std");
}
step.dependOn(&these_tests.step);
}
}
@ -436,7 +439,10 @@ pub const CompareOutputContext = struct {
pub fn addCase(self: *CompareOutputContext, case: TestCase) void {
const b = self.b;
const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable;
const root_src = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, case.sources.items[0].filename },
) catch unreachable;
switch (case.special) {
Special.Asm => {
@ -449,7 +455,10 @@ pub const CompareOutputContext = struct {
exe.addAssemblyFile(root_src);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
const expanded_src_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
@ -473,7 +482,10 @@ pub const CompareOutputContext = struct {
}
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
const expanded_src_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
@ -496,7 +508,10 @@ pub const CompareOutputContext = struct {
}
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
const expanded_src_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
exe.step.dependOn(&write_src.step);
}
@ -569,8 +584,14 @@ pub const CompileErrorContext = struct {
const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
const b = self.context.b;
const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable;
const obj_path = os.path.join(b.allocator, b.cache_root, "test.o") catch unreachable;
const root_src = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, self.case.sources.items[0].filename },
) catch unreachable;
const obj_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, "test.o" },
) catch unreachable;
var zig_args = ArrayList([]const u8).init(b.allocator);
zig_args.append(b.zig_exe) catch unreachable;
@ -718,7 +739,10 @@ pub const CompileErrorContext = struct {
self.step.dependOn(&compile_and_cmp_errors.step);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
const expanded_src_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
compile_and_cmp_errors.step.dependOn(&write_src.step);
}
@ -849,7 +873,10 @@ pub const TranslateCContext = struct {
const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
const b = self.context.b;
const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable;
const root_src = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, self.case.sources.items[0].filename },
) catch unreachable;
var zig_args = ArrayList([]const u8).init(b.allocator);
zig_args.append(b.zig_exe) catch unreachable;
@ -983,7 +1010,10 @@ pub const TranslateCContext = struct {
self.step.dependOn(&translate_c_and_cmp.step);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
const expanded_src_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
translate_c_and_cmp.step.dependOn(&write_src.step);
}
@ -1098,7 +1128,10 @@ pub const GenHContext = struct {
pub fn addCase(self: *GenHContext, case: *const TestCase) void {
const b = self.b;
const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable;
const root_src = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, case.sources.items[0].filename },
) catch unreachable;
const mode = builtin.Mode.Debug;
const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable;
@ -1110,7 +1143,10 @@ pub const GenHContext = struct {
obj.setBuildMode(mode);
for (case.sources.toSliceConst()) |src_file| {
const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
const expanded_src_path = os.path.join(
b.allocator,
[][]const u8{ b.cache_root, src_file.filename },
) catch unreachable;
const write_src = b.addWriteFile(expanded_src_path, src_file.source);
obj.step.dependOn(&write_src.step);
}