authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-07 12:21:20-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-02-07 12:21:20-05:00
loga94304d3e4b9220d2b22501eb2666e5aa89df236
treea3b85b53581853b62dabf6f763feca099e4fee84
parentd974afde1d366a28f1b07bff6ebfb5c5756d3b61
parent2b2bf53a49616192e2b2bdf40b88400964ff1500
signature Commit is signed but in an unrecognized format.

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


45 files changed, 779 insertions(+), 345 deletions(-)

CMakeLists.txt+1
......@@ -596,6 +596,7 @@ set(ZIG_STD_FILES
596596 "os/windows/ntdll.zig"
597597 "os/windows/ole32.zig"
598598 "os/windows/shell32.zig"
599 "os/windows/tls.zig"
599600 "os/windows/util.zig"
600601 "os/zen.zig"
601602 "pdb.zig"
build.zig+27-8
......@@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void {
1616 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
1717
1818 const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe);
19 const langref_out_path = os.path.join(b.allocator, b.cache_root, "langref.html") catch unreachable;
19 const langref_out_path = os.path.join(
20 b.allocator,
21 [][]const u8{ b.cache_root, "langref.html" },
22 ) catch unreachable;
2023 var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{
2124 docgen_exe.getOutputPath(),
2225 rel_zig_exe,
......@@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
125128 for (dep.libdirs.toSliceConst()) |lib_dir| {
126129 lib_exe_obj.addLibPath(lib_dir);
127130 }
128 const lib_dir = os.path.join(b.allocator, dep.prefix, "lib") catch unreachable;
131 const lib_dir = os.path.join(
132 b.allocator,
133 [][]const u8{ dep.prefix, "lib" },
134 ) catch unreachable;
129135 for (dep.system_libs.toSliceConst()) |lib| {
130136 const static_bare_name = if (mem.eql(u8, lib, "curses"))
131137 ([]const u8)("libncurses.a")
132138 else
133139 b.fmt("lib{}.a", lib);
134 const static_lib_name = os.path.join(b.allocator, lib_dir, static_bare_name) catch unreachable;
140 const static_lib_name = os.path.join(
141 b.allocator,
142 [][]const u8{ lib_dir, static_bare_name },
143 ) catch unreachable;
135144 const have_static = fileExists(static_lib_name) catch unreachable;
136145 if (have_static) {
137146 lib_exe_obj.addObjectFile(static_lib_name);
......@@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool {
159168
160169fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
161170 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";
162 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);
171 lib_exe_obj.addObjectFile(os.path.join(b.allocator, [][]const u8{
172 cmake_binary_dir,
173 "zig_cpp",
174 b.fmt("{}{}{}", lib_prefix, lib_name, lib_exe_obj.target.libFileExt()),
175 }) catch unreachable);
163176}
164177
165178const LibraryDep = struct {
......@@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
235248pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
236249 var it = mem.tokenize(stdlib_files, ";");
237250 while (it.next()) |stdlib_file| {
238 const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable;
239 const dest_path = os.path.join(b.allocator, "lib", "zig", "std", stdlib_file) catch unreachable;
251 const src_path = os.path.join(b.allocator, [][]const u8{ "std", stdlib_file }) catch unreachable;
252 const dest_path = os.path.join(
253 b.allocator,
254 [][]const u8{ "lib", "zig", "std", stdlib_file },
255 ) catch unreachable;
240256 b.installFile(src_path, dest_path);
241257 }
242258}
......@@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
244260pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
245261 var it = mem.tokenize(c_header_files, ";");
246262 while (it.next()) |c_header_file| {
247 const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable;
248 const dest_path = os.path.join(b.allocator, "lib", "zig", "include", c_header_file) catch unreachable;
263 const src_path = os.path.join(b.allocator, [][]const u8{ "c_headers", c_header_file }) catch unreachable;
264 const dest_path = os.path.join(
265 b.allocator,
266 [][]const u8{ "lib", "zig", "include", c_header_file },
267 ) catch unreachable;
249268 b.installFile(src_path, dest_path);
250269 }
251270}
doc/docgen.zig+20-5
......@@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
990990 try tokenizeAndPrint(tokenizer, out, code.source_token);
991991 try out.write("</pre>");
992992 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);
993 const tmp_source_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_ext);
993 const tmp_source_file_name = try os.path.join(
994 allocator,
995 [][]const u8{ tmp_dir_name, name_plus_ext },
996 );
994997 try io.writeFile(tmp_source_file_name, trimmed_raw_source);
995998
996999 switch (code.id) {
9971000 Code.Id.Exe => |expected_outcome| {
9981001 const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);
999 const tmp_bin_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_bin_ext);
1002 const tmp_bin_file_name = try os.path.join(
1003 allocator,
1004 [][]const u8{ tmp_dir_name, name_plus_bin_ext },
1005 );
10001006 var build_args = std.ArrayList([]const u8).init(allocator);
10011007 defer build_args.deinit();
10021008 try build_args.appendSlice([][]const u8{
......@@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
10241030 }
10251031 for (code.link_objects) |link_object| {
10261032 const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);
1027 const full_path_object = try os.path.join(allocator, tmp_dir_name, name_with_ext);
1033 const full_path_object = try os.path.join(
1034 allocator,
1035 [][]const u8{ tmp_dir_name, name_with_ext },
1036 );
10281037 try build_args.append("--object");
10291038 try build_args.append(full_path_object);
10301039 try out.print(" --object {}", name_with_ext);
......@@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
12161225 },
12171226 Code.Id.Obj => |maybe_error_match| {
12181227 const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);
1219 const tmp_obj_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_obj_ext);
1228 const tmp_obj_file_name = try os.path.join(
1229 allocator,
1230 [][]const u8{ tmp_dir_name, name_plus_obj_ext },
1231 );
12201232 var build_args = std.ArrayList([]const u8).init(allocator);
12211233 defer build_args.deinit();
12221234
12231235 const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name);
1224 const output_h_file_name = try os.path.join(allocator, tmp_dir_name, name_plus_h_ext);
1236 const output_h_file_name = try os.path.join(
1237 allocator,
1238 [][]const u8{ tmp_dir_name, name_plus_h_ext },
1239 );
12251240
12261241 try build_args.appendSlice([][]const u8{
12271242 zig_exe,
doc/langref.html.in+15-6
......@@ -3192,7 +3192,16 @@ fn foo() void { }
31923192 {#code_end#}
31933193 {#header_open|Pass-by-value Parameters#}
31943194 <p>
3195 In Zig, structs, unions, and enums with payloads can be passed directly to a function:
3195 Primitive types such as {#link|Integers#} and {#link|Floats#} passed as parameters
3196 are copied, and then the copy is available in the function body. This is called "passing by value".
3197 Copying a primitive type is essentially free and typically involves nothing more than
3198 setting a register.
3199 </p>
3200 <p>
3201 Structs, unions, and arrays can sometimes be more efficiently passed as a reference, since a copy
3202 could be arbitrarily expensive depending on the size. When these types are passed
3203 as parameters, Zig may choose to copy and pass by value, or pass by reference, whichever way
3204 Zig decides will be faster. This is made possible, in part, by the fact that parameters are immutable.
31963205 </p>
31973206 {#code_begin|test#}
31983207const Point = struct {
......@@ -3201,20 +3210,20 @@ const Point = struct {
32013210};
32023211
32033212fn foo(point: Point) i32 {
3213 // Here, `point` could be a reference, or a copy. The function body
3214 // can ignore the difference and treat it as a value. Be very careful
3215 // taking the address of the parameter - it should be treated as if
3216 // the address will become invalid when the function returns.
32043217 return point.x + point.y;
32053218}
32063219
32073220const assert = @import("std").debug.assert;
32083221
3209test "pass aggregate type by non-copy value to function" {
3222test "pass struct to function" {
32103223 assert(foo(Point{ .x = 1, .y = 2 }) == 3);
32113224}
32123225 {#code_end#}
32133226 <p>
3214 In this case, the value may be passed by reference, or by value, whichever way
3215 Zig decides will be faster.
3216 </p>
3217 <p>
32183227 For extern functions, Zig follows the C ABI for passing structs and unions by value.
32193228 </p>
32203229 {#header_close#}
doc/targets.md deleted-15
......@@ -1,15 +0,0 @@
1# How to Add Support For More Targets
2
3Create bootstrap code in std/bootstrap.zig and add conditional compilation
4logic. This code is responsible for the real executable entry point, calling
5main() and making the exit syscall when main returns.
6
7How to pass a byvalue struct parameter in the C calling convention is
8target-specific. Add logic for how to do function prototypes and function calls
9for the target when an exported or external function has a byvalue struct.
10
11Write the target-specific code in the standard library.
12
13Update the C integer types to be the correct size for the target.
14
15Make sure that `c_longdouble` codegens the correct floating point value.
src-self-hosted/compilation.zig+3-3
......@@ -487,7 +487,7 @@ pub const Compilation = struct {
487487 comp.name = try Buffer.init(comp.arena(), name);
488488 comp.llvm_triple = try target.getTriple(comp.arena());
489489 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
490 comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
490 comp.zig_std_dir = try std.os.path.join(comp.arena(), [][]const u8{ zig_lib_dir, "std" });
491491
492492 const opt_level = switch (build_mode) {
493493 builtin.Mode.Debug => llvm.CodeGenLevelNone,
......@@ -1198,7 +1198,7 @@ pub const Compilation = struct {
11981198 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
11991199 defer self.gpa().free(file_name);
12001200
1201 const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]);
1201 const full_path = try os.path.join(self.gpa(), [][]const u8{ tmp_dir, file_name[0..] });
12021202 errdefer self.gpa().free(full_path);
12031203
12041204 return Buffer.fromOwnedSlice(self.gpa(), full_path);
......@@ -1219,7 +1219,7 @@ pub const Compilation = struct {
12191219 const zig_dir_path = try getZigDir(self.gpa());
12201220 defer self.gpa().free(zig_dir_path);
12211221
1222 const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]);
1222 const tmp_dir = try os.path.join(self.arena(), [][]const u8{ zig_dir_path, comp_dir_name[0..] });
12231223 try os.makePath(self.gpa(), tmp_dir);
12241224 return tmp_dir;
12251225 }
src-self-hosted/introspect.zig+2-2
......@@ -8,10 +8,10 @@ const warn = std.debug.warn;
88
99/// Caller must free result
1010pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {
11 const test_zig_dir = try os.path.join(allocator, test_path, "lib", "zig");
11 const test_zig_dir = try os.path.join(allocator, [][]const u8{ test_path, "lib", "zig" });
1212 errdefer allocator.free(test_zig_dir);
1313
14 const test_index_file = try os.path.join(allocator, test_zig_dir, "std", "index.zig");
14 const test_index_file = try os.path.join(allocator, [][]const u8{ test_zig_dir, "std", "index.zig" });
1515 defer allocator.free(test_index_file);
1616
1717 var file = try os.File.openRead(test_index_file);
src-self-hosted/libc_installation.zig+13-4
......@@ -230,7 +230,7 @@ pub const LibCInstallation = struct {
230230 while (path_i < search_paths.len) : (path_i += 1) {
231231 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
232232 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
233 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
233 const stdlib_path = try std.os.path.join(loop.allocator, [][]const u8{ search_path, "stdlib.h" });
234234 defer loop.allocator.free(stdlib_path);
235235
236236 if (try fileExists(stdlib_path)) {
......@@ -254,7 +254,10 @@ pub const LibCInstallation = struct {
254254 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
255255 try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
256256
257 const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
257 const stdlib_path = try std.os.path.join(
258 loop.allocator,
259 [][]const u8{ result_buf.toSliceConst(), "stdlib.h" },
260 );
258261 defer loop.allocator.free(stdlib_path);
259262
260263 if (try fileExists(stdlib_path)) {
......@@ -283,7 +286,10 @@ pub const LibCInstallation = struct {
283286 builtin.Arch.aarch64v8 => try stream.write("arm"),
284287 else => return error.UnsupportedArchitecture,
285288 }
286 const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
289 const ucrt_lib_path = try std.os.path.join(
290 loop.allocator,
291 [][]const u8{ result_buf.toSliceConst(), "ucrt.lib" },
292 );
287293 defer loop.allocator.free(ucrt_lib_path);
288294 if (try fileExists(ucrt_lib_path)) {
289295 self.lib_dir = result_buf.toOwnedSlice();
......@@ -358,7 +364,10 @@ pub const LibCInstallation = struct {
358364 builtin.Arch.aarch64v8 => try stream.write("arm\\"),
359365 else => return error.UnsupportedArchitecture,
360366 }
361 const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
367 const kernel32_path = try std.os.path.join(
368 loop.allocator,
369 [][]const u8{ result_buf.toSliceConst(), "kernel32.lib" },
370 );
362371 defer loop.allocator.free(kernel32_path);
363372 if (try fileExists(kernel32_path)) {
364373 self.kernel32_lib_dir = result_buf.toOwnedSlice();
src-self-hosted/link.zig+1-1
......@@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
315315}
316316
317317fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
318 const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
318 const full_path = try std.os.path.join(&ctx.arena.allocator, [][]const u8{ dirname, basename });
319319 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
320320 try ctx.args.append(full_path_with_null.ptr);
321321}
src-self-hosted/main.zig+1-1
......@@ -757,7 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
757757 var group = event.Group(FmtError!void).init(fmt.loop);
758758 while (try dir.next()) |entry| {
759759 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {
760 const full_path = try os.path.join(fmt.loop.allocator, file_path, entry.name);
760 const full_path = try os.path.join(fmt.loop.allocator, [][]const u8{ file_path, entry.name });
761761 try group.call(fmtPath, fmt, full_path, check_mode);
762762 }
763763 }
src-self-hosted/test.zig+2-2
......@@ -87,7 +87,7 @@ pub const TestContext = struct {
8787 ) !void {
8888 var file_index_buf: [20]u8 = undefined;
8989 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
90 const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
90 const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 });
9191
9292 if (std.os.path.dirname(file1_path)) |dirname| {
9393 try std.os.makePath(allocator, dirname);
......@@ -120,7 +120,7 @@ pub const TestContext = struct {
120120 ) !void {
121121 var file_index_buf: [20]u8 = undefined;
122122 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
123 const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
123 const file1_path = try std.os.path.join(allocator, [][]const u8{ tmp_dir_name, file_index, file1 });
124124
125125 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
126126 if (std.os.path.dirname(file1_path)) |dirname| {
src/all_types.hpp+8-5
......@@ -544,12 +544,7 @@ struct AstNodeDefer {
544544};
545545
546546struct AstNodeVariableDeclaration {
547 VisibMod visib_mod;
548547 Buf *symbol;
549 bool is_const;
550 bool is_comptime;
551 bool is_export;
552 bool is_extern;
553548 // one or both of type and expr will be non null
554549 AstNode *type;
555550 AstNode *expr;
......@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
559554 AstNode *align_expr;
560555 // populated if the "section(S)" is present
561556 AstNode *section_expr;
557 Token *threadlocal_tok;
558
559 VisibMod visib_mod;
560 bool is_const;
561 bool is_comptime;
562 bool is_export;
563 bool is_extern;
562564};
563565
564566struct AstNodeTestDecl {
......@@ -1873,6 +1875,7 @@ struct ZigVar {
18731875 bool shadowable;
18741876 bool src_is_const;
18751877 bool gen_is_const;
1878 bool is_thread_local;
18761879};
18771880
18781881struct ErrorTableEntry {
src/analyze.cpp+45-24
......@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
2828static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
2929static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
3030
31ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
32 if (node->owner->c_import_node != nullptr) {
33 // if this happens, then translate_c generated code that
34 // failed semantic analysis, which isn't supposed to happen
35 ErrorMsg *err = add_node_error(g, node->owner->c_import_node,
36 buf_sprintf("compiler bug: @cImport generated invalid zig code"));
37
38 add_error_note(g, err, node, msg);
39
40 g->errors.append(err);
41 return err;
42 }
43
44 ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
45 node->owner->source_code, node->owner->line_offsets, msg);
46
47 g->errors.append(err);
48 return err;
49}
50
51ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
52 if (node->owner->c_import_node != nullptr) {
31static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
32 Buf *msg)
33{
34 if (owner->c_import_node != nullptr) {
5335 // if this happens, then translate_c generated code that
5436 // failed semantic analysis, which isn't supposed to happen
5537
......@@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
6446 return note;
6547 }
6648
67 ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,
68 node->owner->source_code, node->owner->line_offsets, msg);
49 ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
50 owner->source_code, owner->line_offsets, msg);
6951
7052 err_msg_add_note(parent_msg, err);
7153 return err;
7254}
7355
56ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg) {
57 if (owner->c_import_node != nullptr) {
58 // if this happens, then translate_c generated code that
59 // failed semantic analysis, which isn't supposed to happen
60 ErrorMsg *err = add_node_error(g, owner->c_import_node,
61 buf_sprintf("compiler bug: @cImport generated invalid zig code"));
62
63 add_error_note_token(g, err, owner, token, msg);
64
65 g->errors.append(err);
66 return err;
67 }
68 ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
69 owner->source_code, owner->line_offsets, msg);
70
71 g->errors.append(err);
72 return err;
73}
74
75ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {
76 Token fake_token;
77 fake_token.start_line = node->line;
78 fake_token.start_column = node->column;
79 return add_token_error(g, node->owner, &fake_token, msg);
80}
81
82ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg) {
83 Token fake_token;
84 fake_token.start_line = node->line;
85 fake_token.start_column = node->column;
86 return add_error_note_token(g, parent_msg, node->owner, &fake_token, msg);
87}
88
7489ZigType *new_type_table_entry(ZigTypeId id) {
7590 ZigType *entry = allocate<ZigType>(1);
7691 entry->id = id;
......@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
36683683 bool is_const = var_decl->is_const;
36693684 bool is_extern = var_decl->is_extern;
36703685 bool is_export = var_decl->is_export;
3686 bool is_thread_local = var_decl->threadlocal_tok != nullptr;
36713687
36723688 ZigType *explicit_type = nullptr;
36733689 if (var_decl->type) {
......@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
37273743 tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
37283744 is_const, init_val, &tld_var->base, type);
37293745 tld_var->var->linkage = linkage;
3746 tld_var->var->is_thread_local = is_thread_local;
37303747
37313748 if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
37323749 tld_var->var->var_type = g->builtin_types.entry_invalid;
......@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
37473764 }
37483765 }
37493766
3767 if (is_thread_local && is_const) {
3768 add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
3769 }
3770
37503771 g->global_vars.append(tld_var);
37513772}
37523773
src/analyze.hpp+1
......@@ -12,6 +12,7 @@
1212
1313void semantic_analyze(CodeGen *g);
1414ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
15ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
1516ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
1617ZigType *new_type_table_entry(ZigTypeId id);
1718ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);
src/ast_render.cpp+6-1
......@@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {
132132 return is_const ? "const" : "var";
133133}
134134
135static const char *thread_local_string(Token *tok) {
136 return (tok == nullptr) ? "" : "threadlocal ";
137}
138
135139const char *container_string(ContainerKind kind) {
136140 switch (kind) {
137141 case ContainerKindEnum: return "enum";
......@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
554558 {
555559 const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
556560 const char *extern_str = extern_string(node->data.variable_declaration.is_extern);
561 const char *thread_local_str = thread_local_string(node->data.variable_declaration.threadlocal_tok);
557562 const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);
558 fprintf(ar->f, "%s%s%s ", pub_str, extern_str, const_or_var);
563 fprintf(ar->f, "%s%s%s%s ", pub_str, extern_str, thread_local_str, const_or_var);
559564 print_symbol(ar, node->data.variable_declaration.symbol);
560565
561566 if (node->data.variable_declaration.type) {
src/codegen.cpp+80-77
......@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {
8888};
8989
9090CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
91 Buf *zig_lib_dir)
91 Buf *zig_lib_dir, Buf *override_std_dir)
9292{
9393 CodeGen *g = allocate<CodeGen>(1);
9494
......@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
9696
9797 g->zig_lib_dir = zig_lib_dir;
9898
99 g->zig_std_dir = buf_alloc();
100 os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
99 if (override_std_dir == nullptr) {
100 g->zig_std_dir = buf_alloc();
101 os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);
102 } else {
103 g->zig_std_dir = override_std_dir;
104 }
101105
102106 g->zig_c_headers_dir = buf_alloc();
103107 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
25822586
25832587}
25842588
2589typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
2590
25852591static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
25862592 IrInstructionBinOp *bin_op_instruction)
25872593{
......@@ -2640,50 +2646,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
26402646 } else {
26412647 zig_unreachable();
26422648 }
2649 case IrBinOpMult:
2650 case IrBinOpMultWrap:
26432651 case IrBinOpAdd:
26442652 case IrBinOpAddWrap:
2653 case IrBinOpSub:
2654 case IrBinOpSubWrap: {
2655 // These are lookup table using the AddSubMul enum as the lookup.
2656 // If AddSubMul ever changes, then these tables will be out of
2657 // date.
2658 static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul };
2659 static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul };
2660 static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul };
2661 static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul };
2662
2663 bool is_vector = type_entry->id == ZigTypeIdVector;
2664 bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap);
2665 AddSubMul add_sub_mul =
2666 op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd :
2667 op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub :
2668 AddSubMulMul;
2669
2670 // The code that is generated for vectors and scalars are the same,
2671 // so we can just set type_entry to the vectors elem_type an avoid
2672 // a lot of repeated code.
2673 if (is_vector)
2674 type_entry = type_entry->data.vector.elem_type;
2675
26452676 if (type_entry->id == ZigTypeIdPointer) {
26462677 assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
2678 LLVMValueRef subscript_value;
2679 if (is_vector)
2680 zig_panic("TODO: Implement vector operations on pointers.");
2681
2682 switch (add_sub_mul) {
2683 case AddSubMulAdd:
2684 subscript_value = op2_value;
2685 break;
2686 case AddSubMulSub:
2687 subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
2688 break;
2689 case AddSubMulMul:
2690 zig_unreachable();
2691 }
2692
26472693 // TODO runtime safety
2648 return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, "");
2694 return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
26492695 } else if (type_entry->id == ZigTypeIdFloat) {
26502696 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
2651 return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
2697 return float_op[add_sub_mul](g->builder, op1_value, op2_value, "");
26522698 } else if (type_entry->id == ZigTypeIdInt) {
2653 bool is_wrapping = (op_id == IrBinOpAddWrap);
26542699 if (is_wrapping) {
2655 return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
2700 return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, "");
26562701 } else if (want_runtime_safety) {
2657 return gen_overflow_op(g, type_entry, AddSubMulAdd, op1_value, op2_value);
2702 if (is_vector)
2703 zig_panic("TODO: Implement runtime safety vector operations.");
2704 return gen_overflow_op(g, type_entry, add_sub_mul, op1_value, op2_value);
26582705 } else if (type_entry->data.integral.is_signed) {
2659 return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
2706 return signed_op[add_sub_mul](g->builder, op1_value, op2_value, "");
26602707 } else {
2661 return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
2662 }
2663 } else if (type_entry->id == ZigTypeIdVector) {
2664 ZigType *elem_type = type_entry->data.vector.elem_type;
2665 if (elem_type->id == ZigTypeIdFloat) {
2666 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
2667 return LLVMBuildFAdd(g->builder, op1_value, op2_value, "");
2668 } else if (elem_type->id == ZigTypeIdPointer) {
2669 zig_panic("TODO codegen for pointers in vectors");
2670 } else if (elem_type->id == ZigTypeIdInt) {
2671 bool is_wrapping = (op_id == IrBinOpAddWrap);
2672 if (is_wrapping) {
2673 return LLVMBuildAdd(g->builder, op1_value, op2_value, "");
2674 } else if (want_runtime_safety) {
2675 zig_panic("TODO runtime safety for vector integer addition");
2676 } else if (elem_type->data.integral.is_signed) {
2677 return LLVMBuildNSWAdd(g->builder, op1_value, op2_value, "");
2678 } else {
2679 return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");
2680 }
2681 } else {
2682 zig_unreachable();
2708 return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, "");
26832709 }
26842710 } else {
26852711 zig_unreachable();
26862712 }
2713 }
26872714 case IrBinOpBinOr:
26882715 return LLVMBuildOr(g->builder, op1_value, op2_value, "");
26892716 case IrBinOpBinXor:
......@@ -2728,49 +2755,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
27282755 return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
27292756 }
27302757 }
2731 case IrBinOpSub:
2732 case IrBinOpSubWrap:
2733 if (type_entry->id == ZigTypeIdPointer) {
2734 assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);
2735 // TODO runtime safety
2736 LLVMValueRef subscript_value = LLVMBuildNeg(g->builder, op2_value, "");
2737 return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
2738 } else if (type_entry->id == ZigTypeIdFloat) {
2739 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
2740 return LLVMBuildFSub(g->builder, op1_value, op2_value, "");
2741 } else if (type_entry->id == ZigTypeIdInt) {
2742 bool is_wrapping = (op_id == IrBinOpSubWrap);
2743 if (is_wrapping) {
2744 return LLVMBuildSub(g->builder, op1_value, op2_value, "");
2745 } else if (want_runtime_safety) {
2746 return gen_overflow_op(g, type_entry, AddSubMulSub, op1_value, op2_value);
2747 } else if (type_entry->data.integral.is_signed) {
2748 return LLVMBuildNSWSub(g->builder, op1_value, op2_value, "");
2749 } else {
2750 return LLVMBuildNUWSub(g->builder, op1_value, op2_value, "");
2751 }
2752 } else {
2753 zig_unreachable();
2754 }
2755 case IrBinOpMult:
2756 case IrBinOpMultWrap:
2757 if (type_entry->id == ZigTypeIdFloat) {
2758 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));
2759 return LLVMBuildFMul(g->builder, op1_value, op2_value, "");
2760 } else if (type_entry->id == ZigTypeIdInt) {
2761 bool is_wrapping = (op_id == IrBinOpMultWrap);
2762 if (is_wrapping) {
2763 return LLVMBuildMul(g->builder, op1_value, op2_value, "");
2764 } else if (want_runtime_safety) {
2765 return gen_overflow_op(g, type_entry, AddSubMulMul, op1_value, op2_value);
2766 } else if (type_entry->data.integral.is_signed) {
2767 return LLVMBuildNSWMul(g->builder, op1_value, op2_value, "");
2768 } else {
2769 return LLVMBuildNUWMul(g->builder, op1_value, op2_value, "");
2770 }
2771 } else {
2772 zig_unreachable();
2773 }
27742758 case IrBinOpDivUnspecified:
27752759 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
27762760 op1_value, op2_value, type_entry, DivKindFloat);
......@@ -6361,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) {
63616345 report_errors_and_maybe_exit(g);
63626346}
63636347
6348static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) {
6349 if (var->is_thread_local && !g->is_single_threaded) {
6350 LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
6351 }
6352}
6353
63646354static void do_code_gen(CodeGen *g) {
63656355 assert(!g->errors.length);
63666356
......@@ -6445,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {
64456435 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
64466436 LLVMSetAlignment(global_value, var->align_bytes);
64476437 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6438 set_global_tls(g, var, global_value);
64486439 }
64496440 } else {
64506441 bool exported = (var->linkage == VarLinkageExport);
......@@ -6470,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {
64706461 }
64716462
64726463 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6464 set_global_tls(g, var, global_value);
64736465 }
64746466
64756467 var->value_ref = global_value;
......@@ -7520,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
75207512 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
75217513 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
75227514 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
7515 g->std_package->package_table.put(buf_create_from_str("std"), g->std_package);
75237516 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
75247517 scan_import(g, g->compile_var_import);
75257518
......@@ -7560,7 +7553,13 @@ static void init(CodeGen *g) {
75607553 LLVMTargetRef target_ref;
75617554 char *err_msg = nullptr;
75627555 if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) {
7563 zig_panic("unable to create target based on: %s", buf_ptr(&g->triple_str));
7556 fprintf(stderr,
7557 "Zig is expecting LLVM to understand this target: '%s'\n"
7558 "However LLVM responded with: \"%s\"\n"
7559 "Zig is unable to continue. This is a bug in Zig:\n"
7560 "https://github.com/ziglang/zig/issues/438\n"
7561 , buf_ptr(&g->triple_str), err_msg);
7562 exit(1);
75647563 }
75657564
75667565 bool is_optimized = g->build_mode != BuildModeDebug;
......@@ -8349,8 +8348,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {
83498348 if (!entry)
83508349 break;
83518350
8352 cache_buf(ch, entry->key);
8353 add_cache_pkg(g, ch, entry->value);
8351 // TODO: I think we need a more sophisticated detection of
8352 // packages we have already seen
8353 if (entry->value != pkg) {
8354 cache_buf(ch, entry->key);
8355 add_cache_pkg(g, ch, entry->value);
8356 }
83548357 }
83558358}
83568359
src/codegen.hpp+1-1
......@@ -15,7 +15,7 @@
1515#include <stdio.h>
1616
1717CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
18 Buf *zig_lib_dir);
18 Buf *zig_lib_dir, Buf *override_std_dir);
1919
2020void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
2121void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
src/ir.cpp+4
......@@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
52045204 add_node_error(irb->codegen, variable_declaration->section_expr,
52055205 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
52065206 }
5207 if (variable_declaration->threadlocal_tok != nullptr) {
5208 add_token_error(irb->codegen, node->owner, variable_declaration->threadlocal_tok,
5209 buf_sprintf("function-local variable '%s' cannot be threadlocal", buf_ptr(variable_declaration->symbol)));
5210 }
52075211
52085212 // Temporarily set the name of the IrExecutable to the VariableDeclaration
52095213 // so that the struct or enum from the init expression inherits the name.
src/link.cpp+1-1
......@@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
4242 }
4343
4444 CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,
45 parent_gen->build_mode, parent_gen->zig_lib_dir);
45 parent_gen->build_mode, parent_gen->zig_lib_dir, parent_gen->zig_std_dir);
4646
4747 child_gen->out_h_path = nullptr;
4848 child_gen->verbose_tokenize = parent_gen->verbose_tokenize;
src/main.cpp+9-3
......@@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) {
7474 " -dirafter [dir] same as -isystem but do it last\n"
7575 " -isystem [dir] add additional search path for other .h files\n"
7676 " -mllvm [arg] forward an arg to LLVM's option processing\n"
77 " --override-std-dir [arg] use an alternate Zig standard library\n"
7778 "\n"
7879 "Link Options:\n"
7980 " --dynamic-linker [path] set the path to ld.so\n"
......@@ -395,6 +396,7 @@ int main(int argc, char **argv) {
395396 bool system_linker_hack = false;
396397 TargetSubsystem subsystem = TargetSubsystemAuto;
397398 bool is_single_threaded = false;
399 Buf *override_std_dir = nullptr;
398400
399401 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
400402 Buf zig_exe_path_buf = BUF_INIT;
......@@ -430,7 +432,8 @@ int main(int argc, char **argv) {
430432 Buf *build_runner_path = buf_alloc();
431433 os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);
432434
433 CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir());
435 CodeGen *g = codegen_create(build_runner_path, nullptr, OutTypeExe, BuildModeDebug, get_zig_lib_dir(),
436 override_std_dir);
434437 g->enable_time_report = timing_info;
435438 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
436439 codegen_set_out_name(g, buf_create_from_str("build"));
......@@ -645,6 +648,8 @@ int main(int argc, char **argv) {
645648 clang_argv.append(argv[i]);
646649
647650 llvm_argv.append(argv[i]);
651 } else if (strcmp(arg, "--override-std-dir") == 0) {
652 override_std_dir = buf_create_from_str(argv[i]);
648653 } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
649654 lib_dirs.append(argv[i]);
650655 } else if (strcmp(arg, "--library") == 0) {
......@@ -819,7 +824,7 @@ int main(int argc, char **argv) {
819824
820825 switch (cmd) {
821826 case CmdBuiltin: {
822 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir());
827 CodeGen *g = codegen_create(nullptr, target, out_type, build_mode, get_zig_lib_dir(), override_std_dir);
823828 g->is_single_threaded = is_single_threaded;
824829 Buf *builtin_source = codegen_generate_builtin_source(g);
825830 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) {
878883 if (cmd == CmdRun && buf_out_name == nullptr) {
879884 buf_out_name = buf_create_from_str("run");
880885 }
881 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir());
886 CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir(),
887 override_std_dir);
882888 g->subsystem = subsystem;
883889
884890 if (disable_pic) {
src/parser.cpp+14-8
......@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
844844
845845// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
846846static AstNode *ast_parse_var_decl(ParseContext *pc) {
847 Token *first = eat_token_if(pc, TokenIdKeywordConst);
848 if (first == nullptr)
849 first = eat_token_if(pc, TokenIdKeywordVar);
850 if (first == nullptr)
851 return nullptr;
852
847 Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
848 Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
849 if (mut_kw == nullptr)
850 mut_kw = eat_token_if(pc, TokenIdKeywordVar);
851 if (mut_kw == nullptr) {
852 if (thread_local_kw == nullptr) {
853 return nullptr;
854 } else {
855 ast_invalid_token_error(pc, peek_token(pc));
856 }
857 }
853858 Token *identifier = expect_token(pc, TokenIdSymbol);
854859 AstNode *type_expr = nullptr;
855860 if (eat_token_if(pc, TokenIdColon) != nullptr)
......@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
863868
864869 expect_token(pc, TokenIdSemicolon);
865870
866 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);
867 res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;
871 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
872 res->data.variable_declaration.threadlocal_tok = thread_local_kw;
873 res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
868874 res->data.variable_declaration.symbol = token_buf(identifier);
869875 res->data.variable_declaration.type = type_expr;
870876 res->data.variable_declaration.align_expr = align_expr;
src/tokenizer.cpp+2
......@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
146146 {"suspend", TokenIdKeywordSuspend},
147147 {"switch", TokenIdKeywordSwitch},
148148 {"test", TokenIdKeywordTest},
149 {"threadlocal", TokenIdKeywordThreadLocal},
149150 {"true", TokenIdKeywordTrue},
150151 {"try", TokenIdKeywordTry},
151152 {"undefined", TokenIdKeywordUndefined},
......@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
15861587 case TokenIdKeywordStruct: return "struct";
15871588 case TokenIdKeywordSwitch: return "switch";
15881589 case TokenIdKeywordTest: return "test";
1590 case TokenIdKeywordThreadLocal: return "threadlocal";
15891591 case TokenIdKeywordTrue: return "true";
15901592 case TokenIdKeywordTry: return "try";
15911593 case TokenIdKeywordUndefined: return "undefined";
src/tokenizer.hpp+1
......@@ -88,6 +88,7 @@ enum TokenId {
8888 TokenIdKeywordSuspend,
8989 TokenIdKeywordSwitch,
9090 TokenIdKeywordTest,
91 TokenIdKeywordThreadLocal,
9192 TokenIdKeywordTrue,
9293 TokenIdKeywordTry,
9394 TokenIdKeywordUndefined,
std/build.zig+68-19
......@@ -145,8 +145,8 @@ pub const Builder = struct {
145145
146146 pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
147147 self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
148 self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") catch unreachable;
149 self.exe_dir = os.path.join(self.allocator, self.prefix, "bin") catch unreachable;
148 self.lib_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "lib" }) catch unreachable;
149 self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable;
150150 }
151151
152152 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
......@@ -618,7 +618,10 @@ pub const Builder = struct {
618618
619619 ///::dest_rel_path is relative to prefix path or it can be an absolute path
620620 pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {
621 const full_dest_path = os.path.resolve(self.allocator, self.prefix, dest_rel_path) catch unreachable;
621 const full_dest_path = os.path.resolve(
622 self.allocator,
623 [][]const u8{ self.prefix, dest_rel_path },
624 ) catch unreachable;
622625 self.pushInstalledFile(full_dest_path);
623626
624627 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
......@@ -653,7 +656,7 @@ pub const Builder = struct {
653656 }
654657
655658 fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {
656 return os.path.resolve(self.allocator, self.build_root, rel_path) catch unreachable;
659 return os.path.resolve(self.allocator, [][]const u8{ self.build_root, rel_path }) catch unreachable;
657660 }
658661
659662 pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 {
......@@ -676,7 +679,7 @@ pub const Builder = struct {
676679 if (os.path.isAbsolute(name)) {
677680 return name;
678681 }
679 const full_path = try os.path.join(self.allocator, search_prefix, "bin", self.fmt("{}{}", name, exe_extension));
682 const full_path = try os.path.join(self.allocator, [][]const u8{ search_prefix, "bin", self.fmt("{}{}", name, exe_extension) });
680683 if (os.path.real(self.allocator, full_path)) |real_path| {
681684 return real_path;
682685 } else |_| {
......@@ -691,7 +694,7 @@ pub const Builder = struct {
691694 }
692695 var it = mem.tokenize(PATH, []u8{os.path.delimiter});
693696 while (it.next()) |path| {
694 const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
697 const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
695698 if (os.path.real(self.allocator, full_path)) |real_path| {
696699 return real_path;
697700 } else |_| {
......@@ -705,7 +708,7 @@ pub const Builder = struct {
705708 return name;
706709 }
707710 for (paths) |path| {
708 const full_path = try os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
711 const full_path = try os.path.join(self.allocator, [][]const u8{ path, self.fmt("{}{}", name, exe_extension) });
709712 if (os.path.real(self.allocator, full_path)) |real_path| {
710713 return real_path;
711714 } else |_| {
......@@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct {
11131116 }
11141117
11151118 pub fn getOutputPath(self: *LibExeObjStep) []const u8 {
1116 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;
1119 return if (self.output_path) |output_path| output_path else os.path.join(
1120 self.builder.allocator,
1121 [][]const u8{ self.builder.cache_root, self.out_filename },
1122 ) catch unreachable;
11171123 }
11181124
11191125 pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void {
......@@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct {
11261132 }
11271133
11281134 pub fn getOutputHPath(self: *LibExeObjStep) []const u8 {
1129 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;
1135 return if (self.output_h_path) |output_h_path| output_h_path else os.path.join(
1136 self.builder.allocator,
1137 [][]const u8{ self.builder.cache_root, self.out_h_filename },
1138 ) catch unreachable;
11301139 }
11311140
11321141 pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
......@@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct {
12261235 }
12271236
12281237 if (self.build_options_contents.len() > 0) {
1229 const build_options_file = try os.path.join(builder.allocator, builder.cache_root, builder.fmt("{}_build_options.zig", self.name));
1238 const build_options_file = try os.path.join(
1239 builder.allocator,
1240 [][]const u8{ builder.cache_root, builder.fmt("{}_build_options.zig", self.name) },
1241 );
12301242 try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst());
12311243 try zig_args.append("--pkg-begin");
12321244 try zig_args.append("build_options");
......@@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct {
14761488 cc_args.append("-c") catch unreachable;
14771489 cc_args.append(abs_source_file) catch unreachable;
14781490
1479 const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
1491 const cache_o_src = os.path.join(
1492 builder.allocator,
1493 [][]const u8{ builder.cache_root, source_file },
1494 ) catch unreachable;
14801495 if (os.path.dirname(cache_o_src)) |cache_o_dir| {
14811496 try builder.makePath(cache_o_dir);
14821497 }
......@@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct {
15281543 cc_args.append("-current_version") catch unreachable;
15291544 cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable;
15301545
1531 const install_name = builder.pathFromRoot(os.path.join(builder.allocator, builder.cache_root, self.major_only_filename) catch unreachable);
1546 const install_name = builder.pathFromRoot(os.path.join(
1547 builder.allocator,
1548 [][]const u8{ builder.cache_root, self.major_only_filename },
1549 ) catch unreachable);
15321550 cc_args.append("-install_name") catch unreachable;
15331551 cc_args.append(install_name) catch unreachable;
15341552 } else {
......@@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct {
15941612 cc_args.append("-c") catch unreachable;
15951613 cc_args.append(abs_source_file) catch unreachable;
15961614
1597 const cache_o_src = os.path.join(builder.allocator, builder.cache_root, source_file) catch unreachable;
1615 const cache_o_src = os.path.join(
1616 builder.allocator,
1617 [][]const u8{ builder.cache_root, source_file },
1618 ) catch unreachable;
15981619 if (os.path.dirname(cache_o_src)) |cache_o_dir| {
15991620 try builder.makePath(cache_o_dir);
16001621 }
......@@ -1686,6 +1707,7 @@ pub const TestStep = struct {
16861707 no_rosegment: bool,
16871708 output_path: ?[]const u8,
16881709 system_linker_hack: bool,
1710 override_std_dir: ?[]const u8,
16891711
16901712 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
16911713 const step_name = builder.fmt("test {}", root_src);
......@@ -1707,6 +1729,7 @@ pub const TestStep = struct {
17071729 .no_rosegment = false,
17081730 .output_path = null,
17091731 .system_linker_hack = false,
1732 .override_std_dir = null,
17101733 };
17111734 }
17121735
......@@ -1737,6 +1760,10 @@ pub const TestStep = struct {
17371760 self.build_mode = mode;
17381761 }
17391762
1763 pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
1764 self.override_std_dir = dir_path;
1765 }
1766
17401767 pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
17411768 self.output_path = file_path;
17421769
......@@ -1751,7 +1778,10 @@ pub const TestStep = struct {
17511778 return output_path;
17521779 } else {
17531780 const basename = self.builder.fmt("test{}", self.target.exeFileExt());
1754 return os.path.join(self.builder.allocator, self.builder.cache_root, basename) catch unreachable;
1781 return os.path.join(
1782 self.builder.allocator,
1783 [][]const u8{ self.builder.cache_root, basename },
1784 ) catch unreachable;
17551785 }
17561786 }
17571787
......@@ -1914,6 +1944,10 @@ pub const TestStep = struct {
19141944 if (self.system_linker_hack) {
19151945 try zig_args.append("--system-linker-hack");
19161946 }
1947 if (self.override_std_dir) |dir| {
1948 try zig_args.append("--override-std-dir");
1949 try zig_args.append(builder.pathFromRoot(dir));
1950 }
19171951
19181952 try builder.spawnChild(zig_args.toSliceConst());
19191953 }
......@@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct {
19692003 .builder = builder,
19702004 .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),
19712005 .artifact = artifact,
1972 .dest_file = os.path.join(builder.allocator, dest_dir, artifact.out_filename) catch unreachable,
2006 .dest_file = os.path.join(
2007 builder.allocator,
2008 [][]const u8{ dest_dir, artifact.out_filename },
2009 ) catch unreachable,
19732010 };
19742011 self.step.dependOn(&artifact.step);
19752012 builder.pushInstalledFile(self.dest_file);
19762013 if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {
1977 builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.major_only_filename) catch unreachable);
1978 builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable);
2014 builder.pushInstalledFile(os.path.join(
2015 builder.allocator,
2016 [][]const u8{ builder.lib_dir, artifact.major_only_filename },
2017 ) catch unreachable);
2018 builder.pushInstalledFile(os.path.join(
2019 builder.allocator,
2020 [][]const u8{ builder.lib_dir, artifact.name_only_filename },
2021 ) catch unreachable);
19792022 }
19802023 return self;
19812024 }
......@@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj
21312174 const out_dir = os.path.dirname(output_path) orelse ".";
21322175 const out_basename = os.path.basename(output_path);
21332176 // sym link for libfoo.so.1 to libfoo.so.1.2.3
2134 const major_only_path = os.path.join(allocator, out_dir, filename_major_only) catch unreachable;
2177 const major_only_path = os.path.join(
2178 allocator,
2179 [][]const u8{ out_dir, filename_major_only },
2180 ) catch unreachable;
21352181 os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
21362182 warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);
21372183 return err;
21382184 };
21392185 // sym link for libfoo.so to libfoo.so.1
2140 const name_only_path = os.path.join(allocator, out_dir, filename_name_only) catch unreachable;
2186 const name_only_path = os.path.join(
2187 allocator,
2188 [][]const u8{ out_dir, filename_name_only },
2189 ) catch unreachable;
21412190 os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
21422191 warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
21432192 return err;
std/debug/index.zig+2-3
......@@ -37,7 +37,6 @@ const Module = struct {
3737var stderr_file: os.File = undefined;
3838var stderr_file_out_stream: os.File.OutStream = undefined;
3939
40/// TODO multithreaded awareness
4140var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
4241var stderr_mutex = std.Mutex.init();
4342pub fn warn(comptime fmt: []const u8, args: ...) void {
......@@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
775774 const len = try di.coff.getPdbPath(path_buf[0..]);
776775 const raw_path = path_buf[0..len];
777776
778 const path = try os.path.resolve(allocator, raw_path);
777 const path = try os.path.resolve(allocator, [][]const u8{raw_path});
779778
780779 try di.pdb.openFile(di.coff, path);
781780
......@@ -1353,7 +1352,7 @@ const LineNumberProgram = struct {
13531352 return error.InvalidDebugInfo;
13541353 } else
13551354 self.include_dirs[file_entry.dir_index];
1356 const file_name = try os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name);
1355 const file_name = try os.path.join(self.file_entries.allocator, [][]const u8{ dir_name, file_entry.file_name });
13571356 errdefer self.file_entries.allocator.free(file_name);
13581357 return LineInfo{
13591358 .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0,
std/event/fs.zig+2-2
......@@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type {
871871 }
872872
873873 async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {
874 const resolved_path = try os.path.resolve(self.channel.loop.allocator, file_path);
874 const resolved_path = try os.path.resolve(self.channel.loop.allocator, [][]const u8{file_path});
875875 var resolved_path_consumed = false;
876876 defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);
877877
......@@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
13361336}
13371337
13381338async fn testFsWatch(loop: *Loop) !void {
1339 const file_path = try os.path.join(loop.allocator, test_tmp_dir, "file.txt");
1339 const file_path = try os.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
13401340 defer loop.allocator.free(file_path);
13411341
13421342 const contents =
std/heap.zig+2-5
......@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
106106 };
107107 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
108108 const root_addr = @ptrToInt(ptr);
109 const rem = @rem(root_addr, alignment);
110 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
111 const adjusted_addr = root_addr + march_forward_bytes;
109 const adjusted_addr = mem.alignForward(root_addr, alignment);
112110 const record_addr = adjusted_addr + n;
113111 @intToPtr(*align(1) usize, record_addr).* = root_addr;
114112 return @intToPtr([*]u8, adjusted_addr)[0..n];
......@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
126124 const base_addr = @ptrToInt(old_mem.ptr);
127125 const old_addr_end = base_addr + old_mem.len;
128126 const new_addr_end = base_addr + new_size;
129 const rem = @rem(new_addr_end, os.page_size);
130 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
127 const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
131128 if (old_addr_end > new_addr_end_rounded) {
132129 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
133130 }
std/index.zig+1-1
......@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
3333pub const json = @import("json.zig");
3434pub const macho = @import("macho.zig");
3535pub const math = @import("math/index.zig");
36pub const meta = @import("meta/index.zig");
3736pub const mem = @import("mem.zig");
37pub const meta = @import("meta/index.zig");
3838pub const net = @import("net.zig");
3939pub const os = @import("os/index.zig");
4040pub const pdb = @import("pdb.zig");
std/io.zig+5-5
......@@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
912912 }
913913
914914 /// Flush any remaining bits to the stream.
915 pub fn flushBits(self: *Self) !void {
915 pub fn flushBits(self: *Self) Error!void {
916916 if (self.bit_count == 0) return;
917917 try self.out_stream.writeByte(self.bit_buffer);
918918 self.bit_buffer = 0;
......@@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
10791079 }
10801080
10811081 //@BUG: inferred error issue. See: #1386
1082 fn deserializeInt(self: *Self, comptime T: type) (Stream.Error || error{EndOfStream})!T {
1082 fn deserializeInt(self: *Self, comptime T: type) (Error || error{EndOfStream})!T {
10831083 comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
10841084
10851085 const u8_bit_count = 8;
......@@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
12871287 }
12881288
12891289 /// Flushes any unwritten bits to the stream
1290 pub fn flush(self: *Self) Stream.Error!void {
1290 pub fn flush(self: *Self) Error!void {
12911291 if (is_packed) return self.out_stream.flushBits();
12921292 }
12931293
1294 fn serializeInt(self: *Self, value: var) !void {
1294 fn serializeInt(self: *Self, value: var) Error!void {
12951295 const T = @typeOf(value);
12961296 comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
12971297
......@@ -1323,7 +1323,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
13231323 }
13241324
13251325 /// Serializes the passed value into the stream
1326 pub fn serialize(self: *Self, value: var) !void {
1326 pub fn serialize(self: *Self, value: var) Error!void {
13271327 const T = comptime @typeOf(value);
13281328
13291329 if (comptime trait.isIndexable(T)) {
std/io_test.zig+10-1
......@@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
357357 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
358358
359359 assert(in.pos == if (is_packed) total_packed_bytes else total_bytes);
360
361 //Verify that empty error set works with serializer.
362 //deserializer is covered by SliceInStream
363 const NullError = io.NullOutStream.Error;
364 var null_out = io.NullOutStream.init();
365 var null_out_stream = &null_out.stream;
366 var null_serializer = io.Serializer(endian, is_packed, NullError).init(null_out_stream);
367 try null_serializer.serialize(data_mem[0..]);
368 try null_serializer.flush();
360369}
361370
362371test "Serializer/Deserializer Int" {
......@@ -568,4 +577,4 @@ test "Deserializer bad data" {
568577 try testBadData(builtin.Endian.Little, false);
569578 try testBadData(builtin.Endian.Big, true);
570579 try testBadData(builtin.Endian.Little, true);
571}
\ No newline at end of file
580}
std/mem.zig+45-27
......@@ -882,42 +882,40 @@ pub const SplitIterator = struct {
882882 }
883883};
884884
885/// Naively combines a series of strings with a separator.
885/// Naively combines a series of slices with a separator.
886886/// Allocates memory for the result, which must be freed by the caller.
887pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {
888 comptime assert(strings.len >= 1);
889 var total_strings_len: usize = strings.len; // 1 sep per string
890 {
891 comptime var string_i = 0;
892 inline while (string_i < strings.len) : (string_i += 1) {
893 const arg = ([]const u8)(strings[string_i]);
894 total_strings_len += arg.len;
895 }
896 }
887pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
888 if (slices.len == 0) return (([*]u8)(undefined))[0..0];
889
890 const total_len = blk: {
891 var sum: usize = separator.len * (slices.len - 1);
892 for (slices) |slice|
893 sum += slice.len;
894 break :blk sum;
895 };
897896
898 const buf = try allocator.alloc(u8, total_strings_len);
897 const buf = try allocator.alloc(u8, total_len);
899898 errdefer allocator.free(buf);
900899
901 var buf_index: usize = 0;
902 comptime var string_i = 0;
903 inline while (true) {
904 const arg = ([]const u8)(strings[string_i]);
905 string_i += 1;
906 copy(u8, buf[buf_index..], arg);
907 buf_index += arg.len;
908 if (string_i >= strings.len) break;
909 if (buf[buf_index - 1] != sep) {
910 buf[buf_index] = sep;
911 buf_index += 1;
912 }
900 copy(u8, buf, slices[0]);
901 var buf_index: usize = slices[0].len;
902 for (slices[1..]) |slice| {
903 copy(u8, buf[buf_index..], separator);
904 buf_index += separator.len;
905 copy(u8, buf[buf_index..], slice);
906 buf_index += slice.len;
913907 }
914908
915 return allocator.shrink(u8, buf, buf_index);
909 // No need for shrink since buf is exactly the correct size.
910 return buf;
916911}
917912
918913test "mem.join" {
919 assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c"));
920 assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a"));
914 var buf: [1024]u8 = undefined;
915 const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
916 assert(eql(u8, try join(a, ",", [][]const u8{ "a", "b", "c" }), "a,b,c"));
917 assert(eql(u8, try join(a, ",", [][]const u8{"a"}), "a"));
918 assert(eql(u8, try join(a, ",", [][]const u8{ "a", "", "b", "", "c" }), "a,,b,,c"));
921919}
922920
923921test "testStringEquality" {
......@@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" {
13661364 sub2[1] = 'X';
13671365 debug.assert(std.mem.eql(u8, a2, "abcXef"));
13681366}
1367
1368/// Round an address up to the nearest aligned address
1369pub fn alignForward(addr: usize, alignment: usize) usize {
1370 return (addr + alignment - 1) & ~(alignment - 1);
1371}
1372
1373test "std.mem.alignForward" {
1374 debug.assertOrPanic(alignForward(1, 1) == 1);
1375 debug.assertOrPanic(alignForward(2, 1) == 2);
1376 debug.assertOrPanic(alignForward(1, 2) == 2);
1377 debug.assertOrPanic(alignForward(2, 2) == 2);
1378 debug.assertOrPanic(alignForward(3, 2) == 4);
1379 debug.assertOrPanic(alignForward(4, 2) == 4);
1380 debug.assertOrPanic(alignForward(7, 8) == 8);
1381 debug.assertOrPanic(alignForward(8, 8) == 8);
1382 debug.assertOrPanic(alignForward(9, 8) == 16);
1383 debug.assertOrPanic(alignForward(15, 8) == 16);
1384 debug.assertOrPanic(alignForward(16, 8) == 16);
1385 debug.assertOrPanic(alignForward(17, 8) == 24);
1386}
std/os/child_process.zig+6-3
......@@ -574,7 +574,7 @@ pub const ChildProcess = struct {
574574 // to match posix semantics
575575 const app_name = x: {
576576 if (self.cwd) |cwd| {
577 const resolved = try os.path.resolve(self.allocator, cwd, self.argv[0]);
577 const resolved = try os.path.resolve(self.allocator, [][]const u8{ cwd, self.argv[0] });
578578 defer self.allocator.free(resolved);
579579 break :x try cstr.addNullByte(self.allocator, resolved);
580580 } else {
......@@ -597,10 +597,10 @@ pub const ChildProcess = struct {
597597
598598 var it = mem.tokenize(PATH, ";");
599599 while (it.next()) |search_path| {
600 const joined_path = try os.path.join(self.allocator, search_path, app_name);
600 const joined_path = try os.path.join(self.allocator, [][]const u8{ search_path, app_name });
601601 defer self.allocator.free(joined_path);
602602
603 const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, app_name);
603 const joined_path_w = try unicode.utf8ToUtf16LeWithNull(self.allocator, joined_path);
604604 defer self.allocator.free(joined_path_w);
605605
606606 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 {
610610 } else {
611611 return err;
612612 }
613 } else {
614 // Every other error would have been returned earlier.
615 return error.FileNotFound;
613616 }
614617 };
615618
std/os/get_app_data_dir.zig+3-4
......@@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
3030 error.OutOfMemory => return error.OutOfMemory,
3131 };
3232 defer allocator.free(global_dir);
33 return os.path.join(allocator, global_dir, appname);
33 return os.path.join(allocator, [][]const u8{ global_dir, appname });
3434 },
3535 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
3636 else => return error.AppDataDirUnavailable,
......@@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
4141 // TODO look in /etc/passwd
4242 return error.AppDataDirUnavailable;
4343 };
44 return os.path.join(allocator, home_dir, "Library", "Application Support", appname);
44 return os.path.join(allocator, [][]const u8{ home_dir, "Library", "Application Support", appname });
4545 },
4646 builtin.Os.linux, builtin.Os.freebsd => {
4747 const home_dir = os.getEnvPosix("HOME") orelse {
4848 // TODO look in /etc/passwd
4949 return error.AppDataDirUnavailable;
5050 };
51 return os.path.join(allocator, home_dir, ".local", "share", appname);
51 return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname });
5252 },
5353 else => @compileError("Unsupported OS"),
5454 }
......@@ -67,4 +67,3 @@ test "std.os.getAppDataDir" {
6767 // We can't actually validate the result
6868 _ = getAppDataDir(allocator, "zig") catch return;
6969}
70
std/os/index.zig+68-45
......@@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {
88};
99const os = @This();
1010
11comptime {
12 assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
13}
14
1115test "std.os" {
1216 _ = @import("child_process.zig");
1317 _ = @import("darwin.zig");
......@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {
692696 return base;
693697 }
694698 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
695 const ElfHeader = switch (@sizeOf(usize)) {
696 4 => std.elf.Elf32_Ehdr,
697 8 => std.elf.Elf64_Ehdr,
698 else => @compileError("Unsupported architecture"),
699 };
700 return phdr - @sizeOf(ElfHeader);
699 return phdr - @sizeOf(std.elf.Ehdr);
701700 },
702701 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
703702 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
......@@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void {
12851284/// already exists and is a directory.
12861285/// TODO determine if we can remove the allocator requirement from this function
12871286pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {
1288 const resolved_path = try path.resolve(allocator, full_path);
1287 const resolved_path = try path.resolve(allocator, [][]const u8{full_path});
12891288 defer allocator.free(resolved_path);
12901289
12911290 var end_index: usize = resolved_path.len;
......@@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
23052304 switch (builtin.os) {
23062305 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
23072306 Os.freebsd => {
2308 var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1};
2307 var mib = [4]c_int{ posix.CTL_KERN, posix.KERN_PROC, posix.KERN_PROC_PATHNAME, -1 };
23092308 var out_len: usize = out_buffer.len;
23102309 const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0));
23112310
2312 if (err == 0 ) return mem.toSlice(u8, out_buffer);
2311 if (err == 0) return mem.toSlice(u8, out_buffer);
23132312
23142313 return switch (err) {
23152314 posix.EFAULT => error.BadAdress,
23162315 posix.EPERM => error.PermissionDenied,
23172316 else => unexpectedErrorPosix(err),
23182317 };
2319
23202318 },
23212319 Os.windows => {
23222320 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
......@@ -2908,14 +2906,15 @@ pub const Thread = struct {
29082906 pub const Data = if (use_pthreads)
29092907 struct {
29102908 handle: Thread.Handle,
2911 stack_addr: usize,
2912 stack_len: usize,
2909 mmap_addr: usize,
2910 mmap_len: usize,
29132911 }
29142912 else switch (builtin.os) {
29152913 builtin.Os.linux => struct {
29162914 handle: Thread.Handle,
2917 stack_addr: usize,
2918 stack_len: usize,
2915 mmap_addr: usize,
2916 mmap_len: usize,
2917 tls_end_addr: usize,
29192918 },
29202919 builtin.Os.windows => struct {
29212920 handle: Thread.Handle,
......@@ -2955,7 +2954,7 @@ pub const Thread = struct {
29552954 posix.EDEADLK => unreachable,
29562955 else => unreachable,
29572956 }
2958 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
2957 assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
29592958 } else switch (builtin.os) {
29602959 builtin.Os.linux => {
29612960 while (true) {
......@@ -2969,7 +2968,7 @@ pub const Thread = struct {
29692968 else => unreachable,
29702969 }
29712970 }
2972 assert(posix.munmap(self.data.stack_addr, self.data.stack_len) == 0);
2971 assert(posix.munmap(self.data.mmap_addr, self.data.mmap_len) == 0);
29732972 },
29742973 builtin.Os.windows => {
29752974 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
......@@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{
30083007 Unexpected,
30093008};
30103009
3010pub var linux_tls_phdr: ?*std.elf.Phdr = null;
3011pub var linux_tls_img_src: [*]const u8 = undefined; // defined if linux_tls_phdr is
3012
30113013/// caller must call wait on the returned thread
30123014/// fn startFn(@typeOf(context)) T
30133015/// where T is u8, noreturn, void, or !void
......@@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
30973099
30983100 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
30993101
3100 const mmap_len = default_stack_size;
3101 const stack_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
3102 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;
3103 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);
3102 var stack_end_offset: usize = undefined;
3103 var thread_start_offset: usize = undefined;
3104 var context_start_offset: usize = undefined;
3105 var tls_start_offset: usize = undefined;
3106 const mmap_len = blk: {
3107 // First in memory will be the stack, which grows downwards.
3108 var l: usize = mem.alignForward(default_stack_size, os.page_size);
3109 stack_end_offset = l;
3110 // Above the stack, so that it can be in the same mmap call, put the Thread object.
3111 l = mem.alignForward(l, @alignOf(Thread));
3112 thread_start_offset = l;
3113 l += @sizeOf(Thread);
3114 // Next, the Context object.
3115 if (@sizeOf(Context) != 0) {
3116 l = mem.alignForward(l, @alignOf(Context));
3117 context_start_offset = l;
3118 l += @sizeOf(Context);
3119 }
3120 // Finally, the Thread Local Storage, if any.
3121 if (!Thread.use_pthreads) {
3122 if (linux_tls_phdr) |tls_phdr| {
3123 l = mem.alignForward(l, tls_phdr.p_align);
3124 tls_start_offset = l;
3125 l += tls_phdr.p_memsz;
3126 }
3127 }
3128 break :blk l;
3129 };
3130 const mmap_addr = posix.mmap(null, mmap_len, posix.PROT_READ | posix.PROT_WRITE, posix.MAP_PRIVATE | posix.MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
3131 if (mmap_addr == posix.MAP_FAILED) return error.OutOfMemory;
3132 errdefer assert(posix.munmap(mmap_addr, mmap_len) == 0);
3133
3134 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, mmap_addr + thread_start_offset));
3135 thread_ptr.data.mmap_addr = mmap_addr;
3136 thread_ptr.data.mmap_len = mmap_len;
31043137
3105 var stack_end: usize = stack_addr + mmap_len;
31063138 var arg: usize = undefined;
31073139 if (@sizeOf(Context) != 0) {
3108 stack_end -= @sizeOf(Context);
3109 stack_end -= stack_end % @alignOf(Context);
3110 assert(stack_end >= stack_addr);
3111 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
3140 arg = mmap_addr + context_start_offset;
3141 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
31123142 context_ptr.* = context;
3113 arg = stack_end;
31143143 }
31153144
3116 stack_end -= @sizeOf(Thread);
3117 stack_end -= stack_end % @alignOf(Thread);
3118 assert(stack_end >= stack_addr);
3119 const thread_ptr = @alignCast(@alignOf(Thread), @intToPtr(*Thread, stack_end));
3120
3121 thread_ptr.data.stack_addr = stack_addr;
3122 thread_ptr.data.stack_len = mmap_len;
3123
3124 if (builtin.os == builtin.Os.windows) {
3125 // use windows API directly
3126 @compileError("TODO support spawnThread for Windows");
3127 } else if (Thread.use_pthreads) {
3145 if (Thread.use_pthreads) {
31283146 // use pthreads
31293147 var attr: c.pthread_attr_t = undefined;
31303148 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
31313149 defer assert(c.pthread_attr_destroy(&attr) == 0);
31323150
3133 // align to page
3134 stack_end -= stack_end % os.page_size;
3135 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, stack_addr), stack_end - stack_addr) == 0);
3151 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
31363152
31373153 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
31383154 switch (err) {
......@@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
31433159 else => return unexpectedErrorPosix(@intCast(usize, err)),
31443160 }
31453161 } else if (builtin.os == builtin.Os.linux) {
3146 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly
3147 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;
3148 const newtls: usize = 0;
3149 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
3162 var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
3163 posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
3164 posix.CLONE_DETACHED;
3165 var newtls: usize = undefined;
3166 if (linux_tls_phdr) |tls_phdr| {
3167 @memcpy(@intToPtr([*]u8, mmap_addr + tls_start_offset), linux_tls_img_src, tls_phdr.p_filesz);
3168 thread_ptr.data.tls_end_addr = mmap_addr + mmap_len;
3169 newtls = @ptrToInt(&thread_ptr.data.tls_end_addr);
3170 flags |= posix.CLONE_SETTLS;
3171 }
3172 const rc = posix.clone(MainFuncs.linuxThreadMain, mmap_addr + stack_end_offset, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);
31503173 const err = posix.getErrno(rc);
31513174 switch (err) {
31523175 0 => return thread_ptr,
std/os/path.zig+92-33
......@@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool {
3333 }
3434}
3535
36/// This is different from mem.join in that the separator will not be repeated if
37/// it is found at the end or beginning of a pair of consecutive paths.
38fn joinSep(allocator: *Allocator, separator: u8, paths: []const []const u8) ![]u8 {
39 if (paths.len == 0) return (([*]u8)(undefined))[0..0];
40
41 const total_len = blk: {
42 var sum: usize = paths[0].len;
43 var i: usize = 1;
44 while (i < paths.len) : (i += 1) {
45 const prev_path = paths[i - 1];
46 const this_path = paths[i];
47 const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
48 const this_sep = (this_path.len != 0 and this_path[0] == separator);
49 sum += @boolToInt(!prev_sep and !this_sep);
50 sum += if (prev_sep and this_sep) this_path.len - 1 else this_path.len;
51 }
52 break :blk sum;
53 };
54
55 const buf = try allocator.alloc(u8, total_len);
56 errdefer allocator.free(buf);
57
58 mem.copy(u8, buf, paths[0]);
59 var buf_index: usize = paths[0].len;
60 var i: usize = 1;
61 while (i < paths.len) : (i += 1) {
62 const prev_path = paths[i - 1];
63 const this_path = paths[i];
64 const prev_sep = (prev_path.len != 0 and prev_path[prev_path.len - 1] == separator);
65 const this_sep = (this_path.len != 0 and this_path[0] == separator);
66 if (!prev_sep and !this_sep) {
67 buf[buf_index] = separator;
68 buf_index += 1;
69 }
70 const adjusted_path = if (prev_sep and this_sep) this_path[1..] else this_path;
71 mem.copy(u8, buf[buf_index..], adjusted_path);
72 buf_index += adjusted_path.len;
73 }
74
75 // No need for shrink since buf is exactly the correct size.
76 return buf;
77}
78
79pub const join = if (is_windows) joinWindows else joinPosix;
80
3681/// Naively combines a series of paths with the native path seperator.
3782/// Allocates memory for the result, which must be freed by the caller.
38pub fn join(allocator: *Allocator, paths: ...) ![]u8 {
39 if (is_windows) {
40 return joinWindows(allocator, paths);
41 } else {
42 return joinPosix(allocator, paths);
43 }
83pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
84 return joinSep(allocator, sep_windows, paths);
85}
86
87/// Naively combines a series of paths with the native path seperator.
88/// Allocates memory for the result, which must be freed by the caller.
89pub fn joinPosix(allocator: *Allocator, paths: []const []const u8) ![]u8 {
90 return joinSep(allocator, sep_posix, paths);
4491}
4592
46pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 {
47 return mem.join(allocator, sep_windows, paths);
93fn testJoinWindows(paths: []const []const u8, expected: []const u8) void {
94 var buf: [1024]u8 = undefined;
95 const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
96 const actual = joinWindows(a, paths) catch @panic("fail");
97 debug.assertOrPanic(mem.eql(u8, actual, expected));
4898}
4999
50pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 {
51 return mem.join(allocator, sep_posix, paths);
100fn testJoinPosix(paths: []const []const u8, expected: []const u8) void {
101 var buf: [1024]u8 = undefined;
102 const a = &std.heap.FixedBufferAllocator.init(&buf).allocator;
103 const actual = joinPosix(a, paths) catch @panic("fail");
104 debug.assertOrPanic(mem.eql(u8, actual, expected));
52105}
53106
54107test "os.path.join" {
55 assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b", "c"), "c:\\a\\b\\c"));
56 assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\b\\", "c"), "c:\\a\\b\\c"));
108 testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
109 testJoinWindows([][]const u8{ "c:\\a\\b", "c" }, "c:\\a\\b\\c");
110 testJoinWindows([][]const u8{ "c:\\a\\b\\", "c" }, "c:\\a\\b\\c");
57111
58 assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\", "a", "b\\", "c"), "c:\\a\\b\\c"));
59 assert(mem.eql(u8, try joinWindows(debug.global_allocator, "c:\\a\\", "b\\", "c"), "c:\\a\\b\\c"));
112 testJoinWindows([][]const u8{ "c:\\", "a", "b\\", "c" }, "c:\\a\\b\\c");
113 testJoinWindows([][]const u8{ "c:\\a\\", "b\\", "c" }, "c:\\a\\b\\c");
60114
61 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"));
115 testJoinWindows(
116 [][]const u8{ "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std", "io.zig" },
117 "c:\\home\\andy\\dev\\zig\\build\\lib\\zig\\std\\io.zig",
118 );
62119
63 assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b", "c"), "/a/b/c"));
64 assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/b/", "c"), "/a/b/c"));
120 testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c");
121 testJoinPosix([][]const u8{ "/a/b/", "c" }, "/a/b/c");
65122
66 assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/", "a", "b/", "c"), "/a/b/c"));
67 assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/a/", "b/", "c"), "/a/b/c"));
123 testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c");
124 testJoinPosix([][]const u8{ "/a/", "b/", "c" }, "/a/b/c");
68125
69 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"));
126 testJoinPosix(
127 [][]const u8{ "/home/andy/dev/zig/build/lib/zig/std", "io.zig" },
128 "/home/andy/dev/zig/build/lib/zig/std/io.zig",
129 );
130
131 testJoinPosix([][]const u8{ "a", "/c" }, "a/c");
132 testJoinPosix([][]const u8{ "a/", "/c" }, "a/c");
70133}
71134
72135pub fn isAbsolute(path: []const u8) bool {
......@@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
312375 return true;
313376}
314377
315/// Converts the command line arguments into a slice and calls `resolveSlice`.
316pub fn resolve(allocator: *Allocator, args: ...) ![]u8 {
317 var paths: [args.len][]const u8 = undefined;
318 comptime var arg_i = 0;
319 inline while (arg_i < args.len) : (arg_i += 1) {
320 paths[arg_i] = args[arg_i];
321 }
322 return resolveSlice(allocator, paths);
323}
324
325378/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.
326pub fn resolveSlice(allocator: *Allocator, paths: []const []const u8) ![]u8 {
379pub fn resolve(allocator: *Allocator, paths: []const []const u8) ![]u8 {
327380 if (is_windows) {
328381 return resolveWindows(allocator, paths);
329382 } else {
......@@ -602,7 +655,10 @@ test "os.path.resolveWindows" {
602655 const parsed_cwd = windowsParsePath(cwd);
603656 {
604657 const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });
605 const expected = try join(debug.global_allocator, parsed_cwd.disk_designator, "usr\\local\\lib\\zig\\std\\array_list.zig");
658 const expected = try join(debug.global_allocator, [][]const u8{
659 parsed_cwd.disk_designator,
660 "usr\\local\\lib\\zig\\std\\array_list.zig",
661 });
606662 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
607663 expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
608664 }
......@@ -610,7 +666,10 @@ test "os.path.resolveWindows" {
610666 }
611667 {
612668 const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" });
613 const expected = try join(debug.global_allocator, cwd, "usr\\local\\lib\\zig");
669 const expected = try join(debug.global_allocator, [][]const u8{
670 cwd,
671 "usr\\local\\lib\\zig",
672 });
614673 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
615674 expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
616675 }
std/os/test.zig+16
......@@ -105,3 +105,19 @@ test "AtomicFile" {
105105
106106 try os.deleteFile(test_out_file);
107107}
108
109test "thread local storage" {
110 if (builtin.single_threaded) return error.SkipZigTest;
111 const thread1 = try std.os.spawnThread({}, testTls);
112 const thread2 = try std.os.spawnThread({}, testTls);
113 testTls({});
114 thread1.wait();
115 thread2.wait();
116}
117
118threadlocal var x: i32 = 1234;
119fn testTls(context: void) void {
120 if (x != 1234) @panic("bad start value");
121 x += 1;
122 if (x != 1235) @panic("bad end value");
123}
std/os/windows/index.zig+18-1
......@@ -49,7 +49,10 @@ pub const UNICODE = false;
4949pub const WCHAR = u16;
5050pub const WORD = u16;
5151pub const LARGE_INTEGER = i64;
52pub const LONG = c_long;
52pub const ULONG = u32;
53pub const LONG = i32;
54pub const ULONGLONG = u64;
55pub const LONGLONG = i64;
5356
5457pub const TRUE = 1;
5558pub const FALSE = 0;
......@@ -380,3 +383,17 @@ pub const COORD = extern struct {
380383};
381384
382385pub const CREATE_UNICODE_ENVIRONMENT = 1024;
386
387pub const TLS_OUT_OF_INDEXES = 4294967295;
388pub const IMAGE_TLS_DIRECTORY = extern struct {
389 StartAddressOfRawData: usize,
390 EndAddressOfRawData: usize,
391 AddressOfIndex: usize,
392 AddressOfCallBacks: usize,
393 SizeOfZeroFill: u32,
394 Characteristics: u32,
395};
396pub const IMAGE_TLS_DIRECTORY64 = IMAGE_TLS_DIRECTORY;
397pub const IMAGE_TLS_DIRECTORY32 = IMAGE_TLS_DIRECTORY;
398
399pub const PIMAGE_TLS_CALLBACK = ?extern fn(PVOID, DWORD, PVOID) void;
std/os/windows/kernel32.zig+4
......@@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
164164
165165pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
166166
167pub extern "kernel32" stdcallcc fn TlsAlloc() DWORD;
168
169pub extern "kernel32" stdcallcc fn TlsFree(dwTlsIndex: DWORD) BOOL;
170
167171pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
168172
169173pub extern "kernel32" stdcallcc fn WriteFile(
std/os/windows/tls.zig created+36
......@@ -0,0 +1,36 @@
1const std = @import("../../index.zig");
2
3export var _tls_index: u32 = std.os.windows.TLS_OUT_OF_INDEXES;
4export var _tls_start: u8 linksection(".tls") = 0;
5export var _tls_end: u8 linksection(".tls$ZZZ") = 0;
6export var __xl_a: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLA") = null;
7export var __xl_z: std.os.windows.PIMAGE_TLS_CALLBACK linksection(".CRT$XLZ") = null;
8
9// TODO this is how I would like it to be expressed
10// TODO also note, ReactOS has a +1 on StartAddressOfRawData and AddressOfCallBacks. Investigate
11// why they do that.
12//export const _tls_used linksection(".rdata$T") = std.os.windows.IMAGE_TLS_DIRECTORY {
13// .StartAddressOfRawData = @ptrToInt(&_tls_start),
14// .EndAddressOfRawData = @ptrToInt(&_tls_end),
15// .AddressOfIndex = @ptrToInt(&_tls_index),
16// .AddressOfCallBacks = @ptrToInt(__xl_a),
17// .SizeOfZeroFill = 0,
18// .Characteristics = 0,
19//};
20// This is the workaround because we can't do @ptrToInt at comptime like that.
21pub const IMAGE_TLS_DIRECTORY = extern struct {
22 StartAddressOfRawData: *c_void,
23 EndAddressOfRawData: *c_void,
24 AddressOfIndex: *c_void,
25 AddressOfCallBacks: *c_void,
26 SizeOfZeroFill: u32,
27 Characteristics: u32,
28};
29export const _tls_used linksection(".rdata$T") = IMAGE_TLS_DIRECTORY {
30 .StartAddressOfRawData = &_tls_start,
31 .EndAddressOfRawData = &_tls_end,
32 .AddressOfIndex = &_tls_index,
33 .AddressOfCallBacks = &__xl_a,
34 .SizeOfZeroFill = 0,
35 .Characteristics = 0,
36};
std/special/bootstrap.zig+59-4
......@@ -4,6 +4,7 @@
44const root = @import("@root");
55const std = @import("std");
66const builtin = @import("builtin");
7const assert = std.debug.assert;
78
89var argc_ptr: [*]usize = undefined;
910
......@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {
4445
4546extern fn WinMainCRTStartup() noreturn {
4647 @setAlignStack(16);
47
48 if (!builtin.single_threaded) {
49 _ = @import("../os/windows/tls.zig");
50 }
4851 std.os.windows.ExitProcess(callMain());
4952}
5053
......@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {
6164 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
6265 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
6366 if (builtin.os == builtin.Os.linux) {
64 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);
65 std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);
66 std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);
67 // Scan auxiliary vector.
68 const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
69 std.os.linux_elf_aux_maybe = auxv;
70 var i: usize = 0;
71 var at_phdr: usize = 0;
72 var at_phnum: usize = 0;
73 var at_phent: usize = 0;
74 while (auxv[i].a_un.a_val != 0) : (i += 1) {
75 switch (auxv[i].a_type) {
76 std.elf.AT_PAGESZ => assert(auxv[i].a_un.a_val == std.os.page_size),
77 std.elf.AT_PHDR => at_phdr = auxv[i].a_un.a_val,
78 std.elf.AT_PHNUM => at_phnum = auxv[i].a_un.a_val,
79 std.elf.AT_PHENT => at_phent = auxv[i].a_un.a_val,
80 else => {},
81 }
82 }
83 if (!builtin.single_threaded) linuxInitializeThreadLocalStorage(at_phdr, at_phnum, at_phent);
6784 }
6885
6986 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
......@@ -116,3 +133,41 @@ inline fn callMain() u8 {
116133 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
117134 }
118135}
136
137var tls_end_addr: usize = undefined;
138const main_thread_tls_align = 32;
139var main_thread_tls_bytes: [64]u8 align(main_thread_tls_align) = [1]u8{0} ** 64;
140
141fn linuxInitializeThreadLocalStorage(at_phdr: usize, at_phnum: usize, at_phent: usize) void {
142 var phdr_addr = at_phdr;
143 var n = at_phnum;
144 var base: usize = 0;
145 while (n != 0) : ({n -= 1; phdr_addr += at_phent;}) {
146 const phdr = @intToPtr(*std.elf.Phdr, phdr_addr);
147 // TODO look for PT_DYNAMIC when we have https://github.com/ziglang/zig/issues/1917
148 switch (phdr.p_type) {
149 std.elf.PT_PHDR => base = at_phdr - phdr.p_vaddr,
150 std.elf.PT_TLS => std.os.linux_tls_phdr = phdr,
151 else => continue,
152 }
153 }
154 const tls_phdr = std.os.linux_tls_phdr orelse return;
155 std.os.linux_tls_img_src = @intToPtr([*]const u8, base + tls_phdr.p_vaddr);
156 assert(main_thread_tls_bytes.len >= tls_phdr.p_memsz); // not enough preallocated Thread Local Storage
157 assert(main_thread_tls_align >= tls_phdr.p_align); // preallocated Thread Local Storage not aligned enough
158 @memcpy(&main_thread_tls_bytes, std.os.linux_tls_img_src, tls_phdr.p_filesz);
159 tls_end_addr = @ptrToInt(&main_thread_tls_bytes) + tls_phdr.p_memsz;
160 linuxSetThreadArea(@ptrToInt(&tls_end_addr));
161}
162
163fn linuxSetThreadArea(addr: usize) void {
164 switch (builtin.arch) {
165 builtin.Arch.x86_64 => {
166 const ARCH_SET_FS = 0x1002;
167 const rc = std.os.linux.syscall2(std.os.linux.SYS_arch_prctl, ARCH_SET_FS, addr);
168 // acrh_prctl is documented to never fail
169 assert(rc == 0);
170 },
171 else => @compileError("Unsupported architecture"),
172 }
173}
test/cli.zig+4-4
......@@ -27,9 +27,9 @@ pub fn main() !void {
2727 std.debug.warn("Expected second argument to be cache root directory path\n");
2828 return error.InvalidArgs;
2929 });
30 const zig_exe = try os.path.resolve(a, zig_exe_rel);
30 const zig_exe = try os.path.resolve(a, [][]const u8{zig_exe_rel});
3131
32 const dir_path = try os.path.join(a, cache_root, "clitest");
32 const dir_path = try os.path.join(a, [][]const u8{ cache_root, "clitest" });
3333 const TestFn = fn ([]const u8, []const u8) anyerror!void;
3434 const test_fns = []TestFn{
3535 testZigInitLib,
......@@ -99,8 +99,8 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
9999fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
100100 if (builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64) return;
101101
102 const example_zig_path = try os.path.join(a, dir_path, "example.zig");
103 const example_s_path = try os.path.join(a, dir_path, "example.s");
102 const example_zig_path = try os.path.join(a, [][]const u8{ dir_path, "example.zig" });
103 const example_s_path = try os.path.join(a, [][]const u8{ dir_path, "example.s" });
104104
105105 try std.io.writeFile(example_zig_path,
106106 \\// Type your code here, or load an example.
test/compile_errors.zig+19
......@@ -1,6 +1,25 @@
11const tests = @import("tests.zig");
22
33pub fn addCases(cases: *tests.CompileErrorContext) void {
4 cases.add(
5 "threadlocal qualifier on const",
6 \\threadlocal const x: i32 = 1234;
7 \\export fn entry() i32 {
8 \\ return x;
9 \\}
10 ,
11 ".tmp_source.zig:1:13: error: threadlocal variable cannot be constant",
12 );
13
14 cases.add(
15 "threadlocal qualifier on local variable",
16 \\export fn entry() void {
17 \\ threadlocal var x: i32 = 1234;
18 \\}
19 ,
20 ".tmp_source.zig:2:5: error: function-local variable 'x' cannot be threadlocal",
21 );
22
423 cases.add(
524 "@bitCast same size but bit count mismatch",
625 \\export fn entry(byte: u8) void {
test/stage1/behavior/misc.zig+8
......@@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {
685685fn getNull() ?*i32 {
686686 return null;
687687}
688
689test "thread local variable" {
690 const S = struct {
691 threadlocal var t: i32 = 1234;
692 };
693 S.t += 1;
694 assertOrPanic(S.t == 1235);
695}
test/stage1/behavior/vector.zig+7-10
......@@ -1,20 +1,17 @@
11const std = @import("std");
2const mem = std.mem;
23const assertOrPanic = std.debug.assertOrPanic;
34
4test "implicit array to vector and vector to array" {
5test "vector wrap operators" {
56 const S = struct {
67 fn doTheTest() void {
7 var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};
8 const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};
9 v +%= x;
10 const result: [4]i32 = v;
11 assertOrPanic(result[0] == 11);
12 assertOrPanic(result[1] == 22);
13 assertOrPanic(result[2] == 33);
14 assertOrPanic(result[3] == 44);
8 const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
9 const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
10 assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));
11 assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));
12 assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));
1513 }
1614 };
1715 S.doTheTest();
1816 comptime S.doTheTest();
1917}
20
test/tests.zig+47-11
......@@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
194194 if (link_libc) {
195195 these_tests.linkSystemLibrary("c");
196196 }
197 if (mem.eql(u8, name, "std")) {
198 these_tests.overrideStdDir("std");
199 }
197200 step.dependOn(&these_tests.step);
198201 }
199202 }
......@@ -436,7 +439,10 @@ pub const CompareOutputContext = struct {
436439 pub fn addCase(self: *CompareOutputContext, case: TestCase) void {
437440 const b = self.b;
438441
439 const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable;
442 const root_src = os.path.join(
443 b.allocator,
444 [][]const u8{ b.cache_root, case.sources.items[0].filename },
445 ) catch unreachable;
440446
441447 switch (case.special) {
442448 Special.Asm => {
......@@ -449,7 +455,10 @@ pub const CompareOutputContext = struct {
449455 exe.addAssemblyFile(root_src);
450456
451457 for (case.sources.toSliceConst()) |src_file| {
452 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
458 const expanded_src_path = os.path.join(
459 b.allocator,
460 [][]const u8{ b.cache_root, src_file.filename },
461 ) catch unreachable;
453462 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
454463 exe.step.dependOn(&write_src.step);
455464 }
......@@ -473,7 +482,10 @@ pub const CompareOutputContext = struct {
473482 }
474483
475484 for (case.sources.toSliceConst()) |src_file| {
476 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
485 const expanded_src_path = os.path.join(
486 b.allocator,
487 [][]const u8{ b.cache_root, src_file.filename },
488 ) catch unreachable;
477489 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
478490 exe.step.dependOn(&write_src.step);
479491 }
......@@ -496,7 +508,10 @@ pub const CompareOutputContext = struct {
496508 }
497509
498510 for (case.sources.toSliceConst()) |src_file| {
499 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
511 const expanded_src_path = os.path.join(
512 b.allocator,
513 [][]const u8{ b.cache_root, src_file.filename },
514 ) catch unreachable;
500515 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
501516 exe.step.dependOn(&write_src.step);
502517 }
......@@ -569,8 +584,14 @@ pub const CompileErrorContext = struct {
569584 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
570585 const b = self.context.b;
571586
572 const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable;
573 const obj_path = os.path.join(b.allocator, b.cache_root, "test.o") catch unreachable;
587 const root_src = os.path.join(
588 b.allocator,
589 [][]const u8{ b.cache_root, self.case.sources.items[0].filename },
590 ) catch unreachable;
591 const obj_path = os.path.join(
592 b.allocator,
593 [][]const u8{ b.cache_root, "test.o" },
594 ) catch unreachable;
574595
575596 var zig_args = ArrayList([]const u8).init(b.allocator);
576597 zig_args.append(b.zig_exe) catch unreachable;
......@@ -718,7 +739,10 @@ pub const CompileErrorContext = struct {
718739 self.step.dependOn(&compile_and_cmp_errors.step);
719740
720741 for (case.sources.toSliceConst()) |src_file| {
721 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
742 const expanded_src_path = os.path.join(
743 b.allocator,
744 [][]const u8{ b.cache_root, src_file.filename },
745 ) catch unreachable;
722746 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
723747 compile_and_cmp_errors.step.dependOn(&write_src.step);
724748 }
......@@ -849,7 +873,10 @@ pub const TranslateCContext = struct {
849873 const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
850874 const b = self.context.b;
851875
852 const root_src = os.path.join(b.allocator, b.cache_root, self.case.sources.items[0].filename) catch unreachable;
876 const root_src = os.path.join(
877 b.allocator,
878 [][]const u8{ b.cache_root, self.case.sources.items[0].filename },
879 ) catch unreachable;
853880
854881 var zig_args = ArrayList([]const u8).init(b.allocator);
855882 zig_args.append(b.zig_exe) catch unreachable;
......@@ -983,7 +1010,10 @@ pub const TranslateCContext = struct {
9831010 self.step.dependOn(&translate_c_and_cmp.step);
9841011
9851012 for (case.sources.toSliceConst()) |src_file| {
986 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
1013 const expanded_src_path = os.path.join(
1014 b.allocator,
1015 [][]const u8{ b.cache_root, src_file.filename },
1016 ) catch unreachable;
9871017 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
9881018 translate_c_and_cmp.step.dependOn(&write_src.step);
9891019 }
......@@ -1098,7 +1128,10 @@ pub const GenHContext = struct {
10981128
10991129 pub fn addCase(self: *GenHContext, case: *const TestCase) void {
11001130 const b = self.b;
1101 const root_src = os.path.join(b.allocator, b.cache_root, case.sources.items[0].filename) catch unreachable;
1131 const root_src = os.path.join(
1132 b.allocator,
1133 [][]const u8{ b.cache_root, case.sources.items[0].filename },
1134 ) catch unreachable;
11021135
11031136 const mode = builtin.Mode.Debug;
11041137 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 {
11101143 obj.setBuildMode(mode);
11111144
11121145 for (case.sources.toSliceConst()) |src_file| {
1113 const expanded_src_path = os.path.join(b.allocator, b.cache_root, src_file.filename) catch unreachable;
1146 const expanded_src_path = os.path.join(
1147 b.allocator,
1148 [][]const u8{ b.cache_root, src_file.filename },
1149 ) catch unreachable;
11141150 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
11151151 obj.step.dependOn(&write_src.step);
11161152 }