Merge remote-tracking branch 'origin/master' into async-fs
This commit is contained in:
commit
65140b2fba
@ -134,6 +134,58 @@ pub fn main() void {
|
|||||||
</p>
|
</p>
|
||||||
{#see_also|Values|@import|Errors|Root Source File#}
|
{#see_also|Values|@import|Errors|Root Source File#}
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
{#header_open|Comments#}
|
||||||
|
{#code_begin|test|comments#}
|
||||||
|
const assert = @import("std").debug.assert;
|
||||||
|
|
||||||
|
test "comments" {
|
||||||
|
// Comments in Zig start with "//" and end at the next LF byte (end of line).
|
||||||
|
// The below line is a comment, and won't be executed.
|
||||||
|
|
||||||
|
//assert(false);
|
||||||
|
|
||||||
|
const x = true; // another comment
|
||||||
|
assert(x);
|
||||||
|
}
|
||||||
|
{#code_end#}
|
||||||
|
<p>
|
||||||
|
There are no multiline comments in Zig (e.g. like <code>/* */</code>
|
||||||
|
comments in C). This helps allow Zig to have the property that each line
|
||||||
|
of code can be tokenized out of context.
|
||||||
|
</p>
|
||||||
|
{#header_open|Doc comments#}
|
||||||
|
<p>
|
||||||
|
A doc comment is one that begins with exactly three slashes (i.e.
|
||||||
|
<code class="zig">///</code> but not <code class="zig">////</code>);
|
||||||
|
multiple doc comments in a row are merged together to form a multiline
|
||||||
|
doc comment. The doc comment documents whatever immediately follows it.
|
||||||
|
</p>
|
||||||
|
{#code_begin|syntax|doc_comments#}
|
||||||
|
/// A structure for storing a timestamp, with nanosecond precision (this is a
|
||||||
|
/// multiline doc comment).
|
||||||
|
const Timestamp = struct {
|
||||||
|
/// The number of seconds since the epoch (this is also a doc comment).
|
||||||
|
seconds: i64, // signed so we can represent pre-1970 (not a doc comment)
|
||||||
|
/// The number of nanoseconds past the second (doc comment again).
|
||||||
|
nanos: u32,
|
||||||
|
|
||||||
|
/// Returns a `Timestamp` struct representing the Unix epoch; that is, the
|
||||||
|
/// moment of 1970 Jan 1 00:00:00 UTC (this is a doc comment too).
|
||||||
|
pub fn unixEpoch() Timestamp {
|
||||||
|
return Timestamp{
|
||||||
|
.seconds = 0,
|
||||||
|
.nanos = 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
{#code_end#}
|
||||||
|
<p>
|
||||||
|
Doc comments are only allowed in certain places; eventually, it will
|
||||||
|
become a compile error have a doc comment in an unexpected place, such as
|
||||||
|
in the middle of an expression, or just before a non-doc comment.
|
||||||
|
</p>
|
||||||
|
{#header_close#}
|
||||||
|
{#header_close#}
|
||||||
{#header_open|Values#}
|
{#header_open|Values#}
|
||||||
{#code_begin|exe|values#}
|
{#code_begin|exe|values#}
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
@ -4638,9 +4690,9 @@ test "coroutine suspend with block" {
|
|||||||
var a_promise: promise = undefined;
|
var a_promise: promise = undefined;
|
||||||
var result = false;
|
var result = false;
|
||||||
async fn testSuspendBlock() void {
|
async fn testSuspendBlock() void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
comptime assert(@typeOf(p) == promise->void);
|
comptime assert(@typeOf(@handle()) == promise->void);
|
||||||
a_promise = p;
|
a_promise = @handle();
|
||||||
}
|
}
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
@ -4681,8 +4733,8 @@ test "resume from suspend" {
|
|||||||
std.debug.assert(my_result == 2);
|
std.debug.assert(my_result == 2);
|
||||||
}
|
}
|
||||||
async fn testResumeFromSuspend(my_result: *i32) void {
|
async fn testResumeFromSuspend(my_result: *i32) void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
my_result.* += 1;
|
my_result.* += 1;
|
||||||
suspend;
|
suspend;
|
||||||
@ -4739,9 +4791,9 @@ async fn amain() void {
|
|||||||
}
|
}
|
||||||
async fn another() i32 {
|
async fn another() i32 {
|
||||||
seq('c');
|
seq('c');
|
||||||
suspend |p| {
|
suspend {
|
||||||
seq('d');
|
seq('d');
|
||||||
a_promise = p;
|
a_promise = @handle();
|
||||||
}
|
}
|
||||||
seq('g');
|
seq('g');
|
||||||
return 1234;
|
return 1234;
|
||||||
@ -5331,6 +5383,16 @@ test "main" {
|
|||||||
This function is only valid within function scope.
|
This function is only valid within function scope.
|
||||||
</p>
|
</p>
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
|
{#header_open|@handle#}
|
||||||
|
<pre><code class="zig">@handle()</code></pre>
|
||||||
|
<p>
|
||||||
|
This function returns a <code>promise->T</code> type, where <code>T</code>
|
||||||
|
is the return type of the async function in scope.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This function is only valid within an async function scope.
|
||||||
|
</p>
|
||||||
|
{#header_close#}
|
||||||
{#header_open|@import#}
|
{#header_open|@import#}
|
||||||
<pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>
|
<pre><code class="zig">@import(comptime path: []u8) (namespace)</code></pre>
|
||||||
<p>
|
<p>
|
||||||
@ -7336,7 +7398,7 @@ Defer(body) = ("defer" | "deferror") body
|
|||||||
|
|
||||||
IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
|
IfExpression(body) = "if" "(" Expression ")" body option("else" BlockExpression(body))
|
||||||
|
|
||||||
SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
|
SuspendExpression(body) = "suspend" option( body )
|
||||||
|
|
||||||
IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
|
IfErrorExpression(body) = "if" "(" Expression ")" option("|" option("*") Symbol "|") body "else" "|" Symbol "|" BlockExpression(body)
|
||||||
|
|
||||||
|
@ -899,7 +899,6 @@ struct AstNodeAwaitExpr {
|
|||||||
|
|
||||||
struct AstNodeSuspend {
|
struct AstNodeSuspend {
|
||||||
AstNode *block;
|
AstNode *block;
|
||||||
AstNode *promise_symbol;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AstNodePromiseType {
|
struct AstNodePromiseType {
|
||||||
@ -1358,6 +1357,7 @@ enum BuiltinFnId {
|
|||||||
BuiltinFnIdBreakpoint,
|
BuiltinFnIdBreakpoint,
|
||||||
BuiltinFnIdReturnAddress,
|
BuiltinFnIdReturnAddress,
|
||||||
BuiltinFnIdFrameAddress,
|
BuiltinFnIdFrameAddress,
|
||||||
|
BuiltinFnIdHandle,
|
||||||
BuiltinFnIdEmbedFile,
|
BuiltinFnIdEmbedFile,
|
||||||
BuiltinFnIdCmpxchgWeak,
|
BuiltinFnIdCmpxchgWeak,
|
||||||
BuiltinFnIdCmpxchgStrong,
|
BuiltinFnIdCmpxchgStrong,
|
||||||
@ -1716,6 +1716,7 @@ struct CodeGen {
|
|||||||
LLVMValueRef coro_save_fn_val;
|
LLVMValueRef coro_save_fn_val;
|
||||||
LLVMValueRef coro_promise_fn_val;
|
LLVMValueRef coro_promise_fn_val;
|
||||||
LLVMValueRef coro_alloc_helper_fn_val;
|
LLVMValueRef coro_alloc_helper_fn_val;
|
||||||
|
LLVMValueRef coro_frame_fn_val;
|
||||||
LLVMValueRef merge_err_ret_traces_fn_val;
|
LLVMValueRef merge_err_ret_traces_fn_val;
|
||||||
LLVMValueRef add_error_return_trace_addr_fn_val;
|
LLVMValueRef add_error_return_trace_addr_fn_val;
|
||||||
LLVMValueRef stacksave_fn_val;
|
LLVMValueRef stacksave_fn_val;
|
||||||
@ -2076,6 +2077,7 @@ enum IrInstructionId {
|
|||||||
IrInstructionIdBreakpoint,
|
IrInstructionIdBreakpoint,
|
||||||
IrInstructionIdReturnAddress,
|
IrInstructionIdReturnAddress,
|
||||||
IrInstructionIdFrameAddress,
|
IrInstructionIdFrameAddress,
|
||||||
|
IrInstructionIdHandle,
|
||||||
IrInstructionIdAlignOf,
|
IrInstructionIdAlignOf,
|
||||||
IrInstructionIdOverflowOp,
|
IrInstructionIdOverflowOp,
|
||||||
IrInstructionIdTestErr,
|
IrInstructionIdTestErr,
|
||||||
@ -2793,6 +2795,10 @@ struct IrInstructionFrameAddress {
|
|||||||
IrInstruction base;
|
IrInstruction base;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct IrInstructionHandle {
|
||||||
|
IrInstruction base;
|
||||||
|
};
|
||||||
|
|
||||||
enum IrOverflowOp {
|
enum IrOverflowOp {
|
||||||
IrOverflowOpAdd,
|
IrOverflowOpAdd,
|
||||||
IrOverflowOpSub,
|
IrOverflowOpSub,
|
||||||
|
@ -1112,9 +1112,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
|
|||||||
{
|
{
|
||||||
fprintf(ar->f, "suspend");
|
fprintf(ar->f, "suspend");
|
||||||
if (node->data.suspend.block != nullptr) {
|
if (node->data.suspend.block != nullptr) {
|
||||||
fprintf(ar->f, " |");
|
|
||||||
render_node_grouped(ar, node->data.suspend.promise_symbol);
|
|
||||||
fprintf(ar->f, "| ");
|
|
||||||
render_node_grouped(ar, node->data.suspend.block);
|
render_node_grouped(ar, node->data.suspend.block);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -4146,6 +4146,26 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable
|
|||||||
return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");
|
return LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef get_handle_fn_val(CodeGen *g) {
|
||||||
|
if (g->coro_frame_fn_val)
|
||||||
|
return g->coro_frame_fn_val;
|
||||||
|
|
||||||
|
LLVMTypeRef fn_type = LLVMFunctionType( LLVMPointerType(LLVMInt8Type(), 0)
|
||||||
|
, nullptr, 0, false);
|
||||||
|
Buf *name = buf_sprintf("llvm.coro.frame");
|
||||||
|
g->coro_frame_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type);
|
||||||
|
assert(LLVMGetIntrinsicID(g->coro_frame_fn_val));
|
||||||
|
|
||||||
|
return g->coro_frame_fn_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable,
|
||||||
|
IrInstructionHandle *instruction)
|
||||||
|
{
|
||||||
|
LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_promise->type_ref);
|
||||||
|
return LLVMBuildCall(g->builder, get_handle_fn_val(g), &zero, 0, "");
|
||||||
|
}
|
||||||
|
|
||||||
static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
|
static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) {
|
||||||
TypeTableEntry *int_type = instruction->result_ptr_type;
|
TypeTableEntry *int_type = instruction->result_ptr_type;
|
||||||
assert(int_type->id == TypeTableEntryIdInt);
|
assert(int_type->id == TypeTableEntryIdInt);
|
||||||
@ -4910,6 +4930,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
|
|||||||
return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
|
return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction);
|
||||||
case IrInstructionIdFrameAddress:
|
case IrInstructionIdFrameAddress:
|
||||||
return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
|
return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction);
|
||||||
|
case IrInstructionIdHandle:
|
||||||
|
return ir_render_handle(g, executable, (IrInstructionHandle *)instruction);
|
||||||
case IrInstructionIdOverflowOp:
|
case IrInstructionIdOverflowOp:
|
||||||
return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
|
return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction);
|
||||||
case IrInstructionIdTestErr:
|
case IrInstructionIdTestErr:
|
||||||
@ -6005,6 +6027,7 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
ir_render(g, fn_table_entry);
|
ir_render(g, fn_table_entry);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(!g->errors.length);
|
assert(!g->errors.length);
|
||||||
|
|
||||||
if (buf_len(&g->global_asm) != 0) {
|
if (buf_len(&g->global_asm) != 0) {
|
||||||
@ -6344,6 +6367,7 @@ static void define_builtin_fns(CodeGen *g) {
|
|||||||
create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);
|
create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0);
|
||||||
create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);
|
create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0);
|
||||||
create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
|
create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0);
|
||||||
|
create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0);
|
||||||
create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
|
create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3);
|
||||||
create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);
|
create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3);
|
||||||
create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);
|
create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1);
|
||||||
|
51
src/ir.cpp
51
src/ir.cpp
@ -580,6 +580,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *)
|
|||||||
return IrInstructionIdFrameAddress;
|
return IrInstructionIdFrameAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) {
|
||||||
|
return IrInstructionIdHandle;
|
||||||
|
}
|
||||||
|
|
||||||
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
|
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) {
|
||||||
return IrInstructionIdAlignOf;
|
return IrInstructionIdAlignOf;
|
||||||
}
|
}
|
||||||
@ -2240,6 +2244,17 @@ static IrInstruction *ir_build_frame_address_from(IrBuilder *irb, IrInstruction
|
|||||||
return new_instruction;
|
return new_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) {
|
||||||
|
IrInstructionHandle *instruction = ir_build_instruction<IrInstructionHandle>(irb, scope, source_node);
|
||||||
|
return &instruction->base;
|
||||||
|
}
|
||||||
|
|
||||||
|
static IrInstruction *ir_build_handle_from(IrBuilder *irb, IrInstruction *old_instruction) {
|
||||||
|
IrInstruction *new_instruction = ir_build_handle(irb, old_instruction->scope, old_instruction->source_node);
|
||||||
|
ir_link_new_instruction(new_instruction, old_instruction);
|
||||||
|
return new_instruction;
|
||||||
|
}
|
||||||
|
|
||||||
static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node,
|
||||||
IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
|
IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2,
|
||||||
IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
|
IrInstruction *result_ptr, TypeTableEntry *result_ptr_type)
|
||||||
@ -3843,6 +3858,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||||||
return irb->codegen->invalid_instruction;
|
return irb->codegen->invalid_instruction;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_async = exec_is_async(irb->exec);
|
||||||
|
|
||||||
switch (builtin_fn->id) {
|
switch (builtin_fn->id) {
|
||||||
case BuiltinFnIdInvalid:
|
case BuiltinFnIdInvalid:
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
@ -4475,6 +4492,16 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
|
|||||||
return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
|
return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval);
|
||||||
case BuiltinFnIdFrameAddress:
|
case BuiltinFnIdFrameAddress:
|
||||||
return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
|
return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval);
|
||||||
|
case BuiltinFnIdHandle:
|
||||||
|
if (!irb->exec->fn_entry) {
|
||||||
|
add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition"));
|
||||||
|
return irb->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
if (!is_async) {
|
||||||
|
add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function"));
|
||||||
|
return irb->codegen->invalid_instruction;
|
||||||
|
}
|
||||||
|
return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval);
|
||||||
case BuiltinFnIdAlignOf:
|
case BuiltinFnIdAlignOf:
|
||||||
{
|
{
|
||||||
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
|
||||||
@ -7069,19 +7096,8 @@ static IrInstruction *ir_gen_suspend(IrBuilder *irb, Scope *parent_scope, AstNod
|
|||||||
if (node->data.suspend.block == nullptr) {
|
if (node->data.suspend.block == nullptr) {
|
||||||
suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
|
suspend_code = ir_build_coro_suspend(irb, parent_scope, node, nullptr, const_bool_false);
|
||||||
} else {
|
} else {
|
||||||
assert(node->data.suspend.promise_symbol != nullptr);
|
|
||||||
assert(node->data.suspend.promise_symbol->type == NodeTypeSymbol);
|
|
||||||
Buf *promise_symbol_name = node->data.suspend.promise_symbol->data.symbol_expr.symbol;
|
|
||||||
Scope *child_scope;
|
Scope *child_scope;
|
||||||
if (!buf_eql_str(promise_symbol_name, "_")) {
|
ScopeSuspend *suspend_scope = create_suspend_scope(node, parent_scope);
|
||||||
VariableTableEntry *promise_var = ir_create_var(irb, node, parent_scope, promise_symbol_name,
|
|
||||||
true, true, false, const_bool_false);
|
|
||||||
ir_build_var_decl(irb, parent_scope, node, promise_var, nullptr, nullptr, irb->exec->coro_handle);
|
|
||||||
child_scope = promise_var->child_scope;
|
|
||||||
} else {
|
|
||||||
child_scope = parent_scope;
|
|
||||||
}
|
|
||||||
ScopeSuspend *suspend_scope = create_suspend_scope(node, child_scope);
|
|
||||||
suspend_scope->resume_block = resume_block;
|
suspend_scope->resume_block = resume_block;
|
||||||
child_scope = &suspend_scope->base;
|
child_scope = &suspend_scope->base;
|
||||||
IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
|
IrInstruction *save_token = ir_build_coro_save(irb, child_scope, node, irb->exec->coro_handle);
|
||||||
@ -19007,6 +19023,14 @@ static TypeTableEntry *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIn
|
|||||||
return u8_ptr_const;
|
return u8_ptr_const;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TypeTableEntry *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) {
|
||||||
|
ir_build_handle_from(&ira->new_irb, &instruction->base);
|
||||||
|
|
||||||
|
FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
|
||||||
|
assert(fn_entry != nullptr);
|
||||||
|
return get_promise_type(ira->codegen, fn_entry->type_entry->data.fn.fn_type_id.return_type);
|
||||||
|
}
|
||||||
|
|
||||||
static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
|
static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) {
|
||||||
IrInstruction *type_value = instruction->type_value->other;
|
IrInstruction *type_value = instruction->type_value->other;
|
||||||
if (type_is_invalid(type_value->value.type))
|
if (type_is_invalid(type_value->value.type))
|
||||||
@ -20982,6 +21006,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
|
|||||||
return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
|
return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction);
|
||||||
case IrInstructionIdFrameAddress:
|
case IrInstructionIdFrameAddress:
|
||||||
return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
|
return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction);
|
||||||
|
case IrInstructionIdHandle:
|
||||||
|
return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction);
|
||||||
case IrInstructionIdAlignOf:
|
case IrInstructionIdAlignOf:
|
||||||
return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
|
return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction);
|
||||||
case IrInstructionIdOverflowOp:
|
case IrInstructionIdOverflowOp:
|
||||||
@ -21274,6 +21300,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
|
|||||||
case IrInstructionIdAlignOf:
|
case IrInstructionIdAlignOf:
|
||||||
case IrInstructionIdReturnAddress:
|
case IrInstructionIdReturnAddress:
|
||||||
case IrInstructionIdFrameAddress:
|
case IrInstructionIdFrameAddress:
|
||||||
|
case IrInstructionIdHandle:
|
||||||
case IrInstructionIdTestErr:
|
case IrInstructionIdTestErr:
|
||||||
case IrInstructionIdUnwrapErrCode:
|
case IrInstructionIdUnwrapErrCode:
|
||||||
case IrInstructionIdOptionalWrap:
|
case IrInstructionIdOptionalWrap:
|
||||||
|
@ -791,6 +791,10 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst
|
|||||||
fprintf(irp->f, "@frameAddress()");
|
fprintf(irp->f, "@frameAddress()");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) {
|
||||||
|
fprintf(irp->f, "@handle()");
|
||||||
|
}
|
||||||
|
|
||||||
static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
|
static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) {
|
||||||
fprintf(irp->f, "@returnAddress()");
|
fprintf(irp->f, "@returnAddress()");
|
||||||
}
|
}
|
||||||
@ -1556,6 +1560,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
|
|||||||
case IrInstructionIdFrameAddress:
|
case IrInstructionIdFrameAddress:
|
||||||
ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
|
ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction);
|
||||||
break;
|
break;
|
||||||
|
case IrInstructionIdHandle:
|
||||||
|
ir_print_handle(irp, (IrInstructionHandle *)instruction);
|
||||||
|
break;
|
||||||
case IrInstructionIdAlignOf:
|
case IrInstructionIdAlignOf:
|
||||||
ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
|
ir_print_align_of(irp, (IrInstructionAlignOf *)instruction);
|
||||||
break;
|
break;
|
||||||
|
@ -648,12 +648,11 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, size_t *token_index, bool m
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SuspendExpression(body) = "suspend" option(("|" Symbol "|" body))
|
SuspendExpression(body) = "suspend" option( body )
|
||||||
*/
|
*/
|
||||||
static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
|
static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, bool mandatory) {
|
||||||
size_t orig_token_index = *token_index;
|
|
||||||
|
|
||||||
Token *suspend_token = &pc->tokens->at(*token_index);
|
Token *suspend_token = &pc->tokens->at(*token_index);
|
||||||
|
|
||||||
if (suspend_token->id == TokenIdKeywordSuspend) {
|
if (suspend_token->id == TokenIdKeywordSuspend) {
|
||||||
*token_index += 1;
|
*token_index += 1;
|
||||||
} else if (mandatory) {
|
} else if (mandatory) {
|
||||||
@ -663,23 +662,18 @@ static AstNode *ast_parse_suspend_block(ParseContext *pc, size_t *token_index, b
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Token *bar_token = &pc->tokens->at(*token_index);
|
Token *lbrace = &pc->tokens->at(*token_index);
|
||||||
if (bar_token->id == TokenIdBinOr) {
|
if (lbrace->id == TokenIdLBrace) {
|
||||||
*token_index += 1;
|
AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
|
||||||
|
node->data.suspend.block = ast_parse_block(pc, token_index, true);
|
||||||
|
return node;
|
||||||
} else if (mandatory) {
|
} else if (mandatory) {
|
||||||
ast_expect_token(pc, suspend_token, TokenIdBinOr);
|
ast_expect_token(pc, lbrace, TokenIdLBrace);
|
||||||
zig_unreachable();
|
zig_unreachable();
|
||||||
} else {
|
} else {
|
||||||
*token_index = orig_token_index;
|
*token_index -= 1;
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
AstNode *node = ast_create_node(pc, NodeTypeSuspend, suspend_token);
|
|
||||||
node->data.suspend.promise_symbol = ast_parse_symbol(pc, token_index);
|
|
||||||
ast_eat_token(pc, token_index, TokenIdBinOr);
|
|
||||||
node->data.suspend.block = ast_parse_block(pc, token_index, true);
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3134,7 +3128,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
|
|||||||
visit_field(&node->data.await_expr.expr, visit, context);
|
visit_field(&node->data.await_expr.expr, visit, context);
|
||||||
break;
|
break;
|
||||||
case NodeTypeSuspend:
|
case NodeTypeSuspend:
|
||||||
visit_field(&node->data.suspend.promise_symbol, visit, context);
|
|
||||||
visit_field(&node->data.suspend.block, visit, context);
|
visit_field(&node->data.suspend.block, visit, context);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -88,13 +88,11 @@ pub fn Channel(comptime T: type) type {
|
|||||||
/// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
|
/// buffer, or in the case of a zero size buffer, when the item has been retrieved by a getter.
|
||||||
pub async fn put(self: *SelfChannel, data: T) void {
|
pub async fn put(self: *SelfChannel, data: T) void {
|
||||||
// TODO fix this workaround
|
// TODO fix this workaround
|
||||||
var my_handle: promise = undefined;
|
suspend {
|
||||||
suspend |p| {
|
resume @handle();
|
||||||
my_handle = p;
|
|
||||||
resume p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var my_tick_node = Loop.NextTickNode.init(my_handle);
|
var my_tick_node = Loop.NextTickNode.init(@handle());
|
||||||
var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
|
var queue_node = std.atomic.Queue(PutNode).Node.init(PutNode{
|
||||||
.tick_node = &my_tick_node,
|
.tick_node = &my_tick_node,
|
||||||
.data = data,
|
.data = data,
|
||||||
@ -111,7 +109,7 @@ pub fn Channel(comptime T: type) type {
|
|||||||
self.dispatch();
|
self.dispatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
suspend |handle| {
|
suspend {
|
||||||
self.putters.put(&queue_node);
|
self.putters.put(&queue_node);
|
||||||
_ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
_ = @atomicRmw(usize, &self.put_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
||||||
|
|
||||||
@ -123,16 +121,14 @@ pub fn Channel(comptime T: type) type {
|
|||||||
/// complete when the next item is put in the channel.
|
/// complete when the next item is put in the channel.
|
||||||
pub async fn get(self: *SelfChannel) T {
|
pub async fn get(self: *SelfChannel) T {
|
||||||
// TODO fix this workaround
|
// TODO fix this workaround
|
||||||
var my_handle: promise = undefined;
|
suspend {
|
||||||
suspend |p| {
|
resume @handle();
|
||||||
my_handle = p;
|
|
||||||
resume p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO integrate this function with named return values
|
// TODO integrate this function with named return values
|
||||||
// so we can get rid of this extra result copy
|
// so we can get rid of this extra result copy
|
||||||
var result: T = undefined;
|
var result: T = undefined;
|
||||||
var my_tick_node = Loop.NextTickNode.init(my_handle);
|
var my_tick_node = Loop.NextTickNode.init(@handle());
|
||||||
var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
|
var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
|
||||||
.tick_node = &my_tick_node,
|
.tick_node = &my_tick_node,
|
||||||
.data = GetNode.Data{
|
.data = GetNode.Data{
|
||||||
@ -152,7 +148,7 @@ pub fn Channel(comptime T: type) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend |_| {
|
suspend {
|
||||||
self.getters.put(&queue_node);
|
self.getters.put(&queue_node);
|
||||||
_ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
_ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
||||||
|
|
||||||
@ -176,16 +172,14 @@ pub fn Channel(comptime T: type) type {
|
|||||||
/// for data and will not wait for data to be available.
|
/// for data and will not wait for data to be available.
|
||||||
pub async fn getOrNull(self: *SelfChannel) ?T {
|
pub async fn getOrNull(self: *SelfChannel) ?T {
|
||||||
// TODO fix this workaround
|
// TODO fix this workaround
|
||||||
var my_handle: promise = undefined;
|
suspend {
|
||||||
suspend |p| {
|
resume @handle();
|
||||||
my_handle = p;
|
|
||||||
resume p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO integrate this function with named return values
|
// TODO integrate this function with named return values
|
||||||
// so we can get rid of this extra result copy
|
// so we can get rid of this extra result copy
|
||||||
var result: ?T = null;
|
var result: ?T = null;
|
||||||
var my_tick_node = Loop.NextTickNode.init(my_handle);
|
var my_tick_node = Loop.NextTickNode.init(@handle());
|
||||||
var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
|
var or_null_node = std.atomic.Queue(*std.atomic.Queue(GetNode).Node).Node.init(undefined);
|
||||||
var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
|
var queue_node = std.atomic.Queue(GetNode).Node.init(GetNode{
|
||||||
.tick_node = &my_tick_node,
|
.tick_node = &my_tick_node,
|
||||||
@ -211,7 +205,7 @@ pub fn Channel(comptime T: type) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend |_| {
|
suspend {
|
||||||
self.getters.put(&queue_node);
|
self.getters.put(&queue_node);
|
||||||
_ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
_ = @atomicRmw(usize, &self.get_count, AtomicRmwOp.Add, 1, AtomicOrder.SeqCst);
|
||||||
self.or_null_queue.put(&or_null_node);
|
self.or_null_queue.put(&or_null_node);
|
||||||
|
@ -100,8 +100,8 @@ test "std.event.Future" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn testFuture(loop: *Loop) void {
|
async fn testFuture(loop: *Loop) void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
var future = Future(i32).init(loop);
|
var future = Future(i32).init(loop);
|
||||||
|
|
||||||
@ -115,15 +115,15 @@ async fn testFuture(loop: *Loop) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn waitOnFuture(future: *Future(i32)) i32 {
|
async fn waitOnFuture(future: *Future(i32)) i32 {
|
||||||
suspend |p| {
|
suspend {
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
return (await (async future.get() catch @panic("memory"))).*;
|
return (await (async future.get() catch @panic("memory"))).*;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn resolveFuture(future: *Future(i32)) void {
|
async fn resolveFuture(future: *Future(i32)) void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
future.data = 6;
|
future.data = 6;
|
||||||
future.resolve();
|
future.resolve();
|
||||||
|
@ -65,10 +65,10 @@ pub fn Group(comptime ReturnType: type) type {
|
|||||||
const S = struct {
|
const S = struct {
|
||||||
async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
|
async fn asyncFunc(node: **Stack.Node, args2: ...) ReturnType {
|
||||||
// TODO this is a hack to make the memory following be inside the coro frame
|
// TODO this is a hack to make the memory following be inside the coro frame
|
||||||
suspend |p| {
|
suspend {
|
||||||
var my_node: Stack.Node = undefined;
|
var my_node: Stack.Node = undefined;
|
||||||
node.* = &my_node;
|
node.* = &my_node;
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO this allocation elision should be guaranteed because we await it in
|
// TODO this allocation elision should be guaranteed because we await it in
|
||||||
|
@ -92,12 +92,10 @@ pub const Lock = struct {
|
|||||||
|
|
||||||
pub async fn acquire(self: *Lock) Held {
|
pub async fn acquire(self: *Lock) Held {
|
||||||
// TODO explicitly put this memory in the coroutine frame #1194
|
// TODO explicitly put this memory in the coroutine frame #1194
|
||||||
var my_handle: promise = undefined;
|
suspend {
|
||||||
suspend |p| {
|
resume @handle();
|
||||||
my_handle = p;
|
|
||||||
resume p;
|
|
||||||
}
|
}
|
||||||
var my_tick_node = Loop.NextTickNode.init(my_handle);
|
var my_tick_node = Loop.NextTickNode.init(@handle());
|
||||||
|
|
||||||
errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
|
errdefer _ = self.queue.remove(&my_tick_node); // TODO test canceling an acquire
|
||||||
suspend |_| {
|
suspend |_| {
|
||||||
@ -110,35 +108,12 @@ pub const Lock = struct {
|
|||||||
// will attempt to grab the lock.
|
// will attempt to grab the lock.
|
||||||
_ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
|
_ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
|
||||||
|
|
||||||
while (true) {
|
const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
||||||
const old_bit = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
if (old_bit == 0) {
|
||||||
if (old_bit != 0) {
|
|
||||||
// We did not obtain the lock. Trust that our queue entry will resume us, and allow
|
|
||||||
// suspend to complete.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// We got the lock. However we might have already been resumed from the queue.
|
|
||||||
if (self.queue.get()) |node| {
|
if (self.queue.get()) |node| {
|
||||||
// Whether this node is us or someone else, we tail resume it.
|
// Whether this node is us or someone else, we tail resume it.
|
||||||
resume node.data;
|
resume node.data;
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
// We already got resumed, and there are none left in the queue, which means that
|
|
||||||
// we aren't even supposed to hold the lock right now.
|
|
||||||
_ = @atomicRmw(u8, &self.queue_empty_bit, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst);
|
|
||||||
_ = @atomicRmw(u8, &self.shared_bit, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);
|
|
||||||
|
|
||||||
// There might be a queue item. If we know the queue is empty, we can be done,
|
|
||||||
// because the other actor will try to obtain the lock.
|
|
||||||
// But if there's a queue item, we are the actor which must loop and attempt
|
|
||||||
// to grab the lock again.
|
|
||||||
if (@atomicLoad(u8, &self.queue_empty_bit, AtomicOrder.SeqCst) == 1) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
unreachable;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,8 +143,8 @@ test "std.event.Lock" {
|
|||||||
|
|
||||||
async fn testLock(loop: *Loop, lock: *Lock) void {
|
async fn testLock(loop: *Loop, lock: *Lock) void {
|
||||||
// TODO explicitly put next tick node memory in the coroutine frame #1194
|
// TODO explicitly put next tick node memory in the coroutine frame #1194
|
||||||
suspend |p| {
|
suspend {
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
const handle1 = async lockRunner(lock) catch @panic("out of memory");
|
const handle1 = async lockRunner(lock) catch @panic("out of memory");
|
||||||
var tick_node1 = Loop.NextTickNode{
|
var tick_node1 = Loop.NextTickNode{
|
||||||
|
@ -354,11 +354,11 @@ pub const Loop = struct {
|
|||||||
|
|
||||||
pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
|
pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
|
||||||
defer self.linuxRemoveFd(fd);
|
defer self.linuxRemoveFd(fd);
|
||||||
suspend |p| {
|
suspend {
|
||||||
// TODO explicitly put this memory in the coroutine frame #1194
|
// TODO explicitly put this memory in the coroutine frame #1194
|
||||||
var resume_node = ResumeNode{
|
var resume_node = ResumeNode{
|
||||||
.id = ResumeNode.Id.Basic,
|
.id = ResumeNode.Id.Basic,
|
||||||
.handle = p,
|
.handle = @handle(),
|
||||||
};
|
};
|
||||||
try self.linuxAddFd(fd, &resume_node, flags);
|
try self.linuxAddFd(fd, &resume_node, flags);
|
||||||
}
|
}
|
||||||
@ -449,12 +449,12 @@ pub const Loop = struct {
|
|||||||
pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {
|
pub fn call(self: *Loop, comptime func: var, args: ...) !(promise->@typeOf(func).ReturnType) {
|
||||||
const S = struct {
|
const S = struct {
|
||||||
async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {
|
async fn asyncFunc(loop: *Loop, handle: *promise->@typeOf(func).ReturnType, args2: ...) @typeOf(func).ReturnType {
|
||||||
suspend |p| {
|
suspend {
|
||||||
handle.* = p;
|
handle.* = @handle();
|
||||||
var my_tick_node = Loop.NextTickNode{
|
var my_tick_node = Loop.NextTickNode{
|
||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
.next = undefined,
|
.next = undefined,
|
||||||
.data = p,
|
.data = @handle(),
|
||||||
};
|
};
|
||||||
loop.onNextTick(&my_tick_node);
|
loop.onNextTick(&my_tick_node);
|
||||||
}
|
}
|
||||||
@ -472,11 +472,11 @@ pub const Loop = struct {
|
|||||||
/// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
|
/// CPU bound tasks would be waiting in the event loop but never get started because no async I/O
|
||||||
/// is performed.
|
/// is performed.
|
||||||
pub async fn yield(self: *Loop) void {
|
pub async fn yield(self: *Loop) void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
var my_tick_node = Loop.NextTickNode{
|
var my_tick_node = Loop.NextTickNode{
|
||||||
.prev = undefined,
|
.prev = undefined,
|
||||||
.next = undefined,
|
.next = undefined,
|
||||||
.data = p,
|
.data = @handle(),
|
||||||
};
|
};
|
||||||
self.onNextTick(&my_tick_node);
|
self.onNextTick(&my_tick_node);
|
||||||
}
|
}
|
||||||
|
@ -88,8 +88,8 @@ pub const Server = struct {
|
|||||||
},
|
},
|
||||||
error.ProcessFdQuotaExceeded => {
|
error.ProcessFdQuotaExceeded => {
|
||||||
errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
|
errdefer std.os.emfile_promise_queue.remove(&self.waiting_for_emfile_node);
|
||||||
suspend |p| {
|
suspend {
|
||||||
self.waiting_for_emfile_node = PromiseNode.init(p);
|
self.waiting_for_emfile_node = PromiseNode.init( @handle() );
|
||||||
std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
|
std.os.emfile_promise_queue.append(&self.waiting_for_emfile_node);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
@ -141,8 +141,8 @@ test "listen on a port, send bytes, receive bytes" {
|
|||||||
(await next_handler) catch |err| {
|
(await next_handler) catch |err| {
|
||||||
std.debug.panic("unable to handle connection: {}\n", err);
|
std.debug.panic("unable to handle connection: {}\n", err);
|
||||||
};
|
};
|
||||||
suspend |p| {
|
suspend {
|
||||||
cancel p;
|
cancel @handle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
|
async fn errorableHandler(self: *Self, _addr: *const std.net.Address, _socket: *const std.os.File) !void {
|
||||||
|
@ -18,6 +18,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
OpenBrace,
|
OpenBrace,
|
||||||
CloseBrace,
|
CloseBrace,
|
||||||
FormatString,
|
FormatString,
|
||||||
|
Pointer,
|
||||||
};
|
};
|
||||||
|
|
||||||
comptime var start_index = 0;
|
comptime var start_index = 0;
|
||||||
@ -54,6 +55,7 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
state = State.Start;
|
state = State.Start;
|
||||||
start_index = i + 1;
|
start_index = i + 1;
|
||||||
},
|
},
|
||||||
|
'*' => state = State.Pointer,
|
||||||
else => {
|
else => {
|
||||||
state = State.FormatString;
|
state = State.FormatString;
|
||||||
},
|
},
|
||||||
@ -75,6 +77,17 @@ pub fn format(context: var, comptime Errors: type, output: fn (@typeOf(context),
|
|||||||
},
|
},
|
||||||
else => {},
|
else => {},
|
||||||
},
|
},
|
||||||
|
State.Pointer => switch (c) {
|
||||||
|
'}' => {
|
||||||
|
try output(context, @typeName(@typeOf(args[next_arg]).Child));
|
||||||
|
try output(context, "@");
|
||||||
|
try formatInt(@ptrToInt(args[next_arg]), 16, false, 0, context, Errors, output);
|
||||||
|
next_arg += 1;
|
||||||
|
state = State.Start;
|
||||||
|
start_index = i + 1;
|
||||||
|
},
|
||||||
|
else => @compileError("Unexpected format character after '*'"),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
comptime {
|
comptime {
|
||||||
@ -235,6 +248,11 @@ pub fn formatIntValue(
|
|||||||
return formatAsciiChar(value, context, Errors, output);
|
return formatAsciiChar(value, context, Errors, output);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'b' => {
|
||||||
|
radix = 2;
|
||||||
|
uppercase = false;
|
||||||
|
width = 0;
|
||||||
|
},
|
||||||
'd' => {
|
'd' => {
|
||||||
radix = 10;
|
radix = 10;
|
||||||
uppercase = false;
|
uppercase = false;
|
||||||
@ -861,6 +879,31 @@ test "fmt.format" {
|
|||||||
const value: u8 = 'a';
|
const value: u8 = 'a';
|
||||||
try testFmt("u8: a\n", "u8: {c}\n", value);
|
try testFmt("u8: a\n", "u8: {c}\n", value);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
const value: u8 = 0b1100;
|
||||||
|
try testFmt("u8: 0b1100\n", "u8: 0b{b}\n", value);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const value: [3]u8 = "abc";
|
||||||
|
try testFmt("array: abc\n", "array: {}\n", value);
|
||||||
|
try testFmt("array: abc\n", "array: {}\n", &value);
|
||||||
|
|
||||||
|
var buf: [100]u8 = undefined;
|
||||||
|
try testFmt(
|
||||||
|
try bufPrint(buf[0..], "array: [3]u8@{x}\n", @ptrToInt(&value)),
|
||||||
|
"array: {*}\n",
|
||||||
|
&value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const value: []const u8 = "abc";
|
||||||
|
try testFmt("slice: abc\n", "slice: {}\n", value);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const value = @intToPtr(*i32, 0xdeadbeef);
|
||||||
|
try testFmt("pointer: i32@deadbeef\n", "pointer: {}\n", value);
|
||||||
|
try testFmt("pointer: i32@deadbeef\n", "pointer: {*}\n", value);
|
||||||
|
}
|
||||||
try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
|
try testFmt("buf: Test \n", "buf: {s5}\n", "Test");
|
||||||
try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
|
try testFmt("buf: Test\n Other text", "buf: {s}\n Other text", "Test");
|
||||||
try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
|
try testFmt("cstr: Test C\n", "cstr: {s}\n", c"Test C");
|
||||||
|
@ -120,16 +120,10 @@ pub fn getRandomBytes(buf: []u8) !void {
|
|||||||
try posixRead(fd, buf);
|
try posixRead(fd, buf);
|
||||||
},
|
},
|
||||||
Os.windows => {
|
Os.windows => {
|
||||||
var hCryptProv: windows.HCRYPTPROV = undefined;
|
// Call RtlGenRandom() instead of CryptGetRandom() on Windows
|
||||||
if (windows.CryptAcquireContextA(&hCryptProv, null, null, windows.PROV_RSA_FULL, 0) == 0) {
|
// https://github.com/rust-lang-nursery/rand/issues/111
|
||||||
const err = windows.GetLastError();
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=504270
|
||||||
return switch (err) {
|
if (windows.RtlGenRandom(buf.ptr, buf.len) == 0) {
|
||||||
else => unexpectedErrorWindows(err),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
defer _ = windows.CryptReleaseContext(hCryptProv, 0);
|
|
||||||
|
|
||||||
if (windows.CryptGenRandom(hCryptProv, @intCast(windows.DWORD, buf.len), buf.ptr) == 0) {
|
|
||||||
const err = windows.GetLastError();
|
const err = windows.GetLastError();
|
||||||
return switch (err) {
|
return switch (err) {
|
||||||
else => unexpectedErrorWindows(err),
|
else => unexpectedErrorWindows(err),
|
||||||
@ -149,8 +143,14 @@ pub fn getRandomBytes(buf: []u8) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "os.getRandomBytes" {
|
test "os.getRandomBytes" {
|
||||||
var buf: [50]u8 = undefined;
|
var buf_a: [50]u8 = undefined;
|
||||||
try getRandomBytes(buf[0..]);
|
var buf_b: [50]u8 = undefined;
|
||||||
|
// Call Twice
|
||||||
|
try getRandomBytes(buf_a[0..]);
|
||||||
|
try getRandomBytes(buf_b[0..]);
|
||||||
|
|
||||||
|
// Check if random (not 100% conclusive)
|
||||||
|
assert( !mem.eql(u8, buf_a, buf_b) );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Raises a signal in the current kernel thread, ending its execution.
|
/// Raises a signal in the current kernel thread, ending its execution.
|
||||||
@ -2823,7 +2823,7 @@ pub fn cpuCount(fallback_allocator: *mem.Allocator) CpuCountError!usize {
|
|||||||
builtin.Os.macosx => {
|
builtin.Os.macosx => {
|
||||||
var count: c_int = undefined;
|
var count: c_int = undefined;
|
||||||
var count_len: usize = @sizeOf(c_int);
|
var count_len: usize = @sizeOf(c_int);
|
||||||
const rc = posix.sysctlbyname(c"hw.ncpu", @ptrCast(*c_void, &count), &count_len, null, 0);
|
const rc = posix.sysctlbyname(c"hw.logicalcpu", @ptrCast(*c_void, &count), &count_len, null, 0);
|
||||||
const err = posix.getErrno(rc);
|
const err = posix.getErrno(rc);
|
||||||
switch (err) {
|
switch (err) {
|
||||||
0 => return @intCast(usize, count),
|
0 => return @intCast(usize, count),
|
||||||
|
@ -28,3 +28,8 @@ pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR,
|
|||||||
|
|
||||||
pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
|
pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
|
||||||
lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
|
lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;
|
||||||
|
|
||||||
|
// RtlGenRandom is known as SystemFunction036 under advapi32
|
||||||
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx */
|
||||||
|
pub extern "advapi32" stdcallcc fn SystemFunction036(output: [*]u8, length: usize) BOOL;
|
||||||
|
pub const RtlGenRandom = SystemFunction036;
|
||||||
|
@ -166,7 +166,7 @@ pub fn windowsUnloadDll(hModule: windows.HMODULE) void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "InvalidDll" {
|
test "InvalidDll" {
|
||||||
if (builtin.os != builtin.Os.windows) return;
|
if (builtin.os != builtin.Os.windows) return error.SkipZigTest;
|
||||||
|
|
||||||
const DllName = "asdf.dll";
|
const DllName = "asdf.dll";
|
||||||
const allocator = std.debug.global_allocator;
|
const allocator = std.debug.global_allocator;
|
||||||
|
@ -1778,19 +1778,12 @@ pub const Node = struct {
|
|||||||
|
|
||||||
pub const Suspend = struct {
|
pub const Suspend = struct {
|
||||||
base: Node,
|
base: Node,
|
||||||
label: ?TokenIndex,
|
|
||||||
suspend_token: TokenIndex,
|
suspend_token: TokenIndex,
|
||||||
payload: ?*Node,
|
|
||||||
body: ?*Node,
|
body: ?*Node,
|
||||||
|
|
||||||
pub fn iterate(self: *Suspend, index: usize) ?*Node {
|
pub fn iterate(self: *Suspend, index: usize) ?*Node {
|
||||||
var i = index;
|
var i = index;
|
||||||
|
|
||||||
if (self.payload) |payload| {
|
|
||||||
if (i < 1) return payload;
|
|
||||||
i -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.body) |body| {
|
if (self.body) |body| {
|
||||||
if (i < 1) return body;
|
if (i < 1) return body;
|
||||||
i -= 1;
|
i -= 1;
|
||||||
@ -1800,7 +1793,6 @@ pub const Node = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn firstToken(self: *Suspend) TokenIndex {
|
pub fn firstToken(self: *Suspend) TokenIndex {
|
||||||
if (self.label) |label| return label;
|
|
||||||
return self.suspend_token;
|
return self.suspend_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1809,10 +1801,6 @@ pub const Node = struct {
|
|||||||
return body.lastToken();
|
return body.lastToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.payload) |payload| {
|
|
||||||
return payload.lastToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
return self.suspend_token;
|
return self.suspend_token;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -852,19 +852,6 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
|||||||
}) catch unreachable;
|
}) catch unreachable;
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
Token.Id.Keyword_suspend => {
|
|
||||||
const node = try arena.create(ast.Node.Suspend{
|
|
||||||
.base = ast.Node{ .id = ast.Node.Id.Suspend },
|
|
||||||
.label = ctx.label,
|
|
||||||
.suspend_token = token_index,
|
|
||||||
.payload = null,
|
|
||||||
.body = null,
|
|
||||||
});
|
|
||||||
ctx.opt_ctx.store(&node.base);
|
|
||||||
stack.append(State{ .SuspendBody = node }) catch unreachable;
|
|
||||||
try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } });
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
Token.Id.Keyword_inline => {
|
Token.Id.Keyword_inline => {
|
||||||
stack.append(State{
|
stack.append(State{
|
||||||
.Inline = InlineCtx{
|
.Inline = InlineCtx{
|
||||||
@ -1415,10 +1402,21 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
|
|||||||
},
|
},
|
||||||
|
|
||||||
State.SuspendBody => |suspend_node| {
|
State.SuspendBody => |suspend_node| {
|
||||||
if (suspend_node.payload != null) {
|
const token = nextToken(&tok_it, &tree);
|
||||||
try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } });
|
switch (token.ptr.id) {
|
||||||
|
Token.Id.Semicolon => {
|
||||||
|
prevToken(&tok_it, &tree);
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
Token.Id.LBrace => {
|
||||||
|
prevToken(&tok_it, &tree);
|
||||||
|
try stack.append(State{ .AssignmentExpressionBegin = OptionalCtx{ .RequiredNull = &suspend_node.body } });
|
||||||
|
continue;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
((try tree.errors.addOne())).* = Error{ .InvalidToken = Error.InvalidToken{ .token = token.index } };
|
||||||
|
},
|
||||||
}
|
}
|
||||||
continue;
|
|
||||||
},
|
},
|
||||||
State.AsyncAllocator => |async_node| {
|
State.AsyncAllocator => |async_node| {
|
||||||
if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) {
|
if (eatToken(&tok_it, &tree, Token.Id.AngleBracketLeft) == null) {
|
||||||
@ -3086,15 +3084,12 @@ fn parseBlockExpr(stack: *std.ArrayList(State), arena: *mem.Allocator, ctx: *con
|
|||||||
Token.Id.Keyword_suspend => {
|
Token.Id.Keyword_suspend => {
|
||||||
const node = try arena.create(ast.Node.Suspend{
|
const node = try arena.create(ast.Node.Suspend{
|
||||||
.base = ast.Node{ .id = ast.Node.Id.Suspend },
|
.base = ast.Node{ .id = ast.Node.Id.Suspend },
|
||||||
.label = null,
|
|
||||||
.suspend_token = token_index,
|
.suspend_token = token_index,
|
||||||
.payload = null,
|
|
||||||
.body = null,
|
.body = null,
|
||||||
});
|
});
|
||||||
ctx.store(&node.base);
|
ctx.store(&node.base);
|
||||||
|
|
||||||
stack.append(State{ .SuspendBody = node }) catch unreachable;
|
stack.append(State{ .SuspendBody = node }) catch unreachable;
|
||||||
try stack.append(State{ .Payload = OptionalCtx{ .Optional = &node.payload } });
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
Token.Id.Keyword_if => {
|
Token.Id.Keyword_if => {
|
||||||
|
@ -898,11 +898,11 @@ test "zig fmt: union(enum(u32)) with assigned enum values" {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "zig fmt: labeled suspend" {
|
test "zig fmt: resume from suspend block" {
|
||||||
try testCanonical(
|
try testCanonical(
|
||||||
\\fn foo() void {
|
\\fn foo() void {
|
||||||
\\ s: suspend |p| {
|
\\ suspend {
|
||||||
\\ break :s;
|
\\ resume @handle();
|
||||||
\\ }
|
\\ }
|
||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
@ -1784,7 +1784,7 @@ test "zig fmt: coroutines" {
|
|||||||
\\ x += 1;
|
\\ x += 1;
|
||||||
\\ suspend;
|
\\ suspend;
|
||||||
\\ x += 1;
|
\\ x += 1;
|
||||||
\\ suspend |p| {}
|
\\ suspend;
|
||||||
\\ const p: promise->void = async simpleAsyncFn() catch unreachable;
|
\\ const p: promise->void = async simpleAsyncFn() catch unreachable;
|
||||||
\\ await p;
|
\\ await p;
|
||||||
\\}
|
\\}
|
||||||
|
@ -323,21 +323,7 @@ fn renderExpression(
|
|||||||
ast.Node.Id.Suspend => {
|
ast.Node.Id.Suspend => {
|
||||||
const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
|
const suspend_node = @fieldParentPtr(ast.Node.Suspend, "base", base);
|
||||||
|
|
||||||
if (suspend_node.label) |label| {
|
if (suspend_node.body) |body| {
|
||||||
try renderToken(tree, stream, label, indent, start_col, Space.None);
|
|
||||||
try renderToken(tree, stream, tree.nextToken(label), indent, start_col, Space.Space);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (suspend_node.payload) |payload| {
|
|
||||||
if (suspend_node.body) |body| {
|
|
||||||
try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);
|
|
||||||
try renderExpression(allocator, stream, tree, indent, start_col, payload, Space.Space);
|
|
||||||
return renderExpression(allocator, stream, tree, indent, start_col, body, space);
|
|
||||||
} else {
|
|
||||||
try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);
|
|
||||||
return renderExpression(allocator, stream, tree, indent, start_col, payload, space);
|
|
||||||
}
|
|
||||||
} else if (suspend_node.body) |body| {
|
|
||||||
try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);
|
try renderToken(tree, stream, suspend_node.suspend_token, indent, start_col, Space.Space);
|
||||||
return renderExpression(allocator, stream, tree, indent, start_col, body, space);
|
return renderExpression(allocator, stream, tree, indent, start_col, body, space);
|
||||||
} else {
|
} else {
|
||||||
|
@ -85,8 +85,8 @@ async fn b4() void {
|
|||||||
defer {
|
defer {
|
||||||
defer_b4 = true;
|
defer_b4 = true;
|
||||||
}
|
}
|
||||||
suspend |p| {
|
suspend {
|
||||||
b4_handle = p;
|
b4_handle = @handle();
|
||||||
}
|
}
|
||||||
suspend;
|
suspend;
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ async fn await_amain() void {
|
|||||||
}
|
}
|
||||||
async fn await_another() Foo {
|
async fn await_another() Foo {
|
||||||
await_seq('c');
|
await_seq('c');
|
||||||
suspend |p| {
|
suspend {
|
||||||
await_seq('d');
|
await_seq('d');
|
||||||
await_a_promise = p;
|
await_a_promise = @handle();
|
||||||
}
|
}
|
||||||
await_seq('g');
|
await_seq('g');
|
||||||
return Foo{ .x = 1234 };
|
return Foo{ .x = 1234 };
|
||||||
|
@ -62,10 +62,15 @@ test "coroutine suspend with block" {
|
|||||||
var a_promise: promise = undefined;
|
var a_promise: promise = undefined;
|
||||||
var result = false;
|
var result = false;
|
||||||
async fn testSuspendBlock() void {
|
async fn testSuspendBlock() void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
comptime assert(@typeOf(p) == promise->void);
|
comptime assert(@typeOf(@handle()) == promise->void);
|
||||||
a_promise = p;
|
a_promise = @handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Test to make sure that @handle() works as advertised (issue #1296)
|
||||||
|
//var our_handle: promise = @handle();
|
||||||
|
assert( a_promise == @handle() );
|
||||||
|
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,9 +98,9 @@ async fn await_amain() void {
|
|||||||
}
|
}
|
||||||
async fn await_another() i32 {
|
async fn await_another() i32 {
|
||||||
await_seq('c');
|
await_seq('c');
|
||||||
suspend |p| {
|
suspend {
|
||||||
await_seq('d');
|
await_seq('d');
|
||||||
await_a_promise = p;
|
await_a_promise = @handle();
|
||||||
}
|
}
|
||||||
await_seq('g');
|
await_seq('g');
|
||||||
return 1234;
|
return 1234;
|
||||||
@ -244,8 +249,8 @@ test "break from suspend" {
|
|||||||
std.debug.assert(my_result == 2);
|
std.debug.assert(my_result == 2);
|
||||||
}
|
}
|
||||||
async fn testBreakFromSuspend(my_result: *i32) void {
|
async fn testBreakFromSuspend(my_result: *i32) void {
|
||||||
suspend |p| {
|
suspend {
|
||||||
resume p;
|
resume @handle();
|
||||||
}
|
}
|
||||||
my_result.* += 1;
|
my_result.* += 1;
|
||||||
suspend;
|
suspend;
|
||||||
|
@ -1,6 +1,27 @@
|
|||||||
const tests = @import("tests.zig");
|
const tests = @import("tests.zig");
|
||||||
|
|
||||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||||
|
cases.add(
|
||||||
|
"@handle() called outside of function definition",
|
||||||
|
\\var handle_undef: promise = undefined;
|
||||||
|
\\var handle_dummy: promise = @handle();
|
||||||
|
\\export fn entry() bool {
|
||||||
|
\\ return handle_undef == handle_dummy;
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
".tmp_source.zig:2:29: error: @handle() called outside of function definition",
|
||||||
|
);
|
||||||
|
|
||||||
|
cases.add(
|
||||||
|
"@handle() in non-async function",
|
||||||
|
\\export fn entry() bool {
|
||||||
|
\\ var handle_undef: promise = undefined;
|
||||||
|
\\ return handle_undef == @handle();
|
||||||
|
\\}
|
||||||
|
,
|
||||||
|
".tmp_source.zig:3:28: error: @handle() in non-async function",
|
||||||
|
);
|
||||||
|
|
||||||
cases.add(
|
cases.add(
|
||||||
"while loop body expression ignored",
|
"while loop body expression ignored",
|
||||||
\\fn returns() usize {
|
\\fn returns() usize {
|
||||||
@ -367,8 +388,8 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
|||||||
\\}
|
\\}
|
||||||
\\
|
\\
|
||||||
\\async fn foo() void {
|
\\async fn foo() void {
|
||||||
\\ suspend |p| {
|
\\ suspend {
|
||||||
\\ suspend |p1| {
|
\\ suspend {
|
||||||
\\ }
|
\\ }
|
||||||
\\ }
|
\\ }
|
||||||
\\}
|
\\}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user