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...@@ -596,6 +596,7 @@ set(ZIG_STD_FILES
596 "os/windows/ntdll.zig"596 "os/windows/ntdll.zig"
597 "os/windows/ole32.zig"597 "os/windows/ole32.zig"
598 "os/windows/shell32.zig"598 "os/windows/shell32.zig"
599 "os/windows/tls.zig"
599 "os/windows/util.zig"600 "os/windows/util.zig"
600 "os/zen.zig"601 "os/zen.zig"
601 "pdb.zig"602 "pdb.zig"
build.zig+27-8
...@@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void {...@@ -16,7 +16,10 @@ pub fn build(b: *Builder) !void {
16 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");16 var docgen_exe = b.addExecutable("docgen", "doc/docgen.zig");
1717
18 const rel_zig_exe = try os.path.relative(b.allocator, b.build_root, b.zig_exe);18 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;
20 var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{23 var docgen_cmd = b.addCommand(null, b.env_map, [][]const u8{
21 docgen_exe.getOutputPath(),24 docgen_exe.getOutputPath(),
22 rel_zig_exe,25 rel_zig_exe,
...@@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {...@@ -125,13 +128,19 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
125 for (dep.libdirs.toSliceConst()) |lib_dir| {128 for (dep.libdirs.toSliceConst()) |lib_dir| {
126 lib_exe_obj.addLibPath(lib_dir);129 lib_exe_obj.addLibPath(lib_dir);
127 }130 }
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;
129 for (dep.system_libs.toSliceConst()) |lib| {135 for (dep.system_libs.toSliceConst()) |lib| {
130 const static_bare_name = if (mem.eql(u8, lib, "curses"))136 const static_bare_name = if (mem.eql(u8, lib, "curses"))
131 ([]const u8)("libncurses.a")137 ([]const u8)("libncurses.a")
132 else138 else
133 b.fmt("lib{}.a", lib);139 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;
135 const have_static = fileExists(static_lib_name) catch unreachable;144 const have_static = fileExists(static_lib_name) catch unreachable;
136 if (have_static) {145 if (have_static) {
137 lib_exe_obj.addObjectFile(static_lib_name);146 lib_exe_obj.addObjectFile(static_lib_name);
...@@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool {...@@ -159,7 +168,11 @@ fn fileExists(filename: []const u8) !bool {
159168
160fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {169fn addCppLib(b: *Builder, lib_exe_obj: var, cmake_binary_dir: []const u8, lib_name: []const u8) void {
161 const lib_prefix = if (lib_exe_obj.target.isWindows()) "" else "lib";170 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);
163}176}
164177
165const LibraryDep = struct {178const LibraryDep = struct {
...@@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {...@@ -235,8 +248,11 @@ fn findLLVM(b: *Builder, llvm_config_exe: []const u8) !LibraryDep {
235pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {248pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
236 var it = mem.tokenize(stdlib_files, ";");249 var it = mem.tokenize(stdlib_files, ";");
237 while (it.next()) |stdlib_file| {250 while (it.next()) |stdlib_file| {
238 const src_path = os.path.join(b.allocator, "std", stdlib_file) catch unreachable;251 const src_path = os.path.join(b.allocator, [][]const u8{ "std", stdlib_file }) catch unreachable;
239 const dest_path = os.path.join(b.allocator, "lib", "zig", "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;
240 b.installFile(src_path, dest_path);256 b.installFile(src_path, dest_path);
241 }257 }
242}258}
...@@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {...@@ -244,8 +260,11 @@ pub fn installStdLib(b: *Builder, stdlib_files: []const u8) void {
244pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {260pub fn installCHeaders(b: *Builder, c_header_files: []const u8) void {
245 var it = mem.tokenize(c_header_files, ";");261 var it = mem.tokenize(c_header_files, ";");
246 while (it.next()) |c_header_file| {262 while (it.next()) |c_header_file| {
247 const src_path = os.path.join(b.allocator, "c_headers", c_header_file) catch unreachable;263 const src_path = os.path.join(b.allocator, [][]const u8{ "c_headers", c_header_file }) catch unreachable;
248 const dest_path = os.path.join(b.allocator, "lib", "zig", "include", 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;
249 b.installFile(src_path, dest_path);268 b.installFile(src_path, dest_path);
250 }269 }
251}270}
doc/docgen.zig+20-5
...@@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -990,13 +990,19 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
990 try tokenizeAndPrint(tokenizer, out, code.source_token);990 try tokenizeAndPrint(tokenizer, out, code.source_token);
991 try out.write("</pre>");991 try out.write("</pre>");
992 const name_plus_ext = try std.fmt.allocPrint(allocator, "{}.zig", code.name);992 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 );
994 try io.writeFile(tmp_source_file_name, trimmed_raw_source);997 try io.writeFile(tmp_source_file_name, trimmed_raw_source);
995998
996 switch (code.id) {999 switch (code.id) {
997 Code.Id.Exe => |expected_outcome| {1000 Code.Id.Exe => |expected_outcome| {
998 const name_plus_bin_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, exe_ext);1001 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 );
1000 var build_args = std.ArrayList([]const u8).init(allocator);1006 var build_args = std.ArrayList([]const u8).init(allocator);
1001 defer build_args.deinit();1007 defer build_args.deinit();
1002 try build_args.appendSlice([][]const u8{1008 try build_args.appendSlice([][]const u8{
...@@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1024,7 +1030,10 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1024 }1030 }
1025 for (code.link_objects) |link_object| {1031 for (code.link_objects) |link_object| {
1026 const name_with_ext = try std.fmt.allocPrint(allocator, "{}{}", link_object, obj_ext);1032 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 );
1028 try build_args.append("--object");1037 try build_args.append("--object");
1029 try build_args.append(full_path_object);1038 try build_args.append(full_path_object);
1030 try out.print(" --object {}", name_with_ext);1039 try out.print(" --object {}", name_with_ext);
...@@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var...@@ -1216,12 +1225,18 @@ fn genHtml(allocator: *mem.Allocator, tokenizer: *Tokenizer, toc: *Toc, out: var
1216 },1225 },
1217 Code.Id.Obj => |maybe_error_match| {1226 Code.Id.Obj => |maybe_error_match| {
1218 const name_plus_obj_ext = try std.fmt.allocPrint(allocator, "{}{}", code.name, obj_ext);1227 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 );
1220 var build_args = std.ArrayList([]const u8).init(allocator);1232 var build_args = std.ArrayList([]const u8).init(allocator);
1221 defer build_args.deinit();1233 defer build_args.deinit();
12221234
1223 const name_plus_h_ext = try std.fmt.allocPrint(allocator, "{}.h", code.name);1235 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
1226 try build_args.appendSlice([][]const u8{1241 try build_args.appendSlice([][]const u8{
1227 zig_exe,1242 zig_exe,
doc/langref.html.in+15-6
...@@ -3192,7 +3192,16 @@ fn foo() void { }...@@ -3192,7 +3192,16 @@ fn foo() void { }
3192 {#code_end#}3192 {#code_end#}
3193 {#header_open|Pass-by-value Parameters#}3193 {#header_open|Pass-by-value Parameters#}
3194 <p>3194 <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.
3196 </p>3205 </p>
3197 {#code_begin|test#}3206 {#code_begin|test#}
3198const Point = struct {3207const Point = struct {
...@@ -3201,20 +3210,20 @@ const Point = struct {...@@ -3201,20 +3210,20 @@ const Point = struct {
3201};3210};
32023211
3203fn foo(point: Point) i32 {3212fn 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.
3204 return point.x + point.y;3217 return point.x + point.y;
3205}3218}
32063219
3207const assert = @import("std").debug.assert;3220const assert = @import("std").debug.assert;
32083221
3209test "pass aggregate type by non-copy value to function" {3222test "pass struct to function" {
3210 assert(foo(Point{ .x = 1, .y = 2 }) == 3);3223 assert(foo(Point{ .x = 1, .y = 2 }) == 3);
3211}3224}
3212 {#code_end#}3225 {#code_end#}
3213 <p>3226 <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>
3218 For extern functions, Zig follows the C ABI for passing structs and unions by value.3227 For extern functions, Zig follows the C ABI for passing structs and unions by value.
3219 </p>3228 </p>
3220 {#header_close#}3229 {#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 {...@@ -487,7 +487,7 @@ pub const Compilation = struct {
487 comp.name = try Buffer.init(comp.arena(), name);487 comp.name = try Buffer.init(comp.arena(), name);
488 comp.llvm_triple = try target.getTriple(comp.arena());488 comp.llvm_triple = try target.getTriple(comp.arena());
489 comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);489 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
492 const opt_level = switch (build_mode) {492 const opt_level = switch (build_mode) {
493 builtin.Mode.Debug => llvm.CodeGenLevelNone,493 builtin.Mode.Debug => llvm.CodeGenLevelNone,
...@@ -1198,7 +1198,7 @@ pub const Compilation = struct {...@@ -1198,7 +1198,7 @@ pub const Compilation = struct {
1198 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);1198 const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix);
1199 defer self.gpa().free(file_name);1199 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..] });
1202 errdefer self.gpa().free(full_path);1202 errdefer self.gpa().free(full_path);
12031203
1204 return Buffer.fromOwnedSlice(self.gpa(), full_path);1204 return Buffer.fromOwnedSlice(self.gpa(), full_path);
...@@ -1219,7 +1219,7 @@ pub const Compilation = struct {...@@ -1219,7 +1219,7 @@ pub const Compilation = struct {
1219 const zig_dir_path = try getZigDir(self.gpa());1219 const zig_dir_path = try getZigDir(self.gpa());
1220 defer self.gpa().free(zig_dir_path);1220 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..] });
1223 try os.makePath(self.gpa(), tmp_dir);1223 try os.makePath(self.gpa(), tmp_dir);
1224 return tmp_dir;1224 return tmp_dir;
1225 }1225 }
src-self-hosted/introspect.zig+2-2
...@@ -8,10 +8,10 @@ const warn = std.debug.warn;...@@ -8,10 +8,10 @@ const warn = std.debug.warn;
88
9/// Caller must free result9/// Caller must free result
10pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 {10pub 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" });
12 errdefer allocator.free(test_zig_dir);12 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" });
15 defer allocator.free(test_index_file);15 defer allocator.free(test_index_file);
1616
17 var file = try os.File.openRead(test_index_file);17 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 {...@@ -230,7 +230,7 @@ pub const LibCInstallation = struct {
230 while (path_i < search_paths.len) : (path_i += 1) {230 while (path_i < search_paths.len) : (path_i += 1) {
231 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);231 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
232 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");232 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" });
234 defer loop.allocator.free(stdlib_path);234 defer loop.allocator.free(stdlib_path);
235235
236 if (try fileExists(stdlib_path)) {236 if (try fileExists(stdlib_path)) {
...@@ -254,7 +254,10 @@ pub const LibCInstallation = struct {...@@ -254,7 +254,10 @@ pub const LibCInstallation = struct {
254 const stream = &std.io.BufferOutStream.init(&result_buf).stream;254 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
255 try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);255 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 );
258 defer loop.allocator.free(stdlib_path);261 defer loop.allocator.free(stdlib_path);
259262
260 if (try fileExists(stdlib_path)) {263 if (try fileExists(stdlib_path)) {
...@@ -283,7 +286,10 @@ pub const LibCInstallation = struct {...@@ -283,7 +286,10 @@ pub const LibCInstallation = struct {
283 builtin.Arch.aarch64v8 => try stream.write("arm"),286 builtin.Arch.aarch64v8 => try stream.write("arm"),
284 else => return error.UnsupportedArchitecture,287 else => return error.UnsupportedArchitecture,
285 }288 }
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 );
287 defer loop.allocator.free(ucrt_lib_path);293 defer loop.allocator.free(ucrt_lib_path);
288 if (try fileExists(ucrt_lib_path)) {294 if (try fileExists(ucrt_lib_path)) {
289 self.lib_dir = result_buf.toOwnedSlice();295 self.lib_dir = result_buf.toOwnedSlice();
...@@ -358,7 +364,10 @@ pub const LibCInstallation = struct {...@@ -358,7 +364,10 @@ pub const LibCInstallation = struct {
358 builtin.Arch.aarch64v8 => try stream.write("arm\\"),364 builtin.Arch.aarch64v8 => try stream.write("arm\\"),
359 else => return error.UnsupportedArchitecture,365 else => return error.UnsupportedArchitecture,
360 }366 }
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 );
362 defer loop.allocator.free(kernel32_path);371 defer loop.allocator.free(kernel32_path);
363 if (try fileExists(kernel32_path)) {372 if (try fileExists(kernel32_path)) {
364 self.kernel32_lib_dir = result_buf.toOwnedSlice();373 self.kernel32_lib_dir = result_buf.toOwnedSlice();
src-self-hosted/link.zig+1-1
...@@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {...@@ -315,7 +315,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
315}315}
316316
317fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {317fn 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 });
319 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);319 const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
320 try ctx.args.append(full_path_with_null.ptr);320 try ctx.args.append(full_path_with_null.ptr);
321}321}
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...@@ -757,7 +757,7 @@ async fn fmtPath(fmt: *Fmt, file_path_ref: []const u8, check_mode: bool) FmtErro
757 var group = event.Group(FmtError!void).init(fmt.loop);757 var group = event.Group(FmtError!void).init(fmt.loop);
758 while (try dir.next()) |entry| {758 while (try dir.next()) |entry| {
759 if (entry.kind == std.os.Dir.Entry.Kind.Directory or mem.endsWith(u8, entry.name, ".zig")) {759 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 });
761 try group.call(fmtPath, fmt, full_path, check_mode);761 try group.call(fmtPath, fmt, full_path, check_mode);
762 }762 }
763 }763 }
src-self-hosted/test.zig+2-2
...@@ -87,7 +87,7 @@ pub const TestContext = struct {...@@ -87,7 +87,7 @@ pub const TestContext = struct {
87 ) !void {87 ) !void {
88 var file_index_buf: [20]u8 = undefined;88 var file_index_buf: [20]u8 = undefined;
89 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());89 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
92 if (std.os.path.dirname(file1_path)) |dirname| {92 if (std.os.path.dirname(file1_path)) |dirname| {
93 try std.os.makePath(allocator, dirname);93 try std.os.makePath(allocator, dirname);
...@@ -120,7 +120,7 @@ pub const TestContext = struct {...@@ -120,7 +120,7 @@ pub const TestContext = struct {
120 ) !void {120 ) !void {
121 var file_index_buf: [20]u8 = undefined;121 var file_index_buf: [20]u8 = undefined;
122 const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());122 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
125 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());125 const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
126 if (std.os.path.dirname(file1_path)) |dirname| {126 if (std.os.path.dirname(file1_path)) |dirname| {
src/all_types.hpp+8-5
...@@ -544,12 +544,7 @@ struct AstNodeDefer {...@@ -544,12 +544,7 @@ struct AstNodeDefer {
544};544};
545545
546struct AstNodeVariableDeclaration {546struct AstNodeVariableDeclaration {
547 VisibMod visib_mod;
548 Buf *symbol;547 Buf *symbol;
549 bool is_const;
550 bool is_comptime;
551 bool is_export;
552 bool is_extern;
553 // one or both of type and expr will be non null548 // one or both of type and expr will be non null
554 AstNode *type;549 AstNode *type;
555 AstNode *expr;550 AstNode *expr;
...@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {...@@ -559,6 +554,13 @@ struct AstNodeVariableDeclaration {
559 AstNode *align_expr;554 AstNode *align_expr;
560 // populated if the "section(S)" is present555 // populated if the "section(S)" is present
561 AstNode *section_expr;556 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;
562};564};
563565
564struct AstNodeTestDecl {566struct AstNodeTestDecl {
...@@ -1873,6 +1875,7 @@ struct ZigVar {...@@ -1873,6 +1875,7 @@ struct ZigVar {
1873 bool shadowable;1875 bool shadowable;
1874 bool src_is_const;1876 bool src_is_const;
1875 bool gen_is_const;1877 bool gen_is_const;
1878 bool is_thread_local;
1876};1879};
18771880
1878struct ErrorTableEntry {1881struct ErrorTableEntry {
src/analyze.cpp+45-24
...@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum...@@ -28,28 +28,10 @@ static Error ATTRIBUTE_MUST_USE resolve_enum_zero_bits(CodeGen *g, ZigType *enum
28static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);28static Error ATTRIBUTE_MUST_USE resolve_union_zero_bits(CodeGen *g, ZigType *union_type);
29static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);29static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry);
3030
31ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg) {31static ErrorMsg *add_error_note_token(CodeGen *g, ErrorMsg *parent_msg, ImportTableEntry *owner, Token *token,
32 if (node->owner->c_import_node != nullptr) {32 Buf *msg)
33 // if this happens, then translate_c generated code that33{
34 // failed semantic analysis, which isn't supposed to happen34 if (owner->c_import_node != nullptr) {
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) {
53 // if this happens, then translate_c generated code that35 // if this happens, then translate_c generated code that
54 // failed semantic analysis, which isn't supposed to happen36 // 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...@@ -64,13 +46,46 @@ ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *m
64 return note;46 return note;
65 }47 }
6648
67 ErrorMsg *err = err_msg_create_with_line(node->owner->path, node->line, node->column,49 ErrorMsg *err = err_msg_create_with_line(owner->path, token->start_line, token->start_column,
68 node->owner->source_code, node->owner->line_offsets, msg);50 owner->source_code, owner->line_offsets, msg);
6951
70 err_msg_add_note(parent_msg, err);52 err_msg_add_note(parent_msg, err);
71 return err;53 return err;
72}54}
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
74ZigType *new_type_table_entry(ZigTypeId id) {89ZigType *new_type_table_entry(ZigTypeId id) {
75 ZigType *entry = allocate<ZigType>(1);90 ZigType *entry = allocate<ZigType>(1);
76 entry->id = id;91 entry->id = id;
...@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {...@@ -3668,6 +3683,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
3668 bool is_const = var_decl->is_const;3683 bool is_const = var_decl->is_const;
3669 bool is_extern = var_decl->is_extern;3684 bool is_extern = var_decl->is_extern;
3670 bool is_export = var_decl->is_export;3685 bool is_export = var_decl->is_export;
3686 bool is_thread_local = var_decl->threadlocal_tok != nullptr;
36713687
3672 ZigType *explicit_type = nullptr;3688 ZigType *explicit_type = nullptr;
3673 if (var_decl->type) {3689 if (var_decl->type) {
...@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {...@@ -3727,6 +3743,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
3727 tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,3743 tld_var->var = add_variable(g, source_node, tld_var->base.parent_scope, var_decl->symbol,
3728 is_const, init_val, &tld_var->base, type);3744 is_const, init_val, &tld_var->base, type);
3729 tld_var->var->linkage = linkage;3745 tld_var->var->linkage = linkage;
3746 tld_var->var->is_thread_local = is_thread_local;
37303747
3731 if (implicit_type != nullptr && type_is_invalid(implicit_type)) {3748 if (implicit_type != nullptr && type_is_invalid(implicit_type)) {
3732 tld_var->var->var_type = g->builtin_types.entry_invalid;3749 tld_var->var->var_type = g->builtin_types.entry_invalid;
...@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {...@@ -3747,6 +3764,10 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
3747 }3764 }
3748 }3765 }
37493766
3767 if (is_thread_local && is_const) {
3768 add_node_error(g, source_node, buf_sprintf("threadlocal variable cannot be constant"));
3769 }
3770
3750 g->global_vars.append(tld_var);3771 g->global_vars.append(tld_var);
3751}3772}
37523773
src/analyze.hpp+1
...@@ -12,6 +12,7 @@...@@ -12,6 +12,7 @@
1212
13void semantic_analyze(CodeGen *g);13void semantic_analyze(CodeGen *g);
14ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);14ErrorMsg *add_node_error(CodeGen *g, AstNode *node, Buf *msg);
15ErrorMsg *add_token_error(CodeGen *g, ImportTableEntry *owner, Token *token, Buf *msg);
15ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);16ErrorMsg *add_error_note(CodeGen *g, ErrorMsg *parent_msg, AstNode *node, Buf *msg);
16ZigType *new_type_table_entry(ZigTypeId id);17ZigType *new_type_table_entry(ZigTypeId id);
17ZigType *get_pointer_to_type(CodeGen *g, ZigType *child_type, bool is_const);18ZigType *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) {...@@ -132,6 +132,10 @@ static const char *const_or_var_string(bool is_const) {
132 return is_const ? "const" : "var";132 return is_const ? "const" : "var";
133}133}
134134
135static const char *thread_local_string(Token *tok) {
136 return (tok == nullptr) ? "" : "threadlocal ";
137}
138
135const char *container_string(ContainerKind kind) {139const char *container_string(ContainerKind kind) {
136 switch (kind) {140 switch (kind) {
137 case ContainerKindEnum: return "enum";141 case ContainerKindEnum: return "enum";
...@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {...@@ -554,8 +558,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
554 {558 {
555 const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);559 const char *pub_str = visib_mod_string(node->data.variable_declaration.visib_mod);
556 const char *extern_str = extern_string(node->data.variable_declaration.is_extern);560 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);
557 const char *const_or_var = const_or_var_string(node->data.variable_declaration.is_const);562 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);
559 print_symbol(ar, node->data.variable_declaration.symbol);564 print_symbol(ar, node->data.variable_declaration.symbol);
560565
561 if (node->data.variable_declaration.type) {566 if (node->data.variable_declaration.type) {
src/codegen.cpp+80-77
...@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {...@@ -88,7 +88,7 @@ static const char *symbols_that_llvm_depends_on[] = {
88};88};
8989
90CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,90CodeGen *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)
92{92{
93 CodeGen *g = allocate<CodeGen>(1);93 CodeGen *g = allocate<CodeGen>(1);
9494
...@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out...@@ -96,8 +96,12 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out
9696
97 g->zig_lib_dir = zig_lib_dir;97 g->zig_lib_dir = zig_lib_dir;
9898
99 g->zig_std_dir = buf_alloc();99 if (override_std_dir == nullptr) {
100 os_path_join(zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir);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
102 g->zig_c_headers_dir = buf_alloc();106 g->zig_c_headers_dir = buf_alloc();
103 os_path_join(zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir);107 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...@@ -2582,6 +2586,8 @@ static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast
25822586
2583}2587}
25842588
2589typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *);
2590
2585static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,2591static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
2586 IrInstructionBinOp *bin_op_instruction)2592 IrInstructionBinOp *bin_op_instruction)
2587{2593{
...@@ -2640,50 +2646,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -2640,50 +2646,71 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
2640 } else {2646 } else {
2641 zig_unreachable();2647 zig_unreachable();
2642 }2648 }
2649 case IrBinOpMult:
2650 case IrBinOpMultWrap:
2643 case IrBinOpAdd:2651 case IrBinOpAdd:
2644 case IrBinOpAddWrap:2652 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
2645 if (type_entry->id == ZigTypeIdPointer) {2676 if (type_entry->id == ZigTypeIdPointer) {
2646 assert(type_entry->data.pointer.ptr_len == PtrLenUnknown);2677 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
2647 // TODO runtime safety2693 // TODO runtime safety
2648 return LLVMBuildInBoundsGEP(g->builder, op1_value, &op2_value, 1, "");2694 return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, "");
2649 } else if (type_entry->id == ZigTypeIdFloat) {2695 } else if (type_entry->id == ZigTypeIdFloat) {
2650 ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base));2696 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, "");
2652 } else if (type_entry->id == ZigTypeIdInt) {2698 } else if (type_entry->id == ZigTypeIdInt) {
2653 bool is_wrapping = (op_id == IrBinOpAddWrap);
2654 if (is_wrapping) {2699 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, "");
2656 } else if (want_runtime_safety) {2701 } 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);
2658 } else if (type_entry->data.integral.is_signed) {2705 } 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, "");
2660 } else {2707 } else {
2661 return LLVMBuildNUWAdd(g->builder, op1_value, op2_value, "");2708 return unsigned_op[add_sub_mul](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();
2683 }2709 }
2684 } else {2710 } else {
2685 zig_unreachable();2711 zig_unreachable();
2686 }2712 }
2713 }
2687 case IrBinOpBinOr:2714 case IrBinOpBinOr:
2688 return LLVMBuildOr(g->builder, op1_value, op2_value, "");2715 return LLVMBuildOr(g->builder, op1_value, op2_value, "");
2689 case IrBinOpBinXor:2716 case IrBinOpBinXor:
...@@ -2728,49 +2755,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,...@@ -2728,49 +2755,6 @@ static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutable *executable,
2728 return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");2755 return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, "");
2729 }2756 }
2730 }2757 }
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 }
2774 case IrBinOpDivUnspecified:2758 case IrBinOpDivUnspecified:
2775 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),2759 return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base),
2776 op1_value, op2_value, type_entry, DivKindFloat);2760 op1_value, op2_value, type_entry, DivKindFloat);
...@@ -6361,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) {...@@ -6361,6 +6345,12 @@ static void validate_inline_fns(CodeGen *g) {
6361 report_errors_and_maybe_exit(g);6345 report_errors_and_maybe_exit(g);
6362}6346}
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
6364static void do_code_gen(CodeGen *g) {6354static void do_code_gen(CodeGen *g) {
6365 assert(!g->errors.length);6355 assert(!g->errors.length);
63666356
...@@ -6445,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6445,6 +6435,7 @@ static void do_code_gen(CodeGen *g) {
6445 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);6435 maybe_import_dll(g, global_value, GlobalLinkageIdStrong);
6446 LLVMSetAlignment(global_value, var->align_bytes);6436 LLVMSetAlignment(global_value, var->align_bytes);
6447 LLVMSetGlobalConstant(global_value, var->gen_is_const);6437 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6438 set_global_tls(g, var, global_value);
6448 }6439 }
6449 } else {6440 } else {
6450 bool exported = (var->linkage == VarLinkageExport);6441 bool exported = (var->linkage == VarLinkageExport);
...@@ -6470,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {...@@ -6470,6 +6461,7 @@ static void do_code_gen(CodeGen *g) {
6470 }6461 }
64716462
6472 LLVMSetGlobalConstant(global_value, var->gen_is_const);6463 LLVMSetGlobalConstant(global_value, var->gen_is_const);
6464 set_global_tls(g, var, global_value);
6473 }6465 }
64746466
6475 var->value_ref = global_value;6467 var->value_ref = global_value;
...@@ -7520,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {...@@ -7520,6 +7512,7 @@ static Error define_builtin_compile_vars(CodeGen *g) {
7520 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);7512 g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename);
7521 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);7513 g->root_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);
7522 g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package);7514 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);
7523 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);7516 g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents);
7524 scan_import(g, g->compile_var_import);7517 scan_import(g, g->compile_var_import);
75257518
...@@ -7560,7 +7553,13 @@ static void init(CodeGen *g) {...@@ -7560,7 +7553,13 @@ static void init(CodeGen *g) {
7560 LLVMTargetRef target_ref;7553 LLVMTargetRef target_ref;
7561 char *err_msg = nullptr;7554 char *err_msg = nullptr;
7562 if (LLVMGetTargetFromTriple(buf_ptr(&g->triple_str), &target_ref, &err_msg)) {7555 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);
7564 }7563 }
75657564
7566 bool is_optimized = g->build_mode != BuildModeDebug;7565 bool is_optimized = g->build_mode != BuildModeDebug;
...@@ -8349,8 +8348,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {...@@ -8349,8 +8348,12 @@ static void add_cache_pkg(CodeGen *g, CacheHash *ch, PackageTableEntry *pkg) {
8349 if (!entry)8348 if (!entry)
8350 break;8349 break;
83518350
8352 cache_buf(ch, entry->key);8351 // TODO: I think we need a more sophisticated detection of
8353 add_cache_pkg(g, ch, entry->value);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 }
8354 }8357 }
8355}8358}
83568359
src/codegen.hpp+1-1
...@@ -15,7 +15,7 @@...@@ -15,7 +15,7 @@
15#include <stdio.h>15#include <stdio.h>
1616
17CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,17CodeGen *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
20void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);20void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
21void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);21void 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...@@ -5204,6 +5204,10 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
5204 add_node_error(irb->codegen, variable_declaration->section_expr,5204 add_node_error(irb->codegen, variable_declaration->section_expr,
5205 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));5205 buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
5206 }5206 }
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
5208 // Temporarily set the name of the IrExecutable to the VariableDeclaration5212 // Temporarily set the name of the IrExecutable to the VariableDeclaration
5209 // so that the struct or enum from the init expression inherits the name.5213 // 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)...@@ -42,7 +42,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path)
42 }42 }
4343
44 CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,44 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
47 child_gen->out_h_path = nullptr;47 child_gen->out_h_path = nullptr;
48 child_gen->verbose_tokenize = parent_gen->verbose_tokenize;48 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) {...@@ -74,6 +74,7 @@ static int print_full_usage(const char *arg0) {
74 " -dirafter [dir] same as -isystem but do it last\n"74 " -dirafter [dir] same as -isystem but do it last\n"
75 " -isystem [dir] add additional search path for other .h files\n"75 " -isystem [dir] add additional search path for other .h files\n"
76 " -mllvm [arg] forward an arg to LLVM's option processing\n"76 " -mllvm [arg] forward an arg to LLVM's option processing\n"
77 " --override-std-dir [arg] use an alternate Zig standard library\n"
77 "\n"78 "\n"
78 "Link Options:\n"79 "Link Options:\n"
79 " --dynamic-linker [path] set the path to ld.so\n"80 " --dynamic-linker [path] set the path to ld.so\n"
...@@ -395,6 +396,7 @@ int main(int argc, char **argv) {...@@ -395,6 +396,7 @@ int main(int argc, char **argv) {
395 bool system_linker_hack = false;396 bool system_linker_hack = false;
396 TargetSubsystem subsystem = TargetSubsystemAuto;397 TargetSubsystem subsystem = TargetSubsystemAuto;
397 bool is_single_threaded = false;398 bool is_single_threaded = false;
399 Buf *override_std_dir = nullptr;
398400
399 if (argc >= 2 && strcmp(argv[1], "build") == 0) {401 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
400 Buf zig_exe_path_buf = BUF_INIT;402 Buf zig_exe_path_buf = BUF_INIT;
...@@ -430,7 +432,8 @@ int main(int argc, char **argv) {...@@ -430,7 +432,8 @@ int main(int argc, char **argv) {
430 Buf *build_runner_path = buf_alloc();432 Buf *build_runner_path = buf_alloc();
431 os_path_join(get_zig_special_dir(), buf_create_from_str("build_runner.zig"), build_runner_path);433 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);
434 g->enable_time_report = timing_info;437 g->enable_time_report = timing_info;
435 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);438 buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
436 codegen_set_out_name(g, buf_create_from_str("build"));439 codegen_set_out_name(g, buf_create_from_str("build"));
...@@ -645,6 +648,8 @@ int main(int argc, char **argv) {...@@ -645,6 +648,8 @@ int main(int argc, char **argv) {
645 clang_argv.append(argv[i]);648 clang_argv.append(argv[i]);
646649
647 llvm_argv.append(argv[i]);650 llvm_argv.append(argv[i]);
651 } else if (strcmp(arg, "--override-std-dir") == 0) {
652 override_std_dir = buf_create_from_str(argv[i]);
648 } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {653 } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
649 lib_dirs.append(argv[i]);654 lib_dirs.append(argv[i]);
650 } else if (strcmp(arg, "--library") == 0) {655 } else if (strcmp(arg, "--library") == 0) {
...@@ -819,7 +824,7 @@ int main(int argc, char **argv) {...@@ -819,7 +824,7 @@ int main(int argc, char **argv) {
819824
820 switch (cmd) {825 switch (cmd) {
821 case CmdBuiltin: {826 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);
823 g->is_single_threaded = is_single_threaded;828 g->is_single_threaded = is_single_threaded;
824 Buf *builtin_source = codegen_generate_builtin_source(g);829 Buf *builtin_source = codegen_generate_builtin_source(g);
825 if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) {830 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) {...@@ -878,7 +883,8 @@ int main(int argc, char **argv) {
878 if (cmd == CmdRun && buf_out_name == nullptr) {883 if (cmd == CmdRun && buf_out_name == nullptr) {
879 buf_out_name = buf_create_from_str("run");884 buf_out_name = buf_create_from_str("run");
880 }885 }
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);
882 g->subsystem = subsystem;888 g->subsystem = subsystem;
883889
884 if (disable_pic) {890 if (disable_pic) {
src/parser.cpp+14-8
...@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {...@@ -844,12 +844,17 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc) {
844844
845// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON845// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON
846static AstNode *ast_parse_var_decl(ParseContext *pc) {846static AstNode *ast_parse_var_decl(ParseContext *pc) {
847 Token *first = eat_token_if(pc, TokenIdKeywordConst);847 Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal);
848 if (first == nullptr)848 Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst);
849 first = eat_token_if(pc, TokenIdKeywordVar);849 if (mut_kw == nullptr)
850 if (first == nullptr)850 mut_kw = eat_token_if(pc, TokenIdKeywordVar);
851 return nullptr;851 if (mut_kw == nullptr) {
852852 if (thread_local_kw == nullptr) {
853 return nullptr;
854 } else {
855 ast_invalid_token_error(pc, peek_token(pc));
856 }
857 }
853 Token *identifier = expect_token(pc, TokenIdSymbol);858 Token *identifier = expect_token(pc, TokenIdSymbol);
854 AstNode *type_expr = nullptr;859 AstNode *type_expr = nullptr;
855 if (eat_token_if(pc, TokenIdColon) != nullptr)860 if (eat_token_if(pc, TokenIdColon) != nullptr)
...@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {...@@ -863,8 +868,9 @@ static AstNode *ast_parse_var_decl(ParseContext *pc) {
863868
864 expect_token(pc, TokenIdSemicolon);869 expect_token(pc, TokenIdSemicolon);
865870
866 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, first);871 AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw);
867 res->data.variable_declaration.is_const = first->id == TokenIdKeywordConst;872 res->data.variable_declaration.threadlocal_tok = thread_local_kw;
873 res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst;
868 res->data.variable_declaration.symbol = token_buf(identifier);874 res->data.variable_declaration.symbol = token_buf(identifier);
869 res->data.variable_declaration.type = type_expr;875 res->data.variable_declaration.type = type_expr;
870 res->data.variable_declaration.align_expr = align_expr;876 res->data.variable_declaration.align_expr = align_expr;
src/tokenizer.cpp+2
...@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {...@@ -146,6 +146,7 @@ static const struct ZigKeyword zig_keywords[] = {
146 {"suspend", TokenIdKeywordSuspend},146 {"suspend", TokenIdKeywordSuspend},
147 {"switch", TokenIdKeywordSwitch},147 {"switch", TokenIdKeywordSwitch},
148 {"test", TokenIdKeywordTest},148 {"test", TokenIdKeywordTest},
149 {"threadlocal", TokenIdKeywordThreadLocal},
149 {"true", TokenIdKeywordTrue},150 {"true", TokenIdKeywordTrue},
150 {"try", TokenIdKeywordTry},151 {"try", TokenIdKeywordTry},
151 {"undefined", TokenIdKeywordUndefined},152 {"undefined", TokenIdKeywordUndefined},
...@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {...@@ -1586,6 +1587,7 @@ const char * token_name(TokenId id) {
1586 case TokenIdKeywordStruct: return "struct";1587 case TokenIdKeywordStruct: return "struct";
1587 case TokenIdKeywordSwitch: return "switch";1588 case TokenIdKeywordSwitch: return "switch";
1588 case TokenIdKeywordTest: return "test";1589 case TokenIdKeywordTest: return "test";
1590 case TokenIdKeywordThreadLocal: return "threadlocal";
1589 case TokenIdKeywordTrue: return "true";1591 case TokenIdKeywordTrue: return "true";
1590 case TokenIdKeywordTry: return "try";1592 case TokenIdKeywordTry: return "try";
1591 case TokenIdKeywordUndefined: return "undefined";1593 case TokenIdKeywordUndefined: return "undefined";
src/tokenizer.hpp+1
...@@ -88,6 +88,7 @@ enum TokenId {...@@ -88,6 +88,7 @@ enum TokenId {
88 TokenIdKeywordSuspend,88 TokenIdKeywordSuspend,
89 TokenIdKeywordSwitch,89 TokenIdKeywordSwitch,
90 TokenIdKeywordTest,90 TokenIdKeywordTest,
91 TokenIdKeywordThreadLocal,
91 TokenIdKeywordTrue,92 TokenIdKeywordTrue,
92 TokenIdKeywordTry,93 TokenIdKeywordTry,
93 TokenIdKeywordUndefined,94 TokenIdKeywordUndefined,
std/build.zig+68-19
...@@ -145,8 +145,8 @@ pub const Builder = struct {...@@ -145,8 +145,8 @@ pub const Builder = struct {
145145
146 pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {146 pub fn setInstallPrefix(self: *Builder, maybe_prefix: ?[]const u8) void {
147 self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default147 self.prefix = maybe_prefix orelse "/usr/local"; // TODO better default
148 self.lib_dir = os.path.join(self.allocator, self.prefix, "lib") 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, self.prefix, "bin") catch unreachable;149 self.exe_dir = os.path.join(self.allocator, [][]const u8{ self.prefix, "bin" }) catch unreachable;
150 }150 }
151151
152 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {152 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {
...@@ -618,7 +618,10 @@ pub const Builder = struct {...@@ -618,7 +618,10 @@ pub const Builder = struct {
618618
619 ///::dest_rel_path is relative to prefix path or it can be an absolute path619 ///::dest_rel_path is relative to prefix path or it can be an absolute path
620 pub fn addInstallFile(self: *Builder, src_path: []const u8, dest_rel_path: []const u8) *InstallFileStep {620 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;
622 self.pushInstalledFile(full_dest_path);625 self.pushInstalledFile(full_dest_path);
623626
624 const install_step = self.allocator.create(InstallFileStep) catch unreachable;627 const install_step = self.allocator.create(InstallFileStep) catch unreachable;
...@@ -653,7 +656,7 @@ pub const Builder = struct {...@@ -653,7 +656,7 @@ pub const Builder = struct {
653 }656 }
654657
655 fn pathFromRoot(self: *Builder, rel_path: []const u8) []u8 {658 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;
657 }660 }
658661
659 pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 {662 pub fn fmt(self: *Builder, comptime format: []const u8, args: ...) []u8 {
...@@ -676,7 +679,7 @@ pub const Builder = struct {...@@ -676,7 +679,7 @@ pub const Builder = struct {
676 if (os.path.isAbsolute(name)) {679 if (os.path.isAbsolute(name)) {
677 return name;680 return name;
678 }681 }
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) });
680 if (os.path.real(self.allocator, full_path)) |real_path| {683 if (os.path.real(self.allocator, full_path)) |real_path| {
681 return real_path;684 return real_path;
682 } else |_| {685 } else |_| {
...@@ -691,7 +694,7 @@ pub const Builder = struct {...@@ -691,7 +694,7 @@ pub const Builder = struct {
691 }694 }
692 var it = mem.tokenize(PATH, []u8{os.path.delimiter});695 var it = mem.tokenize(PATH, []u8{os.path.delimiter});
693 while (it.next()) |path| {696 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) });
695 if (os.path.real(self.allocator, full_path)) |real_path| {698 if (os.path.real(self.allocator, full_path)) |real_path| {
696 return real_path;699 return real_path;
697 } else |_| {700 } else |_| {
...@@ -705,7 +708,7 @@ pub const Builder = struct {...@@ -705,7 +708,7 @@ pub const Builder = struct {
705 return name;708 return name;
706 }709 }
707 for (paths) |path| {710 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) });
709 if (os.path.real(self.allocator, full_path)) |real_path| {712 if (os.path.real(self.allocator, full_path)) |real_path| {
710 return real_path;713 return real_path;
711 } else |_| {714 } else |_| {
...@@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct {...@@ -1113,7 +1116,10 @@ pub const LibExeObjStep = struct {
1113 }1116 }
11141117
1115 pub fn getOutputPath(self: *LibExeObjStep) []const u8 {1118 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;
1117 }1123 }
11181124
1119 pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void {1125 pub fn setOutputHPath(self: *LibExeObjStep, file_path: []const u8) void {
...@@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct {...@@ -1126,7 +1132,10 @@ pub const LibExeObjStep = struct {
1126 }1132 }
11271133
1128 pub fn getOutputHPath(self: *LibExeObjStep) []const u8 {1134 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;
1130 }1139 }
11311140
1132 pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {1141 pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void {
...@@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct {...@@ -1226,7 +1235,10 @@ pub const LibExeObjStep = struct {
1226 }1235 }
12271236
1228 if (self.build_options_contents.len() > 0) {1237 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 );
1230 try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst());1242 try std.io.writeFile(build_options_file, self.build_options_contents.toSliceConst());
1231 try zig_args.append("--pkg-begin");1243 try zig_args.append("--pkg-begin");
1232 try zig_args.append("build_options");1244 try zig_args.append("build_options");
...@@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct {...@@ -1476,7 +1488,10 @@ pub const LibExeObjStep = struct {
1476 cc_args.append("-c") catch unreachable;1488 cc_args.append("-c") catch unreachable;
1477 cc_args.append(abs_source_file) catch unreachable;1489 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;
1480 if (os.path.dirname(cache_o_src)) |cache_o_dir| {1495 if (os.path.dirname(cache_o_src)) |cache_o_dir| {
1481 try builder.makePath(cache_o_dir);1496 try builder.makePath(cache_o_dir);
1482 }1497 }
...@@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct {...@@ -1528,7 +1543,10 @@ pub const LibExeObjStep = struct {
1528 cc_args.append("-current_version") catch unreachable;1543 cc_args.append("-current_version") catch unreachable;
1529 cc_args.append(builder.fmt("{}.{}.{}", self.version.major, self.version.minor, self.version.patch)) catch unreachable;1544 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);
1532 cc_args.append("-install_name") catch unreachable;1550 cc_args.append("-install_name") catch unreachable;
1533 cc_args.append(install_name) catch unreachable;1551 cc_args.append(install_name) catch unreachable;
1534 } else {1552 } else {
...@@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct {...@@ -1594,7 +1612,10 @@ pub const LibExeObjStep = struct {
1594 cc_args.append("-c") catch unreachable;1612 cc_args.append("-c") catch unreachable;
1595 cc_args.append(abs_source_file) catch unreachable;1613 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;
1598 if (os.path.dirname(cache_o_src)) |cache_o_dir| {1619 if (os.path.dirname(cache_o_src)) |cache_o_dir| {
1599 try builder.makePath(cache_o_dir);1620 try builder.makePath(cache_o_dir);
1600 }1621 }
...@@ -1686,6 +1707,7 @@ pub const TestStep = struct {...@@ -1686,6 +1707,7 @@ pub const TestStep = struct {
1686 no_rosegment: bool,1707 no_rosegment: bool,
1687 output_path: ?[]const u8,1708 output_path: ?[]const u8,
1688 system_linker_hack: bool,1709 system_linker_hack: bool,
1710 override_std_dir: ?[]const u8,
16891711
1690 pub fn init(builder: *Builder, root_src: []const u8) TestStep {1712 pub fn init(builder: *Builder, root_src: []const u8) TestStep {
1691 const step_name = builder.fmt("test {}", root_src);1713 const step_name = builder.fmt("test {}", root_src);
...@@ -1707,6 +1729,7 @@ pub const TestStep = struct {...@@ -1707,6 +1729,7 @@ pub const TestStep = struct {
1707 .no_rosegment = false,1729 .no_rosegment = false,
1708 .output_path = null,1730 .output_path = null,
1709 .system_linker_hack = false,1731 .system_linker_hack = false,
1732 .override_std_dir = null,
1710 };1733 };
1711 }1734 }
17121735
...@@ -1737,6 +1760,10 @@ pub const TestStep = struct {...@@ -1737,6 +1760,10 @@ pub const TestStep = struct {
1737 self.build_mode = mode;1760 self.build_mode = mode;
1738 }1761 }
17391762
1763 pub fn overrideStdDir(self: *TestStep, dir_path: []const u8) void {
1764 self.override_std_dir = dir_path;
1765 }
1766
1740 pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {1767 pub fn setOutputPath(self: *TestStep, file_path: []const u8) void {
1741 self.output_path = file_path;1768 self.output_path = file_path;
17421769
...@@ -1751,7 +1778,10 @@ pub const TestStep = struct {...@@ -1751,7 +1778,10 @@ pub const TestStep = struct {
1751 return output_path;1778 return output_path;
1752 } else {1779 } else {
1753 const basename = self.builder.fmt("test{}", self.target.exeFileExt());1780 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;
1755 }1785 }
1756 }1786 }
17571787
...@@ -1914,6 +1944,10 @@ pub const TestStep = struct {...@@ -1914,6 +1944,10 @@ pub const TestStep = struct {
1914 if (self.system_linker_hack) {1944 if (self.system_linker_hack) {
1915 try zig_args.append("--system-linker-hack");1945 try zig_args.append("--system-linker-hack");
1916 }1946 }
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
1918 try builder.spawnChild(zig_args.toSliceConst());1952 try builder.spawnChild(zig_args.toSliceConst());
1919 }1953 }
...@@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct {...@@ -1969,13 +2003,22 @@ const InstallArtifactStep = struct {
1969 .builder = builder,2003 .builder = builder,
1970 .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),2004 .step = Step.init(builder.fmt("install {}", artifact.step.name), builder.allocator, make),
1971 .artifact = artifact,2005 .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,
1973 };2010 };
1974 self.step.dependOn(&artifact.step);2011 self.step.dependOn(&artifact.step);
1975 builder.pushInstalledFile(self.dest_file);2012 builder.pushInstalledFile(self.dest_file);
1976 if (self.artifact.kind == LibExeObjStep.Kind.Lib and !self.artifact.static) {2013 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);2014 builder.pushInstalledFile(os.path.join(
1978 builder.pushInstalledFile(os.path.join(builder.allocator, builder.lib_dir, artifact.name_only_filename) catch unreachable);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);
1979 }2022 }
1980 return self;2023 return self;
1981 }2024 }
...@@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj...@@ -2131,13 +2174,19 @@ fn doAtomicSymLinks(allocator: *Allocator, output_path: []const u8, filename_maj
2131 const out_dir = os.path.dirname(output_path) orelse ".";2174 const out_dir = os.path.dirname(output_path) orelse ".";
2132 const out_basename = os.path.basename(output_path);2175 const out_basename = os.path.basename(output_path);
2133 // sym link for libfoo.so.1 to libfoo.so.1.2.32176 // 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;
2135 os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {2181 os.atomicSymLink(allocator, out_basename, major_only_path) catch |err| {
2136 warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);2182 warn("Unable to symlink {} -> {}\n", major_only_path, out_basename);
2137 return err;2183 return err;
2138 };2184 };
2139 // sym link for libfoo.so to libfoo.so.12185 // 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;
2141 os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {2190 os.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| {
2142 warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);2191 warn("Unable to symlink {} -> {}\n", name_only_path, filename_major_only);
2143 return err;2192 return err;
std/debug/index.zig+2-3
...@@ -37,7 +37,6 @@ const Module = struct {...@@ -37,7 +37,6 @@ const Module = struct {
37var stderr_file: os.File = undefined;37var stderr_file: os.File = undefined;
38var stderr_file_out_stream: os.File.OutStream = undefined;38var stderr_file_out_stream: os.File.OutStream = undefined;
3939
40/// TODO multithreaded awareness
41var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;40var stderr_stream: ?*io.OutStream(os.File.WriteError) = null;
42var stderr_mutex = std.Mutex.init();41var stderr_mutex = std.Mutex.init();
43pub fn warn(comptime fmt: []const u8, args: ...) void {42pub fn warn(comptime fmt: []const u8, args: ...) void {
...@@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {...@@ -775,7 +774,7 @@ fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
775 const len = try di.coff.getPdbPath(path_buf[0..]);774 const len = try di.coff.getPdbPath(path_buf[0..]);
776 const raw_path = path_buf[0..len];775 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
780 try di.pdb.openFile(di.coff, path);779 try di.pdb.openFile(di.coff, path);
781780
...@@ -1353,7 +1352,7 @@ const LineNumberProgram = struct {...@@ -1353,7 +1352,7 @@ const LineNumberProgram = struct {
1353 return error.InvalidDebugInfo;1352 return error.InvalidDebugInfo;
1354 } else1353 } else
1355 self.include_dirs[file_entry.dir_index];1354 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 });
1357 errdefer self.file_entries.allocator.free(file_name);1356 errdefer self.file_entries.allocator.free(file_name);
1358 return LineInfo{1357 return LineInfo{
1359 .line = if (self.prev_line >= 0) @intCast(usize, self.prev_line) else 0,1358 .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 {...@@ -871,7 +871,7 @@ pub fn Watch(comptime V: type) type {
871 }871 }
872872
873 async fn addFileKEvent(self: *Self, file_path: []const u8, value: V) !?V {873 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});
875 var resolved_path_consumed = false;875 var resolved_path_consumed = false;
876 defer if (!resolved_path_consumed) self.channel.loop.allocator.free(resolved_path);876 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 {...@@ -1336,7 +1336,7 @@ async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
1336}1336}
13371337
1338async fn testFsWatch(loop: *Loop) !void {1338async 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" });
1340 defer loop.allocator.free(file_path);1340 defer loop.allocator.free(file_path);
13411341
1342 const contents =1342 const contents =
std/heap.zig+2-5
...@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {...@@ -106,9 +106,7 @@ pub const DirectAllocator = struct {
106 };106 };
107 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;107 const ptr = os.windows.HeapAlloc(heap_handle, 0, amt) orelse return error.OutOfMemory;
108 const root_addr = @ptrToInt(ptr);108 const root_addr = @ptrToInt(ptr);
109 const rem = @rem(root_addr, alignment);109 const adjusted_addr = mem.alignForward(root_addr, alignment);
110 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
111 const adjusted_addr = root_addr + march_forward_bytes;
112 const record_addr = adjusted_addr + n;110 const record_addr = adjusted_addr + n;
113 @intToPtr(*align(1) usize, record_addr).* = root_addr;111 @intToPtr(*align(1) usize, record_addr).* = root_addr;
114 return @intToPtr([*]u8, adjusted_addr)[0..n];112 return @intToPtr([*]u8, adjusted_addr)[0..n];
...@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {...@@ -126,8 +124,7 @@ pub const DirectAllocator = struct {
126 const base_addr = @ptrToInt(old_mem.ptr);124 const base_addr = @ptrToInt(old_mem.ptr);
127 const old_addr_end = base_addr + old_mem.len;125 const old_addr_end = base_addr + old_mem.len;
128 const new_addr_end = base_addr + new_size;126 const new_addr_end = base_addr + new_size;
129 const rem = @rem(new_addr_end, os.page_size);127 const new_addr_end_rounded = mem.alignForward(new_addr_end, os.page_size);
130 const new_addr_end_rounded = new_addr_end + if (rem == 0) 0 else (os.page_size - rem);
131 if (old_addr_end > new_addr_end_rounded) {128 if (old_addr_end > new_addr_end_rounded) {
132 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);129 _ = os.posix.munmap(new_addr_end_rounded, old_addr_end - new_addr_end_rounded);
133 }130 }
std/index.zig+1-1
...@@ -33,8 +33,8 @@ pub const io = @import("io.zig");...@@ -33,8 +33,8 @@ pub const io = @import("io.zig");
33pub const json = @import("json.zig");33pub const json = @import("json.zig");
34pub const macho = @import("macho.zig");34pub const macho = @import("macho.zig");
35pub const math = @import("math/index.zig");35pub const math = @import("math/index.zig");
36pub const meta = @import("meta/index.zig");
37pub const mem = @import("mem.zig");36pub const mem = @import("mem.zig");
37pub const meta = @import("meta/index.zig");
38pub const net = @import("net.zig");38pub const net = @import("net.zig");
39pub const os = @import("os/index.zig");39pub const os = @import("os/index.zig");
40pub const pdb = @import("pdb.zig");40pub const pdb = @import("pdb.zig");
std/io.zig+5-5
...@@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {...@@ -912,7 +912,7 @@ pub fn BitOutStream(endian: builtin.Endian, comptime Error: type) type {
912 }912 }
913913
914 /// Flush any remaining bits to the stream.914 /// Flush any remaining bits to the stream.
915 pub fn flushBits(self: *Self) !void {915 pub fn flushBits(self: *Self) Error!void {
916 if (self.bit_count == 0) return;916 if (self.bit_count == 0) return;
917 try self.out_stream.writeByte(self.bit_buffer);917 try self.out_stream.writeByte(self.bit_buffer);
918 self.bit_buffer = 0;918 self.bit_buffer = 0;
...@@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E...@@ -1079,7 +1079,7 @@ pub fn Deserializer(comptime endian: builtin.Endian, is_packed: bool, comptime E
1079 }1079 }
10801080
1081 //@BUG: inferred error issue. See: #1386 1081 //@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 {
1083 comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));1083 comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));
10841084
1085 const u8_bit_count = 8;1085 const u8_bit_count = 8;
...@@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com...@@ -1287,11 +1287,11 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
1287 }1287 }
12881288
1289 /// Flushes any unwritten bits to the stream1289 /// Flushes any unwritten bits to the stream
1290 pub fn flush(self: *Self) Stream.Error!void {1290 pub fn flush(self: *Self) Error!void {
1291 if (is_packed) return self.out_stream.flushBits();1291 if (is_packed) return self.out_stream.flushBits();
1292 }1292 }
12931293
1294 fn serializeInt(self: *Self, value: var) !void {1294 fn serializeInt(self: *Self, value: var) Error!void {
1295 const T = @typeOf(value);1295 const T = @typeOf(value);
1296 comptime assert(trait.is(builtin.TypeId.Int)(T) or trait.is(builtin.TypeId.Float)(T));1296 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...@@ -1323,7 +1323,7 @@ pub fn Serializer(comptime endian: builtin.Endian, comptime is_packed: bool, com
1323 }1323 }
13241324
1325 /// Serializes the passed value into the stream1325 /// 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 {
1327 const T = comptime @typeOf(value);1327 const T = comptime @typeOf(value);
13281328
1329 if (comptime trait.isIndexable(T)) {1329 if (comptime trait.isIndexable(T)) {
std/io_test.zig+10-1
...@@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa...@@ -357,6 +357,15 @@ fn testIntSerializerDeserializer(comptime endian: builtin.Endian, comptime is_pa
357 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;357 const total_packed_bytes = (total_bits / u8_bit_count) + extra_packed_byte;
358358
359 assert(in.pos == if (is_packed) total_packed_bytes else total_bytes);359 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();
360}369}
361370
362test "Serializer/Deserializer Int" {371test "Serializer/Deserializer Int" {
...@@ -568,4 +577,4 @@ test "Deserializer bad data" {...@@ -568,4 +577,4 @@ test "Deserializer bad data" {
568 try testBadData(builtin.Endian.Little, false);577 try testBadData(builtin.Endian.Little, false);
569 try testBadData(builtin.Endian.Big, true);578 try testBadData(builtin.Endian.Big, true);
570 try testBadData(builtin.Endian.Little, true);579 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 {...@@ -882,42 +882,40 @@ pub const SplitIterator = struct {
882 }882 }
883};883};
884884
885/// Naively combines a series of strings with a separator.885/// Naively combines a series of slices with a separator.
886/// Allocates memory for the result, which must be freed by the caller.886/// Allocates memory for the result, which must be freed by the caller.
887pub fn join(allocator: *Allocator, sep: u8, strings: ...) ![]u8 {887pub fn join(allocator: *Allocator, separator: []const u8, slices: []const []const u8) ![]u8 {
888 comptime assert(strings.len >= 1);888 if (slices.len == 0) return (([*]u8)(undefined))[0..0];
889 var total_strings_len: usize = strings.len; // 1 sep per string889
890 {890 const total_len = blk: {
891 comptime var string_i = 0;891 var sum: usize = separator.len * (slices.len - 1);
892 inline while (string_i < strings.len) : (string_i += 1) {892 for (slices) |slice|
893 const arg = ([]const u8)(strings[string_i]);893 sum += slice.len;
894 total_strings_len += arg.len;894 break :blk sum;
895 }895 };
896 }
897896
898 const buf = try allocator.alloc(u8, total_strings_len);897 const buf = try allocator.alloc(u8, total_len);
899 errdefer allocator.free(buf);898 errdefer allocator.free(buf);
900899
901 var buf_index: usize = 0;900 copy(u8, buf, slices[0]);
902 comptime var string_i = 0;901 var buf_index: usize = slices[0].len;
903 inline while (true) {902 for (slices[1..]) |slice| {
904 const arg = ([]const u8)(strings[string_i]);903 copy(u8, buf[buf_index..], separator);
905 string_i += 1;904 buf_index += separator.len;
906 copy(u8, buf[buf_index..], arg);905 copy(u8, buf[buf_index..], slice);
907 buf_index += arg.len;906 buf_index += slice.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 }
913 }907 }
914908
915 return allocator.shrink(u8, buf, buf_index);909 // No need for shrink since buf is exactly the correct size.
910 return buf;
916}911}
917912
918test "mem.join" {913test "mem.join" {
919 assert(eql(u8, try join(debug.global_allocator, ',', "a", "b", "c"), "a,b,c"));914 var buf: [1024]u8 = undefined;
920 assert(eql(u8, try join(debug.global_allocator, ',', "a"), "a"));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"));
921}919}
922920
923test "testStringEquality" {921test "testStringEquality" {
...@@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" {...@@ -1366,3 +1364,23 @@ test "std.mem.subArrayPtr" {
1366 sub2[1] = 'X';1364 sub2[1] = 'X';
1367 debug.assert(std.mem.eql(u8, a2, "abcXef"));1365 debug.assert(std.mem.eql(u8, a2, "abcXef"));
1368}1366}
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 {...@@ -574,7 +574,7 @@ pub const ChildProcess = struct {
574 // to match posix semantics574 // to match posix semantics
575 const app_name = x: {575 const app_name = x: {
576 if (self.cwd) |cwd| {576 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] });
578 defer self.allocator.free(resolved);578 defer self.allocator.free(resolved);
579 break :x try cstr.addNullByte(self.allocator, resolved);579 break :x try cstr.addNullByte(self.allocator, resolved);
580 } else {580 } else {
...@@ -597,10 +597,10 @@ pub const ChildProcess = struct {...@@ -597,10 +597,10 @@ pub const ChildProcess = struct {
597597
598 var it = mem.tokenize(PATH, ";");598 var it = mem.tokenize(PATH, ";");
599 while (it.next()) |search_path| {599 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 });
601 defer self.allocator.free(joined_path);601 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);
604 defer self.allocator.free(joined_path_w);604 defer self.allocator.free(joined_path_w);
605605
606 if (windowsCreateProcess(joined_path_w.ptr, cmd_line_w.ptr, envp_ptr, cwd_w_ptr, &siStartInfo, &piProcInfo)) |_| {606 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 {...@@ -610,6 +610,9 @@ pub const ChildProcess = struct {
610 } else {610 } else {
611 return err;611 return err;
612 }612 }
613 } else {
614 // Every other error would have been returned earlier.
615 return error.FileNotFound;
613 }616 }
614 };617 };
615618
std/os/get_app_data_dir.zig+3-4
...@@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD...@@ -30,7 +30,7 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
30 error.OutOfMemory => return error.OutOfMemory,30 error.OutOfMemory => return error.OutOfMemory,
31 };31 };
32 defer allocator.free(global_dir);32 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 });
34 },34 },
35 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,35 os.windows.E_OUTOFMEMORY => return error.OutOfMemory,
36 else => return error.AppDataDirUnavailable,36 else => return error.AppDataDirUnavailable,
...@@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD...@@ -41,14 +41,14 @@ pub fn getAppDataDir(allocator: *mem.Allocator, appname: []const u8) GetAppDataD
41 // TODO look in /etc/passwd41 // TODO look in /etc/passwd
42 return error.AppDataDirUnavailable;42 return error.AppDataDirUnavailable;
43 };43 };
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 });
45 },45 },
46 builtin.Os.linux, builtin.Os.freebsd => {46 builtin.Os.linux, builtin.Os.freebsd => {
47 const home_dir = os.getEnvPosix("HOME") orelse {47 const home_dir = os.getEnvPosix("HOME") orelse {
48 // TODO look in /etc/passwd48 // TODO look in /etc/passwd
49 return error.AppDataDirUnavailable;49 return error.AppDataDirUnavailable;
50 };50 };
51 return os.path.join(allocator, home_dir, ".local", "share", appname);51 return os.path.join(allocator, [][]const u8{ home_dir, ".local", "share", appname });
52 },52 },
53 else => @compileError("Unsupported OS"),53 else => @compileError("Unsupported OS"),
54 }54 }
...@@ -67,4 +67,3 @@ test "std.os.getAppDataDir" {...@@ -67,4 +67,3 @@ test "std.os.getAppDataDir" {
67 // We can't actually validate the result67 // We can't actually validate the result
68 _ = getAppDataDir(allocator, "zig") catch return;68 _ = getAppDataDir(allocator, "zig") catch return;
69}69}
70
std/os/index.zig+68-45
...@@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {...@@ -8,6 +8,10 @@ const is_posix = switch (builtin.os) {
8};8};
9const os = @This();9const os = @This();
1010
11comptime {
12 assert(@import("std") == std); // You have to run the std lib tests with --override-std-dir
13}
14
11test "std.os" {15test "std.os" {
12 _ = @import("child_process.zig");16 _ = @import("child_process.zig");
13 _ = @import("darwin.zig");17 _ = @import("darwin.zig");
...@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {...@@ -692,12 +696,7 @@ pub fn getBaseAddress() usize {
692 return base;696 return base;
693 }697 }
694 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);698 const phdr = linuxGetAuxVal(std.elf.AT_PHDR);
695 const ElfHeader = switch (@sizeOf(usize)) {699 return phdr - @sizeOf(std.elf.Ehdr);
696 4 => std.elf.Elf32_Ehdr,
697 8 => std.elf.Elf64_Ehdr,
698 else => @compileError("Unsupported architecture"),
699 };
700 return phdr - @sizeOf(ElfHeader);
701 },700 },
702 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),701 builtin.Os.macosx, builtin.Os.freebsd => return @ptrToInt(&std.c._mh_execute_header),
703 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),702 builtin.Os.windows => return @ptrToInt(windows.GetModuleHandleW(null)),
...@@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void {...@@ -1285,7 +1284,7 @@ pub fn makeDirPosix(dir_path: []const u8) !void {
1285/// already exists and is a directory.1284/// already exists and is a directory.
1286/// TODO determine if we can remove the allocator requirement from this function1285/// TODO determine if we can remove the allocator requirement from this function
1287pub fn makePath(allocator: *Allocator, full_path: []const u8) !void {1286pub 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});
1289 defer allocator.free(resolved_path);1288 defer allocator.free(resolved_path);
12901289
1291 var end_index: usize = resolved_path.len;1290 var end_index: usize = resolved_path.len;
...@@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {...@@ -2305,18 +2304,17 @@ pub fn selfExePath(out_buffer: *[MAX_PATH_BYTES]u8) ![]u8 {
2305 switch (builtin.os) {2304 switch (builtin.os) {
2306 Os.linux => return readLink(out_buffer, "/proc/self/exe"),2305 Os.linux => return readLink(out_buffer, "/proc/self/exe"),
2307 Os.freebsd => {2306 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 };
2309 var out_len: usize = out_buffer.len;2308 var out_len: usize = out_buffer.len;
2310 const err = posix.getErrno(posix.sysctl(&mib, 4, out_buffer, &out_len, null, 0));2309 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
2314 return switch (err) {2313 return switch (err) {
2315 posix.EFAULT => error.BadAdress,2314 posix.EFAULT => error.BadAdress,
2316 posix.EPERM => error.PermissionDenied,2315 posix.EPERM => error.PermissionDenied,
2317 else => unexpectedErrorPosix(err),2316 else => unexpectedErrorPosix(err),
2318 };2317 };
2319
2320 },2318 },
2321 Os.windows => {2319 Os.windows => {
2322 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;2320 var utf16le_buf: [windows_util.PATH_MAX_WIDE]u16 = undefined;
...@@ -2908,14 +2906,15 @@ pub const Thread = struct {...@@ -2908,14 +2906,15 @@ pub const Thread = struct {
2908 pub const Data = if (use_pthreads)2906 pub const Data = if (use_pthreads)
2909 struct {2907 struct {
2910 handle: Thread.Handle,2908 handle: Thread.Handle,
2911 stack_addr: usize,2909 mmap_addr: usize,
2912 stack_len: usize,2910 mmap_len: usize,
2913 }2911 }
2914 else switch (builtin.os) {2912 else switch (builtin.os) {
2915 builtin.Os.linux => struct {2913 builtin.Os.linux => struct {
2916 handle: Thread.Handle,2914 handle: Thread.Handle,
2917 stack_addr: usize,2915 mmap_addr: usize,
2918 stack_len: usize,2916 mmap_len: usize,
2917 tls_end_addr: usize,
2919 },2918 },
2920 builtin.Os.windows => struct {2919 builtin.Os.windows => struct {
2921 handle: Thread.Handle,2920 handle: Thread.Handle,
...@@ -2955,7 +2954,7 @@ pub const Thread = struct {...@@ -2955,7 +2954,7 @@ pub const Thread = struct {
2955 posix.EDEADLK => unreachable,2954 posix.EDEADLK => unreachable,
2956 else => unreachable,2955 else => unreachable,
2957 }2956 }
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);
2959 } else switch (builtin.os) {2958 } else switch (builtin.os) {
2960 builtin.Os.linux => {2959 builtin.Os.linux => {
2961 while (true) {2960 while (true) {
...@@ -2969,7 +2968,7 @@ pub const Thread = struct {...@@ -2969,7 +2968,7 @@ pub const Thread = struct {
2969 else => unreachable,2968 else => unreachable,
2970 }2969 }
2971 }2970 }
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);
2973 },2972 },
2974 builtin.Os.windows => {2973 builtin.Os.windows => {
2975 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);2974 assert(windows.WaitForSingleObject(self.data.handle, windows.INFINITE) == windows.WAIT_OBJECT_0);
...@@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{...@@ -3008,6 +3007,9 @@ pub const SpawnThreadError = error{
3008 Unexpected,3007 Unexpected,
3009};3008};
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
3011/// caller must call wait on the returned thread3013/// caller must call wait on the returned thread
3012/// fn startFn(@typeOf(context)) T3014/// fn startFn(@typeOf(context)) T
3013/// where T is u8, noreturn, void, or !void3015/// where T is u8, noreturn, void, or !void
...@@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -3097,42 +3099,56 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
30973099
3098 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;3100 const MAP_GROWSDOWN = if (builtin.os == builtin.Os.linux) linux.MAP_GROWSDOWN else 0;
30993101
3100 const mmap_len = default_stack_size;3102 var stack_end_offset: usize = undefined;
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);3103 var thread_start_offset: usize = undefined;
3102 if (stack_addr == posix.MAP_FAILED) return error.OutOfMemory;3104 var context_start_offset: usize = undefined;
3103 errdefer assert(posix.munmap(stack_addr, mmap_len) == 0);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;
3106 var arg: usize = undefined;3138 var arg: usize = undefined;
3107 if (@sizeOf(Context) != 0) {3139 if (@sizeOf(Context) != 0) {
3108 stack_end -= @sizeOf(Context);3140 arg = mmap_addr + context_start_offset;
3109 stack_end -= stack_end % @alignOf(Context);3141 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, arg));
3110 assert(stack_end >= stack_addr);
3111 const context_ptr = @alignCast(@alignOf(Context), @intToPtr(*Context, stack_end));
3112 context_ptr.* = context;3142 context_ptr.* = context;
3113 arg = stack_end;
3114 }3143 }
31153144
3116 stack_end -= @sizeOf(Thread);3145 if (Thread.use_pthreads) {
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) {
3128 // use pthreads3146 // use pthreads
3129 var attr: c.pthread_attr_t = undefined;3147 var attr: c.pthread_attr_t = undefined;
3130 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;3148 if (c.pthread_attr_init(&attr) != 0) return SpawnThreadError.SystemResources;
3131 defer assert(c.pthread_attr_destroy(&attr) == 0);3149 defer assert(c.pthread_attr_destroy(&attr) == 0);
31323150
3133 // align to page3151 assert(c.pthread_attr_setstack(&attr, @intToPtr(*c_void, mmap_addr), stack_end_offset) == 0);
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);
31363152
3137 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));3153 const err = c.pthread_create(&thread_ptr.data.handle, &attr, MainFuncs.posixThreadMain, @intToPtr(*c_void, arg));
3138 switch (err) {3154 switch (err) {
...@@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread...@@ -3143,10 +3159,17 @@ pub fn spawnThread(context: var, comptime startFn: var) SpawnThreadError!*Thread
3143 else => return unexpectedErrorPosix(@intCast(usize, err)),3159 else => return unexpectedErrorPosix(@intCast(usize, err)),
3144 }3160 }
3145 } else if (builtin.os == builtin.Os.linux) {3161 } else if (builtin.os == builtin.Os.linux) {
3146 // use linux API directly. TODO use posix.CLONE_SETTLS and initialize thread local storage correctly3162 var flags: u32 = posix.CLONE_VM | posix.CLONE_FS | posix.CLONE_FILES | posix.CLONE_SIGHAND |
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;3163 posix.CLONE_THREAD | posix.CLONE_SYSVSEM | posix.CLONE_PARENT_SETTID | posix.CLONE_CHILD_CLEARTID |
3148 const newtls: usize = 0;3164 posix.CLONE_DETACHED;
3149 const rc = posix.clone(MainFuncs.linuxThreadMain, stack_end, flags, arg, &thread_ptr.data.handle, newtls, &thread_ptr.data.handle);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);
3150 const err = posix.getErrno(rc);3173 const err = posix.getErrno(rc);
3151 switch (err) {3174 switch (err) {
3152 0 => return thread_ptr,3175 0 => return thread_ptr,
std/os/path.zig+92-33
...@@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool {...@@ -33,40 +33,103 @@ pub fn isSep(byte: u8) bool {
33 }33 }
34}34}
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
36/// Naively combines a series of paths with the native path seperator.81/// Naively combines a series of paths with the native path seperator.
37/// Allocates memory for the result, which must be freed by the caller.82/// Allocates memory for the result, which must be freed by the caller.
38pub fn join(allocator: *Allocator, paths: ...) ![]u8 {83pub fn joinWindows(allocator: *Allocator, paths: []const []const u8) ![]u8 {
39 if (is_windows) {84 return joinSep(allocator, sep_windows, paths);
40 return joinWindows(allocator, paths);85}
41 } else {86
42 return joinPosix(allocator, paths);87/// Naively combines a series of paths with the native path seperator.
43 }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);
44}91}
4592
46pub fn joinWindows(allocator: *Allocator, paths: ...) ![]u8 {93fn testJoinWindows(paths: []const []const u8, expected: []const u8) void {
47 return mem.join(allocator, sep_windows, paths);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));
48}98}
4999
50pub fn joinPosix(allocator: *Allocator, paths: ...) ![]u8 {100fn testJoinPosix(paths: []const []const u8, expected: []const u8) void {
51 return mem.join(allocator, sep_posix, paths);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));
52}105}
53106
54test "os.path.join" {107test "os.path.join" {
55 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");
56 assert(mem.eql(u8, try joinWindows(debug.global_allocator, "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"));112 testJoinWindows([][]const u8{ "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"));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"));120 testJoinPosix([][]const u8{ "/a/b", "c" }, "/a/b/c");
64 assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/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"));123 testJoinPosix([][]const u8{ "/", "a", "b/", "c" }, "/a/b/c");
67 assert(mem.eql(u8, try joinPosix(debug.global_allocator, "/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");
70}133}
71134
72pub fn isAbsolute(path: []const u8) bool {135pub fn isAbsolute(path: []const u8) bool {
...@@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {...@@ -312,18 +375,8 @@ fn asciiEqlIgnoreCase(s1: []const u8, s2: []const u8) bool {
312 return true;375 return true;
313}376}
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
325/// On Windows, this calls `resolveWindows` and on POSIX it calls `resolvePosix`.378/// 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 {
327 if (is_windows) {380 if (is_windows) {
328 return resolveWindows(allocator, paths);381 return resolveWindows(allocator, paths);
329 } else {382 } else {
...@@ -602,7 +655,10 @@ test "os.path.resolveWindows" {...@@ -602,7 +655,10 @@ test "os.path.resolveWindows" {
602 const parsed_cwd = windowsParsePath(cwd);655 const parsed_cwd = windowsParsePath(cwd);
603 {656 {
604 const result = testResolveWindows([][]const u8{ "/usr/local", "lib\\zig\\std\\array_list.zig" });657 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 });
606 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {662 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
607 expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);663 expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
608 }664 }
...@@ -610,7 +666,10 @@ test "os.path.resolveWindows" {...@@ -610,7 +666,10 @@ test "os.path.resolveWindows" {
610 }666 }
611 {667 {
612 const result = testResolveWindows([][]const u8{ "usr/local", "lib\\zig" });668 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 });
614 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {673 if (parsed_cwd.kind == WindowsPath.Kind.Drive) {
615 expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);674 expected[0] = asciiUpper(parsed_cwd.disk_designator[0]);
616 }675 }
std/os/test.zig+16
...@@ -105,3 +105,19 @@ test "AtomicFile" {...@@ -105,3 +105,19 @@ test "AtomicFile" {
105105
106 try os.deleteFile(test_out_file);106 try os.deleteFile(test_out_file);
107}107}
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;...@@ -49,7 +49,10 @@ pub const UNICODE = false;
49pub const WCHAR = u16;49pub const WCHAR = u16;
50pub const WORD = u16;50pub const WORD = u16;
51pub const LARGE_INTEGER = i64;51pub 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
54pub const TRUE = 1;57pub const TRUE = 1;
55pub const FALSE = 0;58pub const FALSE = 0;
...@@ -380,3 +383,17 @@ pub const COORD = extern struct {...@@ -380,3 +383,17 @@ pub const COORD = extern struct {
380};383};
381384
382pub const CREATE_UNICODE_ENVIRONMENT = 1024;385pub 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;...@@ -164,6 +164,10 @@ pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
164164
165pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;165pub 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
167pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;171pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
168172
169pub extern "kernel32" stdcallcc fn WriteFile(173pub 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 @@...@@ -4,6 +4,7 @@
4const root = @import("@root");4const root = @import("@root");
5const std = @import("std");5const std = @import("std");
6const builtin = @import("builtin");6const builtin = @import("builtin");
7const assert = std.debug.assert;
78
8var argc_ptr: [*]usize = undefined;9var argc_ptr: [*]usize = undefined;
910
...@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {...@@ -44,7 +45,9 @@ nakedcc fn _start() noreturn {
4445
45extern fn WinMainCRTStartup() noreturn {46extern fn WinMainCRTStartup() noreturn {
46 @setAlignStack(16);47 @setAlignStack(16);
4748 if (!builtin.single_threaded) {
49 _ = @import("../os/windows/tls.zig");
50 }
48 std.os.windows.ExitProcess(callMain());51 std.os.windows.ExitProcess(callMain());
49}52}
5053
...@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {...@@ -61,9 +64,23 @@ fn posixCallMainAndExit() noreturn {
61 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}64 while (envp_optional[envp_count]) |_| : (envp_count += 1) {}
62 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];65 const envp = @ptrCast([*][*]u8, envp_optional)[0..envp_count];
63 if (builtin.os == builtin.Os.linux) {66 if (builtin.os == builtin.Os.linux) {
64 const auxv = @ptrCast([*]usize, envp.ptr + envp_count + 1);67 // Scan auxiliary vector.
65 std.os.linux_elf_aux_maybe = @ptrCast([*]std.elf.Auxv, auxv);68 const auxv = @ptrCast([*]std.elf.Auxv, envp.ptr + envp_count + 1);
66 std.debug.assert(std.os.linuxGetAuxVal(std.elf.AT_PAGESZ) == std.os.page_size);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);
67 }84 }
6885
69 std.os.posix.exit(callMainWithArgs(argc, argv, envp));86 std.os.posix.exit(callMainWithArgs(argc, argv, envp));
...@@ -116,3 +133,41 @@ inline fn callMain() u8 {...@@ -116,3 +133,41 @@ inline fn callMain() u8 {
116 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),133 else => @compileError("expected return type of main to be 'u8', 'noreturn', 'void', or '!void'"),
117 }134 }
118}135}
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 {...@@ -27,9 +27,9 @@ pub fn main() !void {
27 std.debug.warn("Expected second argument to be cache root directory path\n");27 std.debug.warn("Expected second argument to be cache root directory path\n");
28 return error.InvalidArgs;28 return error.InvalidArgs;
29 });29 });
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" });
33 const TestFn = fn ([]const u8, []const u8) anyerror!void;33 const TestFn = fn ([]const u8, []const u8) anyerror!void;
34 const test_fns = []TestFn{34 const test_fns = []TestFn{
35 testZigInitLib,35 testZigInitLib,
...@@ -99,8 +99,8 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {...@@ -99,8 +99,8 @@ fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void {
99fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {99fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void {
100 if (builtin.os != builtin.Os.linux or builtin.arch != builtin.Arch.x86_64) return;100 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");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, dir_path, "example.s");103 const example_s_path = try os.path.join(a, [][]const u8{ dir_path, "example.s" });
104104
105 try std.io.writeFile(example_zig_path,105 try std.io.writeFile(example_zig_path,
106 \\// Type your code here, or load an example.106 \\// Type your code here, or load an example.
test/compile_errors.zig+19
...@@ -1,6 +1,25 @@...@@ -1,6 +1,25 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompileErrorContext) void {3pub 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
4 cases.add(23 cases.add(
5 "@bitCast same size but bit count mismatch",24 "@bitCast same size but bit count mismatch",
6 \\export fn entry(byte: u8) void {25 \\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" {...@@ -685,3 +685,11 @@ test "fn call returning scalar optional in equality expression" {
685fn getNull() ?*i32 {685fn getNull() ?*i32 {
686 return null;686 return null;
687}687}
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 @@...@@ -1,20 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const mem = std.mem;
2const assertOrPanic = std.debug.assertOrPanic;3const assertOrPanic = std.debug.assertOrPanic;
34
4test "implicit array to vector and vector to array" {5test "vector wrap operators" {
5 const S = struct {6 const S = struct {
6 fn doTheTest() void {7 fn doTheTest() void {
7 var v: @Vector(4, i32) = [4]i32{10, 20, 30, 40};8 const v: @Vector(4, i32) = [4]i32{ 10, 20, 30, 40 };
8 const x: @Vector(4, i32) = [4]i32{1, 2, 3, 4};9 const x: @Vector(4, i32) = [4]i32{ 1, 2, 3, 4 };
9 v +%= x;10 assertOrPanic(mem.eql(i32, ([4]i32)(v +% x), [4]i32{ 11, 22, 33, 44 }));
10 const result: [4]i32 = v;11 assertOrPanic(mem.eql(i32, ([4]i32)(v -% x), [4]i32{ 9, 18, 27, 36 }));
11 assertOrPanic(result[0] == 11);12 assertOrPanic(mem.eql(i32, ([4]i32)(v *% x), [4]i32{ 10, 40, 90, 160 }));
12 assertOrPanic(result[1] == 22);
13 assertOrPanic(result[2] == 33);
14 assertOrPanic(result[3] == 44);
15 }13 }
16 };14 };
17 S.doTheTest();15 S.doTheTest();
18 comptime S.doTheTest();16 comptime S.doTheTest();
19}17}
20
test/tests.zig+47-11
...@@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons...@@ -194,6 +194,9 @@ pub fn addPkgTests(b: *build.Builder, test_filter: ?[]const u8, root_src: []cons
194 if (link_libc) {194 if (link_libc) {
195 these_tests.linkSystemLibrary("c");195 these_tests.linkSystemLibrary("c");
196 }196 }
197 if (mem.eql(u8, name, "std")) {
198 these_tests.overrideStdDir("std");
199 }
197 step.dependOn(&these_tests.step);200 step.dependOn(&these_tests.step);
198 }201 }
199 }202 }
...@@ -436,7 +439,10 @@ pub const CompareOutputContext = struct {...@@ -436,7 +439,10 @@ pub const CompareOutputContext = struct {
436 pub fn addCase(self: *CompareOutputContext, case: TestCase) void {439 pub fn addCase(self: *CompareOutputContext, case: TestCase) void {
437 const b = self.b;440 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
441 switch (case.special) {447 switch (case.special) {
442 Special.Asm => {448 Special.Asm => {
...@@ -449,7 +455,10 @@ pub const CompareOutputContext = struct {...@@ -449,7 +455,10 @@ pub const CompareOutputContext = struct {
449 exe.addAssemblyFile(root_src);455 exe.addAssemblyFile(root_src);
450456
451 for (case.sources.toSliceConst()) |src_file| {457 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;
453 const write_src = b.addWriteFile(expanded_src_path, src_file.source);462 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
454 exe.step.dependOn(&write_src.step);463 exe.step.dependOn(&write_src.step);
455 }464 }
...@@ -473,7 +482,10 @@ pub const CompareOutputContext = struct {...@@ -473,7 +482,10 @@ pub const CompareOutputContext = struct {
473 }482 }
474483
475 for (case.sources.toSliceConst()) |src_file| {484 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;
477 const write_src = b.addWriteFile(expanded_src_path, src_file.source);489 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
478 exe.step.dependOn(&write_src.step);490 exe.step.dependOn(&write_src.step);
479 }491 }
...@@ -496,7 +508,10 @@ pub const CompareOutputContext = struct {...@@ -496,7 +508,10 @@ pub const CompareOutputContext = struct {
496 }508 }
497509
498 for (case.sources.toSliceConst()) |src_file| {510 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;
500 const write_src = b.addWriteFile(expanded_src_path, src_file.source);515 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
501 exe.step.dependOn(&write_src.step);516 exe.step.dependOn(&write_src.step);
502 }517 }
...@@ -569,8 +584,14 @@ pub const CompileErrorContext = struct {...@@ -569,8 +584,14 @@ pub const CompileErrorContext = struct {
569 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);584 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
570 const b = self.context.b;585 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;587 const root_src = os.path.join(
573 const obj_path = os.path.join(b.allocator, b.cache_root, "test.o") catch unreachable;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
575 var zig_args = ArrayList([]const u8).init(b.allocator);596 var zig_args = ArrayList([]const u8).init(b.allocator);
576 zig_args.append(b.zig_exe) catch unreachable;597 zig_args.append(b.zig_exe) catch unreachable;
...@@ -718,7 +739,10 @@ pub const CompileErrorContext = struct {...@@ -718,7 +739,10 @@ pub const CompileErrorContext = struct {
718 self.step.dependOn(&compile_and_cmp_errors.step);739 self.step.dependOn(&compile_and_cmp_errors.step);
719740
720 for (case.sources.toSliceConst()) |src_file| {741 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;
722 const write_src = b.addWriteFile(expanded_src_path, src_file.source);746 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
723 compile_and_cmp_errors.step.dependOn(&write_src.step);747 compile_and_cmp_errors.step.dependOn(&write_src.step);
724 }748 }
...@@ -849,7 +873,10 @@ pub const TranslateCContext = struct {...@@ -849,7 +873,10 @@ pub const TranslateCContext = struct {
849 const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);873 const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step);
850 const b = self.context.b;874 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
854 var zig_args = ArrayList([]const u8).init(b.allocator);881 var zig_args = ArrayList([]const u8).init(b.allocator);
855 zig_args.append(b.zig_exe) catch unreachable;882 zig_args.append(b.zig_exe) catch unreachable;
...@@ -983,7 +1010,10 @@ pub const TranslateCContext = struct {...@@ -983,7 +1010,10 @@ pub const TranslateCContext = struct {
983 self.step.dependOn(&translate_c_and_cmp.step);1010 self.step.dependOn(&translate_c_and_cmp.step);
9841011
985 for (case.sources.toSliceConst()) |src_file| {1012 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;
987 const write_src = b.addWriteFile(expanded_src_path, src_file.source);1017 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
988 translate_c_and_cmp.step.dependOn(&write_src.step);1018 translate_c_and_cmp.step.dependOn(&write_src.step);
989 }1019 }
...@@ -1098,7 +1128,10 @@ pub const GenHContext = struct {...@@ -1098,7 +1128,10 @@ pub const GenHContext = struct {
10981128
1099 pub fn addCase(self: *GenHContext, case: *const TestCase) void {1129 pub fn addCase(self: *GenHContext, case: *const TestCase) void {
1100 const b = self.b;1130 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
1103 const mode = builtin.Mode.Debug;1136 const mode = builtin.Mode.Debug;
1104 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", case.name, @tagName(mode)) catch unreachable;1137 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 {...@@ -1110,7 +1143,10 @@ pub const GenHContext = struct {
1110 obj.setBuildMode(mode);1143 obj.setBuildMode(mode);
11111144
1112 for (case.sources.toSliceConst()) |src_file| {1145 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;
1114 const write_src = b.addWriteFile(expanded_src_path, src_file.source);1150 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
1115 obj.step.dependOn(&write_src.step);1151 obj.step.dependOn(&write_src.step);
1116 }1152 }