| author | |
| committer | |
| log | 5f3d59f0ac78e01bd50419dab54d7fcbae15f17c |
| tree | 59313f6debbbaafc18dafc5dfed6a479cede8cef |
| parent | c39bb3ebc49096af45f3a69d4742e5f4d50cab62 |
| parent | 3b5a8858c29582daf37856534abe150b568a7bb7 |
| signature |
29 files changed, 728 insertions(+), 208 deletions(-)
doc/langref.html.in+16-4| ... | ... | @@ -6379,7 +6379,7 @@ comptime { |
| 6379 | 6379 | {#header_close#} |
| 6380 | 6380 | |
| 6381 | 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 | 6383 | <p> |
| 6384 | 6384 | {#syntax#}@asyncCall{#endsyntax#} performs an {#syntax#}async{#endsyntax#} call on a function pointer, |
| 6385 | 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 | 6405 | bar: async fn (*i32) void, |
| 6406 | 6406 | }; |
| 6407 | 6407 | var foo = Foo{ .bar = func }; |
| 6408 | var bytes: [64]u8 = undefined; | |
| 6408 | var bytes: [64]u8 align(@alignOf(@Frame(func))) = undefined; | |
| 6409 | 6409 | const f = @asyncCall(&bytes, {}, foo.bar, &data); |
| 6410 | 6410 | assert(data == 2); |
| 6411 | 6411 | resume f; |
| ... | ... | @@ -7322,17 +7322,22 @@ mem.set(u8, dest, c);{#endsyntax#}</pre> |
| 7322 | 7322 | {#header_close#} |
| 7323 | 7323 | |
| 7324 | 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 | 7326 | <p> |
| 7327 | 7327 | This calls a function, in the same way that invoking an expression with parentheses does. However, |
| 7328 | 7328 | instead of using the same stack as the caller, the function uses the stack provided in the {#syntax#}new_stack{#endsyntax#} |
| 7329 | 7329 | parameter. |
| 7330 | 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 | 7336 | {#code_begin|test#} |
| 7332 | 7337 | const std = @import("std"); |
| 7333 | 7338 | const assert = std.debug.assert; |
| 7334 | 7339 | |
| 7335 | var new_stack_bytes: [1024]u8 = undefined; | |
| 7340 | var new_stack_bytes: [1024]u8 align(16) = undefined; | |
| 7336 | 7341 | |
| 7337 | 7342 | test "calling a function with a new stack" { |
| 7338 | 7343 | const arg = 1234; |
| ... | ... | @@ -9318,6 +9323,13 @@ const c = @cImport({ |
| 9318 | 9323 | <li>Does not support Zig-only pointer attributes such as alignment. Use normal {#link|Pointers#} |
| 9319 | 9324 | please!</li> |
| 9320 | 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 | 9333 | {#header_close#} |
| 9322 | 9334 | |
| 9323 | 9335 | {#header_open|Exporting a C Library#} |
src/all_types.hpp+7| ... | ... | @@ -1279,6 +1279,12 @@ struct ZigTypeOpaque { |
| 1279 | 1279 | struct ZigTypeFnFrame { |
| 1280 | 1280 | ZigFn *fn; |
| 1281 | 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 | }; |
| 1283 | 1289 | |
| 1284 | 1290 | struct ZigTypeAnyFrame { |
| ... | ... | @@ -1396,6 +1402,7 @@ struct ZigFn { |
| 1396 | 1402 | AstNode *set_cold_node; |
| 1397 | 1403 | const AstNode *inferred_async_node; |
| 1398 | 1404 | ZigFn *inferred_async_fn; |
| 1405 | AstNode *non_async_node; | |
| 1399 | 1406 | |
| 1400 | 1407 | ZigList<GlobalExport> export_list; |
| 1401 | 1408 | ZigList<IrInstructionCallGen *> call_list; |
src/analyze.cpp+52-4| ... | ... | @@ -4144,8 +4144,15 @@ void semantic_analyze(CodeGen *g) { |
| 4144 | 4144 | |
| 4145 | 4145 | // second pass over functions for detecting async |
| 4146 | 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); | |
| 4148 | analyze_fn_async(g, fn_entry, true); | |
| 4147 | ZigFn *fn = g->fn_defs.at(g->fn_defs_index); | |
| 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 | } |
| 4151 | 4158 | |
| ... | ... | @@ -5190,6 +5197,27 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) { |
| 5190 | 5197 | return fn_type; |
| 5191 | 5198 | } |
| 5192 | 5199 | |
| 5200 | static 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 | ||
| 5193 | 5221 | static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { |
| 5194 | 5222 | Error err; |
| 5195 | 5223 | |
| ... | ... | @@ -5199,6 +5227,20 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { |
| 5199 | 5227 | ZigFn *fn = frame_type->data.frame.fn; |
| 5200 | 5228 | assert(!fn->type_entry->data.fn.is_generic); |
| 5201 | 5229 | |
| 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 | 5244 | switch (fn->anal_state) { |
| 5203 | 5245 | case FnAnalStateInvalid: |
| 5204 | 5246 | return ErrorSemanticAnalyzeFail; |
| ... | ... | @@ -5292,6 +5334,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { |
| 5292 | 5334 | return ErrorSemanticAnalyzeFail; |
| 5293 | 5335 | } |
| 5294 | 5336 | |
| 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 | 5341 | analyze_fn_body(g, callee); |
| 5296 | 5342 | if (callee->anal_state == FnAnalStateInvalid) { |
| 5297 | 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 | 5347 | if (!fn_is_async(callee)) |
| 5302 | 5348 | continue; |
| 5303 | 5349 | |
| 5304 | ZigType *callee_frame_type = get_fn_frame_type(g, callee); | |
| 5305 | ||
| 5306 | 5350 | IrInstructionAllocaGen *alloca_gen = allocate<IrInstructionAllocaGen>(1); |
| 5307 | 5351 | alloca_gen->base.id = IrInstructionIdAllocaGen; |
| 5308 | 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 | 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 | 5421 | if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { |
| 5375 | 5422 | return err; |
| 5376 | 5423 | } |
| 5424 | ||
| 5377 | 5425 | const char *name; |
| 5378 | 5426 | if (*instruction->name_hint == 0) { |
| 5379 | 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 | 6743 | return ErrorNone; |
| 6744 | 6744 | } |
| 6745 | 6745 | |
| 6746 | static 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 | ||
| 6746 | 6765 | static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) { |
| 6747 | 6766 | Error err; |
| 6748 | 6767 | assert(node->type == NodeTypeAsmExpr); |
| ... | ... | @@ -6830,6 +6849,22 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod |
| 6830 | 6849 | input_list[i] = input_value; |
| 6831 | 6850 | } |
| 6832 | 6851 | |
| 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 | 6868 | return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length, |
| 6834 | 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 | 9520 | result.id = ConstCastResultIdFnAlign; |
| 9486 | 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 | 9523 | if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { |
| 9493 | 9524 | result.id = ConstCastResultIdFnVarArgs; |
| 9494 | 9525 | return result; |
| ... | ... | @@ -9546,6 +9577,11 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted |
| 9546 | 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 | 9585 | return result; |
| 9550 | 9586 | } |
| 9551 | 9587 | |
| ... | ... | @@ -11780,8 +11816,11 @@ static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCa |
| 11780 | 11816 | add_error_note(ira->codegen, parent_msg, source_node, |
| 11781 | 11817 | buf_sprintf("only one of the functions is generic")); |
| 11782 | 11818 | break; |
| 11819 | case ConstCastResultIdFnCC: | |
| 11820 | add_error_note(ira->codegen, parent_msg, source_node, | |
| 11821 | buf_sprintf("calling convention mismatch")); | |
| 11822 | break; | |
| 11783 | 11823 | case ConstCastResultIdFnAlign: // TODO |
| 11784 | case ConstCastResultIdFnCC: // TODO | |
| 11785 | 11824 | case ConstCastResultIdFnVarArgs: // TODO |
| 11786 | 11825 | case ConstCastResultIdFnReturnType: // TODO |
| 11787 | 11826 | case ConstCastResultIdFnArgCount: // TODO |
| ... | ... | @@ -11891,6 +11930,21 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 11891 | 11930 | return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); |
| 11892 | 11931 | } |
| 11893 | 11932 | |
| 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 | 11948 | // cast from T to ?T |
| 11895 | 11949 | // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism |
| 11896 | 11950 | if (wanted_type->id == ZigTypeIdOptional) { |
| ... | ... | @@ -12110,7 +12164,26 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst |
| 12110 | 12164 | array_type->data.array.child_type, source_node, |
| 12111 | 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 | } |
| 12116 | 12189 | |
| ... | ... | @@ -13902,8 +13975,7 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * |
| 13902 | 13975 | uint64_t old_array_len = array_type->data.array.len; |
| 13903 | 13976 | uint64_t new_array_len; |
| 13904 | 13977 | |
| 13905 | if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len)) | |
| 13906 | { | |
| 13978 | if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len)) { | |
| 13907 | 13979 | ir_add_error(ira, &instruction->base, buf_sprintf("operation results in overflow")); |
| 13908 | 13980 | return ira->codegen->invalid_instruction; |
| 13909 | 13981 | } |
| ... | ... | @@ -13918,6 +13990,15 @@ static IrInstruction *ir_analyze_array_mult(IrAnalyze *ira, IrInstructionBinOp * |
| 13918 | 13990 | return result; |
| 13919 | 13991 | } |
| 13920 | 13992 | |
| 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 | 14002 | // TODO optimize the buf case |
| 13922 | 14003 | expand_undef_array(ira->codegen, array_val); |
| 13923 | 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 | 14006 | uint64_t i = 0; |
| 13926 | 14007 | for (uint64_t x = 0; x < mult_amt; x += 1) { |
| 13927 | 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], | |
| 13929 | &array_val->data.x_array.data.s_none.elements[y], false); | |
| 14009 | ConstExprValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; | |
| 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 | 14014 | i += 1; |
| 13931 | 14015 | } |
| 13932 | 14016 | } |
| ... | ... | @@ -14753,6 +14837,9 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe |
| 14753 | 14837 | return ira->codegen->invalid_instruction; |
| 14754 | 14838 | } |
| 14755 | 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 | 14843 | ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type, |
| 14757 | 14844 | parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, |
| 14758 | 14845 | parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); |
| ... | ... | @@ -15141,6 +15228,20 @@ no_mem_slot: |
| 15141 | 15228 | return var_ptr_instruction; |
| 15142 | 15229 | } |
| 15143 | 15230 | |
| 15231 | // This function is called when a comptime value becomes accessible at runtime. | |
| 15232 | static 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 | ||
| 15144 | 15245 | static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, |
| 15145 | 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 | 15338 | break; |
| 15238 | 15339 | } |
| 15239 | 15340 | |
| 15341 | if (instr_is_comptime(value)) { | |
| 15342 | mark_comptime_value_escape(ira, source_instr, &value->value); | |
| 15343 | } | |
| 15344 | ||
| 15240 | 15345 | IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope, |
| 15241 | 15346 | source_instr->source_node, ptr, value); |
| 15242 | 15347 | return &store_ptr->base; |
| ... | ... | @@ -15421,7 +15526,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c |
| 15421 | 15526 | IrInstruction *casted_new_stack = nullptr; |
| 15422 | 15527 | if (call_instruction->new_stack != nullptr) { |
| 15423 | 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 | 15530 | ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); |
| 15426 | 15531 | IrInstruction *new_stack = call_instruction->new_stack->child; |
| 15427 | 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 | 17228 | buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code", |
| 17124 | 17229 | buf_ptr(lib_name))); |
| 17125 | 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 | 17232 | ira->codegen->reported_bad_link_libc_error = true; |
| 17128 | 17233 | } |
| 17129 | 17234 | |
| ... | ... | @@ -20841,6 +20946,12 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction |
| 20841 | 20946 | if (!ir_resolve_atomic_order(ira, order_value, &order)) |
| 20842 | 20947 | return ira->codegen->invalid_instruction; |
| 20843 | 20948 | |
| 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 | 20955 | IrInstruction *result = ir_build_fence(&ira->new_irb, |
| 20845 | 20956 | instruction->base.scope, instruction->base.source_node, order_value, order); |
| 20846 | 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 | 24602 | if (result_loc != nullptr && (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc))) |
| 24492 | 24603 | return result_loc; |
| 24493 | 24604 | |
| 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 | } |
| 24496 | 24611 | |
| 24497 | 24612 | static 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 | 1755 | |
| 1756 | 1756 | |
| 1757 | 1757 | // libc dep |
| 1758 | if (g->libc_link_lib != nullptr) { | |
| 1758 | if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) { | |
| 1759 | 1759 | if (g->libc != nullptr) { |
| 1760 | 1760 | if (!g->have_dynamic_link) { |
| 1761 | 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 | 55 | pub extern "c" fn fwrite(ptr: [*]const u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; |
| 56 | 56 | pub extern "c" fn fread(ptr: [*]u8, size_of_type: usize, item_count: usize, stream: *FILE) usize; |
| 57 | 57 | |
| 58 | pub extern "c" fn printf(format: [*]const u8, ...) c_int; | |
| 58 | 59 | pub extern "c" fn abort() noreturn; |
| 59 | 60 | pub extern "c" fn exit(code: c_int) noreturn; |
| 60 | 61 | pub 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 | 65 | pub extern "c" fn @"fstat$INODE64"(fd: fd_t, buf: *Stat) c_int; |
| 65 | 66 | pub extern "c" fn lseek(fd: fd_t, offset: isize, whence: c_int) isize; |
| 66 | 67 | pub extern "c" fn open(path: [*]const u8, oflag: c_uint, ...) c_int; |
| 68 | pub extern "c" fn openat(fd: c_int, path: [*]const u8, oflag: c_uint, ...) c_int; | |
| 67 | 69 | pub extern "c" fn raise(sig: c_int) c_int; |
| 68 | 70 | pub extern "c" fn read(fd: fd_t, buf: [*]u8, nbyte: usize) isize; |
| 69 | 71 | pub extern "c" fn pread(fd: fd_t, buf: [*]u8, nbyte: usize, offset: u64) isize; |
| 70 | 72 | pub extern "c" fn preadv(fd: c_int, iov: [*]const iovec, iovcnt: c_uint, offset: usize) isize; |
| 73 | pub extern "c" fn writev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint) isize; | |
| 71 | 74 | pub extern "c" fn pwritev(fd: c_int, iov: [*]const iovec_const, iovcnt: c_uint, offset: usize) isize; |
| 72 | 75 | pub extern "c" fn stat(noalias path: [*]const u8, noalias buf: *Stat) c_int; |
| 73 | 76 | pub 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 | 115 | pub extern "c" fn getsockopt(sockfd: fd_t, level: c_int, optname: c_int, optval: *c_void, optlen: *socklen_t) c_int; |
| 113 | 116 | pub extern "c" fn kill(pid: pid_t, sig: c_int) c_int; |
| 114 | 117 | pub extern "c" fn getdirentries(fd: fd_t, buf_ptr: [*]u8, nbytes: usize, basep: *i64) isize; |
| 115 | pub extern "c" fn openat(fd: c_int, path: [*]const u8, flags: c_int) c_int; | |
| 116 | 118 | pub extern "c" fn setgid(ruid: c_uint, euid: c_uint) c_int; |
| 117 | 119 | pub extern "c" fn setuid(uid: c_uint) c_int; |
| 118 | 120 | pub 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 | 6 | |
| 7 | 7 | pub extern "c" fn getdents(fd: c_int, buf_ptr: [*]u8, nbytes: usize) usize; |
| 8 | 8 | pub extern "c" fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) c_int; |
| 9 | pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; | |
| 9 | pub 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 | |
| 8 | 8 | pub const MAP_FAILED = @intToPtr(*c_void, maxInt(usize)); |
| 9 | 9 | |
| 10 | pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) c_int; | |
| 10 | pub extern "c" fn getrandom(buf_ptr: [*]u8, buf_len: usize, flags: c_uint) isize; | |
| 11 | 11 | pub extern "c" fn sched_getaffinity(pid: c_int, size: usize, set: *cpu_set_t) c_int; |
| 12 | 12 | pub extern "c" fn eventfd(initval: c_uint, flags: c_uint) c_int; |
| 13 | 13 | pub 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 | 23 | }; |
| 24 | 24 | |
| 25 | 25 | pub const Msg = union(enum) { |
| 26 | WriteV: WriteV, | |
| 26 | 27 | PWriteV: PWriteV, |
| 27 | 28 | PReadV: PReadV, |
| 28 | 29 | Open: Open, |
| ... | ... | @@ -30,6 +31,14 @@ pub const Request = struct { |
| 30 | 31 | WriteFile: WriteFile, |
| 31 | 32 | End, // special - means the fs thread should exit |
| 32 | 33 | |
| 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 | 42 | pub const PWriteV = struct { |
| 34 | 43 | fd: fd_t, |
| 35 | 44 | iov: []const os.iovec_const, |
| ... | ... | @@ -77,7 +86,7 @@ pub const Request = struct { |
| 77 | 86 | pub const PWriteVError = error{OutOfMemory} || File.WriteError; |
| 78 | 87 | |
| 79 | 88 | /// data - just the inner references - must live until pwritev frame completes. |
| 80 | pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { | |
| 89 | pub fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) PWriteVError!void { | |
| 81 | 90 | switch (builtin.os) { |
| 82 | 91 | .macosx, |
| 83 | 92 | .linux, |
| ... | ... | @@ -94,31 +103,31 @@ pub async fn pwritev(loop: *Loop, fd: fd_t, data: []const []const u8, offset: us |
| 94 | 103 | }; |
| 95 | 104 | } |
| 96 | 105 | |
| 97 | return await (async pwritevPosix(loop, fd, iovecs, offset) catch unreachable); | |
| 106 | return pwritevPosix(loop, fd, iovecs, offset); | |
| 98 | 107 | }, |
| 99 | 108 | .windows => { |
| 100 | 109 | const data_copy = try std.mem.dupe(loop.allocator, []const u8, data); |
| 101 | 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 | 113 | else => @compileError("Unsupported OS"), |
| 105 | 114 | } |
| 106 | 115 | } |
| 107 | 116 | |
| 108 | 117 | /// data must outlive the returned frame |
| 109 | pub async fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { | |
| 118 | pub fn pwritevWindows(loop: *Loop, fd: fd_t, data: []const []const u8, offset: usize) os.WindowsWriteError!void { | |
| 110 | 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); | |
| 112 | 121 | |
| 113 | 122 | // TODO do these in parallel |
| 114 | 123 | var off = offset; |
| 115 | 124 | for (data) |buf| { |
| 116 | try await (async pwriteWindows(loop, fd, buf, off) catch unreachable); | |
| 125 | try pwriteWindows(loop, fd, buf, off); | |
| 117 | 126 | off += buf.len; |
| 118 | 127 | } |
| 119 | 128 | } |
| 120 | 129 | |
| 121 | pub async fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { | |
| 130 | pub fn pwriteWindows(loop: *Loop, fd: fd_t, data: []const u8, offset: u64) os.WindowsWriteError!void { | |
| 122 | 131 | var resume_node = Loop.ResumeNode.Basic{ |
| 123 | 132 | .base = Loop.ResumeNode{ |
| 124 | 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 | 167 | } |
| 159 | 168 | |
| 160 | 169 | /// iovecs must live until pwritev frame completes. |
| 161 | pub async fn pwritevPosix( | |
| 170 | pub fn pwritevPosix( | |
| 162 | 171 | loop: *Loop, |
| 163 | 172 | fd: fd_t, |
| 164 | 173 | iovecs: []const os.iovec_const, |
| ... | ... | @@ -195,10 +204,44 @@ pub async fn pwritevPosix( |
| 195 | 204 | return req_node.data.msg.PWriteV.result; |
| 196 | 205 | } |
| 197 | 206 | |
| 207 | /// iovecs must live until pwritev frame completes. | |
| 208 | pub 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 | ||
| 198 | 241 | pub const PReadVError = error{OutOfMemory} || File.ReadError; |
| 199 | 242 | |
| 200 | 243 | /// data - just the inner references - must live until preadv frame completes. |
| 201 | pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { | |
| 244 | pub fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PReadVError!usize { | |
| 202 | 245 | assert(data.len != 0); |
| 203 | 246 | switch (builtin.os) { |
| 204 | 247 | .macosx, |
| ... | ... | @@ -216,21 +259,21 @@ pub async fn preadv(loop: *Loop, fd: fd_t, data: []const []u8, offset: usize) PR |
| 216 | 259 | }; |
| 217 | 260 | } |
| 218 | 261 | |
| 219 | return await (async preadvPosix(loop, fd, iovecs, offset) catch unreachable); | |
| 262 | return preadvPosix(loop, fd, iovecs, offset); | |
| 220 | 263 | }, |
| 221 | 264 | .windows => { |
| 222 | 265 | const data_copy = try std.mem.dupe(loop.allocator, []u8, data); |
| 223 | 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 | 269 | else => @compileError("Unsupported OS"), |
| 227 | 270 | } |
| 228 | 271 | } |
| 229 | 272 | |
| 230 | 273 | /// data must outlive the returned frame |
| 231 | pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize { | |
| 274 | pub fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u64) !usize { | |
| 232 | 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); | |
| 234 | 277 | |
| 235 | 278 | // TODO do these in parallel? |
| 236 | 279 | var off: usize = 0; |
| ... | ... | @@ -238,7 +281,7 @@ pub async fn preadvWindows(loop: *Loop, fd: fd_t, data: []const []u8, offset: u6 |
| 238 | 281 | var inner_off: usize = 0; |
| 239 | 282 | while (true) { |
| 240 | 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 | 285 | off += amt_read; |
| 243 | 286 | inner_off += amt_read; |
| 244 | 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 | 295 | } |
| 253 | 296 | } |
| 254 | 297 | |
| 255 | pub async fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { | |
| 298 | pub fn preadWindows(loop: *Loop, fd: fd_t, data: []u8, offset: u64) !usize { | |
| 256 | 299 | var resume_node = Loop.ResumeNode.Basic{ |
| 257 | 300 | .base = Loop.ResumeNode{ |
| 258 | 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 | 334 | } |
| 292 | 335 | |
| 293 | 336 | /// iovecs must live until preadv frame completes |
| 294 | pub async fn preadvPosix( | |
| 337 | pub fn preadvPosix( | |
| 295 | 338 | loop: *Loop, |
| 296 | 339 | fd: fd_t, |
| 297 | 340 | iovecs: []const os.iovec, |
| ... | ... | @@ -328,7 +371,7 @@ pub async fn preadvPosix( |
| 328 | 371 | return req_node.data.msg.PReadV.result; |
| 329 | 372 | } |
| 330 | 373 | |
| 331 | pub async fn openPosix( | |
| 374 | pub fn openPosix( | |
| 332 | 375 | loop: *Loop, |
| 333 | 376 | path: []const u8, |
| 334 | 377 | flags: u32, |
| ... | ... | @@ -367,11 +410,11 @@ pub async fn openPosix( |
| 367 | 410 | return req_node.data.msg.Open.result; |
| 368 | 411 | } |
| 369 | 412 | |
| 370 | pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { | |
| 413 | pub fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { | |
| 371 | 414 | switch (builtin.os) { |
| 372 | 415 | .macosx, .linux, .freebsd, .netbsd => { |
| 373 | 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 | }, |
| 376 | 419 | |
| 377 | 420 | .windows => return windows.CreateFile( |
| ... | ... | @@ -390,12 +433,12 @@ pub async fn openRead(loop: *Loop, path: []const u8) File.OpenError!fd_t { |
| 390 | 433 | |
| 391 | 434 | /// Creates if does not exist. Truncates the file if it exists. |
| 392 | 435 | /// Uses the default mode. |
| 393 | pub async fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t { | |
| 394 | return await (async openWriteMode(loop, path, File.default_mode) catch unreachable); | |
| 436 | pub fn openWrite(loop: *Loop, path: []const u8) File.OpenError!fd_t { | |
| 437 | return openWriteMode(loop, path, File.default_mode); | |
| 395 | 438 | } |
| 396 | 439 | |
| 397 | 440 | /// Creates if does not exist. Truncates the file if it exists. |
| 398 | pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t { | |
| 441 | pub fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File.OpenError!fd_t { | |
| 399 | 442 | switch (builtin.os) { |
| 400 | 443 | .macosx, |
| 401 | 444 | .linux, |
| ... | ... | @@ -403,7 +446,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File. |
| 403 | 446 | .netbsd, |
| 404 | 447 | => { |
| 405 | 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 | 451 | .windows => return windows.CreateFile( |
| 409 | 452 | path, |
| ... | ... | @@ -419,7 +462,7 @@ pub async fn openWriteMode(loop: *Loop, path: []const u8, mode: File.Mode) File. |
| 419 | 462 | } |
| 420 | 463 | |
| 421 | 464 | /// Creates if does not exist. Does not truncate. |
| 422 | pub async fn openReadWrite( | |
| 465 | pub fn openReadWrite( | |
| 423 | 466 | loop: *Loop, |
| 424 | 467 | path: []const u8, |
| 425 | 468 | mode: File.Mode, |
| ... | ... | @@ -427,7 +470,7 @@ pub async fn openReadWrite( |
| 427 | 470 | switch (builtin.os) { |
| 428 | 471 | .macosx, .linux, .freebsd, .netbsd => { |
| 429 | 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 | }, |
| 432 | 475 | |
| 433 | 476 | .windows => return windows.CreateFile( |
| ... | ... | @@ -576,24 +619,24 @@ pub const CloseOperation = struct { |
| 576 | 619 | |
| 577 | 620 | /// contents must remain alive until writeFile completes. |
| 578 | 621 | /// TODO make this atomic or provide writeFileAtomic and rename this one to writeFileTruncate |
| 579 | pub async fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void { | |
| 580 | return await (async writeFileMode(loop, path, contents, File.default_mode) catch unreachable); | |
| 622 | pub fn writeFile(loop: *Loop, path: []const u8, contents: []const u8) !void { | |
| 623 | return writeFileMode(loop, path, contents, File.default_mode); | |
| 581 | 624 | } |
| 582 | 625 | |
| 583 | 626 | /// contents must remain alive until writeFile completes. |
| 584 | pub async fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { | |
| 627 | pub fn writeFileMode(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { | |
| 585 | 628 | switch (builtin.os) { |
| 586 | 629 | .linux, |
| 587 | 630 | .macosx, |
| 588 | 631 | .freebsd, |
| 589 | 632 | .netbsd, |
| 590 | => return await (async writeFileModeThread(loop, path, contents, mode) catch unreachable), | |
| 591 | .windows => return await (async writeFileWindows(loop, path, contents) catch unreachable), | |
| 633 | => return writeFileModeThread(loop, path, contents, mode), | |
| 634 | .windows => return writeFileWindows(loop, path, contents), | |
| 592 | 635 | else => @compileError("Unsupported OS"), |
| 593 | 636 | } |
| 594 | 637 | } |
| 595 | 638 | |
| 596 | async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { | |
| 639 | fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) !void { | |
| 597 | 640 | const handle = try windows.CreateFile( |
| 598 | 641 | path, |
| 599 | 642 | windows.GENERIC_WRITE, |
| ... | ... | @@ -605,10 +648,10 @@ async fn writeFileWindows(loop: *Loop, path: []const u8, contents: []const u8) ! |
| 605 | 648 | ); |
| 606 | 649 | defer os.close(handle); |
| 607 | 650 | |
| 608 | try await (async pwriteWindows(loop, handle, contents, 0) catch unreachable); | |
| 651 | try pwriteWindows(loop, handle, contents, 0); | |
| 609 | 652 | } |
| 610 | 653 | |
| 611 | async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { | |
| 654 | fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8, mode: File.Mode) !void { | |
| 612 | 655 | const path_with_null = try std.cstr.addNullByte(loop.allocator, path); |
| 613 | 656 | defer loop.allocator.free(path_with_null); |
| 614 | 657 | |
| ... | ... | @@ -646,11 +689,11 @@ async fn writeFileModeThread(loop: *Loop, path: []const u8, contents: []const u8 |
| 646 | 689 | /// The frame resumes when the last data has been confirmed written, but before the file handle |
| 647 | 690 | /// is closed. |
| 648 | 691 | /// Caller owns returned memory. |
| 649 | pub async fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 { | |
| 692 | pub fn readFile(loop: *Loop, file_path: []const u8, max_size: usize) ![]u8 { | |
| 650 | 693 | var close_op = try CloseOperation.start(loop); |
| 651 | 694 | defer close_op.finish(); |
| 652 | 695 | |
| 653 | const fd = try await (async openRead(loop, file_path) catch unreachable); | |
| 696 | const fd = try openRead(loop, file_path); | |
| 654 | 697 | close_op.setHandle(fd); |
| 655 | 698 | |
| 656 | 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 | 703 | try list.ensureCapacity(list.len + mem.page_size); |
| 661 | 704 | const buf = list.items[list.len..]; |
| 662 | 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 | 707 | list.len += amt; |
| 665 | 708 | if (list.len > max_size) { |
| 666 | 709 | return error.FileTooBig; |
| ... | ... | @@ -1273,11 +1316,11 @@ const test_tmp_dir = "std_event_fs_test"; |
| 1273 | 1316 | // return result; |
| 1274 | 1317 | //} |
| 1275 | 1318 | |
| 1276 | async fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { | |
| 1277 | result.* = await (async testFsWatch(loop) catch unreachable); | |
| 1319 | fn testFsWatchCantFail(loop: *Loop, result: *(anyerror!void)) void { | |
| 1320 | result.* = testFsWatch(loop); | |
| 1278 | 1321 | } |
| 1279 | 1322 | |
| 1280 | async fn testFsWatch(loop: *Loop) !void { | |
| 1323 | fn testFsWatch(loop: *Loop) !void { | |
| 1281 | 1324 | const file_path = try std.fs.path.join(loop.allocator, [][]const u8{ test_tmp_dir, "file.txt" }); |
| 1282 | 1325 | defer loop.allocator.free(file_path); |
| 1283 | 1326 | |
| ... | ... | @@ -1288,27 +1331,27 @@ async fn testFsWatch(loop: *Loop) !void { |
| 1288 | 1331 | const line2_offset = 7; |
| 1289 | 1332 | |
| 1290 | 1333 | // first just write then read the file |
| 1291 | try await try async writeFile(loop, file_path, contents); | |
| 1334 | try writeFile(loop, file_path, contents); | |
| 1292 | 1335 | |
| 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 | 1337 | testing.expectEqualSlices(u8, contents, read_contents); |
| 1295 | 1338 | |
| 1296 | 1339 | // now watch the file |
| 1297 | 1340 | var watch = try Watch(void).create(loop, 0); |
| 1298 | 1341 | defer watch.destroy(); |
| 1299 | 1342 | |
| 1300 | testing.expect((try await try async watch.addFile(file_path, {})) == null); | |
| 1343 | testing.expect((try watch.addFile(file_path, {})) == null); | |
| 1301 | 1344 | |
| 1302 | const ev = try async watch.channel.get(); | |
| 1345 | const ev = async watch.channel.get(); | |
| 1303 | 1346 | var ev_consumed = false; |
| 1304 | 1347 | defer if (!ev_consumed) await ev; |
| 1305 | 1348 | |
| 1306 | 1349 | // 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 | 1352 | defer os.close(fd); |
| 1310 | 1353 | |
| 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 | } |
| 1313 | 1356 | |
| 1314 | 1357 | ev_consumed = true; |
| ... | ... | @@ -1316,7 +1359,7 @@ async fn testFsWatch(loop: *Loop) !void { |
| 1316 | 1359 | WatchEventId.CloseWrite => {}, |
| 1317 | 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 | 1363 | testing.expectEqualSlices(u8, |
| 1321 | 1364 | \\line 1 |
| 1322 | 1365 | \\lorem ipsum |
std/event/future.zig+5-6| ... | ... | @@ -97,28 +97,27 @@ test "std.event.Future" { |
| 97 | 97 | loop.run(); |
| 98 | 98 | } |
| 99 | 99 | |
| 100 | async fn testFuture(loop: *Loop) void { | |
| 100 | fn testFuture(loop: *Loop) void { | |
| 101 | 101 | var future = Future(i32).init(loop); |
| 102 | 102 | |
| 103 | 103 | var a = async waitOnFuture(&future); |
| 104 | 104 | var b = async waitOnFuture(&future); |
| 105 | var c = async resolveFuture(&future); | |
| 105 | resolveFuture(&future); | |
| 106 | 106 | |
| 107 | // TODO make this work: | |
| 107 | // TODO https://github.com/ziglang/zig/issues/3077 | |
| 108 | 108 | //const result = (await a) + (await b); |
| 109 | 109 | const a_result = await a; |
| 110 | 110 | const b_result = await b; |
| 111 | 111 | const result = a_result + b_result; |
| 112 | 112 | |
| 113 | await c; | |
| 114 | 113 | testing.expect(result == 12); |
| 115 | 114 | } |
| 116 | 115 | |
| 117 | async fn waitOnFuture(future: *Future(i32)) i32 { | |
| 116 | fn waitOnFuture(future: *Future(i32)) i32 { | |
| 118 | 117 | return future.get().*; |
| 119 | 118 | } |
| 120 | 119 | |
| 121 | async fn resolveFuture(future: *Future(i32)) void { | |
| 120 | fn resolveFuture(future: *Future(i32)) void { | |
| 122 | 121 | future.data = 6; |
| 123 | 122 | future.resolve(); |
| 124 | 123 | } |
std/event/loop.zig+16-8| ... | ... | @@ -89,12 +89,15 @@ pub const Loop = struct { |
| 89 | 89 | pub const IoMode = enum { |
| 90 | 90 | blocking, |
| 91 | 91 | evented, |
| 92 | mixed, | |
| 92 | 93 | }; |
| 93 | 94 | pub const io_mode: IoMode = if (@hasDecl(root, "io_mode")) root.io_mode else IoMode.blocking; |
| 94 | 95 | var global_instance_state: Loop = undefined; |
| 96 | threadlocal var per_thread_instance: ?*Loop = null; | |
| 95 | 97 | const default_instance: ?*Loop = switch (io_mode) { |
| 96 | 98 | .blocking => null, |
| 97 | 99 | .evented => &global_instance_state, |
| 100 | .mixed => per_thread_instance, | |
| 98 | 101 | }; |
| 99 | 102 | pub const instance: ?*Loop = if (@hasDecl(root, "event_loop")) root.event_loop else default_instance; |
| 100 | 103 | |
| ... | ... | @@ -146,10 +149,12 @@ pub const Loop = struct { |
| 146 | 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 | 153 | const extra_thread_count = thread_count - 1; |
| 154 | const resume_node_count = std.math.max(extra_thread_count, 1); | |
| 150 | 155 | self.eventfd_resume_nodes = try self.allocator.alloc( |
| 151 | 156 | std.atomic.Stack(ResumeNode.EventFd).Node, |
| 152 | extra_thread_count, | |
| 157 | resume_node_count, | |
| 153 | 158 | ); |
| 154 | 159 | errdefer self.allocator.free(self.eventfd_resume_nodes); |
| 155 | 160 | |
| ... | ... | @@ -194,7 +199,7 @@ pub const Loop = struct { |
| 194 | 199 | eventfd_node.* = std.atomic.Stack(ResumeNode.EventFd).Node{ |
| 195 | 200 | .data = ResumeNode.EventFd{ |
| 196 | 201 | .base = ResumeNode{ |
| 197 | .id = ResumeNode.Id.EventFd, | |
| 202 | .id = .EventFd, | |
| 198 | 203 | .handle = undefined, |
| 199 | 204 | .overlapped = ResumeNode.overlapped_init, |
| 200 | 205 | }, |
| ... | ... | @@ -451,12 +456,12 @@ pub const Loop = struct { |
| 451 | 456 | self.finishOneEvent(); |
| 452 | 457 | } |
| 453 | 458 | |
| 454 | pub async fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { | |
| 459 | pub fn linuxWaitFd(self: *Loop, fd: i32, flags: u32) !void { | |
| 455 | 460 | defer self.linuxRemoveFd(fd); |
| 456 | 461 | suspend { |
| 457 | 462 | var resume_node = ResumeNode.Basic{ |
| 458 | 463 | .base = ResumeNode{ |
| 459 | .id = ResumeNode.Id.Basic, | |
| 464 | .id = .Basic, | |
| 460 | 465 | .handle = @frame(), |
| 461 | 466 | .overlapped = ResumeNode.overlapped_init, |
| 462 | 467 | }, |
| ... | ... | @@ -790,12 +795,15 @@ pub const Loop = struct { |
| 790 | 795 | |
| 791 | 796 | fn posixFsRun(self: *Loop) void { |
| 792 | 797 | while (true) { |
| 793 | if (builtin.os == builtin.Os.linux) { | |
| 794 | _ = @atomicRmw(i32, &self.os_data.fs_queue_item, AtomicRmwOp.Xchg, 0, AtomicOrder.SeqCst); | |
| 798 | if (builtin.os == .linux) { | |
| 799 | _ = @atomicRmw(i32, &self.os_data.fs_queue_item, .Xchg, 0, .SeqCst); | |
| 795 | 800 | } |
| 796 | 801 | while (self.os_data.fs_queue.get()) |node| { |
| 797 | 802 | switch (node.data.msg) { |
| 798 | 803 | .End => return, |
| 804 | .WriteV => |*msg| { | |
| 805 | msg.result = os.writev(msg.fd, msg.iov); | |
| 806 | }, | |
| 799 | 807 | .PWriteV => |*msg| { |
| 800 | 808 | msg.result = os.pwritev(msg.fd, msg.iov, msg.offset); |
| 801 | 809 | }, |
| ... | ... | @@ -827,14 +835,14 @@ pub const Loop = struct { |
| 827 | 835 | self.finishOneEvent(); |
| 828 | 836 | } |
| 829 | 837 | switch (builtin.os) { |
| 830 | builtin.Os.linux => { | |
| 838 | .linux => { | |
| 831 | 839 | const rc = os.linux.futex_wait(&self.os_data.fs_queue_item, os.linux.FUTEX_WAIT, 0, null); |
| 832 | 840 | switch (os.linux.getErrno(rc)) { |
| 833 | 841 | 0, os.EINTR, os.EAGAIN => continue, |
| 834 | 842 | else => unreachable, |
| 835 | 843 | } |
| 836 | 844 | }, |
| 837 | builtin.Os.macosx, builtin.Os.freebsd, builtin.Os.netbsd => { | |
| 845 | .macosx, .freebsd, .netbsd => { | |
| 838 | 846 | const fs_kevs = (*const [1]os.Kevent)(&self.os_data.fs_kevent_wait); |
| 839 | 847 | var out_kevs: [1]os.Kevent = undefined; |
| 840 | 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 | 69 | FormatFillAndAlign, |
| 70 | 70 | FormatWidth, |
| 71 | 71 | FormatPrecision, |
| 72 | Pointer, | |
| 73 | 72 | }; |
| 74 | 73 | |
| 75 | 74 | comptime var start_index = 0; |
| ... | ... | @@ -109,9 +108,6 @@ pub fn format( |
| 109 | 108 | state = .Start; |
| 110 | 109 | start_index = i; |
| 111 | 110 | }, |
| 112 | '*' => { | |
| 113 | state = .Pointer; | |
| 114 | }, | |
| 115 | 111 | ':' => { |
| 116 | 112 | state = if (comptime peekIsAlign(fmt[i..])) State.FormatFillAndAlign else State.FormatWidth; |
| 117 | 113 | specifier_end = i; |
| ... | ... | @@ -256,19 +252,6 @@ pub fn format( |
| 256 | 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 | 257 | comptime { |
| ... | ... | @@ -293,12 +276,19 @@ pub fn format( |
| 293 | 276 | pub fn formatType( |
| 294 | 277 | value: var, |
| 295 | 278 | comptime fmt: []const u8, |
| 296 | comptime options: FormatOptions, | |
| 279 | options: FormatOptions, | |
| 297 | 280 | context: var, |
| 298 | 281 | comptime Errors: type, |
| 299 | 282 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 300 | 283 | max_depth: usize, |
| 301 | 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 | 292 | const T = @typeOf(value); |
| 303 | 293 | switch (@typeInfo(T)) { |
| 304 | 294 | .ComptimeInt, .Int, .Float => { |
| ... | ... | @@ -438,15 +428,15 @@ pub fn formatType( |
| 438 | 428 | fn formatValue( |
| 439 | 429 | value: var, |
| 440 | 430 | comptime fmt: []const u8, |
| 441 | comptime options: FormatOptions, | |
| 431 | options: FormatOptions, | |
| 442 | 432 | context: var, |
| 443 | 433 | comptime Errors: type, |
| 444 | 434 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 445 | 435 | ) Errors!void { |
| 446 | 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 | 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 | } |
| 451 | 441 | |
| 452 | 442 | const T = @typeOf(value); |
| ... | ... | @@ -460,7 +450,7 @@ fn formatValue( |
| 460 | 450 | pub fn formatIntValue( |
| 461 | 451 | value: var, |
| 462 | 452 | comptime fmt: []const u8, |
| 463 | comptime options: FormatOptions, | |
| 453 | options: FormatOptions, | |
| 464 | 454 | context: var, |
| 465 | 455 | comptime Errors: type, |
| 466 | 456 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -479,7 +469,7 @@ pub fn formatIntValue( |
| 479 | 469 | uppercase = false; |
| 480 | 470 | } else if (comptime std.mem.eql(u8, fmt, "c")) { |
| 481 | 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 | 473 | } else { |
| 484 | 474 | @compileError("Cannot print integer that is larger than 8 bits as a ascii"); |
| 485 | 475 | } |
| ... | ... | @@ -496,21 +486,21 @@ pub fn formatIntValue( |
| 496 | 486 | @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 497 | 487 | } |
| 498 | 488 | |
| 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 | } |
| 501 | 491 | |
| 502 | 492 | fn formatFloatValue( |
| 503 | 493 | value: var, |
| 504 | 494 | comptime fmt: []const u8, |
| 505 | comptime options: FormatOptions, | |
| 495 | options: FormatOptions, | |
| 506 | 496 | context: var, |
| 507 | 497 | comptime Errors: type, |
| 508 | 498 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 509 | 499 | ) Errors!void { |
| 510 | 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 | 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 | 504 | } else { |
| 515 | 505 | @compileError("Unknown format string: '" ++ fmt ++ "'"); |
| 516 | 506 | } |
| ... | ... | @@ -519,7 +509,7 @@ fn formatFloatValue( |
| 519 | 509 | pub fn formatText( |
| 520 | 510 | bytes: []const u8, |
| 521 | 511 | comptime fmt: []const u8, |
| 522 | comptime options: FormatOptions, | |
| 512 | options: FormatOptions, | |
| 523 | 513 | context: var, |
| 524 | 514 | comptime Errors: type, |
| 525 | 515 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -527,11 +517,10 @@ pub fn formatText( |
| 527 | 517 | if (fmt.len == 0) { |
| 528 | 518 | return output(context, bytes); |
| 529 | 519 | } else if (comptime std.mem.eql(u8, fmt, "s")) { |
| 530 | if (options.width) |w| return formatBuf(bytes, w, context, Errors, output); | |
| 531 | return formatBuf(bytes, 0, context, Errors, output); | |
| 520 | return formatBuf(bytes, options, context, Errors, output); | |
| 532 | 521 | } else if (comptime (std.mem.eql(u8, fmt, "x") or std.mem.eql(u8, fmt, "X"))) { |
| 533 | 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 | 525 | return; |
| 537 | 526 | } else { |
| ... | ... | @@ -541,6 +530,7 @@ pub fn formatText( |
| 541 | 530 | |
| 542 | 531 | pub fn formatAsciiChar( |
| 543 | 532 | c: u8, |
| 533 | options: FormatOptions, | |
| 544 | 534 | context: var, |
| 545 | 535 | comptime Errors: type, |
| 546 | 536 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -550,15 +540,16 @@ pub fn formatAsciiChar( |
| 550 | 540 | |
| 551 | 541 | pub fn formatBuf( |
| 552 | 542 | buf: []const u8, |
| 553 | width: usize, | |
| 543 | options: FormatOptions, | |
| 554 | 544 | context: var, |
| 555 | 545 | comptime Errors: type, |
| 556 | 546 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 557 | 547 | ) Errors!void { |
| 558 | 548 | try output(context, buf); |
| 559 | 549 | |
| 550 | const width = options.width orelse 0; | |
| 560 | 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 | 553 | while (leftover_padding > 0) : (leftover_padding -= 1) { |
| 563 | 554 | try output(context, (*const [1]u8)(&pad_byte)[0..1]); |
| 564 | 555 | } |
| ... | ... | @@ -569,7 +560,7 @@ pub fn formatBuf( |
| 569 | 560 | // same type unambiguously. |
| 570 | 561 | pub fn formatFloatScientific( |
| 571 | 562 | value: var, |
| 572 | maybe_precision: ?usize, | |
| 563 | options: FormatOptions, | |
| 573 | 564 | context: var, |
| 574 | 565 | comptime Errors: type, |
| 575 | 566 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -591,7 +582,7 @@ pub fn formatFloatScientific( |
| 591 | 582 | if (x == 0.0) { |
| 592 | 583 | try output(context, "0"); |
| 593 | 584 | |
| 594 | if (maybe_precision) |precision| { | |
| 585 | if (options.precision) |precision| { | |
| 595 | 586 | if (precision != 0) { |
| 596 | 587 | try output(context, "."); |
| 597 | 588 | var i: usize = 0; |
| ... | ... | @@ -610,7 +601,7 @@ pub fn formatFloatScientific( |
| 610 | 601 | var buffer: [32]u8 = undefined; |
| 611 | 602 | var float_decimal = errol.errol3(x, buffer[0..]); |
| 612 | 603 | |
| 613 | if (maybe_precision) |precision| { | |
| 604 | if (options.precision) |precision| { | |
| 614 | 605 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Scientific); |
| 615 | 606 | |
| 616 | 607 | try output(context, float_decimal.digits[0..1]); |
| ... | ... | @@ -650,13 +641,13 @@ pub fn formatFloatScientific( |
| 650 | 641 | if (exp > -10 and exp < 10) { |
| 651 | 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 | 645 | } else { |
| 655 | 646 | try output(context, "-"); |
| 656 | 647 | if (exp > -10 and exp < 10) { |
| 657 | 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 | } |
| 662 | 653 | |
| ... | ... | @@ -664,7 +655,7 @@ pub fn formatFloatScientific( |
| 664 | 655 | // By default floats are printed at full precision (no rounding). |
| 665 | 656 | pub fn formatFloatDecimal( |
| 666 | 657 | value: var, |
| 667 | maybe_precision: ?usize, | |
| 658 | options: FormatOptions, | |
| 668 | 659 | context: var, |
| 669 | 660 | comptime Errors: type, |
| 670 | 661 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -686,7 +677,7 @@ pub fn formatFloatDecimal( |
| 686 | 677 | if (x == 0.0) { |
| 687 | 678 | try output(context, "0"); |
| 688 | 679 | |
| 689 | if (maybe_precision) |precision| { | |
| 680 | if (options.precision) |precision| { | |
| 690 | 681 | if (precision != 0) { |
| 691 | 682 | try output(context, "."); |
| 692 | 683 | var i: usize = 0; |
| ... | ... | @@ -707,7 +698,7 @@ pub fn formatFloatDecimal( |
| 707 | 698 | var buffer: [32]u8 = undefined; |
| 708 | 699 | var float_decimal = errol.errol3(x, buffer[0..]); |
| 709 | 700 | |
| 710 | if (maybe_precision) |precision| { | |
| 701 | if (options.precision) |precision| { | |
| 711 | 702 | errol.roundToPrecision(&float_decimal, precision, errol.RoundMode.Decimal); |
| 712 | 703 | |
| 713 | 704 | // exp < 0 means the leading is always 0 as errol result is normalized. |
| ... | ... | @@ -809,7 +800,7 @@ pub fn formatFloatDecimal( |
| 809 | 800 | |
| 810 | 801 | pub fn formatBytes( |
| 811 | 802 | value: var, |
| 812 | width: ?usize, | |
| 803 | options: FormatOptions, | |
| 813 | 804 | comptime radix: usize, |
| 814 | 805 | context: var, |
| 815 | 806 | comptime Errors: type, |
| ... | ... | @@ -833,7 +824,7 @@ pub fn formatBytes( |
| 833 | 824 | else => unreachable, |
| 834 | 825 | }; |
| 835 | 826 | |
| 836 | try formatFloatDecimal(new_value, width, context, Errors, output); | |
| 827 | try formatFloatDecimal(new_value, options, context, Errors, output); | |
| 837 | 828 | |
| 838 | 829 | if (suffix == ' ') { |
| 839 | 830 | return output(context, "B"); |
| ... | ... | @@ -851,7 +842,7 @@ pub fn formatInt( |
| 851 | 842 | value: var, |
| 852 | 843 | base: u8, |
| 853 | 844 | uppercase: bool, |
| 854 | width: usize, | |
| 845 | options: FormatOptions, | |
| 855 | 846 | context: var, |
| 856 | 847 | comptime Errors: type, |
| 857 | 848 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -863,9 +854,9 @@ pub fn formatInt( |
| 863 | 854 | value; |
| 864 | 855 | |
| 865 | 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 | 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 | } |
| 871 | 862 | |
| ... | ... | @@ -873,26 +864,30 @@ fn formatIntSigned( |
| 873 | 864 | value: var, |
| 874 | 865 | base: u8, |
| 875 | 866 | uppercase: bool, |
| 876 | width: usize, | |
| 867 | options: FormatOptions, | |
| 877 | 868 | context: var, |
| 878 | 869 | comptime Errors: type, |
| 879 | 870 | output: fn (@typeOf(context), []const u8) Errors!void, |
| 880 | 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 | 878 | const uint = @IntType(false, @typeOf(value).bit_count); |
| 882 | 879 | if (value < 0) { |
| 883 | 880 | const minus_sign: u8 = '-'; |
| 884 | 881 | try output(context, (*const [1]u8)(&minus_sign)[0..]); |
| 885 | 882 | const new_value = @intCast(uint, -(value + 1)) + 1; |
| 886 | const new_width = if (width == 0) 0 else (width - 1); | |
| 887 | return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); | |
| 888 | } else if (width == 0) { | |
| 889 | return formatIntUnsigned(@intCast(uint, value), base, uppercase, width, context, Errors, output); | |
| 883 | return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); | |
| 884 | } else if (options.width == null or options.width.? == 0) { | |
| 885 | return formatIntUnsigned(@intCast(uint, value), base, uppercase, options, context, Errors, output); | |
| 890 | 886 | } else { |
| 891 | 887 | const plus_sign: u8 = '+'; |
| 892 | 888 | try output(context, (*const [1]u8)(&plus_sign)[0..]); |
| 893 | 889 | const new_value = @intCast(uint, value); |
| 894 | const new_width = if (width == 0) 0 else (width - 1); | |
| 895 | return formatIntUnsigned(new_value, base, uppercase, new_width, context, Errors, output); | |
| 890 | return formatIntUnsigned(new_value, base, uppercase, new_options, context, Errors, output); | |
| 896 | 891 | } |
| 897 | 892 | } |
| 898 | 893 | |
| ... | ... | @@ -900,7 +895,7 @@ fn formatIntUnsigned( |
| 900 | 895 | value: var, |
| 901 | 896 | base: u8, |
| 902 | 897 | uppercase: bool, |
| 903 | width: usize, | |
| 898 | options: FormatOptions, | |
| 904 | 899 | context: var, |
| 905 | 900 | comptime Errors: type, |
| 906 | 901 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -921,31 +916,32 @@ fn formatIntUnsigned( |
| 921 | 916 | } |
| 922 | 917 | |
| 923 | 918 | const digits_buf = buf[index..]; |
| 919 | const width = options.width orelse 0; | |
| 924 | 920 | const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; |
| 925 | 921 | |
| 926 | 922 | if (padding > index) { |
| 927 | const zero_byte: u8 = '0'; | |
| 923 | const zero_byte: u8 = options.fill; | |
| 928 | 924 | var leftover_padding = padding - index; |
| 929 | 925 | while (true) { |
| 930 | 926 | try output(context, (*const [1]u8)(&zero_byte)[0..]); |
| 931 | 927 | leftover_padding -= 1; |
| 932 | 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 | 931 | return output(context, buf); |
| 936 | 932 | } else { |
| 937 | 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 | 935 | return output(context, padded_buf); |
| 940 | 936 | } |
| 941 | 937 | } |
| 942 | 938 | |
| 943 | pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) usize { | |
| 939 | pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) usize { | |
| 944 | 940 | var context = FormatIntBuf{ |
| 945 | 941 | .out_buf = out_buf, |
| 946 | 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 | 945 | return context.index; |
| 950 | 946 | } |
| 951 | 947 | const FormatIntBuf = struct { |
| ... | ... | @@ -1088,23 +1084,23 @@ fn countSize(size: *usize, bytes: []const u8) (error{}!void) { |
| 1088 | 1084 | test "bufPrintInt" { |
| 1089 | 1085 | var buffer: [100]u8 = undefined; |
| 1090 | 1086 | const buf = buffer[0..]; |
| 1091 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); | |
| 1092 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); | |
| 1093 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); | |
| 1094 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); | |
| 1087 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, FormatOptions{}), "-101111000110000101001110")); | |
| 1088 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, FormatOptions{}), "-12345678")); | |
| 1089 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, FormatOptions{}), "-bc614e")); | |
| 1090 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, FormatOptions{}), "-BC614E")); | |
| 1095 | 1091 | |
| 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")); | |
| 1097 | 1093 | |
| 1098 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); | |
| 1099 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); | |
| 1100 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); | |
| 1094 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, FormatOptions{ .width = 6 }), " 666")); | |
| 1095 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 6 }), " 1234")); | |
| 1096 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, FormatOptions{ .width = 1 }), "1234")); | |
| 1101 | 1097 | |
| 1102 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); | |
| 1103 | 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")); | |
| 1099 | testing.expect(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, FormatOptions{ .width = 3 }), "-42")); | |
| 1104 | 1100 | } |
| 1105 | 1101 | |
| 1106 | fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) []u8 { | |
| 1107 | return buf[0..formatIntBuf(buf, value, base, uppercase, width)]; | |
| 1102 | fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, options: FormatOptions) []u8 { | |
| 1103 | return buf[0..formatIntBuf(buf, value, base, uppercase, options)]; | |
| 1108 | 1104 | } |
| 1109 | 1105 | |
| 1110 | 1106 | test "parse u64 digit too big" { |
| ... | ... | @@ -1162,7 +1158,8 @@ test "int.specifier" { |
| 1162 | 1158 | } |
| 1163 | 1159 | |
| 1164 | 1160 | test "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 | } |
| 1167 | 1164 | |
| 1168 | 1165 | test "buffer" { |
| ... | ... | @@ -1237,7 +1234,7 @@ test "cstr" { |
| 1237 | 1234 | |
| 1238 | 1235 | test "filesize" { |
| 1239 | 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 | } |
| 1242 | 1239 | |
| 1243 | 1240 | test "struct" { |
| ... | ... | @@ -1342,7 +1339,7 @@ test "custom" { |
| 1342 | 1339 | pub fn format( |
| 1343 | 1340 | self: SelfType, |
| 1344 | 1341 | comptime fmt: []const u8, |
| 1345 | comptime options: FormatOptions, | |
| 1342 | options: FormatOptions, | |
| 1346 | 1343 | context: var, |
| 1347 | 1344 | comptime Errors: type, |
| 1348 | 1345 | output: fn (@typeOf(context), []const u8) Errors!void, |
| ... | ... | @@ -1548,7 +1545,7 @@ test "formatType max_depth" { |
| 1548 | 1545 | pub fn format( |
| 1549 | 1546 | self: SelfType, |
| 1550 | 1547 | comptime fmt: []const u8, |
| 1551 | comptime options: FormatOptions, | |
| 1548 | options: FormatOptions, | |
| 1552 | 1549 | context: var, |
| 1553 | 1550 | comptime Errors: type, |
| 1554 | 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 | 442 | } |
| 443 | 443 | } |
| 444 | 444 | |
| 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. | |
| 445 | 447 | pub const Dir = struct { |
| 446 | 448 | handle: Handle, |
| 447 | 449 | allocator: *Allocator, |
| ... | ... | @@ -564,6 +566,17 @@ pub const Dir = struct { |
| 564 | 566 | } |
| 565 | 567 | } |
| 566 | 568 | |
| 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 | 580 | fn nextDarwin(self: *Dir) !?Entry { |
| 568 | 581 | start_over: while (true) { |
| 569 | 582 | if (self.handle.index >= self.handle.end_index) { |
std/fs/file.zig+8| ... | ... | @@ -302,6 +302,14 @@ pub const File = struct { |
| 302 | 302 | return os.write(self.handle, bytes); |
| 303 | 303 | } |
| 304 | 304 | |
| 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 | 313 | pub fn inStream(file: File) InStream { |
| 306 | 314 | return InStream{ |
| 307 | 315 | .file = file, |
std/io.zig+1-1| ... | ... | @@ -146,7 +146,7 @@ pub fn InStream(comptime ReadError: type) type { |
| 146 | 146 | |
| 147 | 147 | /// Same as `readFull` but end of stream returns `error.EndOfStream`. |
| 148 | 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 | 150 | if (amt_read < buf.len) return error.EndOfStream; |
| 151 | 151 | } |
| 152 | 152 |
std/math/big/int.zig+1-1| ... | ... | @@ -519,7 +519,7 @@ pub const Int = struct { |
| 519 | 519 | pub fn format( |
| 520 | 520 | self: Int, |
| 521 | 521 | comptime fmt: []const u8, |
| 522 | comptime options: std.fmt.FormatOptions, | |
| 522 | options: std.fmt.FormatOptions, | |
| 523 | 523 | context: var, |
| 524 | 524 | comptime FmtError: type, |
| 525 | 525 | output: fn (@typeOf(context), []const u8) FmtError!void, |
std/net.zig+30| ... | ... | @@ -215,3 +215,33 @@ test "std.net.parseIp6" { |
| 215 | 215 | assert(addr.addr[1] == 0x01); |
| 216 | 216 | assert(addr.addr[2] == 0x00); |
| 217 | 217 | } |
| 218 | ||
| 219 | pub 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 | 99 | /// When linking against libc, this calls the |
| 100 | 100 | /// appropriate OS-specific library call. Otherwise it uses the zig standard |
| 101 | 101 | /// library implementation. |
| 102 | pub fn getrandom(buf: []u8) GetRandomError!void { | |
| 102 | pub fn getrandom(buffer: []u8) GetRandomError!void { | |
| 103 | 103 | if (windows.is_the_target) { |
| 104 | return windows.RtlGenRandom(buf); | |
| 104 | return windows.RtlGenRandom(buffer); | |
| 105 | 105 | } |
| 106 | if (linux.is_the_target) { | |
| 107 | while (true) { | |
| 108 | const err = if (std.c.versionCheck(builtin.Version{ .major = 2, .minor = 25, .patch = 0 }).ok) blk: { | |
| 109 | break :blk errno(std.c.getrandom(buf.ptr, buf.len, 0)); | |
| 106 | if (linux.is_the_target or freebsd.is_the_target) { | |
| 107 | var buf = buffer; | |
| 108 | const use_c = !linux.is_the_target or | |
| 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 | 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)); | |
| 126 | 123 | |
| 127 | 124 | switch (err) { |
| 128 | 0 => return, | |
| 125 | 0 => buf = buf[num_read..], | |
| 129 | 126 | EINVAL => unreachable, |
| 130 | 127 | EFAULT => unreachable, |
| 131 | 128 | EINTR => continue, |
| 129 | ENOSYS => return getRandomBytesDevURandom(buf), | |
| 132 | 130 | else => return unexpectedErrno(err), |
| 133 | 131 | } |
| 134 | 132 | } |
| 133 | return; | |
| 135 | 134 | } |
| 136 | 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 | 137 | 0 => return, |
| 139 | 138 | else => |err| return unexpectedErrno(err), |
| 140 | 139 | } |
| 141 | 140 | } |
| 142 | return getRandomBytesDevURandom(buf); | |
| 141 | return getRandomBytesDevURandom(buffer); | |
| 143 | 142 | } |
| 144 | 143 | |
| 145 | 144 | fn getRandomBytesDevURandom(buf: []u8) !void { |
| ... | ... | @@ -440,6 +439,33 @@ pub fn write(fd: fd_t, bytes: []const u8) WriteError!void { |
| 440 | 439 | |
| 441 | 440 | /// Write multiple buffers to a file descriptor. Keeps trying if it gets interrupted. |
| 442 | 441 | /// This function is for blocking file descriptors only. For non-blocking, see |
| 442 | /// `writevAsync`. | |
| 443 | pub 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 | 469 | /// `pwritevAsync`. |
| 444 | 470 | pub fn pwritev(fd: fd_t, iov: []const iovec_const, offset: u64) WriteError!void { |
| 445 | 471 | if (darwin.is_the_target) { |
| ... | ... | @@ -524,7 +550,6 @@ pub const OpenError = error{ |
| 524 | 550 | }; |
| 525 | 551 | |
| 526 | 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 | 553 | /// See also `openC`. |
| 529 | 554 | pub fn open(file_path: []const u8, flags: u32, perm: usize) OpenError!fd_t { |
| 530 | 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 | 589 | } |
| 565 | 590 | } |
| 566 | 591 | |
| 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`. | |
| 595 | pub 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`. | |
| 603 | pub 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 | ||
| 567 | 633 | pub fn dup2(old_fd: fd_t, new_fd: fd_t) !void { |
| 568 | 634 | while (true) { |
| 569 | 635 | switch (errno(system.dup2(old_fd, new_fd))) { |
| ... | ... | @@ -1655,7 +1721,7 @@ pub const ConnectError = error{ |
| 1655 | 1721 | /// For non-blocking, see `connect_async`. |
| 1656 | 1722 | pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { |
| 1657 | 1723 | while (true) { |
| 1658 | switch (errno(system.connect(sockfd, sock_addr, @sizeOf(sockaddr)))) { | |
| 1724 | switch (errno(system.connect(sockfd, sock_addr, len))) { | |
| 1659 | 1725 | 0 => return, |
| 1660 | 1726 | EACCES => return error.PermissionDenied, |
| 1661 | 1727 | EPERM => return error.PermissionDenied, |
| ... | ... | @@ -1683,7 +1749,8 @@ pub fn connect(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!v |
| 1683 | 1749 | /// It expects to receive EINPROGRESS`. |
| 1684 | 1750 | pub fn connect_async(sockfd: i32, sock_addr: *sockaddr, len: socklen_t) ConnectError!void { |
| 1685 | 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 | 1754 | EINTR => continue, |
| 1688 | 1755 | 0, EINPROGRESS => return, |
| 1689 | 1756 | EACCES => return error.PermissionDenied, |
std/os/bits/linux.zig+1| ... | ... | @@ -784,6 +784,7 @@ pub const socklen_t = u32; |
| 784 | 784 | pub const sockaddr = extern union { |
| 785 | 785 | in: sockaddr_in, |
| 786 | 786 | in6: sockaddr_in6, |
| 787 | un: sockaddr_un, | |
| 787 | 788 | }; |
| 788 | 789 | |
| 789 | 790 | pub const sockaddr_in = extern struct { |
test/compare_output.zig+1-1| ... | ... | @@ -124,7 +124,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 124 | 124 | \\ const stdout = &(io.getStdOut() catch unreachable).outStream().stream; |
| 125 | 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"); | |
| 128 | 128 | |
| 129 | 129 | cases.addC("number literals", |
| 130 | 130 | \\const builtin = @import("builtin"); |
test/compile_errors.zig+102-1| ... | ... | @@ -2,6 +2,107 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub 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 | 106 | cases.add( |
| 6 | 107 | "result location incompatibility mismatching handle_is_ptr (generic call)", |
| 7 | 108 | \\export fn entry() void { |
| ... | ... | @@ -164,7 +265,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 164 | 265 | "non async function pointer passed to @asyncCall", |
| 165 | 266 | \\export fn entry() void { |
| 166 | 267 | \\ var ptr = afunc; |
| 167 | \\ var bytes: [100]u8 = undefined; | |
| 268 | \\ var bytes: [100]u8 align(16) = undefined; | |
| 168 | 269 | \\ _ = @asyncCall(&bytes, {}, ptr); |
| 169 | 270 | \\} |
| 170 | 271 | \\fn afunc() void { } |
test/runtime_safety.zig+1-1| ... | ... | @@ -30,7 +30,7 @@ pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 30 | 30 | \\ @import("std").os.exit(126); |
| 31 | 31 | \\} |
| 32 | 32 | \\pub fn main() void { |
| 33 | \\ var bytes: [1]u8 = undefined; | |
| 33 | \\ var bytes: [1]u8 align(16) = undefined; | |
| 34 | 34 | \\ var ptr = other; |
| 35 | 35 | \\ var frame = @asyncCall(&bytes, {}, ptr); |
| 36 | 36 | \\} |
test/stage1/behavior/array.zig+20-2| ... | ... | @@ -1,5 +1,6 @@ |
| 1 | const expect = @import("std").testing.expect; | |
| 2 | const mem = @import("std").mem; | |
| 1 | const std = @import("std"); | |
| 2 | const expect = std.testing.expect; | |
| 3 | const mem = std.mem; | |
| 3 | 4 | |
| 4 | 5 | test "arrays" { |
| 5 | 6 | var array: [5]u32 = undefined; |
| ... | ... | @@ -274,3 +275,20 @@ test "double nested array to const slice cast in array literal" { |
| 274 | 275 | S.entry(2); |
| 275 | 276 | comptime S.entry(2); |
| 276 | 277 | } |
| 278 | ||
| 279 | test "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 | 280 | bar: async fn (*i32) void, |
| 281 | 281 | }; |
| 282 | 282 | var foo = Foo{ .bar = simpleAsyncFn2 }; |
| 283 | var bytes: [64]u8 = undefined; | |
| 283 | var bytes: [64]u8 align(16) = undefined; | |
| 284 | 284 | const f = @asyncCall(&bytes, {}, foo.bar, &data); |
| 285 | 285 | comptime expect(@typeOf(f) == anyframe->void); |
| 286 | 286 | expect(data == 2); |
| ... | ... | @@ -317,7 +317,7 @@ test "@asyncCall with return type" { |
| 317 | 317 | } |
| 318 | 318 | }; |
| 319 | 319 | var foo = Foo{ .bar = Foo.middle }; |
| 320 | var bytes: [150]u8 = undefined; | |
| 320 | var bytes: [150]u8 align(16) = undefined; | |
| 321 | 321 | var aresult: i32 = 0; |
| 322 | 322 | _ = @asyncCall(&bytes, &aresult, foo.bar); |
| 323 | 323 | expect(aresult == 0); |
| ... | ... | @@ -817,3 +817,30 @@ test "struct parameter to async function is copied to the frame" { |
| 817 | 817 | }; |
| 818 | 818 | S.doTheTest(); |
| 819 | 819 | } |
| 820 | ||
| 821 | test "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 | 125 | S.entry(); |
| 126 | 126 | comptime S.entry(); |
| 127 | 127 | } |
| 128 | ||
| 129 | // issue #3010: compiler segfault | |
| 130 | test "bitcast literal [4]u8 param to u32" { | |
| 131 | const ip = @bitCast(u32, [_]u8{ 255, 255, 255, 255 }); | |
| 132 | expect(ip == maxInt(u32)); | |
| 133 | } | |
| 134 | ||
| 135 | test "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 | |
| 9 | 9 | pub fn print(a: *const ET, buf: []u8) anyerror!usize { |
| 10 | 10 | return switch (a.*) { |
| 11 | ET.SINT => |x| fmt.formatIntBuf(buf, x, 10, false, 0), | |
| 12 | ET.UINT => |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, fmt.FormatOptions{}), | |
| 13 | 13 | }; |
| 14 | 14 | } |
| 15 | 15 | }; |
test/stage1/behavior/new_stack_call.zig+1-1| ... | ... | @@ -1,7 +1,7 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const expect = std.testing.expect; |
| 3 | 3 | |
| 4 | var new_stack_bytes: [1024]u8 = undefined; | |
| 4 | var new_stack_bytes: [1024]u8 align(16) = undefined; | |
| 5 | 5 | |
| 6 | 6 | test "calling a function with a new stack" { |
| 7 | 7 | const arg = 1234; |
test/stage1/behavior/void.zig+5| ... | ... | @@ -33,3 +33,8 @@ test "void optional" { |
| 33 | 33 | var x: ?void = {}; |
| 34 | 34 | expect(x != null); |
| 35 | 35 | } |
| 36 | ||
| 37 | test "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 | 2 | const debug = std.debug; |
| 3 | 3 | const warn = debug.warn; |
| 4 | 4 | const build = std.build; |
| 5 | pub const Target = build.Target; | |
| 6 | pub const CrossTarget = build.CrossTarget; | |
| 5 | 7 | const Buffer = std.Buffer; |
| 6 | 8 | const io = std.io; |
| 7 | 9 | const fs = std.fs; |
| ... | ... | @@ -20,24 +22,18 @@ const runtime_safety = @import("runtime_safety.zig"); |
| 20 | 22 | const translate_c = @import("translate_c.zig"); |
| 21 | 23 | const gen_h = @import("gen_h.zig"); |
| 22 | 24 | |
| 23 | const TestTarget = struct { | |
| 24 | os: builtin.Os, | |
| 25 | arch: builtin.Arch, | |
| 26 | abi: builtin.Abi, | |
| 27 | }; | |
| 28 | ||
| 29 | const test_targets = [_]TestTarget{ | |
| 30 | TestTarget{ | |
| 25 | const test_targets = [_]CrossTarget{ | |
| 26 | CrossTarget{ | |
| 31 | 27 | .os = .linux, |
| 32 | 28 | .arch = .x86_64, |
| 33 | 29 | .abi = .gnu, |
| 34 | 30 | }, |
| 35 | TestTarget{ | |
| 31 | CrossTarget{ | |
| 36 | 32 | .os = .macosx, |
| 37 | 33 | .arch = .x86_64, |
| 38 | 34 | .abi = .gnu, |
| 39 | 35 | }, |
| 40 | TestTarget{ | |
| 36 | CrossTarget{ | |
| 41 | 37 | .os = .windows, |
| 42 | 38 | .arch = .x86_64, |
| 43 | 39 | .abi = .msvc, |
| ... | ... | @@ -568,6 +564,7 @@ pub const CompileErrorContext = struct { |
| 568 | 564 | link_libc: bool, |
| 569 | 565 | is_exe: bool, |
| 570 | 566 | is_test: bool, |
| 567 | target: Target = .Native, | |
| 571 | 568 | |
| 572 | 569 | const SourceFile = struct { |
| 573 | 570 | filename: []const u8, |
| ... | ... | @@ -655,6 +652,14 @@ pub const CompileErrorContext = struct { |
| 655 | 652 | zig_args.append("--output-dir") catch unreachable; |
| 656 | 653 | zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable; |
| 657 | 654 | |
| 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 | 663 | switch (self.build_mode) { |
| 659 | 664 | Mode.Debug => {}, |
| 660 | 665 | Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, |