authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-20 19:09:52-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-20 19:09:52-04:00
log5f3d59f0ac78e01bd50419dab54d7fcbae15f17c
tree59313f6debbbaafc18dafc5dfed6a479cede8cef
parentc39bb3ebc49096af45f3a69d4742e5f4d50cab62
parent3b5a8858c29582daf37856534abe150b568a7bb7
signature Commit is signed but in an unrecognized format.

Merge branch 'master' into llvm9


29 files changed, 728 insertions(+), 208 deletions(-)

doc/langref.html.in+16-4
...@@ -6379,7 +6379,7 @@ comptime {...@@ -6379,7 +6379,7 @@ comptime {
6379 {#header_close#}6379 {#header_close#}
63806380
6381 {#header_open|@asyncCall#}6381 {#header_open|@asyncCall#}
6382 <pre>{#syntax#}@asyncCall(frame_buffer: []u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>6382 <pre>{#syntax#}@asyncCall(frame_buffer: []align(@alignOf(@Frame(anyAsyncFunction))) u8, result_ptr, function_ptr, args: ...) anyframe->T{#endsyntax#}</pre>
6383 <p>6383 <p>
6384 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,6384 {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer,
6385 which may or may not be an {#link|async function|Async Functions#}.6385 which may or may not be an {#link|async function|Async Functions#}.
...@@ -6405,7 +6405,7 @@ test "async fn pointer in a struct field" {...@@ -6405,7 +6405,7 @@ test "async fn pointer in a struct field" {
6405 bar: async fn (*i32) void,6405 bar: async fn (*i32) void,
6406 };6406 };
6407 var foo = Foo{ .bar = func };6407 var foo = Foo{ .bar = func };
6408 var bytes: [64]u8 = undefined;6408 var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined;
6409 const f = @asyncCall(&bytes, {}, foo.bar, &data);6409 const f = @asyncCall(&bytes, {}, foo.bar, &data);
6410 assert(data == 2);6410 assert(data == 2);
6411 resume f;6411 resume f;
...@@ -7322,17 +7322,22 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>...@@ -7322,17 +7322,22 @@ mem.set(u8, dest, c);{#endsyntax#}</pre>
7322 {#header_close#}7322 {#header_close#}
73237323
7324 {#header_open|@newStackCall#}7324 {#header_open|@newStackCall#}
7325 <pre>{#syntax#}@newStackCall(new_stack: []u8, function: var, args: ...) var{#endsyntax#}</pre>7325 <pre>{#syntax#}@newStackCall(new_stack: []align(target_stack_align) u8, function: var, args: ...) var{#endsyntax#}</pre>
7326 <p>7326 <p>
7327 This calls a function, in the same way that invoking an expression with parentheses does. However,7327 This calls a function, in the same way that invoking an expression with parentheses does. However,
7328 instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#}7328 instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#}
7329 parameter.7329 parameter.
7330 </p>7330 </p>
7331 <p>
7332 The new stack must be aligned to {#syntax#}target_stack_align{#endsyntax#} bytes. This is a target-specific
7333 number. A safe value that will work on all targets is {#syntax#}16{#endsyntax#}. This value can
7334 also be obtained by using {#link|@sizeOf#} on the {#link|@Frame#} type of {#link|Async Functions#}.
7335 </p>
7331 {#code_begin|test#}7336 {#code_begin|test#}
7332const std = @import("std");7337const std = @import("std");
7333const assert = std.debug.assert;7338const assert = std.debug.assert;
73347339
7335var new_stack_bytes: [1024]u8 = undefined;7340var new_stack_bytes: [1024]u8 align(16) = undefined;
73367341
7337test "calling a function with a new stack" {7342test "calling a function with a new stack" {
7338 const arg = 1234;7343 const arg = 1234;
...@@ -9318,6 +9323,13 @@ const c = @cImport({...@@ -9318,6 +9323,13 @@ const c = @cImport({
9318 <li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}9323 <li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#}
9319 please!</li>9324 please!</li>
9320 </ul>9325 </ul>
9326 <p>When a C pointer is pointing to a single struct (not an array), deference the C pointer to
9327 access to the struct's fields or member data. That syntax looks like
9328 this: </p>
9329 <p>{#syntax#}ptr_to_struct.*.struct_member{#endsyntax#}</p>
9330 <p>This is comparable to doing {#syntax#}->{#endsyntax#} in C.</p>
9331 <p> When a C pointer is pointing to an array of structs, the syntax reverts to this:</p>
9332 <p>{#syntax#}ptr_to_struct_array[index].struct_member{#endsyntax#}</p>
9321 {#header_close#}9333 {#header_close#}
93229334
9323 {#header_open|Exporting a C Library#}9335 {#header_open|Exporting a C Library#}
src/all_types.hpp+7
...@@ -1279,6 +1279,12 @@ struct ZigTypeOpaque {...@@ -1279,6 +1279,12 @@ struct ZigTypeOpaque {
1279struct ZigTypeFnFrame {1279struct ZigTypeFnFrame {
1280 ZigFn *fn;1280 ZigFn *fn;
1281 ZigType *locals_struct;1281 ZigType *locals_struct;
1282
1283 // This is set to the type that resolving the frame currently depends on, null if none.
1284 // It's for generating a helpful error message.
1285 ZigType *resolve_loop_type;
1286 AstNode *resolve_loop_src_node;
1287 bool reported_loop_err;
1282};1288};
12831289
1284struct ZigTypeAnyFrame {1290struct ZigTypeAnyFrame {
...@@ -1396,6 +1402,7 @@ struct ZigFn {...@@ -1396,6 +1402,7 @@ struct ZigFn {
1396 AstNode *set_cold_node;1402 AstNode *set_cold_node;
1397 const AstNode *inferred_async_node;1403 const AstNode *inferred_async_node;
1398 ZigFn *inferred_async_fn;1404 ZigFn *inferred_async_fn;
1405 AstNode *non_async_node;
13991406
1400 ZigList<GlobalExport> export_list;1407 ZigList<GlobalExport> export_list;
1401 ZigList<IrInstructionCallGen *> call_list;1408 ZigList<IrInstructionCallGen *> call_list;
src/analyze.cpp+52-4
...@@ -4144,8 +4144,15 @@ void semantic_analyze(CodeGen *g) {...@@ -4144,8 +4144,15 @@ void semantic_analyze(CodeGen *g) {
41444144
4145 // second pass over functions for detecting async4145 // second pass over functions for detecting async
4146 for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {4146 for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) {
4147 ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index);4147 ZigFn *fn = g->fn_defs.at(g->fn_defs_index);
4148 analyze_fn_async(g, fn_entry, true);4148 analyze_fn_async(g, fn, true);
4149 if (fn_is_async(fn) && fn->non_async_node != nullptr) {
4150 ErrorMsg *msg = add_node_error(g, fn->proto_node,
4151 buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name)));
4152 add_error_note(g, msg, fn->non_async_node,
4153 buf_sprintf("required to be non-async here"));
4154 add_async_error_notes(g, msg, fn);
4155 }
4149 }4156 }
4150}4157}
41514158
...@@ -5190,6 +5197,27 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {...@@ -5190,6 +5197,27 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) {
5190 return fn_type;5197 return fn_type;
5191}5198}
51925199
5200static void emit_error_notes_for_type_loop(CodeGen *g, ErrorMsg *msg, ZigType *stop_type,
5201 ZigType *ty, AstNode *src_node)
5202{
5203 ErrorMsg *note = add_error_note(g, msg, src_node,
5204 buf_sprintf("when analyzing type '%s' here", buf_ptr(&ty->name)));
5205 if (ty == stop_type)
5206 return;
5207 switch (ty->id) {
5208 case ZigTypeIdFnFrame: {
5209 ty->data.frame.reported_loop_err = true;
5210 ZigType *depending_type = ty->data.frame.resolve_loop_type;
5211 if (depending_type == nullptr)
5212 return;
5213 emit_error_notes_for_type_loop(g, note, stop_type,
5214 depending_type, ty->data.frame.resolve_loop_src_node);
5215 }
5216 default:
5217 return;
5218 }
5219}
5220
5193static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {5221static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
5194 Error err;5222 Error err;
51955223
...@@ -5199,6 +5227,20 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {...@@ -5199,6 +5227,20 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
5199 ZigFn *fn = frame_type->data.frame.fn;5227 ZigFn *fn = frame_type->data.frame.fn;
5200 assert(!fn->type_entry->data.fn.is_generic);5228 assert(!fn->type_entry->data.fn.is_generic);
52015229
5230 if (frame_type->data.frame.resolve_loop_type != nullptr) {
5231 if (!frame_type->data.frame.reported_loop_err) {
5232 frame_type->data.frame.reported_loop_err = true;
5233 ErrorMsg *msg = add_node_error(g, fn->proto_node,
5234 buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name)));
5235 emit_error_notes_for_type_loop(g, msg,
5236 frame_type,
5237 frame_type->data.frame.resolve_loop_type,
5238 frame_type->data.frame.resolve_loop_src_node);
5239 emit_error_notes_for_ref_stack(g, msg);
5240 }
5241 return ErrorSemanticAnalyzeFail;
5242 }
5243
5202 switch (fn->anal_state) {5244 switch (fn->anal_state) {
5203 case FnAnalStateInvalid:5245 case FnAnalStateInvalid:
5204 return ErrorSemanticAnalyzeFail;5246 return ErrorSemanticAnalyzeFail;
...@@ -5292,6 +5334,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {...@@ -5292,6 +5334,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
5292 return ErrorSemanticAnalyzeFail;5334 return ErrorSemanticAnalyzeFail;
5293 }5335 }
52945336
5337 ZigType *callee_frame_type = get_fn_frame_type(g, callee);
5338 frame_type->data.frame.resolve_loop_type = callee_frame_type;
5339 frame_type->data.frame.resolve_loop_src_node = call->base.source_node;
5340
5295 analyze_fn_body(g, callee);5341 analyze_fn_body(g, callee);
5296 if (callee->anal_state == FnAnalStateInvalid) {5342 if (callee->anal_state == FnAnalStateInvalid) {
5297 frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;5343 frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid;
...@@ -5301,8 +5347,6 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {...@@ -5301,8 +5347,6 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
5301 if (!fn_is_async(callee))5347 if (!fn_is_async(callee))
5302 continue;5348 continue;
53035349
5304 ZigType *callee_frame_type = get_fn_frame_type(g, callee);
5305
5306 IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);5350 IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1);
5307 alloca_gen->base.id = IrInstructionIdAllocaGen;5351 alloca_gen->base.id = IrInstructionIdAllocaGen;
5308 alloca_gen->base.source_node = call->base.source_node;5352 alloca_gen->base.source_node = call->base.source_node;
...@@ -5371,9 +5415,13 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {...@@ -5371,9 +5415,13 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) {
5371 continue;5415 continue;
5372 }5416 }
5373 }5417 }
5418
5419 frame_type->data.frame.resolve_loop_type = child_type;
5420 frame_type->data.frame.resolve_loop_src_node = instruction->base.source_node;
5374 if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {5421 if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) {
5375 return err;5422 return err;
5376 }5423 }
5424
5377 const char *name;5425 const char *name;
5378 if (*instruction->name_hint == 0) {5426 if (*instruction->name_hint == 0) {
5379 name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));5427 name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i));
src/ir.cpp+128-13
...@@ -6743,6 +6743,25 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t...@@ -6743,6 +6743,25 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t
6743 return ErrorNone;6743 return ErrorNone;
6744}6744}
67456745
6746static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) {
6747 const char *ptr = buf_ptr(src_template) + tok->start + 2;
6748 size_t len = tok->end - tok->start - 2;
6749 size_t result = 0;
6750 for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) {
6751 AsmOutput *asm_output = node->data.asm_expr.output_list.at(i);
6752 if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) {
6753 return result;
6754 }
6755 }
6756 for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) {
6757 AsmInput *asm_input = node->data.asm_expr.input_list.at(i);
6758 if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) {
6759 return result;
6760 }
6761 }
6762 return SIZE_MAX;
6763}
6764
6746static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {6765static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
6747 Error err;6766 Error err;
6748 assert(node->type == NodeTypeAsmExpr);6767 assert(node->type == NodeTypeAsmExpr);
...@@ -6830,6 +6849,22 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod...@@ -6830,6 +6849,22 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod
6830 input_list[i] = input_value;6849 input_list[i] = input_value;
6831 }6850 }
68326851
6852 for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) {
6853 AsmToken asm_token = tok_list.at(token_i);
6854 if (asm_token.id == AsmTokenIdVar) {
6855 size_t index = find_asm_index(irb->codegen, node, &asm_token, template_buf);
6856 if (index == SIZE_MAX) {
6857 const char *ptr = buf_ptr(template_buf) + asm_token.start + 2;
6858 uint32_t len = asm_token.end - asm_token.start - 2;
6859
6860 add_node_error(irb->codegen, node,
6861 buf_sprintf("could not find '%.*s' in the inputs or outputs.",
6862 len, ptr));
6863 return irb->codegen->invalid_instruction;
6864 }
6865 }
6866 }
6867
6833 return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,6868 return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length,
6834 input_list, output_types, output_vars, return_count, is_volatile);6869 input_list, output_types, output_vars, return_count, is_volatile);
6835}6870}
...@@ -9485,10 +9520,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -9485,10 +9520,6 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
9485 result.id = ConstCastResultIdFnAlign;9520 result.id = ConstCastResultIdFnAlign;
9486 return result;9521 return result;
9487 }9522 }
9488 if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
9489 result.id = ConstCastResultIdFnCC;
9490 return result;
9491 }
9492 if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {9523 if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) {
9493 result.id = ConstCastResultIdFnVarArgs;9524 result.id = ConstCastResultIdFnVarArgs;
9494 return result;9525 return result;
...@@ -9546,6 +9577,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -9546,6 +9577,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
9546 return result;9577 return result;
9547 }9578 }
9548 }9579 }
9580 if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) {
9581 // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
9582 result.id = ConstCastResultIdFnCC;
9583 return result;
9584 }
9549 return result;9585 return result;
9550 }9586 }
95519587
...@@ -11780,8 +11816,11 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa...@@ -11780,8 +11816,11 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa
11780 add_error_note(ira->codegen, parent_msg, source_node,11816 add_error_note(ira->codegen, parent_msg, source_node,
11781 buf_sprintf("only one of the functions is generic"));11817 buf_sprintf("only one of the functions is generic"));
11782 break;11818 break;
11819 case ConstCastResultIdFnCC:
11820 add_error_note(ira->codegen, parent_msg, source_node,
11821 buf_sprintf("calling convention mismatch"));
11822 break;
11783 case ConstCastResultIdFnAlign: // TODO11823 case ConstCastResultIdFnAlign: // TODO
11784 case ConstCastResultIdFnCC: // TODO
11785 case ConstCastResultIdFnVarArgs: // TODO11824 case ConstCastResultIdFnVarArgs: // TODO
11786 case ConstCastResultIdFnReturnType: // TODO11825 case ConstCastResultIdFnReturnType: // TODO
11787 case ConstCastResultIdFnArgCount: // TODO11826 case ConstCastResultIdFnArgCount: // TODO
...@@ -11891,6 +11930,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -11891,6 +11930,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
11891 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);11930 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
11892 }11931 }
1189311932
11933 if (const_cast_result.id == ConstCastResultIdFnCC) {
11934 ir_assert(value->value.type->id == ZigTypeIdFn, source_instr);
11935 // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok.
11936 if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync &&
11937 actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified)
11938 {
11939 ir_assert(value->value.data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
11940 ZigFn *fn = value->value.data.x_ptr.data.fn.fn_entry;
11941 if (fn->inferred_async_node == nullptr) {
11942 fn->inferred_async_node = source_instr->source_node;
11943 }
11944 return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop);
11945 }
11946 }
11947
11894 // cast from T to ?T11948 // cast from T to ?T
11895 // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism11949 // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism
11896 if (wanted_type->id == ZigTypeIdOptional) {11950 if (wanted_type->id == ZigTypeIdOptional) {
...@@ -12110,7 +12164,26 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst...@@ -12110,7 +12164,26 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst
12110 array_type->data.array.child_type, source_node,12164 array_type->data.array.child_type, source_node,
12111 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)12165 !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk)
12112 {12166 {
12113 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);12167 // If the pointers both have ABI align, it works.
12168 bool ok_align = slice_ptr_type->data.pointer.explicit_alignment == 0 &&
12169 actual_type->data.pointer.explicit_alignment == 0;
12170 if (!ok_align) {
12171 // If either one has non ABI align, we have to resolve them both
12172 if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type,
12173 ResolveStatusAlignmentKnown)))
12174 {
12175 return ira->codegen->invalid_instruction;
12176 }
12177 if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type,
12178 ResolveStatusAlignmentKnown)))
12179 {
12180 return ira->codegen->invalid_instruction;
12181 }
12182 ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type);
12183 }
12184 if (ok_align) {
12185 return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, wanted_type, result_loc);
12186 }
12114 }12187 }
12115 }12188 }
1211612189
...@@ -13902,8 +13975,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *...@@ -13902,8 +13975,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *
13902 uint64_t old_array_len = array_type->data.array.len;13975 uint64_t old_array_len = array_type->data.array.len;
13903 uint64_t new_array_len;13976 uint64_t new_array_len;
1390413977
13905 if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len))13978 if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len)) {
13906 {
13907 ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));13979 ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow"));
13908 return ira->codegen->invalid_instruction;13980 return ira->codegen->invalid_instruction;
13909 }13981 }
...@@ -13918,6 +13990,15 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *...@@ -13918,6 +13990,15 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *
13918 return result;13990 return result;
13919 }13991 }
1392013992
13993 switch (type_has_one_possible_value(ira->codegen, result->value.type)) {
13994 case OnePossibleValueInvalid:
13995 return ira->codegen->invalid_instruction;
13996 case OnePossibleValueYes:
13997 return result;
13998 case OnePossibleValueNo:
13999 break;
14000 }
14001
13921 // TODO optimize the buf case14002 // TODO optimize the buf case
13922 expand_undef_array(ira->codegen, array_val);14003 expand_undef_array(ira->codegen, array_val);
13923 out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len);14004 out_val->data.x_array.data.s_none.elements = create_const_vals(new_array_len);
...@@ -13925,8 +14006,11 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *...@@ -13925,8 +14006,11 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp *
13925 uint64_t i = 0;14006 uint64_t i = 0;
13926 for (uint64_t x = 0; x < mult_amt; x += 1) {14007 for (uint64_t x = 0; x < mult_amt; x += 1) {
13927 for (uint64_t y = 0; y < old_array_len; y += 1) {14008 for (uint64_t y = 0; y < old_array_len; y += 1) {
13928 copy_const_val(&out_val->data.x_array.data.s_none.elements[i],14009 ConstExprValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i];
13929 &array_val->data.x_array.data.s_none.elements[y], false);14010 copy_const_val(elem_dest_val, &array_val->data.x_array.data.s_none.elements[y], false);
14011 elem_dest_val->parent.id = ConstParentIdArray;
14012 elem_dest_val->parent.data.p_array.array_val = out_val;
14013 elem_dest_val->parent.data.p_array.elem_index = i;
13930 i += 1;14014 i += 1;
13931 }14015 }
13932 }14016 }
...@@ -14753,6 +14837,9 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe...@@ -14753,6 +14837,9 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe
14753 return ira->codegen->invalid_instruction;14837 return ira->codegen->invalid_instruction;
14754 }14838 }
14755 uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);14839 uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type);
14840 if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) {
14841 return ira->codegen->invalid_instruction;
14842 }
14756 ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type,14843 ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type,
14757 parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,14844 parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle,
14758 parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);14845 parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero);
...@@ -15141,6 +15228,20 @@ no_mem_slot:...@@ -15141,6 +15228,20 @@ no_mem_slot:
15141 return var_ptr_instruction;15228 return var_ptr_instruction;
15142}15229}
1514315230
15231// This function is called when a comptime value becomes accessible at runtime.
15232static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *val) {
15233 ir_assert(value_is_comptime(val), source_instr);
15234 if (val->special == ConstValSpecialUndef)
15235 return;
15236
15237 if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) {
15238 ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr);
15239 if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) {
15240 val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node;
15241 }
15242 }
15243}
15244
15144static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,15245static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr,
15145 IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)15246 IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const)
15146{15247{
...@@ -15237,6 +15338,10 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source...@@ -15237,6 +15338,10 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source
15237 break;15338 break;
15238 }15339 }
1523915340
15341 if (instr_is_comptime(value)) {
15342 mark_comptime_value_escape(ira, source_instr, &value->value);
15343 }
15344
15240 IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,15345 IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope,
15241 source_instr->source_node, ptr, value);15346 source_instr->source_node, ptr, value);
15242 return &store_ptr->base;15347 return &store_ptr->base;
...@@ -15421,7 +15526,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c...@@ -15421,7 +15526,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c
15421 IrInstruction *casted_new_stack = nullptr;15526 IrInstruction *casted_new_stack = nullptr;
15422 if (call_instruction->new_stack != nullptr) {15527 if (call_instruction->new_stack != nullptr) {
15423 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,15528 ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8,
15424 false, false, PtrLenUnknown, 0, 0, 0, false);15529 false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false);
15425 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);15530 ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr);
15426 IrInstruction *new_stack = call_instruction->new_stack->child;15531 IrInstruction *new_stack = call_instruction->new_stack->child;
15427 if (type_is_invalid(new_stack->value.type))15532 if (type_is_invalid(new_stack->value.type))
...@@ -17123,7 +17228,7 @@ static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name,...@@ -17123,7 +17228,7 @@ static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name,
17123 buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",17228 buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code",
17124 buf_ptr(lib_name)));17229 buf_ptr(lib_name)));
17125 add_error_note(ira->codegen, msg, source_node,17230 add_error_note(ira->codegen, msg, source_node,
17126 buf_sprintf("fixed by `--library %s` or `--enable-pic`", buf_ptr(lib_name)));17231 buf_sprintf("fixed by `--library %s` or `-fPIC`", buf_ptr(lib_name)));
17127 ira->codegen->reported_bad_link_libc_error = true;17232 ira->codegen->reported_bad_link_libc_error = true;
17128 }17233 }
1712917234
...@@ -20841,6 +20946,12 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction...@@ -20841,6 +20946,12 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction
20841 if (!ir_resolve_atomic_order(ira, order_value, &order))20946 if (!ir_resolve_atomic_order(ira, order_value, &order))
20842 return ira->codegen->invalid_instruction;20947 return ira->codegen->invalid_instruction;
2084320948
20949 if (order < AtomicOrderAcquire) {
20950 ir_add_error(ira, order_value,
20951 buf_sprintf("atomic ordering must be Acquire or stricter"));
20952 return ira->codegen->invalid_instruction;
20953 }
20954
20844 IrInstruction *result = ir_build_fence(&ira->new_irb,20955 IrInstruction *result = ir_build_fence(&ira->new_irb,
20845 instruction->base.scope, instruction->base.source_node, order_value, order);20956 instruction->base.scope, instruction->base.source_node, order_value, order);
20846 result->value.type = ira->codegen->builtin_types.entry_void;20957 result->value.type = ira->codegen->builtin_types.entry_void;
...@@ -24491,7 +24602,11 @@ static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInst...@@ -24491,7 +24602,11 @@ static IrInstruction *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInst
24491 if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))24602 if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)))
24492 return result_loc;24603 return result_loc;
2449324604
24494 return instruction->result_loc_bit_cast->parent->gen_instruction;24605 if (instruction->result_loc_bit_cast->parent->gen_instruction != nullptr) {
24606 return instruction->result_loc_bit_cast->parent->gen_instruction;
24607 }
24608
24609 return result_loc;
24495}24610}
2449624611
24497static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira,24612static IrInstruction *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira,
src/link.cpp+1-1
...@@ -1755,7 +1755,7 @@ static void construct_linker_job_elf(LinkJob *lj) {...@@ -1755,7 +1755,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
17551755
17561756
1757 // libc dep1757 // libc dep
1758 if (g->libc_link_lib != nullptr) {1758 if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) {
1759 if (g->libc != nullptr) {1759 if (g->libc != nullptr) {
1760 if (!g->have_dynamic_link) {1760 if (!g->have_dynamic_link) {
1761 lj->args.append("--start-group");1761 lj->args.append("--start-group");
std/c.zig+3-1
...@@ -55,6 +55,7 @@ pub extern "c" fn fclose(stream: *FILE) c_int;...@@ -55,6 +55,7 @@ pub extern "c" fn fclose(stream: *FILE) c_int;
55pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;55pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
56pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;56pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize;
5757
58pub extern "c" fn printf(format: [*]const u8, ...) c_int;
58pub extern "c" fn abort() noreturn;59pub extern "c" fn abort() noreturn;
59pub extern "c" fn exit(code: c_int) noreturn;60pub extern "c" fn exit(code: c_int) noreturn;
60pub extern "c" fn isatty(fd: fd_t) c_int;61pub extern "c" fn isatty(fd: fd_t) c_int;
...@@ -64,10 +65,12 @@ pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int;...@@ -64,10 +65,12 @@ pub extern "c" fn fstat(fd: fd_t, buf: *Stat) c_int;
64pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;65pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int;
65pub extern "c" fn lseek(fd: fd_t, offset: isize, whence: c_int) isize;66pub extern "c" fn lseek(fd: fd_t, offset: isize, whence: c_int) isize;
66pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int;67pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int;
68pub extern "c" fn openat(fd: c_int, path: [*]const u8, oflag: c_uint, ...) c_int;
67pub extern "c" fn raise(sig: c_int) c_int;69pub extern "c" fn raise(sig: c_int) c_int;
68pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize;70pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize;
69pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize;71pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize;
70pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: usize) isize;72pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: usize) isize;
73pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) isize;
71pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: usize) isize;74pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: usize) isize;
72pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;75pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int;
73pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;76pub extern "c" fn write(fd: fd_t, buf: [*]const u8, nbyte: usize) isize;
...@@ -112,7 +115,6 @@ pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, fl...@@ -112,7 +115,6 @@ pub extern "c" fn accept4(sockfd: fd_t, addr: *sockaddr, addrlen: *socklen_t, fl
112pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;115pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int;
113pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;116pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int;
114pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;117pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize;
115pub extern "c" fn openat(fd: c_int, path: [*]const u8, flags: c_int) c_int;
116pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;118pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int;
117pub extern "c" fn setuid(uid: c_uint) c_int;119pub extern "c" fn setuid(uid: c_uint) c_int;
118pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;120pub extern "c" fn clock_gettime(clk_id: c_int, tp: *timespec) c_int;
std/c/freebsd.zig+1-1
...@@ -6,4 +6,4 @@ pub const _errno = __error;...@@ -6,4 +6,4 @@ pub const _errno = __error;
66
7pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;7pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize;
8pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;8pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int;
9pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;9pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
std/c/linux.zig+1-1
...@@ -7,7 +7,7 @@ pub const _errno = __errno_location;...@@ -7,7 +7,7 @@ pub const _errno = __errno_location;
77
8pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));8pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize));
99
10pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int;10pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize;
11pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;11pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int;
12pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;12pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int;
13pub extern "c" fn epoll_ctl(epfd: fd_t, op: c_uint, fd: fd_t, event: ?*epoll_event) c_int;13pub extern "c" fn epoll_ctl(epfd: fd_t, op: c_uint, fd: fd_t, event: ?*epoll_event) c_int;
std/event/fs.zig+89-46
...@@ -23,6 +23,7 @@ pub const Request = struct {...@@ -23,6 +23,7 @@ pub const Request = struct {
23 };23 };
2424
25 pub const Msg = union(enum) {25 pub const Msg = union(enum) {
26 WriteV: WriteV,
26 PWriteV: PWriteV,27 PWriteV: PWriteV,
27 PReadV: PReadV,28 PReadV: PReadV,
28 Open: Open,29 Open: Open,
...@@ -30,6 +31,14 @@ pub const Request = struct {...@@ -30,6 +31,14 @@ pub const Request = struct {
30 WriteFile: WriteFile,31 WriteFile: WriteFile,
31 End, // special - means the fs thread should exit32 End, // special - means the fs thread should exit
3233
34 pub const WriteV = struct {
35 fd: fd_t,
36 iov: []const os.iovec_const,
37 result: Error!void,
38
39 pub const Error = os.WriteError;
40 };
41
33 pub const PWriteV = struct {42 pub const PWriteV = struct {
34 fd: fd_t,43 fd: fd_t,
35 iov: []const os.iovec_const,44 iov: []const os.iovec_const,
...@@ -77,7 +86,7 @@ pub const Request = struct {...@@ -77,7 +86,7 @@ pub const Request = struct {
77pub const PWriteVError = error{OutOfMemory} || File.WriteError;86pub const PWriteVError = error{OutOfMemory} || File.WriteError;
7887
79/// data - just the inner references - must live until pwritev frame completes.88/// data - just the inner references - must live until pwritev frame completes.
80pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {89pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void {
81 switch (builtin.os) {90 switch (builtin.os) {
82 .macosx,91 .macosx,
83 .linux,92 .linux,
...@@ -94,31 +103,31 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us...@@ -94,31 +103,31 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us
94 };103 };
95 }104 }
96105
97 return await (async pwritevPosix(loop, fd, iovecs, offset) catch unreachable);106 return pwritevPosix(loop, fd, iovecs, offset);
98 },107 },
99 .windows => {108 .windows => {
100 const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);109 const data_copy = try std.mem.dupe(loop.allocator, []const u8, data);
101 defer loop.allocator.free(data_copy);110 defer loop.allocator.free(data_copy);
102 return await (async pwritevWindows(loop, fd, data, offset) catch unreachable);111 return pwritevWindows(loop, fd, data, offset);
103 },112 },
104 else => @compileError("Unsupported OS"),113 else => @compileError("Unsupported OS"),
105 }114 }
106}115}
107116
108/// data must outlive the returned frame117/// data must outlive the returned frame
109pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {118pub fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void {
110 if (data.len == 0) return;119 if (data.len == 0) return;
111 if (data.len == 1) return await (async pwriteWindows(loop, fd, data[0], offset) catch unreachable);120 if (data.len == 1) return pwriteWindows(loop, fd, data[0], offset);
112121
113 // TODO do these in parallel122 // TODO do these in parallel
114 var off = offset;123 var off = offset;
115 for (data) |buf| {124 for (data) |buf| {
116 try await (async pwriteWindows(loop, fd, buf, off) catch unreachable);125 try pwriteWindows(loop, fd, buf, off);
117 off += buf.len;126 off += buf.len;
118 }127 }
119}128}
120129
121pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {130pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void {
122 var resume_node = Loop.ResumeNode.Basic{131 var resume_node = Loop.ResumeNode.Basic{
123 .base = Loop.ResumeNode{132 .base = Loop.ResumeNode{
124 .id = Loop.ResumeNode.Id.Basic,133 .id = Loop.ResumeNode.Id.Basic,
...@@ -158,7 +167,7 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)...@@ -158,7 +167,7 @@ pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64)
158}167}
159168
160/// iovecs must live until pwritev frame completes.169/// iovecs must live until pwritev frame completes.
161pub async fn pwritevPosix(170pub fn pwritevPosix(
162 loop: *Loop,171 loop: *Loop,
163 fd: fd_t,172 fd: fd_t,
164 iovecs: []const os.iovec_const,173 iovecs: []const os.iovec_const,
...@@ -195,10 +204,44 @@ pub async fn pwritevPosix(...@@ -195,10 +204,44 @@ pub async fn pwritevPosix(
195 return req_node.data.msg.PWriteV.result;204 return req_node.data.msg.PWriteV.result;
196}205}
197206
207/// iovecs must live until pwritev frame completes.
208pub fn writevPosix(
209 loop: *Loop,
210 fd: fd_t,
211 iovecs: []const os.iovec_const,
212) os.WriteError!void {
213 var req_node = RequestNode{
214 .prev = null,
215 .next = null,
216 .data = Request{
217 .msg = Request.Msg{
218 .WriteV = Request.Msg.WriteV{
219 .fd = fd,
220 .iov = iovecs,
221 .result = undefined,
222 },
223 },
224 .finish = Request.Finish{
225 .TickNode = Loop.NextTickNode{
226 .prev = null,
227 .next = null,
228 .data = @frame(),
229 },
230 },
231 },
232 };
233
234 suspend {
235 loop.posixFsRequest(&req_node);
236 }
237
238 return req_node.data.msg.WriteV.result;
239}
240
198pub const PReadVError = error{OutOfMemory} || File.ReadError;241pub const PReadVError = error{OutOfMemory} || File.ReadError;
199242
200/// data - just the inner references - must live until preadv frame completes.243/// data - just the inner references - must live until preadv frame completes.
201pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {244pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize {
202 assert(data.len != 0);245 assert(data.len != 0);
203 switch (builtin.os) {246 switch (builtin.os) {
204 .macosx,247 .macosx,
...@@ -216,21 +259,21 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR...@@ -216,21 +259,21 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR
216 };259 };
217 }260 }
218261
219 return await (async preadvPosix(loop, fd, iovecs, offset) catch unreachable);262 return preadvPosix(loop, fd, iovecs, offset);
220 },263 },
221 .windows => {264 .windows => {
222 const data_copy = try std.mem.dupe(loop.allocator, []u8, data);265 const data_copy = try std.mem.dupe(loop.allocator, []u8, data);
223 defer loop.allocator.free(data_copy);266 defer loop.allocator.free(data_copy);
224 return await (async preadvWindows(loop, fd, data_copy, offset) catch unreachable);267 return preadvWindows(loop, fd, data_copy, offset);
225 },268 },
226 else => @compileError("Unsupported OS"),269 else => @compileError("Unsupported OS"),
227 }270 }
228}271}
229272
230/// data must outlive the returned frame273/// data must outlive the returned frame
231pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {274pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize {
232 assert(data.len != 0);275 assert(data.len != 0);
233 if (data.len == 1) return await (async preadWindows(loop, fd, data[0], offset) catch unreachable);276 if (data.len == 1) return preadWindows(loop, fd, data[0], offset);
234277
235 // TODO do these in parallel?278 // TODO do these in parallel?
236 var off: usize = 0;279 var off: usize = 0;
...@@ -238,7 +281,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6...@@ -238,7 +281,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6
238 var inner_off: usize = 0;281 var inner_off: usize = 0;
239 while (true) {282 while (true) {
240 const v = data[iov_i];283 const v = data[iov_i];
241 const amt_read = try await (async preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off) catch unreachable);284 const amt_read = try preadWindows(loop, fd, v[inner_off .. v.len - inner_off], offset + off);
242 off += amt_read;285 off += amt_read;
243 inner_off += amt_read;286 inner_off += amt_read;
244 if (inner_off == v.len) {287 if (inner_off == v.len) {
...@@ -252,7 +295,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6...@@ -252,7 +295,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6
252 }295 }
253}296}
254297
255pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {298pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize {
256 var resume_node = Loop.ResumeNode.Basic{299 var resume_node = Loop.ResumeNode.Basic{
257 .base = Loop.ResumeNode{300 .base = Loop.ResumeNode{
258 .id = Loop.ResumeNode.Id.Basic,301 .id = Loop.ResumeNode.Id.Basic,
...@@ -291,7 +334,7 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize...@@ -291,7 +334,7 @@ pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize
291}334}
292335
293/// iovecs must live until preadv frame completes336/// iovecs must live until preadv frame completes
294pub async fn preadvPosix(337pub fn preadvPosix(
295 loop: *Loop,338 loop: *Loop,
296 fd: fd_t,339 fd: fd_t,
297 iovecs: []const os.iovec,340 iovecs: []const os.iovec,
...@@ -328,7 +371,7 @@ pub async fn preadvPosix(...@@ -328,7 +371,7 @@ pub async fn preadvPosix(
328 return req_node.data.msg.PReadV.result;371 return req_node.data.msg.PReadV.result;
329}372}
330373
331pub async fn openPosix(374pub fn openPosix(
332 loop: *Loop,375 loop: *Loop,
333 path: []const u8,376 path: []const u8,
334 flags: u32,377 flags: u32,
...@@ -367,11 +410,11 @@ pub async fn openPosix(...@@ -367,11 +410,11 @@ pub async fn openPosix(
367 return req_node.data.msg.Open.result;410 return req_node.data.msg.Open.result;
368}411}
369412
370pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {413pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
371 switch (builtin.os) {414 switch (builtin.os) {
372 .macosx, .linux, .freebsd, .netbsd => {415 .macosx, .linux, .freebsd, .netbsd => {
373 const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;416 const flags = os.O_LARGEFILE | os.O_RDONLY | os.O_CLOEXEC;
374 return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable);417 return openPosix(loop, path, flags, File.default_mode);
375 },418 },
376419
377 .windows => return windows.CreateFile(420 .windows => return windows.CreateFile(
...@@ -390,12 +433,12 @@ pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {...@@ -390,12 +433,12 @@ pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t {
390433
391/// Creates if does not exist. Truncates the file if it exists.434/// Creates if does not exist. Truncates the file if it exists.
392/// Uses the default mode.435/// Uses the default mode.
393pub async fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t {436pub fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t {
394 return await (async openWriteMode(loop, path, File.default_mode) catch unreachable);437 return openWriteMode(loop, path, File.default_mode);
395}438}
396439
397/// Creates if does not exist. Truncates the file if it exists.440/// Creates if does not exist. Truncates the file if it exists.
398pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t {441pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t {
399 switch (builtin.os) {442 switch (builtin.os) {
400 .macosx,443 .macosx,
401 .linux,444 .linux,
...@@ -403,7 +446,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File....@@ -403,7 +446,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.
403 .netbsd,446 .netbsd,
404 => {447 => {
405 const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;448 const flags = os.O_LARGEFILE | os.O_WRONLY | os.O_CREAT | os.O_CLOEXEC | os.O_TRUNC;
406 return await (async openPosix(loop, path, flags, File.default_mode) catch unreachable);449 return openPosix(loop, path, flags, File.default_mode);
407 },450 },
408 .windows => return windows.CreateFile(451 .windows => return windows.CreateFile(
409 path,452 path,
...@@ -419,7 +462,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File....@@ -419,7 +462,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.
419}462}
420463
421/// Creates if does not exist. Does not truncate.464/// Creates if does not exist. Does not truncate.
422pub async fn openReadWrite(465pub fn openReadWrite(
423 loop: *Loop,466 loop: *Loop,
424 path: []const u8,467 path: []const u8,
425 mode: File.Mode,468 mode: File.Mode,
...@@ -427,7 +470,7 @@ pub async fn openReadWrite(...@@ -427,7 +470,7 @@ pub async fn openReadWrite(
427 switch (builtin.os) {470 switch (builtin.os) {
428 .macosx, .linux, .freebsd, .netbsd => {471 .macosx, .linux, .freebsd, .netbsd => {
429 const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC;472 const flags = os.O_LARGEFILE | os.O_RDWR | os.O_CREAT | os.O_CLOEXEC;
430 return await (async openPosix(loop, path, flags, mode) catch unreachable);473 return openPosix(loop, path, flags, mode);
431 },474 },
432475
433 .windows => return windows.CreateFile(476 .windows => return windows.CreateFile(
...@@ -576,24 +619,24 @@ pub const CloseOperation = struct {...@@ -576,24 +619,24 @@ pub const CloseOperation = struct {
576619
577/// contents must remain alive until writeFile completes.620/// contents must remain alive until writeFile completes.
578/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate621/// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate
579pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {622pub fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void {
580 return await (async writeFileMode(loop, path, contents, File.default_mode) catch unreachable);623 return writeFileMode(loop, path, contents, File.default_mode);
581}624}
582625
583/// contents must remain alive until writeFile completes.626/// contents must remain alive until writeFile completes.
584pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {627pub fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
585 switch (builtin.os) {628 switch (builtin.os) {
586 .linux,629 .linux,
587 .macosx,630 .macosx,
588 .freebsd,631 .freebsd,
589 .netbsd,632 .netbsd,
590 => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable),633 => return writeFileModeThread(loop, path, contents, mode),
591 .windows => return await (async writeFileWindows(loop, path, contents) catch unreachable),634 .windows => return writeFileWindows(loop, path, contents),
592 else => @compileError("Unsupported OS"),635 else => @compileError("Unsupported OS"),
593 }636 }
594}637}
595638
596async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {639fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void {
597 const handle = try windows.CreateFile(640 const handle = try windows.CreateFile(
598 path,641 path,
599 windows.GENERIC_WRITE,642 windows.GENERIC_WRITE,
...@@ -605,10 +648,10 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !...@@ -605,10 +648,10 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !
605 );648 );
606 defer os.close(handle);649 defer os.close(handle);
607650
608 try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable);651 try pwriteWindows(loop, handle, contents, 0);
609}652}
610653
611async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {654fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void {
612 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);655 const path_with_null = try std.cstr.addNullByte(loop.allocator, path);
613 defer loop.allocator.free(path_with_null);656 defer loop.allocator.free(path_with_null);
614657
...@@ -646,11 +689,11 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8...@@ -646,11 +689,11 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8
646/// The frame resumes when the last data has been confirmed written, but before the file handle689/// The frame resumes when the last data has been confirmed written, but before the file handle
647/// is closed.690/// is closed.
648/// Caller owns returned memory.691/// Caller owns returned memory.
649pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {692pub fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 {
650 var close_op = try CloseOperation.start(loop);693 var close_op = try CloseOperation.start(loop);
651 defer close_op.finish();694 defer close_op.finish();
652695
653 const fd = try await (async openRead(loop, file_path) catch unreachable);696 const fd = try openRead(loop, file_path);
654 close_op.setHandle(fd);697 close_op.setHandle(fd);
655698
656 var list = std.ArrayList(u8).init(loop.allocator);699 var list = std.ArrayList(u8).init(loop.allocator);
...@@ -660,7 +703,7 @@ pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8...@@ -660,7 +703,7 @@ pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8
660 try list.ensureCapacity(list.len + mem.page_size);703 try list.ensureCapacity(list.len + mem.page_size);
661 const buf = list.items[list.len..];704 const buf = list.items[list.len..];
662 const buf_array = [_][]u8{buf};705 const buf_array = [_][]u8{buf};
663 const amt = try await (async preadv(loop, fd, buf_array, list.len) catch unreachable);706 const amt = try preadv(loop, fd, buf_array, list.len);
664 list.len += amt;707 list.len += amt;
665 if (list.len > max_size) {708 if (list.len > max_size) {
666 return error.FileTooBig;709 return error.FileTooBig;
...@@ -1273,11 +1316,11 @@ const test_tmp_dir = "std_event_fs_test";...@@ -1273,11 +1316,11 @@ const test_tmp_dir = "std_event_fs_test";
1273// return result;1316// return result;
1274//}1317//}
12751318
1276async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {1319fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void {
1277 result.* = await (async testFsWatch(loop) catch unreachable);1320 result.* = testFsWatch(loop);
1278}1321}
12791322
1280async fn testFsWatch(loop: *Loop) !void {1323fn testFsWatch(loop: *Loop) !void {
1281 const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });1324 const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" });
1282 defer loop.allocator.free(file_path);1325 defer loop.allocator.free(file_path);
12831326
...@@ -1288,27 +1331,27 @@ async fn testFsWatch(loop: *Loop) !void {...@@ -1288,27 +1331,27 @@ async fn testFsWatch(loop: *Loop) !void {
1288 const line2_offset = 7;1331 const line2_offset = 7;
12891332
1290 // first just write then read the file1333 // first just write then read the file
1291 try await try async writeFile(loop, file_path, contents);1334 try writeFile(loop, file_path, contents);
12921335
1293 const read_contents = try await try async readFile(loop, file_path, 1024 * 1024);1336 const read_contents = try readFile(loop, file_path, 1024 * 1024);
1294 testing.expectEqualSlices(u8, contents, read_contents);1337 testing.expectEqualSlices(u8, contents, read_contents);
12951338
1296 // now watch the file1339 // now watch the file
1297 var watch = try Watch(void).create(loop, 0);1340 var watch = try Watch(void).create(loop, 0);
1298 defer watch.destroy();1341 defer watch.destroy();
12991342
1300 testing.expect((try await try async watch.addFile(file_path, {})) == null);1343 testing.expect((try watch.addFile(file_path, {})) == null);
13011344
1302 const ev = try async watch.channel.get();1345 const ev = async watch.channel.get();
1303 var ev_consumed = false;1346 var ev_consumed = false;
1304 defer if (!ev_consumed) await ev;1347 defer if (!ev_consumed) await ev;
13051348
1306 // overwrite line 21349 // overwrite line 2
1307 const fd = try await try async openReadWrite(loop, file_path, File.default_mode);1350 const fd = try await openReadWrite(loop, file_path, File.default_mode);
1308 {1351 {
1309 defer os.close(fd);1352 defer os.close(fd);
13101353
1311 try await try async pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);1354 try pwritev(loop, fd, []const []const u8{"lorem ipsum"}, line2_offset);
1312 }1355 }
13131356
1314 ev_consumed = true;1357 ev_consumed = true;
...@@ -1316,7 +1359,7 @@ async fn testFsWatch(loop: *Loop) !void {...@@ -1316,7 +1359,7 @@ async fn testFsWatch(loop: *Loop) !void {
1316 WatchEventId.CloseWrite => {},1359 WatchEventId.CloseWrite => {},
1317 WatchEventId.Delete => @panic("wrong event"),1360 WatchEventId.Delete => @panic("wrong event"),
1318 }1361 }
1319 const contents_updated = try await try async readFile(loop, file_path, 1024 * 1024);1362 const contents_updated = try readFile(loop, file_path, 1024 * 1024);
1320 testing.expectEqualSlices(u8,1363 testing.expectEqualSlices(u8,
1321 \\line 11364 \\line 1
1322 \\lorem ipsum1365 \\lorem ipsum
std/event/future.zig+5-6
...@@ -97,28 +97,27 @@ test "std.event.Future" {...@@ -97,28 +97,27 @@ test "std.event.Future" {
97 loop.run();97 loop.run();
98}98}
9999
100async fn testFuture(loop: *Loop) void {100fn testFuture(loop: *Loop) void {
101 var future = Future(i32).init(loop);101 var future = Future(i32).init(loop);
102102
103 var a = async waitOnFuture(&future);103 var a = async waitOnFuture(&future);
104 var b = async waitOnFuture(&future);104 var b = async waitOnFuture(&future);
105 var c = async resolveFuture(&future);105 resolveFuture(&future);
106106
107 // TODO make this work:107 // TODO https://github.com/ziglang/zig/issues/3077
108 //const result = (await a) + (await b);108 //const result = (await a) + (await b);
109 const a_result = await a;109 const a_result = await a;
110 const b_result = await b;110 const b_result = await b;
111 const result = a_result + b_result;111 const result = a_result + b_result;
112112
113 await c;
114 testing.expect(result == 12);113 testing.expect(result == 12);
115}114}
116115
117async fn waitOnFuture(future: *Future(i32)) i32 {116fn waitOnFuture(future: *Future(i32)) i32 {
118 return future.get().*;117 return future.get().*;
119}118}
120119
121async fn resolveFuture(future: *Future(i32)) void {120fn resolveFuture(future: *Future(i32)) void {
122 future.data = 6;121 future.data = 6;
123 future.resolve();122 future.resolve();
124}123}
std/event/loop.zig+16-8
...@@ -89,12 +89,15 @@ pub const Loop = struct {...@@ -89,12 +89,15 @@ pub const Loop = struct {
89 pub const IoMode = enum {89 pub const IoMode = enum {
90 blocking,90 blocking,
91 evented,91 evented,
92 mixed,
92 };93 };
93 pub const io_mode: IoMode = if (@hasDecl(root, "io_mode")) root.io_mode else IoMode.blocking;94 pub const io_mode: IoMode = if (@hasDecl(root, "io_mode")) root.io_mode else IoMode.blocking;
94 var global_instance_state: Loop = undefined;95 var global_instance_state: Loop = undefined;
96 threadlocal var per_thread_instance: ?*Loop = null;
95 const default_instance: ?*Loop = switch (io_mode) {97 const default_instance: ?*Loop = switch (io_mode) {
96 .blocking => null,98 .blocking => null,
97 .evented => &global_instance_state,99 .evented => &global_instance_state,
100 .mixed => per_thread_instance,
98 };101 };
99 pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;102 pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance;
100103
...@@ -146,10 +149,12 @@ pub const Loop = struct {...@@ -146,10 +149,12 @@ pub const Loop = struct {
146 .overlapped = ResumeNode.overlapped_init,149 .overlapped = ResumeNode.overlapped_init,
147 },150 },
148 };151 };
152 // We need at least one of these in case the fs thread wants to use onNextTick
149 const extra_thread_count = thread_count - 1;153 const extra_thread_count = thread_count - 1;
154 const resume_node_count = std.math.max(extra_thread_count, 1);
150 self.eventfd_resume_nodes = try self.allocator.alloc(155 self.eventfd_resume_nodes = try self.allocator.alloc(
151 std.atomic.Stack(ResumeNode.EventFd).Node,156 std.atomic.Stack(ResumeNode.EventFd).Node,
152 extra_thread_count,157 resume_node_count,
153 );158 );
154 errdefer self.allocator.free(self.eventfd_resume_nodes);159 errdefer self.allocator.free(self.eventfd_resume_nodes);
155160
...@@ -194,7 +199,7 @@ pub const Loop = struct {...@@ -194,7 +199,7 @@ pub const Loop = struct {
194 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{199 eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{
195 .data = ResumeNode.EventFd{200 .data = ResumeNode.EventFd{
196 .base = ResumeNode{201 .base = ResumeNode{
197 .id = ResumeNode.Id.EventFd,202 .id = .EventFd,
198 .handle = undefined,203 .handle = undefined,
199 .overlapped = ResumeNode.overlapped_init,204 .overlapped = ResumeNode.overlapped_init,
200 },205 },
...@@ -451,12 +456,12 @@ pub const Loop = struct {...@@ -451,12 +456,12 @@ pub const Loop = struct {
451 self.finishOneEvent();456 self.finishOneEvent();
452 }457 }
453458
454 pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {459 pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void {
455 defer self.linuxRemoveFd(fd);460 defer self.linuxRemoveFd(fd);
456 suspend {461 suspend {
457 var resume_node = ResumeNode.Basic{462 var resume_node = ResumeNode.Basic{
458 .base = ResumeNode{463 .base = ResumeNode{
459 .id = ResumeNode.Id.Basic,464 .id = .Basic,
460 .handle = @frame(),465 .handle = @frame(),
461 .overlapped = ResumeNode.overlapped_init,466 .overlapped = ResumeNode.overlapped_init,
462 },467 },
...@@ -790,12 +795,15 @@ pub const Loop = struct {...@@ -790,12 +795,15 @@ pub const Loop = struct {
790795
791 fn posixFsRun(self: *Loop) void {796 fn posixFsRun(self: *Loop) void {
792 while (true) {797 while (true) {
793 if (builtin.os == builtin.Os.linux) {798 if (builtin.os == .linux) {
794 _ = @atomicRmw(i32, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst);799 _ = @atomicRmw(i32, &self.os_data.fs_queue_item, .Xchg, 0, .SeqCst);
795 }800 }
796 while (self.os_data.fs_queue.get()) |node| {801 while (self.os_data.fs_queue.get()) |node| {
797 switch (node.data.msg) {802 switch (node.data.msg) {
798 .End => return,803 .End => return,
804 .WriteV => |*msg| {
805 msg.result = os.writev(msg.fd, msg.iov);
806 },
799 .PWriteV => |*msg| {807 .PWriteV => |*msg| {
800 msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);808 msg.result = os.pwritev(msg.fd, msg.iov, msg.offset);
801 },809 },
...@@ -827,14 +835,14 @@ pub const Loop = struct {...@@ -827,14 +835,14 @@ pub const Loop = struct {
827 self.finishOneEvent();835 self.finishOneEvent();
828 }836 }
829 switch (builtin.os) {837 switch (builtin.os) {
830 builtin.Os.linux => {838 .linux => {
831 const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);839 const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null);
832 switch (os.linux.getErrno(rc)) {840 switch (os.linux.getErrno(rc)) {
833 0, os.EINTR, os.EAGAIN => continue,841 0, os.EINTR, os.EAGAIN => continue,
834 else => unreachable,842 else => unreachable,
835 }843 }
836 },844 },
837 builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => {845 .macosx, .freebsd, .netbsd => {
838 const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait);846 const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait);
839 var out_kevs: [1]os.Kevent = undefined;847 var out_kevs: [1]os.Kevent = undefined;
840 _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;848 _ = os.kevent(self.os_data.fs_kqfd, fs_kevs, out_kevs[0..], null) catch unreachable;
std/fmt.zig+72-75
...@@ -69,7 +69,6 @@ pub fn format(...@@ -69,7 +69,6 @@ pub fn format(
69 FormatFillAndAlign,69 FormatFillAndAlign,
70 FormatWidth,70 FormatWidth,
71 FormatPrecision,71 FormatPrecision,
72 Pointer,
73 };72 };
7473
75 comptime var start_index = 0;74 comptime var start_index = 0;
...@@ -109,9 +108,6 @@ pub fn format(...@@ -109,9 +108,6 @@ pub fn format(
109 state = .Start;108 state = .Start;
110 start_index = i;109 start_index = i;
111 },110 },
112 '*' => {
113 state = .Pointer;
114 },
115 ':' => {111 ':' => {
116 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;112 state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth;
117 specifier_end = i;113 specifier_end = i;
...@@ -256,19 +252,6 @@ pub fn format(...@@ -256,19 +252,6 @@ pub fn format(
256 @compileError("Unexpected character in precision value: " ++ [_]u8{c});252 @compileError("Unexpected character in precision value: " ++ [_]u8{c});
257 },253 },
258 },254 },
259 .Pointer => switch (c) {
260 '}' => {
261 const arg_to_print = comptime nextArg(&used_pos_args, maybe_pos_arg, &next_arg);
262
263 try output(context, @typeName(@typeOf(args[arg_to_print]).Child));
264 try output(context, "@");
265 try formatInt(@ptrToInt(args[arg_to_print]), 16, false, 0, context, Errors, output);
266
267 state = .Start;
268 start_index = i + 1;
269 },
270 else => @compileError("Unexpected format character after '*'"),
271 },
272 }255 }
273 }256 }
274 comptime {257 comptime {
...@@ -293,12 +276,19 @@ pub fn format(...@@ -293,12 +276,19 @@ pub fn format(
293pub fn formatType(276pub fn formatType(
294 value: var,277 value: var,
295 comptime fmt: []const u8,278 comptime fmt: []const u8,
296 comptime options: FormatOptions,279 options: FormatOptions,
297 context: var,280 context: var,
298 comptime Errors: type,281 comptime Errors: type,
299 output: fn (@typeOf(context), []const u8) Errors!void,282 output: fn (@typeOf(context), []const u8) Errors!void,
300 max_depth: usize,283 max_depth: usize,
301) Errors!void {284) Errors!void {
285 if (comptime std.mem.eql(u8, fmt, "*")) {
286 try output(context, @typeName(@typeOf(value).Child));
287 try output(context, "@");
288 try formatInt(@ptrToInt(value), 16, false, FormatOptions{}, context, Errors, output);
289 return;
290 }
291
302 const T = @typeOf(value);292 const T = @typeOf(value);
303 switch (@typeInfo(T)) {293 switch (@typeInfo(T)) {
304 .ComptimeInt, .Int, .Float => {294 .ComptimeInt, .Int, .Float => {
...@@ -438,15 +428,15 @@ pub fn formatType(...@@ -438,15 +428,15 @@ pub fn formatType(
438fn formatValue(428fn formatValue(
439 value: var,429 value: var,
440 comptime fmt: []const u8,430 comptime fmt: []const u8,
441 comptime options: FormatOptions,431 options: FormatOptions,
442 context: var,432 context: var,
443 comptime Errors: type,433 comptime Errors: type,
444 output: fn (@typeOf(context), []const u8) Errors!void,434 output: fn (@typeOf(context), []const u8) Errors!void,
445) Errors!void {435) Errors!void {
446 if (comptime std.mem.eql(u8, fmt, "B")) {436 if (comptime std.mem.eql(u8, fmt, "B")) {
447 return formatBytes(value, options.width, 1000, context, Errors, output);437 return formatBytes(value, options, 1000, context, Errors, output);
448 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {438 } else if (comptime std.mem.eql(u8, fmt, "Bi")) {
449 return formatBytes(value, options.width, 1024, context, Errors, output);439 return formatBytes(value, options, 1024, context, Errors, output);
450 }440 }
451441
452 const T = @typeOf(value);442 const T = @typeOf(value);
...@@ -460,7 +450,7 @@ fn formatValue(...@@ -460,7 +450,7 @@ fn formatValue(
460pub fn formatIntValue(450pub fn formatIntValue(
461 value: var,451 value: var,
462 comptime fmt: []const u8,452 comptime fmt: []const u8,
463 comptime options: FormatOptions,453 options: FormatOptions,
464 context: var,454 context: var,
465 comptime Errors: type,455 comptime Errors: type,
466 output: fn (@typeOf(context), []const u8) Errors!void,456 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -479,7 +469,7 @@ pub fn formatIntValue(...@@ -479,7 +469,7 @@ pub fn formatIntValue(
479 uppercase = false;469 uppercase = false;
480 } else if (comptime std.mem.eql(u8, fmt, "c")) {470 } else if (comptime std.mem.eql(u8, fmt, "c")) {
481 if (@typeOf(int_value).bit_count <= 8) {471 if (@typeOf(int_value).bit_count <= 8) {
482 return formatAsciiChar(u8(int_value), context, Errors, output);472 return formatAsciiChar(u8(int_value), options, context, Errors, output);
483 } else {473 } else {
484 @compileError("Cannot print integer that is larger than 8 bits as a ascii");474 @compileError("Cannot print integer that is larger than 8 bits as a ascii");
485 }475 }
...@@ -496,21 +486,21 @@ pub fn formatIntValue(...@@ -496,21 +486,21 @@ pub fn formatIntValue(
496 @compileError("Unknown format string: '" ++ fmt ++ "'");486 @compileError("Unknown format string: '" ++ fmt ++ "'");
497 }487 }
498488
499 return formatInt(int_value, radix, uppercase, options.width orelse 0, context, Errors, output);489 return formatInt(int_value, radix, uppercase, options, context, Errors, output);
500}490}
501491
502fn formatFloatValue(492fn formatFloatValue(
503 value: var,493 value: var,
504 comptime fmt: []const u8,494 comptime fmt: []const u8,
505 comptime options: FormatOptions,495 options: FormatOptions,
506 context: var,496 context: var,
507 comptime Errors: type,497 comptime Errors: type,
508 output: fn (@typeOf(context), []const u8) Errors!void,498 output: fn (@typeOf(context), []const u8) Errors!void,
509) Errors!void {499) Errors!void {
510 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {500 if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "e")) {
511 return formatFloatScientific(value, options.precision, context, Errors, output);501 return formatFloatScientific(value, options, context, Errors, output);
512 } else if (comptime std.mem.eql(u8, fmt, "d")) {502 } else if (comptime std.mem.eql(u8, fmt, "d")) {
513 return formatFloatDecimal(value, options.precision, context, Errors, output);503 return formatFloatDecimal(value, options, context, Errors, output);
514 } else {504 } else {
515 @compileError("Unknown format string: '" ++ fmt ++ "'");505 @compileError("Unknown format string: '" ++ fmt ++ "'");
516 }506 }
...@@ -519,7 +509,7 @@ fn formatFloatValue(...@@ -519,7 +509,7 @@ fn formatFloatValue(
519pub fn formatText(509pub fn formatText(
520 bytes: []const u8,510 bytes: []const u8,
521 comptime fmt: []const u8,511 comptime fmt: []const u8,
522 comptime options: FormatOptions,512 options: FormatOptions,
523 context: var,513 context: var,
524 comptime Errors: type,514 comptime Errors: type,
525 output: fn (@typeOf(context), []const u8) Errors!void,515 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -527,11 +517,10 @@ pub fn formatText(...@@ -527,11 +517,10 @@ pub fn formatText(
527 if (fmt.len == 0) {517 if (fmt.len == 0) {
528 return output(context, bytes);518 return output(context, bytes);
529 } else if (comptime std.mem.eql(u8, fmt, "s")) {519 } else if (comptime std.mem.eql(u8, fmt, "s")) {
530 if (options.width) |w| return formatBuf(bytes, w, context, Errors, output);520 return formatBuf(bytes, options, context, Errors, output);
531 return formatBuf(bytes, 0, context, Errors, output);
532 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {521 } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) {
533 for (bytes) |c| {522 for (bytes) |c| {
534 try formatInt(c, 16, fmt[0] == 'X', 2, context, Errors, output);523 try formatInt(c, 16, fmt[0] == 'X', FormatOptions{ .width = 2, .fill = '0' }, context, Errors, output);
535 }524 }
536 return;525 return;
537 } else {526 } else {
...@@ -541,6 +530,7 @@ pub fn formatText(...@@ -541,6 +530,7 @@ pub fn formatText(
541530
542pub fn formatAsciiChar(531pub fn formatAsciiChar(
543 c: u8,532 c: u8,
533 options: FormatOptions,
544 context: var,534 context: var,
545 comptime Errors: type,535 comptime Errors: type,
546 output: fn (@typeOf(context), []const u8) Errors!void,536 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -550,15 +540,16 @@ pub fn formatAsciiChar(...@@ -550,15 +540,16 @@ pub fn formatAsciiChar(
550540
551pub fn formatBuf(541pub fn formatBuf(
552 buf: []const u8,542 buf: []const u8,
553 width: usize,543 options: FormatOptions,
554 context: var,544 context: var,
555 comptime Errors: type,545 comptime Errors: type,
556 output: fn (@typeOf(context), []const u8) Errors!void,546 output: fn (@typeOf(context), []const u8) Errors!void,
557) Errors!void {547) Errors!void {
558 try output(context, buf);548 try output(context, buf);
559549
550 const width = options.width orelse 0;
560 var leftover_padding = if (width > buf.len) (width - buf.len) else return;551 var leftover_padding = if (width > buf.len) (width - buf.len) else return;
561 const pad_byte: u8 = ' ';552 const pad_byte: u8 = options.fill;
562 while (leftover_padding > 0) : (leftover_padding -= 1) {553 while (leftover_padding > 0) : (leftover_padding -= 1) {
563 try output(context, (*const [1]u8)(&pad_byte)[0..1]);554 try output(context, (*const [1]u8)(&pad_byte)[0..1]);
564 }555 }
...@@ -569,7 +560,7 @@ pub fn formatBuf(...@@ -569,7 +560,7 @@ pub fn formatBuf(
569// same type unambiguously.560// same type unambiguously.
570pub fn formatFloatScientific(561pub fn formatFloatScientific(
571 value: var,562 value: var,
572 maybe_precision: ?usize,563 options: FormatOptions,
573 context: var,564 context: var,
574 comptime Errors: type,565 comptime Errors: type,
575 output: fn (@typeOf(context), []const u8) Errors!void,566 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -591,7 +582,7 @@ pub fn formatFloatScientific(...@@ -591,7 +582,7 @@ pub fn formatFloatScientific(
591 if (x == 0.0) {582 if (x == 0.0) {
592 try output(context, "0");583 try output(context, "0");
593584
594 if (maybe_precision) |precision| {585 if (options.precision) |precision| {
595 if (precision != 0) {586 if (precision != 0) {
596 try output(context, ".");587 try output(context, ".");
597 var i: usize = 0;588 var i: usize = 0;
...@@ -610,7 +601,7 @@ pub fn formatFloatScientific(...@@ -610,7 +601,7 @@ pub fn formatFloatScientific(
610 var buffer: [32]u8 = undefined;601 var buffer: [32]u8 = undefined;
611 var float_decimal = errol.errol3(x, buffer[0..]);602 var float_decimal = errol.errol3(x, buffer[0..]);
612603
613 if (maybe_precision) |precision| {604 if (options.precision) |precision| {
614 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);605 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific);
615606
616 try output(context, float_decimal.digits[0..1]);607 try output(context, float_decimal.digits[0..1]);
...@@ -650,13 +641,13 @@ pub fn formatFloatScientific(...@@ -650,13 +641,13 @@ pub fn formatFloatScientific(
650 if (exp > -10 and exp < 10) {641 if (exp > -10 and exp < 10) {
651 try output(context, "0");642 try output(context, "0");
652 }643 }
653 try formatInt(exp, 10, false, 0, context, Errors, output);644 try formatInt(exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output);
654 } else {645 } else {
655 try output(context, "-");646 try output(context, "-");
656 if (exp > -10 and exp < 10) {647 if (exp > -10 and exp < 10) {
657 try output(context, "0");648 try output(context, "0");
658 }649 }
659 try formatInt(-exp, 10, false, 0, context, Errors, output);650 try formatInt(-exp, 10, false, FormatOptions{ .width = 0 }, context, Errors, output);
660 }651 }
661}652}
662653
...@@ -664,7 +655,7 @@ pub fn formatFloatScientific(...@@ -664,7 +655,7 @@ pub fn formatFloatScientific(
664// By default floats are printed at full precision (no rounding).655// By default floats are printed at full precision (no rounding).
665pub fn formatFloatDecimal(656pub fn formatFloatDecimal(
666 value: var,657 value: var,
667 maybe_precision: ?usize,658 options: FormatOptions,
668 context: var,659 context: var,
669 comptime Errors: type,660 comptime Errors: type,
670 output: fn (@typeOf(context), []const u8) Errors!void,661 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -686,7 +677,7 @@ pub fn formatFloatDecimal(...@@ -686,7 +677,7 @@ pub fn formatFloatDecimal(
686 if (x == 0.0) {677 if (x == 0.0) {
687 try output(context, "0");678 try output(context, "0");
688679
689 if (maybe_precision) |precision| {680 if (options.precision) |precision| {
690 if (precision != 0) {681 if (precision != 0) {
691 try output(context, ".");682 try output(context, ".");
692 var i: usize = 0;683 var i: usize = 0;
...@@ -707,7 +698,7 @@ pub fn formatFloatDecimal(...@@ -707,7 +698,7 @@ pub fn formatFloatDecimal(
707 var buffer: [32]u8 = undefined;698 var buffer: [32]u8 = undefined;
708 var float_decimal = errol.errol3(x, buffer[0..]);699 var float_decimal = errol.errol3(x, buffer[0..]);
709700
710 if (maybe_precision) |precision| {701 if (options.precision) |precision| {
711 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);702 errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal);
712703
713 // exp < 0 means the leading is always 0 as errol result is normalized.704 // exp < 0 means the leading is always 0 as errol result is normalized.
...@@ -809,7 +800,7 @@ pub fn formatFloatDecimal(...@@ -809,7 +800,7 @@ pub fn formatFloatDecimal(
809800
810pub fn formatBytes(801pub fn formatBytes(
811 value: var,802 value: var,
812 width: ?usize,803 options: FormatOptions,
813 comptime radix: usize,804 comptime radix: usize,
814 context: var,805 context: var,
815 comptime Errors: type,806 comptime Errors: type,
...@@ -833,7 +824,7 @@ pub fn formatBytes(...@@ -833,7 +824,7 @@ pub fn formatBytes(
833 else => unreachable,824 else => unreachable,
834 };825 };
835826
836 try formatFloatDecimal(new_value, width, context, Errors, output);827 try formatFloatDecimal(new_value, options, context, Errors, output);
837828
838 if (suffix == ' ') {829 if (suffix == ' ') {
839 return output(context, "B");830 return output(context, "B");
...@@ -851,7 +842,7 @@ pub fn formatInt(...@@ -851,7 +842,7 @@ pub fn formatInt(
851 value: var,842 value: var,
852 base: u8,843 base: u8,
853 uppercase: bool,844 uppercase: bool,
854 width: usize,845 options: FormatOptions,
855 context: var,846 context: var,
856 comptime Errors: type,847 comptime Errors: type,
857 output: fn (@typeOf(context), []const u8) Errors!void,848 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -863,9 +854,9 @@ pub fn formatInt(...@@ -863,9 +854,9 @@ pub fn formatInt(
863 value;854 value;
864855
865 if (@typeOf(int_value).is_signed) {856 if (@typeOf(int_value).is_signed) {
866 return formatIntSigned(int_value, base, uppercase, width, context, Errors, output);857 return formatIntSigned(int_value, base, uppercase, options, context, Errors, output);
867 } else {858 } else {
868 return formatIntUnsigned(int_value, base, uppercase, width, context, Errors, output);859 return formatIntUnsigned(int_value, base, uppercase, options, context, Errors, output);
869 }860 }
870}861}
871862
...@@ -873,26 +864,30 @@ fn formatIntSigned(...@@ -873,26 +864,30 @@ fn formatIntSigned(
873 value: var,864 value: var,
874 base: u8,865 base: u8,
875 uppercase: bool,866 uppercase: bool,
876 width: usize,867 options: FormatOptions,
877 context: var,868 context: var,
878 comptime Errors: type,869 comptime Errors: type,
879 output: fn (@typeOf(context), []const u8) Errors!void,870 output: fn (@typeOf(context), []const u8) Errors!void,
880) Errors!void {871) Errors!void {
872 const new_options = FormatOptions{
873 .width = if (options.width) |w| (if (w == 0) 0 else w - 1) else null,
874 .precision = options.precision,
875 .fill = options.fill,
876 };
877
881 const uint = @IntType(false, @typeOf(value).bit_count);878 const uint = @IntType(false, @typeOf(value).bit_count);
882 if (value < 0) {879 if (value < 0) {
883 const minus_sign: u8 = '-';880 const minus_sign: u8 = '-';
884 try output(context, (*const [1]u8)(&minus_sign)[0..]);881 try output(context, (*const [1]u8)(&minus_sign)[0..]);
885 const new_value = @intCast(uint, -(value + 1)) + 1;882 const new_value = @intCast(uint, -(value + 1)) + 1;
886 const new_width = if (width == 0) 0 else (width - 1);883 return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output);
887 return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);884 } else if (options.width == null or options.width.? == 0) {
888 } else if (width == 0) {885 return formatIntUnsigned(@intCast(uint, value), base, uppercase, options, context, Errors, output);
889 return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output);
890 } else {886 } else {
891 const plus_sign: u8 = '+';887 const plus_sign: u8 = '+';
892 try output(context, (*const [1]u8)(&plus_sign)[0..]);888 try output(context, (*const [1]u8)(&plus_sign)[0..]);
893 const new_value = @intCast(uint, value);889 const new_value = @intCast(uint, value);
894 const new_width = if (width == 0) 0 else (width - 1);890 return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output);
895 return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output);
896 }891 }
897}892}
898893
...@@ -900,7 +895,7 @@ fn formatIntUnsigned(...@@ -900,7 +895,7 @@ fn formatIntUnsigned(
900 value: var,895 value: var,
901 base: u8,896 base: u8,
902 uppercase: bool,897 uppercase: bool,
903 width: usize,898 options: FormatOptions,
904 context: var,899 context: var,
905 comptime Errors: type,900 comptime Errors: type,
906 output: fn (@typeOf(context), []const u8) Errors!void,901 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -921,31 +916,32 @@ fn formatIntUnsigned(...@@ -921,31 +916,32 @@ fn formatIntUnsigned(
921 }916 }
922917
923 const digits_buf = buf[index..];918 const digits_buf = buf[index..];
919 const width = options.width orelse 0;
924 const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;920 const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0;
925921
926 if (padding > index) {922 if (padding > index) {
927 const zero_byte: u8 = '0';923 const zero_byte: u8 = options.fill;
928 var leftover_padding = padding - index;924 var leftover_padding = padding - index;
929 while (true) {925 while (true) {
930 try output(context, (*const [1]u8)(&zero_byte)[0..]);926 try output(context, (*const [1]u8)(&zero_byte)[0..]);
931 leftover_padding -= 1;927 leftover_padding -= 1;
932 if (leftover_padding == 0) break;928 if (leftover_padding == 0) break;
933 }929 }
934 mem.set(u8, buf[0..index], '0');930 mem.set(u8, buf[0..index], options.fill);
935 return output(context, buf);931 return output(context, buf);
936 } else {932 } else {
937 const padded_buf = buf[index - padding ..];933 const padded_buf = buf[index - padding ..];
938 mem.set(u8, padded_buf[0..padding], '0');934 mem.set(u8, padded_buf[0..padding], options.fill);
939 return output(context, padded_buf);935 return output(context, padded_buf);
940 }936 }
941}937}
942938
943pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize {939pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize {
944 var context = FormatIntBuf{940 var context = FormatIntBuf{
945 .out_buf = out_buf,941 .out_buf = out_buf,
946 .index = 0,942 .index = 0,
947 };943 };
948 formatInt(value, base, uppercase, width, &context, error{}, formatIntCallback) catch unreachable;944 formatInt(value, base, uppercase, options, &context, error{}, formatIntCallback) catch unreachable;
949 return context.index;945 return context.index;
950}946}
951const FormatIntBuf = struct {947const FormatIntBuf = struct {
...@@ -1088,23 +1084,23 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) {...@@ -1088,23 +1084,23 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
1088test "bufPrintInt" {1084test "bufPrintInt" {
1089 var buffer: [100]u8 = undefined;1085 var buffer: [100]u8 = undefined;
1090 const buf = buffer[0..];1086 const buf = buffer[0..];
1091 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110"));1087 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, FormatOptions{}), "-101111000110000101001110"));
1092 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678"));1088 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, FormatOptions{}), "-12345678"));
1093 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e"));1089 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, FormatOptions{}), "-bc614e"));
1094 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E"));1090 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, FormatOptions{}), "-BC614E"));
10951091
1096 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678"));1092 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, FormatOptions{}), "12345678"));
10971093
1098 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666"));1094 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, FormatOptions{ .width = 6 }), " 666"));
1099 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234"));1095 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 6 }), " 1234"));
1100 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234"));1096 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 1 }), "1234"));
11011097
1102 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42"));1098 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, FormatOptions{ .width = 3 }), "+42"));
1103 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42"));1099 testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, FormatOptions{ .width = 3 }), "-42"));
1104}1100}
11051101
1106fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 {1102fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 {
1107 return buf[0..formatIntBuf(buf, value, base, uppercase, width)];1103 return buf[0..formatIntBuf(buf, value, base, uppercase, options)];
1108}1104}
11091105
1110test "parse u64 digit too big" {1106test "parse u64 digit too big" {
...@@ -1162,7 +1158,8 @@ test "int.specifier" {...@@ -1162,7 +1158,8 @@ test "int.specifier" {
1162}1158}
11631159
1164test "int.padded" {1160test "int.padded" {
1165 try testFmt("u8: '0001'", "u8: '{:4}'", u8(1));1161 try testFmt("u8: ' 1'", "u8: '{:4}'", u8(1));
1162 try testFmt("u8: 'xxx1'", "u8: '{:x<4}'", u8(1));
1166}1163}
11671164
1168test "buffer" {1165test "buffer" {
...@@ -1237,7 +1234,7 @@ test "cstr" {...@@ -1237,7 +1234,7 @@ test "cstr" {
12371234
1238test "filesize" {1235test "filesize" {
1239 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));1236 try testFmt("file size: 63MiB\n", "file size: {Bi}\n", usize(63 * 1024 * 1024));
1240 try testFmt("file size: 66.06MB\n", "file size: {B:2}\n", usize(63 * 1024 * 1024));1237 try testFmt("file size: 66.06MB\n", "file size: {B:.2}\n", usize(63 * 1024 * 1024));
1241}1238}
12421239
1243test "struct" {1240test "struct" {
...@@ -1342,7 +1339,7 @@ test "custom" {...@@ -1342,7 +1339,7 @@ test "custom" {
1342 pub fn format(1339 pub fn format(
1343 self: SelfType,1340 self: SelfType,
1344 comptime fmt: []const u8,1341 comptime fmt: []const u8,
1345 comptime options: FormatOptions,1342 options: FormatOptions,
1346 context: var,1343 context: var,
1347 comptime Errors: type,1344 comptime Errors: type,
1348 output: fn (@typeOf(context), []const u8) Errors!void,1345 output: fn (@typeOf(context), []const u8) Errors!void,
...@@ -1548,7 +1545,7 @@ test "formatType max_depth" {...@@ -1548,7 +1545,7 @@ test "formatType max_depth" {
1548 pub fn format(1545 pub fn format(
1549 self: SelfType,1546 self: SelfType,
1550 comptime fmt: []const u8,1547 comptime fmt: []const u8,
1551 comptime options: FormatOptions,1548 options: FormatOptions,
1552 context: var,1549 context: var,
1553 comptime Errors: type,1550 comptime Errors: type,
1554 output: fn (@typeOf(context), []const u8) Errors!void,1551 output: fn (@typeOf(context), []const u8) Errors!void,
std/fs.zig+13
...@@ -442,6 +442,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!...@@ -442,6 +442,8 @@ pub fn deleteTree(allocator: *Allocator, full_path: []const u8) DeleteTreeError!
442 }442 }
443}443}
444444
445/// TODO: separate this API into the one that opens directory handles to then subsequently open
446/// files, and into the one that reads files from an open directory handle.
445pub const Dir = struct {447pub const Dir = struct {
446 handle: Handle,448 handle: Handle,
447 allocator: *Allocator,449 allocator: *Allocator,
...@@ -564,6 +566,17 @@ pub const Dir = struct {...@@ -564,6 +566,17 @@ pub const Dir = struct {
564 }566 }
565 }567 }
566568
569 pub fn openRead(self: Dir, file_path: []const u8) os.OpenError!File {
570 const path_c = try os.toPosixPath(file_path);
571 return self.openReadC(&path_c);
572 }
573
574 pub fn openReadC(self: Dir, file_path: [*]const u8) OpenError!File {
575 const flags = os.O_LARGEFILE | os.O_RDONLY;
576 const fd = try os.openatC(self.handle.fd, file_path, flags, 0);
577 return File.openHandle(fd);
578 }
579
567 fn nextDarwin(self: *Dir) !?Entry {580 fn nextDarwin(self: *Dir) !?Entry {
568 start_over: while (true) {581 start_over: while (true) {
569 if (self.handle.index >= self.handle.end_index) {582 if (self.handle.index >= self.handle.end_index) {
std/fs/file.zig+8
...@@ -302,6 +302,14 @@ pub const File = struct {...@@ -302,6 +302,14 @@ pub const File = struct {
302 return os.write(self.handle, bytes);302 return os.write(self.handle, bytes);
303 }303 }
304304
305 pub fn writev_iovec(self: File, iovecs: []const os.iovec_const) WriteError!void {
306 if (std.event.Loop.instance) |loop| {
307 return std.event.fs.writevPosix(loop, self.handle, iovecs);
308 } else {
309 return os.writev(self.handle, iovecs);
310 }
311 }
312
305 pub fn inStream(file: File) InStream {313 pub fn inStream(file: File) InStream {
306 return InStream{314 return InStream{
307 .file = file,315 .file = file,
std/io.zig+1-1
...@@ -146,7 +146,7 @@ pub fn InStream(comptime ReadError: type) type {...@@ -146,7 +146,7 @@ pub fn InStream(comptime ReadError: type) type {
146146
147 /// Same as `readFull` but end of stream returns `error.EndOfStream`.147 /// Same as `readFull` but end of stream returns `error.EndOfStream`.
148 pub fn readNoEof(self: *Self, buf: []u8) !void {148 pub fn readNoEof(self: *Self, buf: []u8) !void {
149 const amt_read = try self.read(buf);149 const amt_read = try self.readFull(buf);
150 if (amt_read < buf.len) return error.EndOfStream;150 if (amt_read < buf.len) return error.EndOfStream;
151 }151 }
152152
std/math/big/int.zig+1-1
...@@ -519,7 +519,7 @@ pub const Int = struct {...@@ -519,7 +519,7 @@ pub const Int = struct {
519 pub fn format(519 pub fn format(
520 self: Int,520 self: Int,
521 comptime fmt: []const u8,521 comptime fmt: []const u8,
522 comptime options: std.fmt.FormatOptions,522 options: std.fmt.FormatOptions,
523 context: var,523 context: var,
524 comptime FmtError: type,524 comptime FmtError: type,
525 output: fn (@typeOf(context), []const u8) FmtError!void,525 output: fn (@typeOf(context), []const u8) FmtError!void,
std/net.zig+30
...@@ -215,3 +215,33 @@ test "std.net.parseIp6" {...@@ -215,3 +215,33 @@ test "std.net.parseIp6" {
215 assert(addr.addr[1] == 0x01);215 assert(addr.addr[1] == 0x01);
216 assert(addr.addr[2] == 0x00);216 assert(addr.addr[2] == 0x00);
217}217}
218
219pub fn connectUnixSocket(path: []const u8) !std.fs.File {
220 const opt_non_block = if (std.event.Loop.instance != null) os.SOCK_NONBLOCK else 0;
221 const sockfd = try os.socket(
222 os.AF_UNIX,
223 os.SOCK_STREAM | os.SOCK_CLOEXEC | opt_non_block,
224 0,
225 );
226 errdefer os.close(sockfd);
227
228 var sock_addr = os.sockaddr{
229 .un = os.sockaddr_un{
230 .family = os.AF_UNIX,
231 .path = undefined,
232 },
233 };
234
235 if (path.len > @typeOf(sock_addr.un.path).len) return error.NameTooLong;
236 mem.copy(u8, sock_addr.un.path[0..], path);
237 const size = @intCast(u32, @sizeOf(os.sa_family_t) + path.len);
238 if (std.event.Loop.instance) |loop| {
239 try os.connect_async(sockfd, &sock_addr, size);
240 try loop.linuxWaitFd(sockfd, os.EPOLLIN | os.EPOLLOUT | os.EPOLLET);
241 try os.getsockoptError(sockfd);
242 } else {
243 try os.connect(sockfd, &sock_addr, size);
244 }
245
246 return std.fs.File.openHandle(sockfd);
247}
std/os.zig+93-26
...@@ -99,47 +99,46 @@ pub const GetRandomError = OpenError;...@@ -99,47 +99,46 @@ pub const GetRandomError = OpenError;
99/// When linking against libc, this calls the99/// When linking against libc, this calls the
100/// appropriate OS-specific library call. Otherwise it uses the zig standard100/// appropriate OS-specific library call. Otherwise it uses the zig standard
101/// library implementation.101/// library implementation.
102pub fn getrandom(buf: []u8) GetRandomError!void {102pub fn getrandom(buffer: []u8) GetRandomError!void {
103 if (windows.is_the_target) {103 if (windows.is_the_target) {
104 return windows.RtlGenRandom(buf);104 return windows.RtlGenRandom(buffer);
105 }105 }
106 if (linux.is_the_target) {106 if (linux.is_the_target or freebsd.is_the_target) {
107 while (true) {107 var buf = buffer;
108 const err = if (std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok) blk: {108 const use_c = !linux.is_the_target or
109 break :blk errno(std.c.getrandom(buf.ptr, buf.len, 0));109 std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok;
110
111 while (buf.len != 0) {
112 var err: u16 = undefined;
113
114 const num_read = if (use_c) blk: {
115 const rc = std.c.getrandom(buf.ptr, buf.len, 0);
116 err = std.c.getErrno(rc);
117 break :blk @bitCast(usize, rc);
110 } else blk: {118 } else blk: {
111 break :blk linux.getErrno(linux.getrandom(buf.ptr, buf.len, 0));119 const rc = linux.getrandom(buf.ptr, buf.len, 0);
120 err = linux.getErrno(rc);
121 break :blk rc;
112 };122 };
113 switch (err) {
114 0 => return,
115 EINVAL => unreachable,
116 EFAULT => unreachable,
117 EINTR => continue,
118 ENOSYS => return getRandomBytesDevURandom(buf),
119 else => return unexpectedErrno(err),
120 }
121 }
122 }
123 if (freebsd.is_the_target) {
124 while (true) {
125 const err = std.c.getErrno(std.c.getrandom(buf.ptr, buf.len, 0));
126123
127 switch (err) {124 switch (err) {
128 0 => return,125 0 => buf = buf[num_read..],
129 EINVAL => unreachable,126 EINVAL => unreachable,
130 EFAULT => unreachable,127 EFAULT => unreachable,
131 EINTR => continue,128 EINTR => continue,
129 ENOSYS => return getRandomBytesDevURandom(buf),
132 else => return unexpectedErrno(err),130 else => return unexpectedErrno(err),
133 }131 }
134 }132 }
133 return;
135 }134 }
136 if (wasi.is_the_target) {135 if (wasi.is_the_target) {
137 switch (wasi.random_get(buf.ptr, buf.len)) {136 switch (wasi.random_get(buffer.ptr, buffer.len)) {
138 0 => return,137 0 => return,
139 else => |err| return unexpectedErrno(err),138 else => |err| return unexpectedErrno(err),
140 }139 }
141 }140 }
142 return getRandomBytesDevURandom(buf);141 return getRandomBytesDevURandom(buffer);
143}142}
144143
145fn getRandomBytesDevURandom(buf: []u8) !void {144fn getRandomBytesDevURandom(buf: []u8) !void {
...@@ -440,6 +439,33 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {...@@ -440,6 +439,33 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void {
440439
441/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.440/// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted.
442/// This function is for blocking file descriptors only. For non-blocking, see441/// This function is for blocking file descriptors only. For non-blocking, see
442/// `writevAsync`.
443pub fn writev(fd: fd_t, iov: []const iovec_const) WriteError!void {
444 while (true) {
445 // TODO handle the case when iov_len is too large and get rid of this @intCast
446 const rc = system.writev(fd, iov.ptr, @intCast(u32, iov.len));
447 switch (errno(rc)) {
448 0 => return,
449 EINTR => continue,
450 EINVAL => unreachable,
451 EFAULT => unreachable,
452 EAGAIN => unreachable, // This function is for blocking writes.
453 EBADF => unreachable, // Always a race condition.
454 EDESTADDRREQ => unreachable, // `connect` was never called.
455 EDQUOT => return error.DiskQuota,
456 EFBIG => return error.FileTooBig,
457 EIO => return error.InputOutput,
458 ENOSPC => return error.NoSpaceLeft,
459 EPERM => return error.AccessDenied,
460 EPIPE => return error.BrokenPipe,
461 else => |err| return unexpectedErrno(err),
462 }
463 }
464}
465
466/// Write multiple buffers to a file descriptor, with a position offset.
467/// Keeps trying if it gets interrupted.
468/// This function is for blocking file descriptors only. For non-blocking, see
443/// `pwritevAsync`.469/// `pwritevAsync`.
444pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {470pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void {
445 if (darwin.is_the_target) {471 if (darwin.is_the_target) {
...@@ -524,7 +550,6 @@ pub const OpenError = error{...@@ -524,7 +550,6 @@ pub const OpenError = error{
524};550};
525551
526/// Open and possibly create a file. Keeps trying if it gets interrupted.552/// Open and possibly create a file. Keeps trying if it gets interrupted.
527/// `file_path` needs to be copied in memory to add a null terminating byte.
528/// See also `openC`.553/// See also `openC`.
529pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {554pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t {
530 const file_path_c = try toPosixPath(file_path);555 const file_path_c = try toPosixPath(file_path);
...@@ -564,6 +589,47 @@ pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {...@@ -564,6 +589,47 @@ pub fn openC(file_path: [*]const u8, flags: u32, perm: usize) OpenError!fd_t {
564 }589 }
565}590}
566591
592/// Open and possibly create a file. Keeps trying if it gets interrupted.
593/// `file_path` is relative to the open directory handle `dir_fd`.
594/// See also `openatC`.
595pub fn openat(dir_fd: fd_t, file_path: []const u8, flags: u32, mode: usize) OpenError!fd_t {
596 const file_path_c = try toPosixPath(file_path);
597 return openatC(dir_fd, &file_path_c, flags, mode);
598}
599
600/// Open and possibly create a file. Keeps trying if it gets interrupted.
601/// `file_path` is relative to the open directory handle `dir_fd`.
602/// See also `openat`.
603pub fn openatC(dir_fd: fd_t, file_path: [*]const u8, flags: u32, mode: usize) OpenError!fd_t {
604 while (true) {
605 const rc = system.openat(dir_fd, file_path, flags, mode);
606 switch (errno(rc)) {
607 0 => return @intCast(fd_t, rc),
608 EINTR => continue,
609
610 EFAULT => unreachable,
611 EINVAL => unreachable,
612 EACCES => return error.AccessDenied,
613 EFBIG => return error.FileTooBig,
614 EOVERFLOW => return error.FileTooBig,
615 EISDIR => return error.IsDir,
616 ELOOP => return error.SymLinkLoop,
617 EMFILE => return error.ProcessFdQuotaExceeded,
618 ENAMETOOLONG => return error.NameTooLong,
619 ENFILE => return error.SystemFdQuotaExceeded,
620 ENODEV => return error.NoDevice,
621 ENOENT => return error.FileNotFound,
622 ENOMEM => return error.SystemResources,
623 ENOSPC => return error.NoSpaceLeft,
624 ENOTDIR => return error.NotDir,
625 EPERM => return error.AccessDenied,
626 EEXIST => return error.PathAlreadyExists,
627 EBUSY => return error.DeviceBusy,
628 else => |err| return unexpectedErrno(err),
629 }
630 }
631}
632
567pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {633pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void {
568 while (true) {634 while (true) {
569 switch (errno(system.dup2(old_fd, new_fd))) {635 switch (errno(system.dup2(old_fd, new_fd))) {
...@@ -1655,7 +1721,7 @@ pub const ConnectError = error{...@@ -1655,7 +1721,7 @@ pub const ConnectError = error{
1655/// For non-blocking, see `connect_async`.1721/// For non-blocking, see `connect_async`.
1656pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {1722pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
1657 while (true) {1723 while (true) {
1658 switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) {1724 switch (errno(system.connect(sockfd, sock_addr, len))) {
1659 0 => return,1725 0 => return,
1660 EACCES => return error.PermissionDenied,1726 EACCES => return error.PermissionDenied,
1661 EPERM => return error.PermissionDenied,1727 EPERM => return error.PermissionDenied,
...@@ -1683,7 +1749,8 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v...@@ -1683,7 +1749,8 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v
1683/// It expects to receive EINPROGRESS`.1749/// It expects to receive EINPROGRESS`.
1684pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {1750pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void {
1685 while (true) {1751 while (true) {
1686 switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) {1752 switch (errno(system.connect(sockfd, sock_addr, len))) {
1753 EINVAL => unreachable,
1687 EINTR => continue,1754 EINTR => continue,
1688 0, EINPROGRESS => return,1755 0, EINPROGRESS => return,
1689 EACCES => return error.PermissionDenied,1756 EACCES => return error.PermissionDenied,
std/os/bits/linux.zig+1
...@@ -784,6 +784,7 @@ pub const socklen_t = u32;...@@ -784,6 +784,7 @@ pub const socklen_t = u32;
784pub const sockaddr = extern union {784pub const sockaddr = extern union {
785 in: sockaddr_in,785 in: sockaddr_in,
786 in6: sockaddr_in6,786 in6: sockaddr_in6,
787 un: sockaddr_un,
787};788};
788789
789pub const sockaddr_in = extern struct {790pub const sockaddr_in = extern struct {
test/compare_output.zig+1-1
...@@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
124 \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream;124 \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream;
125 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;125 \\ stdout.print("Hello, world!\n{d:4} {x:3} {c}\n", u32(12), u16(0x12), u8('a')) catch unreachable;
126 \\}126 \\}
127 , "Hello, world!\n0012 012 a\n");127 , "Hello, world!\n 12 12 a\n");
128128
129 cases.addC("number literals",129 cases.addC("number literals",
130 \\const builtin = @import("builtin");130 \\const builtin = @import("builtin");
test/compile_errors.zig+102-1
...@@ -2,6 +2,107 @@ const tests = @import("tests.zig");...@@ -2,6 +2,107 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub fn addCases(cases: *tests.CompileErrorContext) void {
5 cases.addCase(x: {
6 var tc = cases.create("variable in inline assembly template cannot be found",
7 \\export fn entry() void {
8 \\ var sp = asm volatile (
9 \\ "mov %[foo], sp"
10 \\ : [bar] "=r" (-> usize)
11 \\ );
12 \\}
13 , "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs.");
14 tc.target = tests.Target{
15 .Cross = tests.CrossTarget{
16 .arch = .x86_64,
17 .os = .linux,
18 .abi = .gnu,
19 },
20 };
21 break :x tc;
22 });
23
24 cases.add(
25 "indirect recursion of async functions detected",
26 \\var frame: ?anyframe = null;
27 \\
28 \\export fn a() void {
29 \\ _ = async rangeSum(10);
30 \\ while (frame) |f| resume f;
31 \\}
32 \\
33 \\fn rangeSum(x: i32) i32 {
34 \\ suspend {
35 \\ frame = @frame();
36 \\ }
37 \\ frame = null;
38 \\
39 \\ if (x == 0) return 0;
40 \\ var child = rangeSumIndirect(x - 1);
41 \\ return child + 1;
42 \\}
43 \\
44 \\fn rangeSumIndirect(x: i32) i32 {
45 \\ suspend {
46 \\ frame = @frame();
47 \\ }
48 \\ frame = null;
49 \\
50 \\ if (x == 0) return 0;
51 \\ var child = rangeSum(x - 1);
52 \\ return child + 1;
53 \\}
54 ,
55 "tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself",
56 "tmp.zig:15:33: note: when analyzing type '@Frame(rangeSumIndirect)' here",
57 "tmp.zig:26:25: note: when analyzing type '@Frame(rangeSum)' here",
58 );
59
60 cases.add(
61 "non-async function pointer eventually is inferred to become async",
62 \\export fn a() void {
63 \\ var non_async_fn: fn () void = undefined;
64 \\ non_async_fn = func;
65 \\}
66 \\fn func() void {
67 \\ suspend;
68 \\}
69 ,
70 "tmp.zig:5:1: error: 'func' cannot be async",
71 "tmp.zig:3:20: note: required to be non-async here",
72 "tmp.zig:6:5: note: suspends here",
73 );
74
75 cases.add(
76 "bad alignment in @asyncCall",
77 \\export fn entry() void {
78 \\ var ptr: async fn () void = func;
79 \\ var bytes: [64]u8 = undefined;
80 \\ _ = @asyncCall(&bytes, {}, ptr);
81 \\}
82 \\async fn func() void {}
83 ,
84 "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'",
85 );
86
87 cases.add(
88 "atomic orderings of fence Acquire or stricter",
89 \\export fn entry() void {
90 \\ @fence(.Monotonic);
91 \\}
92 ,
93 "tmp.zig:2:12: error: atomic ordering must be Acquire or stricter",
94 );
95
96 cases.add(
97 "bad alignment in implicit cast from array pointer to slice",
98 \\export fn a() void {
99 \\ var x: [10]u8 = undefined;
100 \\ var y: []align(16) u8 = &x;
101 \\}
102 ,
103 "tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8'",
104 );
105
5 cases.add(106 cases.add(
6 "result location incompatibility mismatching handle_is_ptr (generic call)",107 "result location incompatibility mismatching handle_is_ptr (generic call)",
7 \\export fn entry() void {108 \\export fn entry() void {
...@@ -164,7 +265,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {...@@ -164,7 +265,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
164 "non async function pointer passed to @asyncCall",265 "non async function pointer passed to @asyncCall",
165 \\export fn entry() void {266 \\export fn entry() void {
166 \\ var ptr = afunc;267 \\ var ptr = afunc;
167 \\ var bytes: [100]u8 = undefined;268 \\ var bytes: [100]u8 align(16) = undefined;
168 \\ _ = @asyncCall(&bytes, {}, ptr);269 \\ _ = @asyncCall(&bytes, {}, ptr);
169 \\}270 \\}
170 \\fn afunc() void { }271 \\fn afunc() void { }
test/runtime_safety.zig+1-1
...@@ -30,7 +30,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {...@@ -30,7 +30,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void {
30 \\ @import("std").os.exit(126);30 \\ @import("std").os.exit(126);
31 \\}31 \\}
32 \\pub fn main() void {32 \\pub fn main() void {
33 \\ var bytes: [1]u8 = undefined;33 \\ var bytes: [1]u8 align(16) = undefined;
34 \\ var ptr = other;34 \\ var ptr = other;
35 \\ var frame = @asyncCall(&bytes, {}, ptr);35 \\ var frame = @asyncCall(&bytes, {}, ptr);
36 \\}36 \\}
test/stage1/behavior/array.zig+20-2
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const expect = @import("std").testing.expect;1const std = @import("std");
2const mem = @import("std").mem;2const expect = std.testing.expect;
3const mem = std.mem;
34
4test "arrays" {5test "arrays" {
5 var array: [5]u32 = undefined;6 var array: [5]u32 = undefined;
...@@ -274,3 +275,20 @@ test "double nested array to const slice cast in array literal" {...@@ -274,3 +275,20 @@ test "double nested array to const slice cast in array literal" {
274 S.entry(2);275 S.entry(2);
275 comptime S.entry(2);276 comptime S.entry(2);
276}277}
278
279test "read/write through global variable array of struct fields initialized via array mult" {
280 const S = struct {
281 fn doTheTest() void {
282 expect(storage[0].term == 1);
283 storage[0] = MyStruct{ .term = 123 };
284 expect(storage[0].term == 123);
285 }
286
287 pub const MyStruct = struct {
288 term: usize,
289 };
290
291 var storage: [1]MyStruct = [_]MyStruct{MyStruct{ .term = 1 }} ** 1;
292 };
293 S.doTheTest();
294}
test/stage1/behavior/async_fn.zig+29-2
...@@ -280,7 +280,7 @@ test "async fn pointer in a struct field" {...@@ -280,7 +280,7 @@ test "async fn pointer in a struct field" {
280 bar: async fn (*i32) void,280 bar: async fn (*i32) void,
281 };281 };
282 var foo = Foo{ .bar = simpleAsyncFn2 };282 var foo = Foo{ .bar = simpleAsyncFn2 };
283 var bytes: [64]u8 = undefined;283 var bytes: [64]u8 align(16) = undefined;
284 const f = @asyncCall(&bytes, {}, foo.bar, &data);284 const f = @asyncCall(&bytes, {}, foo.bar, &data);
285 comptime expect(@typeOf(f) == anyframe->void);285 comptime expect(@typeOf(f) == anyframe->void);
286 expect(data == 2);286 expect(data == 2);
...@@ -317,7 +317,7 @@ test "@asyncCall with return type" {...@@ -317,7 +317,7 @@ test "@asyncCall with return type" {
317 }317 }
318 };318 };
319 var foo = Foo{ .bar = Foo.middle };319 var foo = Foo{ .bar = Foo.middle };
320 var bytes: [150]u8 = undefined;320 var bytes: [150]u8 align(16) = undefined;
321 var aresult: i32 = 0;321 var aresult: i32 = 0;
322 _ = @asyncCall(&bytes, &aresult, foo.bar);322 _ = @asyncCall(&bytes, &aresult, foo.bar);
323 expect(aresult == 0);323 expect(aresult == 0);
...@@ -817,3 +817,30 @@ test "struct parameter to async function is copied to the frame" {...@@ -817,3 +817,30 @@ test "struct parameter to async function is copied to the frame" {
817 };817 };
818 S.doTheTest();818 S.doTheTest();
819}819}
820
821test "cast fn to async fn when it is inferred to be async" {
822 const S = struct {
823 var frame: anyframe = undefined;
824 var ok = false;
825
826 fn doTheTest() void {
827 var ptr: async fn () i32 = undefined;
828 ptr = func;
829 var buf: [100]u8 align(16) = undefined;
830 var result: i32 = undefined;
831 _ = await @asyncCall(&buf, &result, ptr);
832 expect(result == 1234);
833 ok = true;
834 }
835
836 fn func() i32 {
837 suspend {
838 frame = @frame();
839 }
840 return 1234;
841 }
842 };
843 _ = async S.doTheTest();
844 resume S.frame;
845 expect(S.ok);
846}
test/stage1/behavior/bitcast.zig+14
...@@ -125,3 +125,17 @@ test "implicit cast to error union by returning" {...@@ -125,3 +125,17 @@ test "implicit cast to error union by returning" {
125 S.entry();125 S.entry();
126 comptime S.entry();126 comptime S.entry();
127}127}
128
129// issue #3010: compiler segfault
130test "bitcast literal [4]u8 param to u32" {
131 const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 });
132 expect(ip == maxInt(u32));
133}
134
135test "bitcast packed struct literal to byte" {
136 const Foo = packed struct {
137 value: u8,
138 };
139 const casted = @bitCast(u8, Foo{ .value = 0xF });
140 expect(casted == 0xf);
141}
test/stage1/behavior/enum_with_members.zig+2-2
...@@ -8,8 +8,8 @@ const ET = union(enum) {...@@ -8,8 +8,8 @@ const ET = union(enum) {
88
9 pub fn print(a: *const ET, buf: []u8) anyerror!usize {9 pub fn print(a: *const ET, buf: []u8) anyerror!usize {
10 return switch (a.*) {10 return switch (a.*) {
11 ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),11 ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}),
12 ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0),12 ET.UINT => |x| fmt.formatIntBuf(buf, x, 10, false, fmt.FormatOptions{}),
13 };13 };
14 }14 }
15};15};
test/stage1/behavior/new_stack_call.zig+1-1
...@@ -1,7 +1,7 @@...@@ -1,7 +1,7 @@
1const std = @import("std");1const std = @import("std");
2const expect = std.testing.expect;2const expect = std.testing.expect;
33
4var new_stack_bytes: [1024]u8 = undefined;4var new_stack_bytes: [1024]u8 align(16) = undefined;
55
6test "calling a function with a new stack" {6test "calling a function with a new stack" {
7 const arg = 1234;7 const arg = 1234;
test/stage1/behavior/void.zig+5
...@@ -33,3 +33,8 @@ test "void optional" {...@@ -33,3 +33,8 @@ test "void optional" {
33 var x: ?void = {};33 var x: ?void = {};
34 expect(x != null);34 expect(x != null);
35}35}
36
37test "void array as a local variable initializer" {
38 var x = [_]void{{}} ** 1004;
39 var y = x[0];
40}
test/tests.zig+15-10
...@@ -2,6 +2,8 @@ const std = @import("std");...@@ -2,6 +2,8 @@ const std = @import("std");
2const debug = std.debug;2const debug = std.debug;
3const warn = debug.warn;3const warn = debug.warn;
4const build = std.build;4const build = std.build;
5pub const Target = build.Target;
6pub const CrossTarget = build.CrossTarget;
5const Buffer = std.Buffer;7const Buffer = std.Buffer;
6const io = std.io;8const io = std.io;
7const fs = std.fs;9const fs = std.fs;
...@@ -20,24 +22,18 @@ const runtime_safety = @import("runtime_safety.zig");...@@ -20,24 +22,18 @@ const runtime_safety = @import("runtime_safety.zig");
20const translate_c = @import("translate_c.zig");22const translate_c = @import("translate_c.zig");
21const gen_h = @import("gen_h.zig");23const gen_h = @import("gen_h.zig");
2224
23const TestTarget = struct {25const test_targets = [_]CrossTarget{
24 os: builtin.Os,26 CrossTarget{
25 arch: builtin.Arch,
26 abi: builtin.Abi,
27};
28
29const test_targets = [_]TestTarget{
30 TestTarget{
31 .os = .linux,27 .os = .linux,
32 .arch = .x86_64,28 .arch = .x86_64,
33 .abi = .gnu,29 .abi = .gnu,
34 },30 },
35 TestTarget{31 CrossTarget{
36 .os = .macosx,32 .os = .macosx,
37 .arch = .x86_64,33 .arch = .x86_64,
38 .abi = .gnu,34 .abi = .gnu,
39 },35 },
40 TestTarget{36 CrossTarget{
41 .os = .windows,37 .os = .windows,
42 .arch = .x86_64,38 .arch = .x86_64,
43 .abi = .msvc,39 .abi = .msvc,
...@@ -568,6 +564,7 @@ pub const CompileErrorContext = struct {...@@ -568,6 +564,7 @@ pub const CompileErrorContext = struct {
568 link_libc: bool,564 link_libc: bool,
569 is_exe: bool,565 is_exe: bool,
570 is_test: bool,566 is_test: bool,
567 target: Target = .Native,
571568
572 const SourceFile = struct {569 const SourceFile = struct {
573 filename: []const u8,570 filename: []const u8,
...@@ -655,6 +652,14 @@ pub const CompileErrorContext = struct {...@@ -655,6 +652,14 @@ pub const CompileErrorContext = struct {
655 zig_args.append("--output-dir") catch unreachable;652 zig_args.append("--output-dir") catch unreachable;
656 zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable;653 zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable;
657654
655 switch (self.case.target) {
656 .Native => {},
657 .Cross => {
658 try zig_args.append("-target");
659 try zig_args.append(try self.case.target.zigTriple(b.allocator));
660 },
661 }
662
658 switch (self.build_mode) {663 switch (self.build_mode) {
659 Mode.Debug => {},664 Mode.Debug => {},
660 Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,665 Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable,