diff options
| author | Robin Voetter <robin@voetter.nl> | 2019-09-01 23:45:51 +0200 |
|---|---|---|
| committer | Robin Voetter <robin@voetter.nl> | 2019-09-01 23:45:51 +0200 |
| commit | d62f7c6b605a672f032aff8870496d2ae2366017 (patch) | |
| tree | 659ae46ac0061fcafd3ec29de8682ec1f18e97cc | |
| parent | e7912dee9bd63b03415f441b4de9b3babc79c859 (diff) | |
| parent | 8b1900e5df76a126404c6905b9e91136c738da55 (diff) | |
| download | zig-d62f7c6b605a672f032aff8870496d2ae2366017.tar.gz zig-d62f7c6b605a672f032aff8870496d2ae2366017.zip | |
Merge remote-tracking branch 'upstream/master' into arm-support-improvement
| -rw-r--r-- | doc/langref.html.in | 16 | ||||
| -rw-r--r-- | src/all_types.hpp | 12 | ||||
| -rw-r--r-- | src/analyze.cpp | 37 | ||||
| -rw-r--r-- | src/analyze.hpp | 2 | ||||
| -rw-r--r-- | src/ast_render.cpp | 2 | ||||
| -rw-r--r-- | src/codegen.cpp | 75 | ||||
| -rw-r--r-- | src/ir.cpp | 355 | ||||
| -rw-r--r-- | src/ir_print.cpp | 385 | ||||
| -rw-r--r-- | src/ir_print.hpp | 4 | ||||
| -rw-r--r-- | src/os.cpp | 30 | ||||
| -rw-r--r-- | src/os.hpp | 8 | ||||
| -rw-r--r-- | src/parser.cpp | 2 | ||||
| -rw-r--r-- | src/target.cpp | 15 | ||||
| -rw-r--r-- | std/mem.zig | 10 | ||||
| -rw-r--r-- | std/os/windows.zig | 7 | ||||
| -rw-r--r-- | std/special/compiler_rt/comparetf2.zig | 13 | ||||
| -rw-r--r-- | std/zig/parser_test.zig | 21 | ||||
| -rw-r--r-- | std/zig/render.zig | 6 | ||||
| -rw-r--r-- | test/compile_errors.zig | 18 | ||||
| -rw-r--r-- | test/stage1/behavior/async_fn.zig | 181 | ||||
| -rw-r--r-- | test/stage1/behavior/sizeof_and_typeof.zig | 26 |
21 files changed, 1020 insertions, 205 deletions
diff --git a/doc/langref.html.in b/doc/langref.html.in index c1fe08ddb6..44b2256813 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8114,7 +8114,23 @@ pub const TypeInfo = union(TypeId) { This function returns a compile-time constant, which is the type of the expression passed as an argument. The expression is evaluated. </p> + <p>{#syntax#}@typeOf{#endsyntax#} guarantees no run-time side-effects within the expression:</p> + {#code_begin|test#} +const std = @import("std"); +const assert = std.debug.assert; + +test "no runtime side effects" { + var data: i32 = 0; + const T = @typeOf(foo(i32, &data)); + comptime assert(T == i32); + assert(data == 0); +} +fn foo(comptime T: type, ptr: *T) T { + ptr.* += 1; + return ptr.*; +} + {#code_end#} {#header_close#} {#header_open|@unionInit#} diff --git a/src/all_types.hpp b/src/all_types.hpp index 42b3e04f49..d9e1dc44ca 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -627,7 +627,7 @@ struct AstNodeParamDecl { AstNode *type; Token *var_token; bool is_noalias; - bool is_inline; + bool is_comptime; bool is_var_args; }; @@ -2104,6 +2104,7 @@ enum ScopeId { ScopeIdFnDef, ScopeIdCompTime, ScopeIdRuntime, + ScopeIdTypeOf, }; struct Scope { @@ -2244,6 +2245,13 @@ struct ScopeFnDef { ZigFn *fn_entry; }; +// This scope is created for a @typeOf. +// All runtime side-effects are elided within it. +// NodeTypeFnCallExpr +struct ScopeTypeOf { + Scope base; +}; + // synchronized with code in define_builtin_compile_vars enum AtomicOrder { AtomicOrderUnordered, @@ -2711,6 +2719,7 @@ struct IrInstructionCallSrc { IrInstruction *new_stack; FnInline fn_inline; bool is_async; + bool is_async_call_builtin; bool is_comptime; }; @@ -2727,6 +2736,7 @@ struct IrInstructionCallGen { IrInstruction *new_stack; FnInline fn_inline; bool is_async; + bool is_async_call_builtin; }; struct IrInstructionConst { diff --git a/src/analyze.cpp b/src/analyze.cpp index 0bc42cc971..9b361baa56 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -197,6 +197,12 @@ Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent) { return &scope->base; } +Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent) { + ScopeTypeOf *scope = allocate<ScopeTypeOf>(1); + init_scope(g, &scope->base, ScopeIdTypeOf, node, parent); + return &scope->base; +} + ZigType *get_scope_import(Scope *scope) { while (scope) { if (scope->id == ScopeIdDecls) { @@ -209,6 +215,22 @@ ZigType *get_scope_import(Scope *scope) { zig_unreachable(); } +ScopeTypeOf *get_scope_typeof(Scope *scope) { + while (scope) { + switch (scope->id) { + case ScopeIdTypeOf: + return reinterpret_cast<ScopeTypeOf *>(scope); + case ScopeIdFnDef: + case ScopeIdDecls: + return nullptr; + default: + scope = scope->parent; + continue; + } + } + zig_unreachable(); +} + static ZigType *new_container_type_entry(CodeGen *g, ZigTypeId id, AstNode *source_node, Scope *parent_scope, Buf *bare_name) { @@ -1556,7 +1578,7 @@ static ZigType *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *child_sc AstNode *param_node = fn_proto->params.at(fn_type_id.next_param_index); assert(param_node->type == NodeTypeParamDecl); - bool param_is_comptime = param_node->data.param_decl.is_inline; + bool param_is_comptime = param_node->data.param_decl.is_comptime; bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_comptime) { @@ -4393,7 +4415,7 @@ static void analyze_fn_ir(CodeGen *g, ZigFn *fn, AstNode *return_type_node) { if (g->verbose_ir) { fprintf(stderr, "fn %s() { // (analyzed)\n", buf_ptr(&fn->symbol_name)); - ir_print(g, stderr, &fn->analyzed_executable, 4); + ir_print(g, stderr, &fn->analyzed_executable, 4, 2); fprintf(stderr, "}\n"); } fn->anal_state = FnAnalStateComplete; @@ -4427,7 +4449,7 @@ static void analyze_fn_body(CodeGen *g, ZigFn *fn_table_entry) { fprintf(stderr, "\n"); ast_render(stderr, fn_table_entry->body_node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(g, stderr, &fn_table_entry->ir_executable, 4); + ir_print(g, stderr, &fn_table_entry->ir_executable, 4, 1); fprintf(stderr, "}\n"); } @@ -5705,6 +5727,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { for (size_t i = 0; i < fn->call_list.length; i += 1) { IrInstructionCallGen *call = fn->call_list.at(i); + if (call->new_stack != nullptr) { + // don't need to allocate a frame for this + continue; + } ZigFn *callee = call->fn_entry; if (callee == nullptr) { add_node_error(g, call->base.source_node, @@ -8234,6 +8260,10 @@ static void resolve_llvm_types_anyerror(CodeGen *g) { } static void resolve_llvm_types_async_frame(CodeGen *g, ZigType *frame_type, ResolveStatus wanted_resolve_status) { + Error err; + if ((err = type_resolve(g, frame_type, ResolveStatusSizeKnown))) + zig_unreachable(); + ZigType *passed_frame_type = fn_is_async(frame_type->data.frame.fn) ? frame_type : nullptr; resolve_llvm_types_struct(g, frame_type->data.frame.locals_struct, wanted_resolve_status, passed_frame_type); frame_type->llvm_type = frame_type->data.frame.locals_struct->llvm_type; @@ -8375,7 +8405,6 @@ static void resolve_llvm_types_any_frame(CodeGen *g, ZigType *any_frame_type, Re } static void resolve_llvm_types(CodeGen *g, ZigType *type, ResolveStatus wanted_resolve_status) { - assert(type->id == ZigTypeIdOpaque || type_is_resolved(type, ResolveStatusSizeKnown)); assert(wanted_resolve_status > ResolveStatusSizeKnown); switch (type->id) { case ZigTypeIdInvalid: diff --git a/src/analyze.hpp b/src/analyze.hpp index 6e8897bf82..dc702167e7 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -85,6 +85,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node); ZigFn *scope_fn_entry(Scope *scope); ZigPackage *scope_package(Scope *scope); ZigType *get_scope_import(Scope *scope); +ScopeTypeOf *get_scope_typeof(Scope *scope); void init_tld(Tld *tld, TldId id, Buf *name, VisibMod visib_mod, AstNode *source_node, Scope *parent_scope); ZigVar *add_variable(CodeGen *g, AstNode *source_node, Scope *parent_scope, Buf *name, bool is_const, ConstExprValue *init_value, Tld *src_tld, ZigType *var_type); @@ -112,6 +113,7 @@ ScopeSuspend *create_suspend_scope(CodeGen *g, AstNode *node, Scope *parent); ScopeFnDef *create_fndef_scope(CodeGen *g, AstNode *node, Scope *parent, ZigFn *fn_entry); Scope *create_comptime_scope(CodeGen *g, AstNode *node, Scope *parent); Scope *create_runtime_scope(CodeGen *g, AstNode *node, Scope *parent, IrInstruction *is_comptime); +Scope *create_typeof_scope(CodeGen *g, AstNode *node, Scope *parent); void init_const_str_lit(CodeGen *g, ConstExprValue *const_val, Buf *str); ConstExprValue *create_const_str_lit(CodeGen *g, Buf *str); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 334dc37b59..54a659f7b1 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -448,7 +448,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { assert(param_decl->type == NodeTypeParamDecl); if (param_decl->data.param_decl.name != nullptr) { const char *noalias_str = param_decl->data.param_decl.is_noalias ? "noalias " : ""; - const char *inline_str = param_decl->data.param_decl.is_inline ? "inline " : ""; + const char *inline_str = param_decl->data.param_decl.is_comptime ? "comptime " : ""; fprintf(ar->f, "%s%s", noalias_str, inline_str); print_symbol(ar, param_decl->data.param_decl.name); fprintf(ar->f, ": "); diff --git a/src/codegen.cpp b/src/codegen.cpp index 491ddcd4ea..890724d950 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -645,6 +645,7 @@ static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdRuntime: + case ScopeIdTypeOf: return get_di_scope(g, scope->parent); } zig_unreachable(); @@ -3757,6 +3758,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) { case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdRuntime: + case ScopeIdTypeOf: scope = scope->parent; continue; } @@ -3824,17 +3826,18 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr LLVMValueRef awaiter_init_val; LLVMValueRef ret_ptr; if (callee_is_async) { - if (instruction->is_async) { - if (instruction->new_stack == nullptr) { - awaiter_init_val = zero; + if (instruction->new_stack == nullptr) { + if (instruction->is_async) { frame_result_loc = result_loc; - - if (ret_has_bits) { - // Use the result location which is inside the frame if this is an async call. - ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); - } - } else if (cc == CallingConventionAsync) { - awaiter_init_val = zero; + } else { + frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc); + } + } else { + if (instruction->new_stack->value.type->id == ZigTypeIdPointer && + instruction->new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + frame_result_loc = ir_llvm_value(g, instruction->new_stack); + } else { LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack); if (ir_want_runtime_safety(g, &instruction->base)) { LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, ""); @@ -3854,15 +3857,37 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, ""); LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, ""); - frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, - get_llvm_type(g, instruction->base.value.type), ""); + if (instruction->fn_entry == nullptr) { + ZigType *anyframe_type = get_any_frame_type(g, src_return_type); + frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, get_llvm_type(g, anyframe_type), ""); + } else { + ZigType *ptr_frame_type = get_pointer_to_type(g, + get_fn_frame_type(g, instruction->fn_entry), false); + frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, + get_llvm_type(g, ptr_frame_type), ""); + } + } + } + if (instruction->is_async) { + if (instruction->new_stack == nullptr) { + awaiter_init_val = zero; if (ret_has_bits) { - // Use the result location provided to the @asyncCall builtin - ret_ptr = result_loc; + // Use the result location which is inside the frame if this is an async call. + ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); } } else { - zig_unreachable(); + awaiter_init_val = zero; + + if (ret_has_bits) { + if (result_loc != nullptr) { + // Use the result location provided to the @asyncCall builtin + ret_ptr = result_loc; + } else { + // no result location provided to @asyncCall - use the one inside the frame. + ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); + } + } } // even if prefix_arg_err_ret_stack is true, let the async function do its own @@ -3870,7 +3895,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } else { // async function called as a normal function - frame_result_loc = ir_llvm_value(g, instruction->frame_result_loc); awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer if (ret_has_bits) { if (result_loc == nullptr) { @@ -3986,7 +4010,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr uint32_t arg_start_i = frame_index_arg(g, fn_type->data.fn.fn_type_id.return_type); LLVMValueRef casted_frame; - if (instruction->new_stack != nullptr) { + if (instruction->new_stack != nullptr && instruction->fn_entry == nullptr) { // We need the frame type to be a pointer to a struct that includes the args size_t field_count = arg_start_i + gen_param_values.length; LLVMTypeRef *field_types = allocate_nonzero<LLVMTypeRef>(field_count); @@ -4012,7 +4036,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (instruction->is_async) { gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); if (instruction->new_stack != nullptr) { - return frame_result_loc; + return LLVMBuildBitCast(g->builder, frame_result_loc, + get_llvm_type(g, instruction->base.value.type), ""); } return nullptr; } else { @@ -4039,7 +4064,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr } } - if (instruction->new_stack == nullptr) { + if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) { result = ZigLLVMBuildCall(g->builder, fn_val, gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); } else if (instruction->is_async) { @@ -5942,12 +5967,17 @@ static void ir_render(CodeGen *g, ZigFn *fn_entry) { for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { IrBasicBlock *current_block = executable->basic_block_list.at(block_i); + if (get_scope_typeof(current_block->scope) != nullptr) { + LLVMBuildBr(g->builder, current_block->llvm_block); + } assert(current_block->llvm_block); LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block); for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { IrInstruction *instruction = current_block->instruction_list.at(instr_i); if (instruction->ref_count == 0 && !ir_has_side_effects(instruction)) continue; + if (get_scope_typeof(instruction->scope) != nullptr) + continue; if (!g->strip_debug_symbols) { set_debug_location(g, instruction); @@ -6340,9 +6370,12 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c ZigType *type_entry = const_val->type; assert(type_has_bits(type_entry)); - switch (const_val->special) { +check: switch (const_val->special) { case ConstValSpecialLazy: - zig_unreachable(); + if ((err = ir_resolve_lazy(g, nullptr, const_val))) { + report_errors_and_exit(g); + } + goto check; case ConstValSpecialRuntime: zig_unreachable(); case ConstValSpecialUndef: diff --git a/src/ir.cpp b/src/ir.cpp index 6f740cc937..e1c7bb37fe 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1382,7 +1382,7 @@ static IrInstruction *ir_build_union_field_ptr(IrBuilder *irb, Scope *scope, Ast static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - bool is_comptime, FnInline fn_inline, bool is_async, + bool is_comptime, FnInline fn_inline, bool is_async, bool is_async_call_builtin, IrInstruction *new_stack, ResultLoc *result_loc) { IrInstructionCallSrc *call_instruction = ir_build_instruction<IrInstructionCallSrc>(irb, scope, source_node); @@ -1393,6 +1393,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->is_async = is_async; + call_instruction->is_async_call_builtin = is_async_call_builtin; call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; @@ -1410,7 +1411,7 @@ static IrInstruction *ir_build_call_src(IrBuilder *irb, Scope *scope, AstNode *s static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *source_instruction, ZigFn *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args, - FnInline fn_inline, bool is_async, IrInstruction *new_stack, + FnInline fn_inline, bool is_async, IrInstruction *new_stack, bool is_async_call_builtin, IrInstruction *result_loc, ZigType *return_type) { IrInstructionCallGen *call_instruction = ir_build_instruction<IrInstructionCallGen>(&ira->new_irb, @@ -1422,6 +1423,7 @@ static IrInstructionCallGen *ir_build_call_gen(IrAnalyze *ira, IrInstruction *so call_instruction->args = args; call_instruction->arg_count = arg_count; call_instruction->is_async = is_async; + call_instruction->is_async_call_builtin = is_async_call_builtin; call_instruction->new_stack = new_stack; call_instruction->result_loc = result_loc; @@ -3344,6 +3346,7 @@ static void ir_count_defers(IrBuilder *irb, Scope *inner_scope, Scope *outer_sco case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdRuntime: + case ScopeIdTypeOf: scope = scope->parent; continue; case ScopeIdDeferExpr: @@ -3399,6 +3402,7 @@ static bool ir_gen_defers_for_block(IrBuilder *irb, Scope *inner_scope, Scope *o case ScopeIdSuspend: case ScopeIdCompTime: case ScopeIdRuntime: + case ScopeIdTypeOf: scope = scope->parent; continue; case ScopeIdDeferExpr: @@ -4349,6 +4353,54 @@ static IrInstruction *ir_gen_this(IrBuilder *irb, Scope *orig_scope, AstNode *no zig_unreachable(); } +static IrInstruction *ir_gen_async_call(IrBuilder *irb, Scope *scope, AstNode *await_node, AstNode *call_node, + LVal lval, ResultLoc *result_loc) +{ + size_t arg_offset = 3; + if (call_node->data.fn_call_expr.params.length < arg_offset) { + add_node_error(irb->codegen, call_node, + buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize, + arg_offset, call_node->data.fn_call_expr.params.length)); + return irb->codegen->invalid_instruction; + } + + AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0); + IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope); + if (bytes == irb->codegen->invalid_instruction) + return bytes; + + AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1); + IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope); + if (ret_ptr == irb->codegen->invalid_instruction) + return ret_ptr; + + AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2); + IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_instruction) + return fn_ref; + + size_t arg_count = call_node->data.fn_call_expr.params.length - arg_offset; + + // last "arg" is return pointer + IrInstruction **args = allocate<IrInstruction*>(arg_count + 1); + + for (size_t i = 0; i < arg_count; i += 1) { + AstNode *arg_node = call_node->data.fn_call_expr.params.at(i + arg_offset); + IrInstruction *arg = ir_gen_node(irb, arg_node, scope); + if (arg == irb->codegen->invalid_instruction) + return arg; + args[i] = arg; + } + + args[arg_count] = ret_ptr; + + bool is_async = await_node == nullptr; + bool is_async_call_builtin = true; + IrInstruction *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, false, + FnInlineAuto, is_async, is_async_call_builtin, bytes, result_loc); + return ir_lval_wrap(irb, scope, call, lval, result_loc); +} + static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { @@ -4358,7 +4410,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo Buf *name = fn_ref_expr->data.symbol_expr.symbol; auto entry = irb->codegen->builtin_fn_table.maybe_get(name); - if (!entry) { // new built in not found + if (!entry) { add_node_error(irb->codegen, node, buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); return irb->codegen->invalid_instruction; @@ -4379,8 +4431,10 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo zig_unreachable(); case BuiltinFnIdTypeof: { + Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); + AstNode *arg_node = node->data.fn_call_expr.params.at(0); - IrInstruction *arg = ir_gen_node(irb, arg_node, scope); + IrInstruction *arg = ir_gen_node(irb, arg_node, sub_scope); if (arg == irb->codegen->invalid_instruction) return arg; @@ -5220,7 +5274,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever; IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - fn_inline, false, nullptr, result_loc); + fn_inline, false, false, nullptr, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } case BuiltinFnIdNewStackCall: @@ -5253,53 +5307,11 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo } IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, false, new_stack, result_loc); + FnInlineAuto, false, false, new_stack, result_loc); return ir_lval_wrap(irb, scope, call, lval, result_loc); } case BuiltinFnIdAsyncCall: - { - size_t arg_offset = 3; - if (node->data.fn_call_expr.params.length < arg_offset) { - add_node_error(irb->codegen, node, - buf_sprintf("expected at least %" ZIG_PRI_usize " arguments, found %" ZIG_PRI_usize, - arg_offset, node->data.fn_call_expr.params.length)); - return irb->codegen->invalid_instruction; - } - - AstNode *bytes_node = node->data.fn_call_expr.params.at(0); - IrInstruction *bytes = ir_gen_node(irb, bytes_node, scope); - if (bytes == irb->codegen->invalid_instruction) - return bytes; - - AstNode *ret_ptr_node = node->data.fn_call_expr.params.at(1); - IrInstruction *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope); - if (ret_ptr == irb->codegen->invalid_instruction) - return ret_ptr; - - AstNode *fn_ref_node = node->data.fn_call_expr.params.at(2); - IrInstruction *fn_ref = ir_gen_node(irb, fn_ref_node, scope); - if (fn_ref == irb->codegen->invalid_instruction) - return fn_ref; - - size_t arg_count = node->data.fn_call_expr.params.length - arg_offset; - - // last "arg" is return pointer - IrInstruction **args = allocate<IrInstruction*>(arg_count + 1); - - for (size_t i = 0; i < arg_count; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i + arg_offset); - IrInstruction *arg = ir_gen_node(irb, arg_node, scope); - if (arg == irb->codegen->invalid_instruction) - return arg; - args[i] = arg; - } - - args[arg_count] = ret_ptr; - - IrInstruction *call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, true, bytes, result_loc); - return ir_lval_wrap(irb, scope, call, lval, result_loc); - } + return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc); case BuiltinFnIdTypeId: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -5603,7 +5615,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node bool is_async = node->data.fn_call_expr.is_async; IrInstruction *fn_call = ir_build_call_src(irb, scope, node, nullptr, fn_ref, arg_count, args, false, - FnInlineAuto, is_async, nullptr, result_loc); + FnInlineAuto, is_async, false, nullptr, result_loc); return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); } @@ -7896,6 +7908,19 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n { assert(node->type == NodeTypeAwaitExpr); + AstNode *expr_node = node->data.await_expr.expr; + if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.is_builtin) { + AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr; + Buf *name = fn_ref_expr->data.symbol_expr.symbol; + auto entry = irb->codegen->builtin_fn_table.maybe_get(name); + if (entry != nullptr) { + BuiltinFnEntry *builtin_fn = entry->value; + if (builtin_fn->id == BuiltinFnIdAsyncCall) { + return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc); + } + } + } + ZigFn *fn_entry = exec_fn_entry(irb->exec); if (!fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); @@ -7911,7 +7936,7 @@ static IrInstruction *ir_gen_await_expr(IrBuilder *irb, Scope *scope, AstNode *n return irb->codegen->invalid_instruction; } - IrInstruction *target_inst = ir_gen_node_extra(irb, node->data.await_expr.expr, scope, LValPtr, nullptr); + IrInstruction *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); if (target_inst == irb->codegen->invalid_instruction) return irb->codegen->invalid_instruction; @@ -8269,6 +8294,10 @@ static ConstExprValue *ir_exec_const_result(CodeGen *codegen, IrExecutable *exec break; } } + if (get_scope_typeof(instruction->scope) != nullptr) { + // doesn't count, it's inside a @typeOf() + continue; + } exec_add_error_node(codegen, exec, instruction->source_node, buf_sprintf("unable to evaluate constant expression")); return &codegen->invalid_instruction->value; @@ -9012,7 +9041,42 @@ static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstruction *instruc return false; } - ConstExprValue *const_val = ir_resolve_const(ira, instruction, UndefBad); + ConstExprValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef); + if (const_val == nullptr) + return false; + + if (const_val->special == ConstValSpecialLazy) { + switch (const_val->data.x_lazy->id) { + case LazyValueIdAlignOf: { + // This is guaranteed to fit into a u29 + if (other_type->id == ZigTypeIdComptimeInt) + return true; + size_t align_bits = get_align_amt_type(ira->codegen)->data.integral.bit_count; + if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && + other_type->data.integral.bit_count >= align_bits) + { + return true; + } + break; + } + case LazyValueIdSizeOf: { + // This is guaranteed to fit into a usize + if (other_type->id == ZigTypeIdComptimeInt) + return true; + size_t usize_bits = ira->codegen->builtin_types.entry_usize->data.integral.bit_count; + if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && + other_type->data.integral.bit_count >= usize_bits) + { + return true; + } + break; + } + default: + break; + } + } + + const_val = ir_resolve_const(ira, instruction, UndefBad); if (const_val == nullptr) return false; @@ -10262,7 +10326,7 @@ static void copy_const_val(ConstExprValue *dest, ConstExprValue *src, bool same_ memcpy(dest, src, sizeof(ConstExprValue)); if (!same_global_refs) { dest->global_refs = global_refs; - if (src->special == ConstValSpecialUndef) + if (src->special != ConstValSpecialStatic) return; if (dest->type->id == ZigTypeIdStruct) { dest->data.x_struct.fields = create_const_vals(dest->type->data.structure.src_field_count); @@ -10803,7 +10867,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod fprintf(stderr, "\nSource: "); ast_render(stderr, node, 4); fprintf(stderr, "\n{ // (IR)\n"); - ir_print(codegen, stderr, ir_executable, 2); + ir_print(codegen, stderr, ir_executable, 2, 1); fprintf(stderr, "}\n"); } IrExecutable *analyzed_executable = allocate<IrExecutable>(1); @@ -10824,7 +10888,7 @@ ConstExprValue *ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *nod if (codegen->verbose_ir) { fprintf(stderr, "{ // (analyzed)\n"); - ir_print(codegen, stderr, analyzed_executable, 2); + ir_print(codegen, stderr, analyzed_executable, 2, 2); fprintf(stderr, "}\n"); } @@ -11213,7 +11277,7 @@ static IrInstruction *ir_get_ref(IrAnalyze *ira, IrInstruction *source_instructi return ira->codegen->invalid_instruction; if (instr_is_comptime(value)) { - ConstExprValue *val = ir_resolve_const(ira, value, UndefOk); + ConstExprValue *val = ir_resolve_const(ira, value, LazyOk); if (!val) return ira->codegen->invalid_instruction; return ir_get_const_ptr(ira, source_instruction, val, value->value.type, @@ -12125,7 +12189,8 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { IrInstruction *result = ir_const(ira, source_instr, wanted_type); if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { - bigint_init_bigint(&result->value.data.x_bigint, &value->value.data.x_bigint); + copy_const_val(&result->value, &value->value, false); + result->value.type = wanted_type; } else { float_init_bigint(&result->value.data.x_bigint, &value->value); } @@ -14869,7 +14934,7 @@ static IrInstruction *ir_resolve_result_raw(IrAnalyze *ira, IrInstruction *suspe PtrLenSingle, 0, 0, 0, false); set_up_result_loc_for_inferred_comptime(&alloca_gen->base); ZigFn *fn_entry = exec_fn_entry(ira->new_irb.exec); - if (fn_entry != nullptr) { + if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) { fn_entry->alloca_gen_list.append(alloca_gen); } result_loc->written = true; @@ -15200,44 +15265,61 @@ static IrInstruction *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInst return ir_const_void(ira, &instruction->base); } +static IrInstruction *get_async_call_result_loc(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, + ZigType *fn_ret_type) +{ + ir_assert(call_instruction->is_async_call_builtin, &call_instruction->base); + IrInstruction *ret_ptr_uncasted = call_instruction->args[call_instruction->arg_count]->child; + if (type_is_invalid(ret_ptr_uncasted->value.type)) + return ira->codegen->invalid_instruction; + if (ret_ptr_uncasted->value.type->id == ZigTypeIdVoid) { + // Result location will be inside the async frame. + return nullptr; + } + return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false)); +} + static IrInstruction *ir_analyze_async_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction **casted_args, size_t arg_count, IrInstruction *casted_new_stack) { - if (casted_new_stack != nullptr) { - // this is an @asyncCall - + if (fn_entry == nullptr) { if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) { ir_add_error(ira, fn_ref, buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name))); return ira->codegen->invalid_instruction; } - - IrInstruction *ret_ptr = call_instruction->args[call_instruction->arg_count]->child; - if (type_is_invalid(ret_ptr->value.type)) + if (casted_new_stack == nullptr) { + ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required")); + return ira->codegen->invalid_instruction; + } + } + if (casted_new_stack != nullptr) { + ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type; + IrInstruction *ret_ptr = get_async_call_result_loc(ira, call_instruction, fn_ret_type); + if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value.type)) return ira->codegen->invalid_instruction; - ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_type->data.fn.fn_type_id.return_type); + ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type); - IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, nullptr, fn_ref, - arg_count, casted_args, FnInlineAuto, true, casted_new_stack, ret_ptr, anyframe_type); + IrInstructionCallGen *call_gen = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, + arg_count, casted_args, FnInlineAuto, true, casted_new_stack, + call_instruction->is_async_call_builtin, ret_ptr, anyframe_type); return &call_gen->base; - } else if (fn_entry == nullptr) { - ir_add_error(ira, fn_ref, buf_sprintf("function is not comptime-known; @asyncCall required")); - return ira->codegen->invalid_instruction; - } - - ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry); - IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, - frame_type, nullptr, true, true, false); - if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { - return result_loc; + } else { + ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry); + IrInstruction *result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, + frame_type, nullptr, true, true, false); + if (type_is_invalid(result_loc->value.type) || instr_is_unreachable(result_loc)) { + return result_loc; + } + result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false)); + if (type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count, + casted_args, FnInlineAuto, true, casted_new_stack, call_instruction->is_async_call_builtin, + result_loc, frame_type)->base; } - result_loc = ir_implicit_cast(ira, result_loc, get_pointer_to_type(ira->codegen, frame_type, false)); - if (type_is_invalid(result_loc->value.type)) - return ira->codegen->invalid_instruction; - return &ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, arg_count, - casted_args, FnInlineAuto, true, nullptr, result_loc, frame_type)->base; } static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, IrInstruction *arg, Scope **exec_scope, size_t *next_proto_i) @@ -15301,7 +15383,7 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod } } - bool comptime_arg = param_decl_node->data.param_decl.is_inline || + bool comptime_arg = param_decl_node->data.param_decl.is_comptime || casted_arg->value.type->id == ZigTypeIdComptimeInt || casted_arg->value.type->id == ZigTypeIdComptimeFloat; ConstExprValue *arg_val; @@ -15746,16 +15828,27 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstruction *casted_new_stack = nullptr; if (call_instruction->new_stack != nullptr) { - ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false); - ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); IrInstruction *new_stack = call_instruction->new_stack->child; if (type_is_invalid(new_stack->value.type)) return ira->codegen->invalid_instruction; - casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice); - if (type_is_invalid(casted_new_stack->value.type)) - return ira->codegen->invalid_instruction; + if (call_instruction->is_async_call_builtin && + fn_entry != nullptr && new_stack->value.type->id == ZigTypeIdPointer && + new_stack->value.type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + ZigType *needed_frame_type = get_pointer_to_type(ira->codegen, + get_fn_frame_type(ira->codegen, fn_entry), false); + casted_new_stack = ir_implicit_cast(ira, new_stack, needed_frame_type); + if (type_is_invalid(casted_new_stack->value.type)) + return ira->codegen->invalid_instruction; + } else { + ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false); + ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); + casted_new_stack = ir_implicit_cast(ira, new_stack, u8_slice); + if (type_is_invalid(casted_new_stack->value.type)) + return ira->codegen->invalid_instruction; + } } if (fn_type->data.fn.is_generic) { @@ -15965,8 +16058,24 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + + if (fn_type_can_fail(impl_fn_type_id)) { + parent_fn_entry->calls_or_awaits_errorable_fn = true; + } + + size_t impl_param_count = impl_fn_type_id->param_count; + if (call_instruction->is_async) { + IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, + nullptr, casted_args, impl_param_count, casted_new_stack); + return ir_finish_anal(ira, result); + } + IrInstruction *result_loc; - if (handle_is_ptr(impl_fn_type_id->return_type)) { + if (call_instruction->is_async_call_builtin) { + result_loc = get_async_call_result_loc(ira, call_instruction, impl_fn_type_id->return_type); + if (result_loc != nullptr && type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } else if (handle_is_ptr(impl_fn_type_id->return_type)) { result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, impl_fn_type_id->return_type, nullptr, true, true, false); if (result_loc != nullptr) { @@ -15982,17 +16091,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c result_loc = nullptr; } - if (fn_type_can_fail(impl_fn_type_id)) { - parent_fn_entry->calls_or_awaits_errorable_fn = true; - } - - size_t impl_param_count = impl_fn_type_id->param_count; - if (call_instruction->is_async) { - IrInstruction *result = ir_analyze_async_call(ira, call_instruction, impl_fn, impl_fn->type_entry, - nullptr, casted_args, impl_param_count, casted_new_stack); - return ir_finish_anal(ira, result); - } - if (impl_fn_type_id->cc == CallingConventionAsync && parent_fn_entry->inferred_async_node == nullptr) { parent_fn_entry->inferred_async_node = fn_ref->source_node; parent_fn_entry->inferred_async_fn = impl_fn; @@ -16000,10 +16098,12 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, impl_fn, nullptr, impl_param_count, casted_args, fn_inline, - false, casted_new_stack, result_loc, + false, casted_new_stack, call_instruction->is_async_call_builtin, result_loc, impl_fn_type_id->return_type); - parent_fn_entry->call_list.append(new_call_instruction); + if (get_scope_typeof(call_instruction->base.scope) == nullptr) { + parent_fn_entry->call_list.append(new_call_instruction); + } return ir_finish_anal(ira, &new_call_instruction->base); } @@ -16123,7 +16223,11 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } IrInstruction *result_loc; - if (handle_is_ptr(return_type)) { + if (call_instruction->is_async_call_builtin) { + result_loc = get_async_call_result_loc(ira, call_instruction, return_type); + if (result_loc != nullptr && type_is_invalid(result_loc->value.type)) + return ira->codegen->invalid_instruction; + } else if (handle_is_ptr(return_type)) { result_loc = ir_resolve_result(ira, &call_instruction->base, call_instruction->result_loc, return_type, nullptr, true, true, false); if (result_loc != nullptr) { @@ -16141,8 +16245,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c IrInstructionCallGen *new_call_instruction = ir_build_call_gen(ira, &call_instruction->base, fn_entry, fn_ref, call_param_count, casted_args, fn_inline, false, casted_new_stack, - result_loc, return_type); - parent_fn_entry->call_list.append(new_call_instruction); + call_instruction->is_async_call_builtin, result_loc, return_type); + if (get_scope_typeof(call_instruction->base.scope) == nullptr) { + parent_fn_entry->call_list.append(new_call_instruction); + } return ir_finish_anal(ira, &new_call_instruction->base); } @@ -17594,6 +17700,11 @@ static IrInstruction *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstruc ConstExprValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); if (child_val == nullptr) return ira->codegen->invalid_instruction; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + field_ptr_instruction->base.source_node, child_val, UndefBad))) + { + return ira->codegen->invalid_instruction; + } ZigType *child_type = child_val->data.x_type; if (type_is_invalid(child_type)) { @@ -21293,8 +21404,10 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru src_ptr_align = get_abi_alignment(ira->codegen, target->value.type); } - if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_instruction; + if (src_ptr_align != 0) { + if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_instruction; + } ZigType *dest_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_child_type, src_ptr_const, src_ptr_volatile, PtrLenUnknown, @@ -21337,6 +21450,8 @@ static IrInstruction *ir_analyze_instruction_from_bytes(IrAnalyze *ira, IrInstru } if (have_known_len) { + if ((err = type_resolve(ira->codegen, dest_child_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_instruction; uint64_t child_type_size = type_size(ira->codegen, dest_child_type); uint64_t remainder = known_len % child_type_size; if (remainder != 0) { @@ -23963,15 +24078,23 @@ static IrInstruction *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstruct } static IrInstruction *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstructionAlignCast *instruction) { - uint32_t align_bytes; - IrInstruction *align_bytes_inst = instruction->align_bytes->child; - if (!ir_resolve_align(ira, align_bytes_inst, nullptr, &align_bytes)) - return ira->codegen->invalid_instruction; - IrInstruction *target = instruction->target->child; if (type_is_invalid(target->value.type)) return ira->codegen->invalid_instruction; + ZigType *elem_type = nullptr; + if (is_slice(target->value.type)) { + ZigType *slice_ptr_type = target->value.type->data.structure.fields[slice_ptr_index].type_entry; + elem_type = slice_ptr_type->data.pointer.child_type; + } else if (target->value.type->id == ZigTypeIdPointer) { + elem_type = target->value.type->data.pointer.child_type; + } + + uint32_t align_bytes; + IrInstruction *align_bytes_inst = instruction->align_bytes->child; + if (!ir_resolve_align(ira, align_bytes_inst, elem_type, &align_bytes)) + return ira->codegen->invalid_instruction; + IrInstruction *result = ir_align_cast(ira, target, align_bytes, true); if (type_is_invalid(result->value.type)) return ira->codegen->invalid_instruction; @@ -25644,7 +25767,7 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { } val->special = ConstValSpecialStatic; - assert(val->type->id == ZigTypeIdComptimeInt); + assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, align_in_bytes); return ErrorNone; } @@ -25699,7 +25822,7 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { } val->special = ConstValSpecialStatic; - assert(val->type->id == ZigTypeIdComptimeInt); + assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); bigint_init_unsigned(&val->data.x_bigint, abi_size); return ErrorNone; } @@ -25885,7 +26008,7 @@ static Error ir_resolve_lazy_raw(AstNode *source_node, ConstExprValue *val) { Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ConstExprValue *val) { Error err; if ((err = ir_resolve_lazy_raw(source_node, val))) { - if (codegen->trace_err != nullptr && !source_node->already_traced_this_node) { + if (codegen->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) { source_node->already_traced_this_node = true; codegen->trace_err = add_error_note(codegen, codegen->trace_err, source_node, buf_create_from_str("referenced here")); diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 7580f19059..6de585ec6f 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -10,27 +10,374 @@ #include "ir_print.hpp" #include "os.hpp" +static uint32_t hash_instruction_ptr(IrInstruction* instruction) { + return (uint32_t)(uintptr_t)instruction; +} + +static bool instruction_ptr_equal(IrInstruction* a, IrInstruction* b) { + return a == b; +} + +using InstructionSet = HashMap<IrInstruction*, uint8_t, hash_instruction_ptr, instruction_ptr_equal>; +using InstructionList = ZigList<IrInstruction*>; + struct IrPrint { + size_t pass_num; CodeGen *codegen; FILE *f; int indent; int indent_size; + + // When printing pass 2 instructions referenced var instructions are not + // present in the instruction list. Thus we track which instructions + // are printed (per executable) and after each pass 2 instruction those + // var instructions are rendered in a trailing fashion. + InstructionSet printed; + InstructionList pending; }; static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction); +static const char* ir_instruction_type_str(IrInstruction* instruction) { + switch (instruction->id) { + case IrInstructionIdInvalid: + return "Invalid"; + case IrInstructionIdDeclVarSrc: + return "DeclVarSrc"; + case IrInstructionIdDeclVarGen: + return "DeclVarGen"; + case IrInstructionIdBr: + return "Br"; + case IrInstructionIdCondBr: + return "CondBr"; + case IrInstructionIdSwitchBr: + return "SwitchBr"; + case IrInstructionIdSwitchVar: + return "SwitchVar"; + case IrInstructionIdSwitchElseVar: + return "SwitchElseVar"; + case IrInstructionIdSwitchTarget: + return "SwitchTarget"; + case IrInstructionIdPhi: + return "Phi"; + case IrInstructionIdUnOp: + return "UnOp"; + case IrInstructionIdBinOp: + return "BinOp"; + case IrInstructionIdLoadPtr: + return "LoadPtr"; + case IrInstructionIdLoadPtrGen: + return "LoadPtrGen"; + case IrInstructionIdStorePtr: + return "StorePtr"; + case IrInstructionIdFieldPtr: + return "FieldPtr"; + case IrInstructionIdStructFieldPtr: + return "StructFieldPtr"; + case IrInstructionIdUnionFieldPtr: + return "UnionFieldPtr"; + case IrInstructionIdElemPtr: + return "ElemPtr"; + case IrInstructionIdVarPtr: + return "VarPtr"; + case IrInstructionIdReturnPtr: + return "ReturnPtr"; + case IrInstructionIdCallSrc: + return "CallSrc"; + case IrInstructionIdCallGen: + return "CallGen"; + case IrInstructionIdConst: + return "Const"; + case IrInstructionIdReturn: + return "Return"; + case IrInstructionIdCast: + return "Cast"; + case IrInstructionIdResizeSlice: + return "ResizeSlice"; + case IrInstructionIdContainerInitList: + return "ContainerInitList"; + case IrInstructionIdContainerInitFields: + return "ContainerInitFields"; + case IrInstructionIdUnreachable: + return "Unreachable"; + case IrInstructionIdTypeOf: + return "TypeOf"; + case IrInstructionIdSetCold: + return "SetCold"; + case IrInstructionIdSetRuntimeSafety: + return "SetRuntimeSafety"; + case IrInstructionIdSetFloatMode: + return "SetFloatMode"; + case IrInstructionIdArrayType: + return "ArrayType"; + case IrInstructionIdAnyFrameType: + return "AnyFrameType"; + case IrInstructionIdSliceType: + return "SliceType"; + case IrInstructionIdGlobalAsm: + return "GlobalAsm"; + case IrInstructionIdAsm: + return "Asm"; + case IrInstructionIdSizeOf: + return "SizeOf"; + case IrInstructionIdTestNonNull: + return "TestNonNull"; + case IrInstructionIdOptionalUnwrapPtr: + return "OptionalUnwrapPtr"; + case IrInstructionIdOptionalWrap: + return "OptionalWrap"; + case IrInstructionIdUnionTag: + return "UnionTag"; + case IrInstructionIdClz: + return "Clz"; + case IrInstructionIdCtz: + return "Ctz"; + case IrInstructionIdPopCount: + return "PopCount"; + case IrInstructionIdBswap: + return "Bswap"; + case IrInstructionIdBitReverse: + return "BitReverse"; + case IrInstructionIdImport: + return "Import"; + case IrInstructionIdCImport: + return "CImport"; + case IrInstructionIdCInclude: + return "CInclude"; + case IrInstructionIdCDefine: + return "CDefine"; + case IrInstructionIdCUndef: + return "CUndef"; + case IrInstructionIdRef: + return "Ref"; + case IrInstructionIdRefGen: + return "RefGen"; + case IrInstructionIdCompileErr: + return "CompileErr"; + case IrInstructionIdCompileLog: + return "CompileLog"; + case IrInstructionIdErrName: + return "ErrName"; + case IrInstructionIdEmbedFile: + return "EmbedFile"; + case IrInstructionIdCmpxchgSrc: + return "CmpxchgSrc"; + case IrInstructionIdCmpxchgGen: + return "CmpxchgGen"; + case IrInstructionIdFence: + return "Fence"; + case IrInstructionIdTruncate: + return "Truncate"; + case IrInstructionIdIntCast: + return "IntCast"; + case IrInstructionIdFloatCast: + return "FloatCast"; + case IrInstructionIdIntToFloat: + return "IntToFloat"; + case IrInstructionIdFloatToInt: + return "FloatToInt"; + case IrInstructionIdBoolToInt: + return "BoolToInt"; + case IrInstructionIdIntType: + return "IntType"; + case IrInstructionIdVectorType: + return "VectorType"; + case IrInstructionIdBoolNot: + return "BoolNot"; + case IrInstructionIdMemset: + return "Memset"; + case IrInstructionIdMemcpy: + return "Memcpy"; + case IrInstructionIdSliceSrc: + return "SliceSrc"; + case IrInstructionIdSliceGen: + return "SliceGen"; + case IrInstructionIdMemberCount: + return "MemberCount"; + case IrInstructionIdMemberType: + return "MemberType"; + case IrInstructionIdMemberName: + return "MemberName"; + case IrInstructionIdBreakpoint: + return "Breakpoint"; + case IrInstructionIdReturnAddress: + return "ReturnAddress"; + case IrInstructionIdFrameAddress: + return "FrameAddress"; + case IrInstructionIdFrameHandle: + return "FrameHandle"; + case IrInstructionIdFrameType: + return "FrameType"; + case IrInstructionIdFrameSizeSrc: + return "FrameSizeSrc"; + case IrInstructionIdFrameSizeGen: + return "FrameSizeGen"; + case IrInstructionIdAlignOf: + return "AlignOf"; + case IrInstructionIdOverflowOp: + return "OverflowOp"; + case IrInstructionIdTestErrSrc: + return "TestErrSrc"; + case IrInstructionIdTestErrGen: + return "TestErrGen"; + case IrInstructionIdMulAdd: + return "MulAdd"; + case IrInstructionIdFloatOp: + return "FloatOp"; + case IrInstructionIdUnwrapErrCode: + return "UnwrapErrCode"; + case IrInstructionIdUnwrapErrPayload: + return "UnwrapErrPayload"; + case IrInstructionIdErrWrapCode: + return "ErrWrapCode"; + case IrInstructionIdErrWrapPayload: + return "ErrWrapPayload"; + case IrInstructionIdFnProto: + return "FnProto"; + case IrInstructionIdTestComptime: + return "TestComptime"; + case IrInstructionIdPtrCastSrc: + return "PtrCastSrc"; + case IrInstructionIdPtrCastGen: + return "PtrCastGen"; + case IrInstructionIdBitCastSrc: + return "BitCastSrc"; + case IrInstructionIdBitCastGen: + return "BitCastGen"; + case IrInstructionIdWidenOrShorten: + return "WidenOrShorten"; + case IrInstructionIdIntToPtr: + return "IntToPtr"; + case IrInstructionIdPtrToInt: + return "PtrToInt"; + case IrInstructionIdIntToEnum: + return "IntToEnum"; + case IrInstructionIdEnumToInt: + return "EnumToInt"; + case IrInstructionIdIntToErr: + return "IntToErr"; + case IrInstructionIdErrToInt: + return "ErrToInt"; + case IrInstructionIdCheckSwitchProngs: + return "CheckSwitchProngs"; + case IrInstructionIdCheckStatementIsVoid: + return "CheckStatementIsVoid"; + case IrInstructionIdTypeName: + return "TypeName"; + case IrInstructionIdDeclRef: + return "DeclRef"; + case IrInstructionIdPanic: + return "Panic"; + case IrInstructionIdTagName: + return "TagName"; + case IrInstructionIdTagType: + return "TagType"; + case IrInstructionIdFieldParentPtr: + return "FieldParentPtr"; + case IrInstructionIdByteOffsetOf: + return "ByteOffsetOf"; + case IrInstructionIdBitOffsetOf: + return "BitOffsetOf"; + case IrInstructionIdTypeInfo: + return "TypeInfo"; + case IrInstructionIdHasField: + return "HasField"; + case IrInstructionIdTypeId: + return "TypeId"; + case IrInstructionIdSetEvalBranchQuota: + return "SetEvalBranchQuota"; + case IrInstructionIdPtrType: + return "PtrType"; + case IrInstructionIdAlignCast: + return "AlignCast"; + case IrInstructionIdImplicitCast: + return "ImplicitCast"; + case IrInstructionIdResolveResult: + return "ResolveResult"; + case IrInstructionIdResetResult: + return "ResetResult"; + case IrInstructionIdOpaqueType: + return "OpaqueType"; + case IrInstructionIdSetAlignStack: + return "SetAlignStack"; + case IrInstructionIdArgType: + return "ArgType"; + case IrInstructionIdExport: + return "Export"; + case IrInstructionIdErrorReturnTrace: + return "ErrorReturnTrace"; + case IrInstructionIdErrorUnion: + return "ErrorUnion"; + case IrInstructionIdAtomicRmw: + return "AtomicRmw"; + case IrInstructionIdAtomicLoad: + return "AtomicLoad"; + case IrInstructionIdSaveErrRetAddr: + return "SaveErrRetAddr"; + case IrInstructionIdAddImplicitReturnType: + return "AddImplicitReturnType"; + case IrInstructionIdErrSetCast: + return "ErrSetCast"; + case IrInstructionIdToBytes: + return "ToBytes"; + case IrInstructionIdFromBytes: + return "FromBytes"; + case IrInstructionIdCheckRuntimeScope: + return "CheckRuntimeScope"; + case IrInstructionIdVectorToArray: + return "VectorToArray"; + case IrInstructionIdArrayToVector: + return "ArrayToVector"; + case IrInstructionIdAssertZero: + return "AssertZero"; + case IrInstructionIdAssertNonNull: + return "AssertNonNull"; + case IrInstructionIdHasDecl: + return "HasDecl"; + case IrInstructionIdUndeclaredIdent: + return "UndeclaredIdent"; + case IrInstructionIdAllocaSrc: + return "AllocaSrc"; + case IrInstructionIdAllocaGen: + return "AllocaGen"; + case IrInstructionIdEndExpr: + return "EndExpr"; + case IrInstructionIdPtrOfArrayToSlice: + return "PtrOfArrayToSlice"; + case IrInstructionIdUnionInitNamedField: + return "UnionInitNamedField"; + case IrInstructionIdSuspendBegin: + return "SuspendBegin"; + case IrInstructionIdSuspendFinish: + return "SuspendFinish"; + case IrInstructionIdAwaitSrc: + return "AwaitSrc"; + case IrInstructionIdAwaitGen: + return "AwaitGen"; + case IrInstructionIdResume: + return "Resume"; + case IrInstructionIdSpillBegin: + return "SpillBegin"; + case IrInstructionIdSpillEnd: + return "SpillEnd"; + } + zig_unreachable(); +} + static void ir_print_indent(IrPrint *irp) { for (int i = 0; i < irp->indent; i += 1) { fprintf(irp->f, " "); } } -static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction) { +static void ir_print_prefix(IrPrint *irp, IrInstruction *instruction, bool trailing) { ir_print_indent(irp); + const char mark = trailing ? ':' : '#'; const char *type_name = instruction->value.type ? buf_ptr(&instruction->value.type->name) : "(unknown)"; const char *ref_count = ir_has_side_effects(instruction) ? "-" : buf_ptr(buf_sprintf("%" ZIG_PRI_usize "", instruction->ref_count)); - fprintf(irp->f, "#%-3zu| %-12s| %-2s| ", instruction->debug_id, type_name, ref_count); + fprintf(irp->f, "%c%-3zu| %-22s| %-12s| %-2s| ", mark, instruction->debug_id, + ir_instruction_type_str(instruction), type_name, ref_count); } static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { @@ -42,6 +389,10 @@ static void ir_print_const_value(IrPrint *irp, ConstExprValue *const_val) { static void ir_print_var_instruction(IrPrint *irp, IrInstruction *instruction) { fprintf(irp->f, "#%" ZIG_PRI_usize "", instruction->debug_id); + if (irp->pass_num == 2 && irp->printed.maybe_get(instruction) == nullptr) { + irp->printed.put(instruction, 0); + irp->pending.append(instruction); + } } static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) { @@ -49,6 +400,7 @@ static void ir_print_other_instruction(IrPrint *irp, IrInstruction *instruction) fprintf(irp->f, "(null)"); return; } + if (instruction->value.special != ConstValSpecialRuntime) { ir_print_const_value(irp, &instruction->value); } else { @@ -1550,8 +1902,8 @@ static void ir_print_spill_end(IrPrint *irp, IrInstructionSpillEnd *instruction) fprintf(irp->f, ")"); } -static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { - ir_print_prefix(irp, instruction); +static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool trailing) { + ir_print_prefix(irp, instruction, trailing); switch (instruction->id) { case IrInstructionIdInvalid: zig_unreachable(); @@ -2036,31 +2388,48 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { fprintf(irp->f, "\n"); } -void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size) { +void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, size_t pass_num) { IrPrint ir_print = {}; IrPrint *irp = &ir_print; + irp->pass_num = pass_num; irp->codegen = codegen; irp->f = f; irp->indent = indent_size; irp->indent_size = indent_size; + irp->printed = {}; + irp->printed.init(64); + irp->pending = {}; for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) { IrBasicBlock *current_block = executable->basic_block_list.at(bb_i); fprintf(irp->f, "%s_%" ZIG_PRI_usize ":\n", current_block->name_hint, current_block->debug_id); for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { IrInstruction *instruction = current_block->instruction_list.at(instr_i); - ir_print_instruction(irp, instruction); + if (irp->pass_num == 2) { + irp->printed.put(instruction, 0); + irp->pending.clear(); + } + ir_print_instruction(irp, instruction, false); + for (size_t j = 0; j < irp->pending.length; ++j) + ir_print_instruction(irp, irp->pending.at(j), true); } } + + irp->pending.deinit(); + irp->printed.deinit(); } -void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size) { +void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, size_t pass_num) { IrPrint ir_print = {}; IrPrint *irp = &ir_print; + irp->pass_num = pass_num; irp->codegen = codegen; irp->f = f; irp->indent = indent_size; irp->indent_size = indent_size; + irp->printed = {}; + irp->printed.init(4); + irp->pending = {}; - ir_print_instruction(irp, instruction); + ir_print_instruction(irp, instruction, false); } diff --git a/src/ir_print.hpp b/src/ir_print.hpp index 3c784757d5..3e554ceb95 100644 --- a/src/ir_print.hpp +++ b/src/ir_print.hpp @@ -12,7 +12,7 @@ #include <stdio.h> -void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size); -void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size); +void ir_print(CodeGen *codegen, FILE *f, IrExecutable *executable, int indent_size, size_t pass_num); +void ir_print_instruction(CodeGen *codegen, FILE *f, IrInstruction *instruction, int indent_size, size_t pass_num); #endif diff --git a/src/os.cpp b/src/os.cpp index 6c1a2581df..5fa70bd260 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1125,27 +1125,29 @@ Error os_get_cwd(Buf *out_cwd) { #endif } +#if defined(ZIG_OS_WINDOWS) #define is_wprefix(s, prefix) \ (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) -bool ATTRIBUTE_MUST_USE os_is_cygwin_pty(int fd) { -#if defined(ZIG_OS_WINDOWS) - HANDLE handle = (HANDLE)_get_osfhandle(fd); - - // Cygwin/msys's pty is a pipe. - if (handle == INVALID_HANDLE_VALUE || GetFileType(handle) != FILE_TYPE_PIPE) { +static bool is_stderr_cyg_pty(void) { + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + if (stderr_handle == INVALID_HANDLE_VALUE) return false; - } int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH; + FILE_NAME_INFO *nameinfo; WCHAR *p = NULL; - FILE_NAME_INFO *nameinfo = (FILE_NAME_INFO *)allocate<char>(size); + // Cygwin/msys's pty is a pipe. + if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) { + return 0; + } + nameinfo = (FILE_NAME_INFO *)allocate<char>(size); if (nameinfo == NULL) { - return false; + return 0; } // Check the name of the pipe: // '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' - if (GetFileInformationByHandleEx(handle, FileNameInfo, nameinfo, size)) { + if (GetFileInformationByHandleEx(stderr_handle, FileNameInfo, nameinfo, size)) { nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; p = nameinfo->FileName; if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ @@ -1178,14 +1180,12 @@ bool ATTRIBUTE_MUST_USE os_is_cygwin_pty(int fd) { } free(nameinfo); return (p != NULL); -#else - return false; -#endif } +#endif bool os_stderr_tty(void) { #if defined(ZIG_OS_WINDOWS) - return _isatty(fileno(stderr)) != 0 || os_is_cygwin_pty(fileno(stderr)); + return _isatty(_fileno(stderr)) != 0 || is_stderr_cyg_pty(); #elif defined(ZIG_OS_POSIX) return isatty(STDERR_FILENO) != 0; #else @@ -1486,7 +1486,7 @@ WORD original_console_attributes = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BL void os_stderr_set_color(TermColor color) { #if defined(ZIG_OS_WINDOWS) - if (os_stderr_tty()) { + if (is_stderr_cyg_pty()) { set_color_posix(color); return; } diff --git a/src/os.hpp b/src/os.hpp index 7354528c34..c8135e9844 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -11,7 +11,6 @@ #include "list.hpp" #include "buffer.hpp" #include "error.hpp" -#include "target.hpp" #include "zig_llvm.h" #include "windows_sdk.h" @@ -89,11 +88,6 @@ struct Termination { #define OsFile int #endif -#if defined(ZIG_OS_WINDOWS) -#undef fileno -#define fileno _fileno -#endif - struct OsTimeStamp { uint64_t sec; uint64_t nsec; @@ -158,8 +152,6 @@ Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); Error ATTRIBUTE_MUST_USE os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); -bool ATTRIBUTE_MUST_USE os_is_cygwin_pty(int fd); - Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths); #endif diff --git a/src/parser.cpp b/src/parser.cpp index 6cd6c2f045..21bbc4d246 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2075,7 +2075,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) { res->column = first->start_column; res->data.param_decl.name = token_buf(name); res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias; - res->data.param_decl.is_inline = first->id == TokenIdKeywordCompTime; + res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime; return res; } diff --git a/src/target.cpp b/src/target.cpp index 65d72bf5f2..8d73af6a01 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -491,16 +491,6 @@ Error target_parse_glibc_version(ZigGLibCVersion *glibc_ver, const char *text) { return ErrorNone; } -static ZigLLVM_EnvironmentType target_get_win32_abi() { - FILE* files[] = { stdin, stdout, stderr, nullptr }; - for (int i = 0; files[i] != nullptr; i++) { - if (os_is_cygwin_pty(fileno(files[i]))) { - return ZigLLVM_GNU; - } - } - return ZigLLVM_MSVC; -} - void get_native_target(ZigTarget *target) { // first zero initialize *target = {}; @@ -515,9 +505,6 @@ void get_native_target(ZigTarget *target) { &target->abi, &oformat); target->os = get_zig_os_type(os_type); - if (target->os == OsWindows) { - target->abi = target_get_win32_abi(); - } target->is_native = true; if (target->abi == ZigLLVM_UnknownEnvironment) { target->abi = target_default_abi(target->arch, target->os); @@ -1614,7 +1601,7 @@ ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { return ZigLLVM_GNU; case OsUefi: case OsWindows: - return ZigLLVM_MSVC; + return ZigLLVM_MSVC; case OsLinux: case OsWASI: return ZigLLVM_Musl; diff --git a/std/mem.zig b/std/mem.zig index 014be487cc..61dc5c7a30 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -117,7 +117,15 @@ pub const Allocator = struct { const byte_slice = try self.reallocFn(self, ([*]u8)(undefined)[0..0], undefined, byte_count, a); assert(byte_slice.len == byte_count); @memset(byte_slice.ptr, undefined, byte_slice.len); - return @bytesToSlice(T, @alignCast(a, byte_slice)); + if (alignment == null) { + // TODO This is a workaround for zig not being able to successfully do + // @bytesToSlice(T, @alignCast(a, byte_slice)) without resolving alignment of T, + // which causes a circular dependency in async functions which try to heap-allocate + // their own frame with @Frame(func). + return @intToPtr([*]T, @ptrToInt(byte_slice.ptr))[0..n]; + } else { + return @bytesToSlice(T, @alignCast(a, byte_slice)); + } } /// This function requests a new byte size for an existing allocation, diff --git a/std/os/windows.zig b/std/os/windows.zig index 5b07de6b3b..7c1761a4b8 100644 --- a/std/os/windows.zig +++ b/std/os/windows.zig @@ -65,7 +65,7 @@ pub const CreateFileError = error{ InvalidUtf8, /// On Windows, file paths cannot contain these characters: - /// '*', '?', '"', '<', '>', '|', and '/' (when the ABI is not GNU) + /// '/', '*', '?', '"', '<', '>', '|' BadPathName, Unexpected, @@ -836,10 +836,11 @@ pub fn sliceToPrefixedSuffixedFileW(s: []const u8, comptime suffix: []const u16) // > converting the name to an NT-style name, except when using the "\\?\" // > prefix as detailed in the following sections. // from https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#maximum-path-length-limitation + // Because we want the larger maximum path length for absolute paths, we + // disallow forward slashes in zig std lib file functions on Windows. for (s) |byte| { switch (byte) { - '*', '?', '"', '<', '>', '|' => return error.BadPathName, - '/' => if (builtin.abi == .msvc) return error.BadPathName, + '/', '*', '?', '"', '<', '>', '|' => return error.BadPathName, else => {}, } } diff --git a/std/special/compiler_rt/comparetf2.zig b/std/special/compiler_rt/comparetf2.zig index aaaba954d6..05e5974558 100644 --- a/std/special/compiler_rt/comparetf2.zig +++ b/std/special/compiler_rt/comparetf2.zig @@ -38,12 +38,14 @@ pub extern fn __letf2(a: f128, b: f128) c_int { // If at least one of a and b is positive, we get the same result comparing // a and b as signed integers as we would with a floating-point compare. - return if ((aInt & bInt) >= 0) if (aInt < bInt) - LE_LESS - else if (aInt == bInt) - LE_EQUAL + return if ((aInt & bInt) >= 0) + if (aInt < bInt) + LE_LESS + else if (aInt == bInt) + LE_EQUAL + else + LE_GREATER else - LE_GREATER else // Otherwise, both are negative, so we need to flip the sense of the // comparison to get the correct result. (This assumes a twos- or ones- // complement integer representation; if integers are represented in a @@ -73,7 +75,6 @@ pub extern fn __getf2(a: f128, b: f128) c_int { if (aAbs > infRep or bAbs > infRep) return GE_UNORDERED; if ((aAbs | bAbs) == 0) return GE_EQUAL; - // zig fmt issue here, see https://github.com/ziglang/zig/issues/2661 return if ((aInt & bInt) >= 0) if (aInt < bInt) GE_LESS diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index c601e6e8bf..b0c7b9135e 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -482,6 +482,27 @@ test "zig fmt: if-else with comment before else" { ); } +test "zig fmt: if nested" { + try testCanonical( + \\pub fn foo() void { + \\ return if ((aInt & bInt) >= 0) + \\ if (aInt < bInt) + \\ GE_LESS + \\ else if (aInt == bInt) + \\ GE_EQUAL + \\ else + \\ GE_GREATER + \\ else if (aInt > bInt) + \\ GE_LESS + \\ else if (aInt == bInt) + \\ GE_EQUAL + \\ else + \\ GE_GREATER; + \\} + \\ + ); +} + test "zig fmt: respect line breaks in if-else" { try testCanonical( \\comptime { diff --git a/std/zig/render.zig b/std/zig/render.zig index 5695e8801f..9db7939a22 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -276,7 +276,6 @@ fn renderTopLevelDecl(allocator: *mem.Allocator, stream: var, tree: *ast.Tree, i } else { try renderExpression(allocator, stream, tree, indent, start_col, field.type_expr.?, Space.Comma); // type, } - } else if (field.type_expr == null and field.value_expr != null) { try renderToken(tree, stream, field.name_token, indent, start_col, Space.Space); // name try renderToken(tree, stream, tree.nextToken(field.name_token), indent, start_col, Space.Space); // = @@ -1521,9 +1520,12 @@ fn renderExpression( try renderExpression(allocator, stream, tree, indent, start_col, if_node.condition, Space.None); // condition + const body_is_if_block = if_node.body.id == ast.Node.Id.If; const body_is_block = nodeIsBlock(if_node.body); - if (body_is_block) { + if (body_is_if_block) { + try renderExtraNewline(tree, stream, start_col, if_node.body); + } else if (body_is_block) { const after_rparen_space = if (if_node.payload == null) Space.BlockStart else Space.Space; try renderToken(tree, stream, rparen, indent, start_col, after_rparen_space); // ) diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 91916e6f38..12f17ec790 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -3,6 +3,22 @@ const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { cases.add( + "wrong type for result ptr to @asyncCall", + \\export fn entry() void { + \\ _ = async amain(); + \\} + \\fn amain() i32 { + \\ var frame: @Frame(foo) = undefined; + \\ return await @asyncCall(&frame, false, foo); + \\} + \\fn foo() i32 { + \\ return 1234; + \\} + , + "tmp.zig:6:37: error: expected type '*i32', found 'bool'", + ); + + cases.add( "struct depends on itself via optional field", \\const LhsExpr = struct { \\ rhsExpr: ?AstObject, @@ -1051,6 +1067,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\const Foo = struct {}; \\export fn a() void { \\ const T = [*c]Foo; + \\ var t: T = undefined; \\} , "tmp.zig:3:19: error: C pointers cannot point to non-C-ABI-compatible type 'Foo'", @@ -2290,6 +2307,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "error union operator with non error set LHS", \\comptime { \\ const z = i32!i32; + \\ var x: z = undefined; \\} , "tmp.zig:2:15: error: expected error set type, found type 'i32'", diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index dfed1c4ab7..005a790f5f 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -331,8 +331,9 @@ test "async fn with inferred error set" { fn doTheTest() void { var frame: [1]@Frame(middle) = undefined; - var result: anyerror!void = undefined; - _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle); + var fn_ptr = middle; + var result: @typeOf(fn_ptr).ReturnType.ErrorSet!void = undefined; + _ = @asyncCall(@sliceToBytes(frame[0..]), &result, fn_ptr); resume global_frame; std.testing.expectError(error.Fail, result); } @@ -828,6 +829,34 @@ test "cast fn to async fn when it is inferred to be async" { ptr = func; var buf: [100]u8 align(16) = undefined; var result: i32 = undefined; + const f = @asyncCall(&buf, &result, ptr); + _ = await f; + expect(result == 1234); + ok = true; + } + + fn func() i32 { + suspend { + frame = @frame(); + } + return 1234; + } + }; + _ = async S.doTheTest(); + resume S.frame; + expect(S.ok); +} + +test "cast fn to async fn when it is inferred to be async, awaited directly" { + const S = struct { + var frame: anyframe = undefined; + var ok = false; + + fn doTheTest() void { + var ptr: async fn () i32 = undefined; + ptr = func; + var buf: [100]u8 align(16) = undefined; + var result: i32 = undefined; _ = await @asyncCall(&buf, &result, ptr); expect(result == 1234); ok = true; @@ -854,3 +883,151 @@ test "await does not force async if callee is blocking" { var x = async S.simple(); expect(await x == 1234); } + +test "recursive async function" { + expect(recursiveAsyncFunctionTest(false).doTheTest() == 55); + expect(recursiveAsyncFunctionTest(true).doTheTest() == 55); +} + +fn recursiveAsyncFunctionTest(comptime suspending_implementation: bool) type { + return struct { + fn fib(allocator: *std.mem.Allocator, x: u32) error{OutOfMemory}!u32 { + if (x <= 1) return x; + + if (suspending_implementation) { + suspend { + resume @frame(); + } + } + + const f1 = try allocator.create(@Frame(fib)); + defer allocator.destroy(f1); + + const f2 = try allocator.create(@Frame(fib)); + defer allocator.destroy(f2); + + f1.* = async fib(allocator, x - 1); + var f1_awaited = false; + errdefer if (!f1_awaited) { + _ = await f1; + }; + + f2.* = async fib(allocator, x - 2); + var f2_awaited = false; + errdefer if (!f2_awaited) { + _ = await f2; + }; + + var sum: u32 = 0; + + f1_awaited = true; + const result_f1 = await f1; // TODO https://github.com/ziglang/zig/issues/3077 + sum += try result_f1; + + f2_awaited = true; + const result_f2 = await f2; // TODO https://github.com/ziglang/zig/issues/3077 + sum += try result_f2; + + return sum; + } + + fn doTheTest() u32 { + if (suspending_implementation) { + var result: u32 = undefined; + _ = async amain(&result); + return result; + } else { + return fib(std.heap.direct_allocator, 10) catch unreachable; + } + } + + fn amain(result: *u32) void { + var x = async fib(std.heap.direct_allocator, 10); + const res = await x; // TODO https://github.com/ziglang/zig/issues/3077 + result.* = res catch unreachable; + } + }; +} + +test "@asyncCall with comptime-known function, but not awaited directly" { + const S = struct { + var global_frame: anyframe = undefined; + + fn doTheTest() void { + var frame: [1]@Frame(middle) = undefined; + var result: @typeOf(middle).ReturnType.ErrorSet!void = undefined; + _ = @asyncCall(@sliceToBytes(frame[0..]), &result, middle); + resume global_frame; + std.testing.expectError(error.Fail, result); + } + + async fn middle() !void { + var f = async middle2(); + return await f; + } + + fn middle2() !void { + return failing(); + } + + fn failing() !void { + global_frame = @frame(); + suspend; + return error.Fail; + } + }; + S.doTheTest(); +} + +test "@asyncCall with actual frame instead of byte buffer" { + const S = struct { + fn func() i32 { + suspend; + return 1234; + } + }; + var frame: @Frame(S.func) = undefined; + var result: i32 = undefined; + const ptr = @asyncCall(&frame, &result, S.func); + resume ptr; + expect(result == 1234); +} + +test "@asyncCall using the result location inside the frame" { + const S = struct { + async fn simple2(y: *i32) i32 { + defer y.* += 2; + y.* += 1; + suspend; + return 1234; + } + fn getAnswer(f: anyframe->i32, out: *i32) void { + var res = await f; // TODO https://github.com/ziglang/zig/issues/3077 + out.* = res; + } + }; + var data: i32 = 1; + const Foo = struct { + bar: async fn (*i32) i32, + }; + var foo = Foo{ .bar = S.simple2 }; + var bytes: [64]u8 align(16) = undefined; + const f = @asyncCall(&bytes, {}, foo.bar, &data); + comptime expect(@typeOf(f) == anyframe->i32); + expect(data == 2); + resume f; + expect(data == 4); + _ = async S.getAnswer(f, &data); + expect(data == 1234); +} + +test "@typeOf an async function call of generic fn with error union type" { + const S = struct { + fn func(comptime x: var) anyerror!i32 { + const T = @typeOf(async func(x)); + comptime expect(T == @typeOf(@frame()).Child); + return undefined; + } + }; + _ = async S.func(i32); +} diff --git a/test/stage1/behavior/sizeof_and_typeof.zig b/test/stage1/behavior/sizeof_and_typeof.zig index da79c3a270..6f57bfedd5 100644 --- a/test/stage1/behavior/sizeof_and_typeof.zig +++ b/test/stage1/behavior/sizeof_and_typeof.zig @@ -89,3 +89,29 @@ test "@sizeOf(T) == 0 doesn't force resolving struct size" { expect(@sizeOf(S.Foo) == 4); expect(@sizeOf(S.Bar) == 8); } + +test "@typeOf() has no runtime side effects" { + const S = struct { + fn foo(comptime T: type, ptr: *T) T { + ptr.* += 1; + return ptr.*; + } + }; + var data: i32 = 0; + const T = @typeOf(S.foo(i32, &data)); + comptime expect(T == i32); + expect(data == 0); +} + +test "branching logic inside @typeOf" { + const S = struct { + var data: i32 = 0; + fn foo() anyerror!i32 { + data += 1; + return undefined; + } + }; + const T = @typeOf(S.foo() catch undefined); + comptime expect(T == i32); + expect(S.data == 0); +} |
