From cc6ac779139434d4f1cc342d36fd2ef5f120ded5 Mon Sep 17 00:00:00 2001
From: Tesla Ice Zhang
Date: Sat, 17 Mar 2018 23:17:07 +0800
Subject: [PATCH 001/132] Fix some explicit errors
---
doc/langref.html.in | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 55d3acc59..fca0a342a 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -5733,7 +5733,7 @@ VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("alig
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
-ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression ","
+ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression)) ","
UseDecl = "use" Expression ";"
@@ -5833,7 +5833,7 @@ BinaryAndExpression = BitShiftExpression "&" BinaryAndExpression | BitShiftE
BitShiftExpression = AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression
-BitShiftOperator = "<<" | ">>" | "<<"
+BitShiftOperator = "<<" | ">>"
AdditionExpression = MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
From 4a921b2eba294518c19b7ad71f0b3bd9a4c6522b Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 17 Mar 2018 18:39:58 +0100
Subject: [PATCH 002/132] add CLANG_LIBDIRS cmake build variable
Mirrors LLVM_LIBDIRS, tells cmake where to look for libclang libraries.
---
cmake/Findclang.cmake | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/cmake/Findclang.cmake b/cmake/Findclang.cmake
index 26617d36f..b42abd10a 100644
--- a/cmake/Findclang.cmake
+++ b/cmake/Findclang.cmake
@@ -5,6 +5,7 @@
# CLANG_FOUND
# CLANG_INCLUDE_DIRS
# CLANG_LIBRARIES
+# CLANG_LIBDIRS
if(MSVC)
find_package(CLANG REQUIRED CONFIG)
@@ -34,6 +35,7 @@ else()
string(TOUPPER ${_libname_} _prettylibname_)
find_library(CLANG_${_prettylibname_}_LIB NAMES ${_libname_}
PATHS
+ ${CLANG_LIBDIRS}
/usr/lib/llvm/6/lib
/usr/lib/llvm-6.0/lib
/mingw64/lib
@@ -60,4 +62,4 @@ endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CLANG DEFAULT_MSG CLANG_LIBRARIES CLANG_INCLUDE_DIRS)
-mark_as_advanced(CLANG_INCLUDE_DIRS CLANG_LIBRARIES)
+mark_as_advanced(CLANG_INCLUDE_DIRS CLANG_LIBRARIES CLANG_LIBDIRS)
From c2c34c09b9afd8a5370dce771d5e98af5b3303a1 Mon Sep 17 00:00:00 2001
From: Ben Noordhuis
Date: Sat, 17 Mar 2018 18:39:58 +0100
Subject: [PATCH 003/132] add LLVM_LIBDIRS to link directories
This seems to be the only way to get the zig build to link against llvm
libraries in a non-standard location.
---
cmake/Findllvm.cmake | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake
index d0a4a3cba..296e0a91f 100644
--- a/cmake/Findllvm.cmake
+++ b/cmake/Findllvm.cmake
@@ -66,7 +66,7 @@ if(NOT LLVM_LIBRARIES)
endif()
link_directories("${CMAKE_PREFIX_PATH}/lib")
-
+link_directories("${LLVM_LIBDIRS}")
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LLVM DEFAULT_MSG LLVM_LIBRARIES LLVM_INCLUDE_DIRS)
From bea9e9c7f8abc1e270dd2ea0065934b32627288c Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Wed, 14 Mar 2018 22:19:51 +1300
Subject: [PATCH 004/132] Add addIncludeDir to TestStep + fix build template
Closes #794.
---
std/build.zig | 11 +++++++++++
std/special/build_file_template.zig | 2 +-
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/std/build.zig b/std/build.zig
index e6b667626..a4d745e45 100644
--- a/std/build.zig
+++ b/std/build.zig
@@ -1627,6 +1627,7 @@ pub const TestStep = struct {
filter: ?[]const u8,
target: Target,
exec_cmd_args: ?[]const ?[]const u8,
+ include_dirs: ArrayList([]const u8),
pub fn init(builder: &Builder, root_src: []const u8) TestStep {
const step_name = builder.fmt("test {}", root_src);
@@ -1641,6 +1642,7 @@ pub const TestStep = struct {
.link_libs = BufSet.init(builder.allocator),
.target = Target { .Native = {} },
.exec_cmd_args = null,
+ .include_dirs = ArrayList([]const u8).init(builder.allocator),
};
}
@@ -1648,6 +1650,10 @@ pub const TestStep = struct {
self.verbose = value;
}
+ pub fn addIncludeDir(self: &TestStep, path: []const u8) void {
+ self.include_dirs.append(path) catch unreachable;
+ }
+
pub fn setBuildMode(self: &TestStep, mode: builtin.Mode) void {
self.build_mode = mode;
}
@@ -1746,6 +1752,11 @@ pub const TestStep = struct {
}
}
+ for (self.include_dirs.toSliceConst()) |include_path| {
+ try zig_args.append("-isystem");
+ try zig_args.append(builder.pathFromRoot(include_path));
+ }
+
for (builder.include_paths.toSliceConst()) |include_path| {
try zig_args.append("-isystem");
try zig_args.append(builder.pathFromRoot(include_path));
diff --git a/std/special/build_file_template.zig b/std/special/build_file_template.zig
index 2edfdadf5..1c06c93cd 100644
--- a/std/special/build_file_template.zig
+++ b/std/special/build_file_template.zig
@@ -1,6 +1,6 @@
const Builder = @import("std").build.Builder;
-pub fn build(b: &Builder) !void {
+pub fn build(b: &Builder) void {
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("YOUR_NAME_HERE", "src/main.zig");
exe.setBuildMode(mode);
From d959faa4c78ded05ac457e93a16edfd1d7c58c0e Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 17 Mar 2018 18:19:23 -0400
Subject: [PATCH 005/132] add test for addIncludeDir for test step
---
test/build_examples.zig | 1 +
test/standalone/issue_794/a_directory/foo.h | 1 +
test/standalone/issue_794/build.zig | 11 +++++++++++
test/standalone/issue_794/main.zig | 7 +++++++
4 files changed, 20 insertions(+)
create mode 100644 test/standalone/issue_794/a_directory/foo.h
create mode 100644 test/standalone/issue_794/build.zig
create mode 100644 test/standalone/issue_794/main.zig
diff --git a/test/build_examples.zig b/test/build_examples.zig
index b0d939006..a3b44b913 100644
--- a/test/build_examples.zig
+++ b/test/build_examples.zig
@@ -14,6 +14,7 @@ pub fn addCases(cases: &tests.BuildExamplesContext) void {
cases.addBuildFile("example/mix_o_files/build.zig");
}
cases.addBuildFile("test/standalone/issue_339/build.zig");
+ cases.addBuildFile("test/standalone/issue_794/build.zig");
cases.addBuildFile("test/standalone/pkg_import/build.zig");
cases.addBuildFile("test/standalone/use_alias/build.zig");
cases.addBuildFile("test/standalone/brace_expansion/build.zig");
diff --git a/test/standalone/issue_794/a_directory/foo.h b/test/standalone/issue_794/a_directory/foo.h
new file mode 100644
index 000000000..7598ed89f
--- /dev/null
+++ b/test/standalone/issue_794/a_directory/foo.h
@@ -0,0 +1 @@
+#define NUMBER 1234
diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig
new file mode 100644
index 000000000..4f5dcd7ff
--- /dev/null
+++ b/test/standalone/issue_794/build.zig
@@ -0,0 +1,11 @@
+const Builder = @import("std").build.Builder;
+
+pub fn build(b: &Builder) void {
+ const test_artifact = b.addTest("main.zig");
+ test_artifact.addIncludeDir("a_directory");
+
+ b.default_step.dependOn(&test_artifact.step);
+
+ const test_step = b.step("test", "Test the program");
+ test_step.dependOn(&test_artifact.step);
+}
diff --git a/test/standalone/issue_794/main.zig b/test/standalone/issue_794/main.zig
new file mode 100644
index 000000000..356a10641
--- /dev/null
+++ b/test/standalone/issue_794/main.zig
@@ -0,0 +1,7 @@
+const c = @cImport(@cInclude("foo.h"));
+const std = @import("std");
+const assert = std.debug.assert;
+
+test "c import" {
+ comptime assert(c.NUMBER == 1234);
+}
From c4544df011cf61a61538bdeb18808f67ff71043e Mon Sep 17 00:00:00 2001
From: Tesla Ice Zhang
Date: Tue, 20 Mar 2018 03:00:11 +0800
Subject: [PATCH 006/132] Remove unnecessary rule and re-fix an old bug
The "old bug" is cause my last pr. I'm fixing it now.
---
doc/langref.html.in | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index fca0a342a..5165a7bb9 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -5733,7 +5733,7 @@ VariableDeclaration = ("var" | "const") Symbol option(":" TypeExpr) option("alig
ContainerMember = (ContainerField | FnDef | GlobalVarDecl)
-ContainerField = Symbol option(":" PrefixOpExpression option("=" PrefixOpExpression)) ","
+ContainerField = Symbol option(":" PrefixOpExpression) option("=" PrefixOpExpression) ","
UseDecl = "use" Expression ";"
@@ -5751,9 +5751,7 @@ Block = option(Symbol ":") "{" many(Statement) "}"
Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";"
-TypeExpr = ErrorSetExpr
-
-ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
+TypeExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression
BlockOrExpression = Block | Expression
@@ -5845,7 +5843,7 @@ CurlySuffixExpression = TypeExpr option(ContainerInitExpression)
MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
-PrefixOpExpression = PrefixOp ErrorSetExpr | SuffixOpExpression
+PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression
SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
From cb744f3a28cf5aa6caef797e9265b7165c903a55 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Tue, 20 Mar 2018 13:48:25 -0400
Subject: [PATCH 007/132] self-hosted build: print helpful message when
libstdc++.a not found
closes #843
---
build.zig | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/build.zig b/build.zig
index 2e6c6dd83..88775498c 100644
--- a/build.zig
+++ b/build.zig
@@ -64,6 +64,14 @@ pub fn build(b: &Builder) !void {
if (exe.target.getOs() == builtin.Os.linux) {
const libstdcxx_path_padded = try b.exec([][]const u8{cxx_compiler, "-print-file-name=libstdc++.a"});
const libstdcxx_path = ??mem.split(libstdcxx_path_padded, "\r\n").next();
+ if (mem.eql(u8, libstdcxx_path, "libstdc++.a")) {
+ warn(
+ \\Unable to determine path to libstdc++.a
+ \\On Fedora, install libstdc++-static and try again.
+ \\
+ );
+ return error.RequiredLibraryNotFound;
+ }
exe.addObjectFile(libstdcxx_path);
exe.linkSystemLibrary("pthread");
From 543952eb87f7b06a41dd001273a59a0dea728c39 Mon Sep 17 00:00:00 2001
From: Wander Lairson Costa
Date: Tue, 20 Mar 2018 11:46:31 +0000
Subject: [PATCH 008/132] Include libxml2 and zlib as required libraries
libxml2 is a required library, but we only find out that when the build
fails to link against it, if it is not present. The same for zlib.
We use find_library to find these two libraries and print nice fail
messages if they are not found.
---
CMakeLists.txt | 18 +++++++++++++++++-
1 file changed, 17 insertions(+), 1 deletion(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9a4f57171..979d771cd 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -49,6 +49,22 @@ option(ZIG_FORCE_EXTERNAL_LLD "If your system has the LLD patches use it instead
find_package(llvm)
find_package(clang)
+if(NOT MSVC)
+ find_library(LIBXML2 NAMES xml2 libxml2)
+ if(${LIBXML2} STREQUAL "LIBXML2-NOTFOUND")
+ message(FATAL_ERROR "Could not find libxml2")
+ else()
+ message("${LIBXML2} found")
+ endif()
+
+ find_library(ZLIB NAMES z zlib libz)
+ if(${ZLIB} STREQUAL "ZLIB-NOTFOUND")
+ message(FATAL_ERROR "Could not find zlib")
+ else()
+ message("${ZLIB} found")
+ endif()
+endif()
+
set(ZIG_CPP_LIB_DIR "${CMAKE_BINARY_DIR}/zig_cpp")
if(ZIG_FORCE_EXTERNAL_LLD)
@@ -710,7 +726,7 @@ target_link_libraries(zig LINK_PUBLIC
${CMAKE_THREAD_LIBS_INIT}
)
if(NOT MSVC)
- target_link_libraries(zig LINK_PUBLIC xml2)
+ target_link_libraries(zig LINK_PUBLIC ${LIBXML2})
endif()
if(ZIG_DIA_GUIDS_LIB)
target_link_libraries(zig LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB})
From f885a1ab61a664378c3f3062a3de1150b6a4ee07 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Wed, 21 Mar 2018 19:56:41 -0400
Subject: [PATCH 009/132] change async function call syntax
* instead of `async(allocator) call()`, now it is
`async call()`.
* Fixes syntax ambiguity when leaving off the allocator
* Fixes parse failure when call is a field access
This sets a precedent for using `<` to pass arguments
to a keyword. This will affect `enum`, `union`, and
`fn` (see #661)
---
doc/langref.html.in | 2 +-
src/parser.cpp | 19 ++++++++++---------
test/cases/coroutines.zig | 25 +++++++++++++++++++------
test/compile_errors.zig | 2 +-
4 files changed, 31 insertions(+), 17 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index 5165a7bb9..d227936f8 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -5845,7 +5845,7 @@ MultiplyOperator = "||" | "*" | "/" | "%" | "**" | "*%"
PrefixOpExpression = PrefixOp TypeExpr | SuffixOpExpression
-SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
FieldAccessExpression = "." Symbol
diff --git a/src/parser.cpp b/src/parser.cpp
index c7675ad67..0c9b7e326 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -956,7 +956,7 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde
}
/*
-SuffixOpExpression = ("async" option("(" Expression ")") PrimaryExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
ArrayAccessExpression : token(LBracket) Expression token(RBracket)
SliceExpression = "[" Expression ".." option(Expression) "]"
@@ -972,19 +972,20 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
AstNode *allocator_expr_node = nullptr;
Token *async_lparen_tok = &pc->tokens->at(*token_index);
- if (async_lparen_tok->id == TokenIdLParen) {
+ if (async_lparen_tok->id == TokenIdCmpLessThan) {
*token_index += 1;
- allocator_expr_node = ast_parse_expression(pc, token_index, true);
- ast_eat_token(pc, token_index, TokenIdRParen);
+ allocator_expr_node = ast_parse_prefix_op_expr(pc, token_index, true);
+ ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
}
- AstNode *fn_ref_expr_node = ast_parse_primary_expr(pc, token_index, true);
- Token *lparen_tok = ast_eat_token(pc, token_index, TokenIdLParen);
- AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, lparen_tok);
+ Token *fncall_token = &pc->tokens->at(*token_index);
+ AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
+ if (node->type != NodeTypeFnCallExpr) {
+ ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
+ }
node->data.fn_call_expr.is_async = true;
node->data.fn_call_expr.async_allocator = allocator_expr_node;
- node->data.fn_call_expr.fn_ref_expr = fn_ref_expr_node;
- ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params);
+ assert(node->data.fn_call_expr.fn_ref_expr != nullptr);
primary_expr = node;
} else {
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 12235cf40..9e98276e0 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -4,7 +4,7 @@ const assert = std.debug.assert;
var x: i32 = 1;
test "create a coroutine and cancel it" {
- const p = try async(std.debug.global_allocator) simpleAsyncFn();
+ const p = try async simpleAsyncFn();
cancel p;
assert(x == 2);
}
@@ -17,7 +17,7 @@ async fn simpleAsyncFn() void {
test "coroutine suspend, resume, cancel" {
seq('a');
- const p = try async(std.debug.global_allocator) testAsyncSeq();
+ const p = try async testAsyncSeq();
seq('c');
resume p;
seq('f');
@@ -43,7 +43,7 @@ fn seq(c: u8) void {
}
test "coroutine suspend with block" {
- const p = try async(std.debug.global_allocator) testSuspendBlock();
+ const p = try async testSuspendBlock();
std.debug.assert(!result);
resume a_promise;
std.debug.assert(result);
@@ -65,7 +65,7 @@ var await_final_result: i32 = 0;
test "coroutine await" {
await_seq('a');
- const p = async(std.debug.global_allocator) await_amain() catch unreachable;
+ const p = async await_amain() catch unreachable;
await_seq('f');
resume await_a_promise;
await_seq('i');
@@ -104,7 +104,7 @@ var early_final_result: i32 = 0;
test "coroutine await early return" {
early_seq('a');
- const p = async(std.debug.global_allocator) early_amain() catch unreachable;
+ const p = async early_amain() catch unreachable;
early_seq('f');
assert(early_final_result == 1234);
assert(std.mem.eql(u8, early_points, "abcdef"));
@@ -133,7 +133,7 @@ fn early_seq(c: u8) void {
test "coro allocation failure" {
var failing_allocator = std.debug.FailingAllocator.init(std.debug.global_allocator, 0);
- if (async(&failing_allocator.allocator) asyncFuncThatNeverGetsRun()) {
+ if (async<&failing_allocator.allocator> asyncFuncThatNeverGetsRun()) {
@panic("expected allocation failure");
} else |err| switch (err) {
error.OutOfMemory => {},
@@ -143,3 +143,16 @@ test "coro allocation failure" {
async fn asyncFuncThatNeverGetsRun() void {
@panic("coro frame allocation should fail");
}
+
+test "async function with dot syntax" {
+ const S = struct {
+ var y: i32 = 1;
+ async fn foo() void {
+ y += 1;
+ suspend;
+ }
+ };
+ const p = try async S.foo();
+ cancel p;
+ assert(S.y == 2);
+}
diff --git a/test/compile_errors.zig b/test/compile_errors.zig
index 68737eee1..ddf528633 100644
--- a/test/compile_errors.zig
+++ b/test/compile_errors.zig
@@ -17,7 +17,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void {
cases.add("returning error from void async function",
\\const std = @import("std");
\\export fn entry() void {
- \\ const p = async(std.debug.global_allocator) amain() catch unreachable;
+ \\ const p = async amain() catch unreachable;
\\}
\\async fn amain() void {
\\ return error.ShouldBeCompileError;
From 53588f4f124bb713cc2c67c721d5f4e3586b7d79 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Thu, 22 Mar 2018 19:18:51 +1300
Subject: [PATCH 010/132] Add missing pub specifier to atan2
---
std/math/atan2.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/std/math/atan2.zig b/std/math/atan2.zig
index 6f29b7b18..37c520da4 100644
--- a/std/math/atan2.zig
+++ b/std/math/atan2.zig
@@ -22,7 +22,7 @@ const std = @import("../index.zig");
const math = std.math;
const assert = std.debug.assert;
-fn atan2(comptime T: type, x: T, y: T) T {
+pub fn atan2(comptime T: type, x: T, y: T) T {
return switch (T) {
f32 => atan2_32(x, y),
f64 => atan2_64(x, y),
From 7a99d63c764f3d5d92370c90f932b1bf156269f6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Thu, 22 Mar 2018 16:56:03 -0400
Subject: [PATCH 011/132] ability to use async function pointers
closes #817
---
doc/langref.html.in | 2 +-
src/all_types.hpp | 1 +
src/analyze.cpp | 3 +-
src/ir.cpp | 22 ++++++-
src/parser.cpp | 126 +++++++++++++++++++++-----------------
test/cases/coroutines.zig | 20 ++++++
6 files changed, 113 insertions(+), 61 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index d227936f8..f4aa42eb8 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -5739,7 +5739,7 @@ UseDecl = "use" Expression ";"
ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";"
-FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
+FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("<" Expression ">"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var")
FnDef = option("inline" | "export") FnProto Block
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 6afa7ccf2..c1dec80d7 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -2673,6 +2673,7 @@ struct IrInstructionFnProto {
IrInstruction **param_types;
IrInstruction *align_value;
IrInstruction *return_type;
+ IrInstruction *async_allocator_type_value;
bool is_var_args;
};
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 395df229c..eb1850c8e 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -985,7 +985,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
// populate the name of the type
buf_resize(&fn_type->name, 0);
if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) {
- buf_appendf(&fn_type->name, "async(%s) ", buf_ptr(&fn_type_id->async_allocator_type->name));
+ assert(fn_type_id->async_allocator_type != nullptr);
+ buf_appendf(&fn_type->name, "async<%s> ", buf_ptr(&fn_type_id->async_allocator_type->name));
} else {
const char *cc_str = calling_convention_fn_type_str(fn_type->data.fn.fn_type_id.cc);
buf_appendf(&fn_type->name, "%s", cc_str);
diff --git a/src/ir.cpp b/src/ir.cpp
index db9a2b24c..8f418e2c2 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -2141,12 +2141,14 @@ static IrInstruction *ir_build_unwrap_err_payload_from(IrBuilder *irb, IrInstruc
}
static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *source_node,
- IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type, bool is_var_args)
+ IrInstruction **param_types, IrInstruction *align_value, IrInstruction *return_type,
+ IrInstruction *async_allocator_type_value, bool is_var_args)
{
IrInstructionFnProto *instruction = ir_build_instruction(irb, scope, source_node);
instruction->param_types = param_types;
instruction->align_value = align_value;
instruction->return_type = return_type;
+ instruction->async_allocator_type_value = async_allocator_type_value;
instruction->is_var_args = is_var_args;
assert(source_node->type == NodeTypeFnProto);
@@ -2156,6 +2158,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s
if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block);
}
if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block);
+ if (async_allocator_type_value != nullptr) ir_ref_instruction(async_allocator_type_value, irb->current_basic_block);
ir_ref_instruction(return_type, irb->current_basic_block);
return &instruction->base;
@@ -5989,7 +5992,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo
return_type = nullptr;
}
- return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args);
+ IrInstruction *async_allocator_type_value = nullptr;
+ if (node->data.fn_proto.async_allocator_type != nullptr) {
+ async_allocator_type_value = ir_gen_node(irb, node->data.fn_proto.async_allocator_type, parent_scope);
+ if (async_allocator_type_value == irb->codegen->invalid_instruction)
+ return irb->codegen->invalid_instruction;
+ }
+
+ return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type,
+ async_allocator_type_value, is_var_args);
}
static IrInstruction *ir_gen_cancel(IrBuilder *irb, Scope *parent_scope, AstNode *node) {
@@ -16561,6 +16572,13 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc
if (type_is_invalid(fn_type_id.return_type))
return ira->codegen->builtin_types.entry_invalid;
+ if (fn_type_id.cc == CallingConventionAsync) {
+ IrInstruction *async_allocator_type_value = instruction->async_allocator_type_value->other;
+ fn_type_id.async_allocator_type = ir_resolve_type(ira, async_allocator_type_value);
+ if (type_is_invalid(fn_type_id.async_allocator_type))
+ return ira->codegen->builtin_types.entry_invalid;
+ }
+
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
out_val->data.x_type = get_fn_type(ira->codegen, &fn_type_id);
return ira->codegen->builtin_types.entry_type;
diff --git a/src/parser.cpp b/src/parser.cpp
index 0c9b7e326..666b9da3c 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -955,6 +955,66 @@ static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc, size_t *token_inde
}
}
+static AstNode *ast_parse_fn_proto_partial(ParseContext *pc, size_t *token_index, Token *fn_token,
+ AstNode *async_allocator_type_node, CallingConvention cc, bool is_extern, VisibMod visib_mod)
+{
+ 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;
+ node->data.fn_proto.async_allocator_type = async_allocator_type_node;
+
+ Token *fn_name = &pc->tokens->at(*token_index);
+
+ if (fn_name->id == TokenIdSymbol) {
+ *token_index += 1;
+ node->data.fn_proto.name = token_buf(fn_name);
+ } else {
+ node->data.fn_proto.name = nullptr;
+ }
+
+ ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
+
+ Token *next_token = &pc->tokens->at(*token_index);
+ if (next_token->id == TokenIdKeywordAlign) {
+ *token_index += 1;
+ ast_eat_token(pc, token_index, TokenIdLParen);
+
+ node->data.fn_proto.align_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 == 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 == TokenIdKeywordVar) {
+ node->data.fn_proto.return_var_token = next_token;
+ *token_index += 1;
+ next_token = &pc->tokens->at(*token_index);
+ } else {
+ if (next_token->id == TokenIdKeywordError) {
+ Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
+ if (maybe_lbrace_tok->id == TokenIdLBrace) {
+ *token_index += 1;
+ node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
+ return node;
+ }
+ } else if (next_token->id == TokenIdBang) {
+ *token_index += 1;
+ node->data.fn_proto.auto_err_set = true;
+ next_token = &pc->tokens->at(*token_index);
+ }
+ node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
+ }
+
+ return node;
+}
+
/*
SuffixOpExpression = ("async" option("<" SuffixOpExpression ">") SuffixOpExpression FnCallExpression) | PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
FnCallExpression : token(LParen) list(Expression, token(Comma)) token(RParen)
@@ -979,6 +1039,11 @@ static AstNode *ast_parse_suffix_op_expr(ParseContext *pc, size_t *token_index,
}
Token *fncall_token = &pc->tokens->at(*token_index);
+ if (fncall_token->id == TokenIdKeywordFn) {
+ *token_index += 1;
+ return ast_parse_fn_proto_partial(pc, token_index, fncall_token, allocator_expr_node, CallingConventionAsync,
+ false, VisibModPrivate);
+ }
AstNode *node = ast_parse_suffix_op_expr(pc, token_index, true);
if (node->type != NodeTypeFnCallExpr) {
ast_error(pc, fncall_token, "expected function call, found '%s'", token_name(fncall_token->id));
@@ -2434,9 +2499,10 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
} else if (first_token->id == TokenIdKeywordAsync) {
*token_index += 1;
Token *next_token = &pc->tokens->at(*token_index);
- if (next_token->id == TokenIdLParen) {
+ if (next_token->id == TokenIdCmpLessThan) {
+ *token_index += 1;
async_allocator_type_node = ast_parse_type_expr(pc, token_index, true);
- ast_eat_token(pc, token_index, TokenIdRParen);
+ ast_eat_token(pc, token_index, TokenIdCmpGreaterThan);
}
fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn);
cc = CallingConventionAsync;
@@ -2470,61 +2536,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m
return nullptr;
}
- 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;
- node->data.fn_proto.async_allocator_type = async_allocator_type_node;
-
- Token *fn_name = &pc->tokens->at(*token_index);
-
- if (fn_name->id == TokenIdSymbol) {
- *token_index += 1;
- node->data.fn_proto.name = token_buf(fn_name);
- } else {
- node->data.fn_proto.name = nullptr;
- }
-
- ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
-
- Token *next_token = &pc->tokens->at(*token_index);
- if (next_token->id == TokenIdKeywordAlign) {
- *token_index += 1;
- ast_eat_token(pc, token_index, TokenIdLParen);
-
- node->data.fn_proto.align_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 == 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 == TokenIdKeywordVar) {
- node->data.fn_proto.return_var_token = next_token;
- *token_index += 1;
- next_token = &pc->tokens->at(*token_index);
- } else {
- if (next_token->id == TokenIdKeywordError) {
- Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1);
- if (maybe_lbrace_tok->id == TokenIdLBrace) {
- *token_index += 1;
- node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token);
- return node;
- }
- } else if (next_token->id == TokenIdBang) {
- *token_index += 1;
- node->data.fn_proto.auto_err_set = true;
- next_token = &pc->tokens->at(*token_index);
- }
- node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true);
- }
-
- return node;
+ return ast_parse_fn_proto_partial(pc, token_index, fn_token, async_allocator_type_node, cc, is_extern, visib_mod);
}
/*
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 9e98276e0..f8ad58f70 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -156,3 +156,23 @@ test "async function with dot syntax" {
cancel p;
assert(S.y == 2);
}
+
+test "async fn pointer in a struct field" {
+ var data: i32 = 1;
+ const Foo = struct {
+ bar: async<&std.mem.Allocator> fn(&i32) void,
+ };
+ var foo = Foo {
+ .bar = simpleAsyncFn2,
+ };
+ const p = (async foo.bar(&data)) catch unreachable;
+ assert(data == 2);
+ cancel p;
+ assert(data == 4);
+}
+
+async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void {
+ defer *y += 2;
+ *y += 1;
+ suspend;
+}
From 3d1732ef6c8ebc7edf10485f61b4ec905303cd8a Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Fri, 23 Mar 2018 20:27:11 +1300
Subject: [PATCH 012/132] Fix OpqaueType usage in exported c functions
We prefer `struct typename`. If a typedef is required, this must be done
manually after generation.
---
src/codegen.cpp | 6 +-----
test/gen_h.zig | 11 +++++++++++
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/codegen.cpp b/src/codegen.cpp
index a44091bdb..de8cfa31e 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -6592,6 +6592,7 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
}
}
case TypeTableEntryIdStruct:
+ case TypeTableEntryIdOpaque:
{
buf_init_from_str(out_buf, "struct ");
buf_append_buf(out_buf, &type_entry->name);
@@ -6609,11 +6610,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf
buf_append_buf(out_buf, &type_entry->name);
return;
}
- case TypeTableEntryIdOpaque:
- {
- buf_init_from_buf(out_buf, &type_entry->name);
- return;
- }
case TypeTableEntryIdArray:
{
TypeTableEntryArray *array_data = &type_entry->data.array;
diff --git a/test/gen_h.zig b/test/gen_h.zig
index 5f28326ff..30d168cf2 100644
--- a/test/gen_h.zig
+++ b/test/gen_h.zig
@@ -51,6 +51,16 @@ pub fn addCases(cases: &tests.GenHContext) void {
\\
);
+ cases.add("declare opaque type",
+ \\export const Foo = @OpaqueType();
+ \\
+ \\export fn entry(foo: ?&Foo) void { }
+ ,
+ \\struct Foo;
+ \\
+ \\TEST_EXPORT void entry(struct Foo * foo);
+ );
+
cases.add("array field-type",
\\const Foo = extern struct {
\\ A: [2]i32,
@@ -66,4 +76,5 @@ pub fn addCases(cases: &tests.GenHContext) void {
\\TEST_EXPORT void entry(struct Foo foo, uint8_t bar[]);
\\
);
+
}
From 7350181a4a778f9d03186e5123beffdf80f58606 Mon Sep 17 00:00:00 2001
From: Marc Tiehuis
Date: Fri, 23 Mar 2018 22:41:08 +1300
Subject: [PATCH 013/132] Fix os.File.mode function
---
std/os/file.zig | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/std/os/file.zig b/std/os/file.zig
index 772fbf7c7..eed3a443b 100644
--- a/std/os/file.zig
+++ b/std/os/file.zig
@@ -233,7 +233,7 @@ pub const File = struct {
Unexpected,
};
- fn mode(self: &File) ModeError!FileMode {
+ fn mode(self: &File) ModeError!os.FileMode {
if (is_posix) {
var stat: posix.Stat = undefined;
const err = posix.getErrno(posix.fstat(self.handle, &stat));
From 2cff31937f6008769ad1034f9451d136d03c06bb Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 24 Mar 2018 15:57:36 -0400
Subject: [PATCH 014/132] std.os.linux exposes syscall functions and syscall
numbers
---
std/os/linux/index.zig | 158 +++++++++++++++++------------------------
1 file changed, 67 insertions(+), 91 deletions(-)
diff --git a/std/os/linux/index.zig b/std/os/linux/index.zig
index 646b1ef30..8fd8bcbe7 100644
--- a/std/os/linux/index.zig
+++ b/std/os/linux/index.zig
@@ -1,7 +1,7 @@
const std = @import("../../index.zig");
const assert = std.debug.assert;
const builtin = @import("builtin");
-const arch = switch (builtin.arch) {
+pub use switch (builtin.arch) {
builtin.Arch.x86_64 => @import("x86_64.zig"),
builtin.Arch.i386 => @import("i386.zig"),
else => @compileError("unsupported arch"),
@@ -93,27 +93,6 @@ pub const O_RDONLY = 0o0;
pub const O_WRONLY = 0o1;
pub const O_RDWR = 0o2;
-pub const O_CREAT = arch.O_CREAT;
-pub const O_EXCL = arch.O_EXCL;
-pub const O_NOCTTY = arch.O_NOCTTY;
-pub const O_TRUNC = arch.O_TRUNC;
-pub const O_APPEND = arch.O_APPEND;
-pub const O_NONBLOCK = arch.O_NONBLOCK;
-pub const O_DSYNC = arch.O_DSYNC;
-pub const O_SYNC = arch.O_SYNC;
-pub const O_RSYNC = arch.O_RSYNC;
-pub const O_DIRECTORY = arch.O_DIRECTORY;
-pub const O_NOFOLLOW = arch.O_NOFOLLOW;
-pub const O_CLOEXEC = arch.O_CLOEXEC;
-
-pub const O_ASYNC = arch.O_ASYNC;
-pub const O_DIRECT = arch.O_DIRECT;
-pub const O_LARGEFILE = arch.O_LARGEFILE;
-pub const O_NOATIME = arch.O_NOATIME;
-pub const O_PATH = arch.O_PATH;
-pub const O_TMPFILE = arch.O_TMPFILE;
-pub const O_NDELAY = arch.O_NDELAY;
-
pub const SEEK_SET = 0;
pub const SEEK_CUR = 1;
pub const SEEK_END = 2;
@@ -394,65 +373,65 @@ pub fn getErrno(r: usize) usize {
}
pub fn dup2(old: i32, new: i32) usize {
- return arch.syscall2(arch.SYS_dup2, usize(old), usize(new));
+ return syscall2(SYS_dup2, usize(old), usize(new));
}
pub fn chdir(path: &const u8) usize {
- return arch.syscall1(arch.SYS_chdir, @ptrToInt(path));
+ return syscall1(SYS_chdir, @ptrToInt(path));
}
pub fn execve(path: &const u8, argv: &const ?&const u8, envp: &const ?&const u8) usize {
- return arch.syscall3(arch.SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
+ return syscall3(SYS_execve, @ptrToInt(path), @ptrToInt(argv), @ptrToInt(envp));
}
pub fn fork() usize {
- return arch.syscall0(arch.SYS_fork);
+ return syscall0(SYS_fork);
}
pub fn getcwd(buf: &u8, size: usize) usize {
- return arch.syscall2(arch.SYS_getcwd, @ptrToInt(buf), size);
+ return syscall2(SYS_getcwd, @ptrToInt(buf), size);
}
pub fn getdents(fd: i32, dirp: &u8, count: usize) usize {
- return arch.syscall3(arch.SYS_getdents, usize(fd), @ptrToInt(dirp), count);
+ return syscall3(SYS_getdents, usize(fd), @ptrToInt(dirp), count);
}
pub fn isatty(fd: i32) bool {
var wsz: winsize = undefined;
- return arch.syscall3(arch.SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
+ return syscall3(SYS_ioctl, usize(fd), TIOCGWINSZ, @ptrToInt(&wsz)) == 0;
}
pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) usize {
- return arch.syscall3(arch.SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
+ return syscall3(SYS_readlink, @ptrToInt(path), @ptrToInt(buf_ptr), buf_len);
}
pub fn mkdir(path: &const u8, mode: u32) usize {
- return arch.syscall2(arch.SYS_mkdir, @ptrToInt(path), mode);
+ return syscall2(SYS_mkdir, @ptrToInt(path), mode);
}
pub fn mmap(address: ?&u8, length: usize, prot: usize, flags: usize, fd: i32, offset: isize) usize {
- return arch.syscall6(arch.SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
+ return syscall6(SYS_mmap, @ptrToInt(address), length, prot, flags, usize(fd),
@bitCast(usize, offset));
}
pub fn munmap(address: &u8, length: usize) usize {
- return arch.syscall2(arch.SYS_munmap, @ptrToInt(address), length);
+ return syscall2(SYS_munmap, @ptrToInt(address), length);
}
pub fn read(fd: i32, buf: &u8, count: usize) usize {
- return arch.syscall3(arch.SYS_read, usize(fd), @ptrToInt(buf), count);
+ return syscall3(SYS_read, usize(fd), @ptrToInt(buf), count);
}
pub fn rmdir(path: &const u8) usize {
- return arch.syscall1(arch.SYS_rmdir, @ptrToInt(path));
+ return syscall1(SYS_rmdir, @ptrToInt(path));
}
pub fn symlink(existing: &const u8, new: &const u8) usize {
- return arch.syscall2(arch.SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
+ return syscall2(SYS_symlink, @ptrToInt(existing), @ptrToInt(new));
}
pub fn pread(fd: i32, buf: &u8, count: usize, offset: usize) usize {
- return arch.syscall4(arch.SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
+ return syscall4(SYS_pread, usize(fd), @ptrToInt(buf), count, offset);
}
pub fn pipe(fd: &[2]i32) usize {
@@ -460,84 +439,84 @@ pub fn pipe(fd: &[2]i32) usize {
}
pub fn pipe2(fd: &[2]i32, flags: usize) usize {
- return arch.syscall2(arch.SYS_pipe2, @ptrToInt(fd), flags);
+ return syscall2(SYS_pipe2, @ptrToInt(fd), flags);
}
pub fn write(fd: i32, buf: &const u8, count: usize) usize {
- return arch.syscall3(arch.SYS_write, usize(fd), @ptrToInt(buf), count);
+ return syscall3(SYS_write, usize(fd), @ptrToInt(buf), count);
}
pub fn pwrite(fd: i32, buf: &const u8, count: usize, offset: usize) usize {
- return arch.syscall4(arch.SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset);
+ return syscall4(SYS_pwrite, usize(fd), @ptrToInt(buf), count, offset);
}
pub fn rename(old: &const u8, new: &const u8) usize {
- return arch.syscall2(arch.SYS_rename, @ptrToInt(old), @ptrToInt(new));
+ return syscall2(SYS_rename, @ptrToInt(old), @ptrToInt(new));
}
pub fn open(path: &const u8, flags: u32, perm: usize) usize {
- return arch.syscall3(arch.SYS_open, @ptrToInt(path), flags, perm);
+ return syscall3(SYS_open, @ptrToInt(path), flags, perm);
}
pub fn create(path: &const u8, perm: usize) usize {
- return arch.syscall2(arch.SYS_creat, @ptrToInt(path), perm);
+ return syscall2(SYS_creat, @ptrToInt(path), perm);
}
pub fn openat(dirfd: i32, path: &const u8, flags: usize, mode: usize) usize {
- return arch.syscall4(arch.SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
+ return syscall4(SYS_openat, usize(dirfd), @ptrToInt(path), flags, mode);
}
pub fn close(fd: i32) usize {
- return arch.syscall1(arch.SYS_close, usize(fd));
+ return syscall1(SYS_close, usize(fd));
}
pub fn lseek(fd: i32, offset: isize, ref_pos: usize) usize {
- return arch.syscall3(arch.SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos);
+ return syscall3(SYS_lseek, usize(fd), @bitCast(usize, offset), ref_pos);
}
pub fn exit(status: i32) noreturn {
- _ = arch.syscall1(arch.SYS_exit, @bitCast(usize, isize(status)));
+ _ = syscall1(SYS_exit, @bitCast(usize, isize(status)));
unreachable;
}
pub fn getrandom(buf: &u8, count: usize, flags: u32) usize {
- return arch.syscall3(arch.SYS_getrandom, @ptrToInt(buf), count, usize(flags));
+ return syscall3(SYS_getrandom, @ptrToInt(buf), count, usize(flags));
}
pub fn kill(pid: i32, sig: i32) usize {
- return arch.syscall2(arch.SYS_kill, @bitCast(usize, isize(pid)), usize(sig));
+ return syscall2(SYS_kill, @bitCast(usize, isize(pid)), usize(sig));
}
pub fn unlink(path: &const u8) usize {
- return arch.syscall1(arch.SYS_unlink, @ptrToInt(path));
+ return syscall1(SYS_unlink, @ptrToInt(path));
}
pub fn waitpid(pid: i32, status: &i32, options: i32) usize {
- return arch.syscall4(arch.SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
+ return syscall4(SYS_wait4, @bitCast(usize, isize(pid)), @ptrToInt(status), @bitCast(usize, isize(options)), 0);
}
pub fn nanosleep(req: &const timespec, rem: ?×pec) usize {
- return arch.syscall2(arch.SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
+ return syscall2(SYS_nanosleep, @ptrToInt(req), @ptrToInt(rem));
}
pub fn setuid(uid: u32) usize {
- return arch.syscall1(arch.SYS_setuid, uid);
+ return syscall1(SYS_setuid, uid);
}
pub fn setgid(gid: u32) usize {
- return arch.syscall1(arch.SYS_setgid, gid);
+ return syscall1(SYS_setgid, gid);
}
pub fn setreuid(ruid: u32, euid: u32) usize {
- return arch.syscall2(arch.SYS_setreuid, ruid, euid);
+ return syscall2(SYS_setreuid, ruid, euid);
}
pub fn setregid(rgid: u32, egid: u32) usize {
- return arch.syscall2(arch.SYS_setregid, rgid, egid);
+ return syscall2(SYS_setregid, rgid, egid);
}
pub fn sigprocmask(flags: u32, noalias set: &const sigset_t, noalias oldset: ?&sigset_t) usize {
- return arch.syscall4(arch.SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
+ return syscall4(SYS_rt_sigprocmask, flags, @ptrToInt(set), @ptrToInt(oldset), NSIG/8);
}
pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigaction) usize {
@@ -548,11 +527,11 @@ pub fn sigaction(sig: u6, noalias act: &const Sigaction, noalias oact: ?&Sigacti
.handler = act.handler,
.flags = act.flags | SA_RESTORER,
.mask = undefined,
- .restorer = @ptrCast(extern fn()void, arch.restore_rt),
+ .restorer = @ptrCast(extern fn()void, restore_rt),
};
var ksa_old: k_sigaction = undefined;
@memcpy(@ptrCast(&u8, &ksa.mask), @ptrCast(&const u8, &act.mask), 8);
- const result = arch.syscall4(arch.SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask)));
+ const result = syscall4(SYS_rt_sigaction, sig, @ptrToInt(&ksa), @ptrToInt(&ksa_old), @sizeOf(@typeOf(ksa.mask)));
const err = getErrno(result);
if (err != 0) {
return result;
@@ -592,22 +571,22 @@ pub const empty_sigset = []usize{0} ** sigset_t.len;
pub fn raise(sig: i32) usize {
var set: sigset_t = undefined;
blockAppSignals(&set);
- const tid = i32(arch.syscall0(arch.SYS_gettid));
- const ret = arch.syscall2(arch.SYS_tkill, usize(tid), usize(sig));
+ const tid = i32(syscall0(SYS_gettid));
+ const ret = syscall2(SYS_tkill, usize(tid), usize(sig));
restoreSignals(&set);
return ret;
}
fn blockAllSignals(set: &sigset_t) void {
- _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG/8);
+ _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&all_mask), @ptrToInt(set), NSIG/8);
}
fn blockAppSignals(set: &sigset_t) void {
- _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG/8);
+ _ = syscall4(SYS_rt_sigprocmask, SIG_BLOCK, @ptrToInt(&app_mask), @ptrToInt(set), NSIG/8);
}
fn restoreSignals(set: &sigset_t) void {
- _ = arch.syscall4(arch.SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG/8);
+ _ = syscall4(SYS_rt_sigprocmask, SIG_SETMASK, @ptrToInt(set), 0, NSIG/8);
}
pub fn sigaddset(set: &sigset_t, sig: u6) void {
@@ -653,61 +632,61 @@ pub const iovec = extern struct {
};
pub fn getsockname(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
- return arch.syscall3(arch.SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len));
+ return syscall3(SYS_getsockname, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
pub fn getpeername(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
- return arch.syscall3(arch.SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
+ return syscall3(SYS_getpeername, usize(fd), @ptrToInt(addr), @ptrToInt(len));
}
pub fn socket(domain: i32, socket_type: i32, protocol: i32) usize {
- return arch.syscall3(arch.SYS_socket, usize(domain), usize(socket_type), usize(protocol));
+ return syscall3(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 {
- return arch.syscall5(arch.SYS_setsockopt, usize(fd), usize(level), usize(optname), usize(optval), @ptrToInt(optlen));
+ return syscall5(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 {
- return arch.syscall5(arch.SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
+ return syscall5(SYS_getsockopt, usize(fd), usize(level), usize(optname), @ptrToInt(optval), @ptrToInt(optlen));
}
-pub fn sendmsg(fd: i32, msg: &const arch.msghdr, flags: u32) usize {
- return arch.syscall3(arch.SYS_sendmsg, usize(fd), @ptrToInt(msg), flags);
+pub fn sendmsg(fd: i32, msg: &const msghdr, flags: u32) usize {
+ return syscall3(SYS_sendmsg, usize(fd), @ptrToInt(msg), flags);
}
pub fn connect(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
- return arch.syscall3(arch.SYS_connect, usize(fd), @ptrToInt(addr), usize(len));
+ return syscall3(SYS_connect, usize(fd), @ptrToInt(addr), usize(len));
}
-pub fn recvmsg(fd: i32, msg: &arch.msghdr, flags: u32) usize {
- return arch.syscall3(arch.SYS_recvmsg, usize(fd), @ptrToInt(msg), flags);
+pub fn recvmsg(fd: i32, msg: &msghdr, flags: u32) usize {
+ return syscall3(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
{
- return arch.syscall6(arch.SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
+ return syscall6(SYS_recvfrom, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), @ptrToInt(alen));
}
pub fn shutdown(fd: i32, how: i32) usize {
- return arch.syscall2(arch.SYS_shutdown, usize(fd), usize(how));
+ return syscall2(SYS_shutdown, usize(fd), usize(how));
}
pub fn bind(fd: i32, addr: &const sockaddr, len: socklen_t) usize {
- return arch.syscall3(arch.SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
+ return syscall3(SYS_bind, usize(fd), @ptrToInt(addr), usize(len));
}
pub fn listen(fd: i32, backlog: i32) usize {
- return arch.syscall2(arch.SYS_listen, usize(fd), usize(backlog));
+ return syscall2(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 {
- return arch.syscall6(arch.SYS_sendto, usize(fd), @ptrToInt(buf), len, flags, @ptrToInt(addr), usize(alen));
+ return syscall6(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 {
- return arch.syscall4(arch.SYS_socketpair, usize(domain), usize(socket_type), usize(protocol), @ptrToInt(&fd[0]));
+ return syscall4(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 {
@@ -715,7 +694,7 @@ pub fn accept(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t) usize {
}
pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags: u32) usize {
- return arch.syscall4(arch.SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
+ return syscall4(SYS_accept4, usize(fd), @ptrToInt(addr), @ptrToInt(len), flags);
}
// error NameTooLong;
@@ -746,11 +725,8 @@ pub fn accept4(fd: i32, noalias addr: &sockaddr, noalias len: &socklen_t, flags:
// return ifr.ifr_ifindex;
// }
-pub const Stat = arch.Stat;
-pub const timespec = arch.timespec;
-
pub fn fstat(fd: i32, stat_buf: &Stat) usize {
- return arch.syscall2(arch.SYS_fstat, usize(fd), @ptrToInt(stat_buf));
+ return syscall2(SYS_fstat, usize(fd), @ptrToInt(stat_buf));
}
pub const epoll_data = extern union {
@@ -770,19 +746,19 @@ pub fn epoll_create() usize {
}
pub fn epoll_create1(flags: usize) usize {
- return arch.syscall1(arch.SYS_epoll_create1, flags);
+ return syscall1(SYS_epoll_create1, flags);
}
pub fn epoll_ctl(epoll_fd: i32, op: i32, fd: i32, ev: &epoll_event) usize {
- return arch.syscall4(arch.SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
+ return syscall4(SYS_epoll_ctl, usize(epoll_fd), usize(op), usize(fd), @ptrToInt(ev));
}
pub fn epoll_wait(epoll_fd: i32, events: &epoll_event, maxevents: u32, timeout: i32) usize {
- return arch.syscall4(arch.SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
+ return syscall4(SYS_epoll_wait, usize(epoll_fd), @ptrToInt(events), usize(maxevents), usize(timeout));
}
pub fn timerfd_create(clockid: i32, flags: u32) usize {
- return arch.syscall2(arch.SYS_timerfd_create, usize(clockid), usize(flags));
+ return syscall2(SYS_timerfd_create, usize(clockid), usize(flags));
}
pub const itimerspec = extern struct {
@@ -791,11 +767,11 @@ pub const itimerspec = extern struct {
};
pub fn timerfd_gettime(fd: i32, curr_value: &itimerspec) usize {
- return arch.syscall2(arch.SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value));
+ return syscall2(SYS_timerfd_gettime, usize(fd), @ptrToInt(curr_value));
}
pub fn timerfd_settime(fd: i32, flags: u32, new_value: &const itimerspec, old_value: ?&itimerspec) usize {
- return arch.syscall4(arch.SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
+ return syscall4(SYS_timerfd_settime, usize(fd), usize(flags), @ptrToInt(new_value), @ptrToInt(old_value));
}
test "import linux test" {
From b1c07c0ea9e351a43c9bc2fe747fc07c0a19e005 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Mon, 12 Mar 2018 23:03:47 -0400
Subject: [PATCH 015/132] move error ret tracing codegen to zig ir
progress towards #821
---
src/all_types.hpp | 5 +++
src/codegen.cpp | 39 ++++++++++-----------
src/ir.cpp | 86 ++++++++++++++++++++++++++++++++++-------------
src/ir_print.cpp | 7 ++++
4 files changed, 91 insertions(+), 46 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
index c1dec80d7..b5428c959 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -2034,6 +2034,7 @@ enum IrInstructionId {
IrInstructionIdAtomicRmw,
IrInstructionIdPromiseResultType,
IrInstructionIdAwaitBookkeeping,
+ IrInstructionIdSaveErrRetAddr,
};
struct IrInstruction {
@@ -2988,6 +2989,10 @@ struct IrInstructionAwaitBookkeeping {
IrInstruction *promise_result_type;
};
+struct IrInstructionSaveErrRetAddr {
+ IrInstruction base;
+};
+
static const size_t slice_ptr_index = 0;
static const size_t slice_len_index = 1;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index de8cfa31e..eddadb94e 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -1607,32 +1607,25 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) {
return instruction->llvm_value;
}
+static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *executable,
+ IrInstructionSaveErrRetAddr *save_err_ret_addr_instruction)
+{
+ assert(g->have_err_ret_tracing);
+
+ LLVMValueRef return_err_fn = get_return_err_fn(g);
+ LLVMValueRef args[] = {
+ g->cur_err_ret_trace_val,
+ };
+ LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
+ get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
+ LLVMSetTailCall(call_instruction, true);
+ return call_instruction;
+}
+
static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) {
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
TypeTableEntry *return_type = return_instruction->value->value.type;
- if (g->have_err_ret_tracing) {
- bool is_err_return = false;
- if (return_type->id == TypeTableEntryIdErrorUnion) {
- if (return_instruction->value->value.special == ConstValSpecialStatic) {
- is_err_return = return_instruction->value->value.data.x_err_union.err != nullptr;
- } else if (return_instruction->value->value.special == ConstValSpecialRuntime) {
- is_err_return = return_instruction->value->value.data.rh_error_union == RuntimeHintErrorUnionError;
- // TODO: emit a branch to check if the return value is an error
- }
- } else if (return_type->id == TypeTableEntryIdErrorSet) {
- is_err_return = true;
- }
- if (is_err_return) {
- LLVMValueRef return_err_fn = get_return_err_fn(g);
- LLVMValueRef args[] = {
- g->cur_err_ret_trace_val,
- };
- LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
- get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
- LLVMSetTailCall(call_instruction, true);
- }
- }
if (handle_is_ptr(return_type)) {
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
assert(g->cur_ret_ptr);
@@ -4400,6 +4393,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_coro_alloc_helper(g, executable, (IrInstructionCoroAllocHelper *)instruction);
case IrInstructionIdAtomicRmw:
return ir_render_atomic_rmw(g, executable, (IrInstructionAtomicRmw *)instruction);
+ case IrInstructionIdSaveErrRetAddr:
+ return ir_render_save_err_ret_addr(g, executable, (IrInstructionSaveErrRetAddr *)instruction);
}
zig_unreachable();
}
diff --git a/src/ir.cpp b/src/ir.cpp
index 8f418e2c2..a78be66dd 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -713,6 +713,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionAwaitBookkeeping
return IrInstructionIdAwaitBookkeeping;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *) {
+ return IrInstructionIdSaveErrRetAddr;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2678,6 +2682,11 @@ static IrInstruction *ir_build_await_bookkeeping(IrBuilder *irb, Scope *scope, A
return &instruction->base;
}
+static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *source_node) {
+ IrInstructionSaveErrRetAddr *instruction = ir_build_instruction(irb, scope, source_node);
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -2750,16 +2759,16 @@ static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) {
return nullptr;
}
+static bool exec_is_async(IrExecutable *exec) {
+ FnTableEntry *fn_entry = exec_fn_entry(exec);
+ return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
+}
+
static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
bool is_generated_code)
{
- FnTableEntry *fn_entry = exec_fn_entry(irb->exec);
- bool is_async = fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
+ bool is_async = exec_is_async(irb->exec);
if (!is_async) {
- //if (irb->codegen->have_err_ret_tracing) {
- // IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node);
- // ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr);
- //}
IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value);
return_inst->is_gen = is_generated_code;
return return_inst;
@@ -2781,21 +2790,33 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode
// the above blocks are rendered by ir_gen after the rest of codegen
}
-//static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node, bool is_async) {
-// if (!irb->codegen->have_err_ret_tracing)
-// return;
-//
-// if (is_async) {
-// IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr);
-// IrInstruction *return_address_ptr = ir_build_return_address(irb, scope, node);
-// IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr);
-// ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize);
-// return;
-// }
-//
-// IrInstruction *stack_trace_ptr = ir_build_error_return_trace_nonnull(irb, scope, node);
-// ir_build_save_err_ret_addr(irb, scope, node, stack_trace_ptr);
-//}
+static bool exec_have_err_ret_trace(CodeGen *g, IrExecutable *exec) {
+ if (!g->have_err_ret_tracing)
+ return false;
+ FnTableEntry *fn_entry = exec_fn_entry(exec);
+ if (fn_entry == nullptr)
+ return false;
+ if (exec->is_inline)
+ return false;
+ return type_can_fail(fn_entry->type_entry->data.fn.fn_type_id.return_type);
+}
+
+static void ir_gen_save_err_ret_addr(IrBuilder *irb, Scope *scope, AstNode *node) {
+ if (!exec_have_err_ret_trace(irb->codegen, irb->exec))
+ return;
+
+ bool is_async = exec_is_async(irb->exec);
+
+ if (is_async) {
+ //IrInstruction *err_ret_addr_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_err_ret_addr_ptr);
+ //IrInstruction *return_address_ptr = ir_build_instr_addr(irb, scope, node);
+ //IrInstruction *return_address_usize = ir_build_ptr_to_int(irb, scope, node, return_address_ptr);
+ //ir_build_store_ptr(irb, scope, node, err_ret_addr_ptr, return_address_usize);
+ return;
+ }
+
+ ir_build_save_err_ret_addr(irb, scope, node);
+}
static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) {
assert(node->type == NodeTypeReturnExpr);
@@ -2856,7 +2877,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
if (have_err_defers) {
ir_gen_defers_for_block(irb, scope, outer_scope, true);
}
- //ir_gen_save_err_ret_addr(irb, scope, node, is_async);
+ ir_gen_save_err_ret_addr(irb, scope, node);
ir_build_br(irb, scope, node, ret_stmt_block, is_comptime);
ir_set_cursor_at_end_and_append_block(irb, ok_block);
@@ -2895,6 +2916,7 @@ static IrInstruction *ir_gen_return(IrBuilder *irb, Scope *scope, AstNode *node,
ir_set_cursor_at_end_and_append_block(irb, return_block);
ir_gen_defers_for_block(irb, scope, outer_scope, true);
IrInstruction *err_val = ir_build_unwrap_err_code(irb, scope, node, err_union_ptr);
+ ir_gen_save_err_ret_addr(irb, scope, node);
ir_gen_async_return(irb, scope, node, err_val, false);
ir_set_cursor_at_end_and_append_block(irb, continue_block);
@@ -6406,6 +6428,7 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
return false;
if (!instr_is_unreachable(result)) {
+ // no need for save_err_ret_addr because this cannot return error
ir_gen_async_return(irb, scope, result->source_node, result, true);
}
@@ -11464,13 +11487,17 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi
return ira->codegen->builtin_types.entry_void;
}
+static bool exec_has_err_ret_trace(CodeGen *g, IrExecutable *exec) {
+ FnTableEntry *fn_entry = exec_fn_entry(exec);
+ return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
+}
+
static TypeTableEntry *ir_analyze_instruction_error_return_trace(IrAnalyze *ira,
IrInstructionErrorReturnTrace *instruction)
{
- FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(ira->codegen);
TypeTableEntry *nullable_type = get_maybe_type(ira->codegen, ptr_to_stack_trace_type);
- if (fn_entry == nullptr || !fn_entry->calls_or_awaits_errorable_fn || !ira->codegen->have_err_ret_tracing) {
+ if (!exec_has_err_ret_trace(ira->codegen, ira->new_irb.exec)) {
ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
out_val->data.x_maybe = nullptr;
return nullable_type;
@@ -17775,6 +17802,14 @@ static TypeTableEntry *ir_analyze_instruction_await_bookkeeping(IrAnalyze *ira,
return out_val->type;
}
+static TypeTableEntry *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstructionSaveErrRetAddr *instruction) {
+ IrInstruction *result = ir_build_save_err_ret_addr(&ira->new_irb, instruction->base.scope,
+ instruction->base.source_node);
+ ir_link_new_instruction(result, &instruction->base);
+ result->value.type = ira->codegen->builtin_types.entry_void;
+ return result->value.type;
+}
+
static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
switch (instruction->id) {
case IrInstructionIdInvalid:
@@ -18012,6 +18047,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_promise_result_type(ira, (IrInstructionPromiseResultType *)instruction);
case IrInstructionIdAwaitBookkeeping:
return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction);
+ case IrInstructionIdSaveErrRetAddr:
+ return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);
}
zig_unreachable();
}
@@ -18137,6 +18174,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCoroSave:
case IrInstructionIdCoroAllocHelper:
case IrInstructionIdAwaitBookkeeping:
+ case IrInstructionIdSaveErrRetAddr:
return true;
case IrInstructionIdPhi:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 167bd2083..709cb4394 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -1161,6 +1161,10 @@ static void ir_print_await_bookkeeping(IrPrint *irp, IrInstructionAwaitBookkeepi
fprintf(irp->f, ")");
}
+static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr *instruction) {
+ fprintf(irp->f, "@saveErrRetAddr()");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -1532,6 +1536,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdAwaitBookkeeping:
ir_print_await_bookkeeping(irp, (IrInstructionAwaitBookkeeping *)instruction);
break;
+ case IrInstructionIdSaveErrRetAddr:
+ ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
From 18af2f9a2764cc340571578d58cb2575faeccdc6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 24 Mar 2018 18:21:51 -0400
Subject: [PATCH 016/132] fix async fns with inferred error sets
closes #856
---
src/all_types.hpp | 12 ++++++++++-
src/analyze.cpp | 10 ++++-----
src/ast_render.cpp | 11 +++++++++-
src/codegen.cpp | 1 +
src/ir.cpp | 45 ++++++++++++++++++++++++++++++++++-----
src/ir_print.cpp | 13 +++++++++--
test/cases/coroutines.zig | 11 ++++++++++
7 files changed, 89 insertions(+), 14 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
index b5428c959..f5afecfbd 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1251,7 +1251,10 @@ struct FnTableEntry {
ScopeBlock *def_scope; // parent is child_scope
Buf symbol_name;
TypeTableEntry *type_entry; // function type
- TypeTableEntry *implicit_return_type;
+ // in the case of normal functions this is the implicit return type
+ // in the case of async functions this is the implicit return type according to the
+ // zig source code, not according to zig ir
+ TypeTableEntry *src_implicit_return_type;
bool is_test;
FnInline fn_inline;
FnAnalState anal_state;
@@ -2035,6 +2038,7 @@ enum IrInstructionId {
IrInstructionIdPromiseResultType,
IrInstructionIdAwaitBookkeeping,
IrInstructionIdSaveErrRetAddr,
+ IrInstructionIdAddImplicitReturnType,
};
struct IrInstruction {
@@ -2993,6 +2997,12 @@ struct IrInstructionSaveErrRetAddr {
IrInstruction base;
};
+struct IrInstructionAddImplicitReturnType {
+ IrInstruction base;
+
+ IrInstruction *value;
+};
+
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 eb1850c8e..5f2162b2c 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -3865,7 +3865,7 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
TypeTableEntry *block_return_type = ir_analyze(g, &fn_table_entry->ir_executable,
&fn_table_entry->analyzed_executable, fn_type_id->return_type, return_type_node);
- fn_table_entry->implicit_return_type = block_return_type;
+ fn_table_entry->src_implicit_return_type = block_return_type;
if (type_is_invalid(block_return_type) || fn_table_entry->analyzed_executable.invalid) {
assert(g->errors.length > 0);
@@ -3877,10 +3877,10 @@ void analyze_fn_ir(CodeGen *g, FnTableEntry *fn_table_entry, AstNode *return_typ
TypeTableEntry *return_err_set_type = fn_type_id->return_type->data.error_union.err_set_type;
if (return_err_set_type->data.error_set.infer_fn != nullptr) {
TypeTableEntry *inferred_err_set_type;
- if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorSet) {
- inferred_err_set_type = fn_table_entry->implicit_return_type;
- } else if (fn_table_entry->implicit_return_type->id == TypeTableEntryIdErrorUnion) {
- inferred_err_set_type = fn_table_entry->implicit_return_type->data.error_union.err_set_type;
+ if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorSet) {
+ inferred_err_set_type = fn_table_entry->src_implicit_return_type;
+ } else if (fn_table_entry->src_implicit_return_type->id == TypeTableEntryIdErrorUnion) {
+ inferred_err_set_type = fn_table_entry->src_implicit_return_type->data.error_union.err_set_type;
} else {
add_node_error(g, return_type_node,
buf_sprintf("function with inferred error set must return at least one possible error"));
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index f88feee85..432489c4d 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -658,6 +658,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
if (node->data.fn_call_expr.is_builtin) {
fprintf(ar->f, "@");
}
+ if (node->data.fn_call_expr.is_async) {
+ fprintf(ar->f, "async");
+ if (node->data.fn_call_expr.async_allocator != nullptr) {
+ fprintf(ar->f, "<");
+ render_node_extra(ar, node->data.fn_call_expr.async_allocator, true);
+ fprintf(ar->f, ">");
+ }
+ fprintf(ar->f, " ");
+ }
AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr;
bool grouped = (fn_ref_node->type != NodeTypePrefixOpExpr && fn_ref_node->type != NodeTypeAddrOfExpr);
render_node_extra(ar, fn_ref_node, grouped);
@@ -1023,7 +1032,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
case NodeTypeUnwrapErrorExpr:
{
render_node_ungrouped(ar, node->data.unwrap_err_expr.op1);
- fprintf(ar->f, " %%%% ");
+ fprintf(ar->f, " catch ");
if (node->data.unwrap_err_expr.symbol) {
Buf *var_name = node->data.unwrap_err_expr.symbol->data.symbol_expr.symbol;
fprintf(ar->f, "|%s| ", buf_ptr(var_name));
diff --git a/src/codegen.cpp b/src/codegen.cpp
index eddadb94e..d67501053 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -4245,6 +4245,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdErrorUnion:
case IrInstructionIdPromiseResultType:
case IrInstructionIdAwaitBookkeeping:
+ case IrInstructionIdAddImplicitReturnType:
zig_unreachable();
case IrInstructionIdReturn:
diff --git a/src/ir.cpp b/src/ir.cpp
index a78be66dd..d896153d0 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -34,7 +34,7 @@ struct IrAnalyze {
size_t old_bb_index;
size_t instruction_index;
TypeTableEntry *explicit_return_type;
- ZigList implicit_return_type_list;
+ ZigList src_implicit_return_type_list;
IrBasicBlock *const_predecessor_bb;
};
@@ -717,6 +717,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSaveErrRetAddr *
return IrInstructionIdSaveErrRetAddr;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionAddImplicitReturnType *) {
+ return IrInstructionIdAddImplicitReturnType;
+}
+
template
static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = allocate(1);
@@ -2687,6 +2691,17 @@ static IrInstruction *ir_build_save_err_ret_addr(IrBuilder *irb, Scope *scope, A
return &instruction->base;
}
+static IrInstruction *ir_build_add_implicit_return_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *value)
+{
+ IrInstructionAddImplicitReturnType *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->value = value;
+
+ ir_ref_instruction(value, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) {
results[ReturnKindUnconditional] = 0;
results[ReturnKindError] = 0;
@@ -2767,6 +2782,8 @@ static bool exec_is_async(IrExecutable *exec) {
static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value,
bool is_generated_code)
{
+ ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value));
+
bool is_async = exec_is_async(irb->exec);
if (!is_async) {
IrInstruction *return_inst = ir_build_return(irb, scope, node, return_value);
@@ -6399,6 +6416,8 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_build_cond_br(irb, scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
+ // we can return undefined here, because the caller passes a pointer to the error struct field
+ // in the error union result, and we populate it in case of allocation failure.
IrInstruction *undef = ir_build_const_undefined(irb, scope, node);
ir_build_return(irb, scope, node, undef);
@@ -10108,13 +10127,26 @@ static Buf *ir_resolve_str(IrAnalyze *ira, IrInstruction *value) {
return result;
}
+static TypeTableEntry *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira,
+ IrInstructionAddImplicitReturnType *instruction)
+{
+ IrInstruction *value = instruction->value->other;
+ if (type_is_invalid(value->value.type))
+ return ir_unreach_error(ira);
+
+ ira->src_implicit_return_type_list.append(value);
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->type = ira->codegen->builtin_types.entry_void;
+ return out_val->type;
+}
+
static TypeTableEntry *ir_analyze_instruction_return(IrAnalyze *ira,
IrInstructionReturn *return_instruction)
{
IrInstruction *value = return_instruction->value->other;
if (type_is_invalid(value->value.type))
return ir_unreach_error(ira);
- ira->implicit_return_type_list.append(value);
IrInstruction *casted_value = ir_implicit_cast(ira, value, ira->explicit_return_type);
if (casted_value == ira->codegen->invalid_instruction)
@@ -18049,6 +18081,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_await_bookkeeping(ira, (IrInstructionAwaitBookkeeping *)instruction);
case IrInstructionIdSaveErrRetAddr:
return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstructionSaveErrRetAddr *)instruction);
+ case IrInstructionIdAddImplicitReturnType:
+ return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstructionAddImplicitReturnType *)instruction);
}
zig_unreachable();
}
@@ -18122,11 +18156,11 @@ TypeTableEntry *ir_analyze(CodeGen *codegen, IrExecutable *old_exec, IrExecutabl
if (new_exec->invalid) {
return ira->codegen->builtin_types.entry_invalid;
- } else if (ira->implicit_return_type_list.length == 0) {
+ } else if (ira->src_implicit_return_type_list.length == 0) {
return codegen->builtin_types.entry_unreachable;
} else {
- return ir_resolve_peer_types(ira, expected_type_source_node, ira->implicit_return_type_list.items,
- ira->implicit_return_type_list.length);
+ return ir_resolve_peer_types(ira, expected_type_source_node, ira->src_implicit_return_type_list.items,
+ ira->src_implicit_return_type_list.length);
}
}
@@ -18175,6 +18209,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdCoroAllocHelper:
case IrInstructionIdAwaitBookkeeping:
case IrInstructionIdSaveErrRetAddr:
+ case IrInstructionIdAddImplicitReturnType:
return true;
case IrInstructionIdPhi:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 709cb4394..5008d3564 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -201,9 +201,9 @@ static void ir_print_call(IrPrint *irp, IrInstructionCall *call_instruction) {
if (call_instruction->is_async) {
fprintf(irp->f, "async");
if (call_instruction->async_allocator != nullptr) {
- fprintf(irp->f, "(");
+ fprintf(irp->f, "<");
ir_print_other_instruction(irp, call_instruction->async_allocator);
- fprintf(irp->f, ")");
+ fprintf(irp->f, ">");
}
fprintf(irp->f, " ");
}
@@ -1165,6 +1165,12 @@ static void ir_print_save_err_ret_addr(IrPrint *irp, IrInstructionSaveErrRetAddr
fprintf(irp->f, "@saveErrRetAddr()");
}
+static void ir_print_add_implicit_return_type(IrPrint *irp, IrInstructionAddImplicitReturnType *instruction) {
+ fprintf(irp->f, "@addImplicitReturnType(");
+ ir_print_other_instruction(irp, instruction->value);
+ fprintf(irp->f, ")");
+}
+
static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
ir_print_prefix(irp, instruction);
switch (instruction->id) {
@@ -1539,6 +1545,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdSaveErrRetAddr:
ir_print_save_err_ret_addr(irp, (IrInstructionSaveErrRetAddr *)instruction);
break;
+ case IrInstructionIdAddImplicitReturnType:
+ ir_print_add_implicit_return_type(irp, (IrInstructionAddImplicitReturnType *)instruction);
+ break;
}
fprintf(irp->f, "\n");
}
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index f8ad58f70..087d56171 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -176,3 +176,14 @@ async<&std.mem.Allocator> fn simpleAsyncFn2(y: &i32) void {
*y += 1;
suspend;
}
+
+test "async fn with inferred error set" {
+ const p = (async failing()) catch unreachable;
+ resume p;
+ cancel p;
+}
+
+async fn failing() !void {
+ suspend;
+ return error.Fail;
+}
From 897e783763d60449ad1b9514cb5ba86a38f7ae4a Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 24 Mar 2018 19:25:53 -0400
Subject: [PATCH 017/132] add promise->T syntax parsing
closes #857
---
doc/langref.html.in | 4 ++-
src/all_types.hpp | 13 ++++++++++
src/analyze.cpp | 1 +
src/ast_render.cpp | 11 ++++++++
src/codegen.cpp | 1 +
src/ir.cpp | 54 +++++++++++++++++++++++++++++++++++++++
src/ir_print.cpp | 11 ++++++++
src/parser.cpp | 14 +++++++++-
src/tokenizer.cpp | 2 ++
src/tokenizer.hpp | 1 +
test/cases/coroutines.zig | 1 +
11 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index f4aa42eb8..b2e14ac19 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -5863,7 +5863,9 @@ StructLiteralField = "." Symbol "=" Expression
PrefixOp = "!" | "-" | "~" | "*" | ("&" option("align" "(" Expression option(":" Integer ":" Integer) ")" ) option("const") option("volatile")) | "?" | "??" | "-%" | "try" | "await"
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
+
+PromiseType = "promise" option("->" TypeExpr)
ArrayType : "[" option(Expression) "]" option("align" "(" Expression option(":" Integer ":" Integer) ")")) option("const") option("volatile") TypeExpr
diff --git a/src/all_types.hpp b/src/all_types.hpp
index f5afecfbd..64b9b3166 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -409,6 +409,7 @@ enum NodeType {
NodeTypeResume,
NodeTypeAwaitExpr,
NodeTypeSuspend,
+ NodeTypePromiseType,
};
struct AstNodeRoot {
@@ -879,6 +880,10 @@ struct AstNodeSuspend {
AstNode *promise_symbol;
};
+struct AstNodePromiseType {
+ AstNode *payload_type; // can be NULL
+};
+
struct AstNode {
enum NodeType type;
size_t line;
@@ -939,6 +944,7 @@ struct AstNode {
AstNodeResumeExpr resume_expr;
AstNodeAwaitExpr await_expr;
AstNodeSuspend suspend;
+ AstNodePromiseType promise_type;
} data;
};
@@ -1947,6 +1953,7 @@ enum IrInstructionId {
IrInstructionIdSetRuntimeSafety,
IrInstructionIdSetFloatMode,
IrInstructionIdArrayType,
+ IrInstructionIdPromiseType,
IrInstructionIdSliceType,
IrInstructionIdAsm,
IrInstructionIdSizeOf,
@@ -2365,6 +2372,12 @@ struct IrInstructionArrayType {
IrInstruction *child_type;
};
+struct IrInstructionPromiseType {
+ IrInstruction base;
+
+ IrInstruction *payload_type;
+};
+
struct IrInstructionSliceType {
IrInstruction base;
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 5f2162b2c..0f4728f82 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -3254,6 +3254,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
case NodeTypeResume:
case NodeTypeAwaitExpr:
case NodeTypeSuspend:
+ case NodeTypePromiseType:
zig_unreachable();
}
}
diff --git a/src/ast_render.cpp b/src/ast_render.cpp
index 432489c4d..7b5fc03ea 100644
--- a/src/ast_render.cpp
+++ b/src/ast_render.cpp
@@ -250,6 +250,8 @@ static const char *node_type_str(NodeType node_type) {
return "AwaitExpr";
case NodeTypeSuspend:
return "Suspend";
+ case NodeTypePromiseType:
+ return "PromiseType";
}
zig_unreachable();
}
@@ -781,6 +783,15 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
render_node_ungrouped(ar, node->data.array_type.child_type);
break;
}
+ case NodeTypePromiseType:
+ {
+ fprintf(ar->f, "promise");
+ if (node->data.promise_type.payload_type != nullptr) {
+ fprintf(ar->f, "->");
+ render_node_grouped(ar, node->data.promise_type.payload_type);
+ }
+ break;
+ }
case NodeTypeErrorType:
fprintf(ar->f, "error");
break;
diff --git a/src/codegen.cpp b/src/codegen.cpp
index d67501053..786bd0398 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -4205,6 +4205,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdSetRuntimeSafety:
case IrInstructionIdSetFloatMode:
case IrInstructionIdArrayType:
+ case IrInstructionIdPromiseType:
case IrInstructionIdSliceType:
case IrInstructionIdSizeOf:
case IrInstructionIdSwitchTarget:
diff --git a/src/ir.cpp b/src/ir.cpp
index d896153d0..95142c88d 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -349,6 +349,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
return IrInstructionIdArrayType;
}
+static constexpr IrInstructionId ir_instruction_id(IrInstructionPromiseType *) {
+ return IrInstructionIdPromiseType;
+}
+
static constexpr IrInstructionId ir_instruction_id(IrInstructionSliceType *) {
return IrInstructionIdSliceType;
}
@@ -1469,6 +1473,17 @@ static IrInstruction *ir_build_array_type(IrBuilder *irb, Scope *scope, AstNode
return &instruction->base;
}
+static IrInstruction *ir_build_promise_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
+ IrInstruction *payload_type)
+{
+ IrInstructionPromiseType *instruction = ir_build_instruction(irb, scope, source_node);
+ instruction->payload_type = payload_type;
+
+ if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block);
+
+ return &instruction->base;
+}
+
static IrInstruction *ir_build_slice_type(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *child_type, bool is_const, bool is_volatile, IrInstruction *align_value)
{
@@ -5074,6 +5089,22 @@ static IrInstruction *ir_gen_array_type(IrBuilder *irb, Scope *scope, AstNode *n
}
}
+static IrInstruction *ir_gen_promise_type(IrBuilder *irb, Scope *scope, AstNode *node) {
+ assert(node->type == NodeTypePromiseType);
+
+ AstNode *payload_type_node = node->data.promise_type.payload_type;
+ IrInstruction *payload_type_value = nullptr;
+
+ if (payload_type_node != nullptr) {
+ payload_type_value = ir_gen_node(irb, payload_type_node, scope);
+ if (payload_type_value == irb->codegen->invalid_instruction)
+ return payload_type_value;
+
+ }
+
+ return ir_build_promise_type(irb, scope, node, payload_type_value);
+}
+
static IrInstruction *ir_gen_undefined_literal(IrBuilder *irb, Scope *scope, AstNode *node) {
assert(node->type == NodeTypeUndefinedLiteral);
return ir_build_const_undefined(irb, scope, node);
@@ -6282,6 +6313,8 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval);
case NodeTypeArrayType:
return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval);
+ case NodeTypePromiseType:
+ return ir_lval_wrap(irb, scope, ir_gen_promise_type(irb, scope, node), lval);
case NodeTypeStringLiteral:
return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval);
case NodeTypeUndefinedLiteral:
@@ -14069,6 +14102,24 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira,
zig_unreachable();
}
+static TypeTableEntry *ir_analyze_instruction_promise_type(IrAnalyze *ira, IrInstructionPromiseType *instruction) {
+ TypeTableEntry *promise_type;
+
+ if (instruction->payload_type == nullptr) {
+ promise_type = ira->codegen->builtin_types.entry_promise;
+ } else {
+ TypeTableEntry *payload_type = ir_resolve_type(ira, instruction->payload_type->other);
+ if (type_is_invalid(payload_type))
+ return ira->codegen->builtin_types.entry_invalid;
+
+ promise_type = get_promise_type(ira->codegen, payload_type);
+ }
+
+ ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base);
+ out_val->data.x_type = promise_type;
+ return ira->codegen->builtin_types.entry_type;
+}
+
static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira,
IrInstructionSizeOf *size_of_instruction)
{
@@ -17907,6 +17958,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_asm(ira, (IrInstructionAsm *)instruction);
case IrInstructionIdArrayType:
return ir_analyze_instruction_array_type(ira, (IrInstructionArrayType *)instruction);
+ case IrInstructionIdPromiseType:
+ return ir_analyze_instruction_promise_type(ira, (IrInstructionPromiseType *)instruction);
case IrInstructionIdSizeOf:
return ir_analyze_instruction_size_of(ira, (IrInstructionSizeOf *)instruction);
case IrInstructionIdTestNonNull:
@@ -18232,6 +18285,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdStructFieldPtr:
case IrInstructionIdUnionFieldPtr:
case IrInstructionIdArrayType:
+ case IrInstructionIdPromiseType:
case IrInstructionIdSliceType:
case IrInstructionIdSizeOf:
case IrInstructionIdTestNonNull:
diff --git a/src/ir_print.cpp b/src/ir_print.cpp
index 5008d3564..b14d49a4c 100644
--- a/src/ir_print.cpp
+++ b/src/ir_print.cpp
@@ -404,6 +404,14 @@ static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instructio
ir_print_other_instruction(irp, instruction->child_type);
}
+static void ir_print_promise_type(IrPrint *irp, IrInstructionPromiseType *instruction) {
+ fprintf(irp->f, "promise");
+ if (instruction->payload_type != nullptr) {
+ fprintf(irp->f, "->");
+ ir_print_other_instruction(irp, instruction->payload_type);
+ }
+}
+
static void ir_print_slice_type(IrPrint *irp, IrInstructionSliceType *instruction) {
const char *const_kw = instruction->is_const ? "const " : "";
fprintf(irp->f, "[]%s", const_kw);
@@ -1263,6 +1271,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdArrayType:
ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
break;
+ case IrInstructionIdPromiseType:
+ ir_print_promise_type(irp, (IrInstructionPromiseType *)instruction);
+ break;
case IrInstructionIdSliceType:
ir_print_slice_type(irp, (IrInstructionSliceType *)instruction);
break;
diff --git a/src/parser.cpp b/src/parser.cpp
index 666b9da3c..d6faf4c98 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -705,7 +705,7 @@ static AstNode *ast_parse_comptime_expr(ParseContext *pc, size_t *token_index, b
}
/*
-PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl
+PrimaryExpression = Integer | Float | String | CharLiteral | KeywordLiteral | GroupedExpression | BlockExpression(BlockOrExpression) | Symbol | ("@" Symbol FnCallExpression) | ArrayType | FnProto | AsmExpression | ContainerDecl | ("continue" option(":" Symbol)) | ErrorSetDecl | PromiseType
KeywordLiteral = "true" | "false" | "null" | "undefined" | "error" | "this" | "unreachable" | "suspend"
ErrorSetDecl = "error" "{" list(Symbol, ",") "}"
*/
@@ -774,6 +774,15 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, size_t *token_index, bo
AstNode *node = ast_create_node(pc, NodeTypeSuspend, token);
*token_index += 1;
return node;
+ } else if (token->id == TokenIdKeywordPromise) {
+ AstNode *node = ast_create_node(pc, NodeTypePromiseType, token);
+ *token_index += 1;
+ Token *arrow_tok = &pc->tokens->at(*token_index);
+ if (arrow_tok->id == TokenIdArrow) {
+ *token_index += 1;
+ node->data.promise_type.payload_type = ast_parse_type_expr(pc, token_index, true);
+ }
+ return node;
} else if (token->id == TokenIdKeywordError) {
Token *next_token = &pc->tokens->at(*token_index + 1);
if (next_token->id == TokenIdLBrace) {
@@ -3081,6 +3090,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
visit_field(&node->data.array_type.child_type, visit, context);
visit_field(&node->data.array_type.align_expr, visit, context);
break;
+ case NodeTypePromiseType:
+ visit_field(&node->data.promise_type.payload_type, visit, context);
+ break;
case NodeTypeErrorType:
// none
break;
diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp
index dc17829c0..365b35cdf 100644
--- a/src/tokenizer.cpp
+++ b/src/tokenizer.cpp
@@ -135,6 +135,7 @@ static const struct ZigKeyword zig_keywords[] = {
{"null", TokenIdKeywordNull},
{"or", TokenIdKeywordOr},
{"packed", TokenIdKeywordPacked},
+ {"promise", TokenIdKeywordPromise},
{"pub", TokenIdKeywordPub},
{"resume", TokenIdKeywordResume},
{"return", TokenIdKeywordReturn},
@@ -1558,6 +1559,7 @@ const char * token_name(TokenId id) {
case TokenIdKeywordNull: return "null";
case TokenIdKeywordOr: return "or";
case TokenIdKeywordPacked: return "packed";
+ case TokenIdKeywordPromise: return "promise";
case TokenIdKeywordPub: return "pub";
case TokenIdKeywordReturn: return "return";
case TokenIdKeywordSection: return "section";
diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp
index 2d7142799..b71929370 100644
--- a/src/tokenizer.hpp
+++ b/src/tokenizer.hpp
@@ -76,6 +76,7 @@ enum TokenId {
TokenIdKeywordNull,
TokenIdKeywordOr,
TokenIdKeywordPacked,
+ TokenIdKeywordPromise,
TokenIdKeywordPub,
TokenIdKeywordResume,
TokenIdKeywordReturn,
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 087d56171..25a75dca5 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -5,6 +5,7 @@ var x: i32 = 1;
test "create a coroutine and cancel it" {
const p = try async simpleAsyncFn();
+ comptime assert(@typeOf(p) == promise->void);
cancel p;
assert(x == 2);
}
From a43c7af3d1e41ccee73678f114bb3844febcaad6 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 24 Mar 2018 19:31:00 -0400
Subject: [PATCH 018/132] add comptime test for the type of suspend promise
---
test/cases/coroutines.zig | 1 +
1 file changed, 1 insertion(+)
diff --git a/test/cases/coroutines.zig b/test/cases/coroutines.zig
index 25a75dca5..922c1a7e5 100644
--- a/test/cases/coroutines.zig
+++ b/test/cases/coroutines.zig
@@ -56,6 +56,7 @@ var result = false;
async fn testSuspendBlock() void {
suspend |p| {
+ comptime assert(@typeOf(p) == promise->void);
a_promise = p;
}
result = true;
From aa2995ee395b2c1329a61513debcac6225fcb8a8 Mon Sep 17 00:00:00 2001
From: Andrew Kelley
Date: Sat, 24 Mar 2018 22:05:29 -0400
Subject: [PATCH 019/132] fix invalid codegen for error return traces across
suspend points
See #821
Now the code works correctly, but error return traces are missing
the frames from coroutines.
---
src/all_types.hpp | 10 +++++-
src/analyze.cpp | 7 +++++
src/analyze.hpp | 1 +
src/codegen.cpp | 65 +++++++++++++++++++++++++++++---------
src/ir.cpp | 69 +++++++++++++++++++++--------------------
test/runtime_safety.zig | 30 ++++++++++++++++++
6 files changed, 132 insertions(+), 50 deletions(-)
diff --git a/src/all_types.hpp b/src/all_types.hpp
index 64b9b3166..6951230aa 100644
--- a/src/all_types.hpp
+++ b/src/all_types.hpp
@@ -1621,7 +1621,8 @@ struct CodeGen {
FnTableEntry *panic_fn;
LLVMValueRef cur_ret_ptr;
LLVMValueRef cur_fn_val;
- LLVMValueRef cur_err_ret_trace_val;
+ LLVMValueRef cur_err_ret_trace_val_arg;
+ LLVMValueRef cur_err_ret_trace_val_stack;
bool c_want_stdint;
bool c_want_stdbool;
AstNode *root_export_decl;
@@ -1760,6 +1761,7 @@ enum ScopeId {
ScopeIdLoop,
ScopeIdFnDef,
ScopeIdCompTime,
+ ScopeIdCoroPrelude,
};
struct Scope {
@@ -1867,6 +1869,12 @@ struct ScopeFnDef {
FnTableEntry *fn_entry;
};
+// This scope is created to indicate that the code in the scope
+// is auto-generated coroutine prelude stuff.
+struct ScopeCoroPrelude {
+ Scope base;
+};
+
// synchronized with code in define_builtin_compile_vars
enum AtomicOrder {
AtomicOrderUnordered,
diff --git a/src/analyze.cpp b/src/analyze.cpp
index 0f4728f82..7ee1de78a 100644
--- a/src/analyze.cpp
+++ b/src/analyze.cpp
@@ -170,6 +170,12 @@ Scope *create_comptime_scope(AstNode *node, Scope *parent) {
return &scope->base;
}
+Scope *create_coro_prelude_scope(AstNode *node, Scope *parent) {
+ ScopeCoroPrelude *scope = allocate(1);
+ init_scope(&scope->base, ScopeIdCoroPrelude, node, parent);
+ return &scope->base;
+}
+
ImportTableEntry *get_scope_import(Scope *scope) {
while (scope) {
if (scope->id == ScopeIdDecls) {
@@ -3592,6 +3598,7 @@ FnTableEntry *scope_get_fn_if_root(Scope *scope) {
case ScopeIdCImport:
case ScopeIdLoop:
case ScopeIdCompTime:
+ case ScopeIdCoroPrelude:
scope = scope->parent;
continue;
case ScopeIdFnDef:
diff --git a/src/analyze.hpp b/src/analyze.hpp
index 936134030..aa4557666 100644
--- a/src/analyze.hpp
+++ b/src/analyze.hpp
@@ -107,6 +107,7 @@ ScopeLoop *create_loop_scope(AstNode *node, Scope *parent);
ScopeFnDef *create_fndef_scope(AstNode *node, Scope *parent, FnTableEntry *fn_entry);
ScopeDecls *create_decls_scope(AstNode *node, Scope *parent, TypeTableEntry *container_type, ImportTableEntry *import);
Scope *create_comptime_scope(AstNode *node, Scope *parent);
+Scope *create_coro_prelude_scope(AstNode *node, Scope *parent);
void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str);
ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str);
diff --git a/src/codegen.cpp b/src/codegen.cpp
index 786bd0398..25b2ffbf1 100644
--- a/src/codegen.cpp
+++ b/src/codegen.cpp
@@ -653,6 +653,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) {
case ScopeIdDeferExpr:
case ScopeIdLoop:
case ScopeIdCompTime:
+ case ScopeIdCoroPrelude:
return get_di_scope(g, scope->parent);
}
zig_unreachable();
@@ -1318,9 +1319,34 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
return fn_val;
}
-static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
+static bool is_coro_prelude_scope(Scope *scope) {
+ while (scope != nullptr) {
+ if (scope->id == ScopeIdCoroPrelude) {
+ return true;
+ } else if (scope->id == ScopeIdFnDef) {
+ break;
+ }
+ scope = scope->parent;
+ }
+ return false;
+}
+
+static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope) {
+ if (!g->have_err_ret_tracing) {
+ return nullptr;
+ }
+ if (g->cur_fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync) {
+ return is_coro_prelude_scope(scope) ? g->cur_err_ret_trace_val_arg : g->cur_err_ret_trace_val_stack;
+ }
+ if (g->cur_err_ret_trace_val_stack != nullptr) {
+ return g->cur_err_ret_trace_val_stack;
+ }
+ return g->cur_err_ret_trace_val_arg;
+}
+
+static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
- LLVMValueRef err_ret_trace_val = g->cur_err_ret_trace_val;
+ LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope);
if (err_ret_trace_val == nullptr) {
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
err_ret_trace_val = LLVMConstNull(ptr_to_stack_trace_type->type_ref);
@@ -1614,7 +1640,7 @@ static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *execut
LLVMValueRef return_err_fn = get_return_err_fn(g);
LLVMValueRef args[] = {
- g->cur_err_ret_trace_val,
+ get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.scope),
};
LLVMValueRef call_instruction = ZigLLVMBuildCall(g->builder, return_err_fn, args, 1,
get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_FnInlineAuto, "");
@@ -2725,7 +2751,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
gen_param_index += 1;
}
if (prefix_arg_err_ret_stack) {
- gen_param_values[gen_param_index] = g->cur_err_ret_trace_val;
+ gen_param_values[gen_param_index] = get_cur_err_ret_trace_val(g, instruction->base.scope);
gen_param_index += 1;
}
if (instruction->is_async) {
@@ -3292,11 +3318,12 @@ static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutable *executable, I
static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutable *executable,
IrInstructionErrorReturnTrace *instruction)
{
- if (g->cur_err_ret_trace_val == nullptr) {
+ LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.scope);
+ if (cur_err_ret_trace_val == nullptr) {
TypeTableEntry *ptr_to_stack_trace_type = get_ptr_to_stack_trace_type(g);
return LLVMConstNull(ptr_to_stack_trace_type->type_ref);
}
- return g->cur_err_ret_trace_val;
+ return cur_err_ret_trace_val;
}
static LLVMValueRef ir_render_cancel(CodeGen *g, IrExecutable *executable, IrInstructionCancel *instruction) {
@@ -3726,7 +3753,7 @@ static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutable *execu
LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block);
LLVMPositionBuilderAtEnd(g->builder, err_block);
- gen_safety_crash_for_err(g, err_val);
+ gen_safety_crash_for_err(g, err_val, instruction->base.scope);
LLVMPositionBuilderAtEnd(g->builder, ok_block);
}
@@ -3918,7 +3945,7 @@ static LLVMValueRef ir_render_container_init_list(CodeGen *g, IrExecutable *exec
}
static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutable *executable, IrInstructionPanic *instruction) {
- gen_panic(g, ir_llvm_value(g, instruction->msg), g->cur_err_ret_trace_val);
+ gen_panic(g, ir_llvm_value(g, instruction->msg), get_cur_err_ret_trace_val(g, instruction->base.scope));
return nullptr;
}
@@ -5279,9 +5306,17 @@ static void do_code_gen(CodeGen *g) {
clear_debug_source_node(g);
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
- if (err_ret_trace_arg_index != UINT32_MAX) {
- g->cur_err_ret_trace_val = LLVMGetParam(fn, err_ret_trace_arg_index);
- } else if (g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn) {
+ bool have_err_ret_trace_arg = err_ret_trace_arg_index != UINT32_MAX;
+ if (have_err_ret_trace_arg) {
+ g->cur_err_ret_trace_val_arg = LLVMGetParam(fn, err_ret_trace_arg_index);
+ } else {
+ g->cur_err_ret_trace_val_arg = nullptr;
+ }
+
+ bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
+ bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn &&
+ (is_async || !have_err_ret_trace_arg);
+ if (have_err_ret_trace_stack) {
// TODO call graph analysis to find out what this number needs to be for every function
static const size_t stack_trace_ptr_count = 30;
@@ -5289,13 +5324,13 @@ static void do_code_gen(CodeGen *g) {
TypeTableEntry *array_type = get_array_type(g, usize, stack_trace_ptr_count);
LLVMValueRef err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses",
get_abi_alignment(g, array_type));
- g->cur_err_ret_trace_val = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
+ g->cur_err_ret_trace_val_stack = build_alloca(g, g->stack_trace_type, "error_return_trace", get_abi_alignment(g, g->stack_trace_type));
size_t index_field_index = g->stack_trace_type->data.structure.fields[0].gen_index;
- LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)index_field_index, "");
+ LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, "");
gen_store_untyped(g, LLVMConstNull(usize->type_ref), index_field_ptr, 0, false);
size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1].gen_index;
- LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val, (unsigned)addresses_field_index, "");
+ LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, "");
TypeTableEntry *slice_type = g->stack_trace_type->data.structure.fields[1].type_entry;
size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index].gen_index;
@@ -5311,7 +5346,7 @@ static void do_code_gen(CodeGen *g) {
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, "");
gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false));
} else {
- g->cur_err_ret_trace_val = nullptr;
+ g->cur_err_ret_trace_val_stack = nullptr;
}
// allocate temporary stack data
diff --git a/src/ir.cpp b/src/ir.cpp
index 95142c88d..d86b7f5b2 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -6412,60 +6412,61 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
VariableTableEntry *coro_size_var;
if (is_async) {
// create the coro promise
- const_bool_false = ir_build_const_bool(irb, scope, node, false);
- VariableTableEntry *promise_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false);
+ Scope *coro_scope = create_coro_prelude_scope(node, scope);
+ const_bool_false = ir_build_const_bool(irb, coro_scope, node, false);
+ VariableTableEntry *promise_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
return_type = fn_entry->type_entry->data.fn.fn_type_id.return_type;
- IrInstruction *promise_init = ir_build_const_promise_init(irb, scope, node, return_type);
- ir_build_var_decl(irb, scope, node, promise_var, nullptr, nullptr, promise_init);
- IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, scope, node, promise_var, false, false);
+ IrInstruction *promise_init = ir_build_const_promise_init(irb, coro_scope, node, return_type);
+ ir_build_var_decl(irb, coro_scope, node, promise_var, nullptr, nullptr, promise_init);
+ IrInstruction *coro_promise_ptr = ir_build_var_ptr(irb, coro_scope, node, promise_var, false, false);
- VariableTableEntry *await_handle_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false);
- IrInstruction *null_value = ir_build_const_null(irb, scope, node);
- IrInstruction *await_handle_type_val = ir_build_const_type(irb, scope, node,
+ VariableTableEntry *await_handle_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
+ IrInstruction *null_value = ir_build_const_null(irb, coro_scope, node);
+ IrInstruction *await_handle_type_val = ir_build_const_type(irb, coro_scope, node,
get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise));
- ir_build_var_decl(irb, scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
- irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, scope, node,
+ ir_build_var_decl(irb, coro_scope, node, await_handle_var, await_handle_type_val, nullptr, null_value);
+ irb->exec->await_handle_var_ptr = ir_build_var_ptr(irb, coro_scope, node,
await_handle_var, false, false);
- u8_ptr_type = ir_build_const_type(irb, scope, node,
+ u8_ptr_type = ir_build_const_type(irb, coro_scope, node,
get_pointer_to_type(irb->codegen, irb->codegen->builtin_types.entry_u8, false));
- IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, coro_promise_ptr);
- coro_id = ir_build_coro_id(irb, scope, node, promise_as_u8_ptr);
- coro_size_var = ir_create_var(irb, node, scope, nullptr, false, false, true, const_bool_false);
- IrInstruction *coro_size = ir_build_coro_size(irb, scope, node);
- ir_build_var_decl(irb, scope, node, coro_size_var, nullptr, nullptr, coro_size);
- IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
+ IrInstruction *promise_as_u8_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, coro_promise_ptr);
+ coro_id = ir_build_coro_id(irb, coro_scope, node, promise_as_u8_ptr);
+ coro_size_var = ir_create_var(irb, node, coro_scope, nullptr, false, false, true, const_bool_false);
+ IrInstruction *coro_size = ir_build_coro_size(irb, coro_scope, node);
+ ir_build_var_decl(irb, coro_scope, node, coro_size_var, nullptr, nullptr, coro_size);
+ IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, coro_scope, node,
ImplicitAllocatorIdArg);
- irb->exec->coro_allocator_var = ir_create_var(irb, node, scope, nullptr, true, true, true, const_bool_false);
- ir_build_var_decl(irb, scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
+ irb->exec->coro_allocator_var = ir_create_var(irb, node, coro_scope, nullptr, true, true, true, const_bool_false);
+ ir_build_var_decl(irb, coro_scope, node, irb->exec->coro_allocator_var, nullptr, nullptr, implicit_allocator_ptr);
Buf *alloc_field_name = buf_create_from_str(ASYNC_ALLOC_FIELD_NAME);
- IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, scope, node, implicit_allocator_ptr, alloc_field_name);
- IrInstruction *alloc_fn = ir_build_load_ptr(irb, scope, node, alloc_fn_ptr);
- IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, scope, node, alloc_fn, coro_size);
- IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, scope, node, maybe_coro_mem_ptr);
- IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, scope, "AllocError");
- IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, scope, "AllocOk");
- ir_build_cond_br(irb, scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
+ IrInstruction *alloc_fn_ptr = ir_build_field_ptr(irb, coro_scope, node, implicit_allocator_ptr, alloc_field_name);
+ IrInstruction *alloc_fn = ir_build_load_ptr(irb, coro_scope, node, alloc_fn_ptr);
+ IrInstruction *maybe_coro_mem_ptr = ir_build_coro_alloc_helper(irb, coro_scope, node, alloc_fn, coro_size);
+ IrInstruction *alloc_result_is_ok = ir_build_test_nonnull(irb, coro_scope, node, maybe_coro_mem_ptr);
+ IrBasicBlock *alloc_err_block = ir_create_basic_block(irb, coro_scope, "AllocError");
+ IrBasicBlock *alloc_ok_block = ir_create_basic_block(irb, coro_scope, "AllocOk");
+ ir_build_cond_br(irb, coro_scope, node, alloc_result_is_ok, alloc_ok_block, alloc_err_block, const_bool_false);
ir_set_cursor_at_end_and_append_block(irb, alloc_err_block);
// we can return undefined here, because the caller passes a pointer to the error struct field
// in the error union result, and we populate it in case of allocation failure.
- IrInstruction *undef = ir_build_const_undefined(irb, scope, node);
- ir_build_return(irb, scope, node, undef);
+ IrInstruction *undef = ir_build_const_undefined(irb, coro_scope, node);
+ ir_build_return(irb, coro_scope, node, undef);
ir_set_cursor_at_end_and_append_block(irb, alloc_ok_block);
- IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, scope, node, u8_ptr_type, maybe_coro_mem_ptr);
- irb->exec->coro_handle = ir_build_coro_begin(irb, scope, node, coro_id, coro_mem_ptr);
+ IrInstruction *coro_mem_ptr = ir_build_ptr_cast(irb, coro_scope, node, u8_ptr_type, maybe_coro_mem_ptr);
+ irb->exec->coro_handle = ir_build_coro_begin(irb, coro_scope, node, coro_id, coro_mem_ptr);
Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME);
- irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr,
+ irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr,
awaiter_handle_field_name);
Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME);
- irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name);
+ irb->exec->coro_result_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_field_name);
result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME);
- irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name);
- ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
+ irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, coro_scope, node, coro_promise_ptr, result_ptr_field_name);
+ ir_build_store_ptr(irb, coro_scope, node, irb->exec->coro_result_ptr_field_ptr, irb->exec->coro_result_field_ptr);
irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal");
diff --git a/test/runtime_safety.zig b/test/runtime_safety.zig
index 8b8f61205..1fea6347a 100644
--- a/test/runtime_safety.zig
+++ b/test/runtime_safety.zig
@@ -281,4 +281,34 @@ pub fn addCases(cases: &tests.CompareOutputContext) void {
\\ f.float = 12.34;
\\}
);
+
+ // This case makes sure that the code compiles and runs. There is not actually a special
+ // runtime safety check having to do specifically with error return traces across suspend points.
+ cases.addRuntimeSafety("error return trace across suspend points",
+ \\const std = @import("std");
+ \\
+ \\pub fn panic(message: []const u8, stack_trace: ?&@import("builtin").StackTrace) noreturn {
+ \\ std.os.exit(126);
+ \\}
+ \\
+ \\pub fn main() void {
+ \\ const p = nonFailing();
+ \\ resume p;
+ \\ const p2 = async printTrace(p) catch unreachable;
+ \\ cancel p2;
+ \\}
+ \\
+ \\fn nonFailing() promise->error!void {
+ \\ return async failing() catch unreachable;
+ \\}
+ \\
+ \\async fn failing() error!void {
+ \\ suspend;
+ \\ return error.Fail;
+ \\}
+ \\
+ \\async fn printTrace(p: promise->error!void) void {
+ \\ (await p) catch unreachable;
+ \\}
+ );
}
From 3e836f5516c48bcfa16e0b630e53822af80d4b3b Mon Sep 17 00:00:00 2001
From: Jay Weisskopf
Date: Sun, 25 Mar 2018 18:48:07 -0400
Subject: [PATCH 020/132] doc: fix typo and tighten wording in error sections
Changes:
- Removed superfluous "when possible"
- Fixed typo in "documentationt"
- Added missing comma
- Moved definition of error union type up to first sentence
---
doc/langref.html.in | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/doc/langref.html.in b/doc/langref.html.in
index b2e14ac19..04ca5314c 100644
--- a/doc/langref.html.in
+++ b/doc/langref.html.in
@@ -2864,18 +2864,18 @@ const err = (error {FileNotFound}).FileNotFound;
assert to make sure the error value is in fact in the destination error set.
- The global error set should generally be avoided when possible, because it prevents
- the compiler from knowing what errors are possible at compile-time. Knowing
- the error set at compile-time is better for generated documentationt and for
- helpful error messages such as forgetting a possible error value in a {#link|switch#}.
+ The global error set should generally be avoided because it prevents the
+ compiler from knowing what errors are possible at compile-time. Knowing
+ the error set at compile-time is better for generated documentation and
+ helpful error messages, such as forgetting a possible error value in a {#link|switch#}.
{#header_close#}
{#header_close#}
{#header_open|Error Union Type#}
- Most of the time you will not find yourself using an error set type. Instead,
- likely you will be using the error union type. This is when you take an error set
- and a normal type, and create an error union with the !
binary operator.
+ An error set type and normal type can be combined with the !
+ binary operator to form an error union type. You are likely to use an
+ error union type more often than an error set type by itself.
Here is a function to parse a string into a 64-bit integer:
@@ -6033,4 +6033,3 @@ hljs.registerLanguage("zig", function(t) {