diff --git a/README.md b/README.md index 2a1ab6bcbf..7598c623c3 100644 --- a/README.md +++ b/README.md @@ -119,31 +119,22 @@ libc. Create demo games using Zig. [](https://travis-ci.org/zig-lang/zig) [](https://ci.appveyor.com/project/andrewrk/zig-d3l86/branch/master) -### Dependencies +### Stage 1: Build Zig from C++ Source Code -#### Build Dependencies - -These compile tools must be available on your system and are used to build -the Zig compiler itself: +#### Dependencies ##### POSIX * gcc >= 5.0.0 or clang >= 3.6.0 * cmake >= 2.8.5 + * LLVM, Clang, LLD libraries == 6.x, compiled with the same gcc or clang version above ##### Windows * Microsoft Visual Studio 2015 + * LLVM, Clang, LLD libraries == 6.x, compiled with the same MSVC version above -#### Library Dependencies - -These libraries must be installed on your system, with the development files -available. The Zig compiler links against them. You have to use the same -compiler for these libraries as you do to compile Zig. - - * LLVM, Clang, and LLD libraries == 6.x - -### Debug / Development Build +#### Instructions If you have gcc or clang installed, you can find out what `ZIG_LIBC_LIB_DIR`, `ZIG_LIBC_STATIC_LIB_DIR`, and `ZIG_LIBC_INCLUDE_DIR` should be set to @@ -158,7 +149,7 @@ make install ./zig build --build-file ../build.zig test ``` -#### MacOS +##### MacOS `ZIG_LIBC_LIB_DIR` and `ZIG_LIBC_STATIC_LIB_DIR` are unused. @@ -172,21 +163,35 @@ make install ./zig build --build-file ../build.zig test ``` -#### Windows +##### Windows See https://github.com/zig-lang/zig/wiki/Building-Zig-on-Windows -### Release / Install Build +### Stage 2: Build Self-Hosted Zig from Zig Source Code -Once installed, `ZIG_LIBC_LIB_DIR` and `ZIG_LIBC_INCLUDE_DIR` can be overridden -by the `--libc-lib-dir` and `--libc-include-dir` parameters to the zig binary. +*Note: Stage 2 compiler is not complete. Beta users of Zig should use the +Stage 1 compiler for now.* + +Dependencies are the same as Stage 1, except now you have a working zig compiler. ``` -mkdir build -cd build -cmake .. -DCMAKE_BUILD_TYPE=Release -DZIG_LIBC_LIB_DIR=/some/path -DZIG_LIBC_INCLUDE_DIR=/some/path -DZIG_LIBC_STATIC_INCLUDE_DIR=/some/path -make -sudo make install +bin/zig build --build-file ../build.zig --prefix $(pwd)/stage2 install +``` + +### Stage 3: Rebuild Self-Hosted Zig Using the Self-Hosted Compiler + +This is the actual compiler binary that we will install to the system. + +#### Debug / Development Build + +``` +./stage2/bin/zig build --build-file ../build.zig --prefix $(pwd)/stage3 install +``` + +#### Release / Install Build + +``` +./stage2/bin/zig build --build-file ../build.zig install -Drelease-fast ``` ### Test Coverage diff --git a/build.zig b/build.zig index 1c1d4f832e..96638659be 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,13 @@ -const Builder = @import("std").build.Builder; +const std = @import("std"); +const Builder = std.build.Builder; const tests = @import("test/tests.zig"); -const os = @import("std").os; +const os = std.os; +const BufMap = std.BufMap; +const warn = std.debug.warn; +const mem = std.mem; +const ArrayList = std.ArrayList; +const Buffer = std.Buffer; +const io = std.io; pub fn build(b: &Builder) { const mode = b.standardReleaseOptions(); @@ -25,14 +32,18 @@ pub fn build(b: &Builder) { docs_step.dependOn(&docgen_cmd.step); docs_step.dependOn(&docgen_home_cmd.step); - var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); - exe.setBuildMode(mode); - exe.linkSystemLibrary("c"); + if (findLLVM(b)) |llvm| { + var exe = b.addExecutable("zig", "src-self-hosted/main.zig"); + exe.setBuildMode(mode); + exe.linkSystemLibrary("c"); + dependOnLib(exe, llvm); - b.default_step.dependOn(&exe.step); - b.default_step.dependOn(docs_step); + b.default_step.dependOn(&exe.step); + b.default_step.dependOn(docs_step); - b.installArtifact(exe); + b.installArtifact(exe); + installStdLib(b); + } const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); @@ -53,6 +64,10 @@ pub fn build(b: &Builder) { "std/special/compiler_rt/index.zig", "compiler-rt", "Run the compiler_rt tests", with_lldb)); + test_step.dependOn(tests.addPkgTests(b, test_filter, + "src-self-hosted/main.zig", "fmt", "Run the fmt tests", + with_lldb)); + test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); @@ -60,3 +75,198 @@ pub fn build(b: &Builder) { test_step.dependOn(tests.addDebugSafetyTests(b, test_filter)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); } + +fn dependOnLib(lib_exe_obj: &std.build.LibExeObjStep, dep: &const LibraryDep) { + for (dep.libdirs.toSliceConst()) |lib_dir| { + lib_exe_obj.addLibPath(lib_dir); + } + for (dep.libs.toSliceConst()) |lib| { + lib_exe_obj.linkSystemLibrary(lib); + } + for (dep.includes.toSliceConst()) |include_path| { + lib_exe_obj.addIncludeDir(include_path); + } +} + +const LibraryDep = struct { + libdirs: ArrayList([]const u8), + libs: ArrayList([]const u8), + includes: ArrayList([]const u8), +}; + +fn findLLVM(b: &Builder) -> ?LibraryDep { + const llvm_config_exe = b.findProgram( + [][]const u8{"llvm-config-5.0", "llvm-config"}, + [][]const u8{ + "/usr/local/opt/llvm@5/bin", + "/mingw64/bin", + "/c/msys64/mingw64/bin", + "c:/msys64/mingw64/bin", + "C:/Libraries/llvm-5.0.0/bin", + }) %% |err| + { + warn("unable to find llvm-config: {}\n", err); + return null; + }; + const libs_output = b.exec([][]const u8{llvm_config_exe, "--libs", "--system-libs"}); + const includes_output = b.exec([][]const u8{llvm_config_exe, "--includedir"}); + const libdir_output = b.exec([][]const u8{llvm_config_exe, "--libdir"}); + + var result = LibraryDep { + .libs = ArrayList([]const u8).init(b.allocator), + .includes = ArrayList([]const u8).init(b.allocator), + .libdirs = ArrayList([]const u8).init(b.allocator), + }; + { + var it = mem.split(libs_output, " \n"); + while (it.next()) |lib_arg| { + if (mem.startsWith(u8, lib_arg, "-l")) { + %%result.libs.append(lib_arg[2..]); + } + } + } + { + var it = mem.split(includes_output, " \n"); + while (it.next()) |include_arg| { + if (mem.startsWith(u8, include_arg, "-I")) { + %%result.includes.append(include_arg[2..]); + } else { + %%result.includes.append(include_arg); + } + } + } + { + var it = mem.split(libdir_output, " \n"); + while (it.next()) |libdir| { + if (mem.startsWith(u8, libdir, "-L")) { + %%result.libdirs.append(libdir[2..]); + } else { + %%result.libdirs.append(libdir); + } + } + } + return result; +} + +pub fn installStdLib(b: &Builder) { + const stdlib_files = []const []const u8 { + "array_list.zig", + "base64.zig", + "buf_map.zig", + "buf_set.zig", + "buffer.zig", + "build.zig", + "c/darwin.zig", + "c/index.zig", + "c/linux.zig", + "c/windows.zig", + "cstr.zig", + "debug.zig", + "dwarf.zig", + "elf.zig", + "empty.zig", + "endian.zig", + "fmt/errol/enum3.zig", + "fmt/errol/index.zig", + "fmt/errol/lookup.zig", + "fmt/index.zig", + "hash_map.zig", + "heap.zig", + "index.zig", + "io.zig", + "linked_list.zig", + "math/acos.zig", + "math/acosh.zig", + "math/asin.zig", + "math/asinh.zig", + "math/atan.zig", + "math/atan2.zig", + "math/atanh.zig", + "math/cbrt.zig", + "math/ceil.zig", + "math/copysign.zig", + "math/cos.zig", + "math/cosh.zig", + "math/exp.zig", + "math/exp2.zig", + "math/expm1.zig", + "math/expo2.zig", + "math/fabs.zig", + "math/floor.zig", + "math/fma.zig", + "math/frexp.zig", + "math/hypot.zig", + "math/ilogb.zig", + "math/index.zig", + "math/inf.zig", + "math/isfinite.zig", + "math/isinf.zig", + "math/isnan.zig", + "math/isnormal.zig", + "math/ln.zig", + "math/log.zig", + "math/log10.zig", + "math/log1p.zig", + "math/log2.zig", + "math/modf.zig", + "math/nan.zig", + "math/pow.zig", + "math/round.zig", + "math/scalbn.zig", + "math/signbit.zig", + "math/sin.zig", + "math/sinh.zig", + "math/sqrt.zig", + "math/tan.zig", + "math/tanh.zig", + "math/trunc.zig", + "mem.zig", + "net.zig", + "os/child_process.zig", + "os/darwin.zig", + "os/darwin_errno.zig", + "os/get_user_id.zig", + "os/index.zig", + "os/linux.zig", + "os/linux_errno.zig", + "os/linux_i386.zig", + "os/linux_x86_64.zig", + "os/path.zig", + "os/windows/error.zig", + "os/windows/index.zig", + "os/windows/util.zig", + "rand.zig", + "sort.zig", + "special/bootstrap.zig", + "special/bootstrap_lib.zig", + "special/build_file_template.zig", + "special/build_runner.zig", + "special/builtin.zig", + "special/compiler_rt/aulldiv.zig", + "special/compiler_rt/aullrem.zig", + "special/compiler_rt/comparetf2.zig", + "special/compiler_rt/fixuint.zig", + "special/compiler_rt/fixunsdfdi.zig", + "special/compiler_rt/fixunsdfsi.zig", + "special/compiler_rt/fixunsdfti.zig", + "special/compiler_rt/fixunssfdi.zig", + "special/compiler_rt/fixunssfsi.zig", + "special/compiler_rt/fixunssfti.zig", + "special/compiler_rt/fixunstfdi.zig", + "special/compiler_rt/fixunstfsi.zig", + "special/compiler_rt/fixunstfti.zig", + "special/compiler_rt/index.zig", + "special/compiler_rt/udivmod.zig", + "special/compiler_rt/udivmoddi4.zig", + "special/compiler_rt/udivmodti4.zig", + "special/compiler_rt/udivti3.zig", + "special/compiler_rt/umodti3.zig", + "special/panic.zig", + "special/test_runner.zig", + }; + for (stdlib_files) |stdlib_file| { + const src_path = %%os.path.join(b.allocator, "std", stdlib_file); + const dest_path = %%os.path.join(b.allocator, "lib", "zig", "std", stdlib_file); + b.installFile(src_path, dest_path); + } +} diff --git a/doc/docgen.zig b/doc/docgen.zig index 81febb074e..d481baf4b3 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -42,14 +42,14 @@ const State = enum { // TODO look for code segments -fn gen(in: &io.InStream, out: &const io.OutStream) { +fn gen(in: &io.InStream, out: &io.OutStream) { var state = State.Start; while (true) { const byte = in.readByte() %% |err| { if (err == error.EndOfStream) { return; } - std.debug.panic("{}", err) + std.debug.panic("{}", err); }; switch (state) { State.Start => switch (byte) { diff --git a/doc/langref.html.in b/doc/langref.html.in index e5b896410d..162c8b3e03 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -136,6 +136,7 @@
const assert = @import("std").debug.assert;
// Functions are declared like this
-// The last expression in the function can be used as the return value.
fn add(a: i8, b: i8) -> i8 {
if (a == 0) {
// You can still return manually if needed.
return b;
}
- a + b
+ return a + b;
}
// The export specifier makes a function externally visible in the generated
@@ -4368,6 +4368,11 @@ test.zig:6:2: error: found compile log statement
+ @export
+ @export(comptime name: []const u8, target: var, linkage: builtin.GlobalLinkage) -> []const u8
+
+ Creates a symbol in the output object file.
+
@tagName
@tagName(value: var) -> []const u8
@@ -5815,13 +5820,15 @@ TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestD
TestDecl = "test" String Block
-TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
+TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
ErrorValueDecl = "error" Symbol ";"
-GlobalVarDecl = VariableDeclaration ";"
+GlobalVarDecl = option("export") VariableDeclaration ";"
-VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
+LocalVarDecl = option("comptime") VariableDeclaration
+
+VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") option("section" "(" Expression ")") "=" Expression
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
@@ -5831,21 +5838,17 @@ UseDecl = "use" Expression ";"
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
-FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("->" TypeExpr)
+FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
-VisibleMod = "pub" | "export"
-
-FnDef = option("inline" | "extern") FnProto Block
+FnDef = option("inline" | "export") FnProto Block
ParamDeclList = "(" list(ParamDecl, ",") ")"
ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...")
-Block = "{" many(Statement) option(Expression) "}"
+Block = option(Symbol ":") "{" many(Statement) "}"
-Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
-
-Label = Symbol ":"
+Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
TypeExpr = PrefixOpExpression | "var"
@@ -5885,13 +5888,13 @@ SwitchProng = (list(SwitchItem, ",") | "else") "=>" option("|" option("*") Sy
SwitchItem = Expression | (Expression "..." Expression)
-ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
+ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
ReturnExpression = option("%") "return" option(Expression)
-BreakExpression = "break" option(Expression)
+BreakExpression = "break" option(":" Symbol) option(Expression)
Defer(body) = option("%") "defer" body
@@ -5901,7 +5904,7 @@ TryExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|")
TestExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body option("else" BlockExpression(body))
-WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
+WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
BoolAndExpression = ComparisonExpression "and" BoolAndExpression | ComparisonExpression
@@ -5949,15 +5952,13 @@ StructLiteralField = "." Symbol "=" Expression
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "%" | "%%" | "??" | "-%"
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
-GotoExpression = "goto" Symbol
-
GroupedExpression = "(" Expression ")"
-KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
+KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
ContainerDecl = option("extern" | "packed")
("struct" option(GroupedExpression) | "union" option("enum" option(GroupedExpression) | GroupedExpression) | ("enum" option(GroupedExpression)))
diff --git a/example/shared_library/mathtest.zig b/example/shared_library/mathtest.zig
index a11642554f..bb0175bff2 100644
--- a/example/shared_library/mathtest.zig
+++ b/example/shared_library/mathtest.zig
@@ -1,3 +1,3 @@
export fn add(a: i32, b: i32) -> i32 {
- a + b
+ return a + b;
}
diff --git a/src-self-hosted/ast.zig b/src-self-hosted/ast.zig
new file mode 100644
index 0000000000..5fd836b950
--- /dev/null
+++ b/src-self-hosted/ast.zig
@@ -0,0 +1,273 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const ArrayList = std.ArrayList;
+const Token = @import("tokenizer.zig").Token;
+const mem = std.mem;
+
+pub const Node = struct {
+ id: Id,
+
+ pub const Id = enum {
+ Root,
+ VarDecl,
+ Identifier,
+ FnProto,
+ ParamDecl,
+ Block,
+ InfixOp,
+ PrefixOp,
+ IntegerLiteral,
+ FloatLiteral,
+ };
+
+ pub fn iterate(base: &Node, index: usize) -> ?&Node {
+ return switch (base.id) {
+ Id.Root => @fieldParentPtr(NodeRoot, "base", base).iterate(index),
+ Id.VarDecl => @fieldParentPtr(NodeVarDecl, "base", base).iterate(index),
+ Id.Identifier => @fieldParentPtr(NodeIdentifier, "base", base).iterate(index),
+ Id.FnProto => @fieldParentPtr(NodeFnProto, "base", base).iterate(index),
+ Id.ParamDecl => @fieldParentPtr(NodeParamDecl, "base", base).iterate(index),
+ Id.Block => @fieldParentPtr(NodeBlock, "base", base).iterate(index),
+ Id.InfixOp => @fieldParentPtr(NodeInfixOp, "base", base).iterate(index),
+ Id.PrefixOp => @fieldParentPtr(NodePrefixOp, "base", base).iterate(index),
+ Id.IntegerLiteral => @fieldParentPtr(NodeIntegerLiteral, "base", base).iterate(index),
+ Id.FloatLiteral => @fieldParentPtr(NodeFloatLiteral, "base", base).iterate(index),
+ };
+ }
+
+ pub fn destroy(base: &Node, allocator: &mem.Allocator) {
+ return switch (base.id) {
+ Id.Root => allocator.destroy(@fieldParentPtr(NodeRoot, "base", base)),
+ Id.VarDecl => allocator.destroy(@fieldParentPtr(NodeVarDecl, "base", base)),
+ Id.Identifier => allocator.destroy(@fieldParentPtr(NodeIdentifier, "base", base)),
+ Id.FnProto => allocator.destroy(@fieldParentPtr(NodeFnProto, "base", base)),
+ Id.ParamDecl => allocator.destroy(@fieldParentPtr(NodeParamDecl, "base", base)),
+ Id.Block => allocator.destroy(@fieldParentPtr(NodeBlock, "base", base)),
+ Id.InfixOp => allocator.destroy(@fieldParentPtr(NodeInfixOp, "base", base)),
+ Id.PrefixOp => allocator.destroy(@fieldParentPtr(NodePrefixOp, "base", base)),
+ Id.IntegerLiteral => allocator.destroy(@fieldParentPtr(NodeIntegerLiteral, "base", base)),
+ Id.FloatLiteral => allocator.destroy(@fieldParentPtr(NodeFloatLiteral, "base", base)),
+ };
+ }
+};
+
+pub const NodeRoot = struct {
+ base: Node,
+ decls: ArrayList(&Node),
+
+ pub fn iterate(self: &NodeRoot, index: usize) -> ?&Node {
+ if (index < self.decls.len) {
+ return self.decls.items[self.decls.len - index - 1];
+ }
+ return null;
+ }
+};
+
+pub const NodeVarDecl = struct {
+ base: Node,
+ visib_token: ?Token,
+ name_token: Token,
+ eq_token: Token,
+ mut_token: Token,
+ comptime_token: ?Token,
+ extern_token: ?Token,
+ lib_name: ?&Node,
+ type_node: ?&Node,
+ align_node: ?&Node,
+ init_node: ?&Node,
+
+ pub fn iterate(self: &NodeVarDecl, index: usize) -> ?&Node {
+ var i = index;
+
+ if (self.type_node) |type_node| {
+ if (i < 1) return type_node;
+ i -= 1;
+ }
+
+ if (self.align_node) |align_node| {
+ if (i < 1) return align_node;
+ i -= 1;
+ }
+
+ if (self.init_node) |init_node| {
+ if (i < 1) return init_node;
+ i -= 1;
+ }
+
+ return null;
+ }
+};
+
+pub const NodeIdentifier = struct {
+ base: Node,
+ name_token: Token,
+
+ pub fn iterate(self: &NodeIdentifier, index: usize) -> ?&Node {
+ return null;
+ }
+};
+
+pub const NodeFnProto = struct {
+ base: Node,
+ visib_token: ?Token,
+ fn_token: Token,
+ name_token: ?Token,
+ params: ArrayList(&Node),
+ return_type: ?&Node,
+ var_args_token: ?Token,
+ extern_token: ?Token,
+ inline_token: ?Token,
+ cc_token: ?Token,
+ body_node: ?&Node,
+ lib_name: ?&Node, // populated if this is an extern declaration
+ align_expr: ?&Node, // populated if align(A) is present
+
+ pub fn iterate(self: &NodeFnProto, index: usize) -> ?&Node {
+ var i = index;
+
+ if (self.body_node) |body_node| {
+ if (i < 1) return body_node;
+ i -= 1;
+ }
+
+ if (self.return_type) |return_type| {
+ if (i < 1) return return_type;
+ i -= 1;
+ }
+
+ if (self.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
+
+ if (i < self.params.len) return self.params.items[self.params.len - i - 1];
+ i -= self.params.len;
+
+ if (self.lib_name) |lib_name| {
+ if (i < 1) return lib_name;
+ i -= 1;
+ }
+
+ return null;
+ }
+};
+
+pub const NodeParamDecl = struct {
+ base: Node,
+ comptime_token: ?Token,
+ noalias_token: ?Token,
+ name_token: ?Token,
+ type_node: &Node,
+ var_args_token: ?Token,
+
+ pub fn iterate(self: &NodeParamDecl, index: usize) -> ?&Node {
+ var i = index;
+
+ if (i < 1) return self.type_node;
+ i -= 1;
+
+ return null;
+ }
+};
+
+pub const NodeBlock = struct {
+ base: Node,
+ begin_token: Token,
+ end_token: Token,
+ statements: ArrayList(&Node),
+
+ pub fn iterate(self: &NodeBlock, index: usize) -> ?&Node {
+ var i = index;
+
+ if (i < self.statements.len) return self.statements.items[i];
+ i -= self.statements.len;
+
+ return null;
+ }
+};
+
+pub const NodeInfixOp = struct {
+ base: Node,
+ op_token: Token,
+ lhs: &Node,
+ op: InfixOp,
+ rhs: &Node,
+
+ const InfixOp = enum {
+ EqualEqual,
+ BangEqual,
+ };
+
+ pub fn iterate(self: &NodeInfixOp, index: usize) -> ?&Node {
+ var i = index;
+
+ if (i < 1) return self.lhs;
+ i -= 1;
+
+ switch (self.op) {
+ InfixOp.EqualEqual => {},
+ InfixOp.BangEqual => {},
+ }
+
+ if (i < 1) return self.rhs;
+ i -= 1;
+
+ return null;
+ }
+};
+
+pub const NodePrefixOp = struct {
+ base: Node,
+ op_token: Token,
+ op: PrefixOp,
+ rhs: &Node,
+
+ const PrefixOp = union(enum) {
+ Return,
+ AddrOf: AddrOfInfo,
+ };
+ const AddrOfInfo = struct {
+ align_expr: ?&Node,
+ bit_offset_start_token: ?Token,
+ bit_offset_end_token: ?Token,
+ const_token: ?Token,
+ volatile_token: ?Token,
+ };
+
+ pub fn iterate(self: &NodePrefixOp, index: usize) -> ?&Node {
+ var i = index;
+
+ switch (self.op) {
+ PrefixOp.Return => {},
+ PrefixOp.AddrOf => |addr_of_info| {
+ if (addr_of_info.align_expr) |align_expr| {
+ if (i < 1) return align_expr;
+ i -= 1;
+ }
+ },
+ }
+
+ if (i < 1) return self.rhs;
+ i -= 1;
+
+ return null;
+ }
+};
+
+pub const NodeIntegerLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeIntegerLiteral, index: usize) -> ?&Node {
+ return null;
+ }
+};
+
+pub const NodeFloatLiteral = struct {
+ base: Node,
+ token: Token,
+
+ pub fn iterate(self: &NodeFloatLiteral, index: usize) -> ?&Node {
+ return null;
+ }
+};
diff --git a/src-self-hosted/c.zig b/src-self-hosted/c.zig
new file mode 100644
index 0000000000..b7e057b941
--- /dev/null
+++ b/src-self-hosted/c.zig
@@ -0,0 +1,7 @@
+pub use @cImport({
+ @cInclude("llvm-c/Core.h");
+ @cInclude("llvm-c/Analysis.h");
+ @cInclude("llvm-c/Target.h");
+ @cInclude("llvm-c/Initialization.h");
+ @cInclude("llvm-c/TargetMachine.h");
+});
diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig
new file mode 100644
index 0000000000..6a1439291b
--- /dev/null
+++ b/src-self-hosted/llvm.zig
@@ -0,0 +1,13 @@
+const builtin = @import("builtin");
+const c = @import("c.zig");
+const assert = @import("std").debug.assert;
+
+pub const ValueRef = removeNullability(c.LLVMValueRef);
+pub const ModuleRef = removeNullability(c.LLVMModuleRef);
+pub const ContextRef = removeNullability(c.LLVMContextRef);
+pub const BuilderRef = removeNullability(c.LLVMBuilderRef);
+
+fn removeNullability(comptime T: type) -> type {
+ comptime assert(@typeId(T) == builtin.TypeId.Nullable);
+ return T.Child;
+}
diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig
index 71180b2001..6fdacda4b2 100644
--- a/src-self-hosted/main.zig
+++ b/src-self-hosted/main.zig
@@ -1,208 +1,476 @@
+const std = @import("std");
+const mem = std.mem;
+const io = std.io;
+const os = std.os;
+const heap = std.heap;
+const warn = std.debug.warn;
+const assert = std.debug.assert;
+const target = @import("target.zig");
+const Target = target.Target;
+const Module = @import("module.zig").Module;
+const ErrColor = Module.ErrColor;
+const Emit = Module.Emit;
const builtin = @import("builtin");
-const io = @import("std").io;
-const os = @import("std").os;
-const heap = @import("std").heap;
+const ArrayList = std.ArrayList;
-// TODO: sync up CLI with c++ code
-// TODO: concurrency
+error InvalidCommandLineArguments;
+error ZigLibDirNotFound;
+error ZigInstallationNotFound;
-error InvalidArgument;
-error MissingArg0;
-
-var arg0: []u8 = undefined;
-
-var stderr_file: io.File = undefined;
-const stderr = &stderr_file.out_stream;
+const default_zig_cache_name = "zig-cache";
pub fn main() -> %void {
- stderr_file = %return io.getStdErr();
- if (internal_main()) |_| {
- return;
- } else |err| {
- if (err == error.InvalidArgument) {
- stderr.print("\n") %% return err;
- printUsage(stderr) %% return err;
- } else {
- stderr.print("{}\n", err) %% return err;
+ main2() %% |err| {
+ if (err != error.InvalidCommandLineArguments) {
+ warn("{}\n", @errorName(err));
}
return err;
- }
-}
-
-pub fn internal_main() -> %void {
- var args_it = os.args();
-
- var incrementing_allocator = heap.IncrementingAllocator.init(10 * 1024 * 1024) %% |err| {
- io.stderr.printf("Unable to allocate memory") %% {};
- return err;
};
- defer incrementing_allocator.deinit();
+}
- const allocator = &incrementing_allocator.allocator;
-
- arg0 = %return (args_it.next(allocator) ?? error.MissingArg0);
- defer allocator.free(arg0);
+const Cmd = enum {
+ None,
+ Build,
+ Test,
+ Version,
+ Zen,
+ TranslateC,
+ Targets,
+};
+
+fn badArgs(comptime format: []const u8, args: ...) -> error {
+ var stderr = %return io.getStdErr();
+ var stderr_stream_adapter = io.FileOutStream.init(&stderr);
+ const stderr_stream = &stderr_stream_adapter.stream;
+ %return stderr_stream.print(format ++ "\n\n", args);
+ %return printUsage(&stderr_stream_adapter.stream);
+ return error.InvalidCommandLineArguments;
+}
+
+pub fn main2() -> %void {
+ const allocator = std.heap.c_allocator;
+
+ const args = %return os.argsAlloc(allocator);
+ defer os.argsFree(allocator, args);
+
+ var cmd = Cmd.None;
+ var build_kind: Module.Kind = undefined;
+ var build_mode: builtin.Mode = builtin.Mode.Debug;
+ var color = ErrColor.Auto;
+ var emit_file_type = Emit.Binary;
- var build_mode = builtin.Mode.Debug;
var strip = false;
var is_static = false;
- var verbose = false;
+ var verbose_tokenize = false;
+ var verbose_ast_tree = false;
+ var verbose_ast_fmt = false;
var verbose_link = false;
var verbose_ir = false;
+ var verbose_llvm_ir = false;
+ var verbose_cimport = false;
var mwindows = false;
var mconsole = false;
+ var rdynamic = false;
+ var each_lib_rpath = false;
+ var timing_info = false;
- while (args_it.next()) |arg_or_err| {
- const arg = %return arg_or_err;
+ var in_file_arg: ?[]u8 = null;
+ var out_file: ?[]u8 = null;
+ var out_file_h: ?[]u8 = null;
+ var out_name_arg: ?[]u8 = null;
+ var libc_lib_dir_arg: ?[]u8 = null;
+ var libc_static_lib_dir_arg: ?[]u8 = null;
+ var libc_include_dir_arg: ?[]u8 = null;
+ var msvc_lib_dir_arg: ?[]u8 = null;
+ var kernel32_lib_dir_arg: ?[]u8 = null;
+ var zig_install_prefix: ?[]u8 = null;
+ var dynamic_linker_arg: ?[]u8 = null;
+ var cache_dir_arg: ?[]const u8 = null;
+ var target_arch: ?[]u8 = null;
+ var target_os: ?[]u8 = null;
+ var target_environ: ?[]u8 = null;
+ var mmacosx_version_min: ?[]u8 = null;
+ var mios_version_min: ?[]u8 = null;
+ var linker_script_arg: ?[]u8 = null;
+ var test_name_prefix_arg: ?[]u8 = null;
- if (arg[0] == '-') {
- if (strcmp(arg, "--release-fast") == 0) {
+ var test_filters = ArrayList([]const u8).init(allocator);
+ defer test_filters.deinit();
+
+ var lib_dirs = ArrayList([]const u8).init(allocator);
+ defer lib_dirs.deinit();
+
+ var clang_argv = ArrayList([]const u8).init(allocator);
+ defer clang_argv.deinit();
+
+ var llvm_argv = ArrayList([]const u8).init(allocator);
+ defer llvm_argv.deinit();
+
+ var link_libs = ArrayList([]const u8).init(allocator);
+ defer link_libs.deinit();
+
+ var frameworks = ArrayList([]const u8).init(allocator);
+ defer frameworks.deinit();
+
+ var objects = ArrayList([]const u8).init(allocator);
+ defer objects.deinit();
+
+ var asm_files = ArrayList([]const u8).init(allocator);
+ defer asm_files.deinit();
+
+ var rpath_list = ArrayList([]const u8).init(allocator);
+ defer rpath_list.deinit();
+
+ var ver_major: u32 = 0;
+ var ver_minor: u32 = 0;
+ var ver_patch: u32 = 0;
+
+ var arg_i: usize = 1;
+ while (arg_i < args.len) : (arg_i += 1) {
+ const arg = args[arg_i];
+
+ if (arg.len != 0 and arg[0] == '-') {
+ if (mem.eql(u8, arg, "--release-fast")) {
build_mode = builtin.Mode.ReleaseFast;
- } else if (strcmp(arg, "--release-safe") == 0) {
+ } else if (mem.eql(u8, arg, "--release-safe")) {
build_mode = builtin.Mode.ReleaseSafe;
- } else if (strcmp(arg, "--strip") == 0) {
+ } else if (mem.eql(u8, arg, "--strip")) {
strip = true;
- } else if (strcmp(arg, "--static") == 0) {
+ } else if (mem.eql(u8, arg, "--static")) {
is_static = true;
- } else if (strcmp(arg, "--verbose") == 0) {
- verbose = true;
- } else if (strcmp(arg, "--verbose-link") == 0) {
+ } else if (mem.eql(u8, arg, "--verbose-tokenize")) {
+ verbose_tokenize = true;
+ } else if (mem.eql(u8, arg, "--verbose-ast-tree")) {
+ verbose_ast_tree = true;
+ } else if (mem.eql(u8, arg, "--verbose-ast-fmt")) {
+ verbose_ast_fmt = true;
+ } else if (mem.eql(u8, arg, "--verbose-link")) {
verbose_link = true;
- } else if (strcmp(arg, "--verbose-ir") == 0) {
+ } else if (mem.eql(u8, arg, "--verbose-ir")) {
verbose_ir = true;
- } else if (strcmp(arg, "-mwindows") == 0) {
+ } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) {
+ verbose_llvm_ir = true;
+ } else if (mem.eql(u8, arg, "--verbose-cimport")) {
+ verbose_cimport = true;
+ } else if (mem.eql(u8, arg, "-mwindows")) {
mwindows = true;
- } else if (strcmp(arg, "-mconsole") == 0) {
+ } else if (mem.eql(u8, arg, "-mconsole")) {
mconsole = true;
- } else if (strcmp(arg, "-municode") == 0) {
- municode = true;
- } else if (strcmp(arg, "-rdynamic") == 0) {
+ } else if (mem.eql(u8, arg, "-rdynamic")) {
rdynamic = true;
- } else if (strcmp(arg, "--each-lib-rpath") == 0) {
+ } else if (mem.eql(u8, arg, "--each-lib-rpath")) {
each_lib_rpath = true;
- } else if (strcmp(arg, "--enable-timing-info") == 0) {
+ } else if (mem.eql(u8, arg, "--enable-timing-info")) {
timing_info = true;
- } else if (strcmp(arg, "--test-cmd-bin") == 0) {
- test_exec_args.append(nullptr);
- } else if (arg[1] == 'L' && arg[2] != 0) {
+ } else if (mem.eql(u8, arg, "--test-cmd-bin")) {
+ @panic("TODO --test-cmd-bin");
+ } else if (arg[1] == 'L' and arg.len > 2) {
// alias for --library-path
- lib_dirs.append(&arg[2]);
- } else if (strcmp(arg, "--pkg-begin") == 0) {
- if (i + 2 >= argc) {
- fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
- return usage(arg0);
- }
- CliPkg *new_cur_pkg = allocate(1);
- i += 1;
- new_cur_pkg->name = argv[i];
- i += 1;
- new_cur_pkg->path = argv[i];
- new_cur_pkg->parent = cur_pkg;
- cur_pkg->children.append(new_cur_pkg);
- cur_pkg = new_cur_pkg;
- } else if (strcmp(arg, "--pkg-end") == 0) {
- if (cur_pkg->parent == nullptr) {
- fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n");
- return EXIT_FAILURE;
- }
- cur_pkg = cur_pkg->parent;
- } else if (i + 1 >= argc) {
- fprintf(stderr, "Expected another argument after %s\n", arg);
- return usage(arg0);
+ %return lib_dirs.append(arg[1..]);
+ } else if (mem.eql(u8, arg, "--pkg-begin")) {
+ @panic("TODO --pkg-begin");
+ } else if (mem.eql(u8, arg, "--pkg-end")) {
+ @panic("TODO --pkg-end");
+ } else if (arg_i + 1 >= args.len) {
+ return badArgs("expected another argument after {}", arg);
} else {
- i += 1;
- if (strcmp(arg, "--output") == 0) {
- out_file = argv[i];
- } else if (strcmp(arg, "--output-h") == 0) {
- out_file_h = argv[i];
- } else if (strcmp(arg, "--color") == 0) {
- if (strcmp(argv[i], "auto") == 0) {
- color = ErrColorAuto;
- } else if (strcmp(argv[i], "on") == 0) {
- color = ErrColorOn;
- } else if (strcmp(argv[i], "off") == 0) {
- color = ErrColorOff;
+ arg_i += 1;
+ if (mem.eql(u8, arg, "--output")) {
+ out_file = args[arg_i];
+ } else if (mem.eql(u8, arg, "--output-h")) {
+ out_file_h = args[arg_i];
+ } else if (mem.eql(u8, arg, "--color")) {
+ if (mem.eql(u8, args[arg_i], "auto")) {
+ color = ErrColor.Auto;
+ } else if (mem.eql(u8, args[arg_i], "on")) {
+ color = ErrColor.On;
+ } else if (mem.eql(u8, args[arg_i], "off")) {
+ color = ErrColor.Off;
} else {
- fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n");
- return usage(arg0);
+ return badArgs("--color options are 'auto', 'on', or 'off'");
}
- } else if (strcmp(arg, "--name") == 0) {
- out_name = argv[i];
- } else if (strcmp(arg, "--libc-lib-dir") == 0) {
- libc_lib_dir = argv[i];
- } else if (strcmp(arg, "--libc-static-lib-dir") == 0) {
- libc_static_lib_dir = argv[i];
- } else if (strcmp(arg, "--libc-include-dir") == 0) {
- libc_include_dir = argv[i];
- } else if (strcmp(arg, "--msvc-lib-dir") == 0) {
- msvc_lib_dir = argv[i];
- } else if (strcmp(arg, "--kernel32-lib-dir") == 0) {
- kernel32_lib_dir = argv[i];
- } else if (strcmp(arg, "--zig-install-prefix") == 0) {
- zig_install_prefix = argv[i];
- } else if (strcmp(arg, "--dynamic-linker") == 0) {
- dynamic_linker = argv[i];
- } else if (strcmp(arg, "-isystem") == 0) {
- clang_argv.append("-isystem");
- clang_argv.append(argv[i]);
- } else if (strcmp(arg, "-dirafter") == 0) {
- clang_argv.append("-dirafter");
- clang_argv.append(argv[i]);
- } else if (strcmp(arg, "-mllvm") == 0) {
- clang_argv.append("-mllvm");
- clang_argv.append(argv[i]);
+ } else if (mem.eql(u8, arg, "--emit")) {
+ if (mem.eql(u8, args[arg_i], "asm")) {
+ emit_file_type = Emit.Assembly;
+ } else if (mem.eql(u8, args[arg_i], "bin")) {
+ emit_file_type = Emit.Binary;
+ } else if (mem.eql(u8, args[arg_i], "llvm-ir")) {
+ emit_file_type = Emit.LlvmIr;
+ } else {
+ return badArgs("--emit options are 'asm', 'bin', or 'llvm-ir'");
+ }
+ } else if (mem.eql(u8, arg, "--name")) {
+ out_name_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--libc-lib-dir")) {
+ libc_lib_dir_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--libc-static-lib-dir")) {
+ libc_static_lib_dir_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--libc-include-dir")) {
+ libc_include_dir_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--msvc-lib-dir")) {
+ msvc_lib_dir_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--kernel32-lib-dir")) {
+ kernel32_lib_dir_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--zig-install-prefix")) {
+ zig_install_prefix = args[arg_i];
+ } else if (mem.eql(u8, arg, "--dynamic-linker")) {
+ dynamic_linker_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "-isystem")) {
+ %return clang_argv.append("-isystem");
+ %return clang_argv.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "-dirafter")) {
+ %return clang_argv.append("-dirafter");
+ %return clang_argv.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "-mllvm")) {
+ %return clang_argv.append("-mllvm");
+ %return clang_argv.append(args[arg_i]);
- llvm_argv.append(argv[i]);
- } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) {
- lib_dirs.append(argv[i]);
- } else if (strcmp(arg, "--library") == 0) {
- link_libs.append(argv[i]);
- } else if (strcmp(arg, "--object") == 0) {
- objects.append(argv[i]);
- } else if (strcmp(arg, "--assembly") == 0) {
- asm_files.append(argv[i]);
- } else if (strcmp(arg, "--cache-dir") == 0) {
- cache_dir = argv[i];
- } else if (strcmp(arg, "--target-arch") == 0) {
- target_arch = argv[i];
- } else if (strcmp(arg, "--target-os") == 0) {
- target_os = argv[i];
- } else if (strcmp(arg, "--target-environ") == 0) {
- target_environ = argv[i];
- } else if (strcmp(arg, "-mmacosx-version-min") == 0) {
- mmacosx_version_min = argv[i];
- } else if (strcmp(arg, "-mios-version-min") == 0) {
- mios_version_min = argv[i];
- } else if (strcmp(arg, "-framework") == 0) {
- frameworks.append(argv[i]);
- } else if (strcmp(arg, "--linker-script") == 0) {
- linker_script = argv[i];
- } else if (strcmp(arg, "-rpath") == 0) {
- rpath_list.append(argv[i]);
- } else if (strcmp(arg, "--test-filter") == 0) {
- test_filter = argv[i];
- } else if (strcmp(arg, "--test-name-prefix") == 0) {
- test_name_prefix = argv[i];
- } else if (strcmp(arg, "--ver-major") == 0) {
- ver_major = atoi(argv[i]);
- } else if (strcmp(arg, "--ver-minor") == 0) {
- ver_minor = atoi(argv[i]);
- } else if (strcmp(arg, "--ver-patch") == 0) {
- ver_patch = atoi(argv[i]);
- } else if (strcmp(arg, "--test-cmd") == 0) {
- test_exec_args.append(argv[i]);
+ %return llvm_argv.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--library-path") or mem.eql(u8, arg, "-L")) {
+ %return lib_dirs.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--library")) {
+ %return link_libs.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--object")) {
+ %return objects.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--assembly")) {
+ %return asm_files.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--cache-dir")) {
+ cache_dir_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--target-arch")) {
+ target_arch = args[arg_i];
+ } else if (mem.eql(u8, arg, "--target-os")) {
+ target_os = args[arg_i];
+ } else if (mem.eql(u8, arg, "--target-environ")) {
+ target_environ = args[arg_i];
+ } else if (mem.eql(u8, arg, "-mmacosx-version-min")) {
+ mmacosx_version_min = args[arg_i];
+ } else if (mem.eql(u8, arg, "-mios-version-min")) {
+ mios_version_min = args[arg_i];
+ } else if (mem.eql(u8, arg, "-framework")) {
+ %return frameworks.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--linker-script")) {
+ linker_script_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "-rpath")) {
+ %return rpath_list.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--test-filter")) {
+ %return test_filters.append(args[arg_i]);
+ } else if (mem.eql(u8, arg, "--test-name-prefix")) {
+ test_name_prefix_arg = args[arg_i];
+ } else if (mem.eql(u8, arg, "--ver-major")) {
+ ver_major = %return std.fmt.parseUnsigned(u32, args[arg_i], 10);
+ } else if (mem.eql(u8, arg, "--ver-minor")) {
+ ver_minor = %return std.fmt.parseUnsigned(u32, args[arg_i], 10);
+ } else if (mem.eql(u8, arg, "--ver-patch")) {
+ ver_patch = %return std.fmt.parseUnsigned(u32, args[arg_i], 10);
+ } else if (mem.eql(u8, arg, "--test-cmd")) {
+ @panic("TODO --test-cmd");
} else {
- fprintf(stderr, "Invalid argument: %s\n", arg);
- return usage(arg0);
+ return badArgs("invalid argument: {}", arg);
}
}
+ } else if (cmd == Cmd.None) {
+ if (mem.eql(u8, arg, "build-obj")) {
+ cmd = Cmd.Build;
+ build_kind = Module.Kind.Obj;
+ } else if (mem.eql(u8, arg, "build-exe")) {
+ cmd = Cmd.Build;
+ build_kind = Module.Kind.Exe;
+ } else if (mem.eql(u8, arg, "build-lib")) {
+ cmd = Cmd.Build;
+ build_kind = Module.Kind.Lib;
+ } else if (mem.eql(u8, arg, "version")) {
+ cmd = Cmd.Version;
+ } else if (mem.eql(u8, arg, "zen")) {
+ cmd = Cmd.Zen;
+ } else if (mem.eql(u8, arg, "translate-c")) {
+ cmd = Cmd.TranslateC;
+ } else if (mem.eql(u8, arg, "test")) {
+ cmd = Cmd.Test;
+ build_kind = Module.Kind.Exe;
+ } else {
+ return badArgs("unrecognized command: {}", arg);
+ }
+ } else switch (cmd) {
+ Cmd.Build, Cmd.TranslateC, Cmd.Test => {
+ if (in_file_arg == null) {
+ in_file_arg = arg;
+ } else {
+ return badArgs("unexpected extra parameter: {}", arg);
+ }
+ },
+ Cmd.Version, Cmd.Zen, Cmd.Targets => {
+ return badArgs("unexpected extra parameter: {}", arg);
+ },
+ Cmd.None => unreachable,
}
}
+
+ target.initializeAll();
+
+ // TODO
+// ZigTarget alloc_target;
+// ZigTarget *target;
+// if (!target_arch && !target_os && !target_environ) {
+// target = nullptr;
+// } else {
+// target = &alloc_target;
+// get_unknown_target(target);
+// if (target_arch) {
+// if (parse_target_arch(target_arch, &target->arch)) {
+// fprintf(stderr, "invalid --target-arch argument\n");
+// return usage(arg0);
+// }
+// }
+// if (target_os) {
+// if (parse_target_os(target_os, &target->os)) {
+// fprintf(stderr, "invalid --target-os argument\n");
+// return usage(arg0);
+// }
+// }
+// if (target_environ) {
+// if (parse_target_environ(target_environ, &target->env_type)) {
+// fprintf(stderr, "invalid --target-environ argument\n");
+// return usage(arg0);
+// }
+// }
+// }
+
+ switch (cmd) {
+ Cmd.None => return badArgs("expected command"),
+ Cmd.Zen => return printZen(),
+ Cmd.Build, Cmd.Test, Cmd.TranslateC => {
+ if (cmd == Cmd.Build and in_file_arg == null and objects.len == 0 and asm_files.len == 0) {
+ return badArgs("expected source file argument or at least one --object or --assembly argument");
+ } else if ((cmd == Cmd.TranslateC or cmd == Cmd.Test) and in_file_arg == null) {
+ return badArgs("expected source file argument");
+ } else if (cmd == Cmd.Build and build_kind == Module.Kind.Obj and objects.len != 0) {
+ return badArgs("When building an object file, --object arguments are invalid");
+ }
+
+ const root_name = switch (cmd) {
+ Cmd.Build, Cmd.TranslateC => x: {
+ if (out_name_arg) |out_name| {
+ break :x out_name;
+ } else if (in_file_arg) |in_file_path| {
+ const basename = os.path.basename(in_file_path);
+ var it = mem.split(basename, ".");
+ break :x it.next() ?? return badArgs("file name cannot be empty");
+ } else {
+ return badArgs("--name [name] not provided and unable to infer");
+ }
+ },
+ Cmd.Test => "test",
+ else => unreachable,
+ };
+
+ const zig_root_source_file = if (cmd == Cmd.TranslateC) null else in_file_arg;
+
+ const chosen_cache_dir = cache_dir_arg ?? default_zig_cache_name;
+ const full_cache_dir = %return os.path.resolve(allocator, ".", chosen_cache_dir);
+ defer allocator.free(full_cache_dir);
+
+ const zig_lib_dir = %return resolveZigLibDir(allocator, zig_install_prefix);
+ %defer allocator.free(zig_lib_dir);
+
+ const module = %return Module.create(allocator, root_name, zig_root_source_file,
+ Target.Native, build_kind, build_mode, zig_lib_dir, full_cache_dir);
+ defer module.destroy();
+
+ module.version_major = ver_major;
+ module.version_minor = ver_minor;
+ module.version_patch = ver_patch;
+
+ module.is_test = cmd == Cmd.Test;
+ if (linker_script_arg) |linker_script| {
+ module.linker_script = linker_script;
+ }
+ module.each_lib_rpath = each_lib_rpath;
+ module.clang_argv = clang_argv.toSliceConst();
+ module.llvm_argv = llvm_argv.toSliceConst();
+ module.strip = strip;
+ module.is_static = is_static;
+
+ if (libc_lib_dir_arg) |libc_lib_dir| {
+ module.libc_lib_dir = libc_lib_dir;
+ }
+ if (libc_static_lib_dir_arg) |libc_static_lib_dir| {
+ module.libc_static_lib_dir = libc_static_lib_dir;
+ }
+ if (libc_include_dir_arg) |libc_include_dir| {
+ module.libc_include_dir = libc_include_dir;
+ }
+ if (msvc_lib_dir_arg) |msvc_lib_dir| {
+ module.msvc_lib_dir = msvc_lib_dir;
+ }
+ if (kernel32_lib_dir_arg) |kernel32_lib_dir| {
+ module.kernel32_lib_dir = kernel32_lib_dir;
+ }
+ if (dynamic_linker_arg) |dynamic_linker| {
+ module.dynamic_linker = dynamic_linker;
+ }
+ module.verbose_tokenize = verbose_tokenize;
+ module.verbose_ast_tree = verbose_ast_tree;
+ module.verbose_ast_fmt = verbose_ast_fmt;
+ module.verbose_link = verbose_link;
+ module.verbose_ir = verbose_ir;
+ module.verbose_llvm_ir = verbose_llvm_ir;
+ module.verbose_cimport = verbose_cimport;
+
+ module.err_color = color;
+
+ module.lib_dirs = lib_dirs.toSliceConst();
+ module.darwin_frameworks = frameworks.toSliceConst();
+ module.rpath_list = rpath_list.toSliceConst();
+
+ for (link_libs.toSliceConst()) |name| {
+ _ = %return module.addLinkLib(name, true);
+ }
+
+ module.windows_subsystem_windows = mwindows;
+ module.windows_subsystem_console = mconsole;
+ module.linker_rdynamic = rdynamic;
+
+ if (mmacosx_version_min != null and mios_version_min != null) {
+ return badArgs("-mmacosx-version-min and -mios-version-min options not allowed together");
+ }
+
+ if (mmacosx_version_min) |ver| {
+ module.darwin_version_min = Module.DarwinVersionMin { .MacOS = ver };
+ } else if (mios_version_min) |ver| {
+ module.darwin_version_min = Module.DarwinVersionMin { .Ios = ver };
+ }
+
+ module.test_filters = test_filters.toSliceConst();
+ module.test_name_prefix = test_name_prefix_arg;
+ module.out_h_path = out_file_h;
+
+ // TODO
+ //add_package(g, cur_pkg, g->root_package);
+
+ switch (cmd) {
+ Cmd.Build => {
+ module.emit_file_type = emit_file_type;
+
+ module.link_objects = objects.toSliceConst();
+ module.assembly_files = asm_files.toSliceConst();
+
+ %return module.build();
+ %return module.link(out_file);
+ },
+ Cmd.TranslateC => @panic("TODO translate-c"),
+ Cmd.Test => @panic("TODO test cmd"),
+ else => unreachable,
+ }
+ },
+ Cmd.Version => @panic("TODO zig version"),
+ Cmd.Targets => @panic("TODO zig targets"),
+ }
}
-fn printUsage(outstream: &io.OutStream) -> %void {
- %return outstream.print("Usage: {} [command] [options]\n", arg0);
- %return outstream.write(
+fn printUsage(stream: &io.OutStream) -> %void {
+ %return stream.write(
+ \\Usage: zig [command] [options]
+ \\
\\Commands:
\\ build build project from build.zig
\\ build-exe [source] create executable from source or object files
@@ -217,6 +485,7 @@ fn printUsage(outstream: &io.OutStream) -> %void {
\\ --assembly [source] add assembly file to build
\\ --cache-dir [path] override the cache directory
\\ --color [auto|off|on] enable or disable colored error messages
+ \\ --emit [filetype] emit a specific file format as compilation output
\\ --enable-timing-info print timing diagnostics
\\ --libc-include-dir [path] directory where libc stdlib.h resides
\\ --name [name] override output name
@@ -231,9 +500,13 @@ fn printUsage(outstream: &io.OutStream) -> %void {
\\ --target-arch [name] specify target architecture
\\ --target-environ [name] specify target environment
\\ --target-os [name] specify target operating system
- \\ --verbose turn on compiler debug output
- \\ --verbose-link turn on compiler debug output for linking only
- \\ --verbose-ir turn on compiler debug output for IR only
+ \\ --verbose-tokenize enable compiler debug info: tokenization
+ \\ --verbose-ast-tree enable compiler debug info: parsing into an AST (treeview)
+ \\ --verbose-ast-fmt enable compiler debug info: parsing into an AST (render source)
+ \\ --verbose-cimport enable compiler debug info: C imports
+ \\ --verbose-ir enable compiler debug info: Zig IR
+ \\ --verbose-llvm-ir enable compiler debug info: LLVM IR
+ \\ --verbose-link enable compiler debug info: linking
\\ --zig-install-prefix [path] override directory where zig thinks it is installed
\\ -dirafter [dir] same as -isystem but do it last
\\ -isystem [dir] add additional search path for other .h files
@@ -255,7 +528,6 @@ fn printUsage(outstream: &io.OutStream) -> %void {
\\ -rpath [path] add directory to the runtime library search path
\\ -mconsole (windows) --subsystem console to the linker
\\ -mwindows (windows) --subsystem windows to the linker
- \\ -municode (windows) link with unicode
\\ -framework [name] (darwin) link against framework
\\ -mios-version-min [ver] (darwin) set iOS deployment target
\\ -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target
@@ -271,17 +543,81 @@ fn printUsage(outstream: &io.OutStream) -> %void {
);
}
-const ZIG_ZEN =
- \\ * Communicate intent precisely.
- \\ * Edge cases matter.
- \\ * Favor reading code over writing code.
- \\ * Only one obvious way to do things.
- \\ * Runtime crashes are better than bugs.
- \\ * Compile errors are better than runtime crashes.
- \\ * Incremental improvements.
- \\ * Avoid local maximums.
- \\ * Reduce the amount one must remember.
- \\ * Minimize energy spent on coding style.
- \\ * Together we serve end users.
- \\
-;
+fn printZen() -> %void {
+ var stdout_file = %return io.getStdErr();
+ %return stdout_file.write(
+ \\
+ \\ * Communicate intent precisely.
+ \\ * Edge cases matter.
+ \\ * Favor reading code over writing code.
+ \\ * Only one obvious way to do things.
+ \\ * Runtime crashes are better than bugs.
+ \\ * Compile errors are better than runtime crashes.
+ \\ * Incremental improvements.
+ \\ * Avoid local maximums.
+ \\ * Reduce the amount one must remember.
+ \\ * Minimize energy spent on coding style.
+ \\ * Together we serve end users.
+ \\
+ \\
+ );
+}
+
+/// Caller must free result
+fn resolveZigLibDir(allocator: &mem.Allocator, zig_install_prefix_arg: ?[]const u8) -> %[]u8 {
+ if (zig_install_prefix_arg) |zig_install_prefix| {
+ return testZigInstallPrefix(allocator, zig_install_prefix) %% |err| {
+ warn("No Zig installation found at prefix {}: {}\n", zig_install_prefix_arg, @errorName(err));
+ return error.ZigInstallationNotFound;
+ };
+ } else {
+ return findZigLibDir(allocator) %% |err| {
+ warn("Unable to find zig lib directory: {}.\nReinstall Zig or use --zig-install-prefix.\n",
+ @errorName(err));
+ return error.ZigLibDirNotFound;
+ };
+ }
+}
+
+/// Caller must free result
+fn testZigInstallPrefix(allocator: &mem.Allocator, test_path: []const u8) -> %[]u8 {
+ const test_zig_dir = %return os.path.join(allocator, test_path, "lib", "zig");
+ %defer allocator.free(test_zig_dir);
+
+ const test_index_file = %return os.path.join(allocator, test_zig_dir, "std", "index.zig");
+ defer allocator.free(test_index_file);
+
+ var file = %return io.File.openRead(test_index_file, allocator);
+ file.close();
+
+ return test_zig_dir;
+}
+
+/// Caller must free result
+fn findZigLibDir(allocator: &mem.Allocator) -> %[]u8 {
+ const self_exe_path = %return os.selfExeDirPath(allocator);
+ defer allocator.free(self_exe_path);
+
+ var cur_path: []const u8 = self_exe_path;
+ while (true) {
+ const test_dir = os.path.dirname(cur_path);
+
+ if (mem.eql(u8, test_dir, cur_path)) {
+ break;
+ }
+
+ return testZigInstallPrefix(allocator, test_dir) %% |err| {
+ cur_path = test_dir;
+ continue;
+ };
+ }
+
+ // TODO look in hard coded installation path from configuration
+ //if (ZIG_INSTALL_PREFIX != nullptr) {
+ // if (test_zig_install_prefix(buf_create_from_str(ZIG_INSTALL_PREFIX), out_path)) {
+ // return 0;
+ // }
+ //}
+
+ return error.FileNotFound;
+}
diff --git a/src-self-hosted/module.zig b/src-self-hosted/module.zig
new file mode 100644
index 0000000000..700ccf0176
--- /dev/null
+++ b/src-self-hosted/module.zig
@@ -0,0 +1,295 @@
+const std = @import("std");
+const os = std.os;
+const io = std.io;
+const mem = std.mem;
+const Buffer = std.Buffer;
+const llvm = @import("llvm.zig");
+const c = @import("c.zig");
+const builtin = @import("builtin");
+const Target = @import("target.zig").Target;
+const warn = std.debug.warn;
+const Tokenizer = @import("tokenizer.zig").Tokenizer;
+const Token = @import("tokenizer.zig").Token;
+const Parser = @import("parser.zig").Parser;
+const ArrayList = std.ArrayList;
+
+pub const Module = struct {
+ allocator: &mem.Allocator,
+ name: Buffer,
+ root_src_path: ?[]const u8,
+ module: llvm.ModuleRef,
+ context: llvm.ContextRef,
+ builder: llvm.BuilderRef,
+ target: Target,
+ build_mode: builtin.Mode,
+ zig_lib_dir: []const u8,
+
+ version_major: u32,
+ version_minor: u32,
+ version_patch: u32,
+
+ linker_script: ?[]const u8,
+ cache_dir: []const u8,
+ libc_lib_dir: ?[]const u8,
+ libc_static_lib_dir: ?[]const u8,
+ libc_include_dir: ?[]const u8,
+ msvc_lib_dir: ?[]const u8,
+ kernel32_lib_dir: ?[]const u8,
+ dynamic_linker: ?[]const u8,
+ out_h_path: ?[]const u8,
+
+ is_test: bool,
+ each_lib_rpath: bool,
+ strip: bool,
+ is_static: bool,
+ linker_rdynamic: bool,
+
+ clang_argv: []const []const u8,
+ llvm_argv: []const []const u8,
+ lib_dirs: []const []const u8,
+ rpath_list: []const []const u8,
+ assembly_files: []const []const u8,
+ link_objects: []const []const u8,
+
+ windows_subsystem_windows: bool,
+ windows_subsystem_console: bool,
+
+ link_libs_list: ArrayList(&LinkLib),
+ libc_link_lib: ?&LinkLib,
+
+ err_color: ErrColor,
+
+ verbose_tokenize: bool,
+ verbose_ast_tree: bool,
+ verbose_ast_fmt: bool,
+ verbose_cimport: bool,
+ verbose_ir: bool,
+ verbose_llvm_ir: bool,
+ verbose_link: bool,
+
+ darwin_frameworks: []const []const u8,
+ darwin_version_min: DarwinVersionMin,
+
+ test_filters: []const []const u8,
+ test_name_prefix: ?[]const u8,
+
+ emit_file_type: Emit,
+
+ kind: Kind,
+
+ pub const DarwinVersionMin = union(enum) {
+ None,
+ MacOS: []const u8,
+ Ios: []const u8,
+ };
+
+ pub const Kind = enum {
+ Exe,
+ Lib,
+ Obj,
+ };
+
+ pub const ErrColor = enum {
+ Auto,
+ Off,
+ On,
+ };
+
+ pub const LinkLib = struct {
+ name: []const u8,
+ path: ?[]const u8,
+ /// the list of symbols we depend on from this lib
+ symbols: ArrayList([]u8),
+ provided_explicitly: bool,
+ };
+
+ pub const Emit = enum {
+ Binary,
+ Assembly,
+ LlvmIr,
+ };
+
+ pub fn create(allocator: &mem.Allocator, name: []const u8, root_src_path: ?[]const u8, target: &const Target,
+ kind: Kind, build_mode: builtin.Mode, zig_lib_dir: []const u8, cache_dir: []const u8) -> %&Module
+ {
+ var name_buffer = %return Buffer.init(allocator, name);
+ %defer name_buffer.deinit();
+
+ const context = c.LLVMContextCreate() ?? return error.OutOfMemory;
+ %defer c.LLVMContextDispose(context);
+
+ const module = c.LLVMModuleCreateWithNameInContext(name_buffer.ptr(), context) ?? return error.OutOfMemory;
+ %defer c.LLVMDisposeModule(module);
+
+ const builder = c.LLVMCreateBuilderInContext(context) ?? return error.OutOfMemory;
+ %defer c.LLVMDisposeBuilder(builder);
+
+ const module_ptr = %return allocator.create(Module);
+ %defer allocator.destroy(module_ptr);
+
+ *module_ptr = Module {
+ .allocator = allocator,
+ .name = name_buffer,
+ .root_src_path = root_src_path,
+ .module = module,
+ .context = context,
+ .builder = builder,
+ .target = *target,
+ .kind = kind,
+ .build_mode = build_mode,
+ .zig_lib_dir = zig_lib_dir,
+ .cache_dir = cache_dir,
+
+ .version_major = 0,
+ .version_minor = 0,
+ .version_patch = 0,
+
+ .verbose_tokenize = false,
+ .verbose_ast_tree = false,
+ .verbose_ast_fmt = false,
+ .verbose_cimport = false,
+ .verbose_ir = false,
+ .verbose_llvm_ir = false,
+ .verbose_link = false,
+
+ .linker_script = null,
+ .libc_lib_dir = null,
+ .libc_static_lib_dir = null,
+ .libc_include_dir = null,
+ .msvc_lib_dir = null,
+ .kernel32_lib_dir = null,
+ .dynamic_linker = null,
+ .out_h_path = null,
+ .is_test = false,
+ .each_lib_rpath = false,
+ .strip = false,
+ .is_static = false,
+ .linker_rdynamic = false,
+ .clang_argv = [][]const u8{},
+ .llvm_argv = [][]const u8{},
+ .lib_dirs = [][]const u8{},
+ .rpath_list = [][]const u8{},
+ .assembly_files = [][]const u8{},
+ .link_objects = [][]const u8{},
+ .windows_subsystem_windows = false,
+ .windows_subsystem_console = false,
+ .link_libs_list = ArrayList(&LinkLib).init(allocator),
+ .libc_link_lib = null,
+ .err_color = ErrColor.Auto,
+ .darwin_frameworks = [][]const u8{},
+ .darwin_version_min = DarwinVersionMin.None,
+ .test_filters = [][]const u8{},
+ .test_name_prefix = null,
+ .emit_file_type = Emit.Binary,
+ };
+ return module_ptr;
+ }
+
+ fn dump(self: &Module) {
+ c.LLVMDumpModule(self.module);
+ }
+
+ pub fn destroy(self: &Module) {
+ c.LLVMDisposeBuilder(self.builder);
+ c.LLVMDisposeModule(self.module);
+ c.LLVMContextDispose(self.context);
+ self.name.deinit();
+
+ self.allocator.destroy(self);
+ }
+
+ pub fn build(self: &Module) -> %void {
+ const root_src_path = self.root_src_path ?? @panic("TODO handle null root src path");
+ const root_src_real_path = os.path.real(self.allocator, root_src_path) %% |err| {
+ %return printError("unable to open '{}': {}", root_src_path, err);
+ return err;
+ };
+ %defer self.allocator.free(root_src_real_path);
+
+ const source_code = io.readFileAlloc(root_src_real_path, self.allocator) %% |err| {
+ %return printError("unable to open '{}': {}", root_src_real_path, err);
+ return err;
+ };
+ %defer self.allocator.free(source_code);
+
+ warn("====input:====\n");
+
+ warn("{}", source_code);
+
+ warn("====tokenization:====\n");
+ {
+ var tokenizer = Tokenizer.init(source_code);
+ while (true) {
+ const token = tokenizer.next();
+ tokenizer.dump(token);
+ if (token.id == Token.Id.Eof) {
+ break;
+ }
+ }
+ }
+
+ warn("====parse:====\n");
+
+ var tokenizer = Tokenizer.init(source_code);
+ var parser = Parser.init(&tokenizer, self.allocator, root_src_real_path);
+ defer parser.deinit();
+
+ const root_node = %return parser.parse();
+ defer parser.freeAst(root_node);
+
+ var stderr_file = %return std.io.getStdErr();
+ var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
+ const out_stream = &stderr_file_out_stream.stream;
+ %return parser.renderAst(out_stream, root_node);
+
+ warn("====fmt:====\n");
+ %return parser.renderSource(out_stream, root_node);
+
+ warn("====ir:====\n");
+ warn("TODO\n\n");
+
+ warn("====llvm ir:====\n");
+ self.dump();
+
+ }
+
+ pub fn link(self: &Module, out_file: ?[]const u8) -> %void {
+ warn("TODO link");
+ }
+
+ pub fn addLinkLib(self: &Module, name: []const u8, provided_explicitly: bool) -> %&LinkLib {
+ const is_libc = mem.eql(u8, name, "c");
+
+ if (is_libc) {
+ if (self.libc_link_lib) |libc_link_lib| {
+ return libc_link_lib;
+ }
+ }
+
+ for (self.link_libs_list.toSliceConst()) |existing_lib| {
+ if (mem.eql(u8, name, existing_lib.name)) {
+ return existing_lib;
+ }
+ }
+
+ const link_lib = %return self.allocator.create(LinkLib);
+ *link_lib = LinkLib {
+ .name = name,
+ .path = null,
+ .provided_explicitly = provided_explicitly,
+ .symbols = ArrayList([]u8).init(self.allocator),
+ };
+ %return self.link_libs_list.append(link_lib);
+ if (is_libc) {
+ self.libc_link_lib = link_lib;
+ }
+ return link_lib;
+ }
+};
+
+fn printError(comptime format: []const u8, args: ...) -> %void {
+ var stderr_file = %return std.io.getStdErr();
+ var stderr_file_out_stream = std.io.FileOutStream.init(&stderr_file);
+ const out_stream = &stderr_file_out_stream.stream;
+ %return out_stream.print(format, args);
+}
diff --git a/src-self-hosted/parser.zig b/src-self-hosted/parser.zig
new file mode 100644
index 0000000000..c997536ce2
--- /dev/null
+++ b/src-self-hosted/parser.zig
@@ -0,0 +1,1203 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const ArrayList = std.ArrayList;
+const mem = std.mem;
+const ast = @import("ast.zig");
+const Tokenizer = @import("tokenizer.zig").Tokenizer;
+const Token = @import("tokenizer.zig").Token;
+const builtin = @import("builtin");
+const io = std.io;
+
+// TODO when we make parse errors into error types instead of printing directly,
+// get rid of this
+const warn = std.debug.warn;
+
+error ParseError;
+
+pub const Parser = struct {
+ allocator: &mem.Allocator,
+ tokenizer: &Tokenizer,
+ put_back_tokens: [2]Token,
+ put_back_count: usize,
+ source_file_name: []const u8,
+ cleanup_root_node: ?&ast.NodeRoot,
+
+ // This memory contents are used only during a function call. It's used to repurpose memory;
+ // specifically so that freeAst can be guaranteed to succeed.
+ const utility_bytes_align = @alignOf( union { a: RenderAstFrame, b: State, c: RenderState } );
+ utility_bytes: []align(utility_bytes_align) u8,
+
+ pub fn init(tokenizer: &Tokenizer, allocator: &mem.Allocator, source_file_name: []const u8) -> Parser {
+ return Parser {
+ .allocator = allocator,
+ .tokenizer = tokenizer,
+ .put_back_tokens = undefined,
+ .put_back_count = 0,
+ .source_file_name = source_file_name,
+ .utility_bytes = []align(utility_bytes_align) u8{},
+ .cleanup_root_node = null,
+ };
+ }
+
+ pub fn deinit(self: &Parser) {
+ assert(self.cleanup_root_node == null);
+ self.allocator.free(self.utility_bytes);
+ }
+
+ const TopLevelDeclCtx = struct {
+ visib_token: ?Token,
+ extern_token: ?Token,
+ };
+
+ const DestPtr = union(enum) {
+ Field: &&ast.Node,
+ NullableField: &?&ast.Node,
+ List: &ArrayList(&ast.Node),
+
+ pub fn store(self: &const DestPtr, value: &ast.Node) -> %void {
+ switch (*self) {
+ DestPtr.Field => |ptr| *ptr = value,
+ DestPtr.NullableField => |ptr| *ptr = value,
+ DestPtr.List => |list| %return list.append(value),
+ }
+ }
+ };
+
+ const State = union(enum) {
+ TopLevel,
+ TopLevelExtern: ?Token,
+ TopLevelDecl: TopLevelDeclCtx,
+ Expression: DestPtr,
+ ExpectOperand,
+ Operand: &ast.Node,
+ AfterOperand,
+ InfixOp: &ast.NodeInfixOp,
+ PrefixOp: &ast.NodePrefixOp,
+ AddrOfModifiers: &ast.NodePrefixOp.AddrOfInfo,
+ TypeExpr: DestPtr,
+ VarDecl: &ast.NodeVarDecl,
+ VarDeclAlign: &ast.NodeVarDecl,
+ VarDeclEq: &ast.NodeVarDecl,
+ ExpectToken: @TagType(Token.Id),
+ FnProto: &ast.NodeFnProto,
+ FnProtoAlign: &ast.NodeFnProto,
+ ParamDecl: &ast.NodeFnProto,
+ ParamDeclComma,
+ FnDef: &ast.NodeFnProto,
+ Block: &ast.NodeBlock,
+ Statement: &ast.NodeBlock,
+ };
+
+ pub fn freeAst(self: &Parser, root_node: &ast.NodeRoot) {
+ // utility_bytes is big enough to do this iteration since we were able to do
+ // the parsing in the first place
+ comptime assert(@sizeOf(State) >= @sizeOf(&ast.Node));
+
+ var stack = self.initUtilityArrayList(&ast.Node);
+ defer self.deinitUtilityArrayList(stack);
+
+ stack.append(&root_node.base) %% unreachable;
+ while (stack.popOrNull()) |node| {
+ var i: usize = 0;
+ while (node.iterate(i)) |child| : (i += 1) {
+ if (child.iterate(0) != null) {
+ stack.append(child) %% unreachable;
+ } else {
+ child.destroy(self.allocator);
+ }
+ }
+ node.destroy(self.allocator);
+ }
+ }
+
+ pub fn parse(self: &Parser) -> %&ast.NodeRoot {
+ const result = self.parseInner() %% |err| x: {
+ if (self.cleanup_root_node) |root_node| {
+ self.freeAst(root_node);
+ }
+ break :x err;
+ };
+ self.cleanup_root_node = null;
+ return result;
+ }
+
+ pub fn parseInner(self: &Parser) -> %&ast.NodeRoot {
+ var stack = self.initUtilityArrayList(State);
+ defer self.deinitUtilityArrayList(stack);
+
+ const root_node = x: {
+ const root_node = %return self.createRoot();
+ %defer self.allocator.destroy(root_node);
+ // This stack append has to succeed for freeAst to work
+ %return stack.append(State.TopLevel);
+ break :x root_node;
+ };
+ assert(self.cleanup_root_node == null);
+ self.cleanup_root_node = root_node;
+
+ while (true) {
+ //{
+ // const token = self.getNextToken();
+ // warn("{} ", @tagName(token.id));
+ // self.putBackToken(token);
+ // var i: usize = stack.len;
+ // while (i != 0) {
+ // i -= 1;
+ // warn("{} ", @tagName(stack.items[i]));
+ // }
+ // warn("\n");
+ //}
+
+ // This gives us 1 free append that can't fail
+ const state = stack.pop();
+
+ switch (state) {
+ State.TopLevel => {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_pub, Token.Id.Keyword_export => {
+ stack.append(State { .TopLevelExtern = token }) %% unreachable;
+ continue;
+ },
+ Token.Id.Eof => return root_node,
+ else => {
+ self.putBackToken(token);
+ // TODO shouldn't need this cast
+ stack.append(State { .TopLevelExtern = null }) %% unreachable;
+ continue;
+ },
+ }
+ },
+ State.TopLevelExtern => |visib_token| {
+ const token = self.getNextToken();
+ if (token.id == Token.Id.Keyword_extern) {
+ stack.append(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .visib_token = visib_token,
+ .extern_token = token,
+ },
+ }) %% unreachable;
+ continue;
+ }
+ self.putBackToken(token);
+ stack.append(State {
+ .TopLevelDecl = TopLevelDeclCtx {
+ .visib_token = visib_token,
+ .extern_token = null,
+ },
+ }) %% unreachable;
+ continue;
+ },
+ State.TopLevelDecl => |ctx| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_var, Token.Id.Keyword_const => {
+ stack.append(State.TopLevel) %% unreachable;
+ // TODO shouldn't need these casts
+ const var_decl_node = %return self.createAttachVarDecl(&root_node.decls, ctx.visib_token,
+ token, (?Token)(null), ctx.extern_token);
+ %return stack.append(State { .VarDecl = var_decl_node });
+ continue;
+ },
+ Token.Id.Keyword_fn => {
+ stack.append(State.TopLevel) %% unreachable;
+ // TODO shouldn't need these casts
+ const fn_proto = %return self.createAttachFnProto(&root_node.decls, token,
+ ctx.extern_token, (?Token)(null), (?Token)(null), (?Token)(null));
+ %return stack.append(State { .FnDef = fn_proto });
+ %return stack.append(State { .FnProto = fn_proto });
+ continue;
+ },
+ Token.Id.StringLiteral => {
+ @panic("TODO extern with string literal");
+ },
+ Token.Id.Keyword_coldcc, Token.Id.Keyword_nakedcc, Token.Id.Keyword_stdcallcc => {
+ stack.append(State.TopLevel) %% unreachable;
+ const fn_token = %return self.eatToken(Token.Id.Keyword_fn);
+ // TODO shouldn't need this cast
+ const fn_proto = %return self.createAttachFnProto(&root_node.decls, fn_token,
+ ctx.extern_token, (?Token)(token), (?Token)(null), (?Token)(null));
+ %return stack.append(State { .FnDef = fn_proto });
+ %return stack.append(State { .FnProto = fn_proto });
+ continue;
+ },
+ else => return self.parseError(token, "expected variable declaration or function, found {}", @tagName(token.id)),
+ }
+ },
+ State.VarDecl => |var_decl| {
+ var_decl.name_token = %return self.eatToken(Token.Id.Identifier);
+ stack.append(State { .VarDeclAlign = var_decl }) %% unreachable;
+
+ const next_token = self.getNextToken();
+ if (next_token.id == Token.Id.Colon) {
+ %return stack.append(State { .TypeExpr = DestPtr {.NullableField = &var_decl.type_node} });
+ continue;
+ }
+
+ self.putBackToken(next_token);
+ continue;
+ },
+ State.VarDeclAlign => |var_decl| {
+ stack.append(State { .VarDeclEq = var_decl }) %% unreachable;
+
+ const next_token = self.getNextToken();
+ if (next_token.id == Token.Id.Keyword_align) {
+ _ = %return self.eatToken(Token.Id.LParen);
+ %return stack.append(State { .ExpectToken = Token.Id.RParen });
+ %return stack.append(State { .Expression = DestPtr{.NullableField = &var_decl.align_node} });
+ continue;
+ }
+
+ self.putBackToken(next_token);
+ continue;
+ },
+ State.VarDeclEq => |var_decl| {
+ const token = self.getNextToken();
+ if (token.id == Token.Id.Equal) {
+ var_decl.eq_token = token;
+ stack.append(State { .ExpectToken = Token.Id.Semicolon }) %% unreachable;
+ %return stack.append(State {
+ .Expression = DestPtr {.NullableField = &var_decl.init_node},
+ });
+ continue;
+ }
+ if (token.id == Token.Id.Semicolon) {
+ continue;
+ }
+ return self.parseError(token, "expected '=' or ';', found {}", @tagName(token.id));
+ },
+ State.ExpectToken => |token_id| {
+ _ = %return self.eatToken(token_id);
+ continue;
+ },
+
+ State.Expression => |dest_ptr| {
+ // save the dest_ptr for later
+ stack.append(state) %% unreachable;
+ %return stack.append(State.ExpectOperand);
+ continue;
+ },
+ State.ExpectOperand => {
+ // we'll either get an operand (like 1 or x),
+ // or a prefix operator (like ~ or return).
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_return => {
+ %return stack.append(State { .PrefixOp = %return self.createPrefixOp(token,
+ ast.NodePrefixOp.PrefixOp.Return) });
+ %return stack.append(State.ExpectOperand);
+ continue;
+ },
+ Token.Id.Ampersand => {
+ const prefix_op = %return self.createPrefixOp(token, ast.NodePrefixOp.PrefixOp{
+ .AddrOf = ast.NodePrefixOp.AddrOfInfo {
+ .align_expr = null,
+ .bit_offset_start_token = null,
+ .bit_offset_end_token = null,
+ .const_token = null,
+ .volatile_token = null,
+ }
+ });
+ %return stack.append(State { .PrefixOp = prefix_op });
+ %return stack.append(State.ExpectOperand);
+ %return stack.append(State { .AddrOfModifiers = &prefix_op.op.AddrOf });
+ continue;
+ },
+ Token.Id.Identifier => {
+ %return stack.append(State {
+ .Operand = &(%return self.createIdentifier(token)).base
+ });
+ %return stack.append(State.AfterOperand);
+ continue;
+ },
+ Token.Id.IntegerLiteral => {
+ %return stack.append(State {
+ .Operand = &(%return self.createIntegerLiteral(token)).base
+ });
+ %return stack.append(State.AfterOperand);
+ continue;
+ },
+ Token.Id.FloatLiteral => {
+ %return stack.append(State {
+ .Operand = &(%return self.createFloatLiteral(token)).base
+ });
+ %return stack.append(State.AfterOperand);
+ continue;
+ },
+ else => return self.parseError(token, "expected primary expression, found {}", @tagName(token.id)),
+ }
+ },
+
+ State.AfterOperand => {
+ // we'll either get an infix operator (like != or ^),
+ // or a postfix operator (like () or {}),
+ // otherwise this expression is done (like on a ; or else).
+ var token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.EqualEqual => {
+ %return stack.append(State {
+ .InfixOp = %return self.createInfixOp(token, ast.NodeInfixOp.InfixOp.EqualEqual)
+ });
+ %return stack.append(State.ExpectOperand);
+ continue;
+ },
+ Token.Id.BangEqual => {
+ %return stack.append(State {
+ .InfixOp = %return self.createInfixOp(token, ast.NodeInfixOp.InfixOp.BangEqual)
+ });
+ %return stack.append(State.ExpectOperand);
+ continue;
+ },
+ else => {
+ // no postfix/infix operator after this operand.
+ self.putBackToken(token);
+ // reduce the stack
+ var expression: &ast.Node = stack.pop().Operand;
+ while (true) {
+ switch (stack.pop()) {
+ State.Expression => |dest_ptr| {
+ // we're done
+ %return dest_ptr.store(expression);
+ break;
+ },
+ State.InfixOp => |infix_op| {
+ infix_op.rhs = expression;
+ infix_op.lhs = stack.pop().Operand;
+ expression = &infix_op.base;
+ continue;
+ },
+ State.PrefixOp => |prefix_op| {
+ prefix_op.rhs = expression;
+ expression = &prefix_op.base;
+ continue;
+ },
+ else => unreachable,
+ }
+ }
+ continue;
+ },
+ }
+ },
+
+ State.AddrOfModifiers => |addr_of_info| {
+ var token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.Keyword_align => {
+ stack.append(state) %% unreachable;
+ if (addr_of_info.align_expr != null) return self.parseError(token, "multiple align qualifiers");
+ _ = %return self.eatToken(Token.Id.LParen);
+ %return stack.append(State { .ExpectToken = Token.Id.RParen });
+ %return stack.append(State { .Expression = DestPtr{.NullableField = &addr_of_info.align_expr} });
+ continue;
+ },
+ Token.Id.Keyword_const => {
+ stack.append(state) %% unreachable;
+ if (addr_of_info.const_token != null) return self.parseError(token, "duplicate qualifier: const");
+ addr_of_info.const_token = token;
+ continue;
+ },
+ Token.Id.Keyword_volatile => {
+ stack.append(state) %% unreachable;
+ if (addr_of_info.volatile_token != null) return self.parseError(token, "duplicate qualifier: volatile");
+ addr_of_info.volatile_token = token;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ continue;
+ },
+ }
+ },
+
+ State.TypeExpr => |dest_ptr| {
+ const token = self.getNextToken();
+ if (token.id == Token.Id.Keyword_var) {
+ @panic("TODO param with type var");
+ }
+ self.putBackToken(token);
+
+ stack.append(State { .Expression = dest_ptr }) %% unreachable;
+ continue;
+ },
+
+ State.FnProto => |fn_proto| {
+ stack.append(State { .FnProtoAlign = fn_proto }) %% unreachable;
+ %return stack.append(State { .ParamDecl = fn_proto });
+ %return stack.append(State { .ExpectToken = Token.Id.LParen });
+
+ const next_token = self.getNextToken();
+ if (next_token.id == Token.Id.Identifier) {
+ fn_proto.name_token = next_token;
+ continue;
+ }
+ self.putBackToken(next_token);
+ continue;
+ },
+
+ State.FnProtoAlign => |fn_proto| {
+ const token = self.getNextToken();
+ if (token.id == Token.Id.Keyword_align) {
+ @panic("TODO fn proto align");
+ }
+ if (token.id == Token.Id.Arrow) {
+ stack.append(State {
+ .TypeExpr = DestPtr {.NullableField = &fn_proto.return_type},
+ }) %% unreachable;
+ continue;
+ } else {
+ self.putBackToken(token);
+ continue;
+ }
+ },
+
+ State.ParamDecl => |fn_proto| {
+ var token = self.getNextToken();
+ if (token.id == Token.Id.RParen) {
+ continue;
+ }
+ const param_decl = %return self.createAttachParamDecl(&fn_proto.params);
+ if (token.id == Token.Id.Keyword_comptime) {
+ param_decl.comptime_token = token;
+ token = self.getNextToken();
+ } else if (token.id == Token.Id.Keyword_noalias) {
+ param_decl.noalias_token = token;
+ token = self.getNextToken();
+ }
+ if (token.id == Token.Id.Identifier) {
+ const next_token = self.getNextToken();
+ if (next_token.id == Token.Id.Colon) {
+ param_decl.name_token = token;
+ token = self.getNextToken();
+ } else {
+ self.putBackToken(next_token);
+ }
+ }
+ if (token.id == Token.Id.Ellipsis3) {
+ param_decl.var_args_token = token;
+ stack.append(State { .ExpectToken = Token.Id.RParen }) %% unreachable;
+ continue;
+ } else {
+ self.putBackToken(token);
+ }
+
+ stack.append(State { .ParamDecl = fn_proto }) %% unreachable;
+ %return stack.append(State.ParamDeclComma);
+ %return stack.append(State {
+ .TypeExpr = DestPtr {.Field = ¶m_decl.type_node}
+ });
+ continue;
+ },
+
+ State.ParamDeclComma => {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.RParen => {
+ _ = stack.pop(); // pop off the ParamDecl
+ continue;
+ },
+ Token.Id.Comma => continue,
+ else => return self.parseError(token, "expected ',' or ')', found {}", @tagName(token.id)),
+ }
+ },
+
+ State.FnDef => |fn_proto| {
+ const token = self.getNextToken();
+ switch(token.id) {
+ Token.Id.LBrace => {
+ const block = %return self.createBlock(token);
+ fn_proto.body_node = &block.base;
+ stack.append(State { .Block = block }) %% unreachable;
+ continue;
+ },
+ Token.Id.Semicolon => continue,
+ else => return self.parseError(token, "expected ';' or '{{', found {}", @tagName(token.id)),
+ }
+ },
+
+ State.Block => |block| {
+ const token = self.getNextToken();
+ switch (token.id) {
+ Token.Id.RBrace => {
+ block.end_token = token;
+ continue;
+ },
+ else => {
+ self.putBackToken(token);
+ stack.append(State { .Block = block }) %% unreachable;
+ %return stack.append(State { .Statement = block });
+ continue;
+ },
+ }
+ },
+
+ State.Statement => |block| {
+ {
+ // Look for comptime var, comptime const
+ const comptime_token = self.getNextToken();
+ if (comptime_token.id == Token.Id.Keyword_comptime) {
+ const mut_token = self.getNextToken();
+ if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
+ // TODO shouldn't need these casts
+ const var_decl = %return self.createAttachVarDecl(&block.statements, (?Token)(null),
+ mut_token, (?Token)(comptime_token), (?Token)(null));
+ %return stack.append(State { .VarDecl = var_decl });
+ continue;
+ }
+ self.putBackToken(mut_token);
+ }
+ self.putBackToken(comptime_token);
+ }
+ {
+ // Look for const, var
+ const mut_token = self.getNextToken();
+ if (mut_token.id == Token.Id.Keyword_var or mut_token.id == Token.Id.Keyword_const) {
+ // TODO shouldn't need these casts
+ const var_decl = %return self.createAttachVarDecl(&block.statements, (?Token)(null),
+ mut_token, (?Token)(null), (?Token)(null));
+ %return stack.append(State { .VarDecl = var_decl });
+ continue;
+ }
+ self.putBackToken(mut_token);
+ }
+
+ stack.append(State { .ExpectToken = Token.Id.Semicolon }) %% unreachable;
+ %return stack.append(State { .Expression = DestPtr{.List = &block.statements} });
+ continue;
+ },
+
+ // These are data, not control flow.
+ State.InfixOp => unreachable,
+ State.PrefixOp => unreachable,
+ State.Operand => unreachable,
+ }
+ @import("std").debug.panic("{}", @tagName(state));
+ //unreachable;
+ }
+ }
+
+ fn createRoot(self: &Parser) -> %&ast.NodeRoot {
+ const node = %return self.allocator.create(ast.NodeRoot);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeRoot {
+ .base = ast.Node {.id = ast.Node.Id.Root},
+ .decls = ArrayList(&ast.Node).init(self.allocator),
+ };
+ return node;
+ }
+
+ fn createVarDecl(self: &Parser, visib_token: &const ?Token, mut_token: &const Token, comptime_token: &const ?Token,
+ extern_token: &const ?Token) -> %&ast.NodeVarDecl
+ {
+ const node = %return self.allocator.create(ast.NodeVarDecl);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeVarDecl {
+ .base = ast.Node {.id = ast.Node.Id.VarDecl},
+ .visib_token = *visib_token,
+ .mut_token = *mut_token,
+ .comptime_token = *comptime_token,
+ .extern_token = *extern_token,
+ .type_node = null,
+ .align_node = null,
+ .init_node = null,
+ .lib_name = null,
+ // initialized later
+ .name_token = undefined,
+ .eq_token = undefined,
+ };
+ return node;
+ }
+
+ fn createFnProto(self: &Parser, fn_token: &const Token, extern_token: &const ?Token,
+ cc_token: &const ?Token, visib_token: &const ?Token, inline_token: &const ?Token) -> %&ast.NodeFnProto
+ {
+ const node = %return self.allocator.create(ast.NodeFnProto);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeFnProto {
+ .base = ast.Node {.id = ast.Node.Id.FnProto},
+ .visib_token = *visib_token,
+ .name_token = null,
+ .fn_token = *fn_token,
+ .params = ArrayList(&ast.Node).init(self.allocator),
+ .return_type = null,
+ .var_args_token = null,
+ .extern_token = *extern_token,
+ .inline_token = *inline_token,
+ .cc_token = *cc_token,
+ .body_node = null,
+ .lib_name = null,
+ .align_expr = null,
+ };
+ return node;
+ }
+
+ fn createParamDecl(self: &Parser) -> %&ast.NodeParamDecl {
+ const node = %return self.allocator.create(ast.NodeParamDecl);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeParamDecl {
+ .base = ast.Node {.id = ast.Node.Id.ParamDecl},
+ .comptime_token = null,
+ .noalias_token = null,
+ .name_token = null,
+ .type_node = undefined,
+ .var_args_token = null,
+ };
+ return node;
+ }
+
+ fn createBlock(self: &Parser, begin_token: &const Token) -> %&ast.NodeBlock {
+ const node = %return self.allocator.create(ast.NodeBlock);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeBlock {
+ .base = ast.Node {.id = ast.Node.Id.Block},
+ .begin_token = *begin_token,
+ .end_token = undefined,
+ .statements = ArrayList(&ast.Node).init(self.allocator),
+ };
+ return node;
+ }
+
+ fn createInfixOp(self: &Parser, op_token: &const Token, op: &const ast.NodeInfixOp.InfixOp) -> %&ast.NodeInfixOp {
+ const node = %return self.allocator.create(ast.NodeInfixOp);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeInfixOp {
+ .base = ast.Node {.id = ast.Node.Id.InfixOp},
+ .op_token = *op_token,
+ .lhs = undefined,
+ .op = *op,
+ .rhs = undefined,
+ };
+ return node;
+ }
+
+ fn createPrefixOp(self: &Parser, op_token: &const Token, op: &const ast.NodePrefixOp.PrefixOp) -> %&ast.NodePrefixOp {
+ const node = %return self.allocator.create(ast.NodePrefixOp);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodePrefixOp {
+ .base = ast.Node {.id = ast.Node.Id.PrefixOp},
+ .op_token = *op_token,
+ .op = *op,
+ .rhs = undefined,
+ };
+ return node;
+ }
+
+ fn createIdentifier(self: &Parser, name_token: &const Token) -> %&ast.NodeIdentifier {
+ const node = %return self.allocator.create(ast.NodeIdentifier);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeIdentifier {
+ .base = ast.Node {.id = ast.Node.Id.Identifier},
+ .name_token = *name_token,
+ };
+ return node;
+ }
+
+ fn createIntegerLiteral(self: &Parser, token: &const Token) -> %&ast.NodeIntegerLiteral {
+ const node = %return self.allocator.create(ast.NodeIntegerLiteral);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeIntegerLiteral {
+ .base = ast.Node {.id = ast.Node.Id.IntegerLiteral},
+ .token = *token,
+ };
+ return node;
+ }
+
+ fn createFloatLiteral(self: &Parser, token: &const Token) -> %&ast.NodeFloatLiteral {
+ const node = %return self.allocator.create(ast.NodeFloatLiteral);
+ %defer self.allocator.destroy(node);
+
+ *node = ast.NodeFloatLiteral {
+ .base = ast.Node {.id = ast.Node.Id.FloatLiteral},
+ .token = *token,
+ };
+ return node;
+ }
+
+ fn createAttachIdentifier(self: &Parser, dest_ptr: &const DestPtr, name_token: &const Token) -> %&ast.NodeIdentifier {
+ const node = %return self.createIdentifier(name_token);
+ %defer self.allocator.destroy(node);
+ %return dest_ptr.store(&node.base);
+ return node;
+ }
+
+ fn createAttachParamDecl(self: &Parser, list: &ArrayList(&ast.Node)) -> %&ast.NodeParamDecl {
+ const node = %return self.createParamDecl();
+ %defer self.allocator.destroy(node);
+ %return list.append(&node.base);
+ return node;
+ }
+
+ fn createAttachFnProto(self: &Parser, list: &ArrayList(&ast.Node), fn_token: &const Token,
+ extern_token: &const ?Token, cc_token: &const ?Token, visib_token: &const ?Token,
+ inline_token: &const ?Token) -> %&ast.NodeFnProto
+ {
+ const node = %return self.createFnProto(fn_token, extern_token, cc_token, visib_token, inline_token);
+ %defer self.allocator.destroy(node);
+ %return list.append(&node.base);
+ return node;
+ }
+
+ fn createAttachVarDecl(self: &Parser, list: &ArrayList(&ast.Node), visib_token: &const ?Token,
+ mut_token: &const Token, comptime_token: &const ?Token, extern_token: &const ?Token) -> %&ast.NodeVarDecl
+ {
+ const node = %return self.createVarDecl(visib_token, mut_token, comptime_token, extern_token);
+ %defer self.allocator.destroy(node);
+ %return list.append(&node.base);
+ return node;
+ }
+
+ fn parseError(self: &Parser, token: &const Token, comptime fmt: []const u8, args: ...) -> error {
+ const loc = self.tokenizer.getTokenLocation(token);
+ warn("{}:{}:{}: error: " ++ fmt ++ "\n", self.source_file_name, loc.line + 1, loc.column + 1, args);
+ warn("{}\n", self.tokenizer.buffer[loc.line_start..loc.line_end]);
+ {
+ var i: usize = 0;
+ while (i < loc.column) : (i += 1) {
+ warn(" ");
+ }
+ }
+ {
+ const caret_count = token.end - token.start;
+ var i: usize = 0;
+ while (i < caret_count) : (i += 1) {
+ warn("~");
+ }
+ }
+ warn("\n");
+ return error.ParseError;
+ }
+
+ fn expectToken(self: &Parser, token: &const Token, id: @TagType(Token.Id)) -> %void {
+ if (token.id != id) {
+ return self.parseError(token, "expected {}, found {}", @tagName(id), @tagName(token.id));
+ }
+ }
+
+ fn eatToken(self: &Parser, id: @TagType(Token.Id)) -> %Token {
+ const token = self.getNextToken();
+ %return self.expectToken(token, id);
+ return token;
+ }
+
+ fn putBackToken(self: &Parser, token: &const Token) {
+ self.put_back_tokens[self.put_back_count] = *token;
+ self.put_back_count += 1;
+ }
+
+ fn getNextToken(self: &Parser) -> Token {
+ if (self.put_back_count != 0) {
+ const put_back_index = self.put_back_count - 1;
+ const put_back_token = self.put_back_tokens[put_back_index];
+ self.put_back_count = put_back_index;
+ return put_back_token;
+ } else {
+ return self.tokenizer.next();
+ }
+ }
+
+ const RenderAstFrame = struct {
+ node: &ast.Node,
+ indent: usize,
+ };
+
+ pub fn renderAst(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) -> %void {
+ var stack = self.initUtilityArrayList(RenderAstFrame);
+ defer self.deinitUtilityArrayList(stack);
+
+ %return stack.append(RenderAstFrame {
+ .node = &root_node.base,
+ .indent = 0,
+ });
+
+ while (stack.popOrNull()) |frame| {
+ {
+ var i: usize = 0;
+ while (i < frame.indent) : (i += 1) {
+ %return stream.print(" ");
+ }
+ }
+ %return stream.print("{}\n", @tagName(frame.node.id));
+ var child_i: usize = 0;
+ while (frame.node.iterate(child_i)) |child| : (child_i += 1) {
+ %return stack.append(RenderAstFrame {
+ .node = child,
+ .indent = frame.indent + 2,
+ });
+ }
+ }
+ }
+
+ const RenderState = union(enum) {
+ TopLevelDecl: &ast.Node,
+ FnProtoRParen: &ast.NodeFnProto,
+ ParamDecl: &ast.Node,
+ Text: []const u8,
+ Expression: &ast.Node,
+ VarDecl: &ast.NodeVarDecl,
+ Statement: &ast.Node,
+ PrintIndent,
+ Indent: usize,
+ };
+
+ pub fn renderSource(self: &Parser, stream: &std.io.OutStream, root_node: &ast.NodeRoot) -> %void {
+ var stack = self.initUtilityArrayList(RenderState);
+ defer self.deinitUtilityArrayList(stack);
+
+ {
+ var i = root_node.decls.len;
+ while (i != 0) {
+ i -= 1;
+ const decl = root_node.decls.items[i];
+ %return stack.append(RenderState {.TopLevelDecl = decl});
+ }
+ }
+
+ const indent_delta = 4;
+ var indent: usize = 0;
+ while (stack.popOrNull()) |state| {
+ switch (state) {
+ RenderState.TopLevelDecl => |decl| {
+ switch (decl.id) {
+ ast.Node.Id.FnProto => {
+ const fn_proto = @fieldParentPtr(ast.NodeFnProto, "base", decl);
+ if (fn_proto.visib_token) |visib_token| {
+ switch (visib_token.id) {
+ Token.Id.Keyword_pub => %return stream.print("pub "),
+ Token.Id.Keyword_export => %return stream.print("export "),
+ else => unreachable,
+ }
+ }
+ if (fn_proto.extern_token) |extern_token| {
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
+ }
+ %return stream.print("fn");
+
+ if (fn_proto.name_token) |name_token| {
+ %return stream.print(" {}", self.tokenizer.getTokenSlice(name_token));
+ }
+
+ %return stream.print("(");
+
+ %return stack.append(RenderState { .Text = "\n" });
+ if (fn_proto.body_node == null) {
+ %return stack.append(RenderState { .Text = ";" });
+ }
+
+ %return stack.append(RenderState { .FnProtoRParen = fn_proto});
+ var i = fn_proto.params.len;
+ while (i != 0) {
+ i -= 1;
+ const param_decl_node = fn_proto.params.items[i];
+ %return stack.append(RenderState { .ParamDecl = param_decl_node});
+ if (i != 0) {
+ %return stack.append(RenderState { .Text = ", " });
+ }
+ }
+ },
+ ast.Node.Id.VarDecl => {
+ const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", decl);
+ %return stack.append(RenderState { .Text = "\n"});
+ %return stack.append(RenderState { .VarDecl = var_decl});
+
+ },
+ else => unreachable,
+ }
+ },
+
+ RenderState.VarDecl => |var_decl| {
+ if (var_decl.visib_token) |visib_token| {
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(visib_token));
+ }
+ if (var_decl.extern_token) |extern_token| {
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(extern_token));
+ if (var_decl.lib_name != null) {
+ @panic("TODO");
+ }
+ }
+ if (var_decl.comptime_token) |comptime_token| {
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
+ }
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(var_decl.mut_token));
+ %return stream.print("{}", self.tokenizer.getTokenSlice(var_decl.name_token));
+
+ %return stack.append(RenderState { .Text = ";" });
+ if (var_decl.init_node) |init_node| {
+ %return stack.append(RenderState { .Expression = init_node });
+ %return stack.append(RenderState { .Text = " = " });
+ }
+ if (var_decl.align_node) |align_node| {
+ %return stack.append(RenderState { .Text = ")" });
+ %return stack.append(RenderState { .Expression = align_node });
+ %return stack.append(RenderState { .Text = " align(" });
+ }
+ if (var_decl.type_node) |type_node| {
+ %return stream.print(": ");
+ %return stack.append(RenderState { .Expression = type_node });
+ }
+ },
+
+ RenderState.ParamDecl => |base| {
+ const param_decl = @fieldParentPtr(ast.NodeParamDecl, "base", base);
+ if (param_decl.comptime_token) |comptime_token| {
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(comptime_token));
+ }
+ if (param_decl.noalias_token) |noalias_token| {
+ %return stream.print("{} ", self.tokenizer.getTokenSlice(noalias_token));
+ }
+ if (param_decl.name_token) |name_token| {
+ %return stream.print("{}: ", self.tokenizer.getTokenSlice(name_token));
+ }
+ if (param_decl.var_args_token) |var_args_token| {
+ %return stream.print("{}", self.tokenizer.getTokenSlice(var_args_token));
+ } else {
+ %return stack.append(RenderState { .Expression = param_decl.type_node});
+ }
+ },
+ RenderState.Text => |bytes| {
+ %return stream.write(bytes);
+ },
+ RenderState.Expression => |base| switch (base.id) {
+ ast.Node.Id.Identifier => {
+ const identifier = @fieldParentPtr(ast.NodeIdentifier, "base", base);
+ %return stream.print("{}", self.tokenizer.getTokenSlice(identifier.name_token));
+ },
+ ast.Node.Id.Block => {
+ const block = @fieldParentPtr(ast.NodeBlock, "base", base);
+ %return stream.write("{");
+ %return stack.append(RenderState { .Text = "}"});
+ %return stack.append(RenderState.PrintIndent);
+ %return stack.append(RenderState { .Indent = indent});
+ %return stack.append(RenderState { .Text = "\n"});
+ var i = block.statements.len;
+ while (i != 0) {
+ i -= 1;
+ const statement_node = block.statements.items[i];
+ %return stack.append(RenderState { .Statement = statement_node});
+ %return stack.append(RenderState.PrintIndent);
+ %return stack.append(RenderState { .Indent = indent + indent_delta});
+ %return stack.append(RenderState { .Text = "\n" });
+ }
+ },
+ ast.Node.Id.InfixOp => {
+ const prefix_op_node = @fieldParentPtr(ast.NodeInfixOp, "base", base);
+ %return stack.append(RenderState { .Expression = prefix_op_node.rhs });
+ switch (prefix_op_node.op) {
+ ast.NodeInfixOp.InfixOp.EqualEqual => {
+ %return stack.append(RenderState { .Text = " == "});
+ },
+ ast.NodeInfixOp.InfixOp.BangEqual => {
+ %return stack.append(RenderState { .Text = " != "});
+ },
+ else => unreachable,
+ }
+ %return stack.append(RenderState { .Expression = prefix_op_node.lhs });
+ },
+ ast.Node.Id.PrefixOp => {
+ const prefix_op_node = @fieldParentPtr(ast.NodePrefixOp, "base", base);
+ %return stack.append(RenderState { .Expression = prefix_op_node.rhs });
+ switch (prefix_op_node.op) {
+ ast.NodePrefixOp.PrefixOp.Return => {
+ %return stream.write("return ");
+ },
+ ast.NodePrefixOp.PrefixOp.AddrOf => |addr_of_info| {
+ %return stream.write("&");
+ if (addr_of_info.volatile_token != null) {
+ %return stack.append(RenderState { .Text = "volatile "});
+ }
+ if (addr_of_info.const_token != null) {
+ %return stack.append(RenderState { .Text = "const "});
+ }
+ if (addr_of_info.align_expr) |align_expr| {
+ %return stream.print("align(");
+ %return stack.append(RenderState { .Text = ") "});
+ %return stack.append(RenderState { .Expression = align_expr});
+ }
+ },
+ else => unreachable,
+ }
+ },
+ ast.Node.Id.IntegerLiteral => {
+ const integer_literal = @fieldParentPtr(ast.NodeIntegerLiteral, "base", base);
+ %return stream.print("{}", self.tokenizer.getTokenSlice(integer_literal.token));
+ },
+ ast.Node.Id.FloatLiteral => {
+ const float_literal = @fieldParentPtr(ast.NodeFloatLiteral, "base", base);
+ %return stream.print("{}", self.tokenizer.getTokenSlice(float_literal.token));
+ },
+ else => unreachable,
+ },
+ RenderState.FnProtoRParen => |fn_proto| {
+ %return stream.print(")");
+ if (fn_proto.align_expr != null) {
+ @panic("TODO");
+ }
+ if (fn_proto.return_type) |return_type| {
+ %return stream.print(" -> ");
+ if (fn_proto.body_node) |body_node| {
+ %return stack.append(RenderState { .Expression = body_node});
+ %return stack.append(RenderState { .Text = " "});
+ }
+ %return stack.append(RenderState { .Expression = return_type});
+ }
+ },
+ RenderState.Statement => |base| {
+ switch (base.id) {
+ ast.Node.Id.VarDecl => {
+ const var_decl = @fieldParentPtr(ast.NodeVarDecl, "base", base);
+ %return stack.append(RenderState { .VarDecl = var_decl});
+ },
+ else => {
+ %return stack.append(RenderState { .Text = ";"});
+ %return stack.append(RenderState { .Expression = base});
+ },
+ }
+ },
+ RenderState.Indent => |new_indent| indent = new_indent,
+ RenderState.PrintIndent => %return stream.writeByteNTimes(' ', indent),
+ }
+ }
+ }
+
+ fn initUtilityArrayList(self: &Parser, comptime T: type) -> ArrayList(T) {
+ const new_byte_count = self.utility_bytes.len - self.utility_bytes.len % @sizeOf(T);
+ self.utility_bytes = self.allocator.alignedShrink(u8, utility_bytes_align, self.utility_bytes, new_byte_count);
+ const typed_slice = ([]T)(self.utility_bytes);
+ return ArrayList(T) {
+ .allocator = self.allocator,
+ .items = typed_slice,
+ .len = 0,
+ };
+ }
+
+ fn deinitUtilityArrayList(self: &Parser, list: var) {
+ self.utility_bytes = ([]align(utility_bytes_align) u8)(list.items);
+ }
+
+};
+
+var fixed_buffer_mem: [100 * 1024]u8 = undefined;
+
+fn testParse(source: []const u8, allocator: &mem.Allocator) -> %[]u8 {
+ var tokenizer = Tokenizer.init(source);
+ var parser = Parser.init(&tokenizer, allocator, "(memory buffer)");
+ defer parser.deinit();
+
+ const root_node = %return parser.parse();
+ defer parser.freeAst(root_node);
+
+ var buffer = %return std.Buffer.initSize(allocator, 0);
+ var buffer_out_stream = io.BufferOutStream.init(&buffer);
+ %return parser.renderSource(&buffer_out_stream.stream, root_node);
+ return buffer.toOwnedSlice();
+}
+
+// TODO test for memory leaks
+// TODO test for valid frees
+fn testCanonical(source: []const u8) {
+ const needed_alloc_count = x: {
+ // Try it once with unlimited memory, make sure it works
+ var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, @maxValue(usize));
+ const result_source = testParse(source, &failing_allocator.allocator) %% @panic("test failed");
+ if (!mem.eql(u8, result_source, source)) {
+ warn("\n====== expected this output: =========\n");
+ warn("{}", source);
+ warn("\n======== instead found this: =========\n");
+ warn("{}", result_source);
+ warn("\n======================================\n");
+ @panic("test failed");
+ }
+ failing_allocator.allocator.free(result_source);
+ break :x failing_allocator.index;
+ };
+
+ // TODO make this pass
+ //var fail_index = needed_alloc_count;
+ //while (fail_index != 0) {
+ // fail_index -= 1;
+ // var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ // var failing_allocator = std.debug.FailingAllocator.init(&fixed_allocator.allocator, fail_index);
+ // if (testParse(source, &failing_allocator.allocator)) |_| {
+ // @panic("non-deterministic memory usage");
+ // } else |err| {
+ // assert(err == error.OutOfMemory);
+ // }
+ //}
+}
+
+test "zig fmt" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // TODO get this test passing
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+
+ testCanonical(
+ \\extern fn puts(s: &const u8) -> c_int;
+ \\
+ );
+
+ testCanonical(
+ \\const a = b;
+ \\pub const a = b;
+ \\var a = b;
+ \\pub var a = b;
+ \\const a: i32 = b;
+ \\pub const a: i32 = b;
+ \\var a: i32 = b;
+ \\pub var a: i32 = b;
+ \\
+ );
+
+ testCanonical(
+ \\extern var foo: c_int;
+ \\
+ );
+
+ testCanonical(
+ \\var foo: c_int align(1);
+ \\
+ );
+
+ testCanonical(
+ \\fn main(argc: c_int, argv: &&u8) -> c_int {
+ \\ const a = b;
+ \\}
+ \\
+ );
+
+ testCanonical(
+ \\fn foo(argc: c_int, argv: &&u8) -> c_int {
+ \\ return 0;
+ \\}
+ \\
+ );
+
+ testCanonical(
+ \\extern fn f1(s: &align(&u8) u8) -> c_int;
+ \\
+ );
+
+ testCanonical(
+ \\extern fn f1(s: &&align(1) &const &volatile u8) -> c_int;
+ \\extern fn f2(s: &align(1) const &align(1) volatile &const volatile u8) -> c_int;
+ \\extern fn f3(s: &align(1) const volatile u8) -> c_int;
+ \\
+ );
+
+ testCanonical(
+ \\fn f1(a: bool, b: bool) -> bool {
+ \\ a != b;
+ \\ return a == b;
+ \\}
+ \\
+ );
+}
diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig
new file mode 100644
index 0000000000..0d077690dd
--- /dev/null
+++ b/src-self-hosted/target.zig
@@ -0,0 +1,60 @@
+const builtin = @import("builtin");
+const c = @import("c.zig");
+
+pub const CrossTarget = struct {
+ arch: builtin.Arch,
+ os: builtin.Os,
+ environ: builtin.Environ,
+};
+
+pub const Target = union(enum) {
+ Native,
+ Cross: CrossTarget,
+
+ pub fn oFileExt(self: &const Target) -> []const u8 {
+ const environ = switch (*self) {
+ Target.Native => builtin.environ,
+ Target.Cross => |t| t.environ,
+ };
+ return switch (environ) {
+ builtin.Environ.msvc => ".obj",
+ else => ".o",
+ };
+ }
+
+ pub fn exeFileExt(self: &const Target) -> []const u8 {
+ return switch (self.getOs()) {
+ builtin.Os.windows => ".exe",
+ else => "",
+ };
+ }
+
+ pub fn getOs(self: &const Target) -> builtin.Os {
+ return switch (*self) {
+ Target.Native => builtin.os,
+ Target.Cross => |t| t.os,
+ };
+ }
+
+ pub fn isDarwin(self: &const Target) -> bool {
+ return switch (self.getOs()) {
+ builtin.Os.darwin, builtin.Os.ios, builtin.Os.macosx => true,
+ else => false,
+ };
+ }
+
+ pub fn isWindows(self: &const Target) -> bool {
+ return switch (self.getOs()) {
+ builtin.Os.windows => true,
+ else => false,
+ };
+ }
+};
+
+pub fn initializeAll() {
+ c.LLVMInitializeAllTargets();
+ c.LLVMInitializeAllTargetInfos();
+ c.LLVMInitializeAllTargetMCs();
+ c.LLVMInitializeAllAsmPrinters();
+ c.LLVMInitializeAllAsmParsers();
+}
diff --git a/src-self-hosted/tokenizer.zig b/src-self-hosted/tokenizer.zig
new file mode 100644
index 0000000000..570d41fa7e
--- /dev/null
+++ b/src-self-hosted/tokenizer.zig
@@ -0,0 +1,509 @@
+const std = @import("std");
+const mem = std.mem;
+
+pub const Token = struct {
+ id: Id,
+ start: usize,
+ end: usize,
+
+ const KeywordId = struct {
+ bytes: []const u8,
+ id: Id,
+ };
+
+ const keywords = []KeywordId {
+ KeywordId{.bytes="align", .id = Id.Keyword_align},
+ KeywordId{.bytes="and", .id = Id.Keyword_and},
+ KeywordId{.bytes="asm", .id = Id.Keyword_asm},
+ KeywordId{.bytes="break", .id = Id.Keyword_break},
+ KeywordId{.bytes="coldcc", .id = Id.Keyword_coldcc},
+ KeywordId{.bytes="comptime", .id = Id.Keyword_comptime},
+ KeywordId{.bytes="const", .id = Id.Keyword_const},
+ KeywordId{.bytes="continue", .id = Id.Keyword_continue},
+ KeywordId{.bytes="defer", .id = Id.Keyword_defer},
+ KeywordId{.bytes="else", .id = Id.Keyword_else},
+ KeywordId{.bytes="enum", .id = Id.Keyword_enum},
+ KeywordId{.bytes="error", .id = Id.Keyword_error},
+ KeywordId{.bytes="export", .id = Id.Keyword_export},
+ KeywordId{.bytes="extern", .id = Id.Keyword_extern},
+ KeywordId{.bytes="false", .id = Id.Keyword_false},
+ KeywordId{.bytes="fn", .id = Id.Keyword_fn},
+ KeywordId{.bytes="for", .id = Id.Keyword_for},
+ KeywordId{.bytes="goto", .id = Id.Keyword_goto},
+ KeywordId{.bytes="if", .id = Id.Keyword_if},
+ KeywordId{.bytes="inline", .id = Id.Keyword_inline},
+ KeywordId{.bytes="nakedcc", .id = Id.Keyword_nakedcc},
+ KeywordId{.bytes="noalias", .id = Id.Keyword_noalias},
+ KeywordId{.bytes="null", .id = Id.Keyword_null},
+ KeywordId{.bytes="or", .id = Id.Keyword_or},
+ KeywordId{.bytes="packed", .id = Id.Keyword_packed},
+ KeywordId{.bytes="pub", .id = Id.Keyword_pub},
+ KeywordId{.bytes="return", .id = Id.Keyword_return},
+ KeywordId{.bytes="stdcallcc", .id = Id.Keyword_stdcallcc},
+ KeywordId{.bytes="struct", .id = Id.Keyword_struct},
+ KeywordId{.bytes="switch", .id = Id.Keyword_switch},
+ KeywordId{.bytes="test", .id = Id.Keyword_test},
+ KeywordId{.bytes="this", .id = Id.Keyword_this},
+ KeywordId{.bytes="true", .id = Id.Keyword_true},
+ KeywordId{.bytes="undefined", .id = Id.Keyword_undefined},
+ KeywordId{.bytes="union", .id = Id.Keyword_union},
+ KeywordId{.bytes="unreachable", .id = Id.Keyword_unreachable},
+ KeywordId{.bytes="use", .id = Id.Keyword_use},
+ KeywordId{.bytes="var", .id = Id.Keyword_var},
+ KeywordId{.bytes="volatile", .id = Id.Keyword_volatile},
+ KeywordId{.bytes="while", .id = Id.Keyword_while},
+ };
+
+ fn getKeyword(bytes: []const u8) -> ?Id {
+ for (keywords) |kw| {
+ if (mem.eql(u8, kw.bytes, bytes)) {
+ return kw.id;
+ }
+ }
+ return null;
+ }
+
+ const StrLitKind = enum {Normal, C};
+
+ pub const Id = union(enum) {
+ Invalid,
+ Identifier,
+ StringLiteral: StrLitKind,
+ Eof,
+ Builtin,
+ Bang,
+ Equal,
+ EqualEqual,
+ BangEqual,
+ LParen,
+ RParen,
+ Semicolon,
+ Percent,
+ LBrace,
+ RBrace,
+ Period,
+ Ellipsis2,
+ Ellipsis3,
+ Minus,
+ Arrow,
+ Colon,
+ Slash,
+ Comma,
+ Ampersand,
+ AmpersandEqual,
+ IntegerLiteral,
+ FloatLiteral,
+ Keyword_align,
+ Keyword_and,
+ Keyword_asm,
+ Keyword_break,
+ Keyword_coldcc,
+ Keyword_comptime,
+ Keyword_const,
+ Keyword_continue,
+ Keyword_defer,
+ Keyword_else,
+ Keyword_enum,
+ Keyword_error,
+ Keyword_export,
+ Keyword_extern,
+ Keyword_false,
+ Keyword_fn,
+ Keyword_for,
+ Keyword_goto,
+ Keyword_if,
+ Keyword_inline,
+ Keyword_nakedcc,
+ Keyword_noalias,
+ Keyword_null,
+ Keyword_or,
+ Keyword_packed,
+ Keyword_pub,
+ Keyword_return,
+ Keyword_stdcallcc,
+ Keyword_struct,
+ Keyword_switch,
+ Keyword_test,
+ Keyword_this,
+ Keyword_true,
+ Keyword_undefined,
+ Keyword_union,
+ Keyword_unreachable,
+ Keyword_use,
+ Keyword_var,
+ Keyword_volatile,
+ Keyword_while,
+ };
+};
+
+pub const Tokenizer = struct {
+ buffer: []const u8,
+ index: usize,
+
+ pub const Location = struct {
+ line: usize,
+ column: usize,
+ line_start: usize,
+ line_end: usize,
+ };
+
+ pub fn getTokenLocation(self: &Tokenizer, token: &const Token) -> Location {
+ var loc = Location {
+ .line = 0,
+ .column = 0,
+ .line_start = 0,
+ .line_end = 0,
+ };
+ for (self.buffer) |c, i| {
+ if (i == token.start) {
+ loc.line_end = i;
+ while (loc.line_end < self.buffer.len and self.buffer[loc.line_end] != '\n') : (loc.line_end += 1) {}
+ return loc;
+ }
+ if (c == '\n') {
+ loc.line += 1;
+ loc.column = 0;
+ loc.line_start = i + 1;
+ } else {
+ loc.column += 1;
+ }
+ }
+ return loc;
+ }
+
+ /// For debugging purposes
+ pub fn dump(self: &Tokenizer, token: &const Token) {
+ std.debug.warn("{} \"{}\"\n", @tagName(token.id), self.buffer[token.start..token.end]);
+ }
+
+ pub fn init(buffer: []const u8) -> Tokenizer {
+ return Tokenizer {
+ .buffer = buffer,
+ .index = 0,
+ };
+ }
+
+ const State = enum {
+ Start,
+ Identifier,
+ Builtin,
+ C,
+ StringLiteral,
+ StringLiteralBackslash,
+ Equal,
+ Bang,
+ Minus,
+ Slash,
+ LineComment,
+ Zero,
+ IntegerLiteral,
+ NumberDot,
+ FloatFraction,
+ FloatExponentUnsigned,
+ FloatExponentNumber,
+ Ampersand,
+ Period,
+ Period2,
+ };
+
+ pub fn next(self: &Tokenizer) -> Token {
+ var state = State.Start;
+ var result = Token {
+ .id = Token.Id.Eof,
+ .start = self.index,
+ .end = undefined,
+ };
+ while (self.index < self.buffer.len) : (self.index += 1) {
+ const c = self.buffer[self.index];
+ switch (state) {
+ State.Start => switch (c) {
+ ' ', '\n' => {
+ result.start = self.index + 1;
+ },
+ 'c' => {
+ state = State.C;
+ result.id = Token.Id.Identifier;
+ },
+ '"' => {
+ state = State.StringLiteral;
+ result.id = Token.Id { .StringLiteral = Token.StrLitKind.Normal };
+ },
+ 'a'...'b', 'd'...'z', 'A'...'Z', '_' => {
+ state = State.Identifier;
+ result.id = Token.Id.Identifier;
+ },
+ '@' => {
+ state = State.Builtin;
+ result.id = Token.Id.Builtin;
+ },
+ '=' => {
+ state = State.Equal;
+ },
+ '!' => {
+ state = State.Bang;
+ },
+ '(' => {
+ result.id = Token.Id.LParen;
+ self.index += 1;
+ break;
+ },
+ ')' => {
+ result.id = Token.Id.RParen;
+ self.index += 1;
+ break;
+ },
+ ';' => {
+ result.id = Token.Id.Semicolon;
+ self.index += 1;
+ break;
+ },
+ ',' => {
+ result.id = Token.Id.Comma;
+ self.index += 1;
+ break;
+ },
+ ':' => {
+ result.id = Token.Id.Colon;
+ self.index += 1;
+ break;
+ },
+ '%' => {
+ result.id = Token.Id.Percent;
+ self.index += 1;
+ break;
+ },
+ '{' => {
+ result.id = Token.Id.LBrace;
+ self.index += 1;
+ break;
+ },
+ '}' => {
+ result.id = Token.Id.RBrace;
+ self.index += 1;
+ break;
+ },
+ '.' => {
+ state = State.Period;
+ },
+ '-' => {
+ state = State.Minus;
+ },
+ '/' => {
+ state = State.Slash;
+ },
+ '&' => {
+ state = State.Ampersand;
+ },
+ '0' => {
+ state = State.Zero;
+ result.id = Token.Id.IntegerLiteral;
+ },
+ '1'...'9' => {
+ state = State.IntegerLiteral;
+ result.id = Token.Id.IntegerLiteral;
+ },
+ else => {
+ result.id = Token.Id.Invalid;
+ self.index += 1;
+ break;
+ },
+ },
+ State.Ampersand => switch (c) {
+ '=' => {
+ result.id = Token.Id.AmpersandEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Ampersand;
+ break;
+ },
+ },
+ State.Identifier => switch (c) {
+ 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
+ else => {
+ if (Token.getKeyword(self.buffer[result.start..self.index])) |id| {
+ result.id = id;
+ }
+ break;
+ },
+ },
+ State.Builtin => switch (c) {
+ 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
+ else => break,
+ },
+ State.C => switch (c) {
+ '\\' => @panic("TODO"),
+ '"' => {
+ state = State.StringLiteral;
+ result.id = Token.Id { .StringLiteral = Token.StrLitKind.C };
+ },
+ 'a'...'z', 'A'...'Z', '_', '0'...'9' => {
+ state = State.Identifier;
+ },
+ else => break,
+ },
+ State.StringLiteral => switch (c) {
+ '\\' => {
+ state = State.StringLiteralBackslash;
+ },
+ '"' => {
+ self.index += 1;
+ break;
+ },
+ '\n' => break, // Look for this error later.
+ else => {},
+ },
+
+ State.StringLiteralBackslash => switch (c) {
+ '\n' => break, // Look for this error later.
+ else => {
+ state = State.StringLiteral;
+ },
+ },
+
+ State.Bang => switch (c) {
+ '=' => {
+ result.id = Token.Id.BangEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Bang;
+ break;
+ },
+ },
+
+ State.Equal => switch (c) {
+ '=' => {
+ result.id = Token.Id.EqualEqual;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Equal;
+ break;
+ },
+ },
+
+ State.Minus => switch (c) {
+ '>' => {
+ result.id = Token.Id.Arrow;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Minus;
+ break;
+ },
+ },
+
+ State.Period => switch (c) {
+ '.' => {
+ state = State.Period2;
+ },
+ else => {
+ result.id = Token.Id.Period;
+ break;
+ },
+ },
+
+ State.Period2 => switch (c) {
+ '.' => {
+ result.id = Token.Id.Ellipsis3;
+ self.index += 1;
+ break;
+ },
+ else => {
+ result.id = Token.Id.Ellipsis2;
+ break;
+ },
+ },
+
+ State.Slash => switch (c) {
+ '/' => {
+ result.id = undefined;
+ state = State.LineComment;
+ },
+ else => {
+ result.id = Token.Id.Slash;
+ break;
+ },
+ },
+ State.LineComment => switch (c) {
+ '\n' => {
+ state = State.Start;
+ result = Token {
+ .id = Token.Id.Eof,
+ .start = self.index + 1,
+ .end = undefined,
+ };
+ },
+ else => {},
+ },
+ State.Zero => switch (c) {
+ 'b', 'o', 'x' => {
+ state = State.IntegerLiteral;
+ },
+ else => {
+ // reinterpret as a normal number
+ self.index -= 1;
+ state = State.IntegerLiteral;
+ },
+ },
+ State.IntegerLiteral => switch (c) {
+ '.' => {
+ state = State.NumberDot;
+ },
+ 'p', 'P', 'e', 'E' => {
+ state = State.FloatExponentUnsigned;
+ },
+ '0'...'9', 'a'...'f', 'A'...'F' => {},
+ else => break,
+ },
+ State.NumberDot => switch (c) {
+ '.' => {
+ self.index -= 1;
+ state = State.Start;
+ break;
+ },
+ else => {
+ self.index -= 1;
+ result.id = Token.Id.FloatLiteral;
+ state = State.FloatFraction;
+ },
+ },
+ State.FloatFraction => switch (c) {
+ 'p', 'P', 'e', 'E' => {
+ state = State.FloatExponentUnsigned;
+ },
+ '0'...'9', 'a'...'f', 'A'...'F' => {},
+ else => break,
+ },
+ State.FloatExponentUnsigned => switch (c) {
+ '+', '-' => {
+ state = State.FloatExponentNumber;
+ },
+ else => {
+ // reinterpret as a normal exponent number
+ self.index -= 1;
+ state = State.FloatExponentNumber;
+ }
+ },
+ State.FloatExponentNumber => switch (c) {
+ '0'...'9', 'a'...'f', 'A'...'F' => {},
+ else => break,
+ },
+ }
+ }
+ result.end = self.index;
+ // TODO check state when returning EOF
+ return result;
+ }
+
+ pub fn getTokenSlice(self: &const Tokenizer, token: &const Token) -> []const u8 {
+ return self.buffer[token.start..token.end];
+ }
+};
+
+
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 086ad7db49..a582f561cc 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -26,7 +26,6 @@ struct ScopeFnDef;
struct TypeTableEntry;
struct VariableTableEntry;
struct ErrorTableEntry;
-struct LabelTableEntry;
struct BuiltinFnEntry;
struct TypeStructField;
struct CodeGen;
@@ -37,6 +36,7 @@ struct IrBasicBlock;
struct ScopeDecls;
struct ZigWindowsSDK;
struct Tld;
+struct TldExport;
struct IrGotoItem {
AstNode *source_node;
@@ -53,7 +53,6 @@ struct IrExecutable {
size_t *backward_branch_count;
size_t backward_branch_quota;
bool invalid;
- ZigList all_labels;
ZigList goto_list;
bool is_inline;
FnTableEntry *fn_entry;
@@ -272,7 +271,6 @@ enum ReturnKnowledge {
enum VisibMod {
VisibModPrivate,
VisibModPub,
- VisibModExport,
};
enum GlobalLinkageId {
@@ -313,11 +311,8 @@ struct TldVar {
Tld base;
VariableTableEntry *var;
- AstNode *set_global_section_node;
- Buf *section_name;
- AstNode *set_global_linkage_node;
- GlobalLinkageId linkage;
Buf *extern_lib_name;
+ Buf *section_name;
};
struct TldFn {
@@ -389,8 +384,6 @@ enum NodeType {
NodeTypeSwitchExpr,
NodeTypeSwitchProng,
NodeTypeSwitchRange,
- NodeTypeLabel,
- NodeTypeGoto,
NodeTypeCompTime,
NodeTypeBreak,
NodeTypeContinue,
@@ -425,6 +418,7 @@ struct AstNodeFnProto {
AstNode *return_type;
bool is_var_args;
bool is_extern;
+ bool is_export;
bool is_inline;
CallingConvention cc;
AstNode *fn_def_node;
@@ -432,6 +426,8 @@ struct AstNodeFnProto {
Buf *lib_name;
// populated if the "align A" is present
AstNode *align_expr;
+ // populated if the "section(S)" is present
+ AstNode *section_expr;
};
struct AstNodeFnDef {
@@ -452,8 +448,8 @@ struct AstNodeParamDecl {
};
struct AstNodeBlock {
+ Buf *name;
ZigList statements;
- bool last_statement_is_result_expression;
};
enum ReturnKind {
@@ -480,15 +476,18 @@ struct AstNodeVariableDeclaration {
VisibMod visib_mod;
Buf *symbol;
bool is_const;
- bool is_inline;
+ bool is_comptime;
+ bool is_export;
bool is_extern;
// one or both of type and expr will be non null
AstNode *type;
AstNode *expr;
// populated if this is an extern declaration
Buf *lib_name;
- // populated if the "align A" is present
+ // populated if the "align(A)" is present
AstNode *align_expr;
+ // populated if the "section(S)" is present
+ AstNode *section_expr;
};
struct AstNodeErrorValueDecl {
@@ -659,6 +658,7 @@ struct AstNodeTestExpr {
};
struct AstNodeWhileExpr {
+ Buf *name;
AstNode *condition;
Buf *var_symbol;
bool var_is_ptr;
@@ -670,6 +670,7 @@ struct AstNodeWhileExpr {
};
struct AstNodeForExpr {
+ Buf *name;
AstNode *array_expr;
AstNode *elem_node; // always a symbol
AstNode *index_node; // always a symbol, might be null
@@ -701,11 +702,6 @@ struct AstNodeLabel {
Buf *name;
};
-struct AstNodeGoto {
- Buf *name;
- bool is_inline;
-};
-
struct AstNodeCompTime {
AstNode *expr;
};
@@ -833,11 +829,14 @@ struct AstNodeBoolLiteral {
};
struct AstNodeBreakExpr {
+ Buf *name;
AstNode *expr; // may be null
};
struct AstNodeContinueExpr {
+ Buf *name;
};
+
struct AstNodeUnreachableExpr {
};
@@ -883,7 +882,6 @@ struct AstNode {
AstNodeSwitchProng switch_prong;
AstNodeSwitchRange switch_range;
AstNodeLabel label;
- AstNodeGoto goto_expr;
AstNodeCompTime comptime_expr;
AstNodeAsmExpr asm_expr;
AstNodeFieldAccessExpr field_access_expr;
@@ -1177,6 +1175,11 @@ enum FnInline {
FnInlineNever,
};
+struct FnExport {
+ Buf name;
+ GlobalLinkageId linkage;
+};
+
struct FnTableEntry {
LLVMValueRef llvm_value;
const char *llvm_name;
@@ -1204,12 +1207,11 @@ struct FnTableEntry {
ZigList alloca_list;
ZigList variable_list;
- AstNode *set_global_section_node;
Buf *section_name;
- AstNode *set_global_linkage_node;
- GlobalLinkageId linkage;
AstNode *set_alignstack_node;
uint32_t alignstack_value;
+
+ ZigList export_list;
};
uint32_t fn_table_entry_hash(FnTableEntry*);
@@ -1258,8 +1260,6 @@ enum BuiltinFnId {
BuiltinFnIdSetFloatMode,
BuiltinFnIdTypeName,
BuiltinFnIdCanImplicitCast,
- BuiltinFnIdSetGlobalSection,
- BuiltinFnIdSetGlobalLinkage,
BuiltinFnIdPanic,
BuiltinFnIdPtrCast,
BuiltinFnIdBitCast,
@@ -1270,6 +1270,7 @@ enum BuiltinFnId {
BuiltinFnIdFieldParentPtr,
BuiltinFnIdOffsetOf,
BuiltinFnIdInlineCall,
+ BuiltinFnIdNoInlineCall,
BuiltinFnIdTypeId,
BuiltinFnIdShlExact,
BuiltinFnIdShrExact,
@@ -1278,6 +1279,7 @@ enum BuiltinFnId {
BuiltinFnIdOpaqueType,
BuiltinFnIdSetAlignStack,
BuiltinFnIdArgType,
+ BuiltinFnIdExport,
};
struct BuiltinFnEntry {
@@ -1424,7 +1426,7 @@ struct CodeGen {
HashMap generic_table;
HashMap memoized_fn_eval_table;
HashMap llvm_fn_table;
- HashMap exported_symbol_names;
+ HashMap exported_symbol_names;
HashMap external_prototypes;
@@ -1439,7 +1441,7 @@ struct CodeGen {
struct {
TypeTableEntry *entry_bool;
- TypeTableEntry *entry_int[2][11]; // [signed,unsigned][2,3,4,5,6,7,8,16,32,64,128]
+ TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128]
TypeTableEntry *entry_c_int[CIntTypeCount];
TypeTableEntry *entry_c_longdouble;
TypeTableEntry *entry_c_void;
@@ -1639,12 +1641,6 @@ struct ErrorTableEntry {
ConstExprValue *cached_error_name_val;
};
-struct LabelTableEntry {
- AstNode *decl_node;
- IrBasicBlock *bb;
- bool used;
-};
-
enum ScopeId {
ScopeIdDecls,
ScopeIdBlock,
@@ -1688,7 +1684,12 @@ struct ScopeDecls {
struct ScopeBlock {
Scope base;
- HashMap label_table;
+ Buf *name;
+ IrBasicBlock *end_block;
+ IrInstruction *is_comptime;
+ ZigList *incoming_values;
+ ZigList *incoming_blocks;
+
bool safety_off;
AstNode *safety_set_node;
bool fast_math_off;
@@ -1734,6 +1735,7 @@ struct ScopeCImport {
struct ScopeLoop {
Scope base;
+ Buf *name;
IrBasicBlock *break_block;
IrBasicBlock *continue_block;
IrInstruction *is_comptime;
@@ -1885,8 +1887,6 @@ enum IrInstructionId {
IrInstructionIdCheckStatementIsVoid,
IrInstructionIdTypeName,
IrInstructionIdCanImplicitCast,
- IrInstructionIdSetGlobalSection,
- IrInstructionIdSetGlobalLinkage,
IrInstructionIdDeclRef,
IrInstructionIdPanic,
IrInstructionIdTagName,
@@ -1900,6 +1900,7 @@ enum IrInstructionId {
IrInstructionIdOpaqueType,
IrInstructionIdSetAlignStack,
IrInstructionIdArgType,
+ IrInstructionIdExport,
};
struct IrInstruction {
@@ -2102,7 +2103,7 @@ struct IrInstructionCall {
IrInstruction **args;
bool is_comptime;
LLVMValueRef tmp_ptr;
- bool is_inline;
+ FnInline fn_inline;
};
struct IrInstructionConst {
@@ -2625,20 +2626,6 @@ struct IrInstructionCanImplicitCast {
IrInstruction *target_value;
};
-struct IrInstructionSetGlobalSection {
- IrInstruction base;
-
- Tld *tld;
- IrInstruction *value;
-};
-
-struct IrInstructionSetGlobalLinkage {
- IrInstruction base;
-
- Tld *tld;
- IrInstruction *value;
-};
-
struct IrInstructionDeclRef {
IrInstruction base;
@@ -2727,6 +2714,14 @@ struct IrInstructionArgType {
IrInstruction *arg_index;
};
+struct IrInstructionExport {
+ IrInstruction base;
+
+ IrInstruction *name;
+ IrInstruction *linkage;
+ IrInstruction *target;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 96f0eb44db..9ec4db824c 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -110,7 +110,7 @@ ScopeBlock *create_block_scope(AstNode *node, Scope *parent) {
assert(node->type == NodeTypeBlock);
ScopeBlock *scope = allocate(1);
init_scope(&scope->base, ScopeIdBlock, node, parent);
- scope->label_table.init(1);
+ scope->name = node->data.block.name;
return scope;
}
@@ -144,9 +144,15 @@ ScopeCImport *create_cimport_scope(AstNode *node, Scope *parent) {
}
ScopeLoop *create_loop_scope(AstNode *node, Scope *parent) {
- assert(node->type == NodeTypeWhileExpr || node->type == NodeTypeForExpr);
ScopeLoop *scope = allocate(1);
init_scope(&scope->base, ScopeIdLoop, node, parent);
+ if (node->type == NodeTypeWhileExpr) {
+ scope->name = node->data.while_expr.name;
+ } else if (node->type == NodeTypeForExpr) {
+ scope->name = node->data.for_expr.name;
+ } else {
+ zig_unreachable();
+ }
return scope;
}
@@ -429,7 +435,7 @@ TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) {
ensure_complete_type(g, child_type);
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdMaybe);
- assert(child_type->type_ref);
+ assert(child_type->type_ref || child_type->zero_bits);
assert(child_type->di_type);
entry->is_copyable = type_is_copyable(g, child_type);
@@ -1062,7 +1068,7 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
if (fn_proto->cc == CallingConventionUnspecified) {
- bool extern_abi = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport);
+ bool extern_abi = fn_proto->is_extern || fn_proto->is_export;
fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified;
} else {
fn_type_id->cc = fn_proto->cc;
@@ -1093,6 +1099,38 @@ static bool analyze_const_align(CodeGen *g, Scope *scope, AstNode *node, uint32_
return true;
}
+static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf **out_buffer) {
+ TypeTableEntry *ptr_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
+ TypeTableEntry *str_type = get_slice_type(g, ptr_type);
+ IrInstruction *instr = analyze_const_value(g, scope, node, str_type, nullptr);
+ if (type_is_invalid(instr->value.type))
+ return false;
+
+ ConstExprValue *ptr_field = &instr->value.data.x_struct.fields[slice_ptr_index];
+ ConstExprValue *len_field = &instr->value.data.x_struct.fields[slice_len_index];
+
+ assert(ptr_field->data.x_ptr.special == ConstPtrSpecialBaseArray);
+ ConstExprValue *array_val = ptr_field->data.x_ptr.data.base_array.array_val;
+ expand_undef_array(g, array_val);
+ size_t len = bigint_as_unsigned(&len_field->data.x_bigint);
+ Buf *result = buf_alloc();
+ buf_resize(result, len);
+ for (size_t i = 0; i < len; i += 1) {
+ size_t new_index = ptr_field->data.x_ptr.data.base_array.elem_index + i;
+ ConstExprValue *char_val = &array_val->data.x_array.s_none.elements[new_index];
+ if (char_val->special == ConstValSpecialUndef) {
+ add_node_error(g, node, buf_sprintf("use of undefined value"));
+ return false;
+ }
+ uint64_t big_c = bigint_as_unsigned(&char_val->data.x_bigint);
+ assert(big_c <= UINT8_MAX);
+ uint8_t c = (uint8_t)big_c;
+ buf_ptr(result)[i] = c;
+ }
+ *out_buffer = result;
+ return true;
+}
+
static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_scope) {
assert(proto_node->type == NodeTypeFnProto);
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
@@ -1130,6 +1168,15 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c
}
TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type);
+ if (fn_type_id.cc != CallingConventionUnspecified) {
+ type_ensure_zero_bits_known(g, type_entry);
+ if (!type_has_bits(type_entry)) {
+ add_node_error(g, param_node->data.param_decl.type,
+ buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
+ buf_ptr(&type_entry->name), calling_convention_name(fn_type_id.cc)));
+ return g->builtin_types.entry_invalid;
+ }
+ }
switch (type_entry->id) {
case TypeTableEntryIdInvalid:
@@ -2227,7 +2274,7 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
tag_type = new_type_table_entry(TypeTableEntryIdEnum);
buf_resize(&tag_type->name, 0);
- buf_appendf(&tag_type->name, "@EnumTagType(%s)", buf_ptr(&union_type->name));
+ buf_appendf(&tag_type->name, "@TagType(%s)", buf_ptr(&union_type->name));
tag_type->is_copyable = true;
tag_type->type_ref = tag_int_type->type_ref;
tag_type->zero_bits = tag_int_type->zero_bits;
@@ -2244,12 +2291,10 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) {
TypeTableEntry *enum_type = analyze_type_expr(g, scope, enum_type_node);
if (type_is_invalid(enum_type)) {
union_type->data.unionation.is_invalid = true;
- union_type->data.unionation.embedded_in_current = false;
return;
}
if (enum_type->id != TypeTableEntryIdEnum) {
union_type->data.unionation.is_invalid = true;
- union_type->data.unionation.embedded_in_current = false;
add_node_error(g, enum_type_node,
buf_sprintf("expected enum tag type, found '%s'", buf_ptr(&enum_type->name)));
return;
@@ -2474,7 +2519,7 @@ static void get_fully_qualified_decl_name(Buf *buf, Tld *tld, uint8_t sep) {
buf_append_buf(buf, tld->name);
}
-FnTableEntry *create_fn_raw(FnInline inline_value, GlobalLinkageId linkage) {
+FnTableEntry *create_fn_raw(FnInline inline_value) {
FnTableEntry *fn_entry = allocate(1);
fn_entry->analyzed_executable.backward_branch_count = &fn_entry->prealloc_bbc;
@@ -2482,7 +2527,6 @@ FnTableEntry *create_fn_raw(FnInline inline_value, GlobalLinkageId linkage) {
fn_entry->analyzed_executable.fn_entry = fn_entry;
fn_entry->ir_executable.fn_entry = fn_entry;
fn_entry->fn_inline = inline_value;
- fn_entry->linkage = linkage;
return fn_entry;
}
@@ -2492,9 +2536,7 @@ FnTableEntry *create_fn(AstNode *proto_node) {
AstNodeFnProto *fn_proto = &proto_node->data.fn_proto;
FnInline inline_value = fn_proto->is_inline ? FnInlineAlways : FnInlineAuto;
- GlobalLinkageId linkage = (fn_proto->visib_mod == VisibModExport || proto_node->data.fn_proto.is_extern) ?
- GlobalLinkageIdStrong : GlobalLinkageIdInternal;
- FnTableEntry *fn_entry = create_fn_raw(inline_value, linkage);
+ FnTableEntry *fn_entry = create_fn_raw(inline_value);
fn_entry->proto_node = proto_node;
fn_entry->body_node = (proto_node->data.fn_proto.fn_def_node == nullptr) ? nullptr :
@@ -2550,6 +2592,34 @@ TypeTableEntry *get_test_fn_type(CodeGen *g) {
return g->test_fn_type;
}
+void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc) {
+ if (ccc) {
+ if (buf_eql_str(symbol_name, "main") && g->libc_link_lib != nullptr) {
+ g->have_c_main = true;
+ g->windows_subsystem_windows = false;
+ g->windows_subsystem_console = true;
+ } else if (buf_eql_str(symbol_name, "WinMain") &&
+ g->zig_target.os == ZigLLVM_Win32)
+ {
+ g->have_winmain = true;
+ g->windows_subsystem_windows = true;
+ g->windows_subsystem_console = false;
+ } else if (buf_eql_str(symbol_name, "WinMainCRTStartup") &&
+ g->zig_target.os == ZigLLVM_Win32)
+ {
+ g->have_winmain_crt_startup = true;
+ } else if (buf_eql_str(symbol_name, "DllMainCRTStartup") &&
+ g->zig_target.os == ZigLLVM_Win32)
+ {
+ g->have_dllmain_crt_startup = true;
+ }
+ }
+ FnExport *fn_export = fn_table_entry->export_list.add_one();
+ memset(fn_export, 0, sizeof(FnExport));
+ buf_init_from_buf(&fn_export->name, symbol_name);
+ fn_export->linkage = linkage;
+}
+
static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
ImportTableEntry *import = tld_fn->base.import;
AstNode *source_node = tld_fn->base.source_node;
@@ -2561,6 +2631,11 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
FnTableEntry *fn_table_entry = create_fn(source_node);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
+ if (fn_proto->is_export) {
+ bool ccc = (fn_proto->cc == CallingConventionUnspecified || fn_proto->cc == CallingConventionC);
+ add_fn_export(g, fn_table_entry, &fn_table_entry->symbol_name, GlobalLinkageIdStrong, ccc);
+ }
+
tld_fn->fn_entry = fn_table_entry;
if (fn_table_entry->body_node) {
@@ -2574,7 +2649,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
add_node_error(g, param_node, buf_sprintf("missing parameter name"));
}
}
- } else if (fn_table_entry->linkage != GlobalLinkageIdInternal) {
+ } else {
g->external_prototypes.put_unique(tld_fn->base.name, &tld_fn->base);
}
@@ -2582,6 +2657,15 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
fn_table_entry->type_entry = analyze_fn_type(g, source_node, child_scope);
+ if (fn_proto->section_expr != nullptr) {
+ if (fn_table_entry->body_node == nullptr) {
+ add_node_error(g, fn_proto->section_expr,
+ buf_sprintf("cannot set section of external function '%s'", buf_ptr(&fn_table_entry->symbol_name)));
+ } else {
+ analyze_const_string(g, child_scope, fn_proto->section_expr, &fn_table_entry->section_name);
+ }
+ }
+
if (fn_table_entry->type_entry->id == TypeTableEntryIdInvalid) {
tld_fn->base.resolution = TldResolutionInvalid;
return;
@@ -2596,15 +2680,12 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
{
if (g->have_pub_main && buf_eql_str(&fn_table_entry->symbol_name, "main")) {
g->main_fn = fn_table_entry;
-
- if (tld_fn->base.visib_mod != VisibModExport) {
- TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
- TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
- if (actual_return_type != err_void) {
- add_node_error(g, fn_proto->return_type,
- buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
- buf_ptr(&actual_return_type->name)));
- }
+ TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void);
+ TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
+ if (actual_return_type != err_void) {
+ add_node_error(g, fn_proto->return_type,
+ buf_sprintf("expected return type of main to be '%%void', instead is '%s'",
+ buf_ptr(&actual_return_type->name)));
}
} else if ((import->package == g->panic_package || g->have_pub_panic) &&
buf_eql_str(&fn_table_entry->symbol_name, "panic"))
@@ -2615,7 +2696,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) {
}
}
} else if (source_node->type == NodeTypeTestDecl) {
- FnTableEntry *fn_table_entry = create_fn_raw(FnInlineAuto, GlobalLinkageIdStrong);
+ FnTableEntry *fn_table_entry = create_fn_raw(FnInlineAuto);
get_fully_qualified_decl_name(&fn_table_entry->symbol_name, &tld_fn->base, '_');
@@ -2642,17 +2723,23 @@ static void resolve_decl_comptime(CodeGen *g, TldCompTime *tld_comptime) {
}
static void add_top_level_decl(CodeGen *g, ScopeDecls *decls_scope, Tld *tld) {
- if (tld->visib_mod == VisibModExport) {
- g->resolve_queue.append(tld);
+ bool is_export = false;
+ if (tld->id == TldIdVar) {
+ assert(tld->source_node->type == NodeTypeVariableDeclaration);
+ is_export = tld->source_node->data.variable_declaration.is_export;
+ } else if (tld->id == TldIdFn) {
+ assert(tld->source_node->type == NodeTypeFnProto);
+ is_export = tld->source_node->data.fn_proto.is_export;
}
+ if (is_export) {
+ g->resolve_queue.append(tld);
- if (tld->visib_mod == VisibModExport) {
- auto entry = g->exported_symbol_names.put_unique(tld->name, tld);
+ auto entry = g->exported_symbol_names.put_unique(tld->name, tld->source_node);
if (entry) {
- Tld *other_tld = entry->value;
+ AstNode *other_source_node = entry->value;
ErrorMsg *msg = add_node_error(g, tld->source_node,
buf_sprintf("exported symbol collision: '%s'", buf_ptr(tld->name)));
- add_error_note(g, msg, other_tld->source_node, buf_sprintf("other symbol is here"));
+ add_error_note(g, msg, other_source_node, buf_sprintf("other symbol here"));
}
}
@@ -2731,7 +2818,6 @@ static void preview_comptime_decl(CodeGen *g, AstNode *node, ScopeDecls *decls_s
g->resolve_queue.append(&tld_comptime->base);
}
-
void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node,
Scope *parent_scope)
{
@@ -2836,8 +2922,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
case NodeTypeSwitchExpr:
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
- case NodeTypeLabel:
- case NodeTypeGoto:
case NodeTypeBreak:
case NodeTypeContinue:
case NodeTypeUnreachable:
@@ -2987,8 +3071,8 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
AstNodeVariableDeclaration *var_decl = &source_node->data.variable_declaration;
bool is_const = var_decl->is_const;
- bool is_export = (tld_var->base.visib_mod == VisibModExport);
bool is_extern = var_decl->is_extern;
+ bool is_export = var_decl->is_export;
TypeTableEntry *explicit_type = nullptr;
if (var_decl->type) {
@@ -2996,9 +3080,7 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
explicit_type = validate_var_type(g, var_decl->type, proposed_type);
}
- if (is_export && is_extern) {
- add_node_error(g, source_node, buf_sprintf("variable is both export and extern"));
- }
+ assert(!is_export || !is_extern);
VarLinkage linkage;
if (is_export) {
@@ -3009,7 +3091,6 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
linkage = VarLinkageInternal;
}
-
IrInstruction *init_value = nullptr;
// TODO more validation for types that can't be used for export/extern variables
@@ -3058,6 +3139,15 @@ static void resolve_decl_var(CodeGen *g, TldVar *tld_var) {
}
}
+ if (var_decl->section_expr != nullptr) {
+ if (var_decl->is_extern) {
+ add_node_error(g, var_decl->section_expr,
+ buf_sprintf("cannot set section of external variable '%s'", buf_ptr(var_decl->symbol)));
+ } else if (!analyze_const_string(g, tld_var->base.parent_scope, var_decl->section_expr, &tld_var->section_name)) {
+ tld_var->section_name = nullptr;
+ }
+ }
+
g->global_vars.append(tld_var);
}
@@ -3319,7 +3409,7 @@ TypeStructField *find_struct_type_field(TypeTableEntry *type_entry, Buf *name) {
TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
assert(type_entry->id == TypeTableEntryIdUnion);
- assert(type_entry->data.unionation.complete);
+ assert(type_entry->data.unionation.zero_bits_known);
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
TypeUnionField *field = &type_entry->data.unionation.fields[i];
if (buf_eql_buf(field->enum_field->name, name)) {
@@ -3331,7 +3421,7 @@ TypeUnionField *find_union_type_field(TypeTableEntry *type_entry, Buf *name) {
TypeUnionField *find_union_field_by_tag(TypeTableEntry *type_entry, const BigInt *tag) {
assert(type_entry->id == TypeTableEntryIdUnion);
- assert(type_entry->data.unionation.complete);
+ assert(type_entry->data.unionation.zero_bits_known);
assert(type_entry->data.unionation.gen_tag_index != SIZE_MAX);
for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) {
TypeUnionField *field = &type_entry->data.unionation.fields[i];
@@ -3726,8 +3816,10 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *a
Buf *proto_name = proto_node->data.fn_proto.name;
bool is_pub = (proto_node->data.fn_proto.visib_mod == VisibModPub);
+ bool ok_cc = (proto_node->data.fn_proto.cc == CallingConventionUnspecified ||
+ proto_node->data.fn_proto.cc == CallingConventionCold);
- if (is_pub) {
+ if (is_pub && ok_cc) {
if (buf_eql_str(proto_name, "main")) {
g->have_pub_main = true;
g->windows_subsystem_windows = false;
@@ -3735,28 +3827,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *a
} else if (buf_eql_str(proto_name, "panic")) {
g->have_pub_panic = true;
}
- } else if (proto_node->data.fn_proto.visib_mod == VisibModExport && buf_eql_str(proto_name, "main") &&
- g->libc_link_lib != nullptr)
- {
- g->have_c_main = true;
- g->windows_subsystem_windows = false;
- g->windows_subsystem_console = true;
- } else if (proto_node->data.fn_proto.visib_mod == VisibModExport && buf_eql_str(proto_name, "WinMain") &&
- g->zig_target.os == ZigLLVM_Win32)
- {
- g->have_winmain = true;
- g->windows_subsystem_windows = true;
- g->windows_subsystem_console = false;
- } else if (proto_node->data.fn_proto.visib_mod == VisibModExport &&
- buf_eql_str(proto_name, "WinMainCRTStartup") && g->zig_target.os == ZigLLVM_Win32)
- {
- g->have_winmain_crt_startup = true;
- } else if (proto_node->data.fn_proto.visib_mod == VisibModExport &&
- buf_eql_str(proto_name, "DllMainCRTStartup") && g->zig_target.os == ZigLLVM_Win32)
- {
- g->have_dllmain_crt_startup = true;
}
-
}
}
@@ -3820,12 +3891,14 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_b
index = 6;
} else if (size_in_bits == 16) {
index = 7;
- } else if (size_in_bits == 32) {
+ } else if (size_in_bits == 29) {
index = 8;
- } else if (size_in_bits == 64) {
+ } else if (size_in_bits == 32) {
index = 9;
- } else if (size_in_bits == 128) {
+ } else if (size_in_bits == 64) {
index = 10;
+ } else if (size_in_bits == 128) {
+ index = 11;
} else {
return nullptr;
}
@@ -3888,7 +3961,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return false;
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
- case TypeTableEntryIdUnion:
return type_has_bits(type_entry);
case TypeTableEntryIdErrorUnion:
return type_has_bits(type_entry->data.error.child_type);
@@ -3896,6 +3968,14 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
return type_has_bits(type_entry->data.maybe.child_type) &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdPointer &&
type_entry->data.maybe.child_type->id != TypeTableEntryIdFn;
+ case TypeTableEntryIdUnion:
+ assert(type_entry->data.unionation.complete);
+ if (type_entry->data.unionation.gen_field_count == 0)
+ return false;
+ if (!type_has_bits(type_entry))
+ return false;
+ return true;
+
}
zig_unreachable();
}
@@ -5438,3 +5518,4 @@ uint32_t type_ptr_hash(const TypeTableEntry *ptr) {
bool type_ptr_eql(const TypeTableEntry *a, const TypeTableEntry *b) {
return a == b;
}
+
diff --git a/src/analyze.hpp b/src/analyze.hpp
index e6100c692c..6224e64dd5 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -180,5 +180,9 @@ void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name);
uint32_t get_abi_alignment(CodeGen *g, TypeTableEntry *type_entry);
TypeTableEntry *get_align_amt_type(CodeGen *g);
+PackageTableEntry *new_anonymous_package(void);
+
+Buf *const_value_to_buffer(ConstExprValue *const_val);
+void add_fn_export(CodeGen *g, FnTableEntry *fn_table_entry, Buf *symbol_name, GlobalLinkageId linkage, bool ccc);
#endif
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 4f4dc1decd..f0912285f0 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -78,7 +78,6 @@ static const char *visib_mod_string(VisibMod mod) {
switch (mod) {
case VisibModPub: return "pub ";
case VisibModPrivate: return "";
- case VisibModExport: return "export ";
}
zig_unreachable();
}
@@ -112,6 +111,10 @@ static const char *extern_string(bool is_extern) {
return is_extern ? "extern " : "";
}
+static const char *export_string(bool is_export) {
+ return is_export ? "export " : "";
+}
+
//static const char *calling_convention_string(CallingConvention cc) {
// switch (cc) {
// case CallingConventionUnspecified: return "";
@@ -212,10 +215,6 @@ static const char *node_type_str(NodeType node_type) {
return "SwitchProng";
case NodeTypeSwitchRange:
return "SwitchRange";
- case NodeTypeLabel:
- return "Label";
- case NodeTypeGoto:
- return "Goto";
case NodeTypeCompTime:
return "CompTime";
case NodeTypeBreak:
@@ -388,7 +387,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
switch (node->type) {
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
- case NodeTypeLabel:
case NodeTypeStructValueField:
zig_unreachable();
case NodeTypeRoot:
@@ -411,8 +409,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
{
const char *pub_str = visib_mod_string(node->data.fn_proto.visib_mod);
const char *extern_str = extern_string(node->data.fn_proto.is_extern);
+ const char *export_str = export_string(node->data.fn_proto.is_export);
const char *inline_str = inline_string(node->data.fn_proto.is_inline);
- fprintf(ar->f, "%s%s%sfn", pub_str, inline_str, extern_str);
+ fprintf(ar->f, "%s%s%s%sfn", pub_str, inline_str, export_str, extern_str);
if (node->data.fn_proto.name != nullptr) {
fprintf(ar->f, " ");
print_symbol(ar, node->data.fn_proto.name);
@@ -440,6 +439,16 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
}
fprintf(ar->f, ")");
+ if (node->data.fn_proto.align_expr) {
+ fprintf(ar->f, " align(");
+ render_node_grouped(ar, node->data.fn_proto.align_expr);
+ fprintf(ar->f, ")");
+ }
+ if (node->data.fn_proto.section_expr) {
+ fprintf(ar->f, " section(");
+ render_node_grouped(ar, node->data.fn_proto.section_expr);
+ fprintf(ar->f, ")");
+ }
AstNode *return_type_node = node->data.fn_proto.return_type;
if (return_type_node != nullptr) {
@@ -456,6 +465,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
break;
}
case NodeTypeBlock:
+ if (node->data.block.name != nullptr) {
+ fprintf(ar->f, "%s: ", buf_ptr(node->data.block.name));
+ }
if (node->data.block.statements.length == 0) {
fprintf(ar->f, "{}");
break;
@@ -464,19 +476,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
ar->indent += ar->indent_size;
for (size_t i = 0; i < node->data.block.statements.length; i += 1) {
AstNode *statement = node->data.block.statements.at(i);
- if (statement->type == NodeTypeLabel) {
- ar->indent -= ar->indent_size;
- print_indent(ar);
- fprintf(ar->f, "%s:\n", buf_ptr(statement->data.label.name));
- ar->indent += ar->indent_size;
- continue;
- }
print_indent(ar);
render_node_grouped(ar, statement);
- if (!(i == node->data.block.statements.length - 1 &&
- node->data.block.last_statement_is_result_expression)) {
- fprintf(ar->f, ";");
- }
+ fprintf(ar->f, ";");
fprintf(ar->f, "\n");
}
ar->indent -= ar->indent_size;
@@ -501,6 +503,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeBreak:
{
fprintf(ar->f, "break");
+ if (node->data.break_expr.name != nullptr) {
+ fprintf(ar->f, " :%s", buf_ptr(node->data.break_expr.name));
+ }
if (node->data.break_expr.expr) {
fprintf(ar->f, " ");
render_node_grouped(ar, node->data.break_expr.expr);
@@ -526,6 +531,16 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, ": ");
render_node_grouped(ar, node->data.variable_declaration.type);
}
+ if (node->data.variable_declaration.align_expr) {
+ fprintf(ar->f, "align(");
+ render_node_grouped(ar, node->data.variable_declaration.align_expr);
+ fprintf(ar->f, ") ");
+ }
+ if (node->data.variable_declaration.section_expr) {
+ fprintf(ar->f, "section(");
+ render_node_grouped(ar, node->data.variable_declaration.section_expr);
+ fprintf(ar->f, ") ");
+ }
if (node->data.variable_declaration.expr) {
fprintf(ar->f, " = ");
render_node_grouped(ar, node->data.variable_declaration.expr);
@@ -584,12 +599,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
PrefixOp op = node->data.prefix_op_expr.prefix_op;
fprintf(ar->f, "%s", prefix_op_str(op));
- render_node_ungrouped(ar, node->data.prefix_op_expr.primary_expr);
+ AstNode *child_node = node->data.prefix_op_expr.primary_expr;
+ bool new_grouped = child_node->type == NodeTypePrefixOpExpr || child_node->type == NodeTypeAddrOfExpr;
+ render_node_extra(ar, child_node, new_grouped);
if (!grouped) fprintf(ar->f, ")");
break;
}
case NodeTypeAddrOfExpr:
{
+ if (!grouped) fprintf(ar->f, "(");
fprintf(ar->f, "&");
if (node->data.addr_of_expr.align_expr != nullptr) {
fprintf(ar->f, "align(");
@@ -617,6 +635,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
render_node_ungrouped(ar, node->data.addr_of_expr.op_expr);
+ if (!grouped) fprintf(ar->f, ")");
break;
}
case NodeTypeFnCallExpr:
@@ -625,7 +644,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, "@");
}
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
- bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr);
+ bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr);
render_node_extra(ar, fn_ref_node, grouped);
fprintf(ar->f, "(");
for (size_t i = 0; i < node->data.fn_call_expr.params.length; i += 1) {
@@ -800,6 +819,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
case NodeTypeWhileExpr:
{
+ if (node->data.while_expr.name != nullptr) {
+ fprintf(ar->f, "%s: ", buf_ptr(node->data.while_expr.name));
+ }
const char *inline_str = node->data.while_expr.is_inline ? "inline " : "";
fprintf(ar->f, "%swhile (", inline_str);
render_node_grouped(ar, node->data.while_expr.condition);
@@ -929,11 +951,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
fprintf(ar->f, "}");
break;
}
- case NodeTypeGoto:
- {
- fprintf(ar->f, "goto %s", buf_ptr(node->data.goto_expr.name));
- break;
- }
case NodeTypeCompTime:
{
fprintf(ar->f, "comptime ");
@@ -942,6 +959,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
}
case NodeTypeForExpr:
{
+ if (node->data.for_expr.name != nullptr) {
+ fprintf(ar->f, "%s: ", buf_ptr(node->data.for_expr.name));
+ }
const char *inline_str = node->data.for_expr.is_inline ? "inline " : "";
fprintf(ar->f, "%sfor (", inline_str);
render_node_grouped(ar, node->data.for_expr.array_expr);
@@ -967,6 +987,9 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeContinue:
{
fprintf(ar->f, "continue");
+ if (node->data.continue_expr.name != nullptr) {
+ fprintf(ar->f, " :%s", buf_ptr(node->data.continue_expr.name));
+ }
break;
}
case NodeTypeUnreachable:
diff --git a/src/c_tokenizer.cpp b/src/c_tokenizer.cpp
index 6be2cf991e..3746cf5853 100644
--- a/src/c_tokenizer.cpp
+++ b/src/c_tokenizer.cpp
@@ -121,6 +121,9 @@ static void begin_token(CTokenize *ctok, CTokId id) {
case CTokIdRParen:
case CTokIdEOF:
case CTokIdDot:
+ case CTokIdAsterisk:
+ case CTokIdBang:
+ case CTokIdTilde:
break;
}
}
@@ -228,10 +231,22 @@ void tokenize_c_macro(CTokenize *ctok, const uint8_t *c) {
begin_token(ctok, CTokIdRParen);
end_token(ctok);
break;
+ case '*':
+ begin_token(ctok, CTokIdAsterisk);
+ end_token(ctok);
+ break;
case '-':
begin_token(ctok, CTokIdMinus);
end_token(ctok);
break;
+ case '!':
+ begin_token(ctok, CTokIdBang);
+ end_token(ctok);
+ break;
+ case '~':
+ begin_token(ctok, CTokIdTilde);
+ end_token(ctok);
+ break;
default:
return mark_error(ctok);
}
diff --git a/src/c_tokenizer.hpp b/src/c_tokenizer.hpp
index a3df2b94af..d7c9e53bcf 100644
--- a/src/c_tokenizer.hpp
+++ b/src/c_tokenizer.hpp
@@ -22,6 +22,9 @@ enum CTokId {
CTokIdRParen,
CTokIdEOF,
CTokIdDot,
+ CTokIdAsterisk,
+ CTokIdBang,
+ CTokIdTilde,
};
enum CNumLitSuffix {
diff --git a/src/codegen.cpp b/src/codegen.cpp
index f84636502d..0aecacb0b2 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -55,6 +55,10 @@ static PackageTableEntry *new_package(const char *root_src_dir, const char *root
return entry;
}
+PackageTableEntry *new_anonymous_package(void) {
+ return new_package("", "");
+}
+
CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode,
Buf *zig_lib_dir)
{
@@ -387,24 +391,51 @@ static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) {
}
}
+static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) {
+ switch (id) {
+ case GlobalLinkageIdInternal:
+ return LLVMInternalLinkage;
+ case GlobalLinkageIdStrong:
+ return LLVMExternalLinkage;
+ case GlobalLinkageIdWeak:
+ return LLVMWeakODRLinkage;
+ case GlobalLinkageIdLinkOnce:
+ return LLVMLinkOnceODRLinkage;
+ }
+ zig_unreachable();
+}
+
static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
if (fn_table_entry->llvm_value)
return fn_table_entry->llvm_value;
- bool external_linkage = (fn_table_entry->linkage != GlobalLinkageIdInternal);
- Buf *symbol_name = get_mangled_name(g, &fn_table_entry->symbol_name, external_linkage);
+ Buf *unmangled_name = &fn_table_entry->symbol_name;
+ Buf *symbol_name;
+ GlobalLinkageId linkage;
+ if (fn_table_entry->body_node == nullptr) {
+ symbol_name = unmangled_name;
+ linkage = GlobalLinkageIdStrong;
+ } else if (fn_table_entry->export_list.length == 0) {
+ symbol_name = get_mangled_name(g, unmangled_name, false);
+ linkage = GlobalLinkageIdInternal;
+ } else {
+ FnExport *fn_export = &fn_table_entry->export_list.items[0];
+ symbol_name = &fn_export->name;
+ linkage = fn_export->linkage;
+ }
+ bool external_linkage = linkage != GlobalLinkageIdInternal;
if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall && external_linkage &&
g->zig_target.arch.arch == ZigLLVM_x86)
{
- // prevent name mangling
+ // prevent llvm name mangling
symbol_name = buf_sprintf("\x01_%s", buf_ptr(symbol_name));
}
TypeTableEntry *fn_type = fn_table_entry->type_entry;
LLVMTypeRef fn_llvm_type = fn_type->data.fn.raw_type_ref;
- if (external_linkage && fn_table_entry->body_node == nullptr) {
+ if (fn_table_entry->body_node == nullptr) {
LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, buf_ptr(symbol_name));
if (existing_llvm_fn) {
fn_table_entry->llvm_value = LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, 0));
@@ -414,6 +445,12 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
} else {
fn_table_entry->llvm_value = LLVMAddFunction(g->module, buf_ptr(symbol_name), fn_llvm_type);
+
+ for (size_t i = 1; i < fn_table_entry->export_list.length; i += 1) {
+ FnExport *fn_export = &fn_table_entry->export_list.items[i];
+ LLVMAddAlias(g->module, LLVMTypeOf(fn_table_entry->llvm_value),
+ fn_table_entry->llvm_value, buf_ptr(&fn_export->name));
+ }
}
fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value);
@@ -441,20 +478,10 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
}
}
- switch (fn_table_entry->linkage) {
- case GlobalLinkageIdInternal:
- LLVMSetLinkage(fn_table_entry->llvm_value, LLVMInternalLinkage);
- LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
- break;
- case GlobalLinkageIdStrong:
- LLVMSetLinkage(fn_table_entry->llvm_value, LLVMExternalLinkage);
- break;
- case GlobalLinkageIdWeak:
- LLVMSetLinkage(fn_table_entry->llvm_value, LLVMWeakODRLinkage);
- break;
- case GlobalLinkageIdLinkOnce:
- LLVMSetLinkage(fn_table_entry->llvm_value, LLVMLinkOnceODRLinkage);
- break;
+ LLVMSetLinkage(fn_table_entry->llvm_value, to_llvm_linkage(linkage));
+
+ if (linkage == GlobalLinkageIdInternal) {
+ LLVMSetUnnamedAddr(fn_table_entry->llvm_value, true);
}
if (fn_type->data.fn.fn_type_id.return_type->id == TypeTableEntryIdUnreachable) {
@@ -561,7 +588,8 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
bool is_definition = fn_table_entry->body_node != nullptr;
unsigned flags = 0;
bool is_optimized = g->build_mode != BuildModeDebug;
- bool is_internal_linkage = (fn_table_entry->linkage == GlobalLinkageIdInternal);
+ bool is_internal_linkage = (fn_table_entry->body_node != nullptr &&
+ fn_table_entry->export_list.length == 0);
ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder,
get_di_scope(g, scope->parent), buf_ptr(&fn_table_entry->symbol_name), "",
import->di_file, line_number,
@@ -839,7 +867,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
assert(g->panic_fn != nullptr);
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
- ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, false, "");
+ ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}
@@ -988,7 +1016,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
- false, "");
+ ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}
@@ -1210,11 +1238,13 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, TypeTableEntry
return nullptr;
}
+ bool big_endian = g->is_big_endian;
+
LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
- uint32_t shift_amt = host_bit_count - bit_offset - unaligned_bit_count;
+ uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
LLVMValueRef mask_val = LLVMConstAllOnes(child_type->type_ref);
@@ -2170,12 +2200,14 @@ static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutable *executable, IrI
if (unaligned_bit_count == 0)
return get_handle_value(g, ptr, child_type, ptr_type);
+ bool big_endian = g->is_big_endian;
+
assert(!handle_is_ptr(child_type));
LLVMValueRef containing_int = gen_load(g, ptr, ptr_type, "");
uint32_t bit_offset = ptr_type->data.pointer.bit_offset;
uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int));
- uint32_t shift_amt = host_bit_count - bit_offset - unaligned_bit_count;
+ uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - unaligned_bit_count : bit_offset;
LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false);
LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, "");
@@ -2316,12 +2348,22 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
}
}
- bool want_always_inline = (instruction->fn_entry != nullptr &&
- instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_inline;
+ ZigLLVM_FnInline fn_inline;
+ switch (instruction->fn_inline) {
+ case FnInlineAuto:
+ fn_inline = ZigLLVM_FnInlineAuto;
+ break;
+ case FnInlineAlways:
+ fn_inline = (instruction->fn_entry == nullptr) ? ZigLLVM_FnInlineAuto : ZigLLVM_FnInlineAlways;
+ break;
+ case FnInlineNever:
+ fn_inline = ZigLLVM_FnInlineNever;
+ break;
+ }
LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc);
LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
- gen_param_values, (unsigned)gen_param_index, llvm_cc, want_always_inline, "");
+ gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, "");
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
@@ -2684,6 +2726,9 @@ static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutable *executable, IrInstru
}
static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutable *executable, IrInstructionRef *instruction) {
+ if (!type_has_bits(instruction->base.value.type)) {
+ return nullptr;
+ }
LLVMValueRef value = ir_llvm_value(g, instruction->value);
if (handle_is_ptr(instruction->value->value.type)) {
return value;
@@ -2975,6 +3020,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);
}
}
+ if (!type_has_bits(array_type)) {
+ LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_len_index, "");
+
+ // TODO if debug safety is on, store 0xaaaaaaa in ptr field
+ LLVMValueRef len_value = LLVMBuildNSWSub(g->builder, end_val, start_val, "");
+ gen_store_untyped(g, len_value, len_field_ptr, 0, false);
+ return tmp_struct_ptr;
+ }
+
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, slice_ptr_index, "");
LLVMValueRef indices[] = {
@@ -3473,8 +3527,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdCheckStatementIsVoid:
case IrInstructionIdTypeName:
case IrInstructionIdCanImplicitCast:
- case IrInstructionIdSetGlobalSection:
- case IrInstructionIdSetGlobalLinkage:
case IrInstructionIdDeclRef:
case IrInstructionIdSwitchVar:
case IrInstructionIdOffsetOf:
@@ -3485,6 +3537,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdSetAlignStack:
case IrInstructionIdArgType:
case IrInstructionIdTagType:
+ case IrInstructionIdExport:
zig_unreachable();
case IrInstructionIdReturn:
return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -3747,17 +3800,26 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con
case TypeTableEntryIdStruct:
{
assert(type_entry->data.structure.layout == ContainerLayoutPacked);
+ bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type
LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false);
+ size_t used_bits = 0;
for (size_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) {
TypeStructField *field = &type_entry->data.structure.fields[i];
if (field->gen_index == SIZE_MAX) {
continue;
}
LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, &const_val->data.x_struct.fields[i]);
- LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, field->packed_bits_size, false);
- val = LLVMConstShl(val, shift_amt);
- val = LLVMConstOr(val, child_val);
+ if (is_big_endian) {
+ LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, field->packed_bits_size, false);
+ val = LLVMConstShl(val, shift_amt);
+ val = LLVMConstOr(val, child_val);
+ } else {
+ LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
+ LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
+ val = LLVMConstOr(val, child_val_shifted);
+ used_bits += field->packed_bits_size;
+ }
}
return val;
}
@@ -3882,9 +3944,11 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
fields[type_struct_field->gen_index] = val;
make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(field_val->type, val);
} else {
+ bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type
LLVMTypeRef big_int_type_ref = LLVMStructGetTypeAtIndex(type_entry->type_ref,
(unsigned)type_struct_field->gen_index);
LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false);
+ size_t used_bits = 0;
for (size_t i = src_field_index; i < src_field_index_end; i += 1) {
TypeStructField *it_field = &type_entry->data.structure.fields[i];
if (it_field->gen_index == SIZE_MAX) {
@@ -3892,10 +3956,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
}
LLVMValueRef child_val = pack_const_int(g, big_int_type_ref,
&const_val->data.x_struct.fields[i]);
- LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref,
- it_field->packed_bits_size, false);
- val = LLVMConstShl(val, shift_amt);
- val = LLVMConstOr(val, child_val);
+ if (is_big_endian) {
+ LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref,
+ it_field->packed_bits_size, false);
+ val = LLVMConstShl(val, shift_amt);
+ val = LLVMConstOr(val, child_val);
+ } else {
+ LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false);
+ LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt);
+ val = LLVMConstOr(val, child_val_shifted);
+ used_bits += it_field->packed_bits_size;
+ }
}
fields[type_struct_field->gen_index] = val;
}
@@ -3946,8 +4017,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
case TypeTableEntryIdUnion:
{
LLVMTypeRef union_type_ref = type_entry->data.unionation.union_type_ref;
- ConstExprValue *payload_value = const_val->data.x_union.payload;
- assert(payload_value != nullptr);
if (type_entry->data.unionation.gen_field_count == 0) {
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) {
@@ -3960,7 +4029,8 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val) {
LLVMValueRef union_value_ref;
bool make_unnamed_struct;
- if (!type_has_bits(payload_value->type)) {
+ ConstExprValue *payload_value = const_val->data.x_union.payload;
+ if (payload_value == nullptr || !type_has_bits(payload_value->type)) {
if (type_entry->data.unionation.gen_tag_index == SIZE_MAX)
return LLVMGetUndef(type_entry->type_ref);
@@ -4635,6 +4705,7 @@ static const uint8_t int_sizes_in_bits[] = {
7,
8,
16,
+ 29,
32,
64,
128,
@@ -4955,8 +5026,6 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdIntType, "IntType", 2); // TODO rename to Int
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 2);
- create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
- create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);
create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1);
create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrCast", 2);
create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2);
@@ -4972,6 +5041,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
+ create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
@@ -4980,6 +5050,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0);
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
create_builtin_fn(g, BuiltinFnIdArgType, "ArgType", 2);
+ create_builtin_fn(g, BuiltinFnIdExport, "export", 3);
}
static const char *bool_to_str(bool b) {
@@ -5298,18 +5369,19 @@ void codegen_translate_c(CodeGen *g, Buf *full_path) {
ZigList errors = {0};
int err = parse_h_file(import, &errors, buf_ptr(full_path), g, nullptr);
- if (err) {
- fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
- exit(1);
- }
- if (errors.length > 0) {
+ if (err == ErrorCCompileErrors && errors.length > 0) {
for (size_t i = 0; i < errors.length; i += 1) {
ErrorMsg *err_msg = errors.at(i);
print_err_msg(err_msg, g->err_color);
}
exit(1);
}
+
+ if (err) {
+ fprintf(stderr, "unable to parse C file: %s\n", err_str(err));
+ exit(1);
+ }
}
static ImportTableEntry *add_special_code(CodeGen *g, PackageTableEntry *package, const char *basename) {
@@ -5417,6 +5489,27 @@ static void gen_root_source(CodeGen *g) {
assert(g->root_out_name);
assert(g->out_type != OutTypeUnknown);
+ {
+ // Zig has lazy top level definitions. Here we semantically analyze the panic function.
+ ImportTableEntry *import_with_panic;
+ if (g->have_pub_panic) {
+ import_with_panic = g->root_import;
+ } else {
+ g->panic_package = create_panic_pkg(g);
+ import_with_panic = add_special_code(g, g->panic_package, "panic.zig");
+ }
+ scan_import(g, import_with_panic);
+ Tld *panic_tld = find_decl(g, &import_with_panic->decls_scope->base, buf_create_from_str("panic"));
+ assert(panic_tld != nullptr);
+ resolve_top_level_decl(g, panic_tld, false, nullptr);
+ }
+
+
+ if (!g->error_during_imports) {
+ semantic_analyze(g);
+ }
+ report_errors_and_maybe_exit(g);
+
if (!g->is_test_build && g->zig_target.os != ZigLLVM_UnknownOS &&
!g->have_c_main && !g->have_winmain && !g->have_winmain_crt_startup &&
((g->have_pub_main && g->out_type == OutTypeObj) || g->out_type == OutTypeExe))
@@ -5426,20 +5519,6 @@ static void gen_root_source(CodeGen *g) {
if (g->zig_target.os == ZigLLVM_Win32 && !g->have_dllmain_crt_startup && g->out_type == OutTypeLib) {
g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap_lib.zig");
}
- ImportTableEntry *import_with_panic;
- if (g->have_pub_panic) {
- import_with_panic = g->root_import;
- } else {
- g->panic_package = create_panic_pkg(g);
- import_with_panic = add_special_code(g, g->panic_package, "panic.zig");
- }
- // Zig has lazy top level definitions. Here we semantically analyze the panic function.
- {
- scan_import(g, import_with_panic);
- Tld *panic_tld = find_decl(g, &import_with_panic->decls_scope->base, buf_create_from_str("panic"));
- assert(panic_tld != nullptr);
- resolve_top_level_decl(g, panic_tld, false, nullptr);
- }
if (!g->error_during_imports) {
semantic_analyze(g);
@@ -5666,7 +5745,7 @@ static void gen_h_file(CodeGen *g) {
for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) {
FnTableEntry *fn_table_entry = g->fn_defs.at(fn_def_i);
- if (fn_table_entry->linkage == GlobalLinkageIdInternal)
+ if (fn_table_entry->export_list.length == 0)
continue;
FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
diff --git a/src/ir.cpp b/src/ir.cpp
index facd7087f0..a0f5a9be47 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -207,6 +207,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVar *) {
return IrInstructionIdDeclVar;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionExport *) {
+ return IrInstructionIdExport;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionLoadPtr *) {
return IrInstructionIdLoadPtr;
}
@@ -523,14 +527,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionCanImplicitCast
return IrInstructionIdCanImplicitCast;
}
-static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalSection *) {
- return IrInstructionIdSetGlobalSection;
-}
-
-static constexpr IrInstructionId ir_instruction_id(IrInstructionSetGlobalLinkage *) {
- return IrInstructionIdSetGlobalLinkage;
-}
-
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclRef *) {
return IrInstructionIdDeclRef;
}
@@ -928,13 +924,13 @@ static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstructio
static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
- bool is_comptime, bool is_inline)
+ bool is_comptime, FnInline fn_inline)
{
IrInstructionCall *call_instruction = ir_build_instruction(irb, scope, source_node);
call_instruction->fn_entry = fn_entry;
call_instruction->fn_ref = fn_ref;
call_instruction->is_comptime = is_comptime;
- call_instruction->is_inline = is_inline;
+ call_instruction->fn_inline = fn_inline;
call_instruction->args = args;
call_instruction->arg_count = arg_count;
@@ -948,10 +944,10 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc
static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
- bool is_comptime, bool is_inline)
+ bool is_comptime, FnInline fn_inline)
{
IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope,
- old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, is_inline);
+ old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
@@ -1025,7 +1021,7 @@ static IrInstruction *ir_build_ptr_type_of(IrBuilder *irb, Scope *scope, AstNode
ptr_type_of_instruction->bit_offset_start = bit_offset_start;
ptr_type_of_instruction->bit_offset_end = bit_offset_end;
- ir_ref_instruction(align_value, irb->current_basic_block);
+ if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(child_type, irb->current_basic_block);
return &ptr_type_of_instruction->base;
@@ -1191,6 +1187,8 @@ static IrInstruction *ir_build_var_decl(IrBuilder *irb, Scope *scope, AstNode *s
if (align_value) ir_ref_instruction(align_value, irb->current_basic_block);
ir_ref_instruction(init_value, irb->current_basic_block);
+ var->decl_instruction = &decl_var_instruction->base;
+
return &decl_var_instruction->base;
}
@@ -1203,6 +1201,24 @@ static IrInstruction *ir_build_var_decl_from(IrBuilder *irb, IrInstruction *old_
return new_instruction;
}
+static IrInstruction *ir_build_export(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *name, IrInstruction *target, IrInstruction *linkage)
+{
+ IrInstructionExport *export_instruction = ir_build_instruction(
+ irb, scope, source_node);
+ export_instruction->base.value.special = ConstValSpecialStatic;
+ export_instruction->base.value.type = irb->codegen->builtin_types.entry_void;
+ export_instruction->name = name;
+ export_instruction->target = target;
+ export_instruction->linkage = linkage;
+
+ ir_ref_instruction(name, irb->current_basic_block);
+ ir_ref_instruction(target, irb->current_basic_block);
+ if (linkage) ir_ref_instruction(linkage, irb->current_basic_block);
+
+ return &export_instruction->base;
+}
+
static IrInstruction *ir_build_load_ptr(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr) {
IrInstructionLoadPtr *instruction = ir_build_instruction(irb, scope, source_node);
instruction->ptr = ptr;
@@ -2157,32 +2173,6 @@ static IrInstruction *ir_build_can_implicit_cast(IrBuilder *irb, Scope *scope, A
return &instruction->base;
}
-static IrInstruction *ir_build_set_global_section(IrBuilder *irb, Scope *scope, AstNode *source_node,
- Tld *tld, IrInstruction *value)
-{
- IrInstructionSetGlobalSection *instruction = ir_build_instruction(
- irb, scope, source_node);
- instruction->tld = tld;
- instruction->value = value;
-
- ir_ref_instruction(value, irb->current_basic_block);
-
- return &instruction->base;
-}
-
-static IrInstruction *ir_build_set_global_linkage(IrBuilder *irb, Scope *scope, AstNode *source_node,
- Tld *tld, IrInstruction *value)
-{
- IrInstructionSetGlobalLinkage *instruction = ir_build_instruction(
- irb, scope, source_node);
- instruction->tld = tld;
- instruction->value = value;
-
- ir_ref_instruction(value, irb->current_basic_block);
-
- return &instruction->base;
-}
-
static IrInstruction *ir_build_decl_ref(IrBuilder *irb, Scope *scope, AstNode *source_node,
Tld *tld, LVal lval)
{
@@ -2394,6 +2384,21 @@ static IrInstruction *ir_instruction_declvar_get_dep(IrInstructionDeclVar *instr
return nullptr;
}
+static IrInstruction *ir_instruction_export_get_dep(IrInstructionExport *instruction, size_t index) {
+ if (index < 1) return instruction->name;
+ index -= 1;
+
+ if (index < 1) return instruction->target;
+ index -= 1;
+
+ if (instruction->linkage != nullptr) {
+ if (index < 1) return instruction->linkage;
+ index -= 1;
+ }
+
+ return nullptr;
+}
+
static IrInstruction *ir_instruction_loadptr_get_dep(IrInstructionLoadPtr *instruction, size_t index) {
switch (index) {
case 0: return instruction->ptr;
@@ -2977,20 +2982,6 @@ static IrInstruction *ir_instruction_canimplicitcast_get_dep(IrInstructionCanImp
}
}
-static IrInstruction *ir_instruction_setglobalsection_get_dep(IrInstructionSetGlobalSection *instruction, size_t index) {
- switch (index) {
- case 0: return instruction->value;
- default: return nullptr;
- }
-}
-
-static IrInstruction *ir_instruction_setgloballinkage_get_dep(IrInstructionSetGlobalLinkage *instruction, size_t index) {
- switch (index) {
- case 0: return instruction->value;
- default: return nullptr;
- }
-}
-
static IrInstruction *ir_instruction_declref_get_dep(IrInstructionDeclRef *instruction, size_t index) {
return nullptr;
}
@@ -3104,6 +3095,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
return ir_instruction_binop_get_dep((IrInstructionBinOp *) instruction, index);
case IrInstructionIdDeclVar:
return ir_instruction_declvar_get_dep((IrInstructionDeclVar *) instruction, index);
+ case IrInstructionIdExport:
+ return ir_instruction_export_get_dep((IrInstructionExport *) instruction, index);
case IrInstructionIdLoadPtr:
return ir_instruction_loadptr_get_dep((IrInstructionLoadPtr *) instruction, index);
case IrInstructionIdStorePtr:
@@ -3262,10 +3255,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
return ir_instruction_typename_get_dep((IrInstructionTypeName *) instruction, index);
case IrInstructionIdCanImplicitCast:
return ir_instruction_canimplicitcast_get_dep((IrInstructionCanImplicitCast *) instruction, index);
- case IrInstructionIdSetGlobalSection:
- return ir_instruction_setglobalsection_get_dep((IrInstructionSetGlobalSection *) instruction, index);
- case IrInstructionIdSetGlobalLinkage:
- return ir_instruction_setgloballinkage_get_dep((IrInstructionSetGlobalLinkage *) instruction, index);
case IrInstructionIdDeclRef:
return ir_instruction_declref_get_dep((IrInstructionDeclRef *) instruction, index);
case IrInstructionIdPanic:
@@ -3522,33 +3511,14 @@ static VariableTableEntry *ir_create_var(IrBuilder *irb, AstNode *node, Scope *s
return var;
}
-static LabelTableEntry *find_label(IrExecutable *exec, Scope *scope, Buf *name) {
- while (scope) {
- if (scope->id == ScopeIdBlock) {
- ScopeBlock *block_scope = (ScopeBlock *)scope;
- auto entry = block_scope->label_table.maybe_get(name);
- if (entry)
- return entry->value;
- }
- scope = scope->parent;
- }
-
- return nullptr;
-}
-
-static ScopeBlock *find_block_scope(IrExecutable *exec, Scope *scope) {
- while (scope) {
- if (scope->id == ScopeIdBlock)
- return (ScopeBlock *)scope;
- scope = scope->parent;
- }
- return nullptr;
-}
-
static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode *block_node) {
assert(block_node->type == NodeTypeBlock);
+ ZigList incoming_values = {0};
+ ZigList incoming_blocks = {0};
+
ScopeBlock *scope_block = create_block_scope(block_node, parent_scope);
+
Scope *outer_block_scope = &scope_block->base;
Scope *child_scope = outer_block_scope;
@@ -3562,44 +3532,18 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
return ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
}
+ if (block_node->data.block.name != nullptr) {
+ scope_block->incoming_blocks = &incoming_blocks;
+ scope_block->incoming_values = &incoming_values;
+ scope_block->end_block = ir_build_basic_block(irb, parent_scope, "BlockEnd");
+ scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, ir_should_inline(irb->exec, parent_scope));
+ }
+
bool is_continuation_unreachable = false;
IrInstruction *noreturn_return_value = nullptr;
- IrInstruction *return_value = nullptr;
for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) {
AstNode *statement_node = block_node->data.block.statements.at(i);
- if (statement_node->type == NodeTypeLabel) {
- Buf *label_name = statement_node->data.label.name;
- IrBasicBlock *label_block = ir_build_basic_block(irb, child_scope, buf_ptr(label_name));
- LabelTableEntry *label = allocate(1);
- label->decl_node = statement_node;
- label->bb = label_block;
- irb->exec->all_labels.append(label);
-
- LabelTableEntry *existing_label = find_label(irb->exec, child_scope, label_name);
- if (existing_label) {
- ErrorMsg *msg = add_node_error(irb->codegen, statement_node,
- buf_sprintf("duplicate label name '%s'", buf_ptr(label_name)));
- add_error_note(irb->codegen, msg, existing_label->decl_node, buf_sprintf("other label here"));
- return irb->codegen->invalid_instruction;
- } else {
- ScopeBlock *scope_block = find_block_scope(irb->exec, child_scope);
- scope_block->label_table.put(label_name, label);
- }
-
- if (!is_continuation_unreachable) {
- // fall through into new labeled basic block
- IrInstruction *is_comptime = ir_mark_gen(ir_build_const_bool(irb, child_scope, statement_node,
- ir_should_inline(irb->exec, child_scope)));
- ir_mark_gen(ir_build_br(irb, child_scope, statement_node, label_block, is_comptime));
- }
- ir_set_cursor_at_end(irb, label_block);
-
- // a label is an entry point
- is_continuation_unreachable = false;
- continue;
- }
-
IrInstruction *statement_value = ir_gen_node(irb, statement_node, child_scope);
is_continuation_unreachable = instr_is_unreachable(statement_value);
if (is_continuation_unreachable) {
@@ -3614,39 +3558,31 @@ static IrInstruction *ir_gen_block(IrBuilder *irb, Scope *parent_scope, AstNode
// variable declarations start a new scope
IrInstructionDeclVar *decl_var_instruction = (IrInstructionDeclVar *)statement_value;
child_scope = decl_var_instruction->var->child_scope;
- } else {
- // label, defer, variable declaration will never be the result expression
- if (block_node->data.block.last_statement_is_result_expression &&
- i == block_node->data.block.statements.length - 1) {
- // this is the result value statement
- return_value = statement_value;
- } else {
- // there are more statements ahead of this one. this statement's value must be void
- if (statement_value != irb->codegen->invalid_instruction) {
- ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
- }
- }
+ } else if (statement_value != irb->codegen->invalid_instruction) {
+ // this statement's value must be void
+ ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value));
}
}
if (is_continuation_unreachable) {
assert(noreturn_return_value != nullptr);
- return noreturn_return_value;
- }
- // control flow falls out of block
-
- if (block_node->data.block.last_statement_is_result_expression) {
- // return value was determined by the last statement
- assert(return_value != nullptr);
+ if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) {
+ return noreturn_return_value;
+ }
} else {
- // return value is implicitly void
- assert(return_value == nullptr);
- return_value = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node));
+ incoming_blocks.append(irb->current_basic_block);
+ incoming_values.append(ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)));
}
- ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
-
- return return_value;
+ if (block_node->data.block.name != nullptr) {
+ ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
+ ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime));
+ ir_set_cursor_at_end(irb, scope_block->end_block);
+ return ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
+ } else {
+ ir_gen_defers_for_block(irb, child_scope, outer_block_scope, false);
+ return ir_mark_gen(ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)));
+ }
}
static IrInstruction *ir_gen_bin_op_id(IrBuilder *irb, Scope *scope, AstNode *node, IrBinOp op_id) {
@@ -4526,39 +4462,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_can_implicit_cast(irb, scope, node, arg0_value, arg1_value);
}
- case BuiltinFnIdSetGlobalSection:
- case BuiltinFnIdSetGlobalLinkage:
- {
- AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
- if (arg0_node->type != NodeTypeSymbol) {
- add_node_error(irb->codegen, arg0_node, buf_sprintf("expected identifier"));
- return irb->codegen->invalid_instruction;
- }
- Buf *variable_name = arg0_node->data.symbol_expr.symbol;
- Tld *tld = find_decl(irb->codegen, scope, variable_name);
- if (!tld) {
- add_node_error(irb->codegen, node, buf_sprintf("use of undeclared identifier '%s'",
- buf_ptr(variable_name)));
- return irb->codegen->invalid_instruction;
- }
- if (tld->id != TldIdVar && tld->id != TldIdFn) {
- add_node_error(irb->codegen, node, buf_sprintf("'%s' must be global variable or function",
- buf_ptr(variable_name)));
- return irb->codegen->invalid_instruction;
- }
- AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
- IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
- if (arg1_value == irb->codegen->invalid_instruction)
- return arg1_value;
-
- if (builtin_fn->id == BuiltinFnIdSetGlobalSection) {
- return ir_build_set_global_section(irb, scope, node, tld, arg1_value);
- } else if (builtin_fn->id == BuiltinFnIdSetGlobalLinkage) {
- return ir_build_set_global_linkage(irb, scope, node, tld, arg1_value);
- } else {
- zig_unreachable();
- }
- }
case BuiltinFnIdPanic:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -4672,6 +4575,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdInlineCall:
+ case BuiltinFnIdNoInlineCall:
{
if (node->data.fn_call_expr.params.length == 0) {
add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
@@ -4692,8 +4596,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
if (args[i] == irb->codegen->invalid_instruction)
return args[i];
}
+ FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
- return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, true);
+ return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline);
}
case BuiltinFnIdTypeId:
{
@@ -4780,6 +4685,25 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_arg_type(irb, scope, node, arg0_value, arg1_value);
}
+ case BuiltinFnIdExport:
+ {
+ AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+ IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+ if (arg0_value == irb->codegen->invalid_instruction)
+ return arg0_value;
+
+ AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+ IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
+ if (arg1_value == irb->codegen->invalid_instruction)
+ return arg1_value;
+
+ AstNode *arg2_node = node->data.fn_call_expr.params.at(2);
+ IrInstruction *arg2_value = ir_gen_node(irb, arg2_node, scope);
+ if (arg2_value == irb->codegen->invalid_instruction)
+ return arg2_value;
+
+ return ir_build_export(irb, scope, node, arg0_value, arg1_value, arg2_value);
+ }
}
zig_unreachable();
}
@@ -4804,7 +4728,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
return args[i];
}
- return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, false);
+ return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto);
}
static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -4895,13 +4819,18 @@ static IrInstruction *ir_gen_address_of(IrBuilder *irb, Scope *scope, AstNode *n
AstNode *expr_node = node->data.addr_of_expr.op_expr;
AstNode *align_expr = node->data.addr_of_expr.align_expr;
- if (align_expr == nullptr) {
+ if (align_expr == nullptr && !is_const && !is_volatile) {
return ir_gen_node_extra(irb, expr_node, scope, make_lval_addr(is_const, is_volatile));
}
- IrInstruction *align_value = ir_gen_node(irb, align_expr, scope);
- if (align_value == irb->codegen->invalid_instruction)
- return align_value;
+ IrInstruction *align_value;
+ if (align_expr != nullptr) {
+ align_value = ir_gen_node(irb, align_expr, scope);
+ if (align_value == irb->codegen->invalid_instruction)
+ return align_value;
+ } else {
+ align_value = nullptr;
+ }
IrInstruction *child_type = ir_gen_node(irb, expr_node, scope);
if (child_type == irb->codegen->invalid_instruction)
@@ -5078,7 +5007,7 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
bool is_const = variable_declaration->is_const;
bool is_extern = variable_declaration->is_extern;
IrInstruction *is_comptime = ir_build_const_bool(irb, scope, node,
- ir_should_inline(irb->exec, scope) || variable_declaration->is_inline);
+ ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime);
VariableTableEntry *var = ir_create_var(irb, node, scope, variable_declaration->symbol,
is_const, is_const, is_shadowable, is_comptime);
// we detect IrInstructionIdDeclVar in gen_block to make sure the next node
@@ -5097,13 +5026,16 @@ static IrInstruction *ir_gen_var_decl(IrBuilder *irb, Scope *scope, AstNode *nod
return align_value;
}
+ if (variable_declaration->section_expr != nullptr) {
+ add_node_error(irb->codegen, variable_declaration->section_expr,
+ buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol)));
+ }
+
IrInstruction *init_value = ir_gen_node(irb, variable_declaration->expr, scope);
if (init_value == irb->codegen->invalid_instruction)
return init_value;
- IrInstruction *result = ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value);
- var->decl_instruction = result;
- return result;
+ return ir_build_var_decl(irb, scope, node, var, type_instruction, align_value, init_value);
}
static IrInstruction *ir_gen_while_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
@@ -6015,22 +5947,6 @@ static IrInstruction *ir_gen_switch_expr(IrBuilder *irb, Scope *scope, AstNode *
return ir_build_phi(irb, scope, node, incoming_blocks.length, incoming_blocks.items, incoming_values.items);
}
-static IrInstruction *ir_gen_goto(IrBuilder *irb, Scope *scope, AstNode *node) {
- assert(node->type == NodeTypeGoto);
-
- // make a placeholder unreachable statement and a note to come back and
- // replace the instruction with a branch instruction
- IrGotoItem *goto_item = irb->exec->goto_list.add_one();
- goto_item->bb = irb->current_basic_block;
- goto_item->instruction_index = irb->current_basic_block->instruction_list.length;
- goto_item->source_node = node;
- goto_item->scope = scope;
-
- // we don't know if we need to generate defer expressions yet
- // we do that later when we find out which label we're jumping to.
- return ir_build_unreachable(irb, scope, node);
-}
-
static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeCompTime);
@@ -6038,6 +5954,31 @@ static IrInstruction *ir_gen_comptime(IrBuilder *irb, Scope *parent_scope, AstNo
return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval);
}
+static IrInstruction *ir_gen_return_from_block(IrBuilder *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) {
+ IrInstruction *is_comptime;
+ if (ir_should_inline(irb->exec, break_scope)) {
+ is_comptime = ir_build_const_bool(irb, break_scope, node, true);
+ } else {
+ is_comptime = block_scope->is_comptime;
+ }
+
+ IrInstruction *result_value;
+ if (node->data.break_expr.expr) {
+ result_value = ir_gen_node(irb, node->data.break_expr.expr, break_scope);
+ if (result_value == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+ } else {
+ result_value = ir_build_const_void(irb, break_scope, node);
+ }
+
+ IrBasicBlock *dest_block = block_scope->end_block;
+ ir_gen_defers_for_block(irb, break_scope, dest_block->scope, false);
+
+ block_scope->incoming_blocks->append(irb->current_basic_block);
+ block_scope->incoming_values->append(result_value);
+ return ir_build_br(irb, break_scope, node, dest_block, is_comptime);
+}
+
static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *node) {
assert(node->type == NodeTypeBreak);
@@ -6045,19 +5986,38 @@ static IrInstruction *ir_gen_break(IrBuilder *irb, Scope *break_scope, AstNode *
// * function definition scope or global scope => error, break outside loop
// * defer expression scope => error, cannot break out of defer expression
// * loop scope => OK
+ // * (if it's a labeled break) labeled block => OK
Scope *search_scope = break_scope;
ScopeLoop *loop_scope;
for (;;) {
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
- add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
- return irb->codegen->invalid_instruction;
+ if (node->data.break_expr.name != nullptr) {
+ add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name)));
+ return irb->codegen->invalid_instruction;
+ } else {
+ add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop"));
+ return irb->codegen->invalid_instruction;
+ }
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression"));
return irb->codegen->invalid_instruction;
} else if (search_scope->id == ScopeIdLoop) {
- loop_scope = (ScopeLoop *)search_scope;
- break;
+ ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
+ if (node->data.break_expr.name == nullptr ||
+ (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name)))
+ {
+ loop_scope = this_loop_scope;
+ break;
+ }
+ } else if (search_scope->id == ScopeIdBlock) {
+ ScopeBlock *this_block_scope = (ScopeBlock *)search_scope;
+ if (node->data.break_expr.name != nullptr &&
+ (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name)))
+ {
+ assert(this_block_scope->end_block != nullptr);
+ return ir_gen_return_from_block(irb, break_scope, node, this_block_scope);
+ }
}
search_scope = search_scope->parent;
}
@@ -6098,14 +6058,24 @@ static IrInstruction *ir_gen_continue(IrBuilder *irb, Scope *continue_scope, Ast
ScopeLoop *loop_scope;
for (;;) {
if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) {
- add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
- return irb->codegen->invalid_instruction;
+ if (node->data.continue_expr.name != nullptr) {
+ add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name)));
+ return irb->codegen->invalid_instruction;
+ } else {
+ add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop"));
+ return irb->codegen->invalid_instruction;
+ }
} else if (search_scope->id == ScopeIdDeferExpr) {
add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression"));
return irb->codegen->invalid_instruction;
} else if (search_scope->id == ScopeIdLoop) {
- loop_scope = (ScopeLoop *)search_scope;
- break;
+ ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope;
+ if (node->data.continue_expr.name == nullptr ||
+ (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name)))
+ {
+ loop_scope = this_loop_scope;
+ break;
+ }
}
search_scope = search_scope->parent;
}
@@ -6347,7 +6317,10 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
case NodeTypeSwitchProng:
case NodeTypeSwitchRange:
case NodeTypeStructField:
- case NodeTypeLabel:
+ case NodeTypeFnDef:
+ case NodeTypeFnDecl:
+ case NodeTypeErrorValueDecl:
+ case NodeTypeTestDecl:
zig_unreachable();
case NodeTypeBlock:
return ir_lval_wrap(irb, scope, ir_gen_block(irb, scope, node), lval);
@@ -6407,8 +6380,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
return ir_lval_wrap(irb, scope, ir_gen_test_expr(irb, scope, node), lval);
case NodeTypeSwitchExpr:
return ir_lval_wrap(irb, scope, ir_gen_switch_expr(irb, scope, node), lval);
- case NodeTypeGoto:
- return ir_lval_wrap(irb, scope, ir_gen_goto(irb, scope, node), lval);
case NodeTypeCompTime:
return ir_gen_comptime(irb, scope, node, lval);
case NodeTypeErrorType:
@@ -6429,14 +6400,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval);
case NodeTypeFnProto:
return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval);
- case NodeTypeFnDef:
- zig_panic("TODO IR gen NodeTypeFnDef");
- case NodeTypeFnDecl:
- zig_panic("TODO IR gen NodeTypeFnDecl");
- case NodeTypeErrorValueDecl:
- zig_panic("TODO IR gen NodeTypeErrorValueDecl");
- case NodeTypeTestDecl:
- zig_panic("TODO IR gen NodeTypeTestDecl");
}
zig_unreachable();
}
@@ -6451,70 +6414,6 @@ static IrInstruction *ir_gen_node(IrBuilder *irb, AstNode *node, Scope *scope) {
return ir_gen_node_extra(irb, node, scope, LVAL_NONE);
}
-static bool ir_goto_pass2(IrBuilder *irb) {
- for (size_t i = 0; i < irb->exec->goto_list.length; i += 1) {
- IrGotoItem *goto_item = &irb->exec->goto_list.at(i);
- AstNode *source_node = goto_item->source_node;
-
- // Since a goto will always end a basic block, we move the "current instruction"
- // index back to over the placeholder unreachable instruction and begin overwriting
- irb->current_basic_block = goto_item->bb;
- irb->current_basic_block->instruction_list.resize(goto_item->instruction_index);
-
- Buf *label_name = source_node->data.goto_expr.name;
-
- // Search up the scope until we find one of these things:
- // * A block scope with the label in it => OK
- // * A defer expression scope => error, error, cannot leave defer expression
- // * Top level scope => error, didn't find label
-
- LabelTableEntry *label;
- Scope *search_scope = goto_item->scope;
- for (;;) {
- if (search_scope == nullptr) {
- add_node_error(irb->codegen, source_node,
- buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
- return false;
- } else if (search_scope->id == ScopeIdBlock) {
- ScopeBlock *block_scope = (ScopeBlock *)search_scope;
- auto entry = block_scope->label_table.maybe_get(label_name);
- if (entry) {
- label = entry->value;
- break;
- }
- } else if (search_scope->id == ScopeIdDeferExpr) {
- add_node_error(irb->codegen, source_node,
- buf_sprintf("cannot goto out of defer expression"));
- return false;
- }
- search_scope = search_scope->parent;
- }
-
- label->used = true;
-
- IrInstruction *is_comptime = ir_build_const_bool(irb, goto_item->scope, source_node,
- ir_should_inline(irb->exec, goto_item->scope) || source_node->data.goto_expr.is_inline);
- if (!ir_gen_defers_for_block(irb, goto_item->scope, label->bb->scope, false)) {
- add_node_error(irb->codegen, source_node,
- buf_sprintf("no label in scope named '%s'", buf_ptr(label_name)));
- return false;
- }
- ir_build_br(irb, goto_item->scope, source_node, label->bb, is_comptime);
- }
-
- for (size_t i = 0; i < irb->exec->all_labels.length; i += 1) {
- LabelTableEntry *label = irb->exec->all_labels.at(i);
- if (!label->used) {
- add_node_error(irb->codegen, label->decl_node,
- buf_sprintf("label '%s' defined but not used",
- buf_ptr(label->decl_node->data.label.name)));
- return false;
- }
- }
-
- return true;
-}
-
static void invalidate_exec(IrExecutable *exec) {
if (exec->invalid)
return;
@@ -6551,11 +6450,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_mark_gen(ir_build_return(irb, scope, result->source_node, result));
}
- if (!ir_goto_pass2(irb)) {
- invalidate_exec(ir_executable);
- return false;
- }
-
return true;
}
@@ -7468,6 +7362,41 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira,
}
}
+ // implicit union to its enum tag type
+ if (expected_type->id == TypeTableEntryIdEnum && actual_type->id == TypeTableEntryIdUnion &&
+ (actual_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ actual_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, actual_type);
+ if (actual_type->data.unionation.tag_type == expected_type) {
+ return ImplicitCastMatchResultYes;
+ }
+ }
+
+ // implicit enum to union which has the enum as the tag type
+ if (expected_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
+ (expected_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ expected_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, expected_type);
+ if (expected_type->data.unionation.tag_type == actual_type) {
+ return ImplicitCastMatchResultYes;
+ }
+ }
+
+ // implicit enum to &const union which has the enum as the tag type
+ if (actual_type->id == TypeTableEntryIdEnum && expected_type->id == TypeTableEntryIdPointer) {
+ TypeTableEntry *union_type = expected_type->data.pointer.child_type;
+ if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
+ {
+ type_ensure_zero_bits_known(ira->codegen, union_type);
+ if (union_type->data.unionation.tag_type == actual_type) {
+ return ImplicitCastMatchResultYes;
+ }
+ }
+ }
+
// implicit undefined literal to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ImplicitCastMatchResultYes;
@@ -7497,33 +7426,53 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
IrInstruction *cur_inst = instructions[i];
TypeTableEntry *cur_type = cur_inst->value.type;
TypeTableEntry *prev_type = prev_inst->value.type;
+
if (type_is_invalid(cur_type)) {
return cur_type;
- } else if (prev_type->id == TypeTableEntryIdUnreachable) {
- prev_inst = cur_inst;
- } else if (cur_type->id == TypeTableEntryIdUnreachable) {
- continue;
- } else if (prev_type->id == TypeTableEntryIdPureError) {
+ }
+
+ if (prev_type->id == TypeTableEntryIdUnreachable) {
prev_inst = cur_inst;
continue;
- } else if (prev_type->id == TypeTableEntryIdNullLit) {
+ }
+
+ if (cur_type->id == TypeTableEntryIdUnreachable) {
+ continue;
+ }
+
+ if (prev_type->id == TypeTableEntryIdPureError) {
prev_inst = cur_inst;
continue;
- } else if (cur_type->id == TypeTableEntryIdPureError) {
+ }
+
+ if (prev_type->id == TypeTableEntryIdNullLit) {
+ prev_inst = cur_inst;
+ continue;
+ }
+
+ if (cur_type->id == TypeTableEntryIdPureError) {
if (prev_type->id == TypeTableEntryIdArray) {
convert_to_const_slice = true;
}
any_are_pure_error = true;
continue;
- } else if (cur_type->id == TypeTableEntryIdNullLit) {
+ }
+
+ if (cur_type->id == TypeTableEntryIdNullLit) {
any_are_null = true;
continue;
- } else if (types_match_const_cast_only(prev_type, cur_type)) {
+ }
+
+ if (types_match_const_cast_only(prev_type, cur_type)) {
continue;
- } else if (types_match_const_cast_only(cur_type, prev_type)) {
+ }
+
+ if (types_match_const_cast_only(cur_type, prev_type)) {
prev_inst = cur_inst;
continue;
- } else if (prev_type->id == TypeTableEntryIdInt &&
+ }
+
+ if (prev_type->id == TypeTableEntryIdInt &&
cur_type->id == TypeTableEntryIdInt &&
prev_type->data.integral.is_signed == cur_type->data.integral.is_signed)
{
@@ -7531,36 +7480,51 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
prev_inst = cur_inst;
}
continue;
- } else if (prev_type->id == TypeTableEntryIdFloat &&
- cur_type->id == TypeTableEntryIdFloat)
- {
+ }
+
+ if (prev_type->id == TypeTableEntryIdFloat && cur_type->id == TypeTableEntryIdFloat) {
if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) {
prev_inst = cur_inst;
}
- } else if (prev_type->id == TypeTableEntryIdErrorUnion &&
+ continue;
+ }
+
+ if (prev_type->id == TypeTableEntryIdErrorUnion &&
types_match_const_cast_only(prev_type->data.error.child_type, cur_type))
{
continue;
- } else if (cur_type->id == TypeTableEntryIdErrorUnion &&
+ }
+
+ if (cur_type->id == TypeTableEntryIdErrorUnion &&
types_match_const_cast_only(cur_type->data.error.child_type, prev_type))
{
prev_inst = cur_inst;
continue;
- } else if (prev_type->id == TypeTableEntryIdMaybe &&
+ }
+
+ if (prev_type->id == TypeTableEntryIdMaybe &&
types_match_const_cast_only(prev_type->data.maybe.child_type, cur_type))
{
continue;
- } else if (cur_type->id == TypeTableEntryIdMaybe &&
+ }
+
+ if (cur_type->id == TypeTableEntryIdMaybe &&
types_match_const_cast_only(cur_type->data.maybe.child_type, prev_type))
{
prev_inst = cur_inst;
continue;
- } else if (cur_type->id == TypeTableEntryIdUndefLit) {
+ }
+
+ if (cur_type->id == TypeTableEntryIdUndefLit) {
continue;
- } else if (prev_type->id == TypeTableEntryIdUndefLit) {
+ }
+
+ if (prev_type->id == TypeTableEntryIdUndefLit) {
prev_inst = cur_inst;
continue;
- } else if (prev_type->id == TypeTableEntryIdNumLitInt ||
+ }
+
+ if (prev_type->id == TypeTableEntryIdNumLitInt ||
prev_type->id == TypeTableEntryIdNumLitFloat)
{
if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) {
@@ -7569,7 +7533,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
} else {
return ira->codegen->builtin_types.entry_invalid;
}
- } else if (cur_type->id == TypeTableEntryIdNumLitInt ||
+ }
+
+ if (cur_type->id == TypeTableEntryIdNumLitInt ||
cur_type->id == TypeTableEntryIdNumLitFloat)
{
if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) {
@@ -7577,20 +7543,26 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
} else {
return ira->codegen->builtin_types.entry_invalid;
}
- } else if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
+ }
+
+ if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
cur_type->data.array.len != prev_type->data.array.len &&
types_match_const_cast_only(cur_type->data.array.child_type, prev_type->data.array.child_type))
{
convert_to_const_slice = true;
prev_inst = cur_inst;
continue;
- } else if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
+ }
+
+ if (cur_type->id == TypeTableEntryIdArray && prev_type->id == TypeTableEntryIdArray &&
cur_type->data.array.len != prev_type->data.array.len &&
types_match_const_cast_only(prev_type->data.array.child_type, cur_type->data.array.child_type))
{
convert_to_const_slice = true;
continue;
- } else if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&
+ }
+
+ if (cur_type->id == TypeTableEntryIdArray && is_slice(prev_type) &&
(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
cur_type->data.array.len == 0) &&
types_match_const_cast_only(prev_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
@@ -7598,7 +7570,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
{
convert_to_const_slice = false;
continue;
- } else if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&
+ }
+
+ if (prev_type->id == TypeTableEntryIdArray && is_slice(cur_type) &&
(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.is_const ||
prev_type->data.array.len == 0) &&
types_match_const_cast_only(cur_type->data.structure.fields[slice_ptr_index].type_entry->data.pointer.child_type,
@@ -7607,17 +7581,40 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
prev_inst = cur_inst;
convert_to_const_slice = false;
continue;
- } else {
- ErrorMsg *msg = ir_add_error_node(ira, source_node,
- buf_sprintf("incompatible types: '%s' and '%s'",
- buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
- add_error_note(ira->codegen, msg, prev_inst->source_node,
- buf_sprintf("type '%s' here", buf_ptr(&prev_type->name)));
- add_error_note(ira->codegen, msg, cur_inst->source_node,
- buf_sprintf("type '%s' here", buf_ptr(&cur_type->name)));
-
- return ira->codegen->builtin_types.entry_invalid;
}
+
+ if (prev_type->id == TypeTableEntryIdEnum && cur_type->id == TypeTableEntryIdUnion &&
+ (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, cur_type);
+ if (type_is_invalid(cur_type))
+ return ira->codegen->builtin_types.entry_invalid;
+ if (cur_type->data.unionation.tag_type == prev_type) {
+ continue;
+ }
+ }
+
+ if (cur_type->id == TypeTableEntryIdEnum && prev_type->id == TypeTableEntryIdUnion &&
+ (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, prev_type);
+ if (type_is_invalid(prev_type))
+ return ira->codegen->builtin_types.entry_invalid;
+ if (prev_type->data.unionation.tag_type == cur_type) {
+ prev_inst = cur_inst;
+ continue;
+ }
+ }
+
+ ErrorMsg *msg = ir_add_error_node(ira, source_node,
+ buf_sprintf("incompatible types: '%s' and '%s'",
+ buf_ptr(&prev_type->name), buf_ptr(&cur_type->name)));
+ add_error_note(ira->codegen, msg, prev_inst->source_node,
+ buf_sprintf("type '%s' here", buf_ptr(&prev_type->name)));
+ add_error_note(ira->codegen, msg, cur_inst->source_node,
+ buf_sprintf("type '%s' here", buf_ptr(&cur_type->name)));
+
+ return ira->codegen->builtin_types.entry_invalid;
}
if (convert_to_const_slice) {
assert(prev_inst->value.type->id == TypeTableEntryIdArray);
@@ -7664,8 +7661,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod
static void ir_add_alloca(IrAnalyze *ira, IrInstruction *instruction, TypeTableEntry *type_entry) {
if (type_has_bits(type_entry) && handle_is_ptr(type_entry)) {
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
- assert(fn_entry);
- fn_entry->alloca_list.append(instruction);
+ if (fn_entry != nullptr) {
+ fn_entry->alloca_list.append(instruction);
+ }
}
}
@@ -7767,9 +7765,7 @@ static IrInstruction *ir_resolve_cast(IrAnalyze *ira, IrInstruction *source_inst
IrInstruction *result = ir_build_cast(&ira->new_irb, source_instr->scope, source_instr->source_node, wanted_type, value, cast_op);
result->value.type = wanted_type;
if (need_alloca) {
- FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
- if (fn_entry)
- fn_entry->alloca_list.append(result);
+ ir_add_alloca(ira, result, wanted_type);
}
return result;
}
@@ -8203,6 +8199,7 @@ static IrInstruction *ir_analyze_cast_ref(IrAnalyze *ira, IrInstruction *source_
assert(fn_entry);
fn_entry->alloca_list.append(new_instruction);
}
+ ir_add_alloca(ira, new_instruction, child_type);
return new_instruction;
}
}
@@ -8246,13 +8243,15 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi
TypeTableEntry *ptr_type = get_pointer_to_type_extra(ira->codegen, value->value.type,
is_const, is_volatile, get_abi_alignment(ira->codegen, value->value.type), 0, 0);
- FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
- assert(fn_entry);
IrInstruction *new_instruction = ir_build_ref(&ira->new_irb, source_instruction->scope,
source_instruction->source_node, value, is_const, is_volatile);
new_instruction->value.type = ptr_type;
new_instruction->value.data.rh_ptr = RuntimeHintPtrStack;
- fn_entry->alloca_list.append(new_instruction);
+ if (type_has_bits(ptr_type)) {
+ FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
+ assert(fn_entry);
+ fn_entry->alloca_list.append(new_instruction);
+ }
return new_instruction;
}
@@ -8370,6 +8369,63 @@ static IrInstruction *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInstruc
return result;
}
+static IrInstruction *ir_analyze_enum_to_union(IrAnalyze *ira, IrInstruction *source_instr,
+ IrInstruction *target, TypeTableEntry *wanted_type)
+{
+ assert(wanted_type->id == TypeTableEntryIdUnion);
+ assert(target->value.type->id == TypeTableEntryIdEnum);
+
+ if (instr_is_comptime(target)) {
+ ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
+ if (!val)
+ return ira->codegen->invalid_instruction;
+ TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag);
+ assert(union_field != nullptr);
+ type_ensure_zero_bits_known(ira->codegen, union_field->type_entry);
+ if (!union_field->type_entry->zero_bits) {
+ AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(
+ union_field->enum_field->decl_index);
+ ErrorMsg *msg = ir_add_error(ira, source_instr,
+ buf_sprintf("cast to union '%s' must initialize '%s' field '%s'",
+ buf_ptr(&wanted_type->name),
+ buf_ptr(&union_field->type_entry->name),
+ buf_ptr(union_field->name)));
+ add_error_note(ira->codegen, msg, field_node,
+ buf_sprintf("field '%s' declared here", buf_ptr(union_field->name)));
+ return ira->codegen->invalid_instruction;
+ }
+ IrInstruction *result = ir_create_const(&ira->new_irb, source_instr->scope,
+ source_instr->source_node, wanted_type);
+ result->value.special = ConstValSpecialStatic;
+ result->value.type = wanted_type;
+ bigint_init_bigint(&result->value.data.x_union.tag, &val->data.x_enum_tag);
+ return result;
+ }
+
+ // if the union has all fields 0 bits, we can do it
+ // and in fact it's a noop cast because the union value is just the enum value
+ if (wanted_type->data.unionation.gen_field_count == 0) {
+ IrInstruction *result = ir_build_cast(&ira->new_irb, target->scope, target->source_node, wanted_type, target, CastOpNoop);
+ result->value.type = wanted_type;
+ return result;
+ }
+
+ ErrorMsg *msg = ir_add_error(ira, source_instr,
+ buf_sprintf("runtime cast to union '%s' which has non-void fields",
+ buf_ptr(&wanted_type->name)));
+ for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) {
+ TypeUnionField *union_field = &wanted_type->data.unionation.fields[i];
+ if (type_has_bits(union_field->type_entry)) {
+ AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i);
+ add_error_note(ira->codegen, msg, field_node,
+ buf_sprintf("field '%s' has type '%s'",
+ buf_ptr(union_field->name),
+ buf_ptr(&union_field->type_entry->name)));
+ }
+ }
+ return ira->codegen->invalid_instruction;
+}
+
static IrInstruction *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInstruction *source_instr,
IrInstruction *target, TypeTableEntry *wanted_type)
{
@@ -8436,14 +8492,16 @@ static IrInstruction *ir_analyze_int_to_enum(IrAnalyze *ira, IrInstruction *sour
ConstExprValue *val = ir_resolve_const(ira, target, UndefBad);
if (!val)
return ira->codegen->invalid_instruction;
- BigInt enum_member_count;
- bigint_init_unsigned(&enum_member_count, wanted_type->data.enumeration.src_field_count);
- if (bigint_cmp(&val->data.x_bigint, &enum_member_count) != CmpLT) {
+
+ TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint);
+ if (field == nullptr) {
Buf *val_buf = buf_alloc();
bigint_append_buf(val_buf, &val->data.x_bigint, 10);
- ir_add_error(ira, source_instr,
- buf_sprintf("integer value %s too big for enum '%s' which has %" PRIu32 " fields",
- buf_ptr(val_buf), buf_ptr(&wanted_type->name), wanted_type->data.enumeration.src_field_count));
+ ErrorMsg *msg = ir_add_error(ira, source_instr,
+ buf_sprintf("enum '%s' has no tag matching integer value %s",
+ buf_ptr(&wanted_type->name), buf_ptr(val_buf)));
+ add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node,
+ buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name)));
return ira->codegen->invalid_instruction;
}
@@ -8827,7 +8885,17 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
if (actual_type->id == TypeTableEntryIdNumLitFloat ||
actual_type->id == TypeTableEntryIdNumLitInt)
{
- if (wanted_type->id == TypeTableEntryIdPointer &&
+ if (wanted_type->id == TypeTableEntryIdEnum) {
+ IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.enumeration.tag_int_type, value);
+ if (type_is_invalid(cast1->value.type))
+ return ira->codegen->invalid_instruction;
+
+ IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
+ if (type_is_invalid(cast2->value.type))
+ return ira->codegen->invalid_instruction;
+
+ return cast2;
+ } else if (wanted_type->id == TypeTableEntryIdPointer &&
wanted_type->data.pointer.is_const)
{
IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.pointer.child_type, value);
@@ -8907,6 +8975,38 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
}
}
+ // explicit enum to union which has the enum as the tag type
+ if (wanted_type->id == TypeTableEntryIdUnion && actual_type->id == TypeTableEntryIdEnum &&
+ (wanted_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ wanted_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr))
+ {
+ type_ensure_zero_bits_known(ira->codegen, wanted_type);
+ if (wanted_type->data.unionation.tag_type == actual_type) {
+ return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type);
+ }
+ }
+
+ // explicit enum to &const union which has the enum as the tag type
+ if (actual_type->id == TypeTableEntryIdEnum && wanted_type->id == TypeTableEntryIdPointer) {
+ TypeTableEntry *union_type = wanted_type->data.pointer.child_type;
+ if (union_type->data.unionation.decl_node->data.container_decl.auto_enum ||
+ union_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)
+ {
+ type_ensure_zero_bits_known(ira->codegen, union_type);
+ if (union_type->data.unionation.tag_type == actual_type) {
+ IrInstruction *cast1 = ir_analyze_cast(ira, source_instr, union_type, value);
+ if (type_is_invalid(cast1->value.type))
+ return ira->codegen->invalid_instruction;
+
+ IrInstruction *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1);
+ if (type_is_invalid(cast2->value.type))
+ return ira->codegen->invalid_instruction;
+
+ return cast2;
+ }
+ }
+ }
+
// explicit cast from undefined to anything
if (actual_type->id == TypeTableEntryIdUndefLit) {
return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type);
@@ -9334,6 +9434,10 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
TypeTableEntry *resolved_type = ir_resolve_peer_types(ira, bin_op_instruction->base.source_node, instructions, 2);
if (type_is_invalid(resolved_type))
return resolved_type;
+ type_ensure_zero_bits_known(ira->codegen, resolved_type);
+ if (type_is_invalid(resolved_type))
+ return resolved_type;
+
AstNode *source_node = bin_op_instruction->base.source_node;
switch (resolved_type->id) {
@@ -9398,7 +9502,8 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
ConstExprValue *op1_val = &casted_op1->value;
ConstExprValue *op2_val = &casted_op2->value;
- if ((value_is_comptime(op1_val) && value_is_comptime(op2_val)) || resolved_type->id == TypeTableEntryIdVoid) {
+ bool one_possible_value = !type_requires_comptime(resolved_type) && !type_has_bits(resolved_type);
+ if (one_possible_value || (value_is_comptime(op1_val) && value_is_comptime(op2_val))) {
bool answer;
if (resolved_type->id == TypeTableEntryIdNumLitFloat || resolved_type->id == TypeTableEntryIdFloat) {
Cmp cmp_result = float_cmp(op1_val, op2_val);
@@ -9407,7 +9512,7 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp
Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint);
answer = resolve_cmp_op_id(op_id, cmp_result);
} else {
- bool are_equal = resolved_type->id == TypeTableEntryIdVoid || const_values_equal(op1_val, op2_val);
+ bool are_equal = one_possible_value || const_values_equal(op1_val, op2_val);
if (op_id == IrBinOpCmpEq) {
answer = are_equal;
} else if (op_id == IrBinOpCmpNotEq) {
@@ -10265,6 +10370,170 @@ static TypeTableEntry *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstruc
return ira->codegen->builtin_types.entry_void;
}
+static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructionExport *instruction) {
+ IrInstruction *name = instruction->name->other;
+ Buf *symbol_name = ir_resolve_str(ira, name);
+ if (symbol_name == nullptr) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ IrInstruction *target = instruction->target->other;
+ if (type_is_invalid(target->value.type)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
+ GlobalLinkageId global_linkage_id = GlobalLinkageIdStrong;
+ if (instruction->linkage != nullptr) {
+ IrInstruction *linkage_value = instruction->linkage->other;
+ if (!ir_resolve_global_linkage(ira, linkage_value, &global_linkage_id)) {
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+ }
+
+ auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, instruction->base.source_node);
+ if (entry) {
+ AstNode *other_export_node = entry->value;
+ ErrorMsg *msg = ir_add_error(ira, &instruction->base,
+ buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name)));
+ add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here"));
+ }
+
+ switch (target->value.type->id) {
+ case TypeTableEntryIdInvalid:
+ case TypeTableEntryIdVar:
+ case TypeTableEntryIdUnreachable:
+ zig_unreachable();
+ case TypeTableEntryIdFn: {
+ FnTableEntry *fn_entry = target->value.data.x_fn.fn_entry;
+ CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc;
+ switch (cc) {
+ case CallingConventionUnspecified: {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported function must specify calling convention"));
+ add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here"));
+ } break;
+ case CallingConventionC:
+ case CallingConventionNaked:
+ case CallingConventionCold:
+ case CallingConventionStdcall:
+ add_fn_export(ira->codegen, fn_entry, symbol_name, global_linkage_id, cc == CallingConventionC);
+ break;
+ }
+ } break;
+ case TypeTableEntryIdStruct:
+ if (is_slice(target->value.type)) {
+ ir_add_error(ira, target,
+ buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value.type->name)));
+ } else if (target->value.type->data.structure.layout != ContainerLayoutExtern) {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported struct value must be declared extern"));
+ add_error_note(ira->codegen, msg, target->value.type->data.structure.decl_node, buf_sprintf("declared here"));
+ }
+ break;
+ case TypeTableEntryIdUnion:
+ if (target->value.type->data.unionation.layout != ContainerLayoutExtern) {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported union value must be declared extern"));
+ add_error_note(ira->codegen, msg, target->value.type->data.unionation.decl_node, buf_sprintf("declared here"));
+ }
+ break;
+ case TypeTableEntryIdEnum:
+ if (target->value.type->data.enumeration.layout != ContainerLayoutExtern) {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported enum value must be declared extern"));
+ add_error_note(ira->codegen, msg, target->value.type->data.enumeration.decl_node, buf_sprintf("declared here"));
+ }
+ break;
+ case TypeTableEntryIdMetaType: {
+ TypeTableEntry *type_value = target->value.data.x_type;
+ switch (type_value->id) {
+ case TypeTableEntryIdInvalid:
+ case TypeTableEntryIdVar:
+ zig_unreachable();
+ case TypeTableEntryIdStruct:
+ if (is_slice(type_value)) {
+ ir_add_error(ira, target,
+ buf_sprintf("unable to export type '%s'", buf_ptr(&type_value->name)));
+ } else if (type_value->data.structure.layout != ContainerLayoutExtern) {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported struct must be declared extern"));
+ add_error_note(ira->codegen, msg, type_value->data.structure.decl_node, buf_sprintf("declared here"));
+ }
+ break;
+ case TypeTableEntryIdUnion:
+ if (type_value->data.unionation.layout != ContainerLayoutExtern) {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported union must be declared extern"));
+ add_error_note(ira->codegen, msg, type_value->data.unionation.decl_node, buf_sprintf("declared here"));
+ }
+ break;
+ case TypeTableEntryIdEnum:
+ if (type_value->data.enumeration.layout != ContainerLayoutExtern) {
+ ErrorMsg *msg = ir_add_error(ira, target,
+ buf_sprintf("exported enum must be declared extern"));
+ add_error_note(ira->codegen, msg, type_value->data.enumeration.decl_node, buf_sprintf("declared here"));
+ }
+ break;
+ case TypeTableEntryIdFn: {
+ if (type_value->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
+ ir_add_error(ira, target,
+ buf_sprintf("exported function type must specify calling convention"));
+ }
+ } break;
+ case TypeTableEntryIdInt:
+ case TypeTableEntryIdFloat:
+ case TypeTableEntryIdPointer:
+ case TypeTableEntryIdArray:
+ case TypeTableEntryIdBool:
+ break;
+ case TypeTableEntryIdMetaType:
+ case TypeTableEntryIdVoid:
+ case TypeTableEntryIdUnreachable:
+ case TypeTableEntryIdNumLitFloat:
+ case TypeTableEntryIdNumLitInt:
+ case TypeTableEntryIdUndefLit:
+ case TypeTableEntryIdNullLit:
+ case TypeTableEntryIdMaybe:
+ case TypeTableEntryIdErrorUnion:
+ case TypeTableEntryIdPureError:
+ case TypeTableEntryIdNamespace:
+ case TypeTableEntryIdBlock:
+ case TypeTableEntryIdBoundFn:
+ case TypeTableEntryIdArgTuple:
+ case TypeTableEntryIdOpaque:
+ ir_add_error(ira, target,
+ buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name)));
+ break;
+ }
+ } break;
+ case TypeTableEntryIdVoid:
+ case TypeTableEntryIdBool:
+ case TypeTableEntryIdInt:
+ case TypeTableEntryIdFloat:
+ case TypeTableEntryIdPointer:
+ case TypeTableEntryIdArray:
+ case TypeTableEntryIdNumLitFloat:
+ case TypeTableEntryIdNumLitInt:
+ case TypeTableEntryIdUndefLit:
+ case TypeTableEntryIdNullLit:
+ case TypeTableEntryIdMaybe:
+ case TypeTableEntryIdErrorUnion:
+ case TypeTableEntryIdPureError:
+ zig_panic("TODO export const value of type %s", buf_ptr(&target->value.type->name));
+ case TypeTableEntryIdNamespace:
+ case TypeTableEntryIdBlock:
+ case TypeTableEntryIdBoundFn:
+ case TypeTableEntryIdArgTuple:
+ case TypeTableEntryIdOpaque:
+ ir_add_error(ira, target,
+ buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value.type->name)));
+ break;
+ }
+
+ ir_build_const_from(ira, &instruction->base);
+ return ira->codegen->builtin_types.entry_void;
+}
+
static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node,
IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i)
{
@@ -10442,7 +10711,7 @@ no_mem_slot:
static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
- IrInstruction *first_arg_ptr, bool comptime_fn_call, bool inline_fn_call)
+ IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
{
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
@@ -10701,7 +10970,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
if (type_requires_comptime(return_type)) {
// Throw out our work and call the function as if it were comptime.
- return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, false);
+ return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
}
}
@@ -10725,7 +10994,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;
IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
- impl_fn, nullptr, impl_param_count, casted_args, false, inline_fn_call);
+ impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline);
TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
@@ -10784,7 +11053,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
- fn_entry, fn_ref, call_param_count, casted_args, false, inline_fn_call);
+ fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline);
ir_add_alloca(ira, new_call_instruction, return_type);
return ir_finish_anal(ira, return_type);
@@ -10823,13 +11092,13 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
} else if (fn_ref->value.type->id == TypeTableEntryIdFn) {
FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
- fn_ref, nullptr, is_comptime, call_instruction->is_inline);
+ fn_ref, nullptr, is_comptime, call_instruction->fn_inline);
} else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {
assert(fn_ref->value.special == ConstValSpecialStatic);
FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
- nullptr, first_arg_ptr, is_comptime, call_instruction->is_inline);
+ nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline);
} else {
ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
@@ -10839,7 +11108,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
if (fn_ref->value.type->id == TypeTableEntryIdFn) {
return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,
- fn_ref, nullptr, false, false);
+ fn_ref, nullptr, false, FnInlineAuto);
} else {
ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
@@ -12183,102 +12452,6 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_child(IrAnalyze *ira,
return ira->codegen->builtin_types.entry_type;
}
-static TypeTableEntry *ir_analyze_instruction_set_global_section(IrAnalyze *ira,
- IrInstructionSetGlobalSection *instruction)
-{
- Tld *tld = instruction->tld;
- IrInstruction *section_value = instruction->value->other;
-
- resolve_top_level_decl(ira->codegen, tld, true, instruction->base.source_node);
- if (tld->resolution == TldResolutionInvalid)
- return ira->codegen->builtin_types.entry_invalid;
-
- Buf *section_name = ir_resolve_str(ira, section_value);
- if (!section_name)
- return ira->codegen->builtin_types.entry_invalid;
-
- AstNode **set_global_section_node;
- Buf **section_name_ptr;
- if (tld->id == TldIdVar) {
- TldVar *tld_var = (TldVar *)tld;
- set_global_section_node = &tld_var->set_global_section_node;
- section_name_ptr = &tld_var->section_name;
-
- if (tld_var->var->linkage == VarLinkageExternal) {
- ErrorMsg *msg = ir_add_error(ira, &instruction->base,
- buf_sprintf("cannot set section of external variable '%s'", buf_ptr(&tld_var->var->name)));
- add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here"));
- return ira->codegen->builtin_types.entry_invalid;
- }
- } else if (tld->id == TldIdFn) {
- TldFn *tld_fn = (TldFn *)tld;
- FnTableEntry *fn_entry = tld_fn->fn_entry;
- set_global_section_node = &fn_entry->set_global_section_node;
- section_name_ptr = &fn_entry->section_name;
-
- if (fn_entry->def_scope == nullptr) {
- ErrorMsg *msg = ir_add_error(ira, &instruction->base,
- buf_sprintf("cannot set section of external function '%s'", buf_ptr(&fn_entry->symbol_name)));
- add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here"));
- return ira->codegen->builtin_types.entry_invalid;
- }
- } else {
- // error is caught in pass1 IR gen
- zig_unreachable();
- }
-
- AstNode *source_node = instruction->base.source_node;
- if (*set_global_section_node) {
- ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("section set twice"));
- add_error_note(ira->codegen, msg, *set_global_section_node, buf_sprintf("first set here"));
- return ira->codegen->builtin_types.entry_invalid;
- }
- *set_global_section_node = source_node;
- *section_name_ptr = section_name;
-
- ir_build_const_from(ira, &instruction->base);
- return ira->codegen->builtin_types.entry_void;
-}
-
-static TypeTableEntry *ir_analyze_instruction_set_global_linkage(IrAnalyze *ira,
- IrInstructionSetGlobalLinkage *instruction)
-{
- Tld *tld = instruction->tld;
- IrInstruction *linkage_value = instruction->value->other;
-
- GlobalLinkageId linkage_scalar;
- if (!ir_resolve_global_linkage(ira, linkage_value, &linkage_scalar))
- return ira->codegen->builtin_types.entry_invalid;
-
- AstNode **set_global_linkage_node;
- GlobalLinkageId *dest_linkage_ptr;
- if (tld->id == TldIdVar) {
- TldVar *tld_var = (TldVar *)tld;
- set_global_linkage_node = &tld_var->set_global_linkage_node;
- dest_linkage_ptr = &tld_var->linkage;
- } else if (tld->id == TldIdFn) {
- TldFn *tld_fn = (TldFn *)tld;
- FnTableEntry *fn_entry = tld_fn->fn_entry;
- set_global_linkage_node = &fn_entry->set_global_linkage_node;
- dest_linkage_ptr = &fn_entry->linkage;
- } else {
- // error is caught in pass1 IR gen
- zig_unreachable();
- }
-
- AstNode *source_node = instruction->base.source_node;
- if (*set_global_linkage_node) {
- ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("linkage set twice"));
- add_error_note(ira->codegen, msg, *set_global_linkage_node, buf_sprintf("first set here"));
- return ira->codegen->builtin_types.entry_invalid;
- }
- *set_global_linkage_node = source_node;
- *dest_linkage_ptr = linkage_scalar;
-
- ir_build_const_from(ira, &instruction->base);
- return ira->codegen->builtin_types.entry_void;
-}
-
static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
IrInstructionSetDebugSafety *set_debug_safety_instruction)
{
@@ -12999,6 +13172,16 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
return tag_type;
}
case TypeTableEntryIdEnum: {
+ type_ensure_zero_bits_known(ira->codegen, target_type);
+ if (type_is_invalid(target_type))
+ return ira->codegen->builtin_types.entry_invalid;
+ if (target_type->data.enumeration.src_field_count < 2) {
+ TypeEnumField *only_field = &target_type->data.enumeration.fields[0];
+ ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
+ bigint_init_bigint(&out_val->data.x_enum_tag, &only_field->value);
+ return target_type;
+ }
+
if (pointee_val) {
ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
bigint_init_bigint(&out_val->data.x_enum_tag, &pointee_val->data.x_enum_tag);
@@ -13865,6 +14048,9 @@ static TypeTableEntry *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstruc
ImportTableEntry *child_import = allocate(1);
child_import->decls_scope = create_decls_scope(node, nullptr, nullptr, child_import);
child_import->c_import_node = node;
+ child_import->package = new_anonymous_package();
+ child_import->package->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package);
+ child_import->package->package_table.put(buf_create_from_str("std"), ira->codegen->std_package);
ZigList errors = {0};
@@ -15735,8 +15921,12 @@ static TypeTableEntry *ir_analyze_instruction_ptr_type_of(IrAnalyze *ira, IrInst
return ira->codegen->builtin_types.entry_invalid;
uint32_t align_bytes;
- if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes))
- return ira->codegen->builtin_types.entry_invalid;
+ if (instruction->align_value != nullptr) {
+ if (!ir_resolve_align(ira, instruction->align_value->other, &align_bytes))
+ return ira->codegen->builtin_types.entry_invalid;
+ } else {
+ align_bytes = get_abi_alignment(ira->codegen, child_type);
+ }
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
out_val->data.x_type = get_pointer_to_type_extra(ira->codegen, child_type,
@@ -15932,10 +16122,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_to_ptr_type(ira, (IrInstructionToPtrType *)instruction);
case IrInstructionIdPtrTypeChild:
return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
- case IrInstructionIdSetGlobalSection:
- return ir_analyze_instruction_set_global_section(ira, (IrInstructionSetGlobalSection *)instruction);
- case IrInstructionIdSetGlobalLinkage:
- return ir_analyze_instruction_set_global_linkage(ira, (IrInstructionSetGlobalLinkage *)instruction);
case IrInstructionIdSetDebugSafety:
return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
case IrInstructionIdSetFloatMode:
@@ -16078,6 +16264,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_arg_type(ira, (IrInstructionArgType *)instruction);
case IrInstructionIdTagType:
return ir_analyze_instruction_tag_type(ira, (IrInstructionTagType *)instruction);
+ case IrInstructionIdExport:
+ return ir_analyze_instruction_export(ira, (IrInstructionExport *)instruction);
}
zig_unreachable();
}
@@ -16185,12 +16373,11 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdOverflowOp: // TODO when we support multiple returns this can be side effect free
case IrInstructionIdCheckSwitchProngs:
case IrInstructionIdCheckStatementIsVoid:
- case IrInstructionIdSetGlobalSection:
- case IrInstructionIdSetGlobalLinkage:
case IrInstructionIdPanic:
case IrInstructionIdSetEvalBranchQuota:
case IrInstructionIdPtrTypeOf:
case IrInstructionIdSetAlignStack:
+ case IrInstructionIdExport:
return true;
case IrInstructionIdPhi:
case IrInstructionIdUnOp:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 223a012456..f5aba2a45d 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -886,8 +886,12 @@ static void ir_print_can_implicit_cast(IrPrint *irp, IrInstructionCanImplicitCas
}
static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instruction) {
- fprintf(irp->f, "&align ");
- ir_print_other_instruction(irp, instruction->align_value);
+ fprintf(irp->f, "&");
+ if (instruction->align_value != nullptr) {
+ fprintf(irp->f, "align(");
+ ir_print_other_instruction(irp, instruction->align_value);
+ fprintf(irp->f, ")");
+ }
const char *const_str = instruction->is_const ? "const " : "";
const char *volatile_str = instruction->is_volatile ? "volatile " : "";
fprintf(irp->f, ":%" PRIu32 ":%" PRIu32 " %s%s", instruction->bit_offset_start, instruction->bit_offset_end,
@@ -895,19 +899,6 @@ static void ir_print_ptr_type_of(IrPrint *irp, IrInstructionPtrTypeOf *instructi
ir_print_other_instruction(irp, instruction->child_type);
}
-static void ir_print_set_global_section(IrPrint *irp, IrInstructionSetGlobalSection *instruction) {
- fprintf(irp->f, "@setGlobalSection(%s,", buf_ptr(instruction->tld->name));
- ir_print_other_instruction(irp, instruction->value);
- fprintf(irp->f, ")");
-}
-
-static void ir_print_set_global_linkage(IrPrint *irp, IrInstructionSetGlobalLinkage *instruction) {
- fprintf(irp->f, "@setGlobalLinkage(%s,", buf_ptr(instruction->tld->name));
- ir_print_other_instruction(irp, instruction->value);
- fprintf(irp->f, ")");
-}
-
-
static void ir_print_decl_ref(IrPrint *irp, IrInstructionDeclRef *instruction) {
const char *ptr_str = instruction->lval.is_ptr ? "ptr " : "";
const char *const_str = instruction->lval.is_const ? "const " : "";
@@ -987,6 +978,24 @@ static void ir_print_enum_tag_type(IrPrint *irp, IrInstructionTagType *instructi
fprintf(irp->f, ")");
}
+static void ir_print_export(IrPrint *irp, IrInstructionExport *instruction) {
+ if (instruction->linkage == nullptr) {
+ fprintf(irp->f, "@export(");
+ ir_print_other_instruction(irp, instruction->name);
+ fprintf(irp->f, ",");
+ ir_print_other_instruction(irp, instruction->target);
+ fprintf(irp->f, ")");
+ } else {
+ fprintf(irp->f, "@exportWithLinkage(");
+ ir_print_other_instruction(irp, instruction->name);
+ fprintf(irp->f, ",");
+ ir_print_other_instruction(irp, instruction->target);
+ fprintf(irp->f, ",");
+ ir_print_other_instruction(irp, instruction->linkage);
+ fprintf(irp->f, ")");
+ }
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
@@ -1263,12 +1272,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdPtrTypeOf:
ir_print_ptr_type_of(irp, (IrInstructionPtrTypeOf *)instruction);
break;
- case IrInstructionIdSetGlobalSection:
- ir_print_set_global_section(irp, (IrInstructionSetGlobalSection *)instruction);
- break;
- case IrInstructionIdSetGlobalLinkage:
- ir_print_set_global_linkage(irp, (IrInstructionSetGlobalLinkage *)instruction);
- break;
case IrInstructionIdDeclRef:
ir_print_decl_ref(irp, (IrInstructionDeclRef *)instruction);
break;
@@ -1302,6 +1305,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTagType:
ir_print_enum_tag_type(irp, (IrInstructionTagType *)instruction);
break;
+ case IrInstructionIdExport:
+ ir_print_export(irp, (IrInstructionExport *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/src/parser.cpp b/src/parser.cpp
index 26ca7da31a..d069a23c5f 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -632,27 +632,6 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
return node;
}
-/*
-GotoExpression = "goto" Symbol
-*/
-static AstNode *ast_parse_goto_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
- Token *goto_token = &pc->tokens->at(*token_index);
- if (goto_token->id == TokenIdKeywordGoto) {
- *token_index += 1;
- } else if (mandatory) {
- ast_expect_token(pc, goto_token, TokenIdKeywordGoto);
- zig_unreachable();
- } else {
- return nullptr;
- }
-
- AstNode *node = ast_create_node(pc, NodeTypeGoto, goto_token);
-
- Token *dest_symbol = ast_eat_token(pc, token_index, TokenIdSymbol);
- node->data.goto_expr.name = token_buf(dest_symbol);
- return node;
-}
-
/*
CompTimeExpression(body) = "comptime" body
*/
@@ -676,8 +655,8 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
}
/*
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | (option("extern") FnProto) | AsmExpression | ("error" "." Symbol) | ContainerDecl
-KeywordLiteral = "true" | "false" | "null" | "continue" | "undefined" | "error" | "this" | "unreachable"
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." Symbol) | ContainerDecl | ("continue" option(":" Symbol))
+KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable"
*/
static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
Token *token = &pc->tokens->at(*token_index);
@@ -721,6 +700,12 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
} else if (token->id == TokenIdKeywordContinue) {
AstNode *node = ast_create_node(pc, NodeTypeContinue, token);
*token_index += 1;
+ Token *maybe_colon_token = &pc->tokens->at(*token_index);
+ if (maybe_colon_token->id == TokenIdColon) {
+ *token_index += 1;
+ Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
+ node->data.continue_expr.name = token_buf(name);
+ }
return node;
} else if (token->id == TokenIdKeywordUndefined) {
AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token);
@@ -740,9 +725,21 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
return node;
} else if (token->id == TokenIdAtSign) {
*token_index += 1;
- Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol);
+ Token *name_tok = &pc->tokens->at(*token_index);
+ Buf *name_buf;
+ if (name_tok->id == TokenIdKeywordExport) {
+ name_buf = buf_create_from_str("export");
+ *token_index += 1;
+ } else if (name_tok->id == TokenIdSymbol) {
+ name_buf = token_buf(name_tok);
+ *token_index += 1;
+ } else {
+ ast_expect_token(pc, name_tok, TokenIdSymbol);
+ zig_unreachable();
+ }
+
AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok);
- name_node->data.symbol_expr.symbol = token_buf(name_tok);
+ name_node->data.symbol_expr.symbol = name_buf;
AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token);
node->data.fn_call_expr.fn_ref_expr = name_node;
@@ -751,27 +748,25 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
node->data.fn_call_expr.is_builtin = true;
return node;
- } else if (token->id == TokenIdSymbol) {
+ }
+
+ AstNode *block_expr_node = ast_parse_block_expr(pc, token_index, false);
+ if (block_expr_node) {
+ return block_expr_node;
+ }
+
+ if (token->id == TokenIdSymbol) {
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeSymbol, token);
node->data.symbol_expr.symbol = token_buf(token);
return node;
}
- AstNode *goto_node = ast_parse_goto_expr(pc, token_index, false);
- if (goto_node)
- return goto_node;
-
AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false);
if (grouped_expr_node) {
return grouped_expr_node;
}
- AstNode *block_expr_node = ast_parse_block_expr(pc, token_index, false);
- if (block_expr_node) {
- return block_expr_node;
- }
-
AstNode *array_type_node = ast_parse_array_type_expr(pc, token_index, false);
if (array_type_node) {
return array_type_node;
@@ -791,13 +786,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
if (container_decl)
return container_decl;
- if (token->id == TokenIdKeywordExtern) {
- *token_index += 1;
- AstNode *node = ast_parse_fn_proto(pc, token_index, true, VisibModPrivate);
- node->data.fn_proto.is_extern = true;
- return node;
- }
-
if (!mandatory)
return nullptr;
@@ -1483,7 +1471,7 @@ static AstNode *ast_parse_return_expr(ParseContext *pc, size_t *token_index) {
}
/*
-BreakExpression : "break" option(Expression)
+BreakExpression = "break" option(":" Symbol) option(Expression)
*/
static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
Token *token = &pc->tokens->at(*token_index);
@@ -1493,8 +1481,15 @@ static AstNode *ast_parse_break_expr(ParseContext *pc, size_t *token_index) {
} else {
return nullptr;
}
-
AstNode *node = ast_create_node(pc, NodeTypeBreak, token);
+
+ Token *maybe_colon_token = &pc->tokens->at(*token_index);
+ if (maybe_colon_token->id == TokenIdColon) {
+ *token_index += 1;
+ Token *name = ast_eat_token(pc, token_index, TokenIdSymbol);
+ node->data.break_expr.name = token_buf(name);
+ }
+
node->data.break_expr.expr = ast_parse_expression(pc, token_index, false);
return node;
@@ -1534,38 +1529,20 @@ static AstNode *ast_parse_defer_expr(ParseContext *pc, size_t *token_index) {
}
/*
-VariableDeclaration = option("comptime") ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
+VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("align" "(" Expression ")") "=" Expression
*/
static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *token_index, bool mandatory,
- VisibMod visib_mod)
+ VisibMod visib_mod, bool is_comptime, bool is_export)
{
Token *first_token = &pc->tokens->at(*token_index);
Token *var_token;
bool is_const;
- bool is_comptime;
- if (first_token->id == TokenIdKeywordCompTime) {
- is_comptime = true;
- var_token = &pc->tokens->at(*token_index + 1);
-
- if (var_token->id == TokenIdKeywordVar) {
- is_const = false;
- } else if (var_token->id == TokenIdKeywordConst) {
- is_const = true;
- } else if (mandatory) {
- ast_invalid_token_error(pc, var_token);
- } else {
- return nullptr;
- }
-
- *token_index += 2;
- } else if (first_token->id == TokenIdKeywordVar) {
- is_comptime = false;
+ if (first_token->id == TokenIdKeywordVar) {
is_const = false;
var_token = first_token;
*token_index += 1;
} else if (first_token->id == TokenIdKeywordConst) {
- is_comptime = false;
is_const = true;
var_token = first_token;
*token_index += 1;
@@ -1577,7 +1554,8 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
AstNode *node = ast_create_node(pc, NodeTypeVariableDeclaration, var_token);
- node->data.variable_declaration.is_inline = is_comptime;
+ node->data.variable_declaration.is_comptime = is_comptime;
+ node->data.variable_declaration.is_export = is_export;
node->data.variable_declaration.is_const = is_const;
node->data.variable_declaration.visib_mod = visib_mod;
@@ -1600,6 +1578,14 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
next_token = &pc->tokens->at(*token_index);
}
+ if (next_token->id == TokenIdKeywordSection) {
+ *token_index += 1;
+ ast_eat_token(pc, token_index, TokenIdLParen);
+ node->data.variable_declaration.section_expr = ast_parse_expression(pc, token_index, true);
+ ast_eat_token(pc, token_index, TokenIdRParen);
+ next_token = &pc->tokens->at(*token_index);
+ }
+
if (next_token->id == TokenIdEq) {
*token_index += 1;
node->data.variable_declaration.expr = ast_parse_expression(pc, token_index, true);
@@ -1612,6 +1598,50 @@ static AstNode *ast_parse_variable_declaration_expr(ParseContext *pc, size_t *to
return node;
}
+/*
+GlobalVarDecl = option("export") VariableDeclaration ";"
+*/
+static AstNode *ast_parse_global_var_decl(ParseContext *pc, size_t *token_index, VisibMod visib_mod) {
+ Token *first_token = &pc->tokens->at(*token_index);
+
+ bool is_export = false;;
+ if (first_token->id == TokenIdKeywordExport) {
+ *token_index += 1;
+ is_export = true;
+ }
+
+ AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, is_export);
+ if (node == nullptr) {
+ if (is_export) {
+ *token_index -= 1;
+ }
+ return nullptr;
+ }
+ return node;
+}
+
+/*
+LocalVarDecl = option("comptime") VariableDeclaration
+*/
+static AstNode *ast_parse_local_var_decl(ParseContext *pc, size_t *token_index) {
+ Token *first_token = &pc->tokens->at(*token_index);
+
+ bool is_comptime = false;;
+ if (first_token->id == TokenIdKeywordCompTime) {
+ *token_index += 1;
+ is_comptime = true;
+ }
+
+ AstNode *node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate, is_comptime, false);
+ if (node == nullptr) {
+ if (is_comptime) {
+ *token_index -= 1;
+ }
+ return nullptr;
+ }
+ return node;
+}
+
/*
BoolOrExpression = BoolAndExpression "or" BoolOrExpression | BoolAndExpression
*/
@@ -1638,35 +1668,53 @@ static AstNode *ast_parse_bool_or_expr(ParseContext *pc, size_t *token_index, bo
}
/*
-WhileExpression(body) = option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
+WhileExpression(body) = option(Symbol ":") option("inline") "while" "(" Expression ")" option("|" option("*") Symbol "|") option(":" "(" Expression ")") body option("else" option("|" Symbol "|") BlockExpression(body))
*/
static AstNode *ast_parse_while_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
- Token *first_token = &pc->tokens->at(*token_index);
- Token *while_token;
+ size_t orig_token_index = *token_index;
- bool is_inline;
- if (first_token->id == TokenIdKeywordInline) {
- while_token = &pc->tokens->at(*token_index + 1);
- if (while_token->id == TokenIdKeywordWhile) {
- is_inline = true;
- *token_index += 2;
+ Token *name_token = nullptr;
+ Token *token = &pc->tokens->at(*token_index);
+
+ if (token->id == TokenIdSymbol) {
+ *token_index += 1;
+ Token *colon_token = &pc->tokens->at(*token_index);
+ if (colon_token->id == TokenIdColon) {
+ *token_index += 1;
+ name_token = token;
+ token = &pc->tokens->at(*token_index);
} else if (mandatory) {
- ast_expect_token(pc, while_token, TokenIdKeywordWhile);
+ ast_expect_token(pc, colon_token, TokenIdColon);
zig_unreachable();
} else {
+ *token_index = orig_token_index;
return nullptr;
}
- } else if (first_token->id == TokenIdKeywordWhile) {
- while_token = first_token;
- is_inline = false;
+ }
+
+ bool is_inline = false;
+ if (token->id == TokenIdKeywordInline) {
+ is_inline = true;
+ *token_index += 1;
+ token = &pc->tokens->at(*token_index);
+ }
+
+ Token *while_token;
+ if (token->id == TokenIdKeywordWhile) {
+ while_token = token;
*token_index += 1;
} else if (mandatory) {
- ast_expect_token(pc, first_token, TokenIdKeywordWhile);
+ ast_expect_token(pc, token, TokenIdKeywordWhile);
zig_unreachable();
} else {
+ *token_index = orig_token_index;
return nullptr;
}
+
AstNode *node = ast_create_node(pc, NodeTypeWhileExpr, while_token);
+ if (name_token != nullptr) {
+ node->data.while_expr.name = token_buf(name_token);
+ }
node->data.while_expr.is_inline = is_inline;
ast_eat_token(pc, token_index, TokenIdLParen);
@@ -1726,36 +1774,53 @@ static AstNode *ast_parse_symbol(ParseContext *pc, size_t *token_index) {
}
/*
-ForExpression(body) = option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
+ForExpression(body) = option(Symbol ":") option("inline") "for" "(" Expression ")" option("|" option("*") Symbol option("," Symbol) "|") body option("else" BlockExpression(body))
*/
static AstNode *ast_parse_for_expr(ParseContext *pc, size_t *token_index, bool mandatory) {
- Token *first_token = &pc->tokens->at(*token_index);
- Token *for_token;
+ size_t orig_token_index = *token_index;
- bool is_inline;
- if (first_token->id == TokenIdKeywordInline) {
- is_inline = true;
- for_token = &pc->tokens->at(*token_index + 1);
- if (for_token->id == TokenIdKeywordFor) {
- *token_index += 2;
+ Token *name_token = nullptr;
+ Token *token = &pc->tokens->at(*token_index);
+
+ if (token->id == TokenIdSymbol) {
+ *token_index += 1;
+ Token *colon_token = &pc->tokens->at(*token_index);
+ if (colon_token->id == TokenIdColon) {
+ *token_index += 1;
+ name_token = token;
+ token = &pc->tokens->at(*token_index);
} else if (mandatory) {
- ast_expect_token(pc, first_token, TokenIdKeywordFor);
+ ast_expect_token(pc, colon_token, TokenIdColon);
zig_unreachable();
} else {
+ *token_index = orig_token_index;
return nullptr;
}
- } else if (first_token->id == TokenIdKeywordFor) {
- for_token = first_token;
- is_inline = false;
+ }
+
+ bool is_inline = false;
+ if (token->id == TokenIdKeywordInline) {
+ is_inline = true;
+ *token_index += 1;
+ token = &pc->tokens->at(*token_index);
+ }
+
+ Token *for_token;
+ if (token->id == TokenIdKeywordFor) {
+ for_token = token;
*token_index += 1;
} else if (mandatory) {
- ast_expect_token(pc, first_token, TokenIdKeywordFor);
+ ast_expect_token(pc, token, TokenIdKeywordFor);
zig_unreachable();
} else {
+ *token_index = orig_token_index;
return nullptr;
}
AstNode *node = ast_create_node(pc, NodeTypeForExpr, for_token);
+ if (name_token != nullptr) {
+ node->data.for_expr.name = token_buf(name_token);
+ }
node->data.for_expr.is_inline = is_inline;
ast_eat_token(pc, token_index, TokenIdLParen);
@@ -2082,35 +2147,6 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool
return nullptr;
}
-/*
-Label: token(Symbol) token(Colon)
-*/
-static AstNode *ast_parse_label(ParseContext *pc, size_t *token_index, bool mandatory) {
- Token *symbol_token = &pc->tokens->at(*token_index);
- if (symbol_token->id != TokenIdSymbol) {
- if (mandatory) {
- ast_expect_token(pc, symbol_token, TokenIdSymbol);
- } else {
- return nullptr;
- }
- }
-
- Token *colon_token = &pc->tokens->at(*token_index + 1);
- if (colon_token->id != TokenIdColon) {
- if (mandatory) {
- ast_expect_token(pc, colon_token, TokenIdColon);
- } else {
- return nullptr;
- }
- }
-
- *token_index += 2;
-
- AstNode *node = ast_create_node(pc, NodeTypeLabel, symbol_token);
- node->data.label.name = token_buf(symbol_token);
- return node;
-}
-
static bool statement_terminates_without_semicolon(AstNode *node) {
switch (node->type) {
case NodeTypeIfBoolExpr:
@@ -2135,7 +2171,6 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
return node->data.defer.expr->type == NodeTypeBlock;
case NodeTypeSwitchExpr:
case NodeTypeBlock:
- case NodeTypeLabel:
return true;
default:
return false;
@@ -2143,27 +2178,54 @@ static bool statement_terminates_without_semicolon(AstNode *node) {
}
/*
-Block = "{" many(Statement) option(Expression) "}"
-Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
+Block = option(Symbol ":") "{" many(Statement) "}"
+Statement = Label | VariableDeclaration ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" | ExportDecl
*/
static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mandatory) {
+ size_t orig_token_index = *token_index;
+
+ Token *name_token = nullptr;
Token *last_token = &pc->tokens->at(*token_index);
+ if (last_token->id == TokenIdSymbol) {
+ *token_index += 1;
+ Token *colon_token = &pc->tokens->at(*token_index);
+ if (colon_token->id == TokenIdColon) {
+ *token_index += 1;
+ name_token = last_token;
+ last_token = &pc->tokens->at(*token_index);
+ } else if (mandatory) {
+ ast_expect_token(pc, colon_token, TokenIdColon);
+ zig_unreachable();
+ } else {
+ *token_index = orig_token_index;
+ return nullptr;
+ }
+ }
+
if (last_token->id != TokenIdLBrace) {
if (mandatory) {
ast_expect_token(pc, last_token, TokenIdLBrace);
} else {
+ *token_index = orig_token_index;
return nullptr;
}
}
*token_index += 1;
AstNode *node = ast_create_node(pc, NodeTypeBlock, last_token);
+ if (name_token != nullptr) {
+ node->data.block.name = token_buf(name_token);
+ }
for (;;) {
- AstNode *statement_node = ast_parse_label(pc, token_index, false);
- if (!statement_node)
- statement_node = ast_parse_variable_declaration_expr(pc, token_index, false, VisibModPrivate);
+ last_token = &pc->tokens->at(*token_index);
+ if (last_token->id == TokenIdRBrace) {
+ *token_index += 1;
+ return node;
+ }
+
+ AstNode *statement_node = ast_parse_local_var_decl(pc, token_index);
if (!statement_node)
statement_node = ast_parse_defer_expr(pc, token_index);
if (!statement_node)
@@ -2171,47 +2233,28 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand
if (!statement_node)
statement_node = ast_parse_expression(pc, token_index, false);
- bool semicolon_expected = true;
- if (statement_node) {
- node->data.block.statements.append(statement_node);
- if (statement_terminates_without_semicolon(statement_node)) {
- semicolon_expected = false;
- } else {
- if (statement_node->type == NodeTypeDefer) {
- // defer without a block body requires a semicolon
- Token *token = &pc->tokens->at(*token_index);
- ast_expect_token(pc, token, TokenIdSemicolon);
- }
- }
+ if (!statement_node) {
+ ast_invalid_token_error(pc, last_token);
}
- node->data.block.last_statement_is_result_expression = statement_node && !(
- statement_node->type == NodeTypeLabel ||
- statement_node->type == NodeTypeDefer);
+ node->data.block.statements.append(statement_node);
- last_token = &pc->tokens->at(*token_index);
- if (last_token->id == TokenIdRBrace) {
- *token_index += 1;
- return node;
- } else if (!semicolon_expected) {
- continue;
- } else if (last_token->id == TokenIdSemicolon) {
- *token_index += 1;
- } else {
- ast_invalid_token_error(pc, last_token);
+ if (!statement_terminates_without_semicolon(statement_node)) {
+ ast_eat_token(pc, token_index, TokenIdSemicolon);
}
}
zig_unreachable();
}
/*
-FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("->" TypeExpr)
+FnProto = option("coldcc" | "nakedcc" | "stdcallcc" | "extern") "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("->" TypeExpr)
*/
static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
Token *first_token = &pc->tokens->at(*token_index);
Token *fn_token;
CallingConvention cc;
+ bool is_extern = false;
if (first_token->id == TokenIdKeywordColdCC) {
*token_index += 1;
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
@@ -2224,6 +2267,21 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
*token_index += 1;
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
cc = CallingConventionStdcall;
+ } else if (first_token->id == TokenIdKeywordExtern) {
+ is_extern = true;
+ *token_index += 1;
+ Token *next_token = &pc->tokens->at(*token_index);
+ if (next_token->id == TokenIdKeywordFn) {
+ fn_token = next_token;
+ *token_index += 1;
+ } else if (mandatory) {
+ ast_expect_token(pc, next_token, TokenIdKeywordFn);
+ zig_unreachable();
+ } else {
+ *token_index -= 1;
+ return nullptr;
+ }
+ cc = CallingConventionC;
} else if (first_token->id == TokenIdKeywordFn) {
fn_token = first_token;
*token_index += 1;
@@ -2238,6 +2296,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token);
node->data.fn_proto.visib_mod = visib_mod;
node->data.fn_proto.cc = cc;
+ node->data.fn_proto.is_extern = is_extern;
Token *fn_name = &pc->tokens->at(*token_index);
@@ -2259,6 +2318,14 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
ast_eat_token(pc, token_index, TokenIdRParen);
next_token = &pc->tokens->at(*token_index);
}
+ if (next_token->id == TokenIdKeywordSection) {
+ *token_index += 1;
+ ast_eat_token(pc, token_index, TokenIdLParen);
+
+ node->data.fn_proto.section_expr = ast_parse_expression(pc, token_index, true);
+ ast_eat_token(pc, token_index, TokenIdRParen);
+ next_token = &pc->tokens->at(*token_index);
+ }
if (next_token->id == TokenIdArrow) {
*token_index += 1;
node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, false);
@@ -2270,35 +2337,35 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
}
/*
-FnDef = option("inline" | "extern") FnProto Block
+FnDef = option("inline" | "export") FnProto Block
*/
static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) {
Token *first_token = &pc->tokens->at(*token_index);
bool is_inline;
- bool is_extern;
+ bool is_export;
if (first_token->id == TokenIdKeywordInline) {
*token_index += 1;
is_inline = true;
- is_extern = false;
- } else if (first_token->id == TokenIdKeywordExtern) {
+ is_export = false;
+ } else if (first_token->id == TokenIdKeywordExport) {
*token_index += 1;
- is_extern = true;
+ is_export = true;
is_inline = false;
} else {
is_inline = false;
- is_extern = false;
+ is_export = false;
}
AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory, visib_mod);
if (!fn_proto) {
- if (is_inline || is_extern) {
+ if (is_inline || is_export) {
*token_index -= 1;
}
return nullptr;
}
fn_proto->data.fn_proto.is_inline = is_inline;
- fn_proto->data.fn_proto.is_extern = is_extern;
+ fn_proto->data.fn_proto.is_export = is_export;
Token *semi_token = &pc->tokens->at(*token_index);
if (semi_token->id == TokenIdSemicolon) {
@@ -2344,7 +2411,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo
return fn_proto_node;
}
- AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
+ AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod, false, false);
if (var_decl_node) {
ast_eat_token(pc, token_index, TokenIdSemicolon);
@@ -2447,9 +2514,6 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
if (visib_tok->id == TokenIdKeywordPub) {
*token_index += 1;
visib_mod = VisibModPub;
- } else if (visib_tok->id == TokenIdKeywordExport) {
- *token_index += 1;
- visib_mod = VisibModExport;
} else {
visib_mod = VisibModPrivate;
}
@@ -2460,7 +2524,7 @@ static AstNode *ast_parse_container_decl(ParseContext *pc, size_t *token_index,
continue;
}
- AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
+ AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
if (var_decl_node) {
ast_eat_token(pc, token_index, TokenIdSemicolon);
node->data.container_decl.decls.append(var_decl_node);
@@ -2553,7 +2617,7 @@ static AstNode *ast_parse_test_decl_node(ParseContext *pc, size_t *token_index)
/*
TopLevelItem = ErrorValueDecl | CompTimeExpression(Block) | TopLevelDecl | TestDecl
-TopLevelDecl = option(VisibleMod) (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
+TopLevelDecl = option("pub") (FnDef | ExternDecl | GlobalVarDecl | UseDecl)
*/
static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, ZigList *top_level_decls) {
for (;;) {
@@ -2580,9 +2644,6 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
if (visib_tok->id == TokenIdKeywordPub) {
*token_index += 1;
visib_mod = VisibModPub;
- } else if (visib_tok->id == TokenIdKeywordExport) {
- *token_index += 1;
- visib_mod = VisibModExport;
} else {
visib_mod = VisibModPrivate;
}
@@ -2605,7 +2666,7 @@ static void ast_parse_top_level_decls(ParseContext *pc, size_t *token_index, Zig
continue;
}
- AstNode *var_decl_node = ast_parse_variable_declaration_expr(pc, token_index, false, visib_mod);
+ AstNode *var_decl_node = ast_parse_global_var_decl(pc, token_index, visib_mod);
if (var_decl_node) {
ast_eat_token(pc, token_index, TokenIdSemicolon);
top_level_decls->append(var_decl_node);
@@ -2669,6 +2730,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.fn_proto.return_type, visit, context);
visit_node_list(&node->data.fn_proto.params, visit, context);
visit_field(&node->data.fn_proto.align_expr, visit, context);
+ visit_field(&node->data.fn_proto.section_expr, visit, context);
break;
case NodeTypeFnDef:
visit_field(&node->data.fn_def.fn_proto, visit, context);
@@ -2696,6 +2758,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.variable_declaration.type, visit, context);
visit_field(&node->data.variable_declaration.expr, visit, context);
visit_field(&node->data.variable_declaration.align_expr, visit, context);
+ visit_field(&node->data.variable_declaration.section_expr, visit, context);
break;
case NodeTypeErrorValueDecl:
// none
@@ -2799,12 +2862,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.switch_range.start, visit, context);
visit_field(&node->data.switch_range.end, visit, context);
break;
- case NodeTypeLabel:
- // none
- break;
- case NodeTypeGoto:
- // none
- break;
case NodeTypeCompTime:
visit_field(&node->data.comptime_expr.expr, visit, context);
break;
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index 77d74c52ee..9bcc79ede7 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -134,6 +134,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"packed", TokenIdKeywordPacked},
{"pub", TokenIdKeywordPub},
{"return", TokenIdKeywordReturn},
+ {"section", TokenIdKeywordSection},
{"stdcallcc", TokenIdKeywordStdcallCC},
{"struct", TokenIdKeywordStruct},
{"switch", TokenIdKeywordSwitch},
@@ -1533,6 +1534,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordPacked: return "packed";
case TokenIdKeywordPub: return "pub";
case TokenIdKeywordReturn: return "return";
+ case TokenIdKeywordSection: return "section";
case TokenIdKeywordStdcallCC: return "stdcallcc";
case TokenIdKeywordStruct: return "struct";
case TokenIdKeywordSwitch: return "switch";
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index bcad977864..58a20adc1a 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -47,6 +47,7 @@ enum TokenId {
TokenIdFloatLiteral,
TokenIdIntLiteral,
TokenIdKeywordAlign,
+ TokenIdKeywordSection,
TokenIdKeywordAnd,
TokenIdKeywordAsm,
TokenIdKeywordBreak,
diff --git a/src/translate_c.cpp b/src/translate_c.cpp
index 0139774f02..407115b104 100644
--- a/src/translate_c.cpp
+++ b/src/translate_c.cpp
@@ -73,7 +73,7 @@ struct Context {
ImportTableEntry *import;
ZigList *errors;
VisibMod visib_mod;
- VisibMod export_visib_mod;
+ bool want_export;
AstNode *root;
HashMap decl_table;
HashMap macro_table;
@@ -104,10 +104,8 @@ static TransScopeRoot *trans_scope_root_create(Context *c);
static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
-static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
static TransScopeBlock *trans_scope_block_find(TransScope *scope);
-static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
@@ -173,6 +171,28 @@ static AstNode * trans_create_node(Context *c, NodeType id) {
return node;
}
+static AstNode *trans_create_node_break(Context *c, Buf *label_name, AstNode *value_node) {
+ AstNode *node = trans_create_node(c, NodeTypeBreak);
+ node->data.break_expr.name = label_name;
+ node->data.break_expr.expr = value_node;
+ return node;
+}
+
+static AstNode *trans_create_node_return(Context *c, AstNode *value_node) {
+ AstNode *node = trans_create_node(c, NodeTypeReturnExpr);
+ node->data.return_expr.kind = ReturnKindUnconditional;
+ node->data.return_expr.expr = value_node;
+ return node;
+}
+
+static AstNode *trans_create_node_if(Context *c, AstNode *cond_node, AstNode *then_node, AstNode *else_node) {
+ AstNode *node = trans_create_node(c, NodeTypeIfBoolExpr);
+ node->data.if_bool_expr.condition = cond_node;
+ node->data.if_bool_expr.then_block = then_node;
+ node->data.if_bool_expr.else_node = else_node;
+ return node;
+}
+
static AstNode *trans_create_node_float_lit(Context *c, double value) {
AstNode *node = trans_create_node(c, NodeTypeFloatLiteral);
node->data.float_literal.bigfloat = allocate(1);
@@ -257,18 +277,6 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol
return node;
}
-static AstNode *trans_create_node_goto(Context *c, Buf *label_name) {
- AstNode *goto_node = trans_create_node(c, NodeTypeGoto);
- goto_node->data.goto_expr.name = label_name;
- return goto_node;
-}
-
-static AstNode *trans_create_node_label(Context *c, Buf *label_name) {
- AstNode *label_node = trans_create_node(c, NodeTypeLabel);
- label_node->data.label.name = label_name;
- return label_node;
-}
-
static AstNode *trans_create_node_bool(Context *c, bool value) {
AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral);
bool_node->data.bool_literal.value = value;
@@ -378,8 +386,7 @@ static AstNode *trans_create_node_inline_fn(Context *c, Buf *fn_name, AstNode *r
AstNode *block = trans_create_node(c, NodeTypeBlock);
block->data.block.statements.resize(1);
- block->data.block.statements.items[0] = fn_call_node;
- block->data.block.last_statement_is_result_expression = true;
+ block->data.block.statements.items[0] = trans_create_node_return(c, fn_call_node);
fn_def->data.fn_def.body = block;
return fn_def;
@@ -1149,13 +1156,15 @@ static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransSco
} else {
// worst case
// c: lhs = rhs
- // zig: {
+ // zig: x: {
// zig: const _tmp = rhs;
// zig: lhs = _tmp;
- // zig: _tmp
+ // zig: break :x _tmp
// zig: }
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+ Buf *label_name = buf_create_from_str("x");
+ child_scope->node->data.block.name = label_name;
// const _tmp = rhs;
AstNode *rhs_node = trans_expr(c, ResultUsedYes, &child_scope->base, rhs, TransRValue);
@@ -1172,9 +1181,9 @@ static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransSco
trans_create_node_bin_op(c, lhs_node, BinOpTypeAssign,
trans_create_node_symbol(c, tmp_var_name)));
- // _tmp
- child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
- child_scope->node->data.block.last_statement_is_result_expression = true;
+ // break :x _tmp
+ AstNode *tmp_symbol_node = trans_create_node_symbol(c, tmp_var_name);
+ child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, tmp_symbol_node));
return child_scope->node;
}
@@ -1279,6 +1288,9 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS
case BO_Comma:
{
TransScopeBlock *scope_block = trans_scope_block_create(c, scope);
+ Buf *label_name = buf_create_from_str("x");
+ scope_block->node->data.block.name = label_name;
+
AstNode *lhs = trans_expr(c, ResultUsedNo, &scope_block->base, stmt->getLHS(), TransRValue);
if (lhs == nullptr)
return nullptr;
@@ -1287,9 +1299,7 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS
AstNode *rhs = trans_expr(c, result_used, &scope_block->base, stmt->getRHS(), TransRValue);
if (rhs == nullptr)
return nullptr;
- scope_block->node->data.block.statements.append(maybe_suppress_result(c, result_used, rhs));
-
- scope_block->node->data.block.last_statement_is_result_expression = true;
+ scope_block->node->data.block.statements.append(trans_create_node_break(c, label_name, maybe_suppress_result(c, result_used, rhs)));
return scope_block->node;
}
case BO_MulAssign:
@@ -1329,14 +1339,16 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
} else {
// need more complexity. worst case, this looks like this:
// c: lhs >>= rhs
- // zig: {
+ // zig: x: {
// zig: const _ref = &lhs;
// zig: *_ref = result_type(operation_type(*_ref) >> u5(rhs));
- // zig: *_ref
+ // zig: break :x *_ref
// zig: }
// where u5 is the appropriate type
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+ Buf *label_name = buf_create_from_str("x");
+ child_scope->node->data.block.name = label_name;
// const _ref = &lhs;
AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
@@ -1378,11 +1390,11 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result
child_scope->node->data.block.statements.append(assign_statement);
if (result_used == ResultUsedYes) {
- // *_ref
+ // break :x *_ref
child_scope->node->data.block.statements.append(
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)));
- child_scope->node->data.block.last_statement_is_result_expression = true;
+ trans_create_node_break(c, label_name,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name))));
}
return child_scope->node;
@@ -1403,13 +1415,15 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
} else {
// need more complexity. worst case, this looks like this:
// c: lhs += rhs
- // zig: {
+ // zig: x: {
// zig: const _ref = &lhs;
// zig: *_ref = *_ref + rhs;
- // zig: *_ref
+ // zig: break :x *_ref
// zig: }
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+ Buf *label_name = buf_create_from_str("x");
+ child_scope->node->data.block.name = label_name;
// const _ref = &lhs;
AstNode *lhs = trans_expr(c, ResultUsedYes, &child_scope->base, stmt->getLHS(), TransLValue);
@@ -1436,11 +1450,11 @@ static AstNode *trans_create_compound_assign(Context *c, ResultUsed result_used,
rhs));
child_scope->node->data.block.statements.append(assign_statement);
- // *_ref
+ // break :x *_ref
child_scope->node->data.block.statements.append(
- trans_create_node_prefix_op(c, PrefixOpDereference,
- trans_create_node_symbol(c, tmp_var_name)));
- child_scope->node->data.block.last_statement_is_result_expression = true;
+ trans_create_node_break(c, label_name,
+ trans_create_node_prefix_op(c, PrefixOpDereference,
+ trans_create_node_symbol(c, tmp_var_name))));
return child_scope->node;
}
@@ -1735,13 +1749,15 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr
}
// worst case
// c: expr++
- // zig: {
+ // zig: x: {
// zig: const _ref = &expr;
// zig: const _tmp = *_ref;
// zig: *_ref += 1;
- // zig: _tmp
+ // zig: break :x _tmp
// zig: }
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+ Buf *label_name = buf_create_from_str("x");
+ child_scope->node->data.block.name = label_name;
// const _ref = &expr;
AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
@@ -1767,9 +1783,8 @@ static AstNode *trans_create_post_crement(Context *c, ResultUsed result_used, Tr
trans_create_node_unsigned(c, 1));
child_scope->node->data.block.statements.append(assign_statement);
- // _tmp
- child_scope->node->data.block.statements.append(trans_create_node_symbol(c, tmp_var_name));
- child_scope->node->data.block.last_statement_is_result_expression = true;
+ // break :x _tmp
+ child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, trans_create_node_symbol(c, tmp_var_name)));
return child_scope->node;
}
@@ -1790,12 +1805,14 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra
}
// worst case
// c: ++expr
- // zig: {
+ // zig: x: {
// zig: const _ref = &expr;
// zig: *_ref += 1;
- // zig: *_ref
+ // zig: break :x *_ref
// zig: }
TransScopeBlock *child_scope = trans_scope_block_create(c, scope);
+ Buf *label_name = buf_create_from_str("x");
+ child_scope->node->data.block.name = label_name;
// const _ref = &expr;
AstNode *expr = trans_expr(c, ResultUsedYes, &child_scope->base, op_expr, TransLValue);
@@ -1814,11 +1831,10 @@ static AstNode *trans_create_pre_crement(Context *c, ResultUsed result_used, Tra
trans_create_node_unsigned(c, 1));
child_scope->node->data.block.statements.append(assign_statement);
- // *_ref
+ // break :x *_ref
AstNode *deref_expr = trans_create_node_prefix_op(c, PrefixOpDereference,
trans_create_node_symbol(c, ref_var_name));
- child_scope->node->data.block.statements.append(deref_expr);
- child_scope->node->data.block.last_statement_is_result_expression = true;
+ child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, deref_expr));
return child_scope->node;
}
@@ -2374,145 +2390,6 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
return while_scope->node;
}
-static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) {
- TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope);
-
- TransScopeSwitch *switch_scope;
-
- const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
- if (var_decl_stmt == nullptr) {
- switch_scope = trans_scope_switch_create(c, &block_scope->base);
- } else {
- AstNode *vars_node;
- TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
- if (var_scope == nullptr)
- return nullptr;
- if (vars_node != nullptr)
- block_scope->node->data.block.statements.append(vars_node);
- switch_scope = trans_scope_switch_create(c, var_scope);
- }
- block_scope->node->data.block.statements.append(switch_scope->switch_node);
-
- // TODO avoid name collisions
- Buf *end_label_name = buf_create_from_str("end");
- switch_scope->end_label_name = end_label_name;
-
- const Expr *cond_expr = stmt->getCond();
- assert(cond_expr != nullptr);
-
- AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
- if (expr_node == nullptr)
- return nullptr;
- switch_scope->switch_node->data.switch_expr.expr = expr_node;
-
- AstNode *body_node;
- const Stmt *body_stmt = stmt->getBody();
- if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
- if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
- block_scope->node, nullptr))
- {
- return nullptr;
- }
- } else {
- TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
- if (body_scope == nullptr)
- return nullptr;
- if (body_node != nullptr)
- block_scope->node->data.block.statements.append(body_node);
- }
-
- if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
- AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
- prong_node->data.switch_prong.expr = trans_create_node_goto(c, end_label_name);
- switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
- }
-
- // This is necessary if the last switch case "falls through" the end of the switch block
- block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name));
-
- block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name));
-
- return block_scope->node;
-}
-
-static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
- TransScope **out_scope)
-{
- *out_node = nullptr;
-
- if (stmt->getRHS() != nullptr) {
- emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
- return ErrorUnexpected;
- }
-
- TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
- assert(switch_scope != nullptr);
-
- Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
- switch_scope->case_index += 1;
-
- {
- // Add the prong
- AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
- AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
- if (item_node == nullptr)
- return ErrorUnexpected;
- prong_node->data.switch_prong.items.append(item_node);
-
- prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
-
- switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
- }
-
- TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
- scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
-
- AstNode *sub_stmt_node;
- TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
- if (new_scope == nullptr)
- return ErrorUnexpected;
- if (sub_stmt_node != nullptr)
- scope_block->node->data.block.statements.append(sub_stmt_node);
-
- *out_scope = new_scope;
- return ErrorNone;
-}
-
-static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
- TransScope **out_scope)
-{
- *out_node = nullptr;
-
- TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
- assert(switch_scope != nullptr);
-
- Buf *label_name = buf_sprintf("default");
-
- {
- // Add the prong
- AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
-
- prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name);
-
- switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
- switch_scope->found_default = true;
- }
-
- TransScopeBlock *scope_block = trans_scope_block_find(parent_scope);
- scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name));
-
-
- AstNode *sub_stmt_node;
- TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
- if (new_scope == nullptr)
- return ErrorUnexpected;
- if (sub_stmt_node != nullptr)
- scope_block->node->data.block.statements.append(sub_stmt_node);
-
- *out_scope = new_scope;
- return ErrorNone;
-}
-
static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
AstNode *loop_block_node;
TransScopeWhile *while_scope;
@@ -2590,8 +2467,7 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt
if (cur_scope->id == TransScopeIdWhile) {
return trans_create_node(c, NodeTypeBreak);
} else if (cur_scope->id == TransScopeIdSwitch) {
- TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope;
- return trans_create_node_goto(c, switch_scope->end_label_name);
+ zig_panic("TODO");
}
cur_scope = cur_scope->parent;
}
@@ -2691,12 +2567,14 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
return wrap_stmt(out_node, out_child_scope, scope,
trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
case Stmt::SwitchStmtClass:
- return wrap_stmt(out_node, out_child_scope, scope,
- trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
+ emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
+ return ErrorUnexpected;
case Stmt::CaseStmtClass:
- return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);
+ emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
+ return ErrorUnexpected;
case Stmt::DefaultStmtClass:
- return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);
+ emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
+ return ErrorUnexpected;
case Stmt::NoStmtClass:
emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
return ErrorUnexpected;
@@ -3246,7 +3124,8 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) {
StorageClass sc = fn_decl->getStorageClass();
if (sc == SC_None) {
- proto_node->data.fn_proto.visib_mod = fn_decl->hasBody() ? c->export_visib_mod : c->visib_mod;
+ proto_node->data.fn_proto.visib_mod = c->visib_mod;
+ proto_node->data.fn_proto.is_export = fn_decl->hasBody() ? c->want_export : false;
} else if (sc == SC_Extern || sc == SC_Static) {
proto_node->data.fn_proto.visib_mod = c->visib_mod;
} else if (sc == SC_PrivateExtern) {
@@ -3865,14 +3744,6 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop
return result;
}
-static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
- TransScopeSwitch *result = allocate(1);
- result->base.id = TransScopeIdSwitch;
- result->base.parent = parent_scope;
- result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
- return result;
-}
-
static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
while (scope != nullptr) {
if (scope->id == TransScopeIdBlock) {
@@ -3883,16 +3754,6 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
return nullptr;
}
-static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
- while (scope != nullptr) {
- if (scope->id == TransScopeIdSwitch) {
- return (TransScopeSwitch *)scope;
- }
- scope = scope->parent;
- }
- return nullptr;
-}
-
static void render_aliases(Context *c) {
for (size_t i = 0; i < c->aliases.length; i += 1) {
Alias *alias = &c->aliases.at(i);
@@ -4003,6 +3864,10 @@ static void render_macros(Context *c) {
}
}
+static AstNode *parse_ctok_primary_expr(Context *c, CTokenize *ctok, size_t *tok_i);
+static AstNode *parse_ctok_expr(Context *c, CTokenize *ctok, size_t *tok_i);
+static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i);
+
static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, bool negate) {
CTok *tok = &ctok->tokens.at(*tok_i);
if (tok->id == CTokIdNumLitInt) {
@@ -4030,7 +3895,7 @@ static AstNode *parse_ctok_num_lit(Context *c, CTokenize *ctok, size_t *tok_i, b
return nullptr;
}
-static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
+static AstNode *parse_ctok_primary_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
CTok *tok = &ctok->tokens.at(*tok_i);
switch (tok->id) {
case CTokIdCharLit:
@@ -4047,55 +3912,131 @@ static AstNode *parse_ctok(Context *c, CTokenize *ctok, size_t *tok_i) {
return parse_ctok_num_lit(c, ctok, tok_i, false);
case CTokIdSymbol:
{
- bool need_symbol = false;
- CTokId curr_id = CTokIdSymbol;
+ *tok_i += 1;
Buf *symbol_name = buf_create_from_buf(&tok->data.symbol);
- AstNode *curr_node = trans_create_node_symbol(c, symbol_name);
- AstNode *parent_node = curr_node;
- do {
- *tok_i += 1;
- CTok* curr_tok = &ctok->tokens.at(*tok_i);
- if (need_symbol) {
- if (curr_tok->id == CTokIdSymbol) {
- symbol_name = buf_create_from_buf(&curr_tok->data.symbol);
- curr_node = trans_create_node_field_access(c, parent_node, buf_create_from_buf(symbol_name));
- parent_node = curr_node;
- need_symbol = false;
- } else {
- return nullptr;
- }
- } else {
- if (curr_tok->id == CTokIdDot) {
- need_symbol = true;
- continue;
- } else {
- break;
- }
- }
- } while (curr_id != CTokIdEOF);
- return curr_node;
+ return trans_create_node_symbol(c, symbol_name);
}
case CTokIdLParen:
{
*tok_i += 1;
- AstNode *inner_node = parse_ctok(c, ctok, tok_i);
+ AstNode *inner_node = parse_ctok_expr(c, ctok, tok_i);
+ if (inner_node == nullptr) {
+ return nullptr;
+ }
CTok *next_tok = &ctok->tokens.at(*tok_i);
- if (next_tok->id != CTokIdRParen) {
+ if (next_tok->id == CTokIdRParen) {
+ *tok_i += 1;
+ return inner_node;
+ }
+
+ AstNode *node_to_cast = parse_ctok_expr(c, ctok, tok_i);
+ if (node_to_cast == nullptr) {
+ return nullptr;
+ }
+
+ CTok *next_tok2 = &ctok->tokens.at(*tok_i);
+ if (next_tok2->id != CTokIdRParen) {
return nullptr;
}
*tok_i += 1;
- return inner_node;
+
+
+ //if (@typeId(@typeOf(x)) == @import("builtin").TypeId.Pointer)
+ // @ptrCast(dest, x)
+ //else if (@typeId(@typeOf(x)) == @import("builtin").TypeId.Integer)
+ // @intToPtr(dest, x)
+ //else
+ // (dest)(x)
+
+ AstNode *import_builtin = trans_create_node_builtin_fn_call_str(c, "import");
+ import_builtin->data.fn_call_expr.params.append(trans_create_node_str_lit_non_c(c, buf_create_from_str("builtin")));
+ AstNode *typeid_type = trans_create_node_field_access_str(c, import_builtin, "TypeId");
+ AstNode *typeid_pointer = trans_create_node_field_access_str(c, typeid_type, "Pointer");
+ AstNode *typeid_integer = trans_create_node_field_access_str(c, typeid_type, "Int");
+ AstNode *typeof_x = trans_create_node_builtin_fn_call_str(c, "typeOf");
+ typeof_x->data.fn_call_expr.params.append(node_to_cast);
+ AstNode *typeid_value = trans_create_node_builtin_fn_call_str(c, "typeId");
+ typeid_value->data.fn_call_expr.params.append(typeof_x);
+
+ AstNode *outer_if_cond = trans_create_node_bin_op(c, typeid_value, BinOpTypeCmpEq, typeid_pointer);
+ AstNode *inner_if_cond = trans_create_node_bin_op(c, typeid_value, BinOpTypeCmpEq, typeid_integer);
+ AstNode *inner_if_then = trans_create_node_builtin_fn_call_str(c, "intToPtr");
+ inner_if_then->data.fn_call_expr.params.append(inner_node);
+ inner_if_then->data.fn_call_expr.params.append(node_to_cast);
+ AstNode *inner_if_else = trans_create_node_cast(c, inner_node, node_to_cast);
+ AstNode *inner_if = trans_create_node_if(c, inner_if_cond, inner_if_then, inner_if_else);
+ AstNode *outer_if_then = trans_create_node_builtin_fn_call_str(c, "ptrCast");
+ outer_if_then->data.fn_call_expr.params.append(inner_node);
+ outer_if_then->data.fn_call_expr.params.append(node_to_cast);
+ return trans_create_node_if(c, outer_if_cond, outer_if_then, inner_if);
}
case CTokIdDot:
case CTokIdEOF:
case CTokIdRParen:
+ case CTokIdAsterisk:
+ case CTokIdBang:
+ case CTokIdTilde:
// not able to make sense of this
return nullptr;
}
zig_unreachable();
}
+static AstNode *parse_ctok_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
+ return parse_ctok_prefix_op_expr(c, ctok, tok_i);
+}
+
+static AstNode *parse_ctok_suffix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
+ AstNode *node = parse_ctok_primary_expr(c, ctok, tok_i);
+ if (node == nullptr)
+ return nullptr;
+
+ while (true) {
+ CTok *first_tok = &ctok->tokens.at(*tok_i);
+ if (first_tok->id == CTokIdDot) {
+ *tok_i += 1;
+
+ CTok *name_tok = &ctok->tokens.at(*tok_i);
+ if (name_tok->id != CTokIdSymbol) {
+ return nullptr;
+ }
+ *tok_i += 1;
+
+ node = trans_create_node_field_access(c, node, buf_create_from_buf(&name_tok->data.symbol));
+ } else if (first_tok->id == CTokIdAsterisk) {
+ *tok_i += 1;
+
+ node = trans_create_node_addr_of(c, false, false, node);
+ } else {
+ return node;
+ }
+ }
+}
+
+static PrefixOp ctok_to_prefix_op(CTok *token) {
+ switch (token->id) {
+ case CTokIdBang: return PrefixOpBoolNot;
+ case CTokIdMinus: return PrefixOpNegation;
+ case CTokIdTilde: return PrefixOpBinNot;
+ case CTokIdAsterisk: return PrefixOpDereference;
+ default: return PrefixOpInvalid;
+ }
+}
+static AstNode *parse_ctok_prefix_op_expr(Context *c, CTokenize *ctok, size_t *tok_i) {
+ CTok *op_tok = &ctok->tokens.at(*tok_i);
+ PrefixOp prefix_op = ctok_to_prefix_op(op_tok);
+ if (prefix_op == PrefixOpInvalid) {
+ return parse_ctok_suffix_op_expr(c, ctok, tok_i);
+ }
+ *tok_i += 1;
+
+ AstNode *prefix_op_expr = parse_ctok_prefix_op_expr(c, ctok, tok_i);
+ if (prefix_op_expr == nullptr)
+ return nullptr;
+ return trans_create_node_prefix_op(c, prefix_op, prefix_op_expr);
+}
+
static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *char_ptr) {
tokenize_c_macro(ctok, (const uint8_t *)char_ptr);
@@ -4108,7 +4049,7 @@ static void process_macro(Context *c, CTokenize *ctok, Buf *name, const char *ch
assert(name_tok->id == CTokIdSymbol && buf_eql_buf(&name_tok->data.symbol, name));
tok_i += 1;
- AstNode *result_node = parse_ctok(c, ctok, &tok_i);
+ AstNode *result_node = parse_ctok_suffix_op_expr(c, ctok, &tok_i);
if (result_node == nullptr) {
return;
}
@@ -4189,10 +4130,10 @@ int parse_h_file(ImportTableEntry *import, ZigList *errors, const ch
c->errors = errors;
if (buf_ends_with_str(buf_create_from_str(target_file), ".h")) {
c->visib_mod = VisibModPub;
- c->export_visib_mod = VisibModPub;
+ c->want_export = false;
} else {
c->visib_mod = VisibModPub;
- c->export_visib_mod = VisibModExport;
+ c->want_export = true;
}
c->decl_table.init(8);
c->macro_table.init(8);
diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp
index 5623002197..bd3eadbee9 100644
--- a/src/zig_llvm.cpp
+++ b/src/zig_llvm.cpp
@@ -175,12 +175,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
- unsigned NumArgs, unsigned CC, bool always_inline, const char *Name)
+ unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name)
{
CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);
call_inst->setCallingConv(CC);
- if (always_inline) {
- call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
+ switch (fn_inline) {
+ case ZigLLVM_FnInlineAuto:
+ break;
+ case ZigLLVM_FnInlineAlways:
+ call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
+ break;
+ case ZigLLVM_FnInlineNever:
+ call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline);
+ break;
}
return wrap(unwrap(B)->Insert(call_inst));
}
diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp
index 2f742566f4..f335a8955a 100644
--- a/src/zig_llvm.hpp
+++ b/src/zig_llvm.hpp
@@ -45,8 +45,13 @@ enum ZigLLVM_EmitOutputType {
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
+enum ZigLLVM_FnInline {
+ ZigLLVM_FnInlineAuto,
+ ZigLLVM_FnInlineAlways,
+ ZigLLVM_FnInlineNever,
+};
LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
- unsigned NumArgs, unsigned CC, bool always_inline, const char *Name);
+ unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name);
LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
diff --git a/std/array_list.zig b/std/array_list.zig
index 65b7e023e2..04db4dd280 100644
--- a/std/array_list.zig
+++ b/std/array_list.zig
@@ -3,42 +3,46 @@ const assert = debug.assert;
const mem = @import("mem.zig");
const Allocator = mem.Allocator;
-pub fn ArrayList(comptime T: type) -> type{
- struct {
+pub fn ArrayList(comptime T: type) -> type {
+ return AlignedArrayList(T, @alignOf(T));
+}
+
+pub fn AlignedArrayList(comptime T: type, comptime A: u29) -> type{
+ return struct {
const Self = this;
/// Use toSlice instead of slicing this directly, because if you don't
/// specify the end position of the slice, this will potentially give
/// you uninitialized memory.
- items: []T,
+ items: []align(A) T,
len: usize,
allocator: &Allocator,
/// Deinitialize with `deinit` or use `toOwnedSlice`.
pub fn init(allocator: &Allocator) -> Self {
- Self {
- .items = []T{},
+ return Self {
+ .items = []align(A) T{},
.len = 0,
.allocator = allocator,
- }
+ };
}
pub fn deinit(l: &Self) {
l.allocator.free(l.items);
}
- pub fn toSlice(l: &Self) -> []T {
+ pub fn toSlice(l: &Self) -> []align(A) T {
return l.items[0..l.len];
}
- pub fn toSliceConst(l: &const Self) -> []const T {
+ pub fn toSliceConst(l: &const Self) -> []align(A) const T {
return l.items[0..l.len];
}
/// ArrayList takes ownership of the passed in slice. The slice must have been
/// allocated with `allocator`.
/// Deinitialize with `deinit` or use `toOwnedSlice`.
- pub fn fromOwnedSlice(allocator: &Allocator, slice: []T) -> Self {
+ pub fn fromOwnedSlice(allocator: &Allocator, slice: []align(A) T) -> Self {
return Self {
.items = slice,
.len = slice.len,
@@ -47,9 +51,9 @@ pub fn ArrayList(comptime T: type) -> type{
}
/// The caller owns the returned memory. ArrayList becomes empty.
- pub fn toOwnedSlice(self: &Self) -> []T {
+ pub fn toOwnedSlice(self: &Self) -> []align(A) T {
const allocator = self.allocator;
- const result = allocator.shrink(T, self.items, self.len);
+ const result = allocator.alignedShrink(T, A, self.items, self.len);
*self = init(allocator);
return result;
}
@@ -59,7 +63,7 @@ pub fn ArrayList(comptime T: type) -> type{
*new_item_ptr = *item;
}
- pub fn appendSlice(l: &Self, items: []const T) -> %void {
+ pub fn appendSlice(l: &Self, items: []align(A) const T) -> %void {
%return l.ensureCapacity(l.len + items.len);
mem.copy(T, l.items[l.len..], items);
l.len += items.len;
@@ -82,7 +86,7 @@ pub fn ArrayList(comptime T: type) -> type{
better_capacity += better_capacity / 2 + 8;
if (better_capacity >= new_capacity) break;
}
- l.items = %return l.allocator.realloc(T, l.items, better_capacity);
+ l.items = %return l.allocator.alignedRealloc(T, A, l.items, better_capacity);
}
pub fn addOne(l: &Self) -> %&T {
@@ -97,7 +101,13 @@ pub fn ArrayList(comptime T: type) -> type{
self.len -= 1;
return self.items[self.len];
}
- }
+
+ pub fn popOrNull(self: &Self) -> ?T {
+ if (self.len == 0)
+ return null;
+ return self.pop();
+ }
+ };
}
test "basic ArrayList test" {
diff --git a/std/base64.zig b/std/base64.zig
index 25e438c4fb..840643b565 100644
--- a/std/base64.zig
+++ b/std/base64.zig
@@ -193,7 +193,7 @@ pub const Base64DecoderWithIgnore = struct {
/// Decoding more data than can fit in dest results in error.OutputTooSmall. See also ::calcSizeUpperBound.
/// Returns the number of bytes writen to dest.
pub fn decode(decoder_with_ignore: &const Base64DecoderWithIgnore, dest: []u8, source: []const u8) -> %usize {
- const decoder = &const decoder_with_ignore.decoder;
+ const decoder = &decoder_with_ignore.decoder;
var src_cursor: usize = 0;
var dest_cursor: usize = 0;
diff --git a/std/buf_map.zig b/std/buf_map.zig
index e913b8765d..2a9bd77660 100644
--- a/std/buf_map.zig
+++ b/std/buf_map.zig
@@ -42,6 +42,11 @@ pub const BufMap = struct {
}
}
+ pub fn get(self: &BufMap, key: []const u8) -> ?[]const u8 {
+ const entry = self.hash_map.get(key) ?? return null;
+ return entry.value;
+ }
+
pub fn delete(self: &BufMap, key: []const u8) {
const entry = self.hash_map.remove(key) ?? return;
self.free(entry.key);
diff --git a/std/buffer.zig b/std/buffer.zig
index 96abaeb762..4f5d281f48 100644
--- a/std/buffer.zig
+++ b/std/buffer.zig
@@ -30,9 +30,9 @@ pub const Buffer = struct {
/// * ::replaceContentsBuffer
/// * ::resize
pub fn initNull(allocator: &Allocator) -> Buffer {
- Buffer {
+ return Buffer {
.list = ArrayList(u8).init(allocator),
- }
+ };
}
/// Must deinitialize with deinit.
@@ -98,14 +98,17 @@ pub const Buffer = struct {
mem.copy(u8, self.list.toSlice()[old_len..], m);
}
+ // TODO: remove, use OutStream for this
pub fn appendFormat(self: &Buffer, comptime format: []const u8, args: ...) -> %void {
return fmt.format(self, append, format, args);
}
+ // TODO: remove, use OutStream for this
pub fn appendByte(self: &Buffer, byte: u8) -> %void {
return self.appendByteNTimes(byte, 1);
}
+ // TODO: remove, use OutStream for this
pub fn appendByteNTimes(self: &Buffer, byte: u8, count: usize) -> %void {
var prev_size: usize = self.len();
%return self.resize(prev_size + count);
@@ -117,7 +120,7 @@ pub const Buffer = struct {
}
pub fn eql(self: &const Buffer, m: []const u8) -> bool {
- mem.eql(u8, self.toSliceConst(), m)
+ return mem.eql(u8, self.toSliceConst(), m);
}
pub fn startsWith(self: &const Buffer, m: []const u8) -> bool {
@@ -136,6 +139,11 @@ pub const Buffer = struct {
%return self.resize(m.len);
mem.copy(u8, self.list.toSlice(), m);
}
+
+ /// For passing to C functions.
+ pub fn ptr(self: &const Buffer) -> &u8 {
+ return self.list.items.ptr;
+ }
};
test "simple Buffer" {
diff --git a/std/build.zig b/std/build.zig
index 9bdc4b3076..0a23a77f80 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -221,11 +221,11 @@ pub const Builder = struct {
}
pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) -> Version {
- Version {
+ return Version {
.major = major,
.minor = minor,
.patch = patch,
- }
+ };
}
pub fn addCIncludePath(self: &Builder, path: []const u8) {
@@ -432,16 +432,16 @@ pub const Builder = struct {
const release_safe = self.option(bool, "release-safe", "optimizations on and safety on") ?? false;
const release_fast = self.option(bool, "release-fast", "optimizations on and safety off") ?? false;
- const mode = if (release_safe and !release_fast) {
+ const mode = if (release_safe and !release_fast)
builtin.Mode.ReleaseSafe
- } else if (release_fast and !release_safe) {
+ else if (release_fast and !release_safe)
builtin.Mode.ReleaseFast
- } else if (!release_fast and !release_safe) {
+ else if (!release_fast and !release_safe)
builtin.Mode.Debug
- } else {
+ else x: {
warn("Both -Drelease-safe and -Drelease-fast specified");
self.markInvalidUserInput();
- builtin.Mode.Debug
+ break :x builtin.Mode.Debug;
};
self.release_mode = mode;
return mode;
@@ -506,7 +506,7 @@ pub const Builder = struct {
}
fn typeToEnum(comptime T: type) -> TypeId {
- switch (@typeId(T)) {
+ return switch (@typeId(T)) {
builtin.TypeId.Int => TypeId.Int,
builtin.TypeId.Float => TypeId.Float,
builtin.TypeId.Bool => TypeId.Bool,
@@ -515,7 +515,7 @@ pub const Builder = struct {
[]const []const u8 => TypeId.List,
else => @compileError("Unsupported type: " ++ @typeName(T)),
},
- }
+ };
}
fn markInvalidUserInput(self: &Builder) {
@@ -590,8 +590,7 @@ pub const Builder = struct {
return error.UncleanExit;
},
- };
-
+ }
}
pub fn makePath(self: &Builder, path: []const u8) -> %void {
@@ -662,13 +661,70 @@ pub const Builder = struct {
if (builtin.environ == builtin.Environ.msvc) {
return "cl.exe";
} else {
- return os.getEnvVarOwned(self.allocator, "CC") %% |err| {
- if (err == error.EnvironmentVariableNotFound) {
+ return os.getEnvVarOwned(self.allocator, "CC") %% |err|
+ if (err == error.EnvironmentVariableNotFound)
([]const u8)("cc")
- } else {
- debug.panic("Unable to get environment variable: {}", err);
+ else
+ debug.panic("Unable to get environment variable: {}", err)
+ ;
+ }
+ }
+
+ pub fn findProgram(self: &Builder, names: []const []const u8, paths: []const []const u8) -> %[]const u8 {
+ const exe_extension = (Target { .Native = {}}).exeFileExt();
+ if (self.env_map.get("PATH")) |PATH| {
+ for (names) |name| {
+ if (os.path.isAbsolute(name)) {
+ return name;
}
- };
+ var it = mem.split(PATH, []u8{os.path.delimiter});
+ while (it.next()) |path| {
+ const full_path = %return os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
+ if (os.path.real(self.allocator, full_path)) |real_path| {
+ return real_path;
+ } else |_| {
+ continue;
+ }
+ }
+ }
+ }
+ for (names) |name| {
+ if (os.path.isAbsolute(name)) {
+ return name;
+ }
+ for (paths) |path| {
+ const full_path = %return os.path.join(self.allocator, path, self.fmt("{}{}", name, exe_extension));
+ if (os.path.real(self.allocator, full_path)) |real_path| {
+ return real_path;
+ } else |_| {
+ continue;
+ }
+ }
+ }
+ return error.FileNotFound;
+ }
+
+ pub fn exec(self: &Builder, argv: []const []const u8) -> []u8 {
+ const max_output_size = 100 * 1024;
+ const result = os.ChildProcess.exec(self.allocator, argv, null, null, max_output_size) %% |err| {
+ std.debug.panic("Unable to spawn {}: {}", argv[0], @errorName(err));
+ };
+ switch (result.term) {
+ os.ChildProcess.Term.Exited => |code| {
+ if (code != 0) {
+ warn("The following command exited with error code {}:\n", code);
+ printCmd(null, argv);
+ warn("stderr:{}\n", result.stderr);
+ std.debug.panic("command failed");
+ }
+ return result.stdout;
+ },
+ else => {
+ warn("The following command terminated unexpectedly:\n");
+ printCmd(null, argv);
+ warn("stderr:{}\n", result.stderr);
+ std.debug.panic("command failed");
+ },
}
}
};
@@ -755,6 +811,7 @@ pub const LibExeObjStep = struct {
is_zig: bool,
cflags: ArrayList([]const u8),
include_dirs: ArrayList([]const u8),
+ lib_paths: ArrayList([]const u8),
disable_libc: bool,
frameworks: BufSet,
@@ -844,7 +901,7 @@ pub const LibExeObjStep = struct {
.kind = kind,
.root_src = root_src,
.name = name,
- .target = Target { .Native = {} },
+ .target = Target.Native,
.linker_script = null,
.link_libs = BufSet.init(builder.allocator),
.frameworks = BufSet.init(builder.allocator),
@@ -865,6 +922,7 @@ pub const LibExeObjStep = struct {
.cflags = ArrayList([]const u8).init(builder.allocator),
.source_files = undefined,
.include_dirs = ArrayList([]const u8).init(builder.allocator),
+ .lib_paths = ArrayList([]const u8).init(builder.allocator),
.object_src = undefined,
.disable_libc = true,
};
@@ -879,7 +937,7 @@ pub const LibExeObjStep = struct {
.kind = kind,
.version = *version,
.static = static,
- .target = Target { .Native = {} },
+ .target = Target.Native,
.cflags = ArrayList([]const u8).init(builder.allocator),
.source_files = ArrayList([]const u8).init(builder.allocator),
.object_files = ArrayList([]const u8).init(builder.allocator),
@@ -888,6 +946,7 @@ pub const LibExeObjStep = struct {
.frameworks = BufSet.init(builder.allocator),
.full_path_libs = ArrayList([]const u8).init(builder.allocator),
.include_dirs = ArrayList([]const u8).init(builder.allocator),
+ .lib_paths = ArrayList([]const u8).init(builder.allocator),
.output_path = null,
.out_filename = undefined,
.major_only_filename = undefined,
@@ -1018,11 +1077,10 @@ pub const LibExeObjStep = struct {
}
pub fn getOutputPath(self: &LibExeObjStep) -> []const u8 {
- if (self.output_path) |output_path| {
+ return if (self.output_path) |output_path|
output_path
- } else {
- %%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename)
- }
+ else
+ %%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_filename);
}
pub fn setOutputHPath(self: &LibExeObjStep, file_path: []const u8) {
@@ -1035,11 +1093,10 @@ pub const LibExeObjStep = struct {
}
pub fn getOutputHPath(self: &LibExeObjStep) -> []const u8 {
- if (self.output_h_path) |output_h_path| {
+ 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)
- }
+ else
+ %%os.path.join(self.builder.allocator, self.builder.cache_root, self.out_h_filename);
}
pub fn addAssemblyFile(self: &LibExeObjStep, path: []const u8) {
@@ -1069,11 +1126,14 @@ pub const LibExeObjStep = struct {
%%self.include_dirs.append(self.builder.cache_root);
}
- // TODO put include_dirs in zig command line
pub fn addIncludeDir(self: &LibExeObjStep, path: []const u8) {
%%self.include_dirs.append(path);
}
+ pub fn addLibPath(self: &LibExeObjStep, path: []const u8) {
+ %%self.lib_paths.append(path);
+ }
+
pub fn addPackagePath(self: &LibExeObjStep, name: []const u8, pkg_index_path: []const u8) {
assert(self.is_zig);
@@ -1222,6 +1282,11 @@ pub const LibExeObjStep = struct {
%%zig_args.append("--pkg-end");
}
+ for (self.include_dirs.toSliceConst()) |include_path| {
+ %%zig_args.append("-isystem");
+ %%zig_args.append(self.builder.pathFromRoot(include_path));
+ }
+
for (builder.include_paths.toSliceConst()) |include_path| {
%%zig_args.append("-isystem");
%%zig_args.append(builder.pathFromRoot(include_path));
@@ -1232,6 +1297,11 @@ pub const LibExeObjStep = struct {
%%zig_args.append(rpath);
}
+ for (self.lib_paths.toSliceConst()) |lib_path| {
+ %%zig_args.append("--library-path");
+ %%zig_args.append(lib_path);
+ }
+
for (builder.lib_paths.toSliceConst()) |lib_path| {
%%zig_args.append("--library-path");
%%zig_args.append(lib_path);
@@ -1544,7 +1614,7 @@ pub const TestStep = struct {
pub fn init(builder: &Builder, root_src: []const u8) -> TestStep {
const step_name = builder.fmt("test {}", root_src);
- TestStep {
+ return TestStep {
.step = Step.init(step_name, builder.allocator, make),
.builder = builder,
.root_src = root_src,
@@ -1555,7 +1625,7 @@ pub const TestStep = struct {
.link_libs = BufSet.init(builder.allocator),
.target = Target { .Native = {} },
.exec_cmd_args = null,
- }
+ };
}
pub fn setVerbose(self: &TestStep, value: bool) {
@@ -1862,16 +1932,16 @@ pub const Step = struct {
done_flag: bool,
pub fn init(name: []const u8, allocator: &Allocator, makeFn: fn (&Step)->%void) -> Step {
- Step {
+ return Step {
.name = name,
.makeFn = makeFn,
.dependencies = ArrayList(&Step).init(allocator),
.loop_flag = false,
.done_flag = false,
- }
+ };
}
pub fn initNoOp(name: []const u8, allocator: &Allocator) -> Step {
- init(name, allocator, makeNoOp)
+ return init(name, allocator, makeNoOp);
}
pub fn make(self: &Step) -> %void {
diff --git a/std/c/index.zig b/std/c/index.zig
index 2ac867ee71..e04b990633 100644
--- a/std/c/index.zig
+++ b/std/c/index.zig
@@ -48,3 +48,4 @@ pub extern "c" fn setregid(rgid: c_uint, egid: c_uint) -> c_int;
pub extern "c" fn malloc(usize) -> ?&c_void;
pub extern "c" fn realloc(&c_void, usize) -> ?&c_void;
pub extern "c" fn free(&c_void);
+pub extern "c" fn posix_memalign(memptr: &&c_void, alignment: usize, size: usize) -> c_int;
diff --git a/std/cstr.zig b/std/cstr.zig
index e29f90fc01..445f7ab892 100644
--- a/std/cstr.zig
+++ b/std/cstr.zig
@@ -17,7 +17,7 @@ pub fn cmp(a: &const u8, b: &const u8) -> i8 {
return -1;
} else {
return 0;
- };
+ }
}
pub fn toSliceConst(str: &const u8) -> []const u8 {
diff --git a/std/debug.zig b/std/debug.zig
index a2bea9eddd..a683b5ae15 100644
--- a/std/debug.zig
+++ b/std/debug.zig
@@ -32,7 +32,7 @@ fn getStderrStream() -> %&io.OutStream {
const st = &stderr_file_out_stream.stream;
stderr_stream = st;
return st;
- };
+ }
}
/// Tries to print a stack trace to stderr, unbuffered, and ignores any error returned.
@@ -52,9 +52,9 @@ pub fn assert(ok: bool) {
// we insert an explicit call to @panic instead of unreachable.
// TODO we should use `assertOrPanic` in tests and remove this logic.
if (builtin.is_test) {
- @panic("assertion failure")
+ @panic("assertion failure");
} else {
- unreachable // assertion failure
+ unreachable; // assertion failure
}
}
}
@@ -96,8 +96,6 @@ const WHITE = "\x1b[37;1m";
const DIM = "\x1b[2m";
const RESET = "\x1b[0m";
-pub var user_main_fn: ?fn() -> %void = null;
-
error PathNotFound;
error InvalidDebugInfo;
@@ -113,6 +111,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
.debug_abbrev = undefined,
.debug_str = undefined,
.debug_line = undefined,
+ .debug_ranges = null,
.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator),
.compile_unit_list = ArrayList(CompileUnit).init(allocator),
};
@@ -127,6 +126,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
st.debug_abbrev = (%return st.elf.findSection(".debug_abbrev")) ?? return error.MissingDebugInfo;
st.debug_str = (%return st.elf.findSection(".debug_str")) ?? return error.MissingDebugInfo;
st.debug_line = (%return st.elf.findSection(".debug_line")) ?? return error.MissingDebugInfo;
+ st.debug_ranges = (%return st.elf.findSection(".debug_ranges"));
%return scanAllCompileUnits(st);
var ignored_count: usize = 0;
@@ -144,7 +144,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
// at compile time. I'll call it issue #313
const ptr_hex = if (@sizeOf(usize) == 4) "0x{x8}" else "0x{x16}";
- const compile_unit = findCompileUnit(st, return_address) ?? {
+ const compile_unit = findCompileUnit(st, return_address) %% {
%return out_stream.print("???:?:?: " ++ DIM ++ ptr_hex ++ " in ??? (???)" ++ RESET ++ "\n ???\n\n",
return_address);
continue;
@@ -175,7 +175,7 @@ pub fn writeStackTrace(out_stream: &io.OutStream, allocator: &mem.Allocator, tty
return_address, compile_unit_name);
},
else => return err,
- };
+ }
}
},
builtin.ObjectFormat.coff => {
@@ -233,6 +233,7 @@ const ElfStackTrace = struct {
debug_abbrev: &elf.SectionHeader,
debug_str: &elf.SectionHeader,
debug_line: &elf.SectionHeader,
+ debug_ranges: ?&elf.SectionHeader,
abbrev_table_list: ArrayList(AbbrevTableHeader),
compile_unit_list: ArrayList(CompileUnit),
@@ -333,6 +334,15 @@ const Die = struct {
};
}
+ fn getAttrSecOffset(self: &const Die, id: u64) -> %u64 {
+ const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
+ return switch (*form_value) {
+ FormValue.Const => |value| value.asUnsignedLe(),
+ FormValue.SecOffset => |value| value,
+ else => error.InvalidDebugInfo,
+ };
+ }
+
fn getAttrUnsignedLe(self: &const Die, id: u64) -> %u64 {
const form_value = self.getAttr(id) ?? return error.MissingDebugInfo;
return switch (*form_value) {
@@ -347,7 +357,7 @@ const Die = struct {
FormValue.String => |value| value,
FormValue.StrPtr => |offset| getString(st, offset),
else => error.InvalidDebugInfo,
- }
+ };
}
};
@@ -393,7 +403,7 @@ const LineNumberProgram = struct {
pub fn init(is_stmt: bool, include_dirs: []const []const u8,
file_entries: &ArrayList(FileEntry), target_address: usize) -> LineNumberProgram
{
- LineNumberProgram {
+ return LineNumberProgram {
.address = 0,
.file = 1,
.line = 1,
@@ -411,7 +421,7 @@ const LineNumberProgram = struct {
.prev_is_stmt = undefined,
.prev_basic_block = undefined,
.prev_end_sequence = undefined,
- }
+ };
}
pub fn checkLineMatch(self: &LineNumberProgram) -> %?LineInfo {
@@ -420,14 +430,11 @@ const LineNumberProgram = struct {
return error.MissingDebugInfo;
} else if (self.prev_file - 1 >= self.file_entries.len) {
return error.InvalidDebugInfo;
- } else {
- &self.file_entries.items[self.prev_file - 1]
- };
+ } else &self.file_entries.items[self.prev_file - 1];
+
const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
return error.InvalidDebugInfo;
- } else {
- self.include_dirs[file_entry.dir_index]
- };
+ } else self.include_dirs[file_entry.dir_index];
const file_name = %return os.path.join(self.file_entries.allocator, dir_name, file_entry.file_name);
%defer self.file_entries.allocator.free(file_name);
return LineInfo {
@@ -484,28 +491,21 @@ fn parseFormValueBlock(allocator: &mem.Allocator, in_stream: &io.InStream, size:
}
fn parseFormValueConstant(allocator: &mem.Allocator, in_stream: &io.InStream, signed: bool, size: usize) -> %FormValue {
- FormValue { .Const = Constant {
+ return FormValue { .Const = Constant {
.signed = signed,
.payload = %return readAllocBytes(allocator, in_stream, size),
- }}
+ }};
}
fn parseFormValueDwarfOffsetSize(in_stream: &io.InStream, is_64: bool) -> %u64 {
- return if (is_64) {
- %return in_stream.readIntLe(u64)
- } else {
- u64(%return in_stream.readIntLe(u32))
- };
+ return if (is_64) %return in_stream.readIntLe(u64)
+ else u64(%return in_stream.readIntLe(u32)) ;
}
fn parseFormValueTargetAddrSize(in_stream: &io.InStream) -> %u64 {
- return if (@sizeOf(usize) == 4) {
- u64(%return in_stream.readIntLe(u32))
- } else if (@sizeOf(usize) == 8) {
- %return in_stream.readIntLe(u64)
- } else {
- unreachable;
- };
+ return if (@sizeOf(usize) == 4) u64(%return in_stream.readIntLe(u32))
+ else if (@sizeOf(usize) == 8) %return in_stream.readIntLe(u64)
+ else unreachable;
}
fn parseFormValueRefLen(allocator: &mem.Allocator, in_stream: &io.InStream, size: usize) -> %FormValue {
@@ -524,9 +524,9 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
- DW.FORM_block => {
+ DW.FORM_block => x: {
const block_len = %return readULeb128(in_stream);
- parseFormValueBlockLen(allocator, in_stream, block_len)
+ return parseFormValueBlockLen(allocator, in_stream, block_len);
},
DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
@@ -535,7 +535,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
DW.FORM_udata, DW.FORM_sdata => {
const block_len = %return readULeb128(in_stream);
const signed = form_id == DW.FORM_sdata;
- parseFormValueConstant(allocator, in_stream, signed, block_len)
+ return parseFormValueConstant(allocator, in_stream, signed, block_len);
},
DW.FORM_exprloc => {
const size = %return readULeb128(in_stream);
@@ -552,7 +552,7 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, u64),
DW.FORM_ref_udata => {
const ref_len = %return readULeb128(in_stream);
- parseFormValueRefLen(allocator, in_stream, ref_len)
+ return parseFormValueRefLen(allocator, in_stream, ref_len);
},
DW.FORM_ref_addr => FormValue { .RefAddr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) },
@@ -562,10 +562,10 @@ fn parseFormValue(allocator: &mem.Allocator, in_stream: &io.InStream, form_id: u
DW.FORM_strp => FormValue { .StrPtr = %return parseFormValueDwarfOffsetSize(in_stream, is_64) },
DW.FORM_indirect => {
const child_form_id = %return readULeb128(in_stream);
- parseFormValue(allocator, in_stream, child_form_id, is_64)
+ return parseFormValue(allocator, in_stream, child_form_id, is_64);
},
else => error.InvalidDebugInfo,
- }
+ };
}
fn parseAbbrevTable(st: &ElfStackTrace) -> %AbbrevTable {
@@ -842,11 +842,9 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
const version = %return in_stream.readInt(st.elf.endian, u16);
if (version < 2 or version > 5) return error.InvalidDebugInfo;
- const debug_abbrev_offset = if (is_64) {
- %return in_stream.readInt(st.elf.endian, u64)
- } else {
- %return in_stream.readInt(st.elf.endian, u32)
- };
+ const debug_abbrev_offset =
+ if (is_64) %return in_stream.readInt(st.elf.endian, u64)
+ else %return in_stream.readInt(st.elf.endian, u32);
const address_size = %return in_stream.readByte();
if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
@@ -862,28 +860,28 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
if (compile_unit_die.tag_id != DW.TAG_compile_unit)
return error.InvalidDebugInfo;
- const pc_range = {
+ const pc_range = x: {
if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {
if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {
const pc_end = switch (*high_pc_value) {
FormValue.Address => |value| value,
- FormValue.Const => |value| {
+ FormValue.Const => |value| b: {
const offset = %return value.asUnsignedLe();
- low_pc + offset
+ break :b (low_pc + offset);
},
else => return error.InvalidDebugInfo,
};
- PcRange {
+ break :x PcRange {
.start = low_pc,
.end = pc_end,
- }
+ };
} else {
- null
+ break :x null;
}
} else |err| {
if (err != error.MissingDebugInfo)
return err;
- null
+ break :x null;
}
};
@@ -900,25 +898,51 @@ fn scanAllCompileUnits(st: &ElfStackTrace) -> %void {
}
}
-fn findCompileUnit(st: &ElfStackTrace, target_address: u64) -> ?&const CompileUnit {
+fn findCompileUnit(st: &ElfStackTrace, target_address: u64) -> %&const CompileUnit {
+ var in_file_stream = io.FileInStream.init(&st.self_exe_file);
+ const in_stream = &in_file_stream.stream;
for (st.compile_unit_list.toSlice()) |*compile_unit| {
if (compile_unit.pc_range) |range| {
if (target_address >= range.start and target_address < range.end)
return compile_unit;
}
+ if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
+ var base_address: usize = 0;
+ if (st.debug_ranges) |debug_ranges| {
+ %return st.self_exe_file.seekTo(debug_ranges.offset + ranges_offset);
+ while (true) {
+ const begin_addr = %return in_stream.readIntLe(usize);
+ const end_addr = %return in_stream.readIntLe(usize);
+ if (begin_addr == 0 and end_addr == 0) {
+ break;
+ }
+ if (begin_addr == @maxValue(usize)) {
+ base_address = begin_addr;
+ continue;
+ }
+ if (target_address >= begin_addr and target_address < end_addr) {
+ return compile_unit;
+ }
+ }
+ }
+ } else |err| {
+ if (err != error.MissingDebugInfo)
+ return err;
+ continue;
+ }
}
- return null;
+ return error.MissingDebugInfo;
}
fn readInitialLength(in_stream: &io.InStream, is_64: &bool) -> %u64 {
const first_32_bits = %return in_stream.readIntLe(u32);
*is_64 = (first_32_bits == 0xffffffff);
- return if (*is_64) {
- %return in_stream.readIntLe(u64)
+ if (*is_64) {
+ return in_stream.readIntLe(u64);
} else {
if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
- u64(first_32_bits)
- };
+ return u64(first_32_bits);
+ }
}
fn readULeb128(in_stream: &io.InStream) -> %u64 {
@@ -965,40 +989,62 @@ fn readILeb128(in_stream: &io.InStream) -> %i64 {
}
}
-pub const global_allocator = &global_allocator_state;
-var global_allocator_state = mem.Allocator {
- .allocFn = globalAlloc,
- .reallocFn = globalRealloc,
- .freeFn = globalFree,
-};
+pub const global_allocator = &global_fixed_allocator.allocator;
+var global_fixed_allocator = mem.FixedBufferAllocator.init(global_allocator_mem[0..]);
+var global_allocator_mem: [100 * 1024]u8 = undefined;
-var some_mem: [100 * 1024]u8 = undefined;
-var some_mem_index: usize = 0;
+/// Allocator that fails after N allocations, useful for making sure out of
+/// memory conditions are handled correctly.
+pub const FailingAllocator = struct {
+ allocator: mem.Allocator,
+ index: usize,
+ fail_index: usize,
+ internal_allocator: &mem.Allocator,
+ allocated_bytes: usize,
-error OutOfMemory;
-
-fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
- const addr = @ptrToInt(&some_mem[some_mem_index]);
- const rem = @rem(addr, alignment);
- const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
- const adjusted_index = some_mem_index + march_forward_bytes;
- const end_index = adjusted_index + n;
- if (end_index > some_mem.len) {
- return error.OutOfMemory;
+ pub fn init(allocator: &mem.Allocator, fail_index: usize) -> FailingAllocator {
+ return FailingAllocator {
+ .internal_allocator = allocator,
+ .fail_index = fail_index,
+ .index = 0,
+ .allocated_bytes = 0,
+ .allocator = mem.Allocator {
+ .allocFn = alloc,
+ .reallocFn = realloc,
+ .freeFn = free,
+ },
+ };
}
- const result = some_mem[adjusted_index .. end_index];
- some_mem_index = end_index;
- return result;
-}
-fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
- if (new_size <= old_mem.len) {
- return old_mem[0..new_size];
- } else {
- const result = %return globalAlloc(self, new_size, alignment);
- @memcpy(result.ptr, old_mem.ptr, old_mem.len);
+ fn alloc(allocator: &mem.Allocator, n: usize, alignment: u29) -> %[]u8 {
+ const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
+ if (self.index == self.fail_index) {
+ return error.OutOfMemory;
+ }
+ self.index += 1;
+ const result = %return self.internal_allocator.allocFn(self.internal_allocator, n, alignment);
+ self.allocated_bytes += result.len;
return result;
}
-}
-fn globalFree(self: &mem.Allocator, memory: []u8) { }
+ fn realloc(allocator: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
+ const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
+ if (new_size <= old_mem.len) {
+ self.allocated_bytes -= old_mem.len - new_size;
+ return self.internal_allocator.reallocFn(self.internal_allocator, old_mem, new_size, alignment);
+ }
+ if (self.index == self.fail_index) {
+ return error.OutOfMemory;
+ }
+ self.index += 1;
+ const result = %return self.internal_allocator.reallocFn(self.internal_allocator, old_mem, new_size, alignment);
+ self.allocated_bytes += new_size - old_mem.len;
+ return result;
+ }
+
+ fn free(allocator: &mem.Allocator, bytes: []u8) {
+ const self = @fieldParentPtr(FailingAllocator, "allocator", allocator);
+ self.allocated_bytes -= bytes.len;
+ return self.internal_allocator.freeFn(self.internal_allocator, bytes);
+ }
+};
diff --git a/std/elf.zig b/std/elf.zig
index f7be236d15..60b0119894 100644
--- a/std/elf.zig
+++ b/std/elf.zig
@@ -188,39 +188,39 @@ pub const Elf = struct {
if (elf.is_64) {
if (sh_entry_size != 64) return error.InvalidFormat;
- for (elf.section_headers) |*section| {
- section.name = %return in.readInt(elf.endian, u32);
- section.sh_type = %return in.readInt(elf.endian, u32);
- section.flags = %return in.readInt(elf.endian, u64);
- section.addr = %return in.readInt(elf.endian, u64);
- section.offset = %return in.readInt(elf.endian, u64);
- section.size = %return in.readInt(elf.endian, u64);
- section.link = %return in.readInt(elf.endian, u32);
- section.info = %return in.readInt(elf.endian, u32);
- section.addr_align = %return in.readInt(elf.endian, u64);
- section.ent_size = %return in.readInt(elf.endian, u64);
+ for (elf.section_headers) |*elf_section| {
+ elf_section.name = %return in.readInt(elf.endian, u32);
+ elf_section.sh_type = %return in.readInt(elf.endian, u32);
+ elf_section.flags = %return in.readInt(elf.endian, u64);
+ elf_section.addr = %return in.readInt(elf.endian, u64);
+ elf_section.offset = %return in.readInt(elf.endian, u64);
+ elf_section.size = %return in.readInt(elf.endian, u64);
+ elf_section.link = %return in.readInt(elf.endian, u32);
+ elf_section.info = %return in.readInt(elf.endian, u32);
+ elf_section.addr_align = %return in.readInt(elf.endian, u64);
+ elf_section.ent_size = %return in.readInt(elf.endian, u64);
}
} else {
if (sh_entry_size != 40) return error.InvalidFormat;
- for (elf.section_headers) |*section| {
+ for (elf.section_headers) |*elf_section| {
// TODO (multiple occurences) allow implicit cast from %u32 -> %u64 ?
- section.name = %return in.readInt(elf.endian, u32);
- section.sh_type = %return in.readInt(elf.endian, u32);
- section.flags = u64(%return in.readInt(elf.endian, u32));
- section.addr = u64(%return in.readInt(elf.endian, u32));
- section.offset = u64(%return in.readInt(elf.endian, u32));
- section.size = u64(%return in.readInt(elf.endian, u32));
- section.link = %return in.readInt(elf.endian, u32);
- section.info = %return in.readInt(elf.endian, u32);
- section.addr_align = u64(%return in.readInt(elf.endian, u32));
- section.ent_size = u64(%return in.readInt(elf.endian, u32));
+ elf_section.name = %return in.readInt(elf.endian, u32);
+ elf_section.sh_type = %return in.readInt(elf.endian, u32);
+ elf_section.flags = u64(%return in.readInt(elf.endian, u32));
+ elf_section.addr = u64(%return in.readInt(elf.endian, u32));
+ elf_section.offset = u64(%return in.readInt(elf.endian, u32));
+ elf_section.size = u64(%return in.readInt(elf.endian, u32));
+ elf_section.link = %return in.readInt(elf.endian, u32);
+ elf_section.info = %return in.readInt(elf.endian, u32);
+ elf_section.addr_align = u64(%return in.readInt(elf.endian, u32));
+ elf_section.ent_size = u64(%return in.readInt(elf.endian, u32));
}
}
- for (elf.section_headers) |*section| {
- if (section.sh_type != SHT_NOBITS) {
- const file_end_offset = %return math.add(u64, section.offset, section.size);
+ for (elf.section_headers) |*elf_section| {
+ if (elf_section.sh_type != SHT_NOBITS) {
+ const file_end_offset = %return math.add(u64, elf_section.offset, elf_section.size);
if (stream_end < file_end_offset) return error.InvalidFormat;
}
}
@@ -243,29 +243,27 @@ pub const Elf = struct {
var file_stream = io.FileInStream.init(elf.in_file);
const in = &file_stream.stream;
- for (elf.section_headers) |*section| {
- if (section.sh_type == SHT_NULL) continue;
+ section_loop: for (elf.section_headers) |*elf_section| {
+ if (elf_section.sh_type == SHT_NULL) continue;
- const name_offset = elf.string_section.offset + section.name;
+ const name_offset = elf.string_section.offset + elf_section.name;
%return elf.in_file.seekTo(name_offset);
for (name) |expected_c| {
const target_c = %return in.readByte();
- if (target_c == 0 or expected_c != target_c) goto next_section;
+ if (target_c == 0 or expected_c != target_c) continue :section_loop;
}
{
const null_byte = %return in.readByte();
- if (null_byte == 0) return section;
+ if (null_byte == 0) return elf_section;
}
-
- next_section:
}
return null;
}
- pub fn seekToSection(elf: &Elf, section: &SectionHeader) -> %void {
- %return elf.in_file.seekTo(section.offset);
+ pub fn seekToSection(elf: &Elf, elf_section: &SectionHeader) -> %void {
+ %return elf.in_file.seekTo(elf_section.offset);
}
};
diff --git a/std/endian.zig b/std/endian.zig
index 2dc6b8d34e..9f2d2c8dcd 100644
--- a/std/endian.zig
+++ b/std/endian.zig
@@ -2,15 +2,15 @@ const mem = @import("mem.zig");
const builtin = @import("builtin");
pub fn swapIfLe(comptime T: type, x: T) -> T {
- swapIf(false, T, x)
+ return swapIf(false, T, x);
}
pub fn swapIfBe(comptime T: type, x: T) -> T {
- swapIf(true, T, x)
+ return swapIf(true, T, x);
}
pub fn swapIf(endian: builtin.Endian, comptime T: type, x: T) -> T {
- if (builtin.endian == endian) swap(T, x) else x
+ return if (builtin.endian == endian) swap(T, x) else x;
}
pub fn swap(comptime T: type, x: T) -> T {
diff --git a/std/fmt/errol/enum3.zig b/std/fmt/errol/enum3.zig
index 93861cce74..feb84da9a4 100644
--- a/std/fmt/errol/enum3.zig
+++ b/std/fmt/errol/enum3.zig
@@ -439,10 +439,10 @@ const Slab = struct {
};
fn slab(str: []const u8, exp: i32) -> Slab {
- Slab {
+ return Slab {
.str = str,
.exp = exp,
- }
+ };
}
pub const enum3_data = []Slab {
diff --git a/std/fmt/index.zig b/std/fmt/index.zig
index 59376e0458..fef968a1d5 100644
--- a/std/fmt/index.zig
+++ b/std/fmt/index.zig
@@ -251,11 +251,10 @@ pub fn formatFloat(value: var, context: var, output: fn(@typeOf(context), []cons
%return output(context, float_decimal.digits[0..1]);
%return output(context, ".");
if (float_decimal.digits.len > 1) {
- const num_digits = if (@typeOf(value) == f32) {
+ const num_digits = if (@typeOf(value) == f32)
math.min(usize(9), float_decimal.digits.len)
- } else {
- float_decimal.digits.len
- };
+ else
+ float_decimal.digits.len;
%return output(context, float_decimal.digits[1 .. num_digits]);
} else {
%return output(context, "0");
@@ -372,6 +371,10 @@ test "fmt.parseInt" {
assert(%%parseInt(i32, "-10", 10) == -10);
assert(%%parseInt(i32, "+10", 10) == 10);
assert(if (parseInt(i32, " 10", 10)) |_| false else |err| err == error.InvalidChar);
+ assert(if (parseInt(i32, "10 ", 10)) |_| false else |err| err == error.InvalidChar);
+ assert(if (parseInt(u32, "-10", 10)) |_| false else |err| err == error.InvalidChar);
+ assert(%%parseInt(u8, "255", 10) == 255);
+ assert(if (parseInt(u8, "256", 10)) |_| false else |err| err == error.Overflow);
}
pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T {
@@ -413,14 +416,16 @@ const BufPrintContext = struct {
remaining: []u8,
};
+error BufferTooSmall;
fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> %void {
+ if (context.remaining.len < bytes.len) return error.BufferTooSmall;
mem.copy(u8, context.remaining, bytes);
context.remaining = context.remaining[bytes.len..];
}
-pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> []u8 {
+pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) -> %[]u8 {
var context = BufPrintContext { .remaining = buf, };
- %%format(&context, bufPrintWrite, fmt, args);
+ %return format(&context, bufPrintWrite, fmt, args);
return buf[0..buf.len - context.remaining.len];
}
@@ -476,31 +481,31 @@ test "fmt.format" {
{
var buf1: [32]u8 = undefined;
const value: ?i32 = 1234;
- const result = bufPrint(buf1[0..], "nullable: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "nullable: {}\n", value);
assert(mem.eql(u8, result, "nullable: 1234\n"));
}
{
var buf1: [32]u8 = undefined;
const value: ?i32 = null;
- const result = bufPrint(buf1[0..], "nullable: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "nullable: {}\n", value);
assert(mem.eql(u8, result, "nullable: null\n"));
}
{
var buf1: [32]u8 = undefined;
const value: %i32 = 1234;
- const result = bufPrint(buf1[0..], "error union: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "error union: {}\n", value);
assert(mem.eql(u8, result, "error union: 1234\n"));
}
{
var buf1: [32]u8 = undefined;
const value: %i32 = error.InvalidChar;
- const result = bufPrint(buf1[0..], "error union: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "error union: {}\n", value);
assert(mem.eql(u8, result, "error union: error.InvalidChar\n"));
}
{
var buf1: [32]u8 = undefined;
const value: u3 = 0b101;
- const result = bufPrint(buf1[0..], "u3: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "u3: {}\n", value);
assert(mem.eql(u8, result, "u3: 5\n"));
}
@@ -510,28 +515,28 @@ test "fmt.format" {
{
var buf1: [32]u8 = undefined;
const value: f32 = 12.34;
- const result = bufPrint(buf1[0..], "f32: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "f32: {}\n", value);
assert(mem.eql(u8, result, "f32: 1.23400001e1\n"));
}
{
var buf1: [32]u8 = undefined;
const value: f64 = -12.34e10;
- const result = bufPrint(buf1[0..], "f64: {}\n", value);
+ const result = %%bufPrint(buf1[0..], "f64: {}\n", value);
assert(mem.eql(u8, result, "f64: -1.234e11\n"));
}
{
var buf1: [32]u8 = undefined;
- const result = bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
+ const result = %%bufPrint(buf1[0..], "f64: {}\n", math.nan_f64);
assert(mem.eql(u8, result, "f64: NaN\n"));
}
{
var buf1: [32]u8 = undefined;
- const result = bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
+ const result = %%bufPrint(buf1[0..], "f64: {}\n", math.inf_f64);
assert(mem.eql(u8, result, "f64: Infinity\n"));
}
{
var buf1: [32]u8 = undefined;
- const result = bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
+ const result = %%bufPrint(buf1[0..], "f64: {}\n", -math.inf_f64);
assert(mem.eql(u8, result, "f64: -Infinity\n"));
}
}
diff --git a/std/hash_map.zig b/std/hash_map.zig
index d124d7b573..837ee07423 100644
--- a/std/hash_map.zig
+++ b/std/hash_map.zig
@@ -12,7 +12,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
comptime hash: fn(key: K)->u32,
comptime eql: fn(a: K, b: K)->bool) -> type
{
- struct {
+ return struct {
entries: []Entry,
size: usize,
max_distance_from_start_index: usize,
@@ -51,19 +51,19 @@ pub fn HashMap(comptime K: type, comptime V: type,
return entry;
}
}
- unreachable // no next item
+ unreachable; // no next item
}
};
pub fn init(allocator: &Allocator) -> Self {
- Self {
+ return Self {
.entries = []Entry{},
.allocator = allocator,
.size = 0,
.max_distance_from_start_index = 0,
// it doesn't actually matter what we set this to since we use wrapping integer arithmetic
.modification_count = undefined,
- }
+ };
}
pub fn deinit(hm: &Self) {
@@ -133,7 +133,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
entry.distance_from_start_index -= 1;
entry = next_entry;
}
- unreachable // shifting everything in the table
+ unreachable; // shifting everything in the table
}}
return null;
}
@@ -169,7 +169,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
const start_index = hm.keyToIndex(key);
var roll_over: usize = 0;
var distance_from_start_index: usize = 0;
- while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1}) {
+ while (roll_over < hm.entries.len) : ({roll_over += 1; distance_from_start_index += 1;}) {
const index = (start_index + roll_over) % hm.entries.len;
const entry = &hm.entries[index];
@@ -210,7 +210,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
};
return result;
}
- unreachable // put into a full map
+ unreachable; // put into a full map
}
fn internalGet(hm: &Self, key: K) -> ?&Entry {
@@ -228,7 +228,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
fn keyToIndex(hm: &Self, key: K) -> usize {
return usize(hash(key)) % hm.entries.len;
}
- }
+ };
}
test "basicHashMapTest" {
@@ -251,9 +251,9 @@ test "basicHashMapTest" {
}
fn hash_i32(x: i32) -> u32 {
- @bitCast(u32, x)
+ return @bitCast(u32, x);
}
fn eql_i32(a: i32, b: i32) -> bool {
- a == b
+ return a == b;
}
diff --git a/std/heap.zig b/std/heap.zig
index d0bf8ab871..ec447c1aa8 100644
--- a/std/heap.zig
+++ b/std/heap.zig
@@ -10,30 +10,28 @@ const Allocator = mem.Allocator;
error OutOfMemory;
-pub var c_allocator = Allocator {
+pub const c_allocator = &c_allocator_state;
+var c_allocator_state = Allocator {
.allocFn = cAlloc,
.reallocFn = cRealloc,
.freeFn = cFree,
};
-fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
- if (c.malloc(usize(n))) |buf| {
+fn cAlloc(self: &Allocator, n: usize, alignment: u29) -> %[]u8 {
+ return if (c.malloc(usize(n))) |buf|
@ptrCast(&u8, buf)[0..n]
- } else {
- error.OutOfMemory
- }
+ else
+ error.OutOfMemory;
}
-fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
- if (new_size <= old_mem.len) {
- old_mem[0..new_size]
+fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
+ const old_ptr = @ptrCast(&c_void, old_mem.ptr);
+ if (c.realloc(old_ptr, new_size)) |buf| {
+ return @ptrCast(&u8, buf)[0..new_size];
+ } else if (new_size <= old_mem.len) {
+ return old_mem[0..new_size];
} else {
- const old_ptr = @ptrCast(&c_void, old_mem.ptr);
- if (c.realloc(old_ptr, usize(new_size))) |buf| {
- @ptrCast(&u8, buf)[0..new_size]
- } else {
- error.OutOfMemory
- }
+ return error.OutOfMemory;
}
}
@@ -106,7 +104,7 @@ pub const IncrementingAllocator = struct {
return self.bytes.len - self.end_index;
}
- fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
+ fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
const addr = @ptrToInt(&self.bytes[self.end_index]);
const rem = @rem(addr, alignment);
@@ -121,7 +119,7 @@ pub const IncrementingAllocator = struct {
return result;
}
- fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
+ fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
if (new_size <= old_mem.len) {
return old_mem[0..new_size];
} else {
diff --git a/std/index.zig b/std/index.zig
index 8033eee142..323eee203e 100644
--- a/std/index.zig
+++ b/std/index.zig
@@ -1,7 +1,9 @@
pub const ArrayList = @import("array_list.zig").ArrayList;
+pub const AlignedArrayList = @import("array_list.zig").AlignedArrayList;
pub const BufMap = @import("buf_map.zig").BufMap;
pub const BufSet = @import("buf_set.zig").BufSet;
pub const Buffer = @import("buffer.zig").Buffer;
+pub const BufferOutStream = @import("buffer.zig").BufferOutStream;
pub const HashMap = @import("hash_map.zig").HashMap;
pub const LinkedList = @import("linked_list.zig").LinkedList;
@@ -26,12 +28,12 @@ pub const sort = @import("sort.zig");
test "std" {
// run tests from these
- _ = @import("array_list.zig").ArrayList;
- _ = @import("buf_map.zig").BufMap;
- _ = @import("buf_set.zig").BufSet;
- _ = @import("buffer.zig").Buffer;
- _ = @import("hash_map.zig").HashMap;
- _ = @import("linked_list.zig").LinkedList;
+ _ = @import("array_list.zig");
+ _ = @import("buf_map.zig");
+ _ = @import("buf_set.zig");
+ _ = @import("buffer.zig");
+ _ = @import("hash_map.zig");
+ _ = @import("linked_list.zig");
_ = @import("base64.zig");
_ = @import("build.zig");
diff --git a/std/io.zig b/std/io.zig
index c86ebed326..cbf2e0c216 100644
--- a/std/io.zig
+++ b/std/io.zig
@@ -50,35 +50,32 @@ error Unseekable;
error EndOfFile;
pub fn getStdErr() -> %File {
- const handle = if (is_windows) {
+ const handle = if (is_windows)
%return os.windowsGetStdHandle(system.STD_ERROR_HANDLE)
- } else if (is_posix) {
+ else if (is_posix)
system.STDERR_FILENO
- } else {
- unreachable
- };
+ else
+ unreachable;
return File.openHandle(handle);
}
pub fn getStdOut() -> %File {
- const handle = if (is_windows) {
+ const handle = if (is_windows)
%return os.windowsGetStdHandle(system.STD_OUTPUT_HANDLE)
- } else if (is_posix) {
+ else if (is_posix)
system.STDOUT_FILENO
- } else {
- unreachable
- };
+ else
+ unreachable;
return File.openHandle(handle);
}
pub fn getStdIn() -> %File {
- const handle = if (is_windows) {
+ const handle = if (is_windows)
%return os.windowsGetStdHandle(system.STD_INPUT_HANDLE)
- } else if (is_posix) {
+ else if (is_posix)
system.STDIN_FILENO
- } else {
- unreachable
- };
+ else
+ unreachable;
return File.openHandle(handle);
}
@@ -261,7 +258,7 @@ pub const File = struct {
system.EBADF => error.BadFd,
system.ENOMEM => error.SystemResources,
else => os.unexpectedErrorPosix(err),
- }
+ };
}
return usize(stat.size);
@@ -481,6 +478,14 @@ pub const OutStream = struct {
const slice = (&byte)[0..1];
return self.writeFn(self, slice);
}
+
+ pub fn writeByteNTimes(self: &OutStream, byte: u8, n: usize) -> %void {
+ const slice = (&byte)[0..1];
+ var i: usize = 0;
+ while (i < n) : (i += 1) {
+ %return self.writeFn(self, slice);
+ }
+ }
};
/// `path` may need to be copied in memory to add a null terminating byte. In this case
@@ -493,6 +498,20 @@ pub fn writeFile(path: []const u8, data: []const u8, allocator: ?&mem.Allocator)
%return file.write(data);
}
+/// On success, caller owns returned buffer.
+pub fn readFileAlloc(path: []const u8, allocator: &mem.Allocator) -> %[]u8 {
+ var file = %return File.openRead(path, allocator);
+ defer file.close();
+
+ const size = %return file.getEndPos();
+ const buf = %return allocator.alloc(u8, size);
+ %defer allocator.free(buf);
+
+ var adapter = FileInStream.init(&file);
+ %return adapter.stream.readNoEof(buf);
+ return buf;
+}
+
pub const BufferedInStream = BufferedInStreamCustom(os.page_size);
pub fn BufferedInStreamCustom(comptime buffer_size: usize) -> type {
@@ -619,3 +638,24 @@ pub fn BufferedOutStreamCustom(comptime buffer_size: usize) -> type {
}
};
}
+
+/// Implementation of OutStream trait for Buffer
+pub const BufferOutStream = struct {
+ buffer: &Buffer,
+ stream: OutStream,
+
+ pub fn init(buffer: &Buffer) -> BufferOutStream {
+ return BufferOutStream {
+ .buffer = buffer,
+ .stream = OutStream {
+ .writeFn = writeFn,
+ },
+ };
+ }
+
+ fn writeFn(out_stream: &OutStream, bytes: []const u8) -> %void {
+ const self = @fieldParentPtr(BufferOutStream, "stream", out_stream);
+ return self.buffer.append(bytes);
+ }
+};
+
diff --git a/std/linked_list.zig b/std/linked_list.zig
index cbcef793de..f4b6d274c9 100644
--- a/std/linked_list.zig
+++ b/std/linked_list.zig
@@ -5,7 +5,7 @@ const Allocator = mem.Allocator;
/// Generic doubly linked list.
pub fn LinkedList(comptime T: type) -> type {
- struct {
+ return struct {
const Self = this;
/// Node inside the linked list wrapping the actual data.
@@ -15,11 +15,11 @@ pub fn LinkedList(comptime T: type) -> type {
data: T,
pub fn init(data: &const T) -> Node {
- Node {
+ return Node {
.prev = null,
.next = null,
.data = *data,
- }
+ };
}
};
@@ -32,11 +32,11 @@ pub fn LinkedList(comptime T: type) -> type {
/// Returns:
/// An empty linked list.
pub fn init() -> Self {
- Self {
+ return Self {
.first = null,
.last = null,
.len = 0,
- }
+ };
}
/// Insert a new node after an existing one.
@@ -166,7 +166,7 @@ pub fn LinkedList(comptime T: type) -> type {
/// Returns:
/// A pointer to the new node.
pub fn allocateNode(list: &Self, allocator: &Allocator) -> %&Node {
- allocator.create(Node)
+ return allocator.create(Node);
}
/// Deallocate a node.
@@ -191,7 +191,7 @@ pub fn LinkedList(comptime T: type) -> type {
*node = Node.init(data);
return node;
}
- }
+ };
}
test "basic linked list test" {
diff --git a/std/math/acos.zig b/std/math/acos.zig
index 941643db82..7690497da5 100644
--- a/std/math/acos.zig
+++ b/std/math/acos.zig
@@ -7,11 +7,11 @@ const assert = @import("../debug.zig").assert;
pub fn acos(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(acos32, x),
- f64 => @inlineCall(acos64, x),
+ return switch (T) {
+ f32 => acos32(x),
+ f64 => acos64(x),
else => @compileError("acos not implemented for " ++ @typeName(T)),
- }
+ };
}
fn r32(z: f32) -> f32 {
@@ -22,7 +22,7 @@ fn r32(z: f32) -> f32 {
const p = z * (pS0 + z * (pS1 + z * pS2));
const q = 1.0 + z * qS1;
- p / q
+ return p / q;
}
fn acos32(x: f32) -> f32 {
@@ -69,7 +69,7 @@ fn acos32(x: f32) -> f32 {
const df = @bitCast(f32, jx & 0xFFFFF000);
const c = (z - df * df) / (s + df);
const w = r32(z) * s + c;
- 2 * (df + w)
+ return 2 * (df + w);
}
fn r64(z: f64) -> f64 {
@@ -86,7 +86,7 @@ fn r64(z: f64) -> f64 {
const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
- p / q
+ return p / q;
}
fn acos64(x: f64) -> f64 {
@@ -138,7 +138,7 @@ fn acos64(x: f64) -> f64 {
const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);
const c = (z - df * df) / (s + df);
const w = r64(z) * s + c;
- 2 * (df + w)
+ return 2 * (df + w);
}
test "math.acos" {
diff --git a/std/math/acosh.zig b/std/math/acosh.zig
index 9f43edeb5d..3b2c2c0f31 100644
--- a/std/math/acosh.zig
+++ b/std/math/acosh.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn acosh(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(acosh32, x),
- f64 => @inlineCall(acosh64, x),
+ return switch (T) {
+ f32 => acosh32(x),
+ f64 => acosh64(x),
else => @compileError("acosh not implemented for " ++ @typeName(T)),
- }
+ };
}
// acosh(x) = log(x + sqrt(x * x - 1))
@@ -23,15 +23,15 @@ fn acosh32(x: f32) -> f32 {
// |x| < 2, invalid if x < 1 or nan
if (i < 0x3F800000 + (1 << 23)) {
- math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)))
+ return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
}
// |x| < 0x1p12
else if (i < 0x3F800000 + (12 << 23)) {
- math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)))
+ return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)));
}
// |x| >= 0x1p12
else {
- math.ln(x) + 0.693147180559945309417232121458176568
+ return math.ln(x) + 0.693147180559945309417232121458176568;
}
}
@@ -41,15 +41,15 @@ fn acosh64(x: f64) -> f64 {
// |x| < 2, invalid if x < 1 or nan
if (e < 0x3FF + 1) {
- math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)))
+ return math.log1p(x - 1 + math.sqrt((x - 1) * (x - 1) + 2 * (x - 1)));
}
// |x| < 0x1p26
else if (e < 0x3FF + 26) {
- math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)))
+ return math.ln(2 * x - 1 / (x + math.sqrt(x * x - 1)));
}
// |x| >= 0x1p26 or nan
else {
- math.ln(x) + 0.693147180559945309417232121458176568
+ return math.ln(x) + 0.693147180559945309417232121458176568;
}
}
diff --git a/std/math/asin.zig b/std/math/asin.zig
index b0368b5d66..c5d24c35e5 100644
--- a/std/math/asin.zig
+++ b/std/math/asin.zig
@@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
pub fn asin(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(asin32, x),
- f64 => @inlineCall(asin64, x),
+ return switch (T) {
+ f32 => asin32(x),
+ f64 => asin64(x),
else => @compileError("asin not implemented for " ++ @typeName(T)),
- }
+ };
}
fn r32(z: f32) -> f32 {
@@ -23,7 +23,7 @@ fn r32(z: f32) -> f32 {
const p = z * (pS0 + z * (pS1 + z * pS2));
const q = 1.0 + z * qS1;
- p / q
+ return p / q;
}
fn asin32(x: f32) -> f32 {
@@ -58,9 +58,9 @@ fn asin32(x: f32) -> f32 {
const fx = pio2 - 2 * (s + s * r32(z));
if (hx >> 31 != 0) {
- -fx
+ return -fx;
} else {
- fx
+ return fx;
}
}
@@ -78,7 +78,7 @@ fn r64(z: f64) -> f64 {
const p = z * (pS0 + z * (pS1 + z * (pS2 + z * (pS3 + z * (pS4 + z * pS5)))));
const q = 1.0 + z * (qS1 + z * (qS2 + z * (qS3 + z * qS4)));
- p / q
+ return p / q;
}
fn asin64(x: f64) -> f64 {
@@ -119,7 +119,7 @@ fn asin64(x: f64) -> f64 {
// |x| > 0.975
if (ix >= 0x3FEF3333) {
- fx = pio2_hi - 2 * (s + s * r)
+ fx = pio2_hi - 2 * (s + s * r);
} else {
const jx = @bitCast(u64, s);
const df = @bitCast(f64, jx & 0xFFFFFFFF00000000);
@@ -128,9 +128,9 @@ fn asin64(x: f64) -> f64 {
}
if (hx >> 31 != 0) {
- -fx
+ return -fx;
} else {
- fx
+ return fx;
}
}
diff --git a/std/math/asinh.zig b/std/math/asinh.zig
index dab047036a..f963dae77b 100644
--- a/std/math/asinh.zig
+++ b/std/math/asinh.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn asinh(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(asinh32, x),
- f64 => @inlineCall(asinh64, x),
+ return switch (T) {
+ f32 => asinh32(x),
+ f64 => asinh64(x),
else => @compileError("asinh not implemented for " ++ @typeName(T)),
- }
+ };
}
// asinh(x) = sign(x) * log(|x| + sqrt(x * x + 1)) ~= x - x^3/6 + o(x^5)
@@ -46,7 +46,7 @@ fn asinh32(x: f32) -> f32 {
math.forceEval(x + 0x1.0p120);
}
- if (s != 0) -rx else rx
+ return if (s != 0) -rx else rx;
}
fn asinh64(x: f64) -> f64 {
@@ -77,7 +77,7 @@ fn asinh64(x: f64) -> f64 {
math.forceEval(x + 0x1.0p120);
}
- if (s != 0) -rx else rx
+ return if (s != 0) -rx else rx;
}
test "math.asinh" {
diff --git a/std/math/atan.zig b/std/math/atan.zig
index 5f3e207960..cf244eb762 100644
--- a/std/math/atan.zig
+++ b/std/math/atan.zig
@@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
pub fn atan(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(atan32, x),
- f64 => @inlineCall(atan64, x),
+ return switch (T) {
+ f32 => atan32(x),
+ f64 => atan64(x),
else => @compileError("atan not implemented for " ++ @typeName(T)),
- }
+ };
}
fn atan32(x_: f32) -> f32 {
@@ -99,11 +99,11 @@ fn atan32(x_: f32) -> f32 {
const s1 = z * (aT[0] + w * (aT[2] + w * aT[4]));
const s2 = w * (aT[1] + w * aT[3]);
- if (id == null) {
- x - x * (s1 + s2)
+ if (id) |id_value| {
+ const zz = atanhi[id_value] - ((x * (s1 + s2) - atanlo[id_value]) - x);
+ return if (sign != 0) -zz else zz;
} else {
- const zz = atanhi[??id] - ((x * (s1 + s2) - atanlo[??id]) - x);
- if (sign != 0) -zz else zz
+ return x - x * (s1 + s2);
}
}
@@ -198,16 +198,16 @@ fn atan64(x_: f64) -> f64 {
const s1 = z * (aT[0] + w * (aT[2] + w * (aT[4] + w * (aT[6] + w * (aT[8] + w * aT[10])))));
const s2 = w * (aT[1] + w * (aT[3] + w * (aT[5] + w * (aT[7] + w * aT[9]))));
- if (id == null) {
- x - x * (s1 + s2)
+ if (id) |id_value| {
+ const zz = atanhi[id_value] - ((x * (s1 + s2) - atanlo[id_value]) - x);
+ return if (sign != 0) -zz else zz;
} else {
- const zz = atanhi[??id] - ((x * (s1 + s2) - atanlo[??id]) - x);
- if (sign != 0) -zz else zz
+ return x - x * (s1 + s2);
}
}
test "math.atan" {
- assert(atan(f32(0.2)) == atan32(0.2));
+ assert(@bitCast(u32, atan(f32(0.2))) == @bitCast(u32, atan32(0.2)));
assert(atan(f64(0.2)) == atan64(0.2));
}
diff --git a/std/math/atan2.zig b/std/math/atan2.zig
index 0e566837ce..b3af18cd9e 100644
--- a/std/math/atan2.zig
+++ b/std/math/atan2.zig
@@ -22,11 +22,11 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
fn atan2(comptime T: type, x: T, y: T) -> T {
- switch (T) {
- f32 => @inlineCall(atan2_32, x, y),
- f64 => @inlineCall(atan2_64, x, y),
+ return switch (T) {
+ f32 => atan2_32(x, y),
+ f64 => atan2_64(x, y),
else => @compileError("atan2 not implemented for " ++ @typeName(T)),
- }
+ };
}
fn atan2_32(y: f32, x: f32) -> f32 {
@@ -97,11 +97,11 @@ fn atan2_32(y: f32, x: f32) -> f32 {
}
// z = atan(|y / x|) with correct underflow
- var z = {
+ var z = z: {
if ((m & 2) != 0 and iy + (26 << 23) < ix) {
- 0.0
+ break :z 0.0;
} else {
- math.atan(math.fabs(y / x))
+ break :z math.atan(math.fabs(y / x));
}
};
@@ -187,11 +187,11 @@ fn atan2_64(y: f64, x: f64) -> f64 {
}
// z = atan(|y / x|) with correct underflow
- var z = {
+ var z = z: {
if ((m & 2) != 0 and iy +% (64 << 20) < ix) {
- 0.0
+ break :z 0.0;
} else {
- math.atan(math.fabs(y / x))
+ break :z math.atan(math.fabs(y / x));
}
};
diff --git a/std/math/atanh.zig b/std/math/atanh.zig
index 8fe5ab55a7..13de90279b 100644
--- a/std/math/atanh.zig
+++ b/std/math/atanh.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn atanh(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(atanh_32, x),
- f64 => @inlineCall(atanh_64, x),
+ return switch (T) {
+ f32 => atanh_32(x),
+ f64 => atanh_64(x),
else => @compileError("atanh not implemented for " ++ @typeName(T)),
- }
+ };
}
// atanh(x) = log((1 + x) / (1 - x)) / 2 = log1p(2x / (1 - x)) / 2 ~= x + x^3 / 3 + o(x^5)
@@ -32,7 +32,7 @@ fn atanh_32(x: f32) -> f32 {
if (u < 0x3F800000 - (32 << 23)) {
// underflow
if (u < (1 << 23)) {
- math.forceEval(y * y)
+ math.forceEval(y * y);
}
}
// |x| < 0.5
@@ -43,7 +43,7 @@ fn atanh_32(x: f32) -> f32 {
y = 0.5 * math.log1p(2 * (y / (1 - y)));
}
- if (s != 0) -y else y
+ return if (s != 0) -y else y;
}
fn atanh_64(x: f64) -> f64 {
@@ -72,7 +72,7 @@ fn atanh_64(x: f64) -> f64 {
y = 0.5 * math.log1p(2 * (y / (1 - y)));
}
- if (s != 0) -y else y
+ return if (s != 0) -y else y;
}
test "math.atanh" {
diff --git a/std/math/cbrt.zig b/std/math/cbrt.zig
index 273939c6b6..a8df75dff2 100644
--- a/std/math/cbrt.zig
+++ b/std/math/cbrt.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn cbrt(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(cbrt32, x),
- f64 => @inlineCall(cbrt64, x),
+ return switch (T) {
+ f32 => cbrt32(x),
+ f64 => cbrt64(x),
else => @compileError("cbrt not implemented for " ++ @typeName(T)),
- }
+ };
}
fn cbrt32(x: f32) -> f32 {
@@ -53,7 +53,7 @@ fn cbrt32(x: f32) -> f32 {
r = t * t * t;
t = t * (f64(x) + x + r) / (x + r + r);
- f32(t)
+ return f32(t);
}
fn cbrt64(x: f64) -> f64 {
@@ -109,7 +109,7 @@ fn cbrt64(x: f64) -> f64 {
var w = t + t;
q = (q - t) / (w + q);
- t + t * q
+ return t + t * q;
}
test "math.cbrt" {
diff --git a/std/math/ceil.zig b/std/math/ceil.zig
index 2c7b28cc93..8e27132e25 100644
--- a/std/math/ceil.zig
+++ b/std/math/ceil.zig
@@ -10,11 +10,11 @@ const assert = @import("../debug.zig").assert;
pub fn ceil(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(ceil32, x),
- f64 => @inlineCall(ceil64, x),
+ return switch (T) {
+ f32 => ceil32(x),
+ f64 => ceil64(x),
else => @compileError("ceil not implemented for " ++ @typeName(T)),
- }
+ };
}
fn ceil32(x: f32) -> f32 {
@@ -39,13 +39,13 @@ fn ceil32(x: f32) -> f32 {
u += m;
}
u &= ~m;
- @bitCast(f32, u)
+ return @bitCast(f32, u);
} else {
math.forceEval(x + 0x1.0p120);
if (u >> 31 != 0) {
return -0.0;
} else {
- 1.0
+ return 1.0;
}
}
}
@@ -70,14 +70,14 @@ fn ceil64(x: f64) -> f64 {
if (e <= 0x3FF-1) {
math.forceEval(y);
if (u >> 63 != 0) {
- return -0.0; // Compiler requires return.
+ return -0.0;
} else {
- 1.0
+ return 1.0;
}
} else if (y < 0) {
- x + y + 1
+ return x + y + 1;
} else {
- x + y
+ return x + y;
}
}
diff --git a/std/math/copysign.zig b/std/math/copysign.zig
index 109ee8c632..05205e1a0e 100644
--- a/std/math/copysign.zig
+++ b/std/math/copysign.zig
@@ -2,11 +2,11 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
pub fn copysign(comptime T: type, x: T, y: T) -> T {
- switch (T) {
- f32 => @inlineCall(copysign32, x, y),
- f64 => @inlineCall(copysign64, x, y),
+ return switch (T) {
+ f32 => copysign32(x, y),
+ f64 => copysign64(x, y),
else => @compileError("copysign not implemented for " ++ @typeName(T)),
- }
+ };
}
fn copysign32(x: f32, y: f32) -> f32 {
@@ -15,7 +15,7 @@ fn copysign32(x: f32, y: f32) -> f32 {
const h1 = ux & (@maxValue(u32) / 2);
const h2 = uy & (u32(1) << 31);
- @bitCast(f32, h1 | h2)
+ return @bitCast(f32, h1 | h2);
}
fn copysign64(x: f64, y: f64) -> f64 {
@@ -24,7 +24,7 @@ fn copysign64(x: f64, y: f64) -> f64 {
const h1 = ux & (@maxValue(u64) / 2);
const h2 = uy & (u64(1) << 63);
- @bitCast(f64, h1 | h2)
+ return @bitCast(f64, h1 | h2);
}
test "math.copysign" {
diff --git a/std/math/cos.zig b/std/math/cos.zig
index 4b5c9193c3..4c3b8e1282 100644
--- a/std/math/cos.zig
+++ b/std/math/cos.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn cos(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(cos32, x),
- f64 => @inlineCall(cos64, x),
+ return switch (T) {
+ f32 => cos32(x),
+ f64 => cos64(x),
else => @compileError("cos not implemented for " ++ @typeName(T)),
- }
+ };
}
// sin polynomial coefficients
@@ -73,18 +73,18 @@ fn cos32(x_: f32) -> f32 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- const r = {
+ const r = r: {
if (j == 1 or j == 2) {
- z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
+ break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
} else {
- 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
+ break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
}
};
if (sign) {
- -r
+ return -r;
} else {
- r
+ return r;
}
}
@@ -124,18 +124,18 @@ fn cos64(x_: f64) -> f64 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- const r = {
+ const r = r: {
if (j == 1 or j == 2) {
- z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
+ break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
} else {
- 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
+ break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
}
};
if (sign) {
- -r
+ return -r;
} else {
- r
+ return r;
}
}
diff --git a/std/math/cosh.zig b/std/math/cosh.zig
index 34c647eaad..6d40d71b8d 100644
--- a/std/math/cosh.zig
+++ b/std/math/cosh.zig
@@ -11,11 +11,11 @@ const assert = @import("../debug.zig").assert;
pub fn cosh(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(cosh32, x),
- f64 => @inlineCall(cosh64, x),
+ return switch (T) {
+ f32 => cosh32(x),
+ f64 => cosh64(x),
else => @compileError("cosh not implemented for " ++ @typeName(T)),
- }
+ };
}
// cosh(x) = (exp(x) + 1 / exp(x)) / 2
@@ -43,7 +43,7 @@ fn cosh32(x: f32) -> f32 {
}
// |x| > log(FLT_MAX) or nan
- expo2(ax)
+ return expo2(ax);
}
fn cosh64(x: f64) -> f64 {
@@ -76,7 +76,7 @@ fn cosh64(x: f64) -> f64 {
}
// |x| > log(CBL_MAX) or nan
- expo2(ax)
+ return expo2(ax);
}
test "math.cosh" {
diff --git a/std/math/exp.zig b/std/math/exp.zig
index 87c0a1882e..6e591daea3 100644
--- a/std/math/exp.zig
+++ b/std/math/exp.zig
@@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
pub fn exp(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(exp32, x),
- f64 => @inlineCall(exp64, x),
+ return switch (T) {
+ f32 => exp32(x),
+ f64 => exp64(x),
else => @compileError("exp not implemented for " ++ @typeName(T)),
- }
+ };
}
fn exp32(x_: f32) -> f32 {
@@ -86,9 +86,9 @@ fn exp32(x_: f32) -> f32 {
const y = 1 + (x * c / (2 - c) - lo + hi);
if (k == 0) {
- y
+ return y;
} else {
- math.scalbn(y, k)
+ return math.scalbn(y, k);
}
}
@@ -172,9 +172,9 @@ fn exp64(x_: f64) -> f64 {
const y = 1 + (x * c / (2 - c) - lo + hi);
if (k == 0) {
- y
+ return y;
} else {
- math.scalbn(y, k)
+ return math.scalbn(y, k);
}
}
diff --git a/std/math/exp2.zig b/std/math/exp2.zig
index 111c1dad64..6061f32391 100644
--- a/std/math/exp2.zig
+++ b/std/math/exp2.zig
@@ -8,11 +8,11 @@ const assert = @import("../debug.zig").assert;
pub fn exp2(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(exp2_32, x),
- f64 => @inlineCall(exp2_64, x),
+ return switch (T) {
+ f32 => exp2_32(x),
+ f64 => exp2_64(x),
else => @compileError("exp2 not implemented for " ++ @typeName(T)),
- }
+ };
}
const exp2ft = []const f64 {
@@ -88,7 +88,7 @@ fn exp2_32(x: f32) -> f32 {
var r: f64 = exp2ft[i0];
const t: f64 = r * z;
r = r + t * (P1 + z * P2) + t * (z * z) * (P3 + z * P4);
- f32(r * uk)
+ return f32(r * uk);
}
const exp2dt = []f64 {
@@ -414,7 +414,7 @@ fn exp2_64(x: f64) -> f64 {
z -= exp2dt[2 * i0 + 1];
const r = t + t * z * (P1 + z * (P2 + z * (P3 + z * (P4 + z * P5))));
- math.scalbn(r, ik)
+ return math.scalbn(r, ik);
}
test "math.exp2" {
diff --git a/std/math/expm1.zig b/std/math/expm1.zig
index ef0b2766b1..fbe1841030 100644
--- a/std/math/expm1.zig
+++ b/std/math/expm1.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn expm1(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(expm1_32, x),
- f64 => @inlineCall(expm1_64, x),
+ return switch (T) {
+ f32 => expm1_32(x),
+ f64 => expm1_64(x),
else => @compileError("exp1m not implemented for " ++ @typeName(T)),
- }
+ };
}
fn expm1_32(x_: f32) -> f32 {
diff --git a/std/math/expo2.zig b/std/math/expo2.zig
index e0d8a8130d..93111391a2 100644
--- a/std/math/expo2.zig
+++ b/std/math/expo2.zig
@@ -2,11 +2,11 @@ const math = @import("index.zig");
pub fn expo2(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
+ return switch (T) {
f32 => expo2f(x),
f64 => expo2d(x),
else => @compileError("expo2 not implemented for " ++ @typeName(T)),
- }
+ };
}
fn expo2f(x: f32) -> f32 {
@@ -15,7 +15,7 @@ fn expo2f(x: f32) -> f32 {
const u = (0x7F + k / 2) << 23;
const scale = @bitCast(f32, u);
- math.exp(x - kln2) * scale * scale
+ return math.exp(x - kln2) * scale * scale;
}
fn expo2d(x: f64) -> f64 {
@@ -24,5 +24,5 @@ fn expo2d(x: f64) -> f64 {
const u = (0x3FF + k / 2) << 20;
const scale = @bitCast(f64, u64(u) << 32);
- math.exp(x - kln2) * scale * scale
+ return math.exp(x - kln2) * scale * scale;
}
diff --git a/std/math/fabs.zig b/std/math/fabs.zig
index e9bd3eb689..cf1b8f1f14 100644
--- a/std/math/fabs.zig
+++ b/std/math/fabs.zig
@@ -8,23 +8,23 @@ const assert = @import("../debug.zig").assert;
pub fn fabs(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(fabs32, x),
- f64 => @inlineCall(fabs64, x),
+ return switch (T) {
+ f32 => fabs32(x),
+ f64 => fabs64(x),
else => @compileError("fabs not implemented for " ++ @typeName(T)),
- }
+ };
}
fn fabs32(x: f32) -> f32 {
var u = @bitCast(u32, x);
u &= 0x7FFFFFFF;
- @bitCast(f32, u)
+ return @bitCast(f32, u);
}
fn fabs64(x: f64) -> f64 {
var u = @bitCast(u64, x);
u &= @maxValue(u64) >> 1;
- @bitCast(f64, u)
+ return @bitCast(f64, u);
}
test "math.fabs" {
diff --git a/std/math/floor.zig b/std/math/floor.zig
index 85ee2b4923..d7de45e9d0 100644
--- a/std/math/floor.zig
+++ b/std/math/floor.zig
@@ -10,11 +10,11 @@ const math = @import("index.zig");
pub fn floor(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(floor32, x),
- f64 => @inlineCall(floor64, x),
+ return switch (T) {
+ f32 => floor32(x),
+ f64 => floor64(x),
else => @compileError("floor not implemented for " ++ @typeName(T)),
- }
+ };
}
fn floor32(x: f32) -> f32 {
@@ -40,13 +40,13 @@ fn floor32(x: f32) -> f32 {
if (u >> 31 != 0) {
u += m;
}
- @bitCast(f32, u & ~m)
+ return @bitCast(f32, u & ~m);
} else {
math.forceEval(x + 0x1.0p120);
if (u >> 31 == 0) {
- return 0.0; // Compiler requires return
+ return 0.0;
} else {
- -1.0
+ return -1.0;
}
}
}
@@ -71,14 +71,14 @@ fn floor64(x: f64) -> f64 {
if (e <= 0x3FF-1) {
math.forceEval(y);
if (u >> 63 != 0) {
- return -1.0; // Compiler requires return.
+ return -1.0;
} else {
- 0.0
+ return 0.0;
}
} else if (y > 0) {
- x + y - 1
+ return x + y - 1;
} else {
- x + y
+ return x + y;
}
}
diff --git a/std/math/fma.zig b/std/math/fma.zig
index 8dbbc39ed0..8e5adc80b2 100644
--- a/std/math/fma.zig
+++ b/std/math/fma.zig
@@ -2,11 +2,11 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
pub fn fma(comptime T: type, x: T, y: T, z: T) -> T {
- switch (T) {
- f32 => @inlineCall(fma32, x, y, z),
- f64 => @inlineCall(fma64, x, y ,z),
+ return switch (T) {
+ f32 => fma32(x, y, z),
+ f64 => fma64(x, y ,z),
else => @compileError("fma not implemented for " ++ @typeName(T)),
- }
+ };
}
fn fma32(x: f32, y: f32, z: f32) -> f32 {
@@ -16,10 +16,10 @@ fn fma32(x: f32, y: f32, z: f32) -> f32 {
const e = (u >> 52) & 0x7FF;
if ((u & 0x1FFFFFFF) != 0x10000000 or e == 0x7FF or xy_z - xy == z) {
- f32(xy_z)
+ return f32(xy_z);
} else {
// TODO: Handle inexact case with double-rounding
- f32(xy_z)
+ return f32(xy_z);
}
}
@@ -64,9 +64,9 @@ fn fma64(x: f64, y: f64, z: f64) -> f64 {
const adj = add_adjusted(r.lo, xy.lo);
if (spread + math.ilogb(r.hi) > -1023) {
- math.scalbn(r.hi + adj, spread)
+ return math.scalbn(r.hi + adj, spread);
} else {
- add_and_denorm(r.hi, adj, spread)
+ return add_and_denorm(r.hi, adj, spread);
}
}
@@ -77,7 +77,7 @@ fn dd_add(a: f64, b: f64) -> dd {
ret.hi = a + b;
const s = ret.hi - a;
ret.lo = (a - (ret.hi - s)) + (b - s);
- ret
+ return ret;
}
fn dd_mul(a: f64, b: f64) -> dd {
@@ -99,7 +99,7 @@ fn dd_mul(a: f64, b: f64) -> dd {
ret.hi = p + q;
ret.lo = p - ret.hi + q + la * lb;
- ret
+ return ret;
}
fn add_adjusted(a: f64, b: f64) -> f64 {
@@ -113,7 +113,7 @@ fn add_adjusted(a: f64, b: f64) -> f64 {
sum.hi = @bitCast(f64, uhii);
}
}
- sum.hi
+ return sum.hi;
}
fn add_and_denorm(a: f64, b: f64, scale: i32) -> f64 {
@@ -127,7 +127,7 @@ fn add_and_denorm(a: f64, b: f64, scale: i32) -> f64 {
sum.hi = @bitCast(f64, uhii);
}
}
- math.scalbn(sum.hi, scale)
+ return math.scalbn(sum.hi, scale);
}
test "math.fma" {
diff --git a/std/math/frexp.zig b/std/math/frexp.zig
index a40a6dcc39..1a317a2c80 100644
--- a/std/math/frexp.zig
+++ b/std/math/frexp.zig
@@ -8,21 +8,21 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
fn frexp_result(comptime T: type) -> type {
- struct {
+ return struct {
significand: T,
exponent: i32,
- }
+ };
}
pub const frexp32_result = frexp_result(f32);
pub const frexp64_result = frexp_result(f64);
pub fn frexp(x: var) -> frexp_result(@typeOf(x)) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(frexp32, x),
- f64 => @inlineCall(frexp64, x),
+ return switch (T) {
+ f32 => frexp32(x),
+ f64 => frexp64(x),
else => @compileError("frexp not implemented for " ++ @typeName(T)),
- }
+ };
}
fn frexp32(x: f32) -> frexp32_result {
@@ -59,7 +59,7 @@ fn frexp32(x: f32) -> frexp32_result {
y &= 0x807FFFFF;
y |= 0x3F000000;
result.significand = @bitCast(f32, y);
- result
+ return result;
}
fn frexp64(x: f64) -> frexp64_result {
@@ -96,7 +96,7 @@ fn frexp64(x: f64) -> frexp64_result {
y &= 0x800FFFFFFFFFFFFF;
y |= 0x3FE0000000000000;
result.significand = @bitCast(f64, y);
- result
+ return result;
}
test "math.frexp" {
diff --git a/std/math/hypot.zig b/std/math/hypot.zig
index ee8ce0f518..9b09ed53a4 100644
--- a/std/math/hypot.zig
+++ b/std/math/hypot.zig
@@ -9,11 +9,11 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
pub fn hypot(comptime T: type, x: T, y: T) -> T {
- switch (T) {
- f32 => @inlineCall(hypot32, x, y),
- f64 => @inlineCall(hypot64, x, y),
+ return switch (T) {
+ f32 => hypot32(x, y),
+ f64 => hypot64(x, y),
else => @compileError("hypot not implemented for " ++ @typeName(T)),
- }
+ };
}
fn hypot32(x: f32, y: f32) -> f32 {
@@ -48,7 +48,7 @@ fn hypot32(x: f32, y: f32) -> f32 {
yy *= 0x1.0p-90;
}
- z * math.sqrt(f32(f64(x) * x + f64(y) * y))
+ return z * math.sqrt(f32(f64(x) * x + f64(y) * y));
}
fn sq(hi: &f64, lo: &f64, x: f64) {
@@ -109,7 +109,7 @@ fn hypot64(x: f64, y: f64) -> f64 {
sq(&hx, &lx, x);
sq(&hy, &ly, y);
- z * math.sqrt(ly + lx + hy + hx)
+ return z * math.sqrt(ly + lx + hy + hx);
}
test "math.hypot" {
diff --git a/std/math/ilogb.zig b/std/math/ilogb.zig
index 2a0ea7fc5f..41a1e2d83f 100644
--- a/std/math/ilogb.zig
+++ b/std/math/ilogb.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn ilogb(x: var) -> i32 {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(ilogb32, x),
- f64 => @inlineCall(ilogb64, x),
+ return switch (T) {
+ f32 => ilogb32(x),
+ f64 => ilogb64(x),
else => @compileError("ilogb not implemented for " ++ @typeName(T)),
- }
+ };
}
// NOTE: Should these be exposed publically?
@@ -53,7 +53,7 @@ fn ilogb32(x: f32) -> i32 {
}
}
- e - 0x7F
+ return e - 0x7F;
}
fn ilogb64(x: f64) -> i32 {
@@ -88,7 +88,7 @@ fn ilogb64(x: f64) -> i32 {
}
}
- e - 0x3FF
+ return e - 0x3FF;
}
test "math.ilogb" {
diff --git a/std/math/index.zig b/std/math/index.zig
index 8a583fb8e9..1991864f69 100644
--- a/std/math/index.zig
+++ b/std/math/index.zig
@@ -36,7 +36,7 @@ pub const inf = @import("inf.zig").inf;
pub fn approxEq(comptime T: type, x: T, y: T, epsilon: T) -> bool {
assert(@typeId(T) == TypeId.Float);
- fabs(x - y) < epsilon
+ return fabs(x - y) < epsilon;
}
// TODO: Hide the following in an internal module.
@@ -174,14 +174,8 @@ test "math" {
}
-pub const Cmp = enum {
- Less,
- Equal,
- Greater,
-};
-
pub fn min(x: var, y: var) -> @typeOf(x + y) {
- if (x < y) x else y
+ return if (x < y) x else y;
}
test "math.min" {
@@ -189,7 +183,7 @@ test "math.min" {
}
pub fn max(x: var, y: var) -> @typeOf(x + y) {
- if (x > y) x else y
+ return if (x > y) x else y;
}
test "math.max" {
@@ -199,19 +193,19 @@ test "math.max" {
error Overflow;
pub fn mul(comptime T: type, a: T, b: T) -> %T {
var answer: T = undefined;
- if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer
+ return if (@mulWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
error Overflow;
pub fn add(comptime T: type, a: T, b: T) -> %T {
var answer: T = undefined;
- if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer
+ return if (@addWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
error Overflow;
pub fn sub(comptime T: type, a: T, b: T) -> %T {
var answer: T = undefined;
- if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer
+ return if (@subWithOverflow(T, a, b, &answer)) error.Overflow else answer;
}
pub fn negate(x: var) -> %@typeOf(x) {
@@ -221,7 +215,7 @@ pub fn negate(x: var) -> %@typeOf(x) {
error Overflow;
pub fn shlExact(comptime T: type, a: T, shift_amt: Log2Int(T)) -> %T {
var answer: T = undefined;
- if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer
+ return if (@shlWithOverflow(T, a, shift_amt, &answer)) error.Overflow else answer;
}
/// Shifts left. Overflowed bits are truncated.
@@ -273,7 +267,7 @@ test "math.shr" {
}
pub fn Log2Int(comptime T: type) -> type {
- @IntType(false, log2(T.bit_count))
+ return @IntType(false, log2(T.bit_count));
}
test "math overflow functions" {
@@ -522,3 +516,28 @@ pub fn cast(comptime T: type, x: var) -> %T {
return T(x);
}
}
+
+pub fn floorPowerOfTwo(comptime T: type, value: T) -> T {
+ var x = value;
+
+ comptime var i = 1;
+ inline while(T.bit_count > i) : (i *= 2) {
+ x |= (x >> i);
+ }
+
+ return x - (x >> 1);
+}
+
+test "math.floorPowerOfTwo" {
+ testFloorPowerOfTwo();
+ comptime testFloorPowerOfTwo();
+}
+
+fn testFloorPowerOfTwo() {
+ assert(floorPowerOfTwo(u32, 63) == 32);
+ assert(floorPowerOfTwo(u32, 64) == 64);
+ assert(floorPowerOfTwo(u32, 65) == 64);
+ assert(floorPowerOfTwo(u4, 7) == 4);
+ assert(floorPowerOfTwo(u4, 8) == 8);
+ assert(floorPowerOfTwo(u4, 9) == 8);
+}
diff --git a/std/math/inf.zig b/std/math/inf.zig
index ed559c313e..546e6f3af8 100644
--- a/std/math/inf.zig
+++ b/std/math/inf.zig
@@ -2,9 +2,9 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
pub fn inf(comptime T: type) -> T {
- switch (T) {
+ return switch (T) {
f32 => @bitCast(f32, math.inf_u32),
f64 => @bitCast(f64, math.inf_u64),
else => @compileError("inf not implemented for " ++ @typeName(T)),
- }
+ };
}
diff --git a/std/math/isfinite.zig b/std/math/isfinite.zig
index 6dbf984721..d44d373cf1 100644
--- a/std/math/isfinite.zig
+++ b/std/math/isfinite.zig
@@ -6,11 +6,11 @@ pub fn isFinite(x: var) -> bool {
switch (T) {
f32 => {
const bits = @bitCast(u32, x);
- bits & 0x7FFFFFFF < 0x7F800000
+ return bits & 0x7FFFFFFF < 0x7F800000;
},
f64 => {
const bits = @bitCast(u64, x);
- bits & (@maxValue(u64) >> 1) < (0x7FF << 52)
+ return bits & (@maxValue(u64) >> 1) < (0x7FF << 52);
},
else => {
@compileError("isFinite not implemented for " ++ @typeName(T));
diff --git a/std/math/isinf.zig b/std/math/isinf.zig
index b388fabf10..98c90e72a9 100644
--- a/std/math/isinf.zig
+++ b/std/math/isinf.zig
@@ -6,11 +6,11 @@ pub fn isInf(x: var) -> bool {
switch (T) {
f32 => {
const bits = @bitCast(u32, x);
- bits & 0x7FFFFFFF == 0x7F800000
+ return bits & 0x7FFFFFFF == 0x7F800000;
},
f64 => {
const bits = @bitCast(u64, x);
- bits & (@maxValue(u64) >> 1) == (0x7FF << 52)
+ return bits & (@maxValue(u64) >> 1) == (0x7FF << 52);
},
else => {
@compileError("isInf not implemented for " ++ @typeName(T));
@@ -22,10 +22,10 @@ pub fn isPositiveInf(x: var) -> bool {
const T = @typeOf(x);
switch (T) {
f32 => {
- @bitCast(u32, x) == 0x7F800000
+ return @bitCast(u32, x) == 0x7F800000;
},
f64 => {
- @bitCast(u64, x) == 0x7FF << 52
+ return @bitCast(u64, x) == 0x7FF << 52;
},
else => {
@compileError("isPositiveInf not implemented for " ++ @typeName(T));
@@ -37,10 +37,10 @@ pub fn isNegativeInf(x: var) -> bool {
const T = @typeOf(x);
switch (T) {
f32 => {
- @bitCast(u32, x) == 0xFF800000
+ return @bitCast(u32, x) == 0xFF800000;
},
f64 => {
- @bitCast(u64, x) == 0xFFF << 52
+ return @bitCast(u64, x) == 0xFFF << 52;
},
else => {
@compileError("isNegativeInf not implemented for " ++ @typeName(T));
diff --git a/std/math/isnan.zig b/std/math/isnan.zig
index 8bcb200a6a..e996a72693 100644
--- a/std/math/isnan.zig
+++ b/std/math/isnan.zig
@@ -6,11 +6,11 @@ pub fn isNan(x: var) -> bool {
switch (T) {
f32 => {
const bits = @bitCast(u32, x);
- bits & 0x7FFFFFFF > 0x7F800000
+ return bits & 0x7FFFFFFF > 0x7F800000;
},
f64 => {
const bits = @bitCast(u64, x);
- (bits & (@maxValue(u64) >> 1)) > (u64(0x7FF) << 52)
+ return (bits & (@maxValue(u64) >> 1)) > (u64(0x7FF) << 52);
},
else => {
@compileError("isNan not implemented for " ++ @typeName(T));
@@ -21,7 +21,7 @@ pub fn isNan(x: var) -> bool {
// Note: A signalling nan is identical to a standard right now by may have a different bit
// representation in the future when required.
pub fn isSignalNan(x: var) -> bool {
- isNan(x)
+ return isNan(x);
}
test "math.isNan" {
diff --git a/std/math/isnormal.zig b/std/math/isnormal.zig
index b212d204de..f815c2680b 100644
--- a/std/math/isnormal.zig
+++ b/std/math/isnormal.zig
@@ -6,11 +6,11 @@ pub fn isNormal(x: var) -> bool {
switch (T) {
f32 => {
const bits = @bitCast(u32, x);
- (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000
+ return (bits + 0x00800000) & 0x7FFFFFFF >= 0x01000000;
},
f64 => {
const bits = @bitCast(u64, x);
- (bits + (1 << 52)) & (@maxValue(u64) >> 1) >= (1 << 53)
+ return (bits + (1 << 52)) & (@maxValue(u64) >> 1) >= (1 << 53);
},
else => {
@compileError("isNormal not implemented for " ++ @typeName(T));
diff --git a/std/math/ln.zig b/std/math/ln.zig
index 7ced6238ef..4ded89d328 100644
--- a/std/math/ln.zig
+++ b/std/math/ln.zig
@@ -14,7 +14,7 @@ pub fn ln(x: var) -> @typeOf(x) {
const T = @typeOf(x);
switch (@typeId(T)) {
TypeId.FloatLiteral => {
- return @typeOf(1.0)(ln_64(x))
+ return @typeOf(1.0)(ln_64(x));
},
TypeId.Float => {
return switch (T) {
@@ -84,7 +84,7 @@ pub fn ln_32(x_: f32) -> f32 {
const hfsq = 0.5 * f * f;
const dk = f32(k);
- s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi
+ return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
}
pub fn ln_64(x_: f64) -> f64 {
@@ -116,7 +116,7 @@ pub fn ln_64(x_: f64) -> f64 {
// subnormal, scale x
k -= 54;
x *= 0x1.0p54;
- hx = u32(@bitCast(u64, ix) >> 32)
+ hx = u32(@bitCast(u64, ix) >> 32);
}
else if (hx >= 0x7FF00000) {
return x;
@@ -142,7 +142,7 @@ pub fn ln_64(x_: f64) -> f64 {
const R = t2 + t1;
const dk = f64(k);
- s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi
+ return s * (hfsq + R) + dk * ln2_lo - hfsq + f + dk * ln2_hi;
}
test "math.ln" {
diff --git a/std/math/log.zig b/std/math/log.zig
index 075cecc890..1ff226f7e5 100644
--- a/std/math/log.zig
+++ b/std/math/log.zig
@@ -29,7 +29,7 @@ pub fn log(comptime T: type, base: T, x: T) -> T {
f32 => return f32(math.ln(f64(x)) / math.ln(f64(base))),
f64 => return math.ln(x) / math.ln(f64(base)),
else => @compileError("log not implemented for " ++ @typeName(T)),
- };
+ }
},
else => {
diff --git a/std/math/log10.zig b/std/math/log10.zig
index 75e1203ad4..73168dec8d 100644
--- a/std/math/log10.zig
+++ b/std/math/log10.zig
@@ -14,7 +14,7 @@ pub fn log10(x: var) -> @typeOf(x) {
const T = @typeOf(x);
switch (@typeId(T)) {
TypeId.FloatLiteral => {
- return @typeOf(1.0)(log10_64(x))
+ return @typeOf(1.0)(log10_64(x));
},
TypeId.Float => {
return switch (T) {
@@ -90,7 +90,7 @@ pub fn log10_32(x_: f32) -> f32 {
const lo = f - hi - hfsq + s * (hfsq + R);
const dk = f32(k);
- dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi
+ return dk * log10_2lo + (lo + hi) * ivln10lo + lo * ivln10hi + hi * ivln10hi + dk * log10_2hi;
}
pub fn log10_64(x_: f64) -> f64 {
@@ -124,7 +124,7 @@ pub fn log10_64(x_: f64) -> f64 {
// subnormal, scale x
k -= 54;
x *= 0x1.0p54;
- hx = u32(@bitCast(u64, x) >> 32)
+ hx = u32(@bitCast(u64, x) >> 32);
}
else if (hx >= 0x7FF00000) {
return x;
@@ -167,7 +167,7 @@ pub fn log10_64(x_: f64) -> f64 {
val_lo += (y - ww) + val_hi;
val_hi = ww;
- val_lo + val_hi
+ return val_lo + val_hi;
}
test "math.log10" {
diff --git a/std/math/log1p.zig b/std/math/log1p.zig
index 803726fdcc..b369385038 100644
--- a/std/math/log1p.zig
+++ b/std/math/log1p.zig
@@ -11,11 +11,11 @@ const assert = @import("../debug.zig").assert;
pub fn log1p(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(log1p_32, x),
- f64 => @inlineCall(log1p_64, x),
+ return switch (T) {
+ f32 => log1p_32(x),
+ f64 => log1p_64(x),
else => @compileError("log1p not implemented for " ++ @typeName(T)),
- }
+ };
}
fn log1p_32(x: f32) -> f32 {
@@ -91,7 +91,7 @@ fn log1p_32(x: f32) -> f32 {
const hfsq = 0.5 * f * f;
const dk = f32(k);
- s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi
+ return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi;
}
fn log1p_64(x: f64) -> f64 {
@@ -172,7 +172,7 @@ fn log1p_64(x: f64) -> f64 {
const R = t2 + t1;
const dk = f64(k);
- s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi
+ return s * (hfsq + R) + (dk * ln2_lo + c) - hfsq + f + dk * ln2_hi;
}
test "math.log1p" {
diff --git a/std/math/log2.zig b/std/math/log2.zig
index 2199d6bfa1..1b38a9ecee 100644
--- a/std/math/log2.zig
+++ b/std/math/log2.zig
@@ -14,7 +14,7 @@ pub fn log2(x: var) -> @typeOf(x) {
const T = @typeOf(x);
switch (@typeId(T)) {
TypeId.FloatLiteral => {
- return @typeOf(1.0)(log2_64(x))
+ return @typeOf(1.0)(log2_64(x));
},
TypeId.Float => {
return switch (T) {
@@ -26,7 +26,7 @@ pub fn log2(x: var) -> @typeOf(x) {
TypeId.IntLiteral => comptime {
var result = 0;
var x_shifted = x;
- while ({x_shifted >>= 1; x_shifted != 0}) : (result += 1) {}
+ while (b: {x_shifted >>= 1; break :b x_shifted != 0;}) : (result += 1) {}
return result;
},
TypeId.Int => {
@@ -94,7 +94,7 @@ pub fn log2_32(x_: f32) -> f32 {
u &= 0xFFFFF000;
hi = @bitCast(f32, u);
const lo = f - hi - hfsq + s * (hfsq + R);
- (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k)
+ return (lo + hi) * ivln2lo + lo * ivln2hi + hi * ivln2hi + f32(k);
}
pub fn log2_64(x_: f64) -> f64 {
@@ -165,7 +165,7 @@ pub fn log2_64(x_: f64) -> f64 {
val_lo += (y - ww) + val_hi;
val_hi = ww;
- val_lo + val_hi
+ return val_lo + val_hi;
}
test "math.log2" {
diff --git a/std/math/modf.zig b/std/math/modf.zig
index 25eab7f99d..72730b67d7 100644
--- a/std/math/modf.zig
+++ b/std/math/modf.zig
@@ -7,21 +7,21 @@ const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
fn modf_result(comptime T: type) -> type {
- struct {
+ return struct {
fpart: T,
ipart: T,
- }
+ };
}
pub const modf32_result = modf_result(f32);
pub const modf64_result = modf_result(f64);
pub fn modf(x: var) -> modf_result(@typeOf(x)) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(modf32, x),
- f64 => @inlineCall(modf64, x),
+ return switch (T) {
+ f32 => modf32(x),
+ f64 => modf64(x),
else => @compileError("modf not implemented for " ++ @typeName(T)),
- }
+ };
}
fn modf32(x: f32) -> modf32_result {
@@ -66,7 +66,7 @@ fn modf32(x: f32) -> modf32_result {
const uf = @bitCast(f32, u & ~mask);
result.ipart = uf;
result.fpart = x - uf;
- result
+ return result;
}
fn modf64(x: f64) -> modf64_result {
@@ -110,7 +110,7 @@ fn modf64(x: f64) -> modf64_result {
const uf = @bitCast(f64, u & ~mask);
result.ipart = uf;
result.fpart = x - uf;
- result
+ return result;
}
test "math.modf" {
diff --git a/std/math/nan.zig b/std/math/nan.zig
index a4899d6b82..e92bb04cb9 100644
--- a/std/math/nan.zig
+++ b/std/math/nan.zig
@@ -1,19 +1,19 @@
const math = @import("index.zig");
pub fn nan(comptime T: type) -> T {
- switch (T) {
+ return switch (T) {
f32 => @bitCast(f32, math.nan_u32),
f64 => @bitCast(f64, math.nan_u64),
else => @compileError("nan not implemented for " ++ @typeName(T)),
- }
+ };
}
// Note: A signalling nan is identical to a standard right now by may have a different bit
// representation in the future when required.
pub fn snan(comptime T: type) -> T {
- switch (T) {
+ return switch (T) {
f32 => @bitCast(f32, math.nan_u32),
f64 => @bitCast(f64, math.nan_u64),
else => @compileError("snan not implemented for " ++ @typeName(T)),
- }
+ };
}
diff --git a/std/math/pow.zig b/std/math/pow.zig
index 85c3a71d56..55a2cd8c3e 100644
--- a/std/math/pow.zig
+++ b/std/math/pow.zig
@@ -166,12 +166,12 @@ pub fn pow(comptime T: type, x: T, y: T) -> T {
ae = -ae;
}
- math.scalbn(a1, ae)
+ return math.scalbn(a1, ae);
}
fn isOddInteger(x: f64) -> bool {
const r = math.modf(x);
- r.fpart == 0.0 and i64(r.ipart) & 1 == 1
+ return r.fpart == 0.0 and i64(r.ipart) & 1 == 1;
}
test "math.pow" {
diff --git a/std/math/round.zig b/std/math/round.zig
index a16bedc3f5..1e193fb1d7 100644
--- a/std/math/round.zig
+++ b/std/math/round.zig
@@ -10,11 +10,11 @@ const math = @import("index.zig");
pub fn round(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(round32, x),
- f64 => @inlineCall(round64, x),
+ return switch (T) {
+ f32 => round32(x),
+ f64 => round64(x),
else => @compileError("round not implemented for " ++ @typeName(T)),
- }
+ };
}
fn round32(x_: f32) -> f32 {
@@ -48,9 +48,9 @@ fn round32(x_: f32) -> f32 {
}
if (u >> 31 != 0) {
- -y
+ return -y;
} else {
- y
+ return y;
}
}
@@ -85,9 +85,9 @@ fn round64(x_: f64) -> f64 {
}
if (u >> 63 != 0) {
- -y
+ return -y;
} else {
- y
+ return y;
}
}
diff --git a/std/math/scalbn.zig b/std/math/scalbn.zig
index 6e82194494..0be6a3d47e 100644
--- a/std/math/scalbn.zig
+++ b/std/math/scalbn.zig
@@ -3,11 +3,11 @@ const assert = @import("../debug.zig").assert;
pub fn scalbn(x: var, n: i32) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(scalbn32, x, n),
- f64 => @inlineCall(scalbn64, x, n),
+ return switch (T) {
+ f32 => scalbn32(x, n),
+ f64 => scalbn64(x, n),
else => @compileError("scalbn not implemented for " ++ @typeName(T)),
- }
+ };
}
fn scalbn32(x: f32, n_: i32) -> f32 {
@@ -37,7 +37,7 @@ fn scalbn32(x: f32, n_: i32) -> f32 {
}
const u = u32(n +% 0x7F) << 23;
- y * @bitCast(f32, u)
+ return y * @bitCast(f32, u);
}
fn scalbn64(x: f64, n_: i32) -> f64 {
@@ -67,7 +67,7 @@ fn scalbn64(x: f64, n_: i32) -> f64 {
}
const u = u64(n +% 0x3FF) << 52;
- y * @bitCast(f64, u)
+ return y * @bitCast(f64, u);
}
test "math.scalbn" {
diff --git a/std/math/signbit.zig b/std/math/signbit.zig
index 75b087f539..b8ccecfa1b 100644
--- a/std/math/signbit.zig
+++ b/std/math/signbit.zig
@@ -3,21 +3,21 @@ const assert = @import("../debug.zig").assert;
pub fn signbit(x: var) -> bool {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(signbit32, x),
- f64 => @inlineCall(signbit64, x),
+ return switch (T) {
+ f32 => signbit32(x),
+ f64 => signbit64(x),
else => @compileError("signbit not implemented for " ++ @typeName(T)),
- }
+ };
}
fn signbit32(x: f32) -> bool {
const bits = @bitCast(u32, x);
- bits >> 31 != 0
+ return bits >> 31 != 0;
}
fn signbit64(x: f64) -> bool {
const bits = @bitCast(u64, x);
- bits >> 63 != 0
+ return bits >> 63 != 0;
}
test "math.signbit" {
diff --git a/std/math/sin.zig b/std/math/sin.zig
index 508bf5fae4..392bef1bc0 100644
--- a/std/math/sin.zig
+++ b/std/math/sin.zig
@@ -10,11 +10,11 @@ const assert = @import("../debug.zig").assert;
pub fn sin(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(sin32, x),
- f64 => @inlineCall(sin64, x),
+ return switch (T) {
+ f32 => sin32(x),
+ f64 => sin64(x),
else => @compileError("sin not implemented for " ++ @typeName(T)),
- }
+ };
}
// sin polynomial coefficients
@@ -75,18 +75,18 @@ fn sin32(x_: f32) -> f32 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- const r = {
+ const r = r: {
if (j == 1 or j == 2) {
- 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
+ break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
} else {
- z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
+ break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
}
};
if (sign) {
- -r
+ return -r;
} else {
- r
+ return r;
}
}
@@ -127,25 +127,25 @@ fn sin64(x_: f64) -> f64 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- const r = {
+ const r = r: {
if (j == 1 or j == 2) {
- 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))))
+ break :r 1.0 - 0.5 * w + w * w * (C5 + w * (C4 + w * (C3 + w * (C2 + w * (C1 + w * C0)))));
} else {
- z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))))
+ break :r z + z * w * (S5 + w * (S4 + w * (S3 + w * (S2 + w * (S1 + w * S0)))));
}
};
if (sign) {
- -r
+ return -r;
} else {
- r
+ return r;
}
}
test "math.sin" {
assert(sin(f32(0.0)) == sin32(0.0));
assert(sin(f64(0.0)) == sin64(0.0));
- assert(comptime {math.sin(f64(2))} == math.sin(f64(2)));
+ assert(comptime (math.sin(f64(2))) == math.sin(f64(2)));
}
test "math.sin32" {
diff --git a/std/math/sinh.zig b/std/math/sinh.zig
index 32f67a49a8..4c575f10ec 100644
--- a/std/math/sinh.zig
+++ b/std/math/sinh.zig
@@ -11,11 +11,11 @@ const expo2 = @import("expo2.zig").expo2;
pub fn sinh(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(sinh32, x),
- f64 => @inlineCall(sinh64, x),
+ return switch (T) {
+ f32 => sinh32(x),
+ f64 => sinh64(x),
else => @compileError("sinh not implemented for " ++ @typeName(T)),
- }
+ };
}
// sinh(x) = (exp(x) - 1 / exp(x)) / 2
@@ -49,7 +49,7 @@ fn sinh32(x: f32) -> f32 {
}
// |x| > log(FLT_MAX) or nan
- 2 * h * expo2(ax)
+ return 2 * h * expo2(ax);
}
fn sinh64(x: f64) -> f64 {
@@ -83,7 +83,7 @@ fn sinh64(x: f64) -> f64 {
}
// |x| > log(DBL_MAX) or nan
- 2 * h * expo2(ax)
+ return 2 * h * expo2(ax);
}
test "math.sinh" {
diff --git a/std/math/sqrt.zig b/std/math/sqrt.zig
index bd1bb1fca4..263e616617 100644
--- a/std/math/sqrt.zig
+++ b/std/math/sqrt.zig
@@ -7,12 +7,34 @@
const math = @import("index.zig");
const assert = @import("../debug.zig").assert;
+const builtin = @import("builtin");
+const TypeId = builtin.TypeId;
-pub fn sqrt(x: var) -> @typeOf(x) {
+pub fn sqrt(x: var) -> (if (@typeId(@typeOf(x)) == TypeId.Int) @IntType(false, @typeOf(x).bit_count / 2) else @typeOf(x)) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(sqrt32, x),
- f64 => @inlineCall(sqrt64, x),
+ switch (@typeId(T)) {
+ TypeId.FloatLiteral => {
+ return T(sqrt64(x));
+ },
+ TypeId.Float => {
+ return switch (T) {
+ f32 => sqrt32(x),
+ f64 => sqrt64(x),
+ else => @compileError("sqrt not implemented for " ++ @typeName(T)),
+ };
+ },
+ TypeId.IntLiteral => comptime {
+ if (x > @maxValue(u128)) {
+ @compileError("sqrt not implemented for comptime_int greater than 128 bits");
+ }
+ if (x < 0) {
+ @compileError("sqrt on negative number");
+ }
+ return T(sqrt_int(u128, x));
+ },
+ TypeId.Int => {
+ return sqrt_int(T, x);
+ },
else => @compileError("sqrt not implemented for " ++ @typeName(T)),
}
}
@@ -42,7 +64,7 @@ fn sqrt32(x: f32) -> f32 {
// subnormal
var i: i32 = 0;
while (ix & 0x00800000 == 0) : (i += 1) {
- ix <<= 1
+ ix <<= 1;
}
m -= i - 1;
}
@@ -90,7 +112,7 @@ fn sqrt32(x: f32) -> f32 {
ix = (q >> 1) + 0x3f000000;
ix += m << 23;
- @bitCast(f32, ix)
+ return @bitCast(f32, ix);
}
// NOTE: The original code is full of implicit signed -> unsigned assumptions and u32 wraparound
@@ -131,7 +153,7 @@ fn sqrt64(x: f64) -> f64 {
// subnormal
var i: u32 = 0;
while (ix0 & 0x00100000 == 0) : (i += 1) {
- ix0 <<= 1
+ ix0 <<= 1;
}
m -= i32(i) - 1;
ix0 |= ix1 >> u5(32 - i);
@@ -223,7 +245,7 @@ fn sqrt64(x: f64) -> f64 {
iix0 = iix0 +% (m << 20);
const uz = (u64(iix0) << 32) | ix1;
- @bitCast(f64, uz)
+ return @bitCast(f64, uz);
}
test "math.sqrt" {
@@ -274,3 +296,35 @@ test "math.sqrt64.special" {
assert(math.isNan(sqrt64(-1.0)));
assert(math.isNan(sqrt64(math.nan(f64))));
}
+
+fn sqrt_int(comptime T: type, value: T) -> @IntType(false, T.bit_count / 2) {
+ var op = value;
+ var res: T = 0;
+ var one: T = 1 << (T.bit_count - 2);
+
+ // "one" starts at the highest power of four <= than the argument.
+ while (one > op) {
+ one >>= 2;
+ }
+
+ while (one != 0) {
+ if (op >= res + one) {
+ op -= res + one;
+ res += 2 * one;
+ }
+ res >>= 1;
+ one >>= 2;
+ }
+
+ const ResultType = @IntType(false, T.bit_count / 2);
+ return ResultType(res);
+}
+
+test "math.sqrt_int" {
+ assert(sqrt_int(u32, 3) == 1);
+ assert(sqrt_int(u32, 4) == 2);
+ assert(sqrt_int(u32, 5) == 2);
+ assert(sqrt_int(u32, 8) == 2);
+ assert(sqrt_int(u32, 9) == 3);
+ assert(sqrt_int(u32, 10) == 3);
+}
diff --git a/std/math/tan.zig b/std/math/tan.zig
index 6ac30fa667..ff53a758b4 100644
--- a/std/math/tan.zig
+++ b/std/math/tan.zig
@@ -10,11 +10,11 @@ const assert = @import("../debug.zig").assert;
pub fn tan(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(tan32, x),
- f64 => @inlineCall(tan64, x),
+ return switch (T) {
+ f32 => tan32(x),
+ f64 => tan64(x),
else => @compileError("tan not implemented for " ++ @typeName(T)),
- }
+ };
}
const Tp0 = -1.30936939181383777646E4;
@@ -62,11 +62,11 @@ fn tan32(x_: f32) -> f32 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- var r = {
+ var r = r: {
if (w > 1e-14) {
- z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
+ break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
} else {
- z
+ break :r z;
}
};
@@ -77,7 +77,7 @@ fn tan32(x_: f32) -> f32 {
r = -r;
}
- r
+ return r;
}
fn tan64(x_: f64) -> f64 {
@@ -111,11 +111,11 @@ fn tan64(x_: f64) -> f64 {
const z = ((x - y * pi4a) - y * pi4b) - y * pi4c;
const w = z * z;
- var r = {
+ var r = r: {
if (w > 1e-14) {
- z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4))
+ break :r z + z * (w * ((Tp0 * w + Tp1) * w + Tp2) / ((((w + Tq1) * w + Tq2) * w + Tq3) * w + Tq4));
} else {
- z
+ break :r z;
}
};
@@ -126,7 +126,7 @@ fn tan64(x_: f64) -> f64 {
r = -r;
}
- r
+ return r;
}
test "math.tan" {
diff --git a/std/math/tanh.zig b/std/math/tanh.zig
index d9704f458a..7715029361 100644
--- a/std/math/tanh.zig
+++ b/std/math/tanh.zig
@@ -11,11 +11,11 @@ const expo2 = @import("expo2.zig").expo2;
pub fn tanh(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(tanh32, x),
- f64 => @inlineCall(tanh64, x),
+ return switch (T) {
+ f32 => tanh32(x),
+ f64 => tanh64(x),
else => @compileError("tanh not implemented for " ++ @typeName(T)),
- }
+ };
}
// tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))
@@ -59,9 +59,9 @@ fn tanh32(x: f32) -> f32 {
}
if (u >> 31 != 0) {
- -t
+ return -t;
} else {
- t
+ return t;
}
}
@@ -104,9 +104,9 @@ fn tanh64(x: f64) -> f64 {
}
if (u >> 63 != 0) {
- -t
+ return -t;
} else {
- t
+ return t;
}
}
diff --git a/std/math/trunc.zig b/std/math/trunc.zig
index 937a8155e6..81eacb30ba 100644
--- a/std/math/trunc.zig
+++ b/std/math/trunc.zig
@@ -9,11 +9,11 @@ const assert = @import("../debug.zig").assert;
pub fn trunc(x: var) -> @typeOf(x) {
const T = @typeOf(x);
- switch (T) {
- f32 => @inlineCall(trunc32, x),
- f64 => @inlineCall(trunc64, x),
+ return switch (T) {
+ f32 => trunc32(x),
+ f64 => trunc64(x),
else => @compileError("trunc not implemented for " ++ @typeName(T)),
- }
+ };
}
fn trunc32(x: f32) -> f32 {
@@ -30,10 +30,10 @@ fn trunc32(x: f32) -> f32 {
m = u32(@maxValue(u32)) >> u5(e);
if (u & m == 0) {
- x
+ return x;
} else {
math.forceEval(x + 0x1p120);
- @bitCast(f32, u & ~m)
+ return @bitCast(f32, u & ~m);
}
}
@@ -51,10 +51,10 @@ fn trunc64(x: f64) -> f64 {
m = u64(@maxValue(u64)) >> u6(e);
if (u & m == 0) {
- x
+ return x;
} else {
math.forceEval(x + 0x1p120);
- @bitCast(f64, u & ~m)
+ return @bitCast(f64, u & ~m);
}
}
diff --git a/std/mem.zig b/std/mem.zig
index 815e122812..7438eba70a 100644
--- a/std/mem.zig
+++ b/std/mem.zig
@@ -3,26 +3,31 @@ const assert = debug.assert;
const math = @import("math/index.zig");
const builtin = @import("builtin");
-pub const Cmp = math.Cmp;
+error OutOfMemory;
pub const Allocator = struct {
/// Allocate byte_count bytes and return them in a slice, with the
- /// slicer's pointer aligned at least to alignment bytes.
- allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
+ /// slice's pointer aligned at least to alignment bytes.
+ /// The returned newly allocated memory is undefined.
+ allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) -> %[]u8,
- /// Guaranteed: `old_mem.len` is the same as what was returned from allocFn or reallocFn.
- /// Guaranteed: alignment >= alignment of old_mem.ptr
+ /// If `new_byte_count > old_mem.len`:
+ /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
+ /// * alignment >= alignment of old_mem.ptr
///
- /// If `new_byte_count` is less than or equal to `old_mem.len` this function must
- /// return successfully.
- reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
+ /// If `new_byte_count <= old_mem.len`:
+ /// * this function must return successfully.
+ /// * alignment <= alignment of old_mem.ptr
+ ///
+ /// The returned newly allocated memory is undefined.
+ reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) -> %[]u8,
/// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
freeFn: fn (self: &Allocator, old_mem: []u8),
fn create(self: &Allocator, comptime T: type) -> %&T {
const slice = %return self.alloc(T, 1);
- &slice[0]
+ return &slice[0];
}
fn destroy(self: &Allocator, ptr: var) {
@@ -30,28 +35,52 @@ pub const Allocator = struct {
}
fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
+ return self.alignedAlloc(T, @alignOf(T), n);
+ }
+
+ fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
+ n: usize) -> %[]align(alignment) T
+ {
const byte_count = %return math.mul(usize, @sizeOf(T), n);
- const byte_slice = %return self.allocFn(self, byte_count, @alignOf(T));
- ([]T)(@alignCast(@alignOf(T), byte_slice))
+ const byte_slice = %return self.allocFn(self, byte_count, alignment);
+ // This loop should get optimized out in ReleaseFast mode
+ for (byte_slice) |*byte| {
+ *byte = undefined;
+ }
+ return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
}
fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {
+ return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
+ }
+
+ fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,
+ old_mem: []align(alignment) T, n: usize) -> %[]align(alignment) T
+ {
if (old_mem.len == 0) {
return self.alloc(T, n);
}
- // Assert that old_mem.ptr is properly aligned.
- const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
-
+ const old_byte_slice = ([]u8)(old_mem);
const byte_count = %return math.mul(usize, @sizeOf(T), n);
- const byte_slice = %return self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
- return ([]T)(@alignCast(@alignOf(T), byte_slice));
+ const byte_slice = %return self.reallocFn(self, old_byte_slice, byte_count, alignment);
+ // This loop should get optimized out in ReleaseFast mode
+ for (byte_slice[old_byte_slice.len..]) |*byte| {
+ *byte = undefined;
+ }
+ return ([]T)(@alignCast(alignment, byte_slice));
}
/// Reallocate, but `n` must be less than or equal to `old_mem.len`.
/// Unlike `realloc`, this function cannot fail.
/// Shrinking to 0 is the same as calling `free`.
fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> []T {
+ return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
+ }
+
+ fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29,
+ old_mem: []align(alignment) T, n: usize) -> []align(alignment) T
+ {
if (n == 0) {
self.free(old_mem);
return old_mem[0..0];
@@ -59,15 +88,12 @@ pub const Allocator = struct {
assert(n <= old_mem.len);
- // Assert that old_mem.ptr is properly aligned.
- const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
-
// Here we skip the overflow checking on the multiplication because
// n <= old_mem.len and the multiplication didn't overflow for that operation.
const byte_count = @sizeOf(T) * n;
- const byte_slice = %%self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
- return ([]T)(@alignCast(@alignOf(T), byte_slice));
+ const byte_slice = %%self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment);
+ return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
}
fn free(self: &Allocator, memory: var) {
@@ -79,6 +105,51 @@ pub const Allocator = struct {
}
};
+pub const FixedBufferAllocator = struct {
+ allocator: Allocator,
+ end_index: usize,
+ buffer: []u8,
+
+ pub fn init(buffer: []u8) -> FixedBufferAllocator {
+ return FixedBufferAllocator {
+ .allocator = Allocator {
+ .allocFn = alloc,
+ .reallocFn = realloc,
+ .freeFn = free,
+ },
+ .buffer = buffer,
+ .end_index = 0,
+ };
+ }
+
+ fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
+ const self = @fieldParentPtr(FixedBufferAllocator, "allocator", allocator);
+ const addr = @ptrToInt(&self.buffer[self.end_index]);
+ const rem = @rem(addr, alignment);
+ const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
+ const adjusted_index = self.end_index + march_forward_bytes;
+ const new_end_index = adjusted_index + n;
+ if (new_end_index > self.buffer.len) {
+ return error.OutOfMemory;
+ }
+ const result = self.buffer[adjusted_index .. new_end_index];
+ self.end_index = new_end_index;
+ return result;
+ }
+
+ fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
+ if (new_size <= old_mem.len) {
+ return old_mem[0..new_size];
+ } else {
+ const result = %return alloc(allocator, new_size, alignment);
+ copy(u8, result, old_mem);
+ return result;
+ }
+ }
+
+ fn free(allocator: &Allocator, bytes: []u8) { }
+};
+
/// Copy all of source into dest at position 0.
/// dest.len must be >= source.len.
@@ -95,17 +166,24 @@ pub fn set(comptime T: type, dest: []T, value: T) {
for (dest) |*d| *d = value;
}
-/// Return < 0, == 0, or > 0 if memory a is less than, equal to, or greater than,
-/// memory b, respectively.
-pub fn cmp(comptime T: type, a: []const T, b: []const T) -> Cmp {
- const n = math.min(a.len, b.len);
+/// Returns true if lhs < rhs, false otherwise
+pub fn lessThan(comptime T: type, lhs: []const T, rhs: []const T) -> bool {
+ const n = math.min(lhs.len, rhs.len);
var i: usize = 0;
while (i < n) : (i += 1) {
- if (a[i] == b[i]) continue;
- return if (a[i] > b[i]) Cmp.Greater else if (a[i] < b[i]) Cmp.Less else Cmp.Equal;
+ if (lhs[i] == rhs[i]) continue;
+ return lhs[i] < rhs[i];
}
- return if (a.len > b.len) Cmp.Greater else if (a.len < b.len) Cmp.Less else Cmp.Equal;
+ return lhs.len < rhs.len;
+}
+
+test "mem.lessThan" {
+ assert(lessThan(u8, "abcd", "bee"));
+ assert(!lessThan(u8, "abc", "abc"));
+ assert(lessThan(u8, "abc", "abc0"));
+ assert(!lessThan(u8, "", ""));
+ assert(lessThan(u8, "", "a"));
}
/// Compares two slices and returns whether they are equal.
@@ -276,11 +354,11 @@ pub fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
/// split(" abc def ghi ", " ")
/// Will return slices for "abc", "def", "ghi", null, in that order.
pub fn split(buffer: []const u8, split_bytes: []const u8) -> SplitIterator {
- SplitIterator {
+ return SplitIterator {
.index = 0,
.buffer = buffer,
.split_bytes = split_bytes,
- }
+ };
}
test "mem.split" {
@@ -433,9 +511,8 @@ fn testWriteIntImpl() {
pub fn min(comptime T: type, slice: []const T) -> T {
var best = slice[0];
- var i: usize = 1;
- while (i < slice.len) : (i += 1) {
- best = math.min(best, slice[i]);
+ for (slice[1..]) |item| {
+ best = math.min(best, item);
}
return best;
}
@@ -446,9 +523,8 @@ test "mem.min" {
pub fn max(comptime T: type, slice: []const T) -> T {
var best = slice[0];
- var i: usize = 1;
- while (i < slice.len) : (i += 1) {
- best = math.max(best, slice[i]);
+ for (slice[1..]) |item| {
+ best = math.max(best, item);
}
return best;
}
@@ -456,3 +532,40 @@ pub fn max(comptime T: type, slice: []const T) -> T {
test "mem.max" {
assert(max(u8, "abcdefg") == 'g');
}
+
+pub fn swap(comptime T: type, a: &T, b: &T) {
+ const tmp = *a;
+ *a = *b;
+ *b = tmp;
+}
+
+/// In-place order reversal of a slice
+pub fn reverse(comptime T: type, items: []T) {
+ var i: usize = 0;
+ const end = items.len / 2;
+ while (i < end) : (i += 1) {
+ swap(T, &items[i], &items[items.len - i - 1]);
+ }
+}
+
+test "std.mem.reverse" {
+ var arr = []i32{ 5, 3, 1, 2, 4 };
+ reverse(i32, arr[0..]);
+
+ assert(eql(i32, arr, []i32{ 4, 2, 1, 3, 5 }));
+}
+
+/// In-place rotation of the values in an array ([0 1 2 3] becomes [1 2 3 0] if we rotate by 1)
+/// Assumes 0 <= amount <= items.len
+pub fn rotate(comptime T: type, items: []T, amount: usize) {
+ reverse(T, items[0..amount]);
+ reverse(T, items[amount..]);
+ reverse(T, items);
+}
+
+test "std.mem.rotate" {
+ var arr = []i32{ 5, 3, 1, 2, 4 };
+ rotate(i32, arr[0..], 2);
+
+ assert(eql(i32, arr, []i32{ 1, 2, 4, 5, 3 }));
+}
diff --git a/std/net.zig b/std/net.zig
index 3551499c6b..a5fd4d6036 100644
--- a/std/net.zig
+++ b/std/net.zig
@@ -72,7 +72,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
// if (family != AF_INET)
// buf[cnt++] = (struct address){ .family = AF_INET6, .addr = { [15] = 1 } };
//
- unreachable // TODO
+ unreachable; // TODO
}
// TODO
@@ -84,7 +84,7 @@ pub fn lookup(hostname: []const u8, out_addrs: []Address) -> %[]Address {
// else => {},
//};
- unreachable // TODO
+ unreachable; // TODO
}
pub fn connectAddr(addr: &Address, port: u16) -> %Connection {
@@ -96,23 +96,23 @@ pub fn connectAddr(addr: &Address, port: u16) -> %Connection {
}
const socket_fd = i32(socket_ret);
- const connect_ret = if (addr.family == linux.AF_INET) {
+ const connect_ret = if (addr.family == linux.AF_INET) x: {
var os_addr: linux.sockaddr_in = undefined;
os_addr.family = addr.family;
os_addr.port = endian.swapIfLe(u16, port);
@memcpy((&u8)(&os_addr.addr), &addr.addr[0], 4);
@memset(&os_addr.zero[0], 0, @sizeOf(@typeOf(os_addr.zero)));
- linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in))
- } else if (addr.family == linux.AF_INET6) {
+ break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in));
+ } else if (addr.family == linux.AF_INET6) x: {
var os_addr: linux.sockaddr_in6 = undefined;
os_addr.family = addr.family;
os_addr.port = endian.swapIfLe(u16, port);
os_addr.flowinfo = 0;
os_addr.scope_id = addr.scope_id;
@memcpy(&os_addr.addr[0], &addr.addr[0], 16);
- linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6))
+ break :x linux.connect(socket_fd, (&linux.sockaddr)(&os_addr), @sizeOf(linux.sockaddr_in6));
} else {
- unreachable
+ unreachable;
};
const connect_err = linux.getErrno(connect_ret);
if (connect_err > 0) {
@@ -165,13 +165,13 @@ pub fn parseIpLiteral(buf: []const u8) -> %Address {
fn hexDigit(c: u8) -> u8 {
// TODO use switch with range
if ('0' <= c and c <= '9') {
- c - '0'
+ return c - '0';
} else if ('A' <= c and c <= 'Z') {
- c - 'A' + 10
+ return c - 'A' + 10;
} else if ('a' <= c and c <= 'z') {
- c - 'a' + 10
+ return c - 'a' + 10;
} else {
- @maxValue(u8)
+ return @maxValue(u8);
}
}
diff --git a/std/os/child_process.zig b/std/os/child_process.zig
index 75a2dcf24d..6e86c99056 100644
--- a/std/os/child_process.zig
+++ b/std/os/child_process.zig
@@ -5,7 +5,6 @@ const os = std.os;
const posix = os.posix;
const windows = os.windows;
const mem = std.mem;
-const Allocator = mem.Allocator;
const debug = std.debug;
const assert = debug.assert;
const BufMap = std.BufMap;
@@ -74,7 +73,7 @@ pub const ChildProcess = struct {
/// First argument in argv is the executable.
/// On success must call deinit.
- pub fn init(argv: []const []const u8, allocator: &Allocator) -> %&ChildProcess {
+ pub fn init(argv: []const []const u8, allocator: &mem.Allocator) -> %&ChildProcess {
const child = %return allocator.create(ChildProcess);
%defer allocator.destroy(child);
@@ -116,7 +115,7 @@ pub const ChildProcess = struct {
return self.spawnWindows();
} else {
return self.spawnPosix();
- };
+ }
}
pub fn spawnAndWait(self: &ChildProcess) -> %Term {
@@ -180,6 +179,46 @@ pub const ChildProcess = struct {
}
}
+ pub const ExecResult = struct {
+ term: os.ChildProcess.Term,
+ stdout: []u8,
+ stderr: []u8,
+ };
+
+ /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns.
+ /// If it succeeds, the caller owns result.stdout and result.stderr memory.
+ pub fn exec(allocator: &mem.Allocator, argv: []const []const u8, cwd: ?[]const u8,
+ env_map: ?&const BufMap, max_output_size: usize) -> %ExecResult
+ {
+ const child = %%ChildProcess.init(argv, allocator);
+ defer child.deinit();
+
+ child.stdin_behavior = ChildProcess.StdIo.Ignore;
+ child.stdout_behavior = ChildProcess.StdIo.Pipe;
+ child.stderr_behavior = ChildProcess.StdIo.Pipe;
+ child.cwd = cwd;
+ child.env_map = env_map;
+
+ %return child.spawn();
+
+ var stdout = Buffer.initNull(allocator);
+ var stderr = Buffer.initNull(allocator);
+ defer Buffer.deinit(&stdout);
+ defer Buffer.deinit(&stderr);
+
+ var stdout_file_in_stream = io.FileInStream.init(&??child.stdout);
+ var stderr_file_in_stream = io.FileInStream.init(&??child.stderr);
+
+ %return stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size);
+ %return stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size);
+
+ return ExecResult {
+ .term = %return child.wait(),
+ .stdout = stdout.toOwnedSlice(),
+ .stderr = stderr.toOwnedSlice(),
+ };
+ }
+
fn waitWindows(self: &ChildProcess) -> %Term {
if (self.term) |term| {
self.cleanupStreams();
@@ -210,12 +249,12 @@ pub const ChildProcess = struct {
fn waitUnwrappedWindows(self: &ChildProcess) -> %void {
const result = os.windowsWaitSingle(self.handle, windows.INFINITE);
- self.term = (%Term)({
+ self.term = (%Term)(x: {
var exit_code: windows.DWORD = undefined;
if (windows.GetExitCodeProcess(self.handle, &exit_code) == 0) {
- Term { .Unknown = 0 }
+ break :x Term { .Unknown = 0 };
} else {
- Term { .Exited = @bitCast(i32, exit_code)}
+ break :x Term { .Exited = @bitCast(i32, exit_code)};
}
});
@@ -261,7 +300,7 @@ pub const ChildProcess = struct {
defer {
os.close(self.err_pipe[0]);
os.close(self.err_pipe[1]);
- };
+ }
// Write @maxValue(ErrInt) to the write end of the err_pipe. This is after
// waitpid, so this write is guaranteed to be after the child
@@ -280,15 +319,15 @@ pub const ChildProcess = struct {
}
fn statusToTerm(status: i32) -> Term {
- return if (posix.WIFEXITED(status)) {
+ return if (posix.WIFEXITED(status))
Term { .Exited = posix.WEXITSTATUS(status) }
- } else if (posix.WIFSIGNALED(status)) {
+ else if (posix.WIFSIGNALED(status))
Term { .Signal = posix.WTERMSIG(status) }
- } else if (posix.WIFSTOPPED(status)) {
+ else if (posix.WIFSTOPPED(status))
Term { .Stopped = posix.WSTOPSIG(status) }
- } else {
+ else
Term { .Unknown = status }
- };
+ ;
}
fn spawnPosix(self: &ChildProcess) -> %void {
@@ -305,22 +344,22 @@ pub const ChildProcess = struct {
%defer if (self.stderr_behavior == StdIo.Pipe) { destroyPipe(stderr_pipe); };
const any_ignore = (self.stdin_behavior == StdIo.Ignore or self.stdout_behavior == StdIo.Ignore or self.stderr_behavior == StdIo.Ignore);
- const dev_null_fd = if (any_ignore) {
+ const dev_null_fd = if (any_ignore)
%return os.posixOpen("/dev/null", posix.O_RDWR, 0, null)
- } else {
+ else
undefined
- };
- defer { if (any_ignore) os.close(dev_null_fd); };
+ ;
+ defer { if (any_ignore) os.close(dev_null_fd); }
var env_map_owned: BufMap = undefined;
var we_own_env_map: bool = undefined;
- const env_map = if (self.env_map) |env_map| {
+ const env_map = if (self.env_map) |env_map| x: {
we_own_env_map = false;
- env_map
- } else {
+ break :x env_map;
+ } else x: {
we_own_env_map = true;
env_map_owned = %return os.getEnvMap(self.allocator);
- &env_map_owned
+ break :x &env_map_owned;
};
defer { if (we_own_env_map) env_map_owned.deinit(); }
@@ -411,13 +450,13 @@ pub const ChildProcess = struct {
self.stdout_behavior == StdIo.Ignore or
self.stderr_behavior == StdIo.Ignore);
- const nul_handle = if (any_ignore) {
+ const nul_handle = if (any_ignore)
%return os.windowsOpen("NUL", windows.GENERIC_READ, windows.FILE_SHARE_READ,
windows.OPEN_EXISTING, windows.FILE_ATTRIBUTE_NORMAL, null)
- } else {
+ else
undefined
- };
- defer { if (any_ignore) os.close(nul_handle); };
+ ;
+ defer { if (any_ignore) os.close(nul_handle); }
if (any_ignore) {
%return windowsSetHandleInfo(nul_handle, windows.HANDLE_FLAG_INHERIT, 0);
}
@@ -503,30 +542,32 @@ pub const ChildProcess = struct {
};
var piProcInfo: windows.PROCESS_INFORMATION = undefined;
- const cwd_slice = if (self.cwd) |cwd| {
+ const cwd_slice = if (self.cwd) |cwd|
%return cstr.addNullByte(self.allocator, cwd)
- } else {
+ else
null
- };
+ ;
defer if (cwd_slice) |cwd| self.allocator.free(cwd);
const cwd_ptr = if (cwd_slice) |cwd| cwd.ptr else null;
- const maybe_envp_buf = if (self.env_map) |env_map| {
+ const maybe_envp_buf = if (self.env_map) |env_map|
%return os.createWindowsEnvBlock(self.allocator, env_map)
- } else {
+ else
null
- };
+ ;
defer if (maybe_envp_buf) |envp_buf| self.allocator.free(envp_buf);
const envp_ptr = if (maybe_envp_buf) |envp_buf| envp_buf.ptr else null;
// the cwd set in ChildProcess is in effect when choosing the executable path
// to match posix semantics
- const app_name = if (self.cwd) |cwd| {
- const resolved = %return os.path.resolve(self.allocator, cwd, self.argv[0]);
- defer self.allocator.free(resolved);
- %return cstr.addNullByte(self.allocator, resolved)
- } else {
- %return cstr.addNullByte(self.allocator, self.argv[0])
+ const app_name = x: {
+ if (self.cwd) |cwd| {
+ const resolved = %return os.path.resolve(self.allocator, cwd, self.argv[0]);
+ defer self.allocator.free(resolved);
+ break :x %return cstr.addNullByte(self.allocator, resolved);
+ } else {
+ break :x %return cstr.addNullByte(self.allocator, self.argv[0]);
+ }
};
defer self.allocator.free(app_name);
@@ -589,6 +630,7 @@ pub const ChildProcess = struct {
StdIo.Ignore => %return os.posixDup2(dev_null_fd, std_fileno),
}
}
+
};
fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?&u8,
@@ -611,7 +653,7 @@ fn windowsCreateProcess(app_name: &u8, cmd_line: &u8, envp_ptr: ?&u8, cwd_ptr: ?
/// Caller must dealloc.
/// Guarantees a null byte at result[result.len].
-fn windowsCreateCommandLine(allocator: &Allocator, argv: []const []const u8) -> %[]u8 {
+fn windowsCreateCommandLine(allocator: &mem.Allocator, argv: []const []const u8) -> %[]u8 {
var buf = %return Buffer.initSize(allocator, 0);
defer buf.deinit();
@@ -701,7 +743,7 @@ fn makePipe() -> %[2]i32 {
return switch (err) {
posix.EMFILE, posix.ENFILE => error.SystemResources,
else => os.unexpectedErrorPosix(err),
- }
+ };
}
return fds;
}
@@ -760,10 +802,10 @@ fn handleTerm(pid: i32, status: i32) {
}
}
-const sigchld_set = {
+const sigchld_set = x: {
var signal_set = posix.empty_sigset;
posix.sigaddset(&signal_set, posix.SIGCHLD);
- signal_set
+ break :x signal_set;
};
fn block_SIGCHLD() {
diff --git a/std/os/darwin.zig b/std/os/darwin.zig
index 9d80c64006..e230826b7e 100644
--- a/std/os/darwin.zig
+++ b/std/os/darwin.zig
@@ -97,63 +97,63 @@ pub const SIGINFO = 29; /// information request
pub const SIGUSR1 = 30; /// user defined signal 1
pub const SIGUSR2 = 31; /// user defined signal 2
-fn wstatus(x: i32) -> i32 { x & 0o177 }
+fn wstatus(x: i32) -> i32 { return x & 0o177; }
const wstopped = 0o177;
-pub fn WEXITSTATUS(x: i32) -> i32 { x >> 8 }
-pub fn WTERMSIG(x: i32) -> i32 { wstatus(x) }
-pub fn WSTOPSIG(x: i32) -> i32 { x >> 8 }
-pub fn WIFEXITED(x: i32) -> bool { wstatus(x) == 0 }
-pub fn WIFSTOPPED(x: i32) -> bool { wstatus(x) == wstopped and WSTOPSIG(x) != 0x13 }
-pub fn WIFSIGNALED(x: i32) -> bool { wstatus(x) != wstopped and wstatus(x) != 0 }
+pub fn WEXITSTATUS(x: i32) -> i32 { return x >> 8; }
+pub fn WTERMSIG(x: i32) -> i32 { return wstatus(x); }
+pub fn WSTOPSIG(x: i32) -> i32 { return x >> 8; }
+pub fn WIFEXITED(x: i32) -> bool { return wstatus(x) == 0; }
+pub fn WIFSTOPPED(x: i32) -> bool { return wstatus(x) == wstopped and WSTOPSIG(x) != 0x13; }
+pub fn WIFSIGNALED(x: i32) -> bool { return wstatus(x) != wstopped and wstatus(x) != 0; }
/// Get the errno from a syscall return value, or 0 for no error.
pub fn getErrno(r: usize) -> usize {
const signed_r = @bitCast(isize, r);
- if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0
+ return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0;
}
pub fn close(fd: i32) -> usize {
- errnoWrap(c.close(fd))
+ return errnoWrap(c.close(fd));
}
pub fn abort() -> noreturn {
- c.abort()
+ c.abort();
}
pub fn exit(code: i32) -> noreturn {
- c.exit(code)
+ c.exit(code);
}
pub fn isatty(fd: i32) -> bool {
- c.isatty(fd) != 0
+ return c.isatty(fd) != 0;
}
pub fn fstat(fd: i32, buf: &c.Stat) -> usize {
- errnoWrap(c.@"fstat$INODE64"(fd, buf))
+ return errnoWrap(c.@"fstat$INODE64"(fd, buf));
}
pub fn lseek(fd: i32, offset: isize, whence: c_int) -> usize {
- errnoWrap(c.lseek(fd, offset, whence))
+ return errnoWrap(c.lseek(fd, offset, whence));
}
pub fn open(path: &const u8, flags: u32, mode: usize) -> usize {
- errnoWrap(c.open(path, @bitCast(c_int, flags), mode))
+ return errnoWrap(c.open(path, @bitCast(c_int, flags), mode));
}
pub fn raise(sig: i32) -> usize {
- errnoWrap(c.raise(sig))
+ return errnoWrap(c.raise(sig));
}
pub fn read(fd: i32, buf: &u8, nbyte: usize) -> usize {
- errnoWrap(c.read(fd, @ptrCast(&c_void, buf), nbyte))
+ return errnoWrap(c.read(fd, @ptrCast(&c_void, buf), nbyte));
}
pub fn stat(noalias path: &const u8, noalias buf: &stat) -> usize {
- errnoWrap(c.stat(path, buf))
+ return errnoWrap(c.stat(path, buf));
}
pub fn write(fd: i32, buf: &const u8, nbyte: usize) -> usize {
- errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte))
+ return errnoWrap(c.write(fd, @ptrCast(&const c_void, buf), nbyte));
}
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
@@ -166,79 +166,79 @@ pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32,
}
pub fn munmap(address: &u8, length: usize) -> usize {
- errnoWrap(c.munmap(@ptrCast(&c_void, address), length))
+ return errnoWrap(c.munmap(@ptrCast(&c_void, address), length));
}
pub fn unlink(path: &const u8) -> usize {
- errnoWrap(c.unlink(path))
+ return errnoWrap(c.unlink(path));
}
pub fn getcwd(buf: &u8, size: usize) -> usize {
- if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(*c._errno())) else 0
+ return if (c.getcwd(buf, size) == null) @bitCast(usize, -isize(*c._errno())) else 0;
}
pub fn waitpid(pid: i32, status: &i32, options: u32) -> usize {
comptime assert(i32.bit_count == c_int.bit_count);
- errnoWrap(c.waitpid(pid, @ptrCast(&c_int, status), @bitCast(c_int, options)))
+ return errnoWrap(c.waitpid(pid, @ptrCast(&c_int, status), @bitCast(c_int, options)));
}
pub fn fork() -> usize {
- errnoWrap(c.fork())
+ return errnoWrap(c.fork());
}
pub fn pipe(fds: &[2]i32) -> usize {
comptime assert(i32.bit_count == c_int.bit_count);
- errnoWrap(c.pipe(@ptrCast(&c_int, fds)))
+ return errnoWrap(c.pipe(@ptrCast(&c_int, fds)));
}
pub fn mkdir(path: &const u8, mode: u32) -> usize {
- errnoWrap(c.mkdir(path, mode))
+ return errnoWrap(c.mkdir(path, mode));
}
pub fn symlink(existing: &const u8, new: &const u8) -> usize {
- errnoWrap(c.symlink(existing, new))
+ return errnoWrap(c.symlink(existing, new));
}
pub fn rename(old: &const u8, new: &const u8) -> usize {
- errnoWrap(c.rename(old, new))
+ return errnoWrap(c.rename(old, new));
}
pub fn chdir(path: &const u8) -> usize {
- errnoWrap(c.chdir(path))
+ return errnoWrap(c.chdir(path));
}
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8)
-> usize
{
- errnoWrap(c.execve(path, argv, envp))
+ return errnoWrap(c.execve(path, argv, envp));
}
pub fn dup2(old: i32, new: i32) -> usize {
- errnoWrap(c.dup2(old, new))
+ return errnoWrap(c.dup2(old, new));
}
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) -> usize {
- errnoWrap(c.readlink(path, buf_ptr, buf_len))
+ return errnoWrap(c.readlink(path, buf_ptr, buf_len));
}
pub fn nanosleep(req: &const timespec, rem: ?×pec) -> usize {
- errnoWrap(c.nanosleep(req, rem))
+ return errnoWrap(c.nanosleep(req, rem));
}
pub fn realpath(noalias filename: &const u8, noalias resolved_name: &u8) -> usize {
- if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(*c._errno())) else 0
+ return if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(*c._errno())) else 0;
}
pub fn setreuid(ruid: u32, euid: u32) -> usize {
- errnoWrap(c.setreuid(ruid, euid))
+ return errnoWrap(c.setreuid(ruid, euid));
}
pub fn setregid(rgid: u32, egid: u32) -> usize {
- errnoWrap(c.setregid(rgid, egid))
+ return errnoWrap(c.setregid(rgid, egid));
}
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
- errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset))
+ return errnoWrap(c.sigprocmask(@bitCast(c_int, flags), set, oldset));
}
pub fn sigaction(sig: u5, noalias act: &const Sigaction, noalias oact: ?&Sigaction) -> usize {
@@ -285,9 +285,5 @@ pub fn sigaddset(set: &sigset_t, signo: u5) {
/// that the kernel represents it to libc. Errno was a mistake, let's make
/// it go away forever.
fn errnoWrap(value: isize) -> usize {
- @bitCast(usize, if (value == -1) {
- -isize(*c._errno())
- } else {
- value
- })
+ return @bitCast(usize, if (value == -1) -isize(*c._errno()) else value);
}
diff --git a/std/os/index.zig b/std/os/index.zig
index 361750aedc..8e79eda40b 100644
--- a/std/os/index.zig
+++ b/std/os/index.zig
@@ -84,7 +84,7 @@ pub fn getRandomBytes(buf: []u8) -> %void {
posix.EFAULT => unreachable,
posix.EINTR => continue,
else => unexpectedErrorPosix(err),
- }
+ };
}
return;
},
@@ -151,18 +151,17 @@ pub coldcc fn exit(status: i32) -> noreturn {
}
switch (builtin.os) {
Os.linux, Os.darwin, Os.macosx, Os.ios => {
- posix.exit(status)
+ posix.exit(status);
},
Os.windows => {
// Map a possibly negative status code to a non-negative status for the systems default
// integer width.
- const p_status = if (@sizeOf(c_uint) < @sizeOf(u32)) {
+ const p_status = if (@sizeOf(c_uint) < @sizeOf(u32))
@truncate(c_uint, @bitCast(u32, status))
- } else {
- c_uint(@bitCast(u32, status))
- };
+ else
+ c_uint(@bitCast(u32, status));
- windows.ExitProcess(p_status)
+ windows.ExitProcess(p_status);
},
else => @compileError("Unsupported OS"),
}
@@ -289,7 +288,7 @@ pub fn posixOpen(file_path: []const u8, flags: u32, perm: usize, allocator: ?&Al
posix.EPERM => error.AccessDenied,
posix.EEXIST => error.PathAlreadyExists,
else => unexpectedErrorPosix(err),
- }
+ };
}
return i32(result);
}
@@ -680,7 +679,7 @@ pub fn deleteFileWindows(allocator: &Allocator, file_path: []const u8) -> %void
windows.ERROR.ACCESS_DENIED => error.AccessDenied,
windows.ERROR.FILENAME_EXCED_RANGE, windows.ERROR.INVALID_PARAMETER => error.NameTooLong,
else => unexpectedErrorWindows(err),
- }
+ };
}
}
@@ -902,40 +901,41 @@ pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
/// this function recursively removes its entries and then tries again.
// TODO non-recursive implementation
pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
-start_over:
- // First, try deleting the item as a file. This way we don't follow sym links.
- if (deleteFile(allocator, full_path)) {
- return;
- } else |err| {
- if (err == error.FileNotFound)
+ start_over: while (true) {
+ // First, try deleting the item as a file. This way we don't follow sym links.
+ if (deleteFile(allocator, full_path)) {
return;
- if (err != error.IsDir)
- return err;
- }
- {
- var dir = Dir.open(allocator, full_path) %% |err| {
+ } else |err| {
if (err == error.FileNotFound)
return;
- if (err == error.NotDir)
- goto start_over;
- return err;
- };
- defer dir.close();
-
- var full_entry_buf = ArrayList(u8).init(allocator);
- defer full_entry_buf.deinit();
-
- while (%return dir.next()) |entry| {
- %return full_entry_buf.resize(full_path.len + entry.name.len + 1);
- const full_entry_path = full_entry_buf.toSlice();
- mem.copy(u8, full_entry_path, full_path);
- full_entry_path[full_path.len] = '/';
- mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
-
- %return deleteTree(allocator, full_entry_path);
+ if (err != error.IsDir)
+ return err;
}
+ {
+ var dir = Dir.open(allocator, full_path) %% |err| {
+ if (err == error.FileNotFound)
+ return;
+ if (err == error.NotDir)
+ continue :start_over;
+ return err;
+ };
+ defer dir.close();
+
+ var full_entry_buf = ArrayList(u8).init(allocator);
+ defer full_entry_buf.deinit();
+
+ while (%return dir.next()) |entry| {
+ %return full_entry_buf.resize(full_path.len + entry.name.len + 1);
+ const full_entry_path = full_entry_buf.toSlice();
+ mem.copy(u8, full_entry_path, full_path);
+ full_entry_path[full_path.len] = '/';
+ mem.copy(u8, full_entry_path[full_path.len + 1..], entry.name);
+
+ %return deleteTree(allocator, full_entry_path);
+ }
+ }
+ return deleteDir(allocator, full_path);
}
- return deleteDir(allocator, full_path);
}
pub const Dir = struct {
@@ -988,58 +988,59 @@ pub const Dir = struct {
/// Memory such as file names referenced in this returned entry becomes invalid
/// with subsequent calls to next, as well as when this ::Dir is deinitialized.
pub fn next(self: &Dir) -> %?Entry {
- start_over:
- if (self.index >= self.end_index) {
- if (self.buf.len == 0) {
- self.buf = %return self.allocator.alloc(u8, page_size);
- }
-
- while (true) {
- const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
- const err = linux.getErrno(result);
- if (err > 0) {
- switch (err) {
- posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
- posix.EINVAL => {
- self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
- continue;
- },
- else => return unexpectedErrorPosix(err),
- };
+ start_over: while (true) {
+ if (self.index >= self.end_index) {
+ if (self.buf.len == 0) {
+ self.buf = %return self.allocator.alloc(u8, page_size);
+ }
+
+ while (true) {
+ const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
+ const err = linux.getErrno(result);
+ if (err > 0) {
+ switch (err) {
+ posix.EBADF, posix.EFAULT, posix.ENOTDIR => unreachable,
+ posix.EINVAL => {
+ self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
+ continue;
+ },
+ else => return unexpectedErrorPosix(err),
+ }
+ }
+ if (result == 0)
+ return null;
+ self.index = 0;
+ self.end_index = result;
+ break;
}
- if (result == 0)
- return null;
- self.index = 0;
- self.end_index = result;
- break;
}
+ const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
+ const next_index = self.index + linux_entry.d_reclen;
+ self.index = next_index;
+
+ const name = cstr.toSlice(&linux_entry.d_name);
+
+ // skip . and .. entries
+ if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
+ continue :start_over;
+ }
+
+ const type_char = self.buf[next_index - 1];
+ const entry_kind = switch (type_char) {
+ posix.DT_BLK => Entry.Kind.BlockDevice,
+ posix.DT_CHR => Entry.Kind.CharacterDevice,
+ posix.DT_DIR => Entry.Kind.Directory,
+ posix.DT_FIFO => Entry.Kind.NamedPipe,
+ posix.DT_LNK => Entry.Kind.SymLink,
+ posix.DT_REG => Entry.Kind.File,
+ posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
+ else => Entry.Kind.Unknown,
+ };
+ return Entry {
+ .name = name,
+ .kind = entry_kind,
+ };
}
- const linux_entry = @ptrCast(& align(1) LinuxEntry, &self.buf[self.index]);
- const next_index = self.index + linux_entry.d_reclen;
- self.index = next_index;
-
- const name = cstr.toSlice(&linux_entry.d_name);
-
- // skip . and .. entries
- if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
- goto start_over;
- }
-
- const type_char = self.buf[next_index - 1];
- const entry_kind = switch (type_char) {
- posix.DT_BLK => Entry.Kind.BlockDevice,
- posix.DT_CHR => Entry.Kind.CharacterDevice,
- posix.DT_DIR => Entry.Kind.Directory,
- posix.DT_FIFO => Entry.Kind.NamedPipe,
- posix.DT_LNK => Entry.Kind.SymLink,
- posix.DT_REG => Entry.Kind.File,
- posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
- else => Entry.Kind.Unknown,
- };
- return Entry {
- .name = name,
- .kind = entry_kind,
- };
}
};
@@ -1422,6 +1423,54 @@ pub fn args() -> ArgIterator {
return ArgIterator.init();
}
+/// Caller must call freeArgs on result.
+pub fn argsAlloc(allocator: &mem.Allocator) -> %[]const []u8 {
+ // TODO refactor to only make 1 allocation.
+ var it = args();
+ var contents = %return Buffer.initSize(allocator, 0);
+ defer contents.deinit();
+
+ var slice_list = ArrayList(usize).init(allocator);
+ defer slice_list.deinit();
+
+ while (it.next(allocator)) |arg_or_err| {
+ const arg = %return arg_or_err;
+ defer allocator.free(arg);
+ %return contents.append(arg);
+ %return slice_list.append(arg.len);
+ }
+
+ const contents_slice = contents.toSliceConst();
+ const slice_sizes = slice_list.toSliceConst();
+ const slice_list_bytes = %return math.mul(usize, @sizeOf([]u8), slice_sizes.len);
+ const total_bytes = %return math.add(usize, slice_list_bytes, contents_slice.len);
+ const buf = %return allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes);
+ %defer allocator.free(buf);
+
+ const result_slice_list = ([][]u8)(buf[0..slice_list_bytes]);
+ const result_contents = buf[slice_list_bytes..];
+ mem.copy(u8, result_contents, contents_slice);
+
+ var contents_index: usize = 0;
+ for (slice_sizes) |len, i| {
+ const new_index = contents_index + len;
+ result_slice_list[i] = result_contents[contents_index..new_index];
+ contents_index = new_index;
+ }
+
+ return result_slice_list;
+}
+
+pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) {
+ var total_bytes: usize = 0;
+ for (args_alloc) |arg| {
+ total_bytes += @sizeOf([]u8) + arg.len;
+ }
+ const unaligned_allocated_buf = @ptrCast(&u8, args_alloc.ptr)[0..total_bytes];
+ const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf);
+ return allocator.free(aligned_allocated_buf);
+}
+
test "windows arg parsing" {
testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});
@@ -1494,6 +1543,39 @@ pub fn openSelfExe() -> %io.File {
}
}
+/// Get the directory path that contains the current executable.
+/// Caller owns returned memory.
+pub fn selfExeDirPath(allocator: &mem.Allocator) -> %[]u8 {
+ switch (builtin.os) {
+ Os.linux => {
+ // If the currently executing binary has been deleted,
+ // the file path looks something like `/a/b/c/exe (deleted)`
+ // This path cannot be opened, but it's valid for determining the directory
+ // the executable was in when it was run.
+ const full_exe_path = %return readLink(allocator, "/proc/self/exe");
+ %defer allocator.free(full_exe_path);
+ const dir = path.dirname(full_exe_path);
+ return allocator.shrink(u8, full_exe_path, dir.len);
+ },
+ Os.windows => {
+ @panic("TODO windows std.os.selfExeDirPath");
+ //buf_resize(out_path, 256);
+ //for (;;) {
+ // DWORD copied_amt = GetModuleFileName(nullptr, buf_ptr(out_path), buf_len(out_path));
+ // if (copied_amt <= 0) {
+ // return ErrorFileNotFound;
+ // }
+ // if (copied_amt < buf_len(out_path)) {
+ // buf_resize(out_path, copied_amt);
+ // return 0;
+ // }
+ // buf_resize(out_path, buf_len(out_path) * 2);
+ //}
+ },
+ else => @compileError("unimplemented: std.os.selfExeDirPath for " ++ @tagName(builtin.os)),
+ }
+}
+
pub fn isTty(handle: FileHandle) -> bool {
if (is_windows) {
return windows_util.windowsIsTty(handle);
diff --git a/std/os/linux.zig b/std/os/linux.zig
index 5951f9d7bc..f9baa43098 100644
--- a/std/os/linux.zig
+++ b/std/os/linux.zig
@@ -367,14 +367,14 @@ pub const TFD_CLOEXEC = O_CLOEXEC;
pub const TFD_TIMER_ABSTIME = 1;
pub const TFD_TIMER_CANCEL_ON_SET = (1 << 1);
-fn unsigned(s: i32) -> u32 { @bitCast(u32, s) }
-fn signed(s: u32) -> i32 { @bitCast(i32, s) }
-pub fn WEXITSTATUS(s: i32) -> i32 { signed((unsigned(s) & 0xff00) >> 8) }
-pub fn WTERMSIG(s: i32) -> i32 { signed(unsigned(s) & 0x7f) }
-pub fn WSTOPSIG(s: i32) -> i32 { WEXITSTATUS(s) }
-pub fn WIFEXITED(s: i32) -> bool { WTERMSIG(s) == 0 }
-pub fn WIFSTOPPED(s: i32) -> bool { (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00 }
-pub fn WIFSIGNALED(s: i32) -> bool { (unsigned(s)&0xffff)-%1 < 0xff }
+fn unsigned(s: i32) -> u32 { return @bitCast(u32, s); }
+fn signed(s: u32) -> i32 { return @bitCast(i32, s); }
+pub fn WEXITSTATUS(s: i32) -> i32 { return signed((unsigned(s) & 0xff00) >> 8); }
+pub fn WTERMSIG(s: i32) -> i32 { return signed(unsigned(s) & 0x7f); }
+pub fn WSTOPSIG(s: i32) -> i32 { return WEXITSTATUS(s); }
+pub fn WIFEXITED(s: i32) -> bool { return WTERMSIG(s) == 0; }
+pub fn WIFSTOPPED(s: i32) -> bool { return (u16)(((unsigned(s)&0xffff)*%0x10001)>>8) > 0x7f00; }
+pub fn WIFSIGNALED(s: i32) -> bool { return (unsigned(s)&0xffff)-%1 < 0xff; }
pub const winsize = extern struct {
@@ -387,31 +387,31 @@ pub const winsize = extern struct {
/// Get the errno from a syscall return value, or 0 for no error.
pub fn getErrno(r: usize) -> usize {
const signed_r = @bitCast(isize, r);
- if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0
+ return if (signed_r > -4096 and signed_r < 0) usize(-signed_r) else 0;
}
pub fn dup2(old: i32, new: i32) -> usize {
- arch.syscall2(arch.SYS_dup2, usize(old), usize(new))
+ return arch.syscall2(arch.SYS_dup2, usize(old), usize(new));
}
pub fn chdir(path: &const u8) -> usize {
- arch.syscall1(arch.SYS_chdir, @ptrToInt(path))
+ return arch.syscall1(arch.SYS_chdir, @ptrToInt(path));
}
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) -> usize {
- arch.syscall3(arch.SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp))
+ return arch.syscall3(arch.SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
}
pub fn fork() -> usize {
- arch.syscall0(arch.SYS_fork)
+ return arch.syscall0(arch.SYS_fork);
}
pub fn getcwd(buf: &u8, size: usize) -> usize {
- arch.syscall2(arch.SYS_getcwd, @ptrToInt(buf), size)
+ return arch.syscall2(arch.SYS_getcwd, @ptrToInt(buf), size);
}
pub fn getdents(fd: i32, dirp: &u8, count: usize) -> usize {
- arch.syscall3(arch.SYS_getdents, usize(fd), @ptrToInt(dirp), count)
+ return arch.syscall3(arch.SYS_getdents, usize(fd), @ptrToInt(dirp), count);
}
pub fn isatty(fd: i32) -> bool {
@@ -420,123 +420,123 @@ pub fn isatty(fd: i32) -> bool {
}
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) -> usize {
- arch.syscall3(arch.SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len)
+ return arch.syscall3(arch.SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
}
pub fn mkdir(path: &const u8, mode: u32) -> usize {
- arch.syscall2(arch.SYS_mkdir, @ptrToInt(path), mode)
+ return arch.syscall2(arch.SYS_mkdir, @ptrToInt(path), mode);
}
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize)
-> usize
{
- arch.syscall6(arch.SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
- @bitCast(usize, offset))
+ return arch.syscall6(arch.SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
+ @bitCast(usize, offset));
}
pub fn munmap(address: &u8, length: usize) -> usize {
- arch.syscall2(arch.SYS_munmap, @ptrToInt(address), length)
+ return arch.syscall2(arch.SYS_munmap, @ptrToInt(address), length);
}
pub fn read(fd: i32, buf: &u8, count: usize) -> usize {
- arch.syscall3(arch.SYS_read, usize(fd), @ptrToInt(buf), count)
+ return arch.syscall3(arch.SYS_read, usize(fd), @ptrToInt(buf), count);
}
pub fn rmdir(path: &const u8) -> usize {
- arch.syscall1(arch.SYS_rmdir, @ptrToInt(path))
+ return arch.syscall1(arch.SYS_rmdir, @ptrToInt(path));
}
pub fn symlink(existing: &const u8, new: &const u8) -> usize {
- arch.syscall2(arch.SYS_symlink, @ptrToInt(existing), @ptrToInt(new))
+ return arch.syscall2(arch.SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
}
pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) -> usize {
- arch.syscall4(arch.SYS_pread, usize(fd), @ptrToInt(buf), count, offset)
+ return arch.syscall4(arch.SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
}
pub fn pipe(fd: &[2]i32) -> usize {
- pipe2(fd, 0)
+ return pipe2(fd, 0);
}
pub fn pipe2(fd: &[2]i32, flags: usize) -> usize {
- arch.syscall2(arch.SYS_pipe2, @ptrToInt(fd), flags)
+ return arch.syscall2(arch.SYS_pipe2, @ptrToInt(fd), flags);
}
pub fn write(fd: i32, buf: &const u8, count: usize) -> usize {
- arch.syscall3(arch.SYS_write, usize(fd), @ptrToInt(buf), count)
+ return arch.syscall3(arch.SYS_write, usize(fd), @ptrToInt(buf), count);
}
pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) -> usize {
- arch.syscall4(arch.SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset)
+ return arch.syscall4(arch.SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset);
}
pub fn rename(old: &const u8, new: &const u8) -> usize {
- arch.syscall2(arch.SYS_rename, @ptrToInt(old), @ptrToInt(new))
+ return arch.syscall2(arch.SYS_rename, @ptrToInt(old), @ptrToInt(new));
}
pub fn open(path: &const u8, flags: u32, perm: usize) -> usize {
- arch.syscall3(arch.SYS_open, @ptrToInt(path), flags, perm)
+ return arch.syscall3(arch.SYS_open, @ptrToInt(path), flags, perm);
}
pub fn create(path: &const u8, perm: usize) -> usize {
- arch.syscall2(arch.SYS_creat, @ptrToInt(path), perm)
+ return arch.syscall2(arch.SYS_creat, @ptrToInt(path), perm);
}
pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) -> usize {
- arch.syscall4(arch.SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode)
+ return arch.syscall4(arch.SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
}
pub fn close(fd: i32) -> usize {
- arch.syscall1(arch.SYS_close, usize(fd))
+ return arch.syscall1(arch.SYS_close, usize(fd));
}
pub fn lseek(fd: i32, offset: isize, ref_pos: usize) -> usize {
- arch.syscall3(arch.SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos)
+ return arch.syscall3(arch.SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos);
}
pub fn exit(status: i32) -> noreturn {
_ = arch.syscall1(arch.SYS_exit, @bitCast(usize, isize(status)));
- unreachable
+ unreachable;
}
pub fn getrandom(buf: &u8, count: usize, flags: u32) -> usize {
- arch.syscall3(arch.SYS_getrandom, @ptrToInt(buf), count, usize(flags))
+ return arch.syscall3(arch.SYS_getrandom, @ptrToInt(buf), count, usize(flags));
}
pub fn kill(pid: i32, sig: i32) -> usize {
- arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig))
+ return arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig));
}
pub fn unlink(path: &const u8) -> usize {
- arch.syscall1(arch.SYS_unlink, @ptrToInt(path))
+ return arch.syscall1(arch.SYS_unlink, @ptrToInt(path));
}
pub fn waitpid(pid: i32, status: &i32, options: i32) -> usize {
- arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0)
+ return arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
}
pub fn nanosleep(req: &const timespec, rem: ?×pec) -> usize {
- arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem))
+ return arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
}
pub fn setuid(uid: u32) -> usize {
- arch.syscall1(arch.SYS_setuid, uid)
+ return arch.syscall1(arch.SYS_setuid, uid);
}
pub fn setgid(gid: u32) -> usize {
- arch.syscall1(arch.SYS_setgid, gid)
+ return arch.syscall1(arch.SYS_setgid, gid);
}
pub fn setreuid(ruid: u32, euid: u32) -> usize {
- arch.syscall2(arch.SYS_setreuid, ruid, euid)
+ return arch.syscall2(arch.SYS_setreuid, ruid, euid);
}
pub fn setregid(rgid: u32, egid: u32) -> usize {
- arch.syscall2(arch.SYS_setregid, rgid, egid)
+ return arch.syscall2(arch.SYS_setregid, rgid, egid);
}
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) -> usize {
- arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8)
+ return arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
}
pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) -> usize {
@@ -651,92 +651,70 @@ pub const iovec = extern struct {
iov_len: usize,
};
-//
-//const IF_NAMESIZE = 16;
-//
-//export struct ifreq {
-// ifrn_name: [IF_NAMESIZE]u8,
-// union {
-// ifru_addr: sockaddr,
-// ifru_dstaddr: sockaddr,
-// ifru_broadaddr: sockaddr,
-// ifru_netmask: sockaddr,
-// ifru_hwaddr: sockaddr,
-// ifru_flags: i16,
-// ifru_ivalue: i32,
-// ifru_mtu: i32,
-// ifru_map: ifmap,
-// ifru_slave: [IF_NAMESIZE]u8,
-// ifru_newname: [IF_NAMESIZE]u8,
-// ifru_data: &u8,
-// } ifr_ifru;
-//}
-//
-
pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> usize {
- arch.syscall3(arch.SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len))
+ return arch.syscall3(arch.SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> usize {
- arch.syscall3(arch.SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len))
+ return arch.syscall3(arch.SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
pub fn socket(domain: i32, socket_type: i32, protocol: i32) -> usize {
- arch.syscall3(arch.SYS_socket, usize(domain), usize(socket_type), usize(protocol))
+ return arch.syscall3(arch.SYS_socket, usize(domain), usize(socket_type), usize(protocol));
}
pub fn setsockopt(fd: i32, level: i32, optname: i32, optval: &const u8, optlen: socklen_t) -> usize {
- arch.syscall5(arch.SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen))
+ return arch.syscall5(arch.SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
}
pub fn getsockopt(fd: i32, level: i32, optname: i32, noalias optval: &u8, noalias optlen: &socklen_t) -> usize {
- arch.syscall5(arch.SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen))
+ return arch.syscall5(arch.SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
}
pub fn sendmsg(fd: i32, msg: &const arch.msghdr, flags: u32) -> usize {
- arch.syscall3(arch.SYS_sendmsg, usize(fd), @ptrToInt(msg), flags)
+ return arch.syscall3(arch.SYS_sendmsg, usize(fd), @ptrToInt(msg), flags);
}
pub fn connect(fd: i32, addr: &const sockaddr, len: socklen_t) -> usize {
- arch.syscall3(arch.SYS_connect, usize(fd), @ptrToInt(addr), usize(len))
+ return arch.syscall3(arch.SYS_connect, usize(fd), @ptrToInt(addr), usize(len));
}
pub fn recvmsg(fd: i32, msg: &arch.msghdr, flags: u32) -> usize {
- arch.syscall3(arch.SYS_recvmsg, usize(fd), @ptrToInt(msg), flags)
+ return arch.syscall3(arch.SYS_recvmsg, usize(fd), @ptrToInt(msg), flags);
}
pub fn recvfrom(fd: i32, noalias buf: &u8, len: usize, flags: u32,
noalias addr: ?&sockaddr, noalias alen: ?&socklen_t) -> usize
{
- arch.syscall6(arch.SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen))
+ return arch.syscall6(arch.SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
}
pub fn shutdown(fd: i32, how: i32) -> usize {
- arch.syscall2(arch.SYS_shutdown, usize(fd), usize(how))
+ return arch.syscall2(arch.SYS_shutdown, usize(fd), usize(how));
}
pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) -> usize {
- arch.syscall3(arch.SYS_bind, usize(fd), @ptrToInt(addr), usize(len))
+ return arch.syscall3(arch.SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
}
pub fn listen(fd: i32, backlog: i32) -> usize {
- arch.syscall2(arch.SYS_listen, usize(fd), usize(backlog))
+ return arch.syscall2(arch.SYS_listen, usize(fd), usize(backlog));
}
pub fn sendto(fd: i32, buf: &const u8, len: usize, flags: u32, addr: ?&const sockaddr, alen: socklen_t) -> usize {
- arch.syscall6(arch.SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen))
+ return arch.syscall6(arch.SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen));
}
pub fn socketpair(domain: i32, socket_type: i32, protocol: i32, fd: [2]i32) -> usize {
- arch.syscall4(arch.SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]))
+ return arch.syscall4(arch.SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]));
}
pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) -> usize {
- accept4(fd, addr, len, 0)
+ return accept4(fd, addr, len, 0);
}
pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: u32) -> usize {
- arch.syscall4(arch.SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags)
+ return arch.syscall4(arch.SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
}
// error NameTooLong;
@@ -771,7 +749,7 @@ pub const Stat = arch.Stat;
pub const timespec = arch.timespec;
pub fn fstat(fd: i32, stat_buf: &Stat) -> usize {
- arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf))
+ return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
}
pub const epoll_data = u64;
@@ -782,19 +760,19 @@ pub const epoll_event = extern struct {
};
pub fn epoll_create() -> usize {
- arch.syscall1(arch.SYS_epoll_create, usize(1))
+ return arch.syscall1(arch.SYS_epoll_create, usize(1));
}
pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) -> usize {
- arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev))
+ return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
}
pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: i32, timeout: i32) -> usize {
- arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout))
+ return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
}
pub fn timerfd_create(clockid: i32, flags: u32) -> usize {
- arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags))
+ return arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags));
}
pub const itimerspec = extern struct {
@@ -803,11 +781,11 @@ pub const itimerspec = extern struct {
};
pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) -> usize {
- arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value))
+ return arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value));
}
pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) -> usize {
- arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value))
+ return arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
}
test "import linux_test" {
diff --git a/std/os/linux_i386.zig b/std/os/linux_i386.zig
index 215670e3a9..ed49e33c2b 100644
--- a/std/os/linux_i386.zig
+++ b/std/os/linux_i386.zig
@@ -502,13 +502,3 @@ pub nakedcc fn restore_rt() {
: [number] "{eax}" (usize(SYS_rt_sigreturn))
: "rcx", "r11")
}
-
-export struct msghdr {
- msg_name: &u8,
- msg_namelen: socklen_t,
- msg_iov: &iovec,
- msg_iovlen: i32,
- msg_control: &u8,
- msg_controllen: socklen_t,
- msg_flags: i32,
-}
diff --git a/std/os/linux_x86_64.zig b/std/os/linux_x86_64.zig
index 6c94528df0..db78decde2 100644
--- a/std/os/linux_x86_64.zig
+++ b/std/os/linux_x86_64.zig
@@ -371,52 +371,52 @@ pub const F_GETOWN_EX = 16;
pub const F_GETOWNER_UIDS = 17;
pub fn syscall0(number: usize) -> usize {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub fn syscall1(number: usize, arg1: usize) -> usize {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub fn syscall2(number: usize, arg1: usize, arg2: usize) -> usize {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
[arg2] "{rsi}" (arg2)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
[arg2] "{rsi}" (arg2),
[arg3] "{rdx}" (arg3)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub fn syscall4(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
[arg2] "{rsi}" (arg2),
[arg3] "{rdx}" (arg3),
[arg4] "{r10}" (arg4)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
@@ -424,13 +424,13 @@ pub fn syscall5(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
[arg3] "{rdx}" (arg3),
[arg4] "{r10}" (arg4),
[arg5] "{r8}" (arg5)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize,
arg5: usize, arg6: usize) -> usize
{
- asm volatile ("syscall"
+ return asm volatile ("syscall"
: [ret] "={rax}" (-> usize)
: [number] "{rax}" (number),
[arg1] "{rdi}" (arg1),
@@ -439,14 +439,14 @@ pub fn syscall6(number: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usiz
[arg4] "{r10}" (arg4),
[arg5] "{r8}" (arg5),
[arg6] "{r9}" (arg6)
- : "rcx", "r11")
+ : "rcx", "r11");
}
pub nakedcc fn restore_rt() {
- asm volatile ("syscall"
+ return asm volatile ("syscall"
:
: [number] "{rax}" (usize(SYS_rt_sigreturn))
- : "rcx", "r11")
+ : "rcx", "r11");
}
diff --git a/std/os/path.zig b/std/os/path.zig
index 3fd7b5e2db..59e9a53027 100644
--- a/std/os/path.zig
+++ b/std/os/path.zig
@@ -749,21 +749,19 @@ pub fn relativeWindows(allocator: &Allocator, from: []const u8, to: []const u8)
const resolved_to = %return resolveWindows(allocator, [][]const u8{to});
defer if (clean_up_resolved_to) allocator.free(resolved_to);
- const result_is_to = if (drive(resolved_to)) |to_drive| {
- if (drive(resolved_from)) |from_drive| {
+ const result_is_to = if (drive(resolved_to)) |to_drive|
+ if (drive(resolved_from)) |from_drive|
asciiUpper(from_drive[0]) != asciiUpper(to_drive[0])
- } else {
+ else
true
- }
- } else if (networkShare(resolved_to)) |to_ns| {
- if (networkShare(resolved_from)) |from_ns| {
+ else if (networkShare(resolved_to)) |to_ns|
+ if (networkShare(resolved_from)) |from_ns|
!networkShareServersEql(to_ns, from_ns)
- } else {
+ else
true
- }
- } else {
- unreachable
- };
+ else
+ unreachable;
+
if (result_is_to) {
clean_up_resolved_to = false;
return resolved_to;
@@ -964,14 +962,16 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
// windows returns \\?\ prepended to the path
// we strip it because nobody wants \\?\ prepended to their path
- const final_len = if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
- var i: usize = 4;
- while (i < result) : (i += 1) {
- buf[i - 4] = buf[i];
+ const final_len = x: {
+ if (result > 4 and mem.startsWith(u8, buf, "\\\\?\\")) {
+ var i: usize = 4;
+ while (i < result) : (i += 1) {
+ buf[i - 4] = buf[i];
+ }
+ break :x result - 4;
+ } else {
+ break :x result;
}
- result - 4
- } else {
- result
};
return allocator.shrink(u8, buf, final_len);
@@ -1012,7 +1012,7 @@ pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 {
defer os.close(fd);
var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined;
- const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd);
+ const proc_path = %%fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd);
return os.readLink(allocator, proc_path);
},
diff --git a/std/os/windows/util.zig b/std/os/windows/util.zig
index b3fc095d43..5b2dc1efb8 100644
--- a/std/os/windows/util.zig
+++ b/std/os/windows/util.zig
@@ -16,11 +16,11 @@ pub fn windowsWaitSingle(handle: windows.HANDLE, milliseconds: windows.DWORD) ->
windows.WAIT_ABANDONED => error.WaitAbandoned,
windows.WAIT_OBJECT_0 => {},
windows.WAIT_TIMEOUT => error.WaitTimeOut,
- windows.WAIT_FAILED => {
+ windows.WAIT_FAILED => x: {
const err = windows.GetLastError();
- switch (err) {
+ break :x switch (err) {
else => os.unexpectedErrorWindows(err),
- }
+ };
},
else => error.Unexpected,
};
@@ -122,7 +122,7 @@ pub fn windowsOpen(file_path: []const u8, desired_access: windows.DWORD, share_m
/// Caller must free result.
pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap) -> %[]u8 {
// count bytes needed
- const bytes_needed = {
+ const bytes_needed = x: {
var bytes_needed: usize = 1; // 1 for the final null byte
var it = env_map.iterator();
while (it.next()) |pair| {
@@ -130,7 +130,7 @@ pub fn createWindowsEnvBlock(allocator: &mem.Allocator, env_map: &const BufMap)
// +1 for null byte
bytes_needed += pair.key.len + pair.value.len + 2;
}
- bytes_needed
+ break :x bytes_needed;
};
const result = %return allocator.alloc(u8, bytes_needed);
%defer allocator.free(result);
diff --git a/std/rand.zig b/std/rand.zig
index 09e0c8ac78..73801a078f 100644
--- a/std/rand.zig
+++ b/std/rand.zig
@@ -28,9 +28,9 @@ pub const Rand = struct {
/// Initialize random state with the given seed.
pub fn init(seed: usize) -> Rand {
- Rand {
+ return Rand {
.rng = Rng.init(seed),
- }
+ };
}
/// Get an integer or boolean with random bits.
@@ -78,13 +78,13 @@ pub const Rand = struct {
const end_uint = uint(end);
const total_range = math.absCast(start) + end_uint;
const value = r.range(uint, 0, total_range);
- const result = if (value < end_uint) {
- T(value)
- } else if (value == end_uint) {
- start
- } else {
+ const result = if (value < end_uint) x: {
+ break :x T(value);
+ } else if (value == end_uint) x: {
+ break :x start;
+ } else x: {
// Can't overflow because the range is over signed ints
- %%math.negateCast(value - end_uint)
+ break :x %%math.negateCast(value - end_uint);
};
return result;
} else {
@@ -114,13 +114,13 @@ pub const Rand = struct {
// const rand_bits = r.rng.scalar(int) & mask;
// return @float_compose(T, false, 0, rand_bits) - 1.0
const int_type = @IntType(false, @sizeOf(T) * 8);
- const precision = if (T == f32) {
+ const precision = if (T == f32)
16777216
- } else if (T == f64) {
+ else if (T == f64)
9007199254740992
- } else {
+ else
@compileError("unknown floating point type")
- };
+ ;
return T(r.range(int_type, 0, precision)) / T(precision);
}
};
@@ -133,7 +133,7 @@ fn MersenneTwister(
comptime t: math.Log2Int(int), comptime c: int,
comptime l: math.Log2Int(int), comptime f: int) -> type
{
- struct {
+ return struct {
const Self = this;
array: [n]int,
@@ -189,7 +189,7 @@ fn MersenneTwister(
return x;
}
- }
+ };
}
test "rand float 32" {
diff --git a/std/sort.zig b/std/sort.zig
index d02d685e07..a36a5e1747 100644
--- a/std/sort.zig
+++ b/std/sort.zig
@@ -1,75 +1,966 @@
-const assert = @import("debug.zig").assert;
-const mem = @import("mem.zig");
-const math = @import("math/index.zig");
+const std = @import("index.zig");
+const assert = std.debug.assert;
+const mem = std.mem;
+const math = std.math;
+const builtin = @import("builtin");
-pub const Cmp = math.Cmp;
-
-/// Stable sort using O(1) space. Currently implemented as insertion sort.
-pub fn sort_stable(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) {
- {var i: usize = 1; while (i < array.len) : (i += 1) {
- const x = array[i];
+/// Stable in-place sort. O(n) best case, O(pow(n, 2)) worst case. O(1) memory (no allocator required).
+pub fn insertionSort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)->bool) {
+ {var i: usize = 1; while (i < items.len) : (i += 1) {
+ const x = items[i];
var j: usize = i;
- while (j > 0 and cmp(array[j - 1], x) == Cmp.Greater) : (j -= 1) {
- array[j] = array[j - 1];
+ while (j > 0 and lessThan(x, items[j - 1])) : (j -= 1) {
+ items[j] = items[j - 1];
}
- array[j] = x;
+ items[j] = x;
}}
}
-/// Unstable sort using O(n) stack space. Currently implemented as quicksort.
-pub fn sort(comptime T: type, array: []T, comptime cmp: fn(a: &const T, b: &const T)->Cmp) {
- if (array.len > 0) {
- quicksort(T, array, 0, array.len - 1, cmp);
+const Range = struct {
+ start: usize,
+ end: usize,
+
+ fn init(start: usize, end: usize) -> Range {
+ return Range { .start = start, .end = end };
+ }
+
+ fn length(self: &const Range) -> usize {
+ return self.end - self.start;
+ }
+};
+
+
+const Iterator = struct {
+ size: usize,
+ power_of_two: usize,
+ numerator: usize,
+ decimal: usize,
+ denominator: usize,
+ decimal_step: usize,
+ numerator_step: usize,
+
+ fn init(size2: usize, min_level: usize) -> Iterator {
+ const power_of_two = math.floorPowerOfTwo(usize, size2);
+ const denominator = power_of_two / min_level;
+ return Iterator {
+ .numerator = 0,
+ .decimal = 0,
+ .size = size2,
+ .power_of_two = power_of_two,
+ .denominator = denominator,
+ .decimal_step = size2 / denominator,
+ .numerator_step = size2 % denominator,
+ };
+ }
+
+ fn begin(self: &Iterator) {
+ self.numerator = 0;
+ self.decimal = 0;
+ }
+
+ fn nextRange(self: &Iterator) -> Range {
+ const start = self.decimal;
+
+ self.decimal += self.decimal_step;
+ self.numerator += self.numerator_step;
+ if (self.numerator >= self.denominator) {
+ self.numerator -= self.denominator;
+ self.decimal += 1;
+ }
+
+ return Range {.start = start, .end = self.decimal};
+ }
+
+ fn finished(self: &Iterator) -> bool {
+ return self.decimal >= self.size;
+ }
+
+ fn nextLevel(self: &Iterator) -> bool {
+ self.decimal_step += self.decimal_step;
+ self.numerator_step += self.numerator_step;
+ if (self.numerator_step >= self.denominator) {
+ self.numerator_step -= self.denominator;
+ self.decimal_step += 1;
+ }
+
+ return (self.decimal_step < self.size);
+ }
+
+ fn length(self: &Iterator) -> usize {
+ return self.decimal_step;
+ }
+};
+
+const Pull = struct {
+ from: usize,
+ to: usize,
+ count: usize,
+ range: Range,
+};
+
+/// Stable in-place sort. O(n) best case, O(n*log(n)) worst case and average case. O(1) memory (no allocator required).
+/// Currently implemented as block sort.
+pub fn sort(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)->bool) {
+ // Implementation ported from https://github.com/BonzaiThePenguin/WikiSort/blob/master/WikiSort.c
+ var cache: [512]T = undefined;
+
+ if (items.len < 4) {
+ if (items.len == 3) {
+ // hard coded insertion sort
+ if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);
+ if (lessThan(items[2], items[1])) {
+ mem.swap(T, &items[1], &items[2]);
+ if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);
+ }
+ } else if (items.len == 2) {
+ if (lessThan(items[1], items[0])) mem.swap(T, &items[0], &items[1]);
+ }
+ return;
+ }
+
+ // sort groups of 4-8 items at a time using an unstable sorting network,
+ // but keep track of the original item orders to force it to be stable
+ // http://pages.ripco.net/~jgamble/nw.html
+ var iterator = Iterator.init(items.len, 4);
+ while (!iterator.finished()) {
+ var order = []u8{0, 1, 2, 3, 4, 5, 6, 7};
+ const range = iterator.nextRange();
+
+ const sliced_items = items[range.start..];
+ switch (range.length()) {
+ 8 => {
+ swap(T, sliced_items, lessThan, &order, 0, 1);
+ swap(T, sliced_items, lessThan, &order, 2, 3);
+ swap(T, sliced_items, lessThan, &order, 4, 5);
+ swap(T, sliced_items, lessThan, &order, 6, 7);
+ swap(T, sliced_items, lessThan, &order, 0, 2);
+ swap(T, sliced_items, lessThan, &order, 1, 3);
+ swap(T, sliced_items, lessThan, &order, 4, 6);
+ swap(T, sliced_items, lessThan, &order, 5, 7);
+ swap(T, sliced_items, lessThan, &order, 1, 2);
+ swap(T, sliced_items, lessThan, &order, 5, 6);
+ swap(T, sliced_items, lessThan, &order, 0, 4);
+ swap(T, sliced_items, lessThan, &order, 3, 7);
+ swap(T, sliced_items, lessThan, &order, 1, 5);
+ swap(T, sliced_items, lessThan, &order, 2, 6);
+ swap(T, sliced_items, lessThan, &order, 1, 4);
+ swap(T, sliced_items, lessThan, &order, 3, 6);
+ swap(T, sliced_items, lessThan, &order, 2, 4);
+ swap(T, sliced_items, lessThan, &order, 3, 5);
+ swap(T, sliced_items, lessThan, &order, 3, 4);
+ },
+ 7 => {
+ swap(T, sliced_items, lessThan, &order, 1, 2);
+ swap(T, sliced_items, lessThan, &order, 3, 4);
+ swap(T, sliced_items, lessThan, &order, 5, 6);
+ swap(T, sliced_items, lessThan, &order, 0, 2);
+ swap(T, sliced_items, lessThan, &order, 3, 5);
+ swap(T, sliced_items, lessThan, &order, 4, 6);
+ swap(T, sliced_items, lessThan, &order, 0, 1);
+ swap(T, sliced_items, lessThan, &order, 4, 5);
+ swap(T, sliced_items, lessThan, &order, 2, 6);
+ swap(T, sliced_items, lessThan, &order, 0, 4);
+ swap(T, sliced_items, lessThan, &order, 1, 5);
+ swap(T, sliced_items, lessThan, &order, 0, 3);
+ swap(T, sliced_items, lessThan, &order, 2, 5);
+ swap(T, sliced_items, lessThan, &order, 1, 3);
+ swap(T, sliced_items, lessThan, &order, 2, 4);
+ swap(T, sliced_items, lessThan, &order, 2, 3);
+ },
+ 6 => {
+ swap(T, sliced_items, lessThan, &order, 1, 2);
+ swap(T, sliced_items, lessThan, &order, 4, 5);
+ swap(T, sliced_items, lessThan, &order, 0, 2);
+ swap(T, sliced_items, lessThan, &order, 3, 5);
+ swap(T, sliced_items, lessThan, &order, 0, 1);
+ swap(T, sliced_items, lessThan, &order, 3, 4);
+ swap(T, sliced_items, lessThan, &order, 2, 5);
+ swap(T, sliced_items, lessThan, &order, 0, 3);
+ swap(T, sliced_items, lessThan, &order, 1, 4);
+ swap(T, sliced_items, lessThan, &order, 2, 4);
+ swap(T, sliced_items, lessThan, &order, 1, 3);
+ swap(T, sliced_items, lessThan, &order, 2, 3);
+ },
+ 5 => {
+ swap(T, sliced_items, lessThan, &order, 0, 1);
+ swap(T, sliced_items, lessThan, &order, 3, 4);
+ swap(T, sliced_items, lessThan, &order, 2, 4);
+ swap(T, sliced_items, lessThan, &order, 2, 3);
+ swap(T, sliced_items, lessThan, &order, 1, 4);
+ swap(T, sliced_items, lessThan, &order, 0, 3);
+ swap(T, sliced_items, lessThan, &order, 0, 2);
+ swap(T, sliced_items, lessThan, &order, 1, 3);
+ swap(T, sliced_items, lessThan, &order, 1, 2);
+ },
+ 4 => {
+ swap(T, sliced_items, lessThan, &order, 0, 1);
+ swap(T, sliced_items, lessThan, &order, 2, 3);
+ swap(T, sliced_items, lessThan, &order, 0, 2);
+ swap(T, sliced_items, lessThan, &order, 1, 3);
+ swap(T, sliced_items, lessThan, &order, 1, 2);
+ },
+ else => {},
+ }
+ }
+ if (items.len < 8) return;
+
+ // then merge sort the higher levels, which can be 8-15, 16-31, 32-63, 64-127, etc.
+ while (true) {
+ // if every A and B block will fit into the cache, use a special branch specifically for merging with the cache
+ // (we use < rather than <= since the block size might be one more than iterator.length())
+ if (iterator.length() < cache.len) {
+ // if four subarrays fit into the cache, it's faster to merge both pairs of subarrays into the cache,
+ // then merge the two merged subarrays from the cache back into the original array
+ if ((iterator.length() + 1) * 4 <= cache.len and iterator.length() * 4 <= items.len) {
+ iterator.begin();
+ while (!iterator.finished()) {
+ // merge A1 and B1 into the cache
+ var A1 = iterator.nextRange();
+ var B1 = iterator.nextRange();
+ var A2 = iterator.nextRange();
+ var B2 = iterator.nextRange();
+
+ if (lessThan(items[B1.end - 1], items[A1.start])) {
+ // the two ranges are in reverse order, so copy them in reverse order into the cache
+ mem.copy(T, cache[B1.length()..], items[A1.start..A1.end]);
+ mem.copy(T, cache[0..], items[B1.start..B1.end]);
+ } else if (lessThan(items[B1.start], items[A1.end - 1])) {
+ // these two ranges weren't already in order, so merge them into the cache
+ mergeInto(T, items, A1, B1, lessThan, cache[0..]);
+ } else {
+ // if A1, B1, A2, and B2 are all in order, skip doing anything else
+ if (!lessThan(items[B2.start], items[A2.end - 1]) and !lessThan(items[A2.start], items[B1.end - 1])) continue;
+
+ // copy A1 and B1 into the cache in the same order
+ mem.copy(T, cache[0..], items[A1.start..A1.end]);
+ mem.copy(T, cache[A1.length()..], items[B1.start..B1.end]);
+ }
+ A1 = Range.init(A1.start, B1.end);
+
+ // merge A2 and B2 into the cache
+ if (lessThan(items[B2.end - 1], items[A2.start])) {
+ // the two ranges are in reverse order, so copy them in reverse order into the cache
+ mem.copy(T, cache[A1.length() + B2.length()..], items[A2.start..A2.end]);
+ mem.copy(T, cache[A1.length()..], items[B2.start..B2.end]);
+ } else if (lessThan(items[B2.start], items[A2.end - 1])) {
+ // these two ranges weren't already in order, so merge them into the cache
+ mergeInto(T, items, A2, B2, lessThan, cache[A1.length()..]);
+ } else {
+ // copy A2 and B2 into the cache in the same order
+ mem.copy(T, cache[A1.length()..], items[A2.start..A2.end]);
+ mem.copy(T, cache[A1.length() + A2.length()..], items[B2.start..B2.end]);
+ }
+ A2 = Range.init(A2.start, B2.end);
+
+ // merge A1 and A2 from the cache into the items
+ const A3 = Range.init(0, A1.length());
+ const B3 = Range.init(A1.length(), A1.length() + A2.length());
+
+ if (lessThan(cache[B3.end - 1], cache[A3.start])) {
+ // the two ranges are in reverse order, so copy them in reverse order into the items
+ mem.copy(T, items[A1.start + A2.length()..], cache[A3.start..A3.end]);
+ mem.copy(T, items[A1.start..], cache[B3.start..B3.end]);
+ } else if (lessThan(cache[B3.start], cache[A3.end - 1])) {
+ // these two ranges weren't already in order, so merge them back into the items
+ mergeInto(T, cache[0..], A3, B3, lessThan, items[A1.start..]);
+ } else {
+ // copy A3 and B3 into the items in the same order
+ mem.copy(T, items[A1.start..], cache[A3.start..A3.end]);
+ mem.copy(T, items[A1.start + A1.length()..], cache[B3.start..B3.end]);
+ }
+ }
+
+ // we merged two levels at the same time, so we're done with this level already
+ // (iterator.nextLevel() is called again at the bottom of this outer merge loop)
+ _ = iterator.nextLevel();
+
+ } else {
+ iterator.begin();
+ while (!iterator.finished()) {
+ var A = iterator.nextRange();
+ var B = iterator.nextRange();
+
+ if (lessThan(items[B.end - 1], items[A.start])) {
+ // the two ranges are in reverse order, so a simple rotation should fix it
+ mem.rotate(T, items[A.start..B.end], A.length());
+ } else if (lessThan(items[B.start], items[A.end - 1])) {
+ // these two ranges weren't already in order, so we'll need to merge them!
+ mem.copy(T, cache[0..], items[A.start..A.end]);
+ mergeExternal(T, items, A, B, lessThan, cache[0..]);
+ }
+ }
+ }
+ } else {
+ // this is where the in-place merge logic starts!
+ // 1. pull out two internal buffers each containing √A unique values
+ // 1a. adjust block_size and buffer_size if we couldn't find enough unique values
+ // 2. loop over the A and B subarrays within this level of the merge sort
+ // 3. break A and B into blocks of size 'block_size'
+ // 4. "tag" each of the A blocks with values from the first internal buffer
+ // 5. roll the A blocks through the B blocks and drop/rotate them where they belong
+ // 6. merge each A block with any B values that follow, using the cache or the second internal buffer
+ // 7. sort the second internal buffer if it exists
+ // 8. redistribute the two internal buffers back into the items
+
+ var block_size: usize = math.sqrt(iterator.length());
+ var buffer_size = iterator.length()/block_size + 1;
+
+ // as an optimization, we really only need to pull out the internal buffers once for each level of merges
+ // after that we can reuse the same buffers over and over, then redistribute it when we're finished with this level
+ var A: Range = undefined;
+ var B: Range = undefined;
+ var index: usize = 0;
+ var last: usize = 0;
+ var count: usize = 0;
+ var find: usize = 0;
+ var start: usize = 0;
+ var pull_index: usize = 0;
+ var pull = []Pull{
+ Pull {.from = 0, .to = 0, .count = 0, .range = Range.init(0, 0),},
+ Pull {.from = 0, .to = 0, .count = 0, .range = Range.init(0, 0),},
+ };
+
+ var buffer1 = Range.init(0, 0);
+ var buffer2 = Range.init(0, 0);
+
+ // find two internal buffers of size 'buffer_size' each
+ find = buffer_size + buffer_size;
+ var find_separately = false;
+
+ if (block_size <= cache.len) {
+ // if every A block fits into the cache then we won't need the second internal buffer,
+ // so we really only need to find 'buffer_size' unique values
+ find = buffer_size;
+ } else if (find > iterator.length()) {
+ // we can't fit both buffers into the same A or B subarray, so find two buffers separately
+ find = buffer_size;
+ find_separately = true;
+ }
+
+ // we need to find either a single contiguous space containing 2√A unique values (which will be split up into two buffers of size √A each),
+ // or we need to find one buffer of < 2√A unique values, and a second buffer of √A unique values,
+ // OR if we couldn't find that many unique values, we need the largest possible buffer we can get
+
+ // in the case where it couldn't find a single buffer of at least √A unique values,
+ // all of the Merge steps must be replaced by a different merge algorithm (MergeInPlace)
+ iterator.begin();
+ while (!iterator.finished()) {
+ A = iterator.nextRange();
+ B = iterator.nextRange();
+
+ // just store information about where the values will be pulled from and to,
+ // as well as how many values there are, to create the two internal buffers
+
+ // check A for the number of unique values we need to fill an internal buffer
+ // these values will be pulled out to the start of A
+ last = A.start;
+ count = 1;
+ while (count < find) : ({last = index; count += 1;}) {
+ index = findLastForward(T, items, items[last], Range.init(last + 1, A.end), lessThan, find - count);
+ if (index == A.end) break;
+ }
+ index = last;
+
+ if (count >= buffer_size) {
+ // keep track of the range within the items where we'll need to "pull out" these values to create the internal buffer
+ pull[pull_index] = Pull {
+ .range = Range.init(A.start, B.end),
+ .count = count,
+ .from = index,
+ .to = A.start,
+ };
+ pull_index = 1;
+
+ if (count == buffer_size + buffer_size) {
+ // we were able to find a single contiguous section containing 2√A unique values,
+ // so this section can be used to contain both of the internal buffers we'll need
+ buffer1 = Range.init(A.start, A.start + buffer_size);
+ buffer2 = Range.init(A.start + buffer_size, A.start + count);
+ break;
+ } else if (find == buffer_size + buffer_size) {
+ // we found a buffer that contains at least √A unique values, but did not contain the full 2√A unique values,
+ // so we still need to find a second separate buffer of at least √A unique values
+ buffer1 = Range.init(A.start, A.start + count);
+ find = buffer_size;
+ } else if (block_size <= cache.len) {
+ // we found the first and only internal buffer that we need, so we're done!
+ buffer1 = Range.init(A.start, A.start + count);
+ break;
+ } else if (find_separately) {
+ // found one buffer, but now find the other one
+ buffer1 = Range.init(A.start, A.start + count);
+ find_separately = false;
+ } else {
+ // we found a second buffer in an 'A' subarray containing √A unique values, so we're done!
+ buffer2 = Range.init(A.start, A.start + count);
+ break;
+ }
+ } else if (pull_index == 0 and count > buffer1.length()) {
+ // keep track of the largest buffer we were able to find
+ buffer1 = Range.init(A.start, A.start + count);
+ pull[pull_index] = Pull {
+ .range = Range.init(A.start, B.end),
+ .count = count,
+ .from = index,
+ .to = A.start,
+ };
+ }
+
+ // check B for the number of unique values we need to fill an internal buffer
+ // these values will be pulled out to the end of B
+ last = B.end - 1;
+ count = 1;
+ while (count < find) : ({last = index - 1; count += 1;}) {
+ index = findFirstBackward(T, items, items[last], Range.init(B.start, last), lessThan, find - count);
+ if (index == B.start) break;
+ }
+ index = last;
+
+ if (count >= buffer_size) {
+ // keep track of the range within the items where we'll need to "pull out" these values to create the internal buffe
+ pull[pull_index] = Pull {
+ .range = Range.init(A.start, B.end),
+ .count = count,
+ .from = index,
+ .to = B.end,
+ };
+ pull_index = 1;
+
+ if (count == buffer_size + buffer_size) {
+ // we were able to find a single contiguous section containing 2√A unique values,
+ // so this section can be used to contain both of the internal buffers we'll need
+ buffer1 = Range.init(B.end - count, B.end - buffer_size);
+ buffer2 = Range.init(B.end - buffer_size, B.end);
+ break;
+ } else if (find == buffer_size + buffer_size) {
+ // we found a buffer that contains at least √A unique values, but did not contain the full 2√A unique values,
+ // so we still need to find a second separate buffer of at least √A unique values
+ buffer1 = Range.init(B.end - count, B.end);
+ find = buffer_size;
+ } else if (block_size <= cache.len) {
+ // we found the first and only internal buffer that we need, so we're done!
+ buffer1 = Range.init(B.end - count, B.end);
+ break;
+ } else if (find_separately) {
+ // found one buffer, but now find the other one
+ buffer1 = Range.init(B.end - count, B.end);
+ find_separately = false;
+ } else {
+ // buffer2 will be pulled out from a 'B' subarray, so if the first buffer was pulled out from the corresponding 'A' subarray,
+ // we need to adjust the end point for that A subarray so it knows to stop redistributing its values before reaching buffer2
+ if (pull[0].range.start == A.start) pull[0].range.end -= pull[1].count;
+
+ // we found a second buffer in an 'B' subarray containing √A unique values, so we're done!
+ buffer2 = Range.init(B.end - count, B.end);
+ break;
+ }
+ } else if (pull_index == 0 and count > buffer1.length()) {
+ // keep track of the largest buffer we were able to find
+ buffer1 = Range.init(B.end - count, B.end);
+ pull[pull_index] = Pull {
+ .range = Range.init(A.start, B.end),
+ .count = count,
+ .from = index,
+ .to = B.end,
+ };
+ }
+ }
+
+ // pull out the two ranges so we can use them as internal buffers
+ pull_index = 0;
+ while (pull_index < 2) : (pull_index += 1) {
+ const length = pull[pull_index].count;
+
+ if (pull[pull_index].to < pull[pull_index].from) {
+ // we're pulling the values out to the left, which means the start of an A subarray
+ index = pull[pull_index].from;
+ count = 1;
+ while (count < length) : (count += 1) {
+ index = findFirstBackward(T, items, items[index - 1], Range.init(pull[pull_index].to, pull[pull_index].from - (count - 1)), lessThan, length - count);
+ const range = Range.init(index + 1, pull[pull_index].from + 1);
+ mem.rotate(T, items[range.start..range.end], range.length() - count);
+ pull[pull_index].from = index + count;
+ }
+ } else if (pull[pull_index].to > pull[pull_index].from) {
+ // we're pulling values out to the right, which means the end of a B subarray
+ index = pull[pull_index].from + 1;
+ count = 1;
+ while (count < length) : (count += 1) {
+ index = findLastForward(T, items, items[index], Range.init(index, pull[pull_index].to), lessThan, length - count);
+ const range = Range.init(pull[pull_index].from, index - 1);
+ mem.rotate(T, items[range.start..range.end], count);
+ pull[pull_index].from = index - 1 - count;
+ }
+ }
+ }
+
+ // adjust block_size and buffer_size based on the values we were able to pull out
+ buffer_size = buffer1.length();
+ block_size = iterator.length()/buffer_size + 1;
+
+ // the first buffer NEEDS to be large enough to tag each of the evenly sized A blocks,
+ // so this was originally here to test the math for adjusting block_size above
+ // assert((iterator.length() + 1)/block_size <= buffer_size);
+
+ // now that the two internal buffers have been created, it's time to merge each A+B combination at this level of the merge sort!
+ iterator.begin();
+ while (!iterator.finished()) {
+ A = iterator.nextRange();
+ B = iterator.nextRange();
+
+ // remove any parts of A or B that are being used by the internal buffers
+ start = A.start;
+ if (start == pull[0].range.start) {
+ if (pull[0].from > pull[0].to) {
+ A.start += pull[0].count;
+
+ // if the internal buffer takes up the entire A or B subarray, then there's nothing to merge
+ // this only happens for very small subarrays, like √4 = 2, 2 * (2 internal buffers) = 4,
+ // which also only happens when cache.len is small or 0 since it'd otherwise use MergeExternal
+ if (A.length() == 0) continue;
+ } else if (pull[0].from < pull[0].to) {
+ B.end -= pull[0].count;
+ if (B.length() == 0) continue;
+ }
+ }
+ if (start == pull[1].range.start) {
+ if (pull[1].from > pull[1].to) {
+ A.start += pull[1].count;
+ if (A.length() == 0) continue;
+ } else if (pull[1].from < pull[1].to) {
+ B.end -= pull[1].count;
+ if (B.length() == 0) continue;
+ }
+ }
+
+ if (lessThan(items[B.end - 1], items[A.start])) {
+ // the two ranges are in reverse order, so a simple rotation should fix it
+ mem.rotate(T, items[A.start..B.end], A.length());
+ } else if (lessThan(items[A.end], items[A.end - 1])) {
+ // these two ranges weren't already in order, so we'll need to merge them!
+ var findA: usize = undefined;
+
+ // break the remainder of A into blocks. firstA is the uneven-sized first A block
+ var blockA = Range.init(A.start, A.end);
+ var firstA = Range.init(A.start, A.start + blockA.length() % block_size);
+
+ // swap the first value of each A block with the value in buffer1
+ var indexA = buffer1.start;
+ index = firstA.end;
+ while (index < blockA.end) : ({indexA += 1; index += block_size;}) {
+ mem.swap(T, &items[indexA], &items[index]);
+ }
+
+ // start rolling the A blocks through the B blocks!
+ // whenever we leave an A block behind, we'll need to merge the previous A block with any B blocks that follow it, so track that information as well
+ var lastA = firstA;
+ var lastB = Range.init(0, 0);
+ var blockB = Range.init(B.start, B.start + math.min(block_size, B.length()));
+ blockA.start += firstA.length();
+ indexA = buffer1.start;
+
+ // if the first unevenly sized A block fits into the cache, copy it there for when we go to Merge it
+ // otherwise, if the second buffer is available, block swap the contents into that
+ if (lastA.length() <= cache.len) {
+ mem.copy(T, cache[0..], items[lastA.start..lastA.end]);
+ } else if (buffer2.length() > 0) {
+ blockSwap(T, items, lastA.start, buffer2.start, lastA.length());
+ }
+
+ if (blockA.length() > 0) {
+ while (true) {
+ // if there's a previous B block and the first value of the minimum A block is <= the last value of the previous B block,
+ // then drop that minimum A block behind. or if there are no B blocks left then keep dropping the remaining A blocks.
+ if ((lastB.length() > 0 and !lessThan(items[lastB.end - 1], items[indexA])) or blockB.length() == 0) {
+ // figure out where to split the previous B block, and rotate it at the split
+ const B_split = binaryFirst(T, items, items[indexA], lastB, lessThan);
+ const B_remaining = lastB.end - B_split;
+
+ // swap the minimum A block to the beginning of the rolling A blocks
+ var minA = blockA.start;
+ findA = minA + block_size;
+ while (findA < blockA.end) : (findA += block_size) {
+ if (lessThan(items[findA], items[minA])) {
+ minA = findA;
+ }
+ }
+ blockSwap(T, items, blockA.start, minA, block_size);
+
+ // swap the first item of the previous A block back with its original value, which is stored in buffer1
+ mem.swap(T, &items[blockA.start], &items[indexA]);
+ indexA += 1;
+
+ // locally merge the previous A block with the B values that follow it
+ // if lastA fits into the external cache we'll use that (with MergeExternal),
+ // or if the second internal buffer exists we'll use that (with MergeInternal),
+ // or failing that we'll use a strictly in-place merge algorithm (MergeInPlace)
+
+ if (lastA.length() <= cache.len) {
+ mergeExternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, cache[0..]);
+ } else if (buffer2.length() > 0) {
+ mergeInternal(T, items, lastA, Range.init(lastA.end, B_split), lessThan, buffer2);
+ } else {
+ mergeInPlace(T, items, lastA, Range.init(lastA.end, B_split), lessThan);
+ }
+
+ if (buffer2.length() > 0 or block_size <= cache.len) {
+ // copy the previous A block into the cache or buffer2, since that's where we need it to be when we go to merge it anyway
+ if (block_size <= cache.len) {
+ mem.copy(T, cache[0..], items[blockA.start..blockA.start + block_size]);
+ } else {
+ blockSwap(T, items, blockA.start, buffer2.start, block_size);
+ }
+
+ // this is equivalent to rotating, but faster
+ // the area normally taken up by the A block is either the contents of buffer2, or data we don't need anymore since we memcopied it
+ // either way, we don't need to retain the order of those items, so instead of rotating we can just block swap B to where it belongs
+ blockSwap(T, items, B_split, blockA.start + block_size - B_remaining, B_remaining);
+ } else {
+ // we are unable to use the 'buffer2' trick to speed up the rotation operation since buffer2 doesn't exist, so perform a normal rotation
+ mem.rotate(T, items[B_split..blockA.start + block_size], blockA.start - B_split);
+ }
+
+ // update the range for the remaining A blocks, and the range remaining from the B block after it was split
+ lastA = Range.init(blockA.start - B_remaining, blockA.start - B_remaining + block_size);
+ lastB = Range.init(lastA.end, lastA.end + B_remaining);
+
+ // if there are no more A blocks remaining, this step is finished!
+ blockA.start += block_size;
+ if (blockA.length() == 0)
+ break;
+
+ } else if (blockB.length() < block_size) {
+ // move the last B block, which is unevenly sized, to before the remaining A blocks, by using a rotation
+ // the cache is disabled here since it might contain the contents of the previous A block
+ mem.rotate(T, items[blockA.start..blockB.end], blockB.start - blockA.start);
+
+ lastB = Range.init(blockA.start, blockA.start + blockB.length());
+ blockA.start += blockB.length();
+ blockA.end += blockB.length();
+ blockB.end = blockB.start;
+ } else {
+ // roll the leftmost A block to the end by swapping it with the next B block
+ blockSwap(T, items, blockA.start, blockB.start, block_size);
+ lastB = Range.init(blockA.start, blockA.start + block_size);
+
+ blockA.start += block_size;
+ blockA.end += block_size;
+ blockB.start += block_size;
+
+ if (blockB.end > B.end - block_size) {
+ blockB.end = B.end;
+ } else {
+ blockB.end += block_size;
+ }
+ }
+ }
+ }
+
+ // merge the last A block with the remaining B values
+ if (lastA.length() <= cache.len) {
+ mergeExternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, cache[0..]);
+ } else if (buffer2.length() > 0) {
+ mergeInternal(T, items, lastA, Range.init(lastA.end, B.end), lessThan, buffer2);
+ } else {
+ mergeInPlace(T, items, lastA, Range.init(lastA.end, B.end), lessThan);
+ }
+ }
+ }
+
+ // when we're finished with this merge step we should have the one or two internal buffers left over, where the second buffer is all jumbled up
+ // insertion sort the second buffer, then redistribute the buffers back into the items using the opposite process used for creating the buffer
+
+ // while an unstable sort like quicksort could be applied here, in benchmarks it was consistently slightly slower than a simple insertion sort,
+ // even for tens of millions of items. this may be because insertion sort is quite fast when the data is already somewhat sorted, like it is here
+ insertionSort(T, items[buffer2.start..buffer2.end], lessThan);
+
+ pull_index = 0;
+ while (pull_index < 2) : (pull_index += 1) {
+ var unique = pull[pull_index].count * 2;
+ if (pull[pull_index].from > pull[pull_index].to) {
+ // the values were pulled out to the left, so redistribute them back to the right
+ var buffer = Range.init(pull[pull_index].range.start, pull[pull_index].range.start + pull[pull_index].count);
+ while (buffer.length() > 0) {
+ index = findFirstForward(T, items, items[buffer.start], Range.init(buffer.end, pull[pull_index].range.end), lessThan, unique);
+ const amount = index - buffer.end;
+ mem.rotate(T, items[buffer.start..index], buffer.length());
+ buffer.start += (amount + 1);
+ buffer.end += amount;
+ unique -= 2;
+ }
+ } else if (pull[pull_index].from < pull[pull_index].to) {
+ // the values were pulled out to the right, so redistribute them back to the left
+ var buffer = Range.init(pull[pull_index].range.end - pull[pull_index].count, pull[pull_index].range.end);
+ while (buffer.length() > 0) {
+ index = findLastBackward(T, items, items[buffer.end - 1], Range.init(pull[pull_index].range.start, buffer.start), lessThan, unique);
+ const amount = buffer.start - index;
+ mem.rotate(T, items[index..buffer.end], amount);
+ buffer.start -= amount;
+ buffer.end -= (amount + 1);
+ unique -= 2;
+ }
+ }
+ }
+ }
+
+ // double the size of each A and B subarray that will be merged in the next level
+ if (!iterator.nextLevel()) break;
}
}
-fn quicksort(comptime T: type, array: []T, left: usize, right: usize, comptime cmp: fn(a: &const T, b: &const T)->Cmp) {
- var i = left;
- var j = right;
- const p = (i + j) / 2;
+// merge operation without a buffer
+fn mergeInPlace(comptime T: type, items: []T, A_arg: &const Range, B_arg: &const Range, lessThan: fn(&const T,&const T)->bool) {
+ if (A_arg.length() == 0 or B_arg.length() == 0) return;
+
+ // this just repeatedly binary searches into B and rotates A into position.
+ // the paper suggests using the 'rotation-based Hwang and Lin algorithm' here,
+ // but I decided to stick with this because it had better situational performance
+ //
+ // (Hwang and Lin is designed for merging subarrays of very different sizes,
+ // but WikiSort almost always uses subarrays that are roughly the same size)
+ //
+ // normally this is incredibly suboptimal, but this function is only called
+ // when none of the A or B blocks in any subarray contained 2√A unique values,
+ // which places a hard limit on the number of times this will ACTUALLY need
+ // to binary search and rotate.
+ //
+ // according to my analysis the worst case is √A rotations performed on √A items
+ // once the constant factors are removed, which ends up being O(n)
+ //
+ // again, this is NOT a general-purpose solution – it only works well in this case!
+ // kind of like how the O(n^2) insertion sort is used in some places
- while (i <= j) {
- while (cmp(array[i], array[p]) == Cmp.Less) {
- i += 1;
- }
- while (cmp(array[j], array[p]) == Cmp.Greater) {
- j -= 1;
- }
- if (i <= j) {
- const tmp = array[i];
- array[i] = array[j];
- array[j] = tmp;
- i += 1;
- if (j > 0) j -= 1;
+ var A = *A_arg;
+ var B = *B_arg;
+
+ while (true) {
+ // find the first place in B where the first item in A needs to be inserted
+ const mid = binaryFirst(T, items, items[A.start], B, lessThan);
+
+ // rotate A into place
+ const amount = mid - A.end;
+ mem.rotate(T, items[A.start..mid], A.length());
+ if (B.end == mid) break;
+
+ // calculate the new A and B ranges
+ B.start = mid;
+ A = Range.init(A.start + amount, B.start);
+ A.start = binaryLast(T, items, items[A.start], A, lessThan);
+ if (A.length() == 0) break;
+ }
+}
+
+// merge operation using an internal buffer
+fn mergeInternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T,&const T)->bool, buffer: &const Range) {
+ // whenever we find a value to add to the final array, swap it with the value that's already in that spot
+ // when this algorithm is finished, 'buffer' will contain its original contents, but in a different order
+ var A_count: usize = 0;
+ var B_count: usize = 0;
+ var insert: usize = 0;
+
+ if (B.length() > 0 and A.length() > 0) {
+ while (true) {
+ if (!lessThan(items[B.start + B_count], items[buffer.start + A_count])) {
+ mem.swap(T, &items[A.start + insert], &items[buffer.start + A_count]);
+ A_count += 1;
+ insert += 1;
+ if (A_count >= A.length()) break;
+ } else {
+ mem.swap(T, &items[A.start + insert], &items[B.start + B_count]);
+ B_count += 1;
+ insert += 1;
+ if (B_count >= B.length()) break;
+ }
}
}
-
- if (left < j) quicksort(T, array, left, j, cmp);
- if (i < right) quicksort(T, array, i, right, cmp);
+
+ // swap the remainder of A into the final array
+ blockSwap(T, items, buffer.start + A_count, A.start + insert, A.length() - A_count);
}
-pub fn i32asc(a: &const i32, b: &const i32) -> Cmp {
- return if (*a > *b) Cmp.Greater else if (*a < *b) Cmp.Less else Cmp.Equal
+fn blockSwap(comptime T: type, items: []T, start1: usize, start2: usize, block_size: usize) {
+ var index: usize = 0;
+ while (index < block_size) : (index += 1) {
+ mem.swap(T, &items[start1 + index], &items[start2 + index]);
+ }
}
-pub fn i32desc(a: &const i32, b: &const i32) -> Cmp {
- reverse(i32asc(a, b))
+// combine a linear search with a binary search to reduce the number of comparisons in situations
+// where have some idea as to how many unique values there are and where the next value might be
+fn findFirstForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)->bool, unique: usize) -> usize {
+ if (range.length() == 0) return range.start;
+ const skip = math.max(range.length()/unique, usize(1));
+
+ var index = range.start + skip;
+ while (lessThan(items[index - 1], value)) : (index += skip) {
+ if (index >= range.end - skip) {
+ return binaryFirst(T, items, value, Range.init(index, range.end), lessThan);
+ }
+ }
+
+ return binaryFirst(T, items, value, Range.init(index - skip, index), lessThan);
}
-pub fn u8asc(a: &const u8, b: &const u8) -> Cmp {
- if (*a > *b) Cmp.Greater else if (*a < *b) Cmp.Less else Cmp.Equal
+fn findFirstBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)->bool, unique: usize) -> usize {
+ if (range.length() == 0) return range.start;
+ const skip = math.max(range.length()/unique, usize(1));
+
+ var index = range.end - skip;
+ while (index > range.start and !lessThan(items[index - 1], value)) : (index -= skip) {
+ if (index < range.start + skip) {
+ return binaryFirst(T, items, value, Range.init(range.start, index), lessThan);
+ }
+ }
+
+ return binaryFirst(T, items, value, Range.init(index, index + skip), lessThan);
}
-pub fn u8desc(a: &const u8, b: &const u8) -> Cmp {
- reverse(u8asc(a, b))
+fn findLastForward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)->bool, unique: usize) -> usize {
+ if (range.length() == 0) return range.start;
+ const skip = math.max(range.length()/unique, usize(1));
+
+ var index = range.start + skip;
+ while (!lessThan(value, items[index - 1])) : (index += skip) {
+ if (index >= range.end - skip) {
+ return binaryLast(T, items, value, Range.init(index, range.end), lessThan);
+ }
+ }
+
+ return binaryLast(T, items, value, Range.init(index - skip, index), lessThan);
}
-fn reverse(was: Cmp) -> Cmp {
- if (was == Cmp.Greater) Cmp.Less else if (was == Cmp.Less) Cmp.Greater else Cmp.Equal
+fn findLastBackward(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)->bool, unique: usize) -> usize {
+ if (range.length() == 0) return range.start;
+ const skip = math.max(range.length()/unique, usize(1));
+
+ var index = range.end - skip;
+ while (index > range.start and lessThan(value, items[index - 1])) : (index -= skip) {
+ if (index < range.start + skip) {
+ return binaryLast(T, items, value, Range.init(range.start, index), lessThan);
+ }
+ }
+
+ return binaryLast(T, items, value, Range.init(index, index + skip), lessThan);
}
-// ---------------------------------------
-// tests
+fn binaryFirst(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)->bool) -> usize {
+ var start = range.start;
+ var end = range.end - 1;
+ if (range.start >= range.end) return range.end;
+ while (start < end) {
+ const mid = start + (end - start)/2;
+ if (lessThan(items[mid], value)) {
+ start = mid + 1;
+ } else {
+ end = mid;
+ }
+ }
+ if (start == range.end - 1 and lessThan(items[start], value)) {
+ start += 1;
+ }
+ return start;
+}
+
+fn binaryLast(comptime T: type, items: []T, value: &const T, range: &const Range, lessThan: fn(&const T,&const T)->bool) -> usize {
+ var start = range.start;
+ var end = range.end - 1;
+ if (range.start >= range.end) return range.end;
+ while (start < end) {
+ const mid = start + (end - start)/2;
+ if (!lessThan(value, items[mid])) {
+ start = mid + 1;
+ } else {
+ end = mid;
+ }
+ }
+ if (start == range.end - 1 and !lessThan(value, items[start])) {
+ start += 1;
+ }
+ return start;
+}
+
+fn mergeInto(comptime T: type, from: []T, A: &const Range, B: &const Range, lessThan: fn(&const T,&const T)->bool, into: []T) {
+ var A_index: usize = A.start;
+ var B_index: usize = B.start;
+ const A_last = A.end;
+ const B_last = B.end;
+ var insert_index: usize = 0;
+
+ while (true) {
+ if (!lessThan(from[B_index], from[A_index])) {
+ into[insert_index] = from[A_index];
+ A_index += 1;
+ insert_index += 1;
+ if (A_index == A_last) {
+ // copy the remainder of B into the final array
+ mem.copy(T, into[insert_index..], from[B_index..B_last]);
+ break;
+ }
+ } else {
+ into[insert_index] = from[B_index];
+ B_index += 1;
+ insert_index += 1;
+ if (B_index == B_last) {
+ // copy the remainder of A into the final array
+ mem.copy(T, into[insert_index..], from[A_index..A_last]);
+ break;
+ }
+ }
+ }
+}
+
+fn mergeExternal(comptime T: type, items: []T, A: &const Range, B: &const Range, lessThan: fn(&const T,&const T)->bool, cache: []T) {
+ // A fits into the cache, so use that instead of the internal buffer
+ var A_index: usize = 0;
+ var B_index: usize = B.start;
+ var insert_index: usize = A.start;
+ const A_last = A.length();
+ const B_last = B.end;
+
+ if (B.length() > 0 and A.length() > 0) {
+ while (true) {
+ if (!lessThan(items[B_index], cache[A_index])) {
+ items[insert_index] = cache[A_index];
+ A_index += 1;
+ insert_index += 1;
+ if (A_index == A_last) break;
+ } else {
+ items[insert_index] = items[B_index];
+ B_index += 1;
+ insert_index += 1;
+ if (B_index == B_last) break;
+ }
+ }
+ }
+
+ // copy the remainder of A into the final array
+ mem.copy(T, items[insert_index..], cache[A_index..A_last]);
+}
+
+fn swap(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)->bool, order: &[8]u8, x: usize, y: usize) {
+ if (lessThan(items[y], items[x]) or
+ ((*order)[x] > (*order)[y] and !lessThan(items[x], items[y])))
+ {
+ mem.swap(T, &items[x], &items[y]);
+ mem.swap(u8, &(*order)[x], &(*order)[y]);
+ }
+}
+
+fn i32asc(lhs: &const i32, rhs: &const i32) -> bool {
+ return *lhs < *rhs;
+}
+
+fn i32desc(lhs: &const i32, rhs: &const i32) -> bool {
+ return *rhs < *lhs;
+}
+
+fn u8asc(lhs: &const u8, rhs: &const u8) -> bool {
+ return *lhs < *rhs;
+}
+
+fn u8desc(lhs: &const u8, rhs: &const u8) -> bool {
+ return *rhs < *lhs;
+}
test "stable sort" {
testStableSort();
@@ -113,7 +1004,7 @@ fn testStableSort() {
},
};
for (cases) |*case| {
- sort_stable(IdAndValue, (*case)[0..], cmpByValue);
+ insertionSort(IdAndValue, (*case)[0..], cmpByValue);
for (*case) |item, i| {
assert(item.id == expected[i].id);
assert(item.value == expected[i].value);
@@ -121,14 +1012,19 @@ fn testStableSort() {
}
}
const IdAndValue = struct {
- id: i32,
+ id: usize,
value: i32,
};
-fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> Cmp {
+fn cmpByValue(a: &const IdAndValue, b: &const IdAndValue) -> bool {
return i32asc(a.value, b.value);
}
-test "testSort" {
+test "std.sort" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // TODO get this test passing
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
const u8cases = [][]const []const u8 {
[][]const u8{"", ""},
[][]const u8{"a", "a"},
@@ -164,7 +1060,12 @@ test "testSort" {
}
}
-test "testSortDesc" {
+test "std.sort descending" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // TODO get this test passing
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
const rev_cases = [][]const []const i32 {
[][]const i32{[]i32{}, []i32{}},
[][]const i32{[]i32{1}, []i32{1}},
@@ -182,3 +1083,74 @@ test "testSortDesc" {
assert(mem.eql(i32, slice, case[1]));
}
}
+
+test "another sort case" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // TODO get this test passing
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+ var arr = []i32{ 5, 3, 1, 2, 4 };
+ sort(i32, arr[0..], i32asc);
+
+ assert(mem.eql(i32, arr, []i32{ 1, 2, 3, 4, 5 }));
+}
+
+test "sort fuzz testing" {
+ if (builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386) {
+ // TODO get this test passing
+ // https://github.com/zig-lang/zig/issues/537
+ return;
+ }
+ var rng = std.rand.Rand.init(0x12345678);
+ const test_case_count = 10;
+ var i: usize = 0;
+ while (i < test_case_count) : (i += 1) {
+ fuzzTest(&rng);
+ }
+}
+
+var fixed_buffer_mem: [100 * 1024]u8 = undefined;
+
+fn fuzzTest(rng: &std.rand.Rand) {
+ const array_size = rng.range(usize, 0, 1000);
+ var fixed_allocator = mem.FixedBufferAllocator.init(fixed_buffer_mem[0..]);
+ var array = %%fixed_allocator.allocator.alloc(IdAndValue, array_size);
+ // populate with random data
+ for (array) |*item, index| {
+ item.id = index;
+ item.value = rng.range(i32, 0, 100);
+ }
+ sort(IdAndValue, array, cmpByValue);
+
+ var index: usize = 1;
+ while (index < array.len) : (index += 1) {
+ if (array[index].value == array[index - 1].value) {
+ assert(array[index].id > array[index - 1].id);
+ } else {
+ assert(array[index].value > array[index - 1].value);
+ }
+ }
+}
+
+pub fn min(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)->bool) -> T {
+ var i: usize = 0;
+ var smallest = items[0];
+ for (items[1..]) |item| {
+ if (lessThan(item, smallest)) {
+ smallest = item;
+ }
+ }
+ return smallest;
+}
+
+pub fn max(comptime T: type, items: []T, lessThan: fn(lhs: &const T, rhs: &const T)->bool) -> T {
+ var i: usize = 0;
+ var biggest = items[0];
+ for (items[1..]) |item| {
+ if (lessThan(biggest, item)) {
+ biggest = item;
+ }
+ }
+ return biggest;
+}
diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig
index 924e537ddd..177e245400 100644
--- a/std/special/bootstrap.zig
+++ b/std/special/bootstrap.zig
@@ -5,20 +5,20 @@ const root = @import("@root");
const std = @import("std");
const builtin = @import("builtin");
-const is_windows = builtin.os == builtin.Os.windows;
-const want_main_symbol = builtin.link_libc;
-const want_start_symbol = !want_main_symbol and !is_windows;
-const want_WinMainCRTStartup = is_windows and !builtin.link_libc;
-
var argc_ptr: &usize = undefined;
-
-export nakedcc fn _start() -> noreturn {
- if (!want_start_symbol) {
- @setGlobalLinkage(_start, builtin.GlobalLinkage.Internal);
- unreachable;
+comptime {
+ const strong_linkage = builtin.GlobalLinkage.Strong;
+ if (builtin.link_libc) {
+ @export("main", main, strong_linkage);
+ } else if (builtin.os == builtin.Os.windows) {
+ @export("WinMainCRTStartup", WinMainCRTStartup, strong_linkage);
+ } else {
+ @export("_start", _start, strong_linkage);
}
+}
+nakedcc fn _start() -> noreturn {
switch (builtin.arch) {
builtin.Arch.x86_64 => {
argc_ptr = asm("lea (%%rsp), %[argc]": [argc] "=r" (-> &usize));
@@ -28,17 +28,14 @@ export nakedcc fn _start() -> noreturn {
},
else => @compileError("unsupported arch"),
}
- posixCallMainAndExit()
+ // If LLVM inlines stack variables into _start, they will overwrite
+ // the command line argument data.
+ @noInlineCall(posixCallMainAndExit);
}
-export fn WinMainCRTStartup() -> noreturn {
- if (!want_WinMainCRTStartup) {
- @setGlobalLinkage(WinMainCRTStartup, builtin.GlobalLinkage.Internal);
- unreachable;
- }
+extern fn WinMainCRTStartup() -> noreturn {
@setAlignStack(16);
- std.debug.user_main_fn = root.main;
root.main() %% std.os.windows.ExitProcess(1);
std.os.windows.ExitProcess(0);
}
@@ -58,17 +55,10 @@ fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
while (envp[env_count] != null) : (env_count += 1) {}
std.os.posix_environ_raw = @ptrCast(&&u8, envp)[0..env_count];
- std.debug.user_main_fn = root.main;
-
return root.main();
}
-export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
- if (!want_main_symbol) {
- @setGlobalLinkage(main, builtin.GlobalLinkage.Internal);
- unreachable;
- }
-
+extern fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
callMain(usize(c_argc), c_argv, c_envp) %% return 1;
return 0;
}
diff --git a/std/special/bootstrap_lib.zig b/std/special/bootstrap_lib.zig
index 7412d64fa6..3c7789f30a 100644
--- a/std/special/bootstrap_lib.zig
+++ b/std/special/bootstrap_lib.zig
@@ -2,7 +2,11 @@
const std = @import("std");
-export stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,
+comptime {
+ @export("_DllMainCRTStartup", _DllMainCRTStartup);
+}
+
+stdcallcc fn _DllMainCRTStartup(hinstDLL: std.os.windows.HINSTANCE, fdwReason: std.os.windows.DWORD,
lpReserved: std.os.windows.LPVOID) -> std.os.windows.BOOL
{
return std.os.windows.TRUE;
diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig
index 430eeb395e..e54d85e6ef 100644
--- a/std/special/build_runner.zig
+++ b/std/special/build_runner.zig
@@ -45,21 +45,17 @@ pub fn main() -> %void {
var stderr_file = io.getStdErr();
var stderr_file_stream: io.FileOutStream = undefined;
- var stderr_stream: %&io.OutStream = if (stderr_file) |*f| {
+ var stderr_stream: %&io.OutStream = if (stderr_file) |*f| x: {
stderr_file_stream = io.FileOutStream.init(f);
- &stderr_file_stream.stream
- } else |err| {
- err
- };
+ break :x &stderr_file_stream.stream;
+ } else |err| err;
var stdout_file = io.getStdOut();
var stdout_file_stream: io.FileOutStream = undefined;
- var stdout_stream: %&io.OutStream = if (stdout_file) |*f| {
+ var stdout_stream: %&io.OutStream = if (stdout_file) |*f| x: {
stdout_file_stream = io.FileOutStream.init(f);
- &stdout_file_stream.stream
- } else |err| {
- err
- };
+ break :x &stdout_file_stream.stream;
+ } else |err| err;
while (arg_it.next(allocator)) |err_or_arg| {
const arg = %return unwrapArg(err_or_arg);
diff --git a/std/special/builtin.zig b/std/special/builtin.zig
index 51e6646574..e6c09863ca 100644
--- a/std/special/builtin.zig
+++ b/std/special/builtin.zig
@@ -35,25 +35,26 @@ export fn memcpy(noalias dest: ?&u8, noalias src: ?&const u8, n: usize) {
(??dest)[index] = (??src)[index];
}
-export fn __stack_chk_fail() -> noreturn {
- if (builtin.mode == builtin.Mode.ReleaseFast or builtin.os == builtin.Os.windows) {
- @setGlobalLinkage(__stack_chk_fail, builtin.GlobalLinkage.Internal);
- unreachable;
+comptime {
+ if (builtin.mode != builtin.Mode.ReleaseFast and builtin.os != builtin.Os.windows) {
+ @export("__stack_chk_fail", __stack_chk_fail, builtin.GlobalLinkage.Strong);
}
+}
+extern fn __stack_chk_fail() -> noreturn {
@panic("stack smashing detected");
}
const math = @import("../math/index.zig");
-export fn fmodf(x: f32, y: f32) -> f32 { generic_fmod(f32, x, y) }
-export fn fmod(x: f64, y: f64) -> f64 { generic_fmod(f64, x, y) }
+export fn fmodf(x: f32, y: f32) -> f32 { return generic_fmod(f32, x, y); }
+export fn fmod(x: f64, y: f64) -> f64 { return generic_fmod(f64, x, y); }
// TODO add intrinsics for these (and probably the double version too)
// and have the math stuff use the intrinsic. same as @mod and @rem
-export fn floorf(x: f32) -> f32 { math.floor(x) }
-export fn ceilf(x: f32) -> f32 { math.ceil(x) }
-export fn floor(x: f64) -> f64 { math.floor(x) }
-export fn ceil(x: f64) -> f64 { math.ceil(x) }
+export fn floorf(x: f32) -> f32 { return math.floor(x); }
+export fn ceilf(x: f32) -> f32 { return math.ceil(x); }
+export fn floor(x: f64) -> f64 { return math.floor(x); }
+export fn ceil(x: f64) -> f64 { return math.ceil(x); }
fn generic_fmod(comptime T: type, x: T, y: T) -> T {
@setDebugSafety(this, false);
@@ -83,7 +84,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
// normalize x and y
if (ex == 0) {
i = ux << exp_bits;
- while (i >> bits_minus_1 == 0) : ({ex -= 1; i <<= 1}) {}
+ while (i >> bits_minus_1 == 0) : (b: {ex -= 1; break :b i <<= 1;}) {}
ux <<= log2uint(@bitCast(u32, -ex + 1));
} else {
ux &= @maxValue(uint) >> exp_bits;
@@ -91,7 +92,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
}
if (ey == 0) {
i = uy << exp_bits;
- while (i >> bits_minus_1 == 0) : ({ey -= 1; i <<= 1}) {}
+ while (i >> bits_minus_1 == 0) : (b: {ey -= 1; break :b i <<= 1;}) {}
uy <<= log2uint(@bitCast(u32, -ey + 1));
} else {
uy &= @maxValue(uint) >> exp_bits;
@@ -114,7 +115,7 @@ fn generic_fmod(comptime T: type, x: T, y: T) -> T {
return 0 * x;
ux = i;
}
- while (ux >> digits == 0) : ({ux <<= 1; ex -= 1}) {}
+ while (ux >> digits == 0) : (b: {ux <<= 1; break :b ex -= 1;}) {}
// scale result up
if (ex > 0) {
diff --git a/std/special/compiler_rt/aulldiv.zig b/std/special/compiler_rt/aulldiv.zig
index 511aa91f80..9d4faf95b9 100644
--- a/std/special/compiler_rt/aulldiv.zig
+++ b/std/special/compiler_rt/aulldiv.zig
@@ -1,66 +1,55 @@
-const builtin = @import("builtin");
-const linkage = if (builtin.is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
-const is_win32 = builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386;
-
-export nakedcc fn _aulldiv() {
- if (is_win32) {
- @setDebugSafety(this, false);
- @setGlobalLinkage(_aulldiv, linkage);
- asm volatile (
- \\.intel_syntax noprefix
- \\
- \\ push ebx
- \\ push esi
- \\ mov eax,dword ptr [esp+18h]
- \\ or eax,eax
- \\ jne L1
- \\ mov ecx,dword ptr [esp+14h]
- \\ mov eax,dword ptr [esp+10h]
- \\ xor edx,edx
- \\ div ecx
- \\ mov ebx,eax
- \\ mov eax,dword ptr [esp+0Ch]
- \\ div ecx
- \\ mov edx,ebx
- \\ jmp L2
- \\ L1:
- \\ mov ecx,eax
- \\ mov ebx,dword ptr [esp+14h]
- \\ mov edx,dword ptr [esp+10h]
- \\ mov eax,dword ptr [esp+0Ch]
- \\ L3:
- \\ shr ecx,1
- \\ rcr ebx,1
- \\ shr edx,1
- \\ rcr eax,1
- \\ or ecx,ecx
- \\ jne L3
- \\ div ebx
- \\ mov esi,eax
- \\ mul dword ptr [esp+18h]
- \\ mov ecx,eax
- \\ mov eax,dword ptr [esp+14h]
- \\ mul esi
- \\ add edx,ecx
- \\ jb L4
- \\ cmp edx,dword ptr [esp+10h]
- \\ ja L4
- \\ jb L5
- \\ cmp eax,dword ptr [esp+0Ch]
- \\ jbe L5
- \\ L4:
- \\ dec esi
- \\ L5:
- \\ xor edx,edx
- \\ mov eax,esi
- \\ L2:
- \\ pop esi
- \\ pop ebx
- \\ ret 10h
- );
- unreachable;
- }
-
- @setGlobalLinkage(_aulldiv, builtin.GlobalLinkage.Internal);
- unreachable;
+pub nakedcc fn _aulldiv() {
+ @setDebugSafety(this, false);
+ asm volatile (
+ \\.intel_syntax noprefix
+ \\
+ \\ push ebx
+ \\ push esi
+ \\ mov eax,dword ptr [esp+18h]
+ \\ or eax,eax
+ \\ jne L1
+ \\ mov ecx,dword ptr [esp+14h]
+ \\ mov eax,dword ptr [esp+10h]
+ \\ xor edx,edx
+ \\ div ecx
+ \\ mov ebx,eax
+ \\ mov eax,dword ptr [esp+0Ch]
+ \\ div ecx
+ \\ mov edx,ebx
+ \\ jmp L2
+ \\ L1:
+ \\ mov ecx,eax
+ \\ mov ebx,dword ptr [esp+14h]
+ \\ mov edx,dword ptr [esp+10h]
+ \\ mov eax,dword ptr [esp+0Ch]
+ \\ L3:
+ \\ shr ecx,1
+ \\ rcr ebx,1
+ \\ shr edx,1
+ \\ rcr eax,1
+ \\ or ecx,ecx
+ \\ jne L3
+ \\ div ebx
+ \\ mov esi,eax
+ \\ mul dword ptr [esp+18h]
+ \\ mov ecx,eax
+ \\ mov eax,dword ptr [esp+14h]
+ \\ mul esi
+ \\ add edx,ecx
+ \\ jb L4
+ \\ cmp edx,dword ptr [esp+10h]
+ \\ ja L4
+ \\ jb L5
+ \\ cmp eax,dword ptr [esp+0Ch]
+ \\ jbe L5
+ \\ L4:
+ \\ dec esi
+ \\ L5:
+ \\ xor edx,edx
+ \\ mov eax,esi
+ \\ L2:
+ \\ pop esi
+ \\ pop ebx
+ \\ ret 10h
+ );
}
diff --git a/std/special/compiler_rt/aullrem.zig b/std/special/compiler_rt/aullrem.zig
index e218890959..b6c54d33ae 100644
--- a/std/special/compiler_rt/aullrem.zig
+++ b/std/special/compiler_rt/aullrem.zig
@@ -1,67 +1,56 @@
-const builtin = @import("builtin");
-const linkage = if (builtin.is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
-const is_win32 = builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386;
-
-export nakedcc fn _aullrem() {
- if (is_win32) {
- @setDebugSafety(this, false);
- @setGlobalLinkage(_aullrem, linkage);
- asm volatile (
- \\.intel_syntax noprefix
- \\
- \\ push ebx
- \\ mov eax,dword ptr [esp+14h]
- \\ or eax,eax
- \\ jne L1a
- \\ mov ecx,dword ptr [esp+10h]
- \\ mov eax,dword ptr [esp+0Ch]
- \\ xor edx,edx
- \\ div ecx
- \\ mov eax,dword ptr [esp+8]
- \\ div ecx
- \\ mov eax,edx
- \\ xor edx,edx
- \\ jmp L2a
- \\ L1a:
- \\ mov ecx,eax
- \\ mov ebx,dword ptr [esp+10h]
- \\ mov edx,dword ptr [esp+0Ch]
- \\ mov eax,dword ptr [esp+8]
- \\ L3a:
- \\ shr ecx,1
- \\ rcr ebx,1
- \\ shr edx,1
- \\ rcr eax,1
- \\ or ecx,ecx
- \\ jne L3a
- \\ div ebx
- \\ mov ecx,eax
- \\ mul dword ptr [esp+14h]
- \\ xchg eax,ecx
- \\ mul dword ptr [esp+10h]
- \\ add edx,ecx
- \\ jb L4a
- \\ cmp edx,dword ptr [esp+0Ch]
- \\ ja L4a
- \\ jb L5a
- \\ cmp eax,dword ptr [esp+8]
- \\ jbe L5a
- \\ L4a:
- \\ sub eax,dword ptr [esp+10h]
- \\ sbb edx,dword ptr [esp+14h]
- \\ L5a:
- \\ sub eax,dword ptr [esp+8]
- \\ sbb edx,dword ptr [esp+0Ch]
- \\ neg edx
- \\ neg eax
- \\ sbb edx,0
- \\ L2a:
- \\ pop ebx
- \\ ret 10h
- );
- unreachable;
- }
-
- @setGlobalLinkage(_aullrem, builtin.GlobalLinkage.Internal);
- unreachable;
+pub nakedcc fn _aullrem() {
+ @setDebugSafety(this, false);
+ asm volatile (
+ \\.intel_syntax noprefix
+ \\
+ \\ push ebx
+ \\ mov eax,dword ptr [esp+14h]
+ \\ or eax,eax
+ \\ jne L1a
+ \\ mov ecx,dword ptr [esp+10h]
+ \\ mov eax,dword ptr [esp+0Ch]
+ \\ xor edx,edx
+ \\ div ecx
+ \\ mov eax,dword ptr [esp+8]
+ \\ div ecx
+ \\ mov eax,edx
+ \\ xor edx,edx
+ \\ jmp L2a
+ \\ L1a:
+ \\ mov ecx,eax
+ \\ mov ebx,dword ptr [esp+10h]
+ \\ mov edx,dword ptr [esp+0Ch]
+ \\ mov eax,dword ptr [esp+8]
+ \\ L3a:
+ \\ shr ecx,1
+ \\ rcr ebx,1
+ \\ shr edx,1
+ \\ rcr eax,1
+ \\ or ecx,ecx
+ \\ jne L3a
+ \\ div ebx
+ \\ mov ecx,eax
+ \\ mul dword ptr [esp+14h]
+ \\ xchg eax,ecx
+ \\ mul dword ptr [esp+10h]
+ \\ add edx,ecx
+ \\ jb L4a
+ \\ cmp edx,dword ptr [esp+0Ch]
+ \\ ja L4a
+ \\ jb L5a
+ \\ cmp eax,dword ptr [esp+8]
+ \\ jbe L5a
+ \\ L4a:
+ \\ sub eax,dword ptr [esp+10h]
+ \\ sbb edx,dword ptr [esp+14h]
+ \\ L5a:
+ \\ sub eax,dword ptr [esp+8]
+ \\ sbb edx,dword ptr [esp+0Ch]
+ \\ neg edx
+ \\ neg eax
+ \\ sbb edx,0
+ \\ L2a:
+ \\ pop ebx
+ \\ ret 10h
+ );
}
diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig
index f1552ca61f..b88c35019b 100644
--- a/std/special/compiler_rt/comparetf2.zig
+++ b/std/special/compiler_rt/comparetf2.zig
@@ -20,11 +20,9 @@ const infRep = exponentMask;
const builtin = @import("builtin");
const is_test = builtin.is_test;
-const linkage = @import("index.zig").linkage;
-export fn __letf2(a: f128, b: f128) -> c_int {
+pub extern fn __letf2(a: f128, b: f128) -> c_int {
@setDebugSafety(this, is_test);
- @setGlobalLinkage(__letf2, linkage);
const aInt = @bitCast(rep_t, a);
const bInt = @bitCast(rep_t, b);
@@ -40,35 +38,25 @@ export fn __letf2(a: f128, b: f128) -> c_int {
// If at least one of a and b is positive, we get the same result comparing
// a and b as signed integers as we would with a floating-point compare.
- return if ((aInt & bInt) >= 0) {
- if (aInt < bInt) {
+ return if ((aInt & bInt) >= 0)
+ if (aInt < bInt)
LE_LESS
- } else if (aInt == bInt) {
+ else if (aInt == bInt)
LE_EQUAL
- } else {
+ else
LE_GREATER
- }
- } else {
+ else
// Otherwise, both are negative, so we need to flip the sense of the
// comparison to get the correct result. (This assumes a twos- or ones-
// complement integer representation; if integers are represented in a
// sign-magnitude representation, then this flip is incorrect).
- if (aInt > bInt) {
+ if (aInt > bInt)
LE_LESS
- } else if (aInt == bInt) {
+ else if (aInt == bInt)
LE_EQUAL
- } else {
+ else
LE_GREATER
- }
- };
-}
-
-// Alias for libgcc compatibility
-// TODO https://github.com/zig-lang/zig/issues/420
-export fn __cmptf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__cmptf2, linkage);
- @setDebugSafety(this, is_test);
- return __letf2(a, b);
+ ;
}
// TODO https://github.com/zig-lang/zig/issues/305
@@ -78,8 +66,7 @@ const GE_EQUAL = c_int(0);
const GE_GREATER = c_int(1);
const GE_UNORDERED = c_int(-1); // Note: different from LE_UNORDERED
-export fn __getf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__getf2, linkage);
+pub extern fn __getf2(a: f128, b: f128) -> c_int {
@setDebugSafety(this, is_test);
const aInt = @bitCast(srep_t, a);
@@ -89,57 +76,27 @@ export fn __getf2(a: f128, b: f128) -> c_int {
if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED;
if ((aAbs | bAbs) == 0) return GE_EQUAL;
- return if ((aInt & bInt) >= 0) {
- if (aInt < bInt) {
+ return if ((aInt & bInt) >= 0)
+ if (aInt < bInt)
GE_LESS
- } else if (aInt == bInt) {
+ else if (aInt == bInt)
GE_EQUAL
- } else {
+ else
GE_GREATER
- }
- } else {
- if (aInt > bInt) {
+ else
+ if (aInt > bInt)
GE_LESS
- } else if (aInt == bInt) {
+ else if (aInt == bInt)
GE_EQUAL
- } else {
+ else
GE_GREATER
- }
- };
+ ;
}
-export fn __unordtf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__unordtf2, linkage);
+pub extern fn __unordtf2(a: f128, b: f128) -> c_int {
@setDebugSafety(this, is_test);
const aAbs = @bitCast(rep_t, a) & absMask;
const bAbs = @bitCast(rep_t, b) & absMask;
return c_int(aAbs > infRep or bAbs > infRep);
}
-
-// The following are alternative names for the preceding routines.
-// TODO use aliases https://github.com/zig-lang/zig/issues/462
-
-export fn __eqtf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__eqtf2, linkage);
- @setDebugSafety(this, is_test);
- return __letf2(a, b);
-}
-
-export fn __lttf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__lttf2, linkage);
- @setDebugSafety(this, is_test);
- return __letf2(a, b);
-}
-
-export fn __netf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__netf2, linkage);
- @setDebugSafety(this, is_test);
- return __letf2(a, b);
-}
-
-export fn __gttf2(a: f128, b: f128) -> c_int {
- @setGlobalLinkage(__gttf2, linkage);
- @setDebugSafety(this, is_test);
- return __getf2(a, b);
-}
diff --git a/std/special/compiler_rt/fixunsdfdi.zig b/std/special/compiler_rt/fixunsdfdi.zig
index 5f730bbc85..7e33987997 100644
--- a/std/special/compiler_rt/fixunsdfdi.zig
+++ b/std/special/compiler_rt/fixunsdfdi.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunsdfdi(a: f64) -> u64 {
+pub extern fn __fixunsdfdi(a: f64) -> u64 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunsdfdi, linkage);
return fixuint(f64, u64, a);
}
diff --git a/std/special/compiler_rt/fixunsdfsi.zig b/std/special/compiler_rt/fixunsdfsi.zig
index 784d5fde4f..e710e1852b 100644
--- a/std/special/compiler_rt/fixunsdfsi.zig
+++ b/std/special/compiler_rt/fixunsdfsi.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunsdfsi(a: f64) -> u32 {
+pub extern fn __fixunsdfsi(a: f64) -> u32 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunsdfsi, linkage);
return fixuint(f64, u32, a);
}
diff --git a/std/special/compiler_rt/fixunsdfti.zig b/std/special/compiler_rt/fixunsdfti.zig
index 579455c2f9..79d924f0a8 100644
--- a/std/special/compiler_rt/fixunsdfti.zig
+++ b/std/special/compiler_rt/fixunsdfti.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunsdfti(a: f64) -> u128 {
+pub extern fn __fixunsdfti(a: f64) -> u128 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunsdfti, linkage);
return fixuint(f64, u128, a);
}
diff --git a/std/special/compiler_rt/fixunssfdi.zig b/std/special/compiler_rt/fixunssfdi.zig
index eab553d8c9..f72f62d68c 100644
--- a/std/special/compiler_rt/fixunssfdi.zig
+++ b/std/special/compiler_rt/fixunssfdi.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunssfdi(a: f32) -> u64 {
+pub extern fn __fixunssfdi(a: f32) -> u64 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunssfdi, linkage);
return fixuint(f32, u64, a);
}
diff --git a/std/special/compiler_rt/fixunssfsi.zig b/std/special/compiler_rt/fixunssfsi.zig
index 18c0e66677..4c9a5001ab 100644
--- a/std/special/compiler_rt/fixunssfsi.zig
+++ b/std/special/compiler_rt/fixunssfsi.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunssfsi(a: f32) -> u32 {
+pub extern fn __fixunssfsi(a: f32) -> u32 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunssfsi, linkage);
return fixuint(f32, u32, a);
}
diff --git a/std/special/compiler_rt/fixunssfti.zig b/std/special/compiler_rt/fixunssfti.zig
index f513604247..59b94cfc51 100644
--- a/std/special/compiler_rt/fixunssfti.zig
+++ b/std/special/compiler_rt/fixunssfti.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunssfti(a: f32) -> u128 {
+pub extern fn __fixunssfti(a: f32) -> u128 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunssfti, linkage);
return fixuint(f32, u128, a);
}
diff --git a/std/special/compiler_rt/fixunstfdi.zig b/std/special/compiler_rt/fixunstfdi.zig
index 85212e2176..06b117a414 100644
--- a/std/special/compiler_rt/fixunstfdi.zig
+++ b/std/special/compiler_rt/fixunstfdi.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunstfdi(a: f128) -> u64 {
+pub extern fn __fixunstfdi(a: f128) -> u64 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunstfdi, linkage);
return fixuint(f128, u64, a);
}
diff --git a/std/special/compiler_rt/fixunstfsi.zig b/std/special/compiler_rt/fixunstfsi.zig
index 33c85c9224..8a5efe711d 100644
--- a/std/special/compiler_rt/fixunstfsi.zig
+++ b/std/special/compiler_rt/fixunstfsi.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunstfsi(a: f128) -> u32 {
+pub extern fn __fixunstfsi(a: f128) -> u32 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunstfsi, linkage);
return fixuint(f128, u32, a);
}
diff --git a/std/special/compiler_rt/fixunstfti.zig b/std/special/compiler_rt/fixunstfti.zig
index 1bf7fbab4b..d8b654d3a3 100644
--- a/std/special/compiler_rt/fixunstfti.zig
+++ b/std/special/compiler_rt/fixunstfti.zig
@@ -1,10 +1,8 @@
const fixuint = @import("fixuint.zig").fixuint;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __fixunstfti(a: f128) -> u128 {
+pub extern fn __fixunstfti(a: f128) -> u128 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__fixunstfti, linkage);
return fixuint(f128, u128, a);
}
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
index b9f93a601a..5e8c91c8d5 100644
--- a/std/special/compiler_rt/index.zig
+++ b/std/special/compiler_rt/index.zig
@@ -1,34 +1,75 @@
-comptime {
- _ = @import("comparetf2.zig");
- _ = @import("fixunsdfdi.zig");
- _ = @import("fixunsdfsi.zig");
- _ = @import("fixunsdfti.zig");
- _ = @import("fixunssfdi.zig");
- _ = @import("fixunssfsi.zig");
- _ = @import("fixunssfti.zig");
- _ = @import("fixunstfdi.zig");
- _ = @import("fixunstfsi.zig");
- _ = @import("fixunstfti.zig");
- _ = @import("udivmoddi4.zig");
- _ = @import("udivmodti4.zig");
- _ = @import("udivti3.zig");
- _ = @import("umodti3.zig");
- _ = @import("aulldiv.zig");
- _ = @import("aullrem.zig");
-}
-
const builtin = @import("builtin");
const is_test = builtin.is_test;
+
+comptime {
+ const linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
+ const strong_linkage = if (is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
+
+ @export("__letf2", @import("comparetf2.zig").__letf2, linkage);
+ @export("__getf2", @import("comparetf2.zig").__getf2, linkage);
+
+ if (!is_test) {
+ // only create these aliases when not testing
+ @export("__cmptf2", @import("comparetf2.zig").__letf2, linkage);
+ @export("__eqtf2", @import("comparetf2.zig").__letf2, linkage);
+ @export("__lttf2", @import("comparetf2.zig").__letf2, linkage);
+ @export("__netf2", @import("comparetf2.zig").__letf2, linkage);
+ @export("__gttf2", @import("comparetf2.zig").__getf2, linkage);
+ }
+
+ @export("__unordtf2", @import("comparetf2.zig").__unordtf2, linkage);
+
+ @export("__fixunssfsi", @import("fixunssfsi.zig").__fixunssfsi, linkage);
+ @export("__fixunssfdi", @import("fixunssfdi.zig").__fixunssfdi, linkage);
+ @export("__fixunssfti", @import("fixunssfti.zig").__fixunssfti, linkage);
+
+ @export("__fixunsdfsi", @import("fixunsdfsi.zig").__fixunsdfsi, linkage);
+ @export("__fixunsdfdi", @import("fixunsdfdi.zig").__fixunsdfdi, linkage);
+ @export("__fixunsdfti", @import("fixunsdfti.zig").__fixunsdfti, linkage);
+
+ @export("__fixunstfsi", @import("fixunstfsi.zig").__fixunstfsi, linkage);
+ @export("__fixunstfdi", @import("fixunstfdi.zig").__fixunstfdi, linkage);
+ @export("__fixunstfti", @import("fixunstfti.zig").__fixunstfti, linkage);
+
+ @export("__udivmoddi4", @import("udivmoddi4.zig").__udivmoddi4, linkage);
+ @export("__udivmodti4", @import("udivmodti4.zig").__udivmodti4, linkage);
+
+ @export("__udivti3", @import("udivti3.zig").__udivti3, linkage);
+ @export("__umodti3", @import("umodti3.zig").__umodti3, linkage);
+
+ @export("__udivsi3", __udivsi3, linkage);
+ @export("__udivdi3", __udivdi3, linkage);
+ @export("__umoddi3", __umoddi3, linkage);
+ @export("__udivmodsi4", __udivmodsi4, linkage);
+
+ if (isArmArch()) {
+ @export("__aeabi_uldivmod", __aeabi_uldivmod, linkage);
+ @export("__aeabi_uidivmod", __aeabi_uidivmod, linkage);
+ @export("__aeabi_uidiv", __udivsi3, linkage);
+ }
+ if (builtin.os == builtin.Os.windows) {
+ switch (builtin.arch) {
+ builtin.Arch.i386 => {
+ if (!builtin.link_libc) {
+ @export("_chkstk", _chkstk, strong_linkage);
+ @export("__chkstk_ms", __chkstk_ms, linkage);
+ }
+ @export("_aulldiv", @import("aulldiv.zig")._aulldiv, strong_linkage);
+ @export("_aullrem", @import("aullrem.zig")._aullrem, strong_linkage);
+ },
+ builtin.Arch.x86_64 => {
+ if (!builtin.link_libc) {
+ @export("__chkstk", __chkstk, strong_linkage);
+ @export("___chkstk_ms", ___chkstk_ms, linkage);
+ }
+ },
+ else => {},
+ }
+ }
+}
+
const assert = @import("../../debug.zig").assert;
-
-const win32 = builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.i386;
-const win64 = builtin.os == builtin.Os.windows and builtin.arch == builtin.Arch.x86_64;
-const win32_nocrt = win32 and !builtin.link_libc;
-const win64_nocrt = win64 and !builtin.link_libc;
-pub const linkage = if (builtin.is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Weak;
-const strong_linkage = if (builtin.is_test) builtin.GlobalLinkage.Internal else builtin.GlobalLinkage.Strong;
-
const __udivmoddi4 = @import("udivmoddi4.zig").__udivmoddi4;
// Avoid dragging in the debug safety mechanisms into this .o file,
@@ -41,15 +82,13 @@ pub coldcc fn panic(msg: []const u8) -> noreturn {
}
}
-export fn __udivdi3(a: u64, b: u64) -> u64 {
+extern fn __udivdi3(a: u64, b: u64) -> u64 {
@setDebugSafety(this, is_test);
- @setGlobalLinkage(__udivdi3, linkage);
return __udivmoddi4(a, b, null);
}
-export fn __umoddi3(a: u64, b: u64) -> u64 {
+extern fn __umoddi3(a: u64, b: u64) -> u64 {
@setDebugSafety(this, is_test);
- @setGlobalLinkage(__umoddi3, linkage);
var r: u64 = undefined;
_ = __udivmoddi4(a, b, &r);
@@ -60,17 +99,11 @@ const AeabiUlDivModResult = extern struct {
quot: u64,
rem: u64,
};
-export fn __aeabi_uldivmod(numerator: u64, denominator: u64) -> AeabiUlDivModResult {
+extern fn __aeabi_uldivmod(numerator: u64, denominator: u64) -> AeabiUlDivModResult {
@setDebugSafety(this, is_test);
- if (comptime isArmArch()) {
- @setGlobalLinkage(__aeabi_uldivmod, linkage);
- var result: AeabiUlDivModResult = undefined;
- result.quot = __udivmoddi4(numerator, denominator, &result.rem);
- return result;
- }
-
- @setGlobalLinkage(__aeabi_uldivmod, builtin.GlobalLinkage.Internal);
- unreachable;
+ var result: AeabiUlDivModResult = undefined;
+ result.quot = __udivmoddi4(numerator, denominator, &result.rem);
+ return result;
}
fn isArmArch() -> bool {
@@ -115,156 +148,124 @@ fn isArmArch() -> bool {
};
}
-export nakedcc fn __aeabi_uidivmod() {
+nakedcc fn __aeabi_uidivmod() {
@setDebugSafety(this, false);
-
- if (comptime isArmArch()) {
- @setGlobalLinkage(__aeabi_uidivmod, linkage);
- asm volatile (
- \\ push { lr }
- \\ sub sp, sp, #4
- \\ mov r2, sp
- \\ bl __udivmodsi4
- \\ ldr r1, [sp]
- \\ add sp, sp, #4
- \\ pop { pc }
- ::: "r2", "r1");
- unreachable;
- }
-
- @setGlobalLinkage(__aeabi_uidivmod, builtin.GlobalLinkage.Internal);
+ asm volatile (
+ \\ push { lr }
+ \\ sub sp, sp, #4
+ \\ mov r2, sp
+ \\ bl __udivmodsi4
+ \\ ldr r1, [sp]
+ \\ add sp, sp, #4
+ \\ pop { pc }
+ ::: "r2", "r1");
}
// _chkstk (_alloca) routine - probe stack between %esp and (%esp-%eax) in 4k increments,
// then decrement %esp by %eax. Preserves all registers except %esp and flags.
// This routine is windows specific
// http://msdn.microsoft.com/en-us/library/ms648426.aspx
-export nakedcc fn _chkstk() align(4) {
+nakedcc fn _chkstk() align(4) {
@setDebugSafety(this, false);
- if (win32_nocrt) {
- @setGlobalLinkage(_chkstk, strong_linkage);
- asm volatile (
- \\ push %%ecx
- \\ push %%eax
- \\ cmp $0x1000,%%eax
- \\ lea 12(%%esp),%%ecx
- \\ jb 1f
- \\ 2:
- \\ sub $0x1000,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ sub $0x1000,%%eax
- \\ cmp $0x1000,%%eax
- \\ ja 2b
- \\ 1:
- \\ sub %%eax,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ pop %%eax
- \\ pop %%ecx
- \\ ret
- );
- unreachable;
- }
-
- @setGlobalLinkage(_chkstk, builtin.GlobalLinkage.Internal);
+ asm volatile (
+ \\ push %%ecx
+ \\ push %%eax
+ \\ cmp $0x1000,%%eax
+ \\ lea 12(%%esp),%%ecx
+ \\ jb 1f
+ \\ 2:
+ \\ sub $0x1000,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ sub $0x1000,%%eax
+ \\ cmp $0x1000,%%eax
+ \\ ja 2b
+ \\ 1:
+ \\ sub %%eax,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ pop %%eax
+ \\ pop %%ecx
+ \\ ret
+ );
}
-export nakedcc fn __chkstk() align(4) {
+nakedcc fn __chkstk() align(4) {
@setDebugSafety(this, false);
- if (win64_nocrt) {
- @setGlobalLinkage(__chkstk, strong_linkage);
- asm volatile (
- \\ push %%rcx
- \\ push %%rax
- \\ cmp $0x1000,%%rax
- \\ lea 24(%%rsp),%%rcx
- \\ jb 1f
- \\2:
- \\ sub $0x1000,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ sub $0x1000,%%rax
- \\ cmp $0x1000,%%rax
- \\ ja 2b
- \\1:
- \\ sub %%rax,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ pop %%rax
- \\ pop %%rcx
- \\ ret
- );
- unreachable;
- }
-
- @setGlobalLinkage(__chkstk, builtin.GlobalLinkage.Internal);
+ asm volatile (
+ \\ push %%rcx
+ \\ push %%rax
+ \\ cmp $0x1000,%%rax
+ \\ lea 24(%%rsp),%%rcx
+ \\ jb 1f
+ \\2:
+ \\ sub $0x1000,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ sub $0x1000,%%rax
+ \\ cmp $0x1000,%%rax
+ \\ ja 2b
+ \\1:
+ \\ sub %%rax,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ pop %%rax
+ \\ pop %%rcx
+ \\ ret
+ );
}
// _chkstk routine
// This routine is windows specific
// http://msdn.microsoft.com/en-us/library/ms648426.aspx
-export nakedcc fn __chkstk_ms() align(4) {
+nakedcc fn __chkstk_ms() align(4) {
@setDebugSafety(this, false);
- if (win32_nocrt) {
- @setGlobalLinkage(__chkstk_ms, linkage);
- asm volatile (
- \\ push %%ecx
- \\ push %%eax
- \\ cmp $0x1000,%%eax
- \\ lea 12(%%esp),%%ecx
- \\ jb 1f
- \\ 2:
- \\ sub $0x1000,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ sub $0x1000,%%eax
- \\ cmp $0x1000,%%eax
- \\ ja 2b
- \\ 1:
- \\ sub %%eax,%%ecx
- \\ test %%ecx,(%%ecx)
- \\ pop %%eax
- \\ pop %%ecx
- \\ ret
- );
- unreachable;
- }
-
- @setGlobalLinkage(__chkstk_ms, builtin.GlobalLinkage.Internal);
+ asm volatile (
+ \\ push %%ecx
+ \\ push %%eax
+ \\ cmp $0x1000,%%eax
+ \\ lea 12(%%esp),%%ecx
+ \\ jb 1f
+ \\ 2:
+ \\ sub $0x1000,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ sub $0x1000,%%eax
+ \\ cmp $0x1000,%%eax
+ \\ ja 2b
+ \\ 1:
+ \\ sub %%eax,%%ecx
+ \\ test %%ecx,(%%ecx)
+ \\ pop %%eax
+ \\ pop %%ecx
+ \\ ret
+ );
}
-export nakedcc fn ___chkstk_ms() align(4) {
+nakedcc fn ___chkstk_ms() align(4) {
@setDebugSafety(this, false);
- if (win64_nocrt) {
- @setGlobalLinkage(___chkstk_ms, linkage);
- asm volatile (
- \\ push %%rcx
- \\ push %%rax
- \\ cmp $0x1000,%%rax
- \\ lea 24(%%rsp),%%rcx
- \\ jb 1f
- \\2:
- \\ sub $0x1000,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ sub $0x1000,%%rax
- \\ cmp $0x1000,%%rax
- \\ ja 2b
- \\1:
- \\ sub %%rax,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ pop %%rax
- \\ pop %%rcx
- \\ ret
- );
- unreachable;
- }
-
- @setGlobalLinkage(___chkstk_ms, builtin.GlobalLinkage.Internal);
+ asm volatile (
+ \\ push %%rcx
+ \\ push %%rax
+ \\ cmp $0x1000,%%rax
+ \\ lea 24(%%rsp),%%rcx
+ \\ jb 1f
+ \\2:
+ \\ sub $0x1000,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ sub $0x1000,%%rax
+ \\ cmp $0x1000,%%rax
+ \\ ja 2b
+ \\1:
+ \\ sub %%rax,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ pop %%rax
+ \\ pop %%rcx
+ \\ ret
+ );
}
-export fn __udivmodsi4(a: u32, b: u32, rem: &u32) -> u32 {
+extern fn __udivmodsi4(a: u32, b: u32, rem: &u32) -> u32 {
@setDebugSafety(this, is_test);
- @setGlobalLinkage(__udivmodsi4, linkage);
const d = __udivsi3(a, b);
*rem = u32(i32(a) -% (i32(d) * i32(b)));
@@ -272,19 +273,8 @@ export fn __udivmodsi4(a: u32, b: u32, rem: &u32) -> u32 {
}
-// TODO make this an alias instead of an extra function call
-// https://github.com/andrewrk/zig/issues/256
-
-export fn __aeabi_uidiv(n: u32, d: u32) -> u32 {
+extern fn __udivsi3(n: u32, d: u32) -> u32 {
@setDebugSafety(this, is_test);
- @setGlobalLinkage(__aeabi_uidiv, linkage);
-
- return __udivsi3(n, d);
-}
-
-export fn __udivsi3(n: u32, d: u32) -> u32 {
- @setDebugSafety(this, is_test);
- @setGlobalLinkage(__udivsi3, linkage);
const n_uword_bits: c_uint = u32.bit_count;
// special cases
@@ -480,4 +470,3 @@ fn test_one_udivsi3(a: u32, b: u32, expected_q: u32) {
const q: u32 = __udivsi3(a, b);
assert(q == expected_q);
}
-
diff --git a/std/special/compiler_rt/udivmoddi4.zig b/std/special/compiler_rt/udivmoddi4.zig
index 8005538d9a..4e2117cfa5 100644
--- a/std/special/compiler_rt/udivmoddi4.zig
+++ b/std/special/compiler_rt/udivmoddi4.zig
@@ -1,10 +1,8 @@
const udivmod = @import("udivmod.zig").udivmod;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?&u64) -> u64 {
+pub extern fn __udivmoddi4(a: u64, b: u64, maybe_rem: ?&u64) -> u64 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__udivmoddi4, linkage);
return udivmod(u64, a, b, maybe_rem);
}
diff --git a/std/special/compiler_rt/udivmodti4.zig b/std/special/compiler_rt/udivmodti4.zig
index 2ee2fdb57f..c56a958f27 100644
--- a/std/special/compiler_rt/udivmodti4.zig
+++ b/std/special/compiler_rt/udivmodti4.zig
@@ -1,10 +1,8 @@
const udivmod = @import("udivmod.zig").udivmod;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) -> u128 {
+pub extern fn __udivmodti4(a: u128, b: u128, maybe_rem: ?&u128) -> u128 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__udivmodti4, linkage);
return udivmod(u128, a, b, maybe_rem);
}
diff --git a/std/special/compiler_rt/udivti3.zig b/std/special/compiler_rt/udivti3.zig
index 3764449758..115c748cfb 100644
--- a/std/special/compiler_rt/udivti3.zig
+++ b/std/special/compiler_rt/udivti3.zig
@@ -1,9 +1,7 @@
const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __udivti3(a: u128, b: u128) -> u128 {
+pub extern fn __udivti3(a: u128, b: u128) -> u128 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__udivti3, linkage);
return __udivmodti4(a, b, null);
}
diff --git a/std/special/compiler_rt/umodti3.zig b/std/special/compiler_rt/umodti3.zig
index 0ad9e127b3..9f680369eb 100644
--- a/std/special/compiler_rt/umodti3.zig
+++ b/std/special/compiler_rt/umodti3.zig
@@ -1,10 +1,8 @@
const __udivmodti4 = @import("udivmodti4.zig").__udivmodti4;
const builtin = @import("builtin");
-const linkage = @import("index.zig").linkage;
-export fn __umodti3(a: u128, b: u128) -> u128 {
+pub extern fn __umodti3(a: u128, b: u128) -> u128 {
@setDebugSafety(this, builtin.is_test);
- @setGlobalLinkage(__umodti3, linkage);
var r: u128 = undefined;
_ = __udivmodti4(a, b, &r);
return r;
diff --git a/test/behavior.zig b/test/behavior.zig
index bdd428074b..96a323a6c8 100644
--- a/test/behavior.zig
+++ b/test/behavior.zig
@@ -7,6 +7,8 @@ comptime {
_ = @import("cases/bitcast.zig");
_ = @import("cases/bool.zig");
_ = @import("cases/bugs/394.zig");
+ _ = @import("cases/bugs/655.zig");
+ _ = @import("cases/bugs/656.zig");
_ = @import("cases/cast.zig");
_ = @import("cases/const_slice_child.zig");
_ = @import("cases/defer.zig");
@@ -18,7 +20,6 @@ comptime {
_ = @import("cases/fn.zig");
_ = @import("cases/for.zig");
_ = @import("cases/generics.zig");
- _ = @import("cases/goto.zig");
_ = @import("cases/if.zig");
_ = @import("cases/import.zig");
_ = @import("cases/incomplete_struct_param_tld.zig");
diff --git a/test/cases/align.zig b/test/cases/align.zig
index 3bf0d9c9af..3105945e04 100644
--- a/test/cases/align.zig
+++ b/test/cases/align.zig
@@ -10,7 +10,7 @@ test "global variable alignment" {
assert(@typeOf(slice) == []align(4) u8);
}
-fn derp() align(@sizeOf(usize) * 2) -> i32 { 1234 }
+fn derp() align(@sizeOf(usize) * 2) -> i32 { return 1234; }
fn noop1() align(1) {}
fn noop4() align(4) {}
@@ -53,14 +53,14 @@ test "implicitly decreasing pointer alignment" {
assert(addUnaligned(&a, &b) == 7);
}
-fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) -> u32 { *a + *b }
+fn addUnaligned(a: &align(1) const u32, b: &align(1) const u32) -> u32 { return *a + *b; }
test "implicitly decreasing slice alignment" {
const a: u32 align(4) = 3;
const b: u32 align(8) = 4;
assert(addUnalignedSlice((&a)[0..1], (&b)[0..1]) == 7);
}
-fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) -> u32 { a[0] + b[0] }
+fn addUnalignedSlice(a: []align(1) const u32, b: []align(1) const u32) -> u32 { return a[0] + b[0]; }
test "specifying alignment allows pointer cast" {
testBytesAlign(0x33);
@@ -115,20 +115,20 @@ fn testImplicitlyDecreaseFnAlign(ptr: fn () align(1) -> i32, answer: i32) {
assert(ptr() == answer);
}
-fn alignedSmall() align(8) -> i32 { 1234 }
-fn alignedBig() align(16) -> i32 { 5678 }
+fn alignedSmall() align(8) -> i32 { return 1234; }
+fn alignedBig() align(16) -> i32 { return 5678; }
test "@alignCast functions" {
assert(fnExpectsOnly1(simple4) == 0x19);
}
fn fnExpectsOnly1(ptr: fn()align(1) -> i32) -> i32 {
- fnExpects4(@alignCast(4, ptr))
+ return fnExpects4(@alignCast(4, ptr));
}
fn fnExpects4(ptr: fn()align(4) -> i32) -> i32 {
- ptr()
+ return ptr();
}
-fn simple4() align(4) -> i32 { 0x19 }
+fn simple4() align(4) -> i32 { return 0x19; }
test "generic function with align param" {
@@ -137,7 +137,7 @@ test "generic function with align param" {
assert(whyWouldYouEverDoThis(8) == 0x1);
}
-fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) -> u8 { 0x1 }
+fn whyWouldYouEverDoThis(comptime align_bytes: u8) align(align_bytes) -> u8 { return 0x1; }
test "@ptrCast preserves alignment of bigger source" {
diff --git a/test/cases/array.zig b/test/cases/array.zig
index a6fa07b004..bf77e51fee 100644
--- a/test/cases/array.zig
+++ b/test/cases/array.zig
@@ -22,7 +22,7 @@ test "arrays" {
assert(getArrayLen(array) == 5);
}
fn getArrayLen(a: []const u32) -> usize {
- a.len
+ return a.len;
}
test "void arrays" {
@@ -41,7 +41,7 @@ test "array literal" {
}
test "array dot len const expr" {
- assert(comptime {some_array.len == 4});
+ assert(comptime x: {break :x some_array.len == 4;});
}
const ArrayDotLenConstExpr = struct {
diff --git a/test/cases/bitcast.zig b/test/cases/bitcast.zig
index 72ca24cf5e..0a92d9d606 100644
--- a/test/cases/bitcast.zig
+++ b/test/cases/bitcast.zig
@@ -10,5 +10,5 @@ fn testBitCast_i32_u32() {
assert(conv2(@maxValue(u32)) == -1);
}
-fn conv(x: i32) -> u32 { @bitCast(u32, x) }
-fn conv2(x: u32) -> i32 { @bitCast(i32, x) }
+fn conv(x: i32) -> u32 { return @bitCast(u32, x); }
+fn conv2(x: u32) -> i32 { return @bitCast(i32, x); }
diff --git a/test/cases/bool.zig b/test/cases/bool.zig
index 61bb3bf759..1203e696ba 100644
--- a/test/cases/bool.zig
+++ b/test/cases/bool.zig
@@ -22,7 +22,7 @@ test "bool cmp" {
assert(testBoolCmp(true, false) == false);
}
fn testBoolCmp(a: bool, b: bool) -> bool {
- a == b
+ return a == b;
}
const global_f = false;
diff --git a/test/cases/bugs/655.zig b/test/cases/bugs/655.zig
new file mode 100644
index 0000000000..a0da9d53a2
--- /dev/null
+++ b/test/cases/bugs/655.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+const other_file = @import("655_other_file.zig");
+
+test "function with &const parameter with type dereferenced by namespace" {
+ const x: other_file.Integer = 1234;
+ comptime std.debug.assert(@typeOf(&x) == &const other_file.Integer);
+ foo(x);
+}
+
+fn foo(x: &const other_file.Integer) {
+ std.debug.assert(*x == 1234);
+}
diff --git a/test/cases/bugs/655_other_file.zig b/test/cases/bugs/655_other_file.zig
new file mode 100644
index 0000000000..df1df44955
--- /dev/null
+++ b/test/cases/bugs/655_other_file.zig
@@ -0,0 +1 @@
+pub const Integer = u32;
diff --git a/test/cases/bugs/656.zig b/test/cases/bugs/656.zig
new file mode 100644
index 0000000000..70000c9efd
--- /dev/null
+++ b/test/cases/bugs/656.zig
@@ -0,0 +1,30 @@
+const assert = @import("std").debug.assert;
+
+const PrefixOp = union(enum) {
+ Return,
+ AddrOf: Value,
+};
+
+const Value = struct {
+ align_expr: ?u32,
+};
+
+test "nullable if after an if in a switch prong of a switch with 2 prongs in an else" {
+ foo(false, true);
+}
+
+fn foo(a: bool, b: bool) {
+ var prefix_op = PrefixOp { .AddrOf = Value { .align_expr = 1234 } };
+ if (a) {
+ } else {
+ switch (prefix_op) {
+ PrefixOp.AddrOf => |addr_of_info| {
+ if (b) { }
+ if (addr_of_info.align_expr) |align_expr| {
+ assert(align_expr == 1234);
+ }
+ },
+ PrefixOp.Return => {},
+ }
+ }
+}
diff --git a/test/cases/cast.zig b/test/cases/cast.zig
index c93d2e7413..8b16cb44d4 100644
--- a/test/cases/cast.zig
+++ b/test/cases/cast.zig
@@ -50,7 +50,7 @@ test "peer resolve arrays of different size to const slice" {
comptime assert(mem.eql(u8, boolToStr(false), "false"));
}
fn boolToStr(b: bool) -> []const u8 {
- if (b) "true" else "false"
+ return if (b) "true" else "false";
}
@@ -239,17 +239,17 @@ test "peer type resolution: error and [N]T" {
error BadValue;
fn testPeerErrorAndArray(x: u8) -> %[]const u8 {
- switch (x) {
+ return switch (x) {
0x00 => "OK",
else => error.BadValue,
- }
+ };
}
fn testPeerErrorAndArray2(x: u8) -> %[]const u8 {
- switch (x) {
+ return switch (x) {
0x00 => "OK",
0x01 => "OKK",
else => error.BadValue,
- }
+ };
}
test "explicit cast float number literal to integer if no fraction component" {
@@ -269,11 +269,11 @@ fn testCast128() {
}
fn cast128Int(x: f128) -> u128 {
- @bitCast(u128, x)
+ return @bitCast(u128, x);
}
fn cast128Float(x: u128) -> f128 {
- @bitCast(f128, x)
+ return @bitCast(f128, x);
}
test "const slice widen cast" {
diff --git a/test/cases/defer.zig b/test/cases/defer.zig
index 6cafe9f334..d4cb79ec46 100644
--- a/test/cases/defer.zig
+++ b/test/cases/defer.zig
@@ -7,9 +7,9 @@ error FalseNotAllowed;
fn runSomeErrorDefers(x: bool) -> %bool {
index = 0;
- defer {result[index] = 'a'; index += 1;};
- %defer {result[index] = 'b'; index += 1;};
- defer {result[index] = 'c'; index += 1;};
+ defer {result[index] = 'a'; index += 1;}
+ %defer {result[index] = 'b'; index += 1;}
+ defer {result[index] = 'c'; index += 1;}
return if (x) x else error.FalseNotAllowed;
}
@@ -18,9 +18,9 @@ test "mixing normal and error defers" {
assert(result[0] == 'c');
assert(result[1] == 'a');
- const ok = runSomeErrorDefers(false) %% |err| {
+ const ok = runSomeErrorDefers(false) %% |err| x: {
assert(err == error.FalseNotAllowed);
- true
+ break :x true;
};
assert(ok);
assert(result[0] == 'c');
@@ -41,5 +41,5 @@ fn testBreakContInDefer(x: usize) {
if (i == 5) break;
}
assert(i == 5);
- };
+ }
}
diff --git a/test/cases/enum.zig b/test/cases/enum.zig
index ec900511eb..26aa8fb589 100644
--- a/test/cases/enum.zig
+++ b/test/cases/enum.zig
@@ -41,7 +41,7 @@ const Bar = enum {
};
fn returnAnInt(x: i32) -> Foo {
- Foo { .One = x }
+ return Foo { .One = x };
}
@@ -344,3 +344,36 @@ fn testEnumWithSpecifiedAndUnspecifiedTagValues(x: MultipleChoice2) {
MultipleChoice2.Unspecified5 => 9,
});
}
+
+test "cast integer literal to enum" {
+ assert(MultipleChoice2(0) == MultipleChoice2.Unspecified1);
+ assert(MultipleChoice2(40) == MultipleChoice2.B);
+}
+
+const EnumWithOneMember = enum {
+ Eof,
+};
+
+fn doALoopThing(id: EnumWithOneMember) {
+ while (true) {
+ if (id == EnumWithOneMember.Eof) {
+ break;
+ }
+ @compileError("above if condition should be comptime");
+ }
+}
+
+test "comparison operator on enum with one member is comptime known" {
+ doALoopThing(EnumWithOneMember.Eof);
+}
+
+const State = enum {
+ Start,
+};
+test "switch on enum with one member is comptime known" {
+ var state = State.Start;
+ switch (state) {
+ State.Start => return,
+ }
+ @compileError("analysis should not reach here");
+}
diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig
index ae48a266d0..c28692575e 100644
--- a/test/cases/enum_with_members.zig
+++ b/test/cases/enum_with_members.zig
@@ -8,9 +8,9 @@ const ET = union(enum) {
pub fn print(a: &const ET, buf: []u8) -> %usize {
return switch (*a) {
- ET.SINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) },
- ET.UINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) },
- }
+ ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
+ ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),
+ };
}
};
diff --git a/test/cases/error.zig b/test/cases/error.zig
index 9e55f57b6d..3974e9dc7c 100644
--- a/test/cases/error.zig
+++ b/test/cases/error.zig
@@ -3,7 +3,7 @@ const mem = @import("std").mem;
pub fn foo() -> %i32 {
const x = %return bar();
- return x + 1
+ return x + 1;
}
pub fn bar() -> %i32 {
@@ -21,7 +21,7 @@ test "error wrapping" {
error ItBroke;
fn gimmeItBroke() -> []const u8 {
- @errorName(error.ItBroke)
+ return @errorName(error.ItBroke);
}
test "@errorName" {
@@ -48,7 +48,7 @@ error AnError;
error AnError;
error SecondError;
fn shouldBeNotEqual(a: error, b: error) {
- if (a == b) unreachable
+ if (a == b) unreachable;
}
@@ -60,11 +60,7 @@ test "error binary operator" {
}
error ItBroke;
fn errBinaryOperatorG(x: bool) -> %isize {
- if (x) {
- error.ItBroke
- } else {
- isize(10)
- }
+ return if (x) error.ItBroke else isize(10);
}
@@ -72,7 +68,7 @@ test "unwrap simple value from error" {
const i = %%unwrapSimpleValueFromErrorDo();
assert(i == 13);
}
-fn unwrapSimpleValueFromErrorDo() -> %isize { 13 }
+fn unwrapSimpleValueFromErrorDo() -> %isize { return 13; }
test "error return in assignment" {
diff --git a/test/cases/eval.zig b/test/cases/eval.zig
index c657482d08..a2e015fba7 100644
--- a/test/cases/eval.zig
+++ b/test/cases/eval.zig
@@ -44,7 +44,7 @@ test "static function evaluation" {
assert(statically_added_number == 3);
}
const statically_added_number = staticAdd(1, 2);
-fn staticAdd(a: i32, b: i32) -> i32 { a + b }
+fn staticAdd(a: i32, b: i32) -> i32 { return a + b; }
test "const expr eval on single expr blocks" {
@@ -54,10 +54,10 @@ test "const expr eval on single expr blocks" {
fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) -> i32 {
const literal = 3;
- const result = if (b) {
- literal
- } else {
- x
+ const result = if (b) b: {
+ break :b literal;
+ } else b: {
+ break :b x;
};
return result;
@@ -94,9 +94,9 @@ pub const Vec3 = struct {
data: [3]f32,
};
pub fn vec3(x: f32, y: f32, z: f32) -> Vec3 {
- Vec3 {
+ return Vec3 {
.data = []f32 { x, y, z, },
- }
+ };
}
@@ -176,7 +176,7 @@ fn max(comptime T: type, a: T, b: T) -> T {
}
}
fn letsTryToCompareBools(a: bool, b: bool) -> bool {
- max(bool, a, b)
+ return max(bool, a, b);
}
test "inlined block and runtime block phi" {
assert(letsTryToCompareBools(true, true));
@@ -202,9 +202,9 @@ const cmd_fns = []CmdFn{
CmdFn {.name = "two", .func = two},
CmdFn {.name = "three", .func = three},
};
-fn one(value: i32) -> i32 { value + 1 }
-fn two(value: i32) -> i32 { value + 2 }
-fn three(value: i32) -> i32 { value + 3 }
+fn one(value: i32) -> i32 { return value + 1; }
+fn two(value: i32) -> i32 { return value + 2; }
+fn three(value: i32) -> i32 { return value + 3; }
fn performFn(comptime prefix_char: u8, start_value: i32) -> i32 {
var result: i32 = start_value;
@@ -317,12 +317,12 @@ test "create global array with for loop" {
assert(global_array[9] == 9 * 9);
}
-const global_array = {
+const global_array = x: {
var result: [10]usize = undefined;
for (result) |*item, index| {
*item = index * index;
}
- result
+ break :x result;
};
test "compile-time downcast when the bits fit" {
diff --git a/test/cases/fn.zig b/test/cases/fn.zig
index c948d8af3d..aad68447b2 100644
--- a/test/cases/fn.zig
+++ b/test/cases/fn.zig
@@ -4,7 +4,7 @@ test "params" {
assert(testParamsAdd(22, 11) == 33);
}
fn testParamsAdd(a: i32, b: i32) -> i32 {
- a + b
+ return a + b;
}
@@ -22,7 +22,7 @@ test "void parameters" {
}
fn voidFun(a: i32, b: void, c: i32, d: void) {
const v = b;
- const vv: void = if (a == 1) {v} else {};
+ const vv: void = if (a == 1) v else {};
assert(a + c == 3);
return vv;
}
@@ -45,9 +45,9 @@ test "separate block scopes" {
assert(no_conflict == 5);
}
- const c = {
+ const c = x: {
const no_conflict = i32(10);
- no_conflict
+ break :x no_conflict;
};
assert(c == 10);
}
@@ -73,7 +73,7 @@ test "implicit cast function unreachable return" {
fn wantsFnWithVoid(f: fn()) { }
fn fnWithUnreachable() -> noreturn {
- unreachable
+ unreachable;
}
@@ -83,14 +83,14 @@ test "function pointers" {
assert(f() == u32(i) + 5);
}
}
-fn fn1() -> u32 {5}
-fn fn2() -> u32 {6}
-fn fn3() -> u32 {7}
-fn fn4() -> u32 {8}
+fn fn1() -> u32 {return 5;}
+fn fn2() -> u32 {return 6;}
+fn fn3() -> u32 {return 7;}
+fn fn4() -> u32 {return 8;}
test "inline function call" {
assert(@inlineCall(add, 3, 9) == 12);
}
-fn add(a: i32, b: i32) -> i32 { a + b }
+fn add(a: i32, b: i32) -> i32 { return a + b; }
diff --git a/test/cases/for.zig b/test/cases/for.zig
index e10e7acaca..5a7919541f 100644
--- a/test/cases/for.zig
+++ b/test/cases/for.zig
@@ -12,7 +12,7 @@ test "continue in for loop" {
}
break;
}
- if (sum != 6) unreachable
+ if (sum != 6) unreachable;
}
test "for loop with pointer elem var" {
@@ -55,3 +55,37 @@ test "basic for loop" {
assert(mem.eql(u8, buffer[0..buf_index], expected_result));
}
+
+test "break from outer for loop" {
+ testBreakOuter();
+ comptime testBreakOuter();
+}
+
+fn testBreakOuter() {
+ var array = "aoeu";
+ var count: usize = 0;
+ outer: for (array) |_| {
+ for (array) |_2| { // TODO shouldn't get error for redeclaring "_"
+ count += 1;
+ break :outer;
+ }
+ }
+ assert(count == 1);
+}
+
+test "continue outer for loop" {
+ testContinueOuter();
+ comptime testContinueOuter();
+}
+
+fn testContinueOuter() {
+ var array = "aoeu";
+ var counter: usize = 0;
+ outer: for (array) |_| {
+ for (array) |_2| { // TODO shouldn't get error for redeclaring "_"
+ counter += 1;
+ continue :outer;
+ }
+ }
+ assert(counter == array.len);
+}
diff --git a/test/cases/generics.zig b/test/cases/generics.zig
index d6a3192a6b..96500e39b2 100644
--- a/test/cases/generics.zig
+++ b/test/cases/generics.zig
@@ -11,7 +11,7 @@ fn max(comptime T: type, a: T, b: T) -> T {
}
fn add(comptime a: i32, b: i32) -> i32 {
- return (comptime {a}) + b;
+ return (comptime a) + b;
}
const the_max = max(u32, 1234, 5678);
@@ -20,15 +20,15 @@ test "compile time generic eval" {
}
fn gimmeTheBigOne(a: u32, b: u32) -> u32 {
- max(u32, a, b)
+ return max(u32, a, b);
}
fn shouldCallSameInstance(a: u32, b: u32) -> u32 {
- max(u32, a, b)
+ return max(u32, a, b);
}
fn sameButWithFloats(a: f64, b: f64) -> f64 {
- max(f64, a, b)
+ return max(f64, a, b);
}
test "fn with comptime args" {
@@ -49,28 +49,28 @@ comptime {
}
fn max_var(a: var, b: var) -> @typeOf(a + b) {
- if (a > b) a else b
+ return if (a > b) a else b;
}
fn max_i32(a: i32, b: i32) -> i32 {
- max_var(a, b)
+ return max_var(a, b);
}
fn max_f64(a: f64, b: f64) -> f64 {
- max_var(a, b)
+ return max_var(a, b);
}
pub fn List(comptime T: type) -> type {
- SmallList(T, 8)
+ return SmallList(T, 8);
}
pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) -> type {
- struct {
+ return struct {
items: []T,
length: usize,
prealloc_items: [STATIC_SIZE]T,
- }
+ };
}
test "function with return type type" {
@@ -91,20 +91,20 @@ test "generic struct" {
assert(b1.getVal());
}
fn GenNode(comptime T: type) -> type {
- struct {
+ return struct {
value: T,
next: ?&GenNode(T),
- fn getVal(n: &const GenNode(T)) -> T { n.value }
- }
+ fn getVal(n: &const GenNode(T)) -> T { return n.value; }
+ };
}
test "const decls in struct" {
assert(GenericDataThing(3).count_plus_one == 4);
}
fn GenericDataThing(comptime count: isize) -> type {
- struct {
+ return struct {
const count_plus_one = count + 1;
- }
+ };
}
@@ -120,16 +120,16 @@ test "generic fn with implicit cast" {
assert(getFirstByte(u8, []u8 {13}) == 13);
assert(getFirstByte(u16, []u16 {0, 13}) == 0);
}
-fn getByte(ptr: ?&const u8) -> u8 {*??ptr}
+fn getByte(ptr: ?&const u8) -> u8 {return *??ptr;}
fn getFirstByte(comptime T: type, mem: []const T) -> u8 {
- getByte(@ptrCast(&const u8, &mem[0]))
+ return getByte(@ptrCast(&const u8, &mem[0]));
}
const foos = []fn(var) -> bool { foo1, foo2 };
-fn foo1(arg: var) -> bool { arg }
-fn foo2(arg: var) -> bool { !arg }
+fn foo1(arg: var) -> bool { return arg; }
+fn foo2(arg: var) -> bool { return !arg; }
test "array of generic fns" {
assert(foos[0](true));
diff --git a/test/cases/goto.zig b/test/cases/goto.zig
deleted file mode 100644
index 7713bc14aa..0000000000
--- a/test/cases/goto.zig
+++ /dev/null
@@ -1,37 +0,0 @@
-const assert = @import("std").debug.assert;
-
-test "goto and labels" {
- gotoLoop();
- assert(goto_counter == 10);
-}
-fn gotoLoop() {
- var i: i32 = 0;
- goto cond;
-loop:
- i += 1;
-cond:
- if (!(i < 10)) goto end;
- goto_counter += 1;
- goto loop;
-end:
-}
-var goto_counter: i32 = 0;
-
-
-
-test "goto leave defer scope" {
- testGotoLeaveDeferScope(true);
-}
-fn testGotoLeaveDeferScope(b: bool) {
- var it_worked = false;
-
- goto entry;
-exit:
- if (it_worked) {
- return;
- }
- unreachable;
-entry:
- defer it_worked = true;
- if (b) goto exit;
-}
diff --git a/test/cases/if.zig b/test/cases/if.zig
index 10fedcdea5..d1fa717b50 100644
--- a/test/cases/if.zig
+++ b/test/cases/if.zig
@@ -29,10 +29,10 @@ test "else if expression" {
}
fn elseIfExpressionF(c: u8) -> u8 {
if (c == 0) {
- 0
+ return 0;
} else if (c == 1) {
- 1
+ return 1;
} else {
- u8(2)
+ return u8(2);
}
}
diff --git a/test/cases/import/a_namespace.zig b/test/cases/import/a_namespace.zig
index d6926fbb5e..40cdd69139 100644
--- a/test/cases/import/a_namespace.zig
+++ b/test/cases/import/a_namespace.zig
@@ -1 +1 @@
-pub fn foo() -> i32 { 1234 }
+pub fn foo() -> i32 { return 1234; }
diff --git a/test/cases/ir_block_deps.zig b/test/cases/ir_block_deps.zig
index e6ce12bd65..a70dff0c84 100644
--- a/test/cases/ir_block_deps.zig
+++ b/test/cases/ir_block_deps.zig
@@ -8,10 +8,10 @@ fn foo(id: u64) -> %i32 {
return %return getErrInt();
},
else => error.ItBroke,
- }
+ };
}
-fn getErrInt() -> %i32 { 0 }
+fn getErrInt() -> %i32 { return 0; }
error ItBroke;
diff --git a/test/cases/math.zig b/test/cases/math.zig
index 1d29800aad..b4e0e4cfd6 100644
--- a/test/cases/math.zig
+++ b/test/cases/math.zig
@@ -28,16 +28,16 @@ fn testDivision() {
assert(divTrunc(f32, -5.0, 3.0) == -1.0);
}
fn div(comptime T: type, a: T, b: T) -> T {
- a / b
+ return a / b;
}
fn divExact(comptime T: type, a: T, b: T) -> T {
- @divExact(a, b)
+ return @divExact(a, b);
}
fn divFloor(comptime T: type, a: T, b: T) -> T {
- @divFloor(a, b)
+ return @divFloor(a, b);
}
fn divTrunc(comptime T: type, a: T, b: T) -> T {
- @divTrunc(a, b)
+ return @divTrunc(a, b);
}
test "@addWithOverflow" {
@@ -71,7 +71,7 @@ fn testClz() {
}
fn clz(x: var) -> usize {
- @clz(x)
+ return @clz(x);
}
test "@ctz" {
@@ -86,7 +86,7 @@ fn testCtz() {
}
fn ctz(x: var) -> usize {
- @ctz(x)
+ return @ctz(x);
}
test "assignment operators" {
@@ -180,10 +180,10 @@ fn test_u64_div() {
assert(result.remainder == 100663296);
}
fn divWithResult(a: u64, b: u64) -> DivResult {
- DivResult {
+ return DivResult {
.quotient = a / b,
.remainder = a % b,
- }
+ };
}
const DivResult = struct {
quotient: u64,
@@ -191,8 +191,8 @@ const DivResult = struct {
};
test "binary not" {
- assert(comptime {~u16(0b1010101010101010) == 0b0101010101010101});
- assert(comptime {~u64(2147483647) == 18446744071562067968});
+ assert(comptime x: {break :x ~u16(0b1010101010101010) == 0b0101010101010101;});
+ assert(comptime x: {break :x ~u64(2147483647) == 18446744071562067968;});
testBinaryNot(0b1010101010101010);
}
@@ -331,7 +331,7 @@ test "f128" {
comptime test_f128();
}
-fn make_f128(x: f128) -> f128 { x }
+fn make_f128(x: f128) -> f128 { return x; }
fn test_f128() {
assert(@sizeOf(f128) == 16);
diff --git a/test/cases/misc.zig b/test/cases/misc.zig
index 9f4f064f6b..e456ca529a 100644
--- a/test/cases/misc.zig
+++ b/test/cases/misc.zig
@@ -12,8 +12,11 @@ test "empty function with comments" {
emptyFunctionWithComments();
}
-export fn disabledExternFn() {
- @setGlobalLinkage(disabledExternFn, builtin.GlobalLinkage.Internal);
+comptime {
+ @export("disabledExternFn", disabledExternFn, builtin.GlobalLinkage.Internal);
+}
+
+extern fn disabledExternFn() {
}
test "call disabled extern fn" {
@@ -107,17 +110,17 @@ fn testShortCircuit(f: bool, t: bool) {
var hit_3 = f;
var hit_4 = f;
- if (t or {assert(f); f}) {
+ if (t or x: {assert(f); break :x f;}) {
hit_1 = t;
}
- if (f or { hit_2 = t; f }) {
+ if (f or x: { hit_2 = t; break :x f; }) {
assert(f);
}
- if (t and { hit_3 = t; f }) {
+ if (t and x: { hit_3 = t; break :x f; }) {
assert(f);
}
- if (f and {assert(f); f}) {
+ if (f and x: {assert(f); break :x f;}) {
assert(f);
} else {
hit_4 = t;
@@ -132,11 +135,11 @@ test "truncate" {
assert(testTruncate(0x10fd) == 0xfd);
}
fn testTruncate(x: u32) -> u8 {
- @truncate(u8, x)
+ return @truncate(u8, x);
}
fn first4KeysOfHomeRow() -> []const u8 {
- "aoeu"
+ return "aoeu";
}
test "return string from function" {
@@ -164,7 +167,7 @@ test "memcpy and memset intrinsics" {
}
test "builtin static eval" {
- const x : i32 = comptime {1 + 2 + 3};
+ const x : i32 = comptime x: {break :x 1 + 2 + 3;};
assert(x == comptime 6);
}
@@ -187,7 +190,7 @@ test "slicing" {
test "constant equal function pointers" {
const alias = emptyFn;
- assert(comptime {emptyFn == alias});
+ assert(comptime x: {break :x emptyFn == alias;});
}
fn emptyFn() {}
@@ -277,14 +280,14 @@ test "cast small unsigned to larger signed" {
assert(castSmallUnsignedToLargerSigned1(200) == i16(200));
assert(castSmallUnsignedToLargerSigned2(9999) == i64(9999));
}
-fn castSmallUnsignedToLargerSigned1(x: u8) -> i16 { x }
-fn castSmallUnsignedToLargerSigned2(x: u16) -> i64 { x }
+fn castSmallUnsignedToLargerSigned1(x: u8) -> i16 { return x; }
+fn castSmallUnsignedToLargerSigned2(x: u16) -> i64 { return x; }
test "implicit cast after unreachable" {
assert(outer() == 1234);
}
-fn inner() -> i32 { 1234 }
+fn inner() -> i32 { return 1234; }
fn outer() -> i64 {
return inner();
}
@@ -307,8 +310,8 @@ test "call result of if else expression" {
fn f2(x: bool) -> []const u8 {
return (if (x) fA else fB)();
}
-fn fA() -> []const u8 { "a" }
-fn fB() -> []const u8 { "b" }
+fn fA() -> []const u8 { return "a"; }
+fn fB() -> []const u8 { return "b"; }
test "const expression eval handling of variables" {
@@ -376,7 +379,7 @@ test "pointer comparison" {
assert(ptrEql(b, b));
}
fn ptrEql(a: &const []const u8, b: &const []const u8) -> bool {
- a == b
+ return a == b;
}
@@ -480,7 +483,7 @@ test "@typeId" {
assert(@typeId(AUnion) == Tid.Union);
assert(@typeId(fn()) == Tid.Fn);
assert(@typeId(@typeOf(builtin)) == Tid.Namespace);
- assert(@typeId(@typeOf({this})) == Tid.Block);
+ assert(@typeId(@typeOf(x: {break :x this;})) == Tid.Block);
// TODO bound fn
// TODO arg tuple
// TODO opaque
@@ -504,7 +507,7 @@ test "@typeName" {
test "volatile load and store" {
var number: i32 = 1234;
- const ptr = &volatile number;
+ const ptr = (&volatile i32)(&number);
*ptr += 1;
assert(*ptr == 1235);
}
diff --git a/test/cases/reflection.zig b/test/cases/reflection.zig
index cbd98d212f..9b298f8823 100644
--- a/test/cases/reflection.zig
+++ b/test/cases/reflection.zig
@@ -22,7 +22,7 @@ test "reflection: function return type, var args, and param types" {
}
}
-fn dummy(a: bool, b: i32, c: f32) -> i32 { 1234 }
+fn dummy(a: bool, b: i32, c: f32) -> i32 { return 1234; }
fn dummy_varargs(args: ...) {}
test "reflection: struct member types and names" {
diff --git a/test/cases/slice.zig b/test/cases/slice.zig
index 1498f948ea..c47de5b09e 100644
--- a/test/cases/slice.zig
+++ b/test/cases/slice.zig
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
+const mem = @import("std").mem;
const x = @intToPtr(&i32, 0x1000)[0..0x500];
const y = x[0x100..];
@@ -15,3 +16,21 @@ test "slice child property" {
var slice = array[0..];
assert(@typeOf(slice).Child == i32);
}
+
+test "debug safety lets us slice from len..len" {
+ var an_array = []u8{1, 2, 3};
+ assert(mem.eql(u8, sliceFromLenToLen(an_array[0..], 3, 3), ""));
+}
+
+fn sliceFromLenToLen(a_slice: []u8, start: usize, end: usize) -> []u8 {
+ return a_slice[start..end];
+}
+
+test "implicitly cast array of size 0 to slice" {
+ var msg = []u8 {};
+ assertLenIsZero(msg);
+}
+
+fn assertLenIsZero(msg: []const u8) {
+ assert(msg.len == 0);
+}
diff --git a/test/cases/struct.zig b/test/cases/struct.zig
index 1a9a03c71a..bdd2e5d049 100644
--- a/test/cases/struct.zig
+++ b/test/cases/struct.zig
@@ -2,7 +2,7 @@ const assert = @import("std").debug.assert;
const builtin = @import("builtin");
const StructWithNoFields = struct {
- fn add(a: i32, b: i32) -> i32 { a + b }
+ fn add(a: i32, b: i32) -> i32 { return a + b; }
};
const empty_global_instance = StructWithNoFields {};
@@ -109,7 +109,7 @@ const Foo = struct {
ptr: fn() -> i32,
};
-fn aFunc() -> i32 { 13 }
+fn aFunc() -> i32 { return 13; }
fn callStructField(foo: &const Foo) -> i32 {
return foo.ptr();
@@ -124,7 +124,7 @@ test "store member function in variable" {
}
const MemberFnTestFoo = struct {
x: i32,
- fn member(foo: &const MemberFnTestFoo) -> i32 { foo.x }
+ fn member(foo: &const MemberFnTestFoo) -> i32 { return foo.x; }
};
@@ -141,7 +141,7 @@ test "member functions" {
const MemberFnRand = struct {
seed: u32,
pub fn getSeed(r: &const MemberFnRand) -> u32 {
- r.seed
+ return r.seed;
}
};
@@ -154,10 +154,10 @@ const Bar = struct {
y: i32,
};
fn makeBar(x: i32, y: i32) -> Bar {
- Bar {
+ return Bar {
.x = x,
.y = y,
- }
+ };
}
test "empty struct method call" {
@@ -166,7 +166,7 @@ test "empty struct method call" {
}
const EmptyStruct = struct {
fn method(es: &const EmptyStruct) -> i32 {
- 1234
+ return 1234;
}
};
@@ -176,14 +176,14 @@ test "return empty struct from fn" {
}
const EmptyStruct2 = struct {};
fn testReturnEmptyStructFromFn() -> EmptyStruct2 {
- EmptyStruct2 {}
+ return EmptyStruct2 {};
}
test "pass slice of empty struct to fn" {
assert(testPassSliceOfEmptyStructToFn([]EmptyStruct2{ EmptyStruct2{} }) == 1);
}
fn testPassSliceOfEmptyStructToFn(slice: []const EmptyStruct2) -> usize {
- slice.len
+ return slice.len;
}
const APackedStruct = packed struct {
@@ -379,3 +379,28 @@ const Nibbles = packed struct {
x: u4,
y: u4,
};
+
+const Bitfields = packed struct {
+ f1: u16,
+ f2: u16,
+ f3: u8,
+ f4: u8,
+ f5: u4,
+ f6: u4,
+ f7: u8,
+};
+
+test "native bit field understands endianness" {
+ var all: u64 = 0x7765443322221111;
+ var bytes: [8]u8 = undefined;
+ @memcpy(&bytes[0], @ptrCast(&u8, &all), 8);
+ var bitfields = *@ptrCast(&Bitfields, &bytes[0]);
+
+ assert(bitfields.f1 == 0x1111);
+ assert(bitfields.f2 == 0x2222);
+ assert(bitfields.f3 == 0x33);
+ assert(bitfields.f4 == 0x44);
+ assert(bitfields.f5 == 0x5);
+ assert(bitfields.f6 == 0x6);
+ assert(bitfields.f7 == 0x77);
+}
diff --git a/test/cases/switch.zig b/test/cases/switch.zig
index 11c2178427..878c0af9e4 100644
--- a/test/cases/switch.zig
+++ b/test/cases/switch.zig
@@ -21,12 +21,12 @@ test "switch with all ranges" {
}
fn testSwitchWithAllRanges(x: u32, y: u32) -> u32 {
- switch (x) {
+ return switch (x) {
0 ... 100 => 1,
101 ... 200 => 2,
201 ... 300 => 3,
else => y,
- }
+ };
}
test "implicit comptime switch" {
@@ -132,7 +132,7 @@ test "switch with multiple expressions" {
assert(x == 2);
}
fn returnsFive() -> i32 {
- 5
+ return 5;
}
@@ -161,10 +161,10 @@ test "switch on type" {
}
fn trueIfBoolFalseOtherwise(comptime T: type) -> bool {
- switch (T) {
+ return switch (T) {
bool => true,
else => false,
- }
+ };
}
test "switch handles all cases of number" {
@@ -186,22 +186,22 @@ fn testSwitchHandleAllCases() {
}
fn testSwitchHandleAllCasesExhaustive(x: u2) -> u2 {
- switch (x) {
+ return switch (x) {
0 => u2(3),
1 => 2,
2 => 1,
3 => 0,
- }
+ };
}
fn testSwitchHandleAllCasesRange(x: u8) -> u8 {
- switch (x) {
+ return switch (x) {
0 ... 100 => u8(0),
101 ... 200 => 1,
201, 203 => 2,
202 => 4,
204 ... 255 => 3,
- }
+ };
}
test "switch all prongs unreachable" {
diff --git a/test/cases/switch_prong_err_enum.zig b/test/cases/switch_prong_err_enum.zig
index 21f6b04037..be15193c74 100644
--- a/test/cases/switch_prong_err_enum.zig
+++ b/test/cases/switch_prong_err_enum.zig
@@ -18,7 +18,7 @@ fn doThing(form_id: u64) -> %FormValue {
return switch (form_id) {
17 => FormValue { .Address = %return readOnce() },
else => error.InvalidDebugInfo,
- }
+ };
}
test "switch prong returns error enum" {
diff --git a/test/cases/switch_prong_implicit_cast.zig b/test/cases/switch_prong_implicit_cast.zig
index e7fe53b841..9e7c091494 100644
--- a/test/cases/switch_prong_implicit_cast.zig
+++ b/test/cases/switch_prong_implicit_cast.zig
@@ -8,11 +8,11 @@ const FormValue = union(enum) {
error Whatever;
fn foo(id: u64) -> %FormValue {
- switch (id) {
+ return switch (id) {
2 => FormValue { .Two = true },
1 => FormValue { .One = {} },
else => return error.Whatever,
- }
+ };
}
test "switch prong implicit cast" {
diff --git a/test/cases/this.zig b/test/cases/this.zig
index b64c52a098..b4e6b32a09 100644
--- a/test/cases/this.zig
+++ b/test/cases/this.zig
@@ -3,7 +3,7 @@ const assert = @import("std").debug.assert;
const module = this;
fn Point(comptime T: type) -> type {
- struct {
+ return struct {
const Self = this;
x: T,
y: T,
@@ -12,20 +12,16 @@ fn Point(comptime T: type) -> type {
self.x += 1;
self.y += 1;
}
- }
+ };
}
fn add(x: i32, y: i32) -> i32 {
- x + y
+ return x + y;
}
fn factorial(x: i32) -> i32 {
const selfFn = this;
- if (x == 0) {
- 1
- } else {
- x * selfFn(x - 1)
- }
+ return if (x == 0) 1 else x * selfFn(x - 1);
}
test "this refer to module call private fn" {
diff --git a/test/cases/try.zig b/test/cases/try.zig
index 2505443712..25b2f321fb 100644
--- a/test/cases/try.zig
+++ b/test/cases/try.zig
@@ -7,9 +7,9 @@ test "try on error union" {
}
fn tryOnErrorUnionImpl() {
- const x = if (returnsTen()) |val| {
+ const x = if (returnsTen()) |val|
val + 1
- } else |err| switch (err) {
+ else |err| switch (err) {
error.ItBroke, error.NoMem => 1,
error.CrappedOut => i32(2),
else => unreachable,
@@ -21,22 +21,14 @@ error ItBroke;
error NoMem;
error CrappedOut;
fn returnsTen() -> %i32 {
- 10
+ return 10;
}
test "try without vars" {
- const result1 = if (failIfTrue(true)) {
- 1
- } else |_| {
- i32(2)
- };
+ const result1 = if (failIfTrue(true)) 1 else |_| i32(2);
assert(result1 == 2);
- const result2 = if (failIfTrue(false)) {
- 1
- } else |_| {
- i32(2)
- };
+ const result2 = if (failIfTrue(false)) 1 else |_| i32(2);
assert(result2 == 1);
}
diff --git a/test/cases/union.zig b/test/cases/union.zig
index 1db9a1bef1..90d869289f 100644
--- a/test/cases/union.zig
+++ b/test/cases/union.zig
@@ -190,3 +190,33 @@ test "cast union to tag type of union" {
fn testCastUnionToTagType(x: &const TheUnion) {
assert(TheTag(*x) == TheTag.B);
}
+
+test "cast tag type of union to union" {
+ var x: Value2 = Letter2.B;
+ assert(Letter2(x) == Letter2.B);
+}
+const Letter2 = enum { A, B, C };
+const Value2 = union(Letter2) { A: i32, B, C, };
+
+test "implicit cast union to its tag type" {
+ var x: Value2 = Letter2.B;
+ assert(x == Letter2.B);
+ giveMeLetterB(x);
+}
+fn giveMeLetterB(x: Letter2) {
+ assert(x == Value2.B);
+}
+
+test "implicit cast from @EnumTagType(TheUnion) to &const TheUnion" {
+ assertIsTheUnion2Item1(TheUnion2.Item1);
+}
+
+const TheUnion2 = union(enum) {
+ Item1,
+ Item2: i32,
+};
+
+fn assertIsTheUnion2Item1(value: &const TheUnion2) {
+ assert(*value == TheUnion2.Item1);
+}
+
diff --git a/test/cases/var_args.zig b/test/cases/var_args.zig
index 363816bc8a..7d16106362 100644
--- a/test/cases/var_args.zig
+++ b/test/cases/var_args.zig
@@ -58,8 +58,8 @@ fn extraFn(extra: u32, args: ...) -> usize {
const foos = []fn(...) -> bool { foo1, foo2 };
-fn foo1(args: ...) -> bool { true }
-fn foo2(args: ...) -> bool { false }
+fn foo1(args: ...) -> bool { return true; }
+fn foo2(args: ...) -> bool { return false; }
test "array of var args functions" {
assert(foos[0]());
diff --git a/test/cases/while.zig b/test/cases/while.zig
index 33833cecfa..e61fe1f9d2 100644
--- a/test/cases/while.zig
+++ b/test/cases/while.zig
@@ -118,80 +118,95 @@ test "while with error union condition" {
var numbers_left: i32 = undefined;
error OutOfNumbers;
fn getNumberOrErr() -> %i32 {
- return if (numbers_left == 0) {
+ return if (numbers_left == 0)
error.OutOfNumbers
- } else {
+ else x: {
numbers_left -= 1;
- numbers_left
+ break :x numbers_left;
};
}
fn getNumberOrNull() -> ?i32 {
- return if (numbers_left == 0) {
+ return if (numbers_left == 0)
null
- } else {
+ else x: {
numbers_left -= 1;
- numbers_left
+ break :x numbers_left;
};
}
test "while on nullable with else result follow else prong" {
const result = while (returnNull()) |value| {
break value;
- } else {
- i32(2)
- };
+ } else i32(2);
assert(result == 2);
}
test "while on nullable with else result follow break prong" {
const result = while (returnMaybe(10)) |value| {
break value;
- } else {
- i32(2)
- };
+ } else i32(2);
assert(result == 10);
}
test "while on error union with else result follow else prong" {
const result = while (returnError()) |value| {
break value;
- } else |err| {
- i32(2)
- };
+ } else |err| i32(2);
assert(result == 2);
}
test "while on error union with else result follow break prong" {
const result = while (returnSuccess(10)) |value| {
break value;
- } else |err| {
- i32(2)
- };
+ } else |err| i32(2);
assert(result == 10);
}
test "while on bool with else result follow else prong" {
const result = while (returnFalse()) {
break i32(10);
- } else {
- i32(2)
- };
+ } else i32(2);
assert(result == 2);
}
test "while on bool with else result follow break prong" {
const result = while (returnTrue()) {
break i32(10);
- } else {
- i32(2)
- };
+ } else i32(2);
assert(result == 10);
}
-fn returnNull() -> ?i32 { null }
-fn returnMaybe(x: i32) -> ?i32 { x }
+test "break from outer while loop" {
+ testBreakOuter();
+ comptime testBreakOuter();
+}
+
+fn testBreakOuter() {
+ outer: while (true) {
+ while (true) {
+ break :outer;
+ }
+ }
+}
+
+test "continue outer while loop" {
+ testContinueOuter();
+ comptime testContinueOuter();
+}
+
+fn testContinueOuter() {
+ var i: usize = 0;
+ outer: while (i < 10) : (i += 1) {
+ while (true) {
+ continue :outer;
+ }
+ }
+}
+
+fn returnNull() -> ?i32 { return null; }
+fn returnMaybe(x: i32) -> ?i32 { return x; }
error YouWantedAnError;
-fn returnError() -> %i32 { error.YouWantedAnError }
-fn returnSuccess(x: i32) -> %i32 { x }
-fn returnFalse() -> bool { false }
-fn returnTrue() -> bool { true }
+fn returnError() -> %i32 { return error.YouWantedAnError; }
+fn returnSuccess(x: i32) -> %i32 { return x; }
+fn returnFalse() -> bool { return false; }
+fn returnTrue() -> bool { return true; }
diff --git a/test/compare_output.zig b/test/compare_output.zig
index 1a7b8b51dd..88e25bf40c 100644
--- a/test/compare_output.zig
+++ b/test/compare_output.zig
@@ -10,7 +10,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\}
, "Hello, world!" ++ os.line_sep);
- cases.addCase({
+ cases.addCase(x: {
var tc = cases.create("multiple files with private function",
\\use @import("std").io;
\\use @import("foo.zig");
@@ -41,10 +41,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\}
);
- tc
+ break :x tc;
});
- cases.addCase({
+ cases.addCase(x: {
var tc = cases.create("import segregation",
\\use @import("foo.zig");
\\use @import("bar.zig");
@@ -82,10 +82,10 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\}
);
- tc
+ break :x tc;
});
- cases.addCase({
+ cases.addCase(x: {
var tc = cases.create("two files use import each other",
\\use @import("a.zig");
\\
@@ -112,7 +112,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\pub const b_text = a_text;
);
- tc
+ break :x tc;
});
cases.add("hello world without libc",
@@ -286,11 +286,11 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ const a_int = @ptrCast(&align(1) i32, a ?? unreachable);
\\ const b_int = @ptrCast(&align(1) i32, b ?? unreachable);
\\ if (*a_int < *b_int) {
- \\ -1
+ \\ return -1;
\\ } else if (*a_int > *b_int) {
- \\ 1
+ \\ return 1;
\\ } else {
- \\ c_int(0)
+ \\ return 0;
\\ }
\\}
\\
@@ -342,13 +342,13 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\const Foo = struct {
\\ field1: Bar,
\\
- \\ fn method(a: &const Foo) -> bool { true }
+ \\ fn method(a: &const Foo) -> bool { return true; }
\\};
\\
\\const Bar = struct {
\\ field2: i32,
\\
- \\ fn method(b: &const Bar) -> bool { true }
+ \\ fn method(b: &const Bar) -> bool { return true; }
\\};
\\
\\pub fn main() -> %void {
@@ -429,7 +429,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\fn its_gonna_pass() -> %void { }
, "before\nafter\ndefer3\ndefer1\n");
- cases.addCase({
+ cases.addCase(x: {
var tc = cases.create("@embedFile",
\\const foo_txt = @embedFile("foo.txt");
\\const io = @import("std").io;
@@ -442,6 +442,88 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
tc.addSourceFile("foo.txt", "1234\nabcd\n");
- tc
+ break :x tc;
+ });
+
+ cases.addCase(x: {
+ var tc = cases.create("parsing args",
+ \\const std = @import("std");
+ \\const io = std.io;
+ \\const os = std.os;
+ \\const allocator = std.debug.global_allocator;
+ \\
+ \\pub fn main() -> %void {
+ \\ var args_it = os.args();
+ \\ var stdout_file = %return io.getStdOut();
+ \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
+ \\ const stdout = &stdout_adapter.stream;
+ \\ var index: usize = 0;
+ \\ _ = args_it.skip();
+ \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
+ \\ const arg = %return arg_or_err;
+ \\ %return stdout.print("{}: {}\n", index, arg);
+ \\ }
+ \\}
+ ,
+ \\0: first arg
+ \\1: 'a' 'b' \
+ \\2: bare
+ \\3: ba""re
+ \\4: "
+ \\5: last arg
+ \\
+ );
+
+ tc.setCommandLineArgs([][]const u8 {
+ "first arg",
+ "'a' 'b' \\",
+ "bare",
+ "ba\"\"re",
+ "\"",
+ "last arg",
+ });
+
+ break :x tc;
+ });
+
+ cases.addCase(x: {
+ var tc = cases.create("parsing args new API",
+ \\const std = @import("std");
+ \\const io = std.io;
+ \\const os = std.os;
+ \\const allocator = std.debug.global_allocator;
+ \\
+ \\pub fn main() -> %void {
+ \\ var args_it = os.args();
+ \\ var stdout_file = %return io.getStdOut();
+ \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
+ \\ const stdout = &stdout_adapter.stream;
+ \\ var index: usize = 0;
+ \\ _ = args_it.skip();
+ \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
+ \\ const arg = %return arg_or_err;
+ \\ %return stdout.print("{}: {}\n", index, arg);
+ \\ }
+ \\}
+ ,
+ \\0: first arg
+ \\1: 'a' 'b' \
+ \\2: bare
+ \\3: ba""re
+ \\4: "
+ \\5: last arg
+ \\
+ );
+
+ tc.setCommandLineArgs([][]const u8 {
+ "first arg",
+ "'a' 'b' \\",
+ "bare",
+ "ba\"\"re",
+ "\"",
+ "last arg",
+ });
+
+ break :x tc;
});
}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 5d13ed8d48..60e5c3614d 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -1,6 +1,37 @@
const tests = @import("tests.zig");
pub fn addCases(cases: &tests.CompileErrorContext) {
+ cases.add("labeled break not found",
+ \\export fn entry() {
+ \\ blah: while (true) {
+ \\ while (true) {
+ \\ break :outer;
+ \\ }
+ \\ }
+ \\}
+ , ".tmp_source.zig:4:13: error: label not found: 'outer'");
+
+ cases.add("labeled continue not found",
+ \\export fn entry() {
+ \\ var i: usize = 0;
+ \\ blah: while (i < 10) : (i += 1) {
+ \\ while (true) {
+ \\ continue :outer;
+ \\ }
+ \\ }
+ \\}
+ , ".tmp_source.zig:5:13: error: labeled loop not found: 'outer'");
+
+ cases.add("attempt to use 0 bit type in extern fn",
+ \\extern fn foo(ptr: extern fn(&void));
+ \\
+ \\export fn entry() {
+ \\ foo(bar);
+ \\}
+ \\
+ \\extern fn bar(x: &void) { }
+ , ".tmp_source.zig:7:18: error: parameter of type '&void' has 0 bits; not allowed in function with calling convention 'ccc'");
+
cases.add("implicit semicolon - block statement",
\\export fn entry() {
\\ {}
@@ -8,7 +39,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - block expr",
\\export fn entry() {
@@ -17,7 +48,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - comptime statement",
\\export fn entry() {
@@ -26,7 +57,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ comptime ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - comptime expression",
\\export fn entry() {
@@ -35,7 +66,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = comptime {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - defer",
\\export fn entry() {
@@ -53,7 +84,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ if(true) ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if expression",
\\export fn entry() {
@@ -62,7 +93,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = if(true) {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if-else statement",
\\export fn entry() {
@@ -71,7 +102,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ if(true) ({}) else ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if-else expression",
\\export fn entry() {
@@ -80,7 +111,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = if(true) {} else {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if-else-if statement",
\\export fn entry() {
@@ -89,7 +120,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ if(true) ({}) else if(true) ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if-else-if expression",
\\export fn entry() {
@@ -98,7 +129,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = if(true) {} else if(true) {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if-else-if-else statement",
\\export fn entry() {
@@ -107,7 +138,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ if(true) ({}) else if(true) ({}) else ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - if-else-if-else expression",
\\export fn entry() {
@@ -116,7 +147,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = if(true) {} else if(true) {} else {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - test statement",
\\export fn entry() {
@@ -125,7 +156,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ if (foo()) |_| ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - test expression",
\\export fn entry() {
@@ -134,7 +165,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = if (foo()) |_| {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - while statement",
\\export fn entry() {
@@ -143,7 +174,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ while(true) ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - while expression",
\\export fn entry() {
@@ -152,7 +183,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = while(true) {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - while-continue statement",
\\export fn entry() {
@@ -161,7 +192,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ while(true):({}) ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - while-continue expression",
\\export fn entry() {
@@ -170,7 +201,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = while(true):({}) {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - for statement",
\\export fn entry() {
@@ -179,7 +210,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ for(foo()) ({})
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("implicit semicolon - for expression",
\\export fn entry() {
@@ -188,7 +219,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ _ = for(foo()) {}
\\ var bad = {};
\\}
- , ".tmp_source.zig:5:5: error: invalid token: 'var'");
+ , ".tmp_source.zig:5:5: error: expected token ';', found 'var'");
cases.add("multiple function definitions",
\\fn a() {}
@@ -245,12 +276,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("undeclared identifier",
\\export fn a() {
+ \\ return
\\ b +
- \\ c
+ \\ c;
\\}
,
- ".tmp_source.zig:2:5: error: use of undeclared identifier 'b'",
- ".tmp_source.zig:3:5: error: use of undeclared identifier 'c'");
+ ".tmp_source.zig:3:5: error: use of undeclared identifier 'b'",
+ ".tmp_source.zig:4:5: error: use of undeclared identifier 'c'");
cases.add("parameter redeclaration",
\\fn f(a : i32, a : i32) {
@@ -275,9 +307,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("variable has wrong type",
\\export fn f() -> i32 {
\\ const a = c"a";
- \\ a
+ \\ return a;
\\}
- , ".tmp_source.zig:3:5: error: expected type 'i32', found '&const u8'");
+ , ".tmp_source.zig:3:12: error: expected type 'i32', found '&const u8'");
cases.add("if condition is bool, not int",
\\export fn f() {
@@ -362,23 +394,23 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("missing else clause",
\\fn f(b: bool) {
- \\ const x : i32 = if (b) { 1 };
- \\ const y = if (b) { i32(1) };
+ \\ const x : i32 = if (b) h: { break :h 1; };
+ \\ const y = if (b) h: { break :h i32(1); };
\\}
\\export fn entry() { f(true); }
- , ".tmp_source.zig:2:30: error: integer value 1 cannot be implicitly casted to type 'void'",
+ , ".tmp_source.zig:2:42: error: integer value 1 cannot be implicitly casted to type 'void'",
".tmp_source.zig:3:15: error: incompatible types: 'i32' and 'void'");
cases.add("direct struct loop",
\\const A = struct { a : A, };
- \\export fn entry() -> usize { @sizeOf(A) }
+ \\export fn entry() -> usize { return @sizeOf(A); }
, ".tmp_source.zig:1:11: error: struct 'A' contains itself");
cases.add("indirect struct loop",
\\const A = struct { b : B, };
\\const B = struct { c : C, };
\\const C = struct { a : A, };
- \\export fn entry() -> usize { @sizeOf(A) }
+ \\export fn entry() -> usize { return @sizeOf(A); }
, ".tmp_source.zig:1:11: error: struct 'A' contains itself");
cases.add("invalid struct field",
@@ -476,10 +508,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("cast unreachable",
\\fn f() -> i32 {
- \\ i32(return 1)
+ \\ return i32(return 1);
\\}
\\export fn entry() { _ = f(); }
- , ".tmp_source.zig:2:8: error: unreachable code");
+ , ".tmp_source.zig:2:15: error: unreachable code");
cases.add("invalid builtin fn",
\\fn f() -> @bogus(foo) {
@@ -502,7 +534,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("struct init syntax for array",
\\const foo = []u16{.x = 1024,};
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:1:18: error: type '[]u16' does not support struct initialization syntax");
cases.add("type variables must be constant",
@@ -545,7 +577,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ }
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:8:5: error: enumeration value 'Number.Four' not handled in switch");
cases.add("switch expression - duplicate enumeration prong",
@@ -565,7 +597,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ }
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:13:15: error: duplicate switch value",
".tmp_source.zig:10:15: note: other value is here");
@@ -587,7 +619,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ }
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:13:15: error: duplicate switch value",
".tmp_source.zig:10:15: note: other value is here");
@@ -610,20 +642,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ 0 => {},
\\ }
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
,
".tmp_source.zig:2:5: error: switch must handle all possibilities");
cases.add("switch expression - duplicate or overlapping integer value",
\\fn foo(x: u8) -> u8 {
- \\ switch (x) {
+ \\ return switch (x) {
\\ 0 ... 100 => u8(0),
\\ 101 ... 200 => 1,
\\ 201, 203 ... 207 => 2,
\\ 206 ... 255 => 3,
- \\ }
+ \\ };
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
,
".tmp_source.zig:6:9: error: duplicate switch value",
".tmp_source.zig:5:14: note: previous value is here");
@@ -635,14 +667,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ }
\\}
\\const y: u8 = 100;
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
,
".tmp_source.zig:2:5: error: else prong required when switching on type '&u8'");
cases.add("global variable initializer must be constant expression",
\\extern fn foo() -> i32;
\\const x = foo();
- \\export fn entry() -> i32 { x }
+ \\export fn entry() -> i32 { return x; }
, ".tmp_source.zig:2:11: error: unable to evaluate constant expression");
cases.add("array concatenation with wrong type",
@@ -650,38 +682,38 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const derp = usize(1234);
\\const a = derp ++ "foo";
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(a)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(a)); }
, ".tmp_source.zig:3:11: error: expected array or C string literal, found 'usize'");
cases.add("non compile time array concatenation",
\\fn f() -> []u8 {
- \\ s ++ "foo"
+ \\ return s ++ "foo";
\\}
\\var s: [10]u8 = undefined;
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
- , ".tmp_source.zig:2:5: error: unable to evaluate constant expression");
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
+ , ".tmp_source.zig:2:12: error: unable to evaluate constant expression");
cases.add("@cImport with bogus include",
\\const c = @cImport(@cInclude("bogus.h"));
- \\export fn entry() -> usize { @sizeOf(@typeOf(c.bogo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(c.bogo)); }
, ".tmp_source.zig:1:11: error: C import failed",
".h:1:10: note: 'bogus.h' file not found");
cases.add("address of number literal",
\\const x = 3;
\\const y = &x;
- \\fn foo() -> &const i32 { y }
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
- , ".tmp_source.zig:3:26: error: expected type '&const i32', found '&const (integer literal)'");
+ \\fn foo() -> &const i32 { return y; }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
+ , ".tmp_source.zig:3:33: error: expected type '&const i32', found '&const (integer literal)'");
cases.add("integer overflow error",
\\const x : u8 = 300;
- \\export fn entry() -> usize { @sizeOf(@typeOf(x)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(x)); }
, ".tmp_source.zig:1:16: error: integer value 300 cannot be implicitly casted to type 'u8'");
cases.add("incompatible number literals",
\\const x = 2 == 2.0;
- \\export fn entry() -> usize { @sizeOf(@typeOf(x)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(x)); }
, ".tmp_source.zig:1:11: error: integer value 2 cannot be implicitly casted to type '(float literal)'");
cases.add("missing function call param",
@@ -707,32 +739,32 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ const result = members[index]();
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:20:34: error: expected 1 arguments, found 0");
cases.add("missing function name and param name",
\\fn () {}
\\fn f(i32) {}
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
,
".tmp_source.zig:1:1: error: missing function name",
".tmp_source.zig:2:6: error: missing parameter name");
cases.add("wrong function type",
\\const fns = []fn(){ a, b, c };
- \\fn a() -> i32 {0}
- \\fn b() -> i32 {1}
- \\fn c() -> i32 {2}
- \\export fn entry() -> usize { @sizeOf(@typeOf(fns)) }
+ \\fn a() -> i32 {return 0;}
+ \\fn b() -> i32 {return 1;}
+ \\fn c() -> i32 {return 2;}
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(fns)); }
, ".tmp_source.zig:1:21: error: expected type 'fn()', found 'fn() -> i32'");
cases.add("extern function pointer mismatch",
\\const fns = [](fn(i32)->i32){ a, b, c };
- \\pub fn a(x: i32) -> i32 {x + 0}
- \\pub fn b(x: i32) -> i32 {x + 1}
- \\export fn c(x: i32) -> i32 {x + 2}
+ \\pub fn a(x: i32) -> i32 {return x + 0;}
+ \\pub fn b(x: i32) -> i32 {return x + 1;}
+ \\export fn c(x: i32) -> i32 {return x + 2;}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(fns)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(fns)); }
, ".tmp_source.zig:1:37: error: expected type 'fn(i32) -> i32', found 'extern fn(i32) -> i32'");
@@ -740,14 +772,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const x : f64 = 1.0;
\\const y : f32 = x;
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(y)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(y)); }
, ".tmp_source.zig:2:17: error: expected type 'f32', found 'f64'");
cases.add("colliding invalid top level functions",
\\fn func() -> bogus {}
\\fn func() -> bogus {}
- \\export fn entry() -> usize { @sizeOf(@typeOf(func)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(func)); }
,
".tmp_source.zig:2:1: error: redefinition of 'func'",
".tmp_source.zig:1:14: error: use of undeclared identifier 'bogus'");
@@ -755,7 +787,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("bogus compile var",
\\const x = @import("builtin").bogus;
- \\export fn entry() -> usize { @sizeOf(@typeOf(x)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(x)); }
, ".tmp_source.zig:1:29: error: no member named 'bogus' in '");
@@ -764,11 +796,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ y: [get()]u8,
\\};
\\var global_var: usize = 1;
- \\fn get() -> usize { global_var }
+ \\fn get() -> usize { return global_var; }
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(Foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(Foo)); }
,
- ".tmp_source.zig:5:21: error: unable to evaluate constant expression",
+ ".tmp_source.zig:5:28: error: unable to evaluate constant expression",
".tmp_source.zig:2:12: note: called from here",
".tmp_source.zig:2:8: note: called from here");
@@ -779,7 +811,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\};
\\const x = Foo {.field = 1} + Foo {.field = 2};
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(x)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(x)); }
, ".tmp_source.zig:4:28: error: invalid operands to binary expression: 'Foo' and 'Foo'");
@@ -789,10 +821,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const int_x = u32(1) / u32(0);
\\const float_x = f32(1.0) / f32(0.0);
\\
- \\export fn entry1() -> usize { @sizeOf(@typeOf(lit_int_x)) }
- \\export fn entry2() -> usize { @sizeOf(@typeOf(lit_float_x)) }
- \\export fn entry3() -> usize { @sizeOf(@typeOf(int_x)) }
- \\export fn entry4() -> usize { @sizeOf(@typeOf(float_x)) }
+ \\export fn entry1() -> usize { return @sizeOf(@typeOf(lit_int_x)); }
+ \\export fn entry2() -> usize { return @sizeOf(@typeOf(lit_float_x)); }
+ \\export fn entry3() -> usize { return @sizeOf(@typeOf(int_x)); }
+ \\export fn entry4() -> usize { return @sizeOf(@typeOf(float_x)); }
,
".tmp_source.zig:1:21: error: division by zero is undefined",
".tmp_source.zig:2:25: error: division by zero is undefined",
@@ -804,14 +836,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const foo = "a
\\b";
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:1:13: error: newline not allowed in string literal");
cases.add("invalid comparison for function pointers",
\\fn foo() {}
\\const invalid = foo > foo;
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(invalid)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(invalid)); }
, ".tmp_source.zig:2:21: error: operator not allowed for type 'fn()'");
cases.add("generic function instance with non-constant expression",
@@ -820,33 +852,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ return foo(a, b);
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(test1)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(test1)); }
, ".tmp_source.zig:3:16: error: unable to evaluate constant expression");
- cases.add("goto jumping into block",
- \\export fn f() {
- \\ {
- \\a_label:
- \\ }
- \\ goto a_label;
- \\}
- , ".tmp_source.zig:5:5: error: no label in scope named 'a_label'");
-
- cases.add("goto jumping past a defer",
- \\fn f(b: bool) {
- \\ if (b) goto label;
- \\ defer derp();
- \\label:
- \\}
- \\fn derp(){}
- \\
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
- , ".tmp_source.zig:2:12: error: no label in scope named 'label'");
-
cases.add("assign null to non-nullable pointer",
\\const a: &u8 = null;
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(a)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(a)); }
, ".tmp_source.zig:1:16: error: expected type '&u8', found '(null)'");
cases.add("indexing an array of size zero",
@@ -859,18 +871,18 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("compile time division by zero",
\\const y = foo(0);
\\fn foo(x: u32) -> u32 {
- \\ 1 / x
+ \\ return 1 / x;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(y)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(y)); }
,
- ".tmp_source.zig:3:7: error: division by zero is undefined",
+ ".tmp_source.zig:3:14: error: division by zero is undefined",
".tmp_source.zig:1:14: note: called from here");
cases.add("branch on undefined value",
\\const x = if (undefined) true else false;
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(x)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(x)); }
, ".tmp_source.zig:1:15: error: use of undefined value");
@@ -880,7 +892,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ return fibbonaci(x - 1) + fibbonaci(x - 2);
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(seventh_fib_number)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(seventh_fib_number)); }
,
".tmp_source.zig:3:21: error: evaluation exceeded 1000 backwards branches",
".tmp_source.zig:3:21: note: called from here");
@@ -888,7 +900,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("@embedFile with bogus file",
\\const resource = @embedFile("bogus.txt");
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(resource)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(resource)); }
, ".tmp_source.zig:1:29: error: unable to find '", "bogus.txt'");
cases.add("non-const expression in struct literal outside function",
@@ -898,7 +910,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const a = Foo {.x = get_it()};
\\extern fn get_it() -> i32;
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(a)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(a)); }
, ".tmp_source.zig:4:21: error: unable to evaluate constant expression");
cases.add("non-const expression function call with struct return value outside function",
@@ -908,11 +920,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const a = get_it();
\\fn get_it() -> Foo {
\\ global_side_effect = true;
- \\ Foo {.x = 13}
+ \\ return Foo {.x = 13};
\\}
\\var global_side_effect = false;
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(a)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(a)); }
,
".tmp_source.zig:6:24: error: unable to evaluate constant expression",
".tmp_source.zig:4:17: note: called from here");
@@ -928,21 +940,21 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("illegal comparison of types",
\\fn bad_eql_1(a: []u8, b: []u8) -> bool {
- \\ a == b
+ \\ return a == b;
\\}
\\const EnumWithData = union(enum) {
\\ One: void,
\\ Two: i32,
\\};
\\fn bad_eql_2(a: &const EnumWithData, b: &const EnumWithData) -> bool {
- \\ *a == *b
+ \\ return *a == *b;
\\}
\\
- \\export fn entry1() -> usize { @sizeOf(@typeOf(bad_eql_1)) }
- \\export fn entry2() -> usize { @sizeOf(@typeOf(bad_eql_2)) }
+ \\export fn entry1() -> usize { return @sizeOf(@typeOf(bad_eql_1)); }
+ \\export fn entry2() -> usize { return @sizeOf(@typeOf(bad_eql_2)); }
,
- ".tmp_source.zig:2:7: error: operator not allowed for type '[]u8'",
- ".tmp_source.zig:9:8: error: operator not allowed for type 'EnumWithData'");
+ ".tmp_source.zig:2:14: error: operator not allowed for type '[]u8'",
+ ".tmp_source.zig:9:15: error: operator not allowed for type 'EnumWithData'");
cases.add("non-const switch number literal",
\\export fn foo() {
@@ -953,7 +965,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ };
\\}
\\fn bar() -> i32 {
- \\ 2
+ \\ return 2;
\\}
, ".tmp_source.zig:2:15: error: unable to infer expression type");
@@ -976,56 +988,56 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("negation overflow in function evaluation",
\\const y = neg(-128);
\\fn neg(x: i8) -> i8 {
- \\ -x
+ \\ return -x;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(y)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(y)); }
,
- ".tmp_source.zig:3:5: error: negation caused overflow",
+ ".tmp_source.zig:3:12: error: negation caused overflow",
".tmp_source.zig:1:14: note: called from here");
cases.add("add overflow in function evaluation",
\\const y = add(65530, 10);
\\fn add(a: u16, b: u16) -> u16 {
- \\ a + b
+ \\ return a + b;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(y)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(y)); }
,
- ".tmp_source.zig:3:7: error: operation caused overflow",
+ ".tmp_source.zig:3:14: error: operation caused overflow",
".tmp_source.zig:1:14: note: called from here");
cases.add("sub overflow in function evaluation",
\\const y = sub(10, 20);
\\fn sub(a: u16, b: u16) -> u16 {
- \\ a - b
+ \\ return a - b;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(y)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(y)); }
,
- ".tmp_source.zig:3:7: error: operation caused overflow",
+ ".tmp_source.zig:3:14: error: operation caused overflow",
".tmp_source.zig:1:14: note: called from here");
cases.add("mul overflow in function evaluation",
\\const y = mul(300, 6000);
\\fn mul(a: u16, b: u16) -> u16 {
- \\ a * b
+ \\ return a * b;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(y)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(y)); }
,
- ".tmp_source.zig:3:7: error: operation caused overflow",
+ ".tmp_source.zig:3:14: error: operation caused overflow",
".tmp_source.zig:1:14: note: called from here");
cases.add("truncate sign mismatch",
\\fn f() -> i8 {
\\ const x: u32 = 10;
- \\ @truncate(i8, x)
+ \\ return @truncate(i8, x);
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
- , ".tmp_source.zig:3:19: error: expected signed integer type, found 'u32'");
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
+ , ".tmp_source.zig:3:26: error: expected signed integer type, found 'u32'");
cases.add("%return in function with non error return type",
\\export fn f() {
@@ -1056,16 +1068,16 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("export function with comptime parameter",
\\export fn foo(comptime x: i32, y: i32) -> i32{
- \\ x + y
+ \\ return x + y;
\\}
, ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'");
cases.add("extern function with comptime parameter",
\\extern fn foo(comptime x: i32, y: i32) -> i32;
\\fn f() -> i32 {
- \\ foo(1, 2)
+ \\ return foo(1, 2);
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'");
cases.add("convert fixed size array to slice with invalid size",
@@ -1079,15 +1091,15 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\var a: u32 = 0;
\\pub fn List(comptime T: type) -> type {
\\ a += 1;
- \\ SmallList(T, 8)
+ \\ return SmallList(T, 8);
\\}
\\
\\pub fn SmallList(comptime T: type, comptime STATIC_SIZE: usize) -> type {
- \\ struct {
+ \\ return struct {
\\ items: []T,
\\ length: usize,
\\ prealloc_items: [STATIC_SIZE]T,
- \\ }
+ \\ };
\\}
\\
\\export fn function_with_return_type_type() {
@@ -1102,7 +1114,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\fn f(m: []const u8) {
\\ m.copy(u8, self[0..], m);
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:3:6: error: no member named 'copy' in '[]const u8'");
cases.add("wrong number of arguments for method fn call",
@@ -1113,7 +1125,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\
\\ foo.method(1, 2);
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(f)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(f)); }
, ".tmp_source.zig:6:15: error: expected 2 arguments, found 3");
cases.add("assign through constant pointer",
@@ -1138,7 +1150,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\fn foo(blah: []u8) {
\\ for (blah) { }
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:2:5: error: for loop expression missing element parameter");
cases.add("misspelled type with pointer only reference",
@@ -1171,7 +1183,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ var jd = JsonNode {.kind = JsonType.JSONArray , .jobject = JsonOA.JSONArray {jll} };
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:5:16: error: use of undeclared identifier 'JsonList'");
cases.add("method call with first arg type primitive",
@@ -1179,9 +1191,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ x: i32,
\\
\\ fn init(x: i32) -> Foo {
- \\ Foo {
+ \\ return Foo {
\\ .x = x,
- \\ }
+ \\ };
\\ }
\\};
\\
@@ -1198,10 +1210,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ allocator: &Allocator,
\\
\\ pub fn init(allocator: &Allocator) -> List {
- \\ List {
+ \\ return List {
\\ .len = 0,
\\ .allocator = allocator,
- \\ }
+ \\ };
\\ }
\\};
\\
@@ -1224,10 +1236,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const TINY_QUANTUM_SIZE = 1 << TINY_QUANTUM_SHIFT;
\\var block_aligned_stuff: usize = (4 + TINY_QUANTUM_SIZE) & ~(TINY_QUANTUM_SIZE - 1);
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(block_aligned_stuff)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(block_aligned_stuff)); }
, ".tmp_source.zig:3:60: error: unable to perform binary not operation on type '(integer literal)'");
- cases.addCase({
+ cases.addCase(x: {
const tc = cases.create("multiple files with private function error",
\\const foo = @import("foo.zig");
\\
@@ -1242,14 +1254,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\fn privateFunction() { }
);
- tc
+ break :x tc;
});
cases.add("container init with non-type",
\\const zero: i32 = 0;
\\const a = zero{1};
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(a)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(a)); }
, ".tmp_source.zig:2:11: error: expected type, found 'i32'");
cases.add("assign to constant field",
@@ -1277,22 +1289,22 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ return 0;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(testTrickyDefer)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(testTrickyDefer)); }
, ".tmp_source.zig:4:11: error: cannot return from defer expression");
cases.add("attempt to access var args out of bounds",
\\fn add(args: ...) -> i32 {
- \\ args[0] + args[1]
+ \\ return args[0] + args[1];
\\}
\\
\\fn foo() -> i32 {
- \\ add(i32(1234))
+ \\ return add(i32(1234));
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
,
- ".tmp_source.zig:2:19: error: index 1 outside argument list of size 1",
- ".tmp_source.zig:6:8: note: called from here");
+ ".tmp_source.zig:2:26: error: index 1 outside argument list of size 1",
+ ".tmp_source.zig:6:15: note: called from here");
cases.add("pass integer literal to var args",
\\fn add(args: ...) -> i32 {
@@ -1304,11 +1316,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\}
\\
\\fn bar() -> i32 {
- \\ add(1, 2, 3, 4)
+ \\ return add(1, 2, 3, 4);
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(bar)) }
- , ".tmp_source.zig:10:9: error: parameter of type '(integer literal)' requires comptime");
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(bar)); }
+ , ".tmp_source.zig:10:16: error: parameter of type '(integer literal)' requires comptime");
cases.add("assign too big number to u16",
\\export fn foo() {
@@ -1318,12 +1330,12 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("global variable alignment non power of 2",
\\const some_data: [100]u8 align(3) = undefined;
- \\export fn entry() -> usize { @sizeOf(@typeOf(some_data)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(some_data)); }
, ".tmp_source.zig:1:32: error: alignment value 3 is not a power of 2");
cases.add("function alignment non power of 2",
\\extern fn foo() align(3);
- \\export fn entry() { foo() }
+ \\export fn entry() { return foo(); }
, ".tmp_source.zig:1:23: error: alignment value 3 is not a power of 2");
cases.add("compile log",
@@ -1358,7 +1370,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ return *x;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:8:26: error: expected type '&const u3', found '&align(1:3:6) const u3'");
cases.add("referring to a struct that is invalid",
@@ -1394,14 +1406,14 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ bar();
\\}
- \\fn bar() -> i32 { 0 }
+ \\fn bar() -> i32 { return 0; }
, ".tmp_source.zig:2:8: error: expression value is ignored");
cases.add("ignored assert-err-ok return value",
\\export fn foo() {
\\ %%bar();
\\}
- \\fn bar() -> %i32 { 0 }
+ \\fn bar() -> %i32 { return 0; }
, ".tmp_source.zig:2:5: error: expression value is ignored");
cases.add("ignored statement value",
@@ -1428,11 +1440,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\}
, ".tmp_source.zig:2:12: error: expression value is ignored");
- cases.add("ignored defered statement value",
+ cases.add("ignored defered function call",
\\export fn foo() {
\\ defer bar();
\\}
- \\fn bar() -> %i32 { 0 }
+ \\fn bar() -> %i32 { return 0; }
, ".tmp_source.zig:2:14: error: expression value is ignored");
cases.add("dereference an array",
@@ -1443,7 +1455,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ return (*out)[0..1];
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(pass)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(pass)); }
, ".tmp_source.zig:4:5: error: attempt to dereference non pointer type '[10]u8'");
cases.add("pass const ptr to mutable ptr fn",
@@ -1456,10 +1468,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ return true;
\\}
\\
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:4:19: error: expected type '&[]const u8', found '&const []const u8'");
- cases.addCase({
+ cases.addCase(x: {
const tc = cases.create("export collision",
\\const foo = @import("foo.zig");
\\
@@ -1468,20 +1480,20 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\}
,
"foo.zig:1:8: error: exported symbol collision: 'bar'",
- ".tmp_source.zig:3:8: note: other symbol is here");
+ ".tmp_source.zig:3:8: note: other symbol here");
tc.addSourceFile("foo.zig",
\\export fn bar() {}
\\pub const baz = 1234;
);
- tc
+ break :x tc;
});
cases.add("pass non-copyable type by value to function",
\\const Point = struct { x: i32, y: i32, };
\\fn foo(p: Point) { }
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:2:11: error: type 'Point' is not copyable; cannot pass by value");
cases.add("implicit cast from array to mutable slice",
@@ -1504,7 +1516,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\fn foo(e: error) -> u2 {
\\ return u2(e);
\\}
- \\export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
+ \\export fn entry() -> usize { return @sizeOf(@typeOf(foo)); }
, ".tmp_source.zig:4:14: error: too many error values to fit in 'u2'");
cases.add("asm at compile time",
@@ -1611,23 +1623,29 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
"error: 'main' is private",
".tmp_source.zig:1:1: note: declared here");
- cases.add("@setGlobalSection extern variable",
- \\extern var foo: i32;
- \\comptime {
- \\ @setGlobalSection(foo, ".text2");
+ cases.add("setting a section on an extern variable",
+ \\extern var foo: i32 section(".text2");
+ \\export fn entry() -> i32 {
+ \\ return foo;
\\}
,
- ".tmp_source.zig:3:5: error: cannot set section of external variable 'foo'",
- ".tmp_source.zig:1:8: note: declared here");
+ ".tmp_source.zig:1:29: error: cannot set section of external variable 'foo'");
- cases.add("@setGlobalSection extern fn",
- \\extern fn foo();
- \\comptime {
- \\ @setGlobalSection(foo, ".text2");
+ cases.add("setting a section on a local variable",
+ \\export fn entry() -> i32 {
+ \\ var foo: i32 section(".text2") = 1234;
+ \\ return foo;
\\}
,
- ".tmp_source.zig:3:5: error: cannot set section of external function 'foo'",
- ".tmp_source.zig:1:8: note: declared here");
+ ".tmp_source.zig:2:26: error: cannot set section of local variable 'foo'");
+
+ cases.add("setting a section on an extern fn",
+ \\extern fn foo() section(".text2");
+ \\export fn entry() {
+ \\ foo();
+ \\}
+ ,
+ ".tmp_source.zig:1:25: error: cannot set section of external function 'foo'");
cases.add("returning address of local variable - simple",
\\export fn foo() -> &i32 {
@@ -1648,17 +1666,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("inner struct member shadowing outer struct member",
\\fn A() -> type {
- \\ struct {
+ \\ return struct {
\\ b: B(),
\\
\\ const Self = this;
\\
\\ fn B() -> type {
- \\ struct {
+ \\ return struct {
\\ const Self = this;
- \\ }
+ \\ };
\\ }
- \\ }
+ \\ };
\\}
\\comptime {
\\ assert(A().B().Self != A().Self);
@@ -1674,7 +1692,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ while (bar()) {}
\\}
- \\fn bar() -> ?i32 { 1 }
+ \\fn bar() -> ?i32 { return 1; }
,
".tmp_source.zig:2:15: error: expected type 'bool', found '?i32'");
@@ -1682,7 +1700,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ while (bar()) {}
\\}
- \\fn bar() -> %i32 { 1 }
+ \\fn bar() -> %i32 { return 1; }
,
".tmp_source.zig:2:15: error: expected type 'bool', found '%i32'");
@@ -1690,7 +1708,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ while (bar()) |x| {}
\\}
- \\fn bar() -> bool { true }
+ \\fn bar() -> bool { return true; }
,
".tmp_source.zig:2:15: error: expected nullable type, found 'bool'");
@@ -1698,7 +1716,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ while (bar()) |x| {}
\\}
- \\fn bar() -> %i32 { 1 }
+ \\fn bar() -> %i32 { return 1; }
,
".tmp_source.zig:2:15: error: expected nullable type, found '%i32'");
@@ -1706,7 +1724,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ while (bar()) |x| {} else |err| {}
\\}
- \\fn bar() -> bool { true }
+ \\fn bar() -> bool { return true; }
,
".tmp_source.zig:2:15: error: expected error union type, found 'bool'");
@@ -1714,7 +1732,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\export fn foo() {
\\ while (bar()) |x| {} else |err| {}
\\}
- \\fn bar() -> ?i32 { 1 }
+ \\fn bar() -> ?i32 { return 1; }
,
".tmp_source.zig:2:15: error: expected error union type, found '?i32'");
@@ -1745,17 +1763,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("signed integer division",
\\export fn foo(a: i32, b: i32) -> i32 {
- \\ a / b
+ \\ return a / b;
\\}
,
- ".tmp_source.zig:2:7: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact");
+ ".tmp_source.zig:2:14: error: division with 'i32' and 'i32': signed integers must use @divTrunc, @divFloor, or @divExact");
cases.add("signed integer remainder division",
\\export fn foo(a: i32, b: i32) -> i32 {
- \\ a % b
+ \\ return a % b;
\\}
,
- ".tmp_source.zig:2:7: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod");
+ ".tmp_source.zig:2:14: error: remainder division with 'i32' and 'i32': signed integers and floats must use @rem or @mod");
cases.add("cast negative value to unsigned integer",
\\comptime {
@@ -1838,16 +1856,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:4:13: error: cannot continue out of defer expression");
- cases.add("cannot goto out of defer expression",
- \\export fn foo() {
- \\ defer {
- \\ goto label;
- \\ };
- \\label:
- \\}
- ,
- ".tmp_source.zig:3:9: error: cannot goto out of defer expression");
-
cases.add("calling a var args function only known at runtime",
\\var foos = []fn(...) { foo1, foo2 };
\\
@@ -1915,17 +1923,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("explicit cast float literal to integer when there is a fraction component",
\\export fn entry() -> i32 {
- \\ i32(12.34)
+ \\ return i32(12.34);
\\}
,
- ".tmp_source.zig:2:9: error: fractional component prevents float value 12.340000 from being casted to type 'i32'");
+ ".tmp_source.zig:2:16: error: fractional component prevents float value 12.340000 from being casted to type 'i32'");
cases.add("non pointer given to @ptrToInt",
\\export fn entry(x: i32) -> usize {
- \\ @ptrToInt(x)
+ \\ return @ptrToInt(x);
\\}
,
- ".tmp_source.zig:2:15: error: expected pointer, found 'i32'");
+ ".tmp_source.zig:2:22: error: expected pointer, found 'i32'");
cases.add("@shlExact shifts out 1 bits",
\\comptime {
@@ -2021,7 +2029,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("@alignCast expects pointer or slice",
\\export fn entry() {
- \\ @alignCast(4, u32(3))
+ \\ @alignCast(4, u32(3));
\\}
,
".tmp_source.zig:2:22: error: expected pointer or slice, found 'u32'");
@@ -2033,7 +2041,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\fn testImplicitlyDecreaseFnAlign(ptr: fn () align(8) -> i32, answer: i32) {
\\ if (ptr() != answer) unreachable;
\\}
- \\fn alignedSmall() align(4) -> i32 { 1234 }
+ \\fn alignedSmall() align(4) -> i32 { return 1234; }
,
".tmp_source.zig:2:35: error: expected type 'fn() align(8) -> i32', found 'fn() align(4) -> i32'");
@@ -2120,12 +2128,13 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'");
- cases.add("wrong types given to setGlobalLinkage",
- \\export fn entry() {
- \\ @setGlobalLinkage(entry, u32(1234));
+ cases.add("wrong types given to @export",
+ \\extern fn entry() { }
+ \\comptime {
+ \\ @export("entry", entry, u32(1234));
\\}
,
- ".tmp_source.zig:2:33: error: expected type 'GlobalLinkage', found 'u32'");
+ ".tmp_source.zig:3:32: error: expected type 'GlobalLinkage', found 'u32'");
cases.add("struct with invalid field",
\\const std = @import("std");
@@ -2198,17 +2207,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\const Mode = @import("builtin").Mode;
\\
\\fn Free(comptime filename: []const u8) -> TestCase {
- \\ TestCase {
+ \\ return TestCase {
\\ .filename = filename,
\\ .problem_type = ProblemType.Free,
- \\ }
+ \\ };
\\}
\\
\\fn LibC(comptime filename: []const u8) -> TestCase {
- \\ TestCase {
+ \\ return TestCase {
\\ .filename = filename,
\\ .problem_type = ProblemType.LinkLibC,
- \\ }
+ \\ };
\\}
\\
\\const TestCase = struct {
@@ -2366,9 +2375,9 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\pub fn MemoryPool(comptime T: type) -> type {
\\ const free_list_t = @compileError("aoeu");
\\
- \\ struct {
+ \\ return struct {
\\ free_list: free_list_t,
- \\ }
+ \\ };
\\}
\\
\\export fn entry() {
@@ -2643,7 +2652,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ C: bool,
\\};
\\export fn entry() {
- \\ var a = Payload { .A = { 1234 } };
+ \\ var a = Payload { .A = 1234 };
\\}
,
".tmp_source.zig:6:29: error: extern union does not support enum tag type");
@@ -2660,7 +2669,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ C: bool,
\\};
\\export fn entry() {
- \\ var a = Payload { .A = { 1234 } };
+ \\ var a = Payload { .A = 1234 };
\\}
,
".tmp_source.zig:6:29: error: packed union does not support enum tag type");
@@ -2672,7 +2681,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
\\ C: bool,
\\};
\\export fn entry() {
- \\ const a = Payload { .A = { 1234 } };
+ \\ const a = Payload { .A = 1234 };
\\ foo(a);
\\}
\\fn foo(a: &const Payload) {
@@ -2684,4 +2693,47 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
,
".tmp_source.zig:11:13: error: switch on union which has no attached enum",
".tmp_source.zig:1:17: note: consider 'union(enum)' here");
+
+ cases.add("enum in field count range but not matching tag",
+ \\const Foo = enum(u32) {
+ \\ A = 10,
+ \\ B = 11,
+ \\};
+ \\export fn entry() {
+ \\ var x = Foo(0);
+ \\}
+ ,
+ ".tmp_source.zig:6:16: error: enum 'Foo' has no tag matching integer value 0",
+ ".tmp_source.zig:1:13: note: 'Foo' declared here");
+
+ cases.add("comptime cast enum to union but field has payload",
+ \\const Letter = enum { A, B, C };
+ \\const Value = union(Letter) {
+ \\ A: i32,
+ \\ B,
+ \\ C,
+ \\};
+ \\export fn entry() {
+ \\ var x: Value = Letter.A;
+ \\}
+ ,
+ ".tmp_source.zig:8:26: error: cast to union 'Value' must initialize 'i32' field 'A'",
+ ".tmp_source.zig:3:5: note: field 'A' declared here");
+
+ cases.add("runtime cast to union which has non-void fields",
+ \\const Letter = enum { A, B, C };
+ \\const Value = union(Letter) {
+ \\ A: i32,
+ \\ B,
+ \\ C,
+ \\};
+ \\export fn entry() {
+ \\ foo(Letter.A);
+ \\}
+ \\fn foo(l: Letter) {
+ \\ var x: Value = l;
+ \\}
+ ,
+ ".tmp_source.zig:11:20: error: runtime cast to union 'Value' which has non-void fields",
+ ".tmp_source.zig:3:5: note: field 'A' has type 'i32'");
}
diff --git a/test/debug_safety.zig b/test/debug_safety.zig
index 36f8d020c3..58ebf838b0 100644
--- a/test/debug_safety.zig
+++ b/test/debug_safety.zig
@@ -19,7 +19,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ baz(bar(a));
\\}
\\fn bar(a: []const i32) -> i32 {
- \\ a[4]
+ \\ return a[4];
\\}
\\fn baz(a: i32) { }
);
@@ -34,7 +34,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn add(a: u16, b: u16) -> u16 {
- \\ a + b
+ \\ return a + b;
\\}
);
@@ -48,7 +48,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn sub(a: u16, b: u16) -> u16 {
- \\ a - b
+ \\ return a - b;
\\}
);
@@ -62,7 +62,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn mul(a: u16, b: u16) -> u16 {
- \\ a * b
+ \\ return a * b;
\\}
);
@@ -76,7 +76,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 32767) return error.Whatever;
\\}
\\fn neg(a: i16) -> i16 {
- \\ -a
+ \\ return -a;
\\}
);
@@ -90,7 +90,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 32767) return error.Whatever;
\\}
\\fn div(a: i16, b: i16) -> i16 {
- \\ @divTrunc(a, b)
+ \\ return @divTrunc(a, b);
\\}
);
@@ -104,7 +104,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn shl(a: i16, b: u4) -> i16 {
- \\ @shlExact(a, b)
+ \\ return @shlExact(a, b);
\\}
);
@@ -118,7 +118,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn shl(a: u16, b: u4) -> u16 {
- \\ @shlExact(a, b)
+ \\ return @shlExact(a, b);
\\}
);
@@ -132,7 +132,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn shr(a: i16, b: u4) -> i16 {
- \\ @shrExact(a, b)
+ \\ return @shrExact(a, b);
\\}
);
@@ -146,7 +146,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn shr(a: u16, b: u4) -> u16 {
- \\ @shrExact(a, b)
+ \\ return @shrExact(a, b);
\\}
);
@@ -159,7 +159,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ const x = div0(999, 0);
\\}
\\fn div0(a: i32, b: i32) -> i32 {
- \\ @divTrunc(a, b)
+ \\ return @divTrunc(a, b);
\\}
);
@@ -173,7 +173,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn divExact(a: i32, b: i32) -> i32 {
- \\ @divExact(a, b)
+ \\ return @divExact(a, b);
\\}
);
@@ -187,7 +187,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x.len == 0) return error.Whatever;
\\}
\\fn widenSlice(slice: []align(1) const u8) -> []align(1) const i32 {
- \\ ([]align(1) const i32)(slice)
+ \\ return ([]align(1) const i32)(slice);
\\}
);
@@ -201,7 +201,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn shorten_cast(x: i32) -> i8 {
- \\ i8(x)
+ \\ return i8(x);
\\}
);
@@ -215,7 +215,7 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
\\ if (x == 0) return error.Whatever;
\\}
\\fn unsigned_cast(x: i32) -> u32 {
- \\ u32(x)
+ \\ return u32(x);
\\}
);
diff --git a/test/standalone/pkg_import/pkg.zig b/test/standalone/pkg_import/pkg.zig
index a051497fdb..52ca884937 100644
--- a/test/standalone/pkg_import/pkg.zig
+++ b/test/standalone/pkg_import/pkg.zig
@@ -1 +1 @@
-pub fn add(a: i32, b: i32) -> i32 { a + b }
+pub fn add(a: i32, b: i32) -> i32 { return a + b; }
diff --git a/test/tests.zig b/test/tests.zig
index e74afa1755..a184045125 100644
--- a/test/tests.zig
+++ b/test/tests.zig
@@ -189,6 +189,7 @@ pub const CompareOutputContext = struct {
expected_output: []const u8,
link_libc: bool,
special: Special,
+ cli_args: []const []const u8,
const SourceFile = struct {
filename: []const u8,
@@ -201,6 +202,10 @@ pub const CompareOutputContext = struct {
.source = source,
});
}
+
+ pub fn setCommandLineArgs(self: &TestCase, args: []const []const u8) {
+ self.cli_args = args;
+ }
};
const RunCompareOutputStep = struct {
@@ -210,9 +215,11 @@ pub const CompareOutputContext = struct {
name: []const u8,
expected_output: []const u8,
test_index: usize,
+ cli_args: []const []const u8,
pub fn create(context: &CompareOutputContext, exe_path: []const u8,
- name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
+ name: []const u8, expected_output: []const u8,
+ cli_args: []const []const u8) -> &RunCompareOutputStep
{
const allocator = context.b.allocator;
const ptr = %%allocator.create(RunCompareOutputStep);
@@ -223,6 +230,7 @@ pub const CompareOutputContext = struct {
.expected_output = expected_output,
.test_index = context.test_index,
.step = build.Step.init("RunCompareOutput", allocator, make),
+ .cli_args = cli_args,
};
context.test_index += 1;
return ptr;
@@ -233,10 +241,17 @@ pub const CompareOutputContext = struct {
const b = self.context.b;
const full_exe_path = b.pathFromRoot(self.exe_path);
+ var args = ArrayList([]const u8).init(b.allocator);
+ defer args.deinit();
+
+ %%args.append(full_exe_path);
+ for (self.cli_args) |arg| {
+ %%args.append(arg);
+ }
warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
- const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
+ const child = %%os.ChildProcess.init(args.toSliceConst(), b.allocator);
defer child.deinit();
child.stdin_behavior = StdIo.Ignore;
@@ -269,7 +284,7 @@ pub const CompareOutputContext = struct {
warn("Process {} terminated unexpectedly\n", full_exe_path);
return error.TestFailed;
},
- };
+ }
if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
@@ -364,6 +379,7 @@ pub const CompareOutputContext = struct {
.expected_output = expected_output,
.link_libc = false,
.special = special,
+ .cli_args = []const []const u8{},
};
const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";
tc.addSourceFile(root_src_name, source);
@@ -420,7 +436,7 @@ pub const CompareOutputContext = struct {
}
const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(), annotated_case_name,
- case.expected_output);
+ case.expected_output, case.cli_args);
run_and_cmp_output.step.dependOn(&exe.step);
self.step.dependOn(&run_and_cmp_output.step);
@@ -447,7 +463,7 @@ pub const CompareOutputContext = struct {
}
const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(),
- annotated_case_name, case.expected_output);
+ annotated_case_name, case.expected_output, case.cli_args);
run_and_cmp_output.step.dependOn(&exe.step);
self.step.dependOn(&run_and_cmp_output.step);
@@ -599,7 +615,7 @@ pub const CompileErrorContext = struct {
warn("Process {} terminated unexpectedly\n", b.zig_exe);
return error.TestFailed;
},
- };
+ }
const stdout = stdout_buf.toSliceConst();
@@ -875,7 +891,7 @@ pub const TranslateCContext = struct {
warn("Compilation terminated unexpectedly\n");
return error.TestFailed;
},
- };
+ }
const stdout = stdout_buf.toSliceConst();
const stderr = stderr_buf.toSliceConst();
diff --git a/test/translate_c.zig b/test/translate_c.zig
index d4974109da..cdf900c8b2 100644
--- a/test/translate_c.zig
+++ b/test/translate_c.zig
@@ -203,13 +203,13 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\pub extern var fn_ptr: ?extern fn();
,
\\pub inline fn foo() {
- \\ (??fn_ptr)()
+ \\ return (??fn_ptr)();
\\}
,
\\pub extern var fn_ptr2: ?extern fn(c_int, f32) -> u8;
,
\\pub inline fn bar(arg0: c_int, arg1: f32) -> u8 {
- \\ (??fn_ptr2)(arg0, arg1)
+ \\ return (??fn_ptr2)(arg0, arg1);
\\}
);
@@ -325,12 +325,12 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return a;
\\}
,
- \\export fn foo1(_arg_a: c_uint) -> c_uint {
+ \\pub export fn foo1(_arg_a: c_uint) -> c_uint {
\\ var a = _arg_a;
\\ a +%= 1;
\\ return a;
\\}
- \\export fn foo2(_arg_a: c_int) -> c_int {
+ \\pub export fn foo2(_arg_a: c_int) -> c_int {
\\ var a = _arg_a;
\\ a += 1;
\\ return a;
@@ -346,7 +346,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return i;
\\}
,
- \\export fn log2(_arg_a: c_uint) -> c_int {
+ \\pub export fn log2(_arg_a: c_uint) -> c_int {
\\ var a = _arg_a;
\\ var i: c_int = 0;
\\ while (a > c_uint(0)) {
@@ -367,7 +367,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return a;
\\}
,
- \\export fn max(a: c_int, b: c_int) -> c_int {
+ \\pub export fn max(a: c_int, b: c_int) -> c_int {
\\ if (a < b) return b;
\\ if (a < b) return b else return a;
\\}
@@ -382,7 +382,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return a;
\\}
,
- \\export fn max(a: c_int, b: c_int) -> c_int {
+ \\pub export fn max(a: c_int, b: c_int) -> c_int {
\\ if (a == b) return a;
\\ if (a != b) return b;
\\ return a;
@@ -407,7 +407,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ c = a % b;
\\}
,
- \\export fn s(a: c_int, b: c_int) -> c_int {
+ \\pub export fn s(a: c_int, b: c_int) -> c_int {
\\ var c: c_int;
\\ c = (a + b);
\\ c = (a - b);
@@ -415,7 +415,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ c = @divTrunc(a, b);
\\ c = @rem(a, b);
\\}
- \\export fn u(a: c_uint, b: c_uint) -> c_uint {
+ \\pub export fn u(a: c_uint, b: c_uint) -> c_uint {
\\ var c: c_uint;
\\ c = (a +% b);
\\ c = (a -% b);
@@ -430,7 +430,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return (a & b) ^ (a | b);
\\}
,
- \\export fn max(a: c_int, b: c_int) -> c_int {
+ \\pub export fn max(a: c_int, b: c_int) -> c_int {
\\ return (a & b) ^ (a | b);
\\}
);
@@ -444,7 +444,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return a;
\\}
,
- \\export fn max(a: c_int, b: c_int) -> c_int {
+ \\pub export fn max(a: c_int, b: c_int) -> c_int {
\\ if ((a < b) or (a == b)) return b;
\\ if ((a >= b) and (a == b)) return a;
\\ return a;
@@ -458,7 +458,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ a = tmp;
\\}
,
- \\export fn max(_arg_a: c_int) -> c_int {
+ \\pub export fn max(_arg_a: c_int) -> c_int {
\\ var a = _arg_a;
\\ var tmp: c_int;
\\ tmp = a;
@@ -472,13 +472,13 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ c = b = a;
\\}
,
- \\export fn max(a: c_int) {
+ \\pub export fn max(a: c_int) {
\\ var b: c_int;
\\ var c: c_int;
- \\ c = {
+ \\ c = x: {
\\ const _tmp = a;
\\ b = _tmp;
- \\ _tmp
+ \\ break :x _tmp;
\\ };
\\}
);
@@ -493,7 +493,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return i;
\\}
,
- \\export fn log2(_arg_a: u32) -> c_int {
+ \\pub export fn log2(_arg_a: u32) -> c_int {
\\ var a = _arg_a;
\\ var i: c_int = 0;
\\ while (a > c_uint(0)) {
@@ -518,7 +518,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\void foo(void) { bar(); }
,
\\pub fn bar() {}
- \\export fn foo() {
+ \\pub export fn foo() {
\\ bar();
\\}
);
@@ -534,7 +534,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\pub const struct_Foo = extern struct {
\\ field: c_int,
\\};
- \\export fn read_field(foo: ?&struct_Foo) -> c_int {
+ \\pub export fn read_field(foo: ?&struct_Foo) -> c_int {
\\ return (??foo).field;
\\}
);
@@ -544,7 +544,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ ;;;;;
\\}
,
- \\export fn foo() {}
+ \\pub export fn foo() {}
);
cases.add("undefined array global",
@@ -560,7 +560,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\}
,
\\pub var array: [100]c_int = undefined;
- \\export fn foo(index: c_int) -> c_int {
+ \\pub export fn foo(index: c_int) -> c_int {
\\ return array[index];
\\}
);
@@ -571,7 +571,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return (int)a;
\\}
,
- \\export fn float_to_int(a: f32) -> c_int {
+ \\pub export fn float_to_int(a: f32) -> c_int {
\\ return c_int(a);
\\}
);
@@ -581,7 +581,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return x;
\\}
,
- \\export fn foo(x: ?&c_ushort) -> ?&c_void {
+ \\pub export fn foo(x: ?&c_ushort) -> ?&c_void {
\\ return @ptrCast(?&c_void, x);
\\}
);
@@ -592,7 +592,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return sizeof(int);
\\}
,
- \\export fn size_of() -> usize {
+ \\pub export fn size_of() -> usize {
\\ return @sizeOf(c_int);
\\}
);
@@ -602,7 +602,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return 0;
\\}
,
- \\export fn foo() -> ?&c_int {
+ \\pub export fn foo() -> ?&c_int {
\\ return null;
\\}
);
@@ -612,10 +612,10 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return 1, 2;
\\}
,
- \\export fn foo() -> c_int {
- \\ return {
+ \\pub export fn foo() -> c_int {
+ \\ return x: {
\\ _ = 1;
- \\ 2
+ \\ break :x 2;
\\ };
\\}
);
@@ -625,7 +625,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ return (1 << 2) >> 1;
\\}
,
- \\export fn foo() -> c_int {
+ \\pub export fn foo() -> c_int {
\\ return (1 << @import("std").math.Log2Int(c_int)(2)) >> @import("std").math.Log2Int(c_int)(1);
\\}
);
@@ -643,47 +643,47 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ a <<= (a <<= 1);
\\}
,
- \\export fn foo() {
+ \\pub export fn foo() {
\\ var a: c_int = 0;
- \\ a += {
+ \\ a += x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) + 1);
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a -= {
+ \\ a -= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) - 1);
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a *= {
+ \\ a *= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) * 1);
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a &= {
+ \\ a &= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) & 1);
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a |= {
+ \\ a |= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) | 1);
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a ^= {
+ \\ a ^= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) ^ 1);
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a >>= @import("std").math.Log2Int(c_int)({
+ \\ a >>= @import("std").math.Log2Int(c_int)(x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_int)(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ });
- \\ a <<= @import("std").math.Log2Int(c_int)({
+ \\ a <<= @import("std").math.Log2Int(c_int)(x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_int)(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ });
\\}
);
@@ -701,47 +701,47 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ a <<= (a <<= 1);
\\}
,
- \\export fn foo() {
+ \\pub export fn foo() {
\\ var a: c_uint = c_uint(0);
- \\ a +%= {
+ \\ a +%= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) +% c_uint(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a -%= {
+ \\ a -%= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) -% c_uint(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a *%= {
+ \\ a *%= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) *% c_uint(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a &= {
+ \\ a &= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) & c_uint(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a |= {
+ \\ a |= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) | c_uint(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a ^= {
+ \\ a ^= x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) ^ c_uint(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ a >>= @import("std").math.Log2Int(c_uint)({
+ \\ a >>= @import("std").math.Log2Int(c_uint)(x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) >> @import("std").math.Log2Int(c_uint)(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ });
- \\ a <<= @import("std").math.Log2Int(c_uint)({
+ \\ a <<= @import("std").math.Log2Int(c_uint)(x: {
\\ const _ref = &a;
\\ (*_ref) = ((*_ref) << @import("std").math.Log2Int(c_uint)(1));
- \\ *_ref
+ \\ break :x *_ref;
\\ });
\\}
);
@@ -771,36 +771,36 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ u = u--;
\\}
,
- \\export fn foo() {
+ \\pub export fn foo() {
\\ var i: c_int = 0;
\\ var u: c_uint = c_uint(0);
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
\\ u -%= 1;
- \\ i = {
+ \\ i = x: {
\\ const _ref = &i;
\\ const _tmp = *_ref;
\\ (*_ref) += 1;
- \\ _tmp
+ \\ break :x _tmp;
\\ };
- \\ i = {
+ \\ i = x: {
\\ const _ref = &i;
\\ const _tmp = *_ref;
\\ (*_ref) -= 1;
- \\ _tmp
+ \\ break :x _tmp;
\\ };
- \\ u = {
+ \\ u = x: {
\\ const _ref = &u;
\\ const _tmp = *_ref;
\\ (*_ref) +%= 1;
- \\ _tmp
+ \\ break :x _tmp;
\\ };
- \\ u = {
+ \\ u = x: {
\\ const _ref = &u;
\\ const _tmp = *_ref;
\\ (*_ref) -%= 1;
- \\ _tmp
+ \\ break :x _tmp;
\\ };
\\}
);
@@ -819,32 +819,32 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ u = --u;
\\}
,
- \\export fn foo() {
+ \\pub export fn foo() {
\\ var i: c_int = 0;
\\ var u: c_uint = c_uint(0);
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
\\ u -%= 1;
- \\ i = {
+ \\ i = x: {
\\ const _ref = &i;
\\ (*_ref) += 1;
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ i = {
+ \\ i = x: {
\\ const _ref = &i;
\\ (*_ref) -= 1;
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ u = {
+ \\ u = x: {
\\ const _ref = &u;
\\ (*_ref) +%= 1;
- \\ *_ref
+ \\ break :x *_ref;
\\ };
- \\ u = {
+ \\ u = x: {
\\ const _ref = &u;
\\ (*_ref) -%= 1;
- \\ *_ref
+ \\ break :x *_ref;
\\ };
\\}
);
@@ -862,7 +862,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ while (b != 0);
\\}
,
- \\export fn foo() {
+ \\pub export fn foo() {
\\ var a: c_int = 2;
\\ while (true) {
\\ a -= 1;
@@ -886,9 +886,9 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ baz();
\\}
,
- \\export fn foo() {}
- \\export fn baz() {}
- \\export fn bar() {
+ \\pub export fn foo() {}
+ \\pub export fn baz() {}
+ \\pub export fn bar() {
\\ var f: ?extern fn() = foo;
\\ (??f)();
\\ (??f)();
@@ -901,8 +901,8 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ *x = 1;
\\}
,
- \\export fn foo(x: ?&c_int) {
- \\ (*(??x)) = 1;
+ \\pub export fn foo(x: ?&c_int) {
+ \\ (*??x) = 1;
\\}
);
@@ -930,7 +930,7 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\pub fn foo() -> c_int {
\\ var x: c_int = 1234;
\\ var ptr: ?&c_int = &x;
- \\ return *(??ptr);
+ \\ return *??ptr;
\\}
);
@@ -1005,48 +1005,6 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\}
);
- cases.add("switch statement",
- \\int foo(int x) {
- \\ switch (x) {
- \\ case 1:
- \\ x += 1;
- \\ case 2:
- \\ break;
- \\ case 3:
- \\ case 4:
- \\ return x + 1;
- \\ default:
- \\ return 10;
- \\ }
- \\ return x + 13;
- \\}
- ,
- \\fn foo(_arg_x: c_int) -> c_int {
- \\ var x = _arg_x;
- \\ {
- \\ switch (x) {
- \\ 1 => goto case_0,
- \\ 2 => goto case_1,
- \\ 3 => goto case_2,
- \\ 4 => goto case_3,
- \\ else => goto default,
- \\ };
- \\ case_0:
- \\ x += 1;
- \\ case_1:
- \\ goto end;
- \\ case_2:
- \\ case_3:
- \\ return x + 1;
- \\ default:
- \\ return 10;
- \\ goto end;
- \\ end:
- \\ };
- \\ return x + 13;
- \\}
- );
-
cases.add("macros with field targets",
\\typedef unsigned int GLbitfield;
\\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
@@ -1079,50 +1037,12 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\pub const glClearPFN = PFNGLCLEARPROC;
,
\\pub inline fn glClearUnion(arg0: GLbitfield) {
- \\ (??glProcs.gl.Clear)(arg0)
+ \\ return (??glProcs.gl.Clear)(arg0);
\\}
,
\\pub const OpenGLProcs = union_OpenGLProcs;
);
- cases.add("switch statement with no default",
- \\int foo(int x) {
- \\ switch (x) {
- \\ case 1:
- \\ x += 1;
- \\ case 2:
- \\ break;
- \\ case 3:
- \\ case 4:
- \\ return x + 1;
- \\ }
- \\ return x + 13;
- \\}
- ,
- \\fn foo(_arg_x: c_int) -> c_int {
- \\ var x = _arg_x;
- \\ {
- \\ switch (x) {
- \\ 1 => goto case_0,
- \\ 2 => goto case_1,
- \\ 3 => goto case_2,
- \\ 4 => goto case_3,
- \\ else => goto end,
- \\ };
- \\ case_0:
- \\ x += 1;
- \\ case_1:
- \\ goto end;
- \\ case_2:
- \\ case_3:
- \\ return x + 1;
- \\ goto end;
- \\ end:
- \\ };
- \\ return x + 13;
- \\}
- );
-
cases.add("variable name shadowing",
\\int foo(void) {
\\ int x = 1;
@@ -1188,4 +1108,10 @@ pub fn addCases(cases: &tests.TranslateCContext) {
\\ const v2: &const u8 = c"2.2.2";
\\}
);
+
+ cases.add("macro pointer cast",
+ \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
+ ,
+ \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE);
+ );
}