diff --git a/CMakeLists.txt b/CMakeLists.txt index beb4ac671c..f296f9dee4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,14 +23,18 @@ find_program(GIT_EXE NAMES git) if(GIT_EXE) execute_process( COMMAND ${GIT_EXE} -C ${CMAKE_SOURCE_DIR} name-rev HEAD --tags --name-only --no-undefined --always + RESULT_VARIABLE EXIT_STATUS OUTPUT_VARIABLE ZIG_GIT_REV - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(ZIG_GIT_REV MATCHES "\\^0$") - if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0")) - message("WARNING: Tag does not match configured Zig version") + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET) + if(EXIT_STATUS EQUAL "0") + if(ZIG_GIT_REV MATCHES "\\^0$") + if(NOT("${ZIG_GIT_REV}" STREQUAL "${ZIG_VERSION}^0")) + message("WARNING: Tag does not match configured Zig version") + endif() + else() + set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}") endif() - else() - set(ZIG_VERSION "${ZIG_VERSION}+${ZIG_GIT_REV}") endif() endif() message("Configuring zig version ${ZIG_VERSION}") diff --git a/build.zig b/build.zig index 45758d4075..21fa79e863 100644 --- a/build.zig +++ b/build.zig @@ -138,12 +138,13 @@ pub fn build(b: *Builder) !void { test_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); test_step.dependOn(tests.addStandaloneTests(b, test_filter, modes)); + test_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); test_step.dependOn(tests.addCliTests(b, test_filter, modes)); - test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); test_step.dependOn(tests.addTranslateCTests(b, test_filter)); test_step.dependOn(tests.addGenHTests(b, test_filter)); + test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); test_step.dependOn(docs_step); } diff --git a/doc/docgen.zig b/doc/docgen.zig index 458b97d2c0..a2b4e8501c 100644 --- a/doc/docgen.zig +++ b/doc/docgen.zig @@ -307,7 +307,7 @@ const Node = union(enum) { const Toc = struct { nodes: []Node, toc: []u8, - urls: std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8), + urls: std.StringHashMap(Token), }; const Action = enum { @@ -316,7 +316,7 @@ const Action = enum { }; fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { - var urls = std.HashMap([]const u8, Token, mem.hash_slice_u8, mem.eql_slice_u8).init(allocator); + var urls = std.StringHashMap(Token).init(allocator); errdefer urls.deinit(); var header_stack_size: usize = 0; diff --git a/src-self-hosted/arg.zig b/src-self-hosted/arg.zig index 8f95e2d7ef..8b0ea5ea61 100644 --- a/src-self-hosted/arg.zig +++ b/src-self-hosted/arg.zig @@ -5,7 +5,7 @@ const mem = std.mem; const Allocator = mem.Allocator; const ArrayList = std.ArrayList; -const HashMap = std.HashMap; +const StringHashMap = std.StringHashMap; fn trimStart(slice: []const u8, ch: u8) []const u8 { var i: usize = 0; @@ -73,7 +73,7 @@ fn readFlagArguments(allocator: *Allocator, args: []const []const u8, required: } } -const HashMapFlags = HashMap([]const u8, FlagArg, std.hash.Fnv1a_32.hash, mem.eql_slice_u8); +const HashMapFlags = StringHashMap(FlagArg); // A store for querying found flags and positional arguments. pub const Args = struct { diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index a64e52a2b6..1e71a5e561 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -249,7 +249,7 @@ pub const Compilation = struct { const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql); const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql); const FnTypeTable = std.HashMap(*const Type.Fn.Key, *Type.Fn, Type.Fn.Key.hash, Type.Fn.Key.eql); - const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8); + const TypeTable = std.StringHashMap(*Type); const CompileErrList = std.ArrayList(*Msg); diff --git a/src-self-hosted/decl.zig b/src-self-hosted/decl.zig index 25fcf195d1..1af06dea39 100644 --- a/src-self-hosted/decl.zig +++ b/src-self-hosted/decl.zig @@ -20,7 +20,7 @@ pub const Decl = struct { // TODO when we destroy the decl, deref the tree scope tree_scope: *Scope.AstTree, - pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8); + pub const Table = std.StringHashMap(*Decl); pub fn cast(base: *Decl, comptime T: type) ?*T { if (base.id != @field(Id, @typeName(T))) return null; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index 5136b32735..52eda5824a 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -541,7 +541,7 @@ const Fmt = struct { color: errmsg.Color, loop: *event.Loop, - const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); + const SeenMap = std.StringHashMap(void); }; fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void { diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig index 0d31731b55..c8d46c7719 100644 --- a/src-self-hosted/package.zig +++ b/src-self-hosted/package.zig @@ -10,7 +10,7 @@ pub const Package = struct { /// relative to root_src_dir table: Table, - pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8); + pub const Table = std.StringHashMap(*Package); /// makes internal copies of root_src_dir and root_src_path /// allocator should be an arena allocator because Package never frees anything diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index b8f13b5d03..2f48f2f450 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -343,7 +343,7 @@ const Fmt = struct { color: errmsg.Color, allocator: *mem.Allocator, - const SeenMap = std.HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); + const SeenMap = std.StringHashMap(void); }; fn printErrMsgToFile( @@ -376,7 +376,7 @@ fn printErrMsgToFile( const text = text_buf.toOwnedSlice(); const stream = &file.outStream().stream; - try stream.print( "{}:{}:{}: error: {}\n", path, start_loc.line + 1, start_loc.column + 1, text); + try stream.print("{}:{}:{}: error: {}\n", path, start_loc.line + 1, start_loc.column + 1, text); if (!color_on) return; diff --git a/src/all_types.hpp b/src/all_types.hpp index d9e1dc44ca..1a97cf2814 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1989,6 +1989,7 @@ struct CodeGen { bool system_linker_hack; bool reported_bad_link_libc_error; bool is_dynamic; // shared library rather than static library. dynamic musl rather than static musl. + bool need_frame_size_prefix_data; //////////////////////////// Participates in Input Parameter Cache Hash /////// Note: there is a separate cache hash for builtin.zig, when adding fields, @@ -2003,6 +2004,7 @@ struct CodeGen { ZigList assembly_files; ZigList c_source_files; ZigList lib_dirs; + ZigList framework_dirs; ZigLibCInstallation *libc; diff --git a/src/analyze.cpp b/src/analyze.cpp index 9b361baa56..e1d6b59ddf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2671,6 +2671,10 @@ static Error resolve_struct_alignment(CodeGen *g, ZigType *struct_type) { } } + if (!type_has_bits(struct_type)) { + assert(struct_type->abi_align == 0); + } + struct_type->data.structure.resolve_loop_flag_other = false; if (struct_type->data.structure.resolve_status == ResolveStatusInvalid) { @@ -4191,7 +4195,7 @@ bool fn_is_async(ZigFn *fn) { return fn->inferred_async_node != inferred_async_none; } -static void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { +void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn) { assert(fn->inferred_async_node != nullptr); assert(fn->inferred_async_node != inferred_async_checking); assert(fn->inferred_async_node != inferred_async_none); @@ -7687,8 +7691,13 @@ static void resolve_llvm_types_union(CodeGen *g, ZigType *union_type, ResolveSta ZigType *tag_type = union_type->data.unionation.tag_type; uint32_t gen_field_count = union_type->data.unionation.gen_field_count; if (gen_field_count == 0) { - union_type->llvm_type = get_llvm_type(g, tag_type); - union_type->llvm_di_type = get_llvm_di_type(g, tag_type); + if (tag_type == nullptr) { + union_type->llvm_type = g->builtin_types.entry_void->llvm_type; + union_type->llvm_di_type = g->builtin_types.entry_void->llvm_di_type; + } else { + union_type->llvm_type = get_llvm_type(g, tag_type); + union_type->llvm_di_type = get_llvm_di_type(g, tag_type); + } union_type->data.unionation.resolve_status = ResolveStatusLLVMFull; return; } diff --git a/src/analyze.hpp b/src/analyze.hpp index dc702167e7..9f2c984992 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -256,4 +256,6 @@ Error type_val_resolve_zero_bits(CodeGen *g, ConstExprValue *type_val, ZigType * ZigType *resolve_union_field_type(CodeGen *g, TypeUnionField *union_field); ZigType *resolve_struct_field_type(CodeGen *g, TypeStructField *struct_field); +void add_async_error_notes(CodeGen *g, ErrorMsg *msg, ZigFn *fn); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 890724d950..d1499592d2 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3522,6 +3522,15 @@ static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutable *executable, Ir assert(ptr_type->id == ZigTypeIdPointer); if (!type_has_bits(ptr_type)) return nullptr; + if (instruction->ptr->ref_count == 0) { + // In this case, this StorePtr instruction should be elided. Something happened like this: + // var t = true; + // const x = if (t) Num.Two else unreachable; + // The if condition is a runtime value, so the StorePtr for `x = Num.Two` got generated + // (this instruction being rendered) but because of `else unreachable` the result ended + // up being a comptime const value. + return nullptr; + } bool have_init_expr = !value_is_all_undef(&instruction->value->value); if (have_init_expr) { @@ -3766,6 +3775,7 @@ static void render_async_var_decls(CodeGen *g, Scope *scope) { } static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { + assert(g->need_frame_size_prefix_data); LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type; LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0); LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); @@ -4103,6 +4113,8 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executable, IrInstructionStructFieldPtr *instruction) { + Error err; + if (instruction->base.value.special != ConstValSpecialRuntime) return nullptr; @@ -4120,6 +4132,11 @@ static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutable *executa return struct_ptr; } + ZigType *struct_type = (struct_ptr_type->id == ZigTypeIdPointer) ? + struct_ptr_type->data.pointer.child_type : struct_ptr_type; + if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) + report_errors_and_exit(g); + assert(field->gen_index != SIZE_MAX); return LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); } @@ -7199,7 +7216,9 @@ static void do_code_gen(CodeGen *g) { LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; LLVMValueRef size_val = LLVMConstInt(usize_type_ref, fn_table_entry->frame_type->abi_size, false); - ZigLLVMFunctionSetPrefixData(fn_table_entry->llvm_value, size_val); + if (g->need_frame_size_prefix_data) { + ZigLLVMFunctionSetPrefixData(fn_table_entry->llvm_value, size_val); + } if (!g->strip_debug_symbols) { AstNode *source_node = fn_table_entry->proto_node; @@ -8445,8 +8464,21 @@ static void init(CodeGen *g) { Buf *producer = buf_sprintf("zig %d.%d.%d", ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH); const char *flags = ""; unsigned runtime_version = 0; + + // For macOS stack traces, we want to avoid having to parse the compilation unit debug + // info. As long as each debug info file has a path independent of the compilation unit + // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug + // info. If we provide an absolute path to LLVM here for the compilation unit debug info, + // LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we pass "." + // for the compilation unit directory. This forces each debug file to have a directory + // rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug files will + // no longer reference DW_AT_comp_dir, for the purpose of being able to support the + // common practice of stripping all but the line number sections from an executable. + const char *compile_unit_dir = target_os_is_darwin(g->zig_target->os) ? "." : + buf_ptr(&g->root_package->root_src_dir); + ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name), - buf_ptr(&g->root_package->root_src_dir)); + compile_unit_dir); g->compile_unit = ZigLLVMCreateCompileUnit(g->dbuilder, ZigLLVMLang_DW_LANG_C99(), compile_unit_file, buf_ptr(producer), is_optimized, flags, runtime_version, "", 0, !g->strip_debug_symbols); @@ -8873,6 +8905,15 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { for (size_t i = 0; i < g->test_fns.length; i += 1) { ZigFn *test_fn_entry = g->test_fns.at(i); + if (fn_is_async(test_fn_entry)) { + ErrorMsg *msg = add_node_error(g, test_fn_entry->proto_node, + buf_create_from_str("test functions cannot be async")); + add_error_note(g, msg, test_fn_entry->proto_node, + buf_sprintf("this restriction may be lifted in the future. See https://github.com/ziglang/zig/issues/3117 for more details")); + add_async_error_notes(g, msg, test_fn_entry); + continue; + } + ConstExprValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; this_val->special = ConstValSpecialStatic; this_val->type = struct_type; @@ -8892,6 +8933,7 @@ static void create_test_compile_var_and_add_test_runner(CodeGen *g) { fn_field->data.x_ptr.mut = ConstPtrMutComptimeConst; fn_field->data.x_ptr.data.fn.fn_entry = test_fn_entry; } + report_errors_and_maybe_exit(g); ConstExprValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true); @@ -9803,6 +9845,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len); cache_list_of_str(ch, g->clang_argv, g->clang_argv_len); cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); + cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length); if (g->libc) { cache_buf(ch, &g->libc->include_dir); cache_buf(ch, &g->libc->sys_include_dir); diff --git a/src/ir.cpp b/src/ir.cpp index e1c7bb37fe..7415a2dd6b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -330,6 +330,8 @@ static bool ir_should_inline(IrExecutable *exec, Scope *scope) { while (scope != nullptr) { if (scope->id == ScopeIdCompTime) return true; + if (scope->id == ScopeIdTypeOf) + return false; if (scope->id == ScopeIdFnDef) break; scope = scope->parent; @@ -14837,6 +14839,12 @@ static IrInstruction *ir_analyze_alloca(IrAnalyze *ira, IrInstruction *source_in if (align != 0) { if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown))) return ira->codegen->invalid_instruction; + if (!type_has_bits(var_type)) { + ir_add_error(ira, source_inst, + buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned", + name_hint, buf_ptr(&var_type->name))); + return ira->codegen->invalid_instruction; + } } assert(result->base.value.data.x_ptr.special != ConstPtrSpecialInvalid); @@ -15648,6 +15656,32 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source return &store_ptr->base; } +static IrInstruction *analyze_casted_new_stack(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, + ZigFn *fn_entry) +{ + if (call_instruction->new_stack == nullptr) + return nullptr; + + IrInstruction *new_stack = call_instruction->new_stack->child; + if (type_is_invalid(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); + return ir_implicit_cast(ira, new_stack, needed_frame_type); + } 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); + ira->codegen->need_frame_size_prefix_data = true; + return ir_implicit_cast(ira, new_stack, u8_slice); + } +} + static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *call_instruction, ZigFn *fn_entry, ZigType *fn_type, IrInstruction *fn_ref, IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline) @@ -15826,31 +15860,6 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c return ir_finish_anal(ira, new_instruction); } - IrInstruction *casted_new_stack = nullptr; - if (call_instruction->new_stack != nullptr) { - IrInstruction *new_stack = call_instruction->new_stack->child; - if (type_is_invalid(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) { if (!fn_entry) { ir_add_error(ira, call_instruction->fn_ref, @@ -16063,6 +16072,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c parent_fn_entry->calls_or_awaits_errorable_fn = true; } + IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, call_instruction, impl_fn); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value.type)) + return ira->codegen->invalid_instruction; + 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, @@ -16071,11 +16084,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } IrInstruction *result_loc; - 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)) { + 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) { @@ -16087,6 +16096,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c result_loc = nullptr; } } + } else 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 { result_loc = nullptr; } @@ -16211,6 +16224,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c return ira->codegen->invalid_instruction; } + IrInstruction *casted_new_stack = analyze_casted_new_stack(ira, call_instruction, fn_entry); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value.type)) + return ira->codegen->invalid_instruction; + if (call_instruction->is_async) { IrInstruction *result = ir_analyze_async_call(ira, call_instruction, fn_entry, fn_type, fn_ref, casted_args, call_param_count, casted_new_stack); @@ -16223,11 +16240,7 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c } IrInstruction *result_loc; - 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)) { + 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) { @@ -16239,6 +16252,10 @@ static IrInstruction *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCallSrc *c result_loc = nullptr; } } + } else 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 { result_loc = nullptr; } @@ -17453,7 +17470,12 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ return ir_analyze_container_member_access_inner(ira, bare_type, field_name, source_instr, container_ptr, container_type); } - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, + + ZigType *field_type = resolve_union_field_type(ira->codegen, field); + if (field_type == nullptr) + return ira->codegen->invalid_instruction; + + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); if (instr_is_comptime(container_ptr)) { ConstExprValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); @@ -17470,7 +17492,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ if (initializing) { ConstExprValue *payload_val = create_const_vals(1); payload_val->special = ConstValSpecialUndef; - payload_val->type = field->type_entry; + payload_val->type = field_type; payload_val->parent.id = ConstParentIdUnion; payload_val->parent.data.p_union.union_val = union_val; @@ -22523,6 +22545,8 @@ static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstru return ira->codegen->invalid_instruction; } + ira->codegen->need_frame_size_prefix_data = true; + IrInstruction *result = ir_build_frame_size_gen(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn); result->value.type = ira->codegen->builtin_types.entry_usize; diff --git a/src/link.cpp b/src/link.cpp index 8a1f889234..5519f98fd2 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -2510,6 +2510,12 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("dynamic_lookup"); } + for (size_t i = 0; i < g->framework_dirs.length; i += 1) { + const char *framework_dir = g->framework_dirs.at(i); + lj->args.append("-F"); + lj->args.append(framework_dir); + } + for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { lj->args.append("-framework"); lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); diff --git a/src/main.cpp b/src/main.cpp index c0945ef180..6f1ccd418c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -104,6 +104,7 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) { " -rdynamic add all symbols to the dynamic symbol table\n" " -rpath [path] add directory to the runtime library search path\n" " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" + " -F[dir] (darwin) add search path for frameworks\n" " -framework [name] (darwin) link against framework\n" " -mios-version-min [ver] (darwin) set iOS deployment target\n" " -mmacosx-version-min [ver] (darwin) set Mac OS X deployment target\n" @@ -454,6 +455,7 @@ int main(int argc, char **argv) { ZigList lib_dirs = {0}; ZigList link_libs = {0}; ZigList forbidden_link_libs = {0}; + ZigList framework_dirs = {0}; ZigList frameworks = {0}; bool have_libc = false; const char *target_string = nullptr; @@ -686,6 +688,8 @@ int main(int argc, char **argv) { } else if (arg[1] == 'L' && arg[2] != 0) { // alias for --library-path lib_dirs.append(&arg[2]); + } else if (arg[1] == 'F' && arg[2] != 0) { + framework_dirs.append(&arg[2]); } else if (strcmp(arg, "--pkg-begin") == 0) { if (i + 2 >= argc) { fprintf(stderr, "Expected 2 arguments after --pkg-begin\n"); @@ -772,6 +776,8 @@ int main(int argc, char **argv) { main_pkg_path = buf_create_from_str(argv[i]); } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { lib_dirs.append(argv[i]); + } else if (strcmp(arg, "-F") == 0) { + framework_dirs.append(argv[i]); } else if (strcmp(arg, "--library") == 0) { if (strcmp(argv[i], "c") == 0) have_libc = true; @@ -1153,6 +1159,9 @@ int main(int argc, char **argv) { for (size_t i = 0; i < lib_dirs.length; i += 1) { codegen_add_lib_dir(g, lib_dirs.at(i)); } + for (size_t i = 0; i < framework_dirs.length; i += 1) { + g->framework_dirs.append(framework_dirs.at(i)); + } for (size_t i = 0; i < link_libs.length; i += 1) { LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); link_lib->provided_explicitly = true; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index c19abbbac8..4f25cd3b98 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -842,6 +842,7 @@ const char *ZigLLVMGetSubArchTypeName(ZigLLVM_SubArchType sub_arch) { void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module) { unwrap(module)->addModuleFlag(Module::Warning, "Debug Info Version", DEBUG_METADATA_VERSION); + unwrap(module)->addModuleFlag(Module::Warning, "Dwarf Version", 4); } void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module) { diff --git a/std/buf_map.zig b/std/buf_map.zig index 4079d41caf..d7aa314157 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -1,5 +1,5 @@ const std = @import("std.zig"); -const HashMap = std.HashMap; +const StringHashMap = std.StringHashMap; const mem = std.mem; const Allocator = mem.Allocator; const testing = std.testing; @@ -9,7 +9,7 @@ const testing = std.testing; pub const BufMap = struct { hash_map: BufMapHashMap, - const BufMapHashMap = HashMap([]const u8, []const u8, mem.hash_slice_u8, mem.eql_slice_u8); + const BufMapHashMap = StringHashMap([]const u8); pub fn init(allocator: *Allocator) BufMap { var self = BufMap{ .hash_map = BufMapHashMap.init(allocator) }; diff --git a/std/buf_set.zig b/std/buf_set.zig index 33e66a64e8..1a321e89c9 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -1,5 +1,5 @@ const std = @import("std.zig"); -const HashMap = @import("hash_map.zig").HashMap; +const StringHashMap = std.StringHashMap; const mem = @import("mem.zig"); const Allocator = mem.Allocator; const testing = std.testing; @@ -7,7 +7,7 @@ const testing = std.testing; pub const BufSet = struct { hash_map: BufSetHashMap, - const BufSetHashMap = HashMap([]const u8, void, mem.hash_slice_u8, mem.eql_slice_u8); + const BufSetHashMap = StringHashMap(void); pub fn init(a: *Allocator) BufSet { var self = BufSet{ .hash_map = BufSetHashMap.init(a) }; diff --git a/std/build.zig b/std/build.zig index 9393d72c15..1318a87b65 100644 --- a/std/build.zig +++ b/std/build.zig @@ -4,10 +4,11 @@ const io = std.io; const fs = std.fs; const mem = std.mem; const debug = std.debug; +const panic = std.debug.panic; const assert = debug.assert; const warn = std.debug.warn; const ArrayList = std.ArrayList; -const HashMap = std.HashMap; +const StringHashMap = std.StringHashMap; const Allocator = mem.Allocator; const process = std.process; const BufSet = std.BufSet; @@ -42,8 +43,8 @@ pub const Builder = struct { top_level_steps: ArrayList(*TopLevelStep), install_prefix: ?[]const u8, dest_dir: ?[]const u8, - lib_dir: ?[]const u8, - exe_dir: ?[]const u8, + lib_dir: []const u8, + exe_dir: []const u8, install_path: []const u8, search_prefixes: ArrayList([]const u8), installed_files: ArrayList(InstalledFile), @@ -60,8 +61,8 @@ pub const Builder = struct { C11, }; - const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8); - const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8); + const UserInputOptionsMap = StringHashMap(UserInputOption); + const AvailableOptionsMap = StringHashMap(AvailableOption); const AvailableOption = struct { name: []const u8, @@ -129,8 +130,8 @@ pub const Builder = struct { .env_map = env_map, .search_prefixes = ArrayList([]const u8).init(allocator), .install_prefix = null, - .lib_dir = null, - .exe_dir = null, + .lib_dir = undefined, + .exe_dir = undefined, .dest_dir = env_map.get("DESTDIR"), .installed_files = ArrayList(InstalledFile).init(allocator), .install_tls = TopLevelStep{ @@ -163,11 +164,13 @@ pub const Builder = struct { self.allocator.destroy(self); } + /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. pub fn setInstallPrefix(self: *Builder, optional_prefix: ?[]const u8) void { self.install_prefix = optional_prefix; } - fn resolveInstallPrefix(self: *Builder) void { + /// This function is intended to be called by std/special/build_runner.zig, not a build.zig file. + pub fn resolveInstallPrefix(self: *Builder) void { if (self.dest_dir) |dest_dir| { const install_prefix = self.install_prefix orelse "/usr"; self.install_path = fs.path.join(self.allocator, [_][]const u8{ dest_dir, install_prefix }) catch unreachable; @@ -437,7 +440,7 @@ pub const Builder = struct { .description = description, }; if ((self.available_options_map.put(name, available_option) catch unreachable) != null) { - debug.panic("Option '{}' declared twice", name); + panic("Option '{}' declared twice", name); } self.available_options_list.append(available_option) catch unreachable; @@ -463,8 +466,8 @@ pub const Builder = struct { return null; }, }, - TypeId.Int => debug.panic("TODO integer options to build script"), - TypeId.Float => debug.panic("TODO float options to build script"), + TypeId.Int => panic("TODO integer options to build script"), + TypeId.Float => panic("TODO float options to build script"), TypeId.String => switch (entry.value.value) { UserValue.Flag => { warn("Expected -D{} to be a string, but received a boolean.\n", name); @@ -478,7 +481,7 @@ pub const Builder = struct { }, UserValue.Scalar => |s| return s, }, - TypeId.List => debug.panic("TODO list options to build script"), + TypeId.List => panic("TODO list options to build script"), } } @@ -644,8 +647,6 @@ pub const Builder = struct { } pub fn validateUserInputDidItFail(self: *Builder) bool { - self.resolveInstallPrefix(); - // make sure all args are used var it = self.user_input_options.iterator(); while (true) { @@ -855,7 +856,7 @@ pub const Builder = struct { var stdout_file_in_stream = child.stdout.?.inStream(); try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); - const term = child.wait() catch |err| std.debug.panic("unable to spawn {}: {}", argv[0], err); + const term = child.wait() catch |err| panic("unable to spawn {}: {}", argv[0], err); switch (term) { .Exited => |code| { if (code != 0) { @@ -882,8 +883,8 @@ pub const Builder = struct { fn getInstallPath(self: *Builder, dir: InstallDir, dest_rel_path: []const u8) []const u8 { const base_dir = switch (dir) { .Prefix => self.install_path, - .Bin => self.exe_dir.?, - .Lib => self.lib_dir.?, + .Bin => self.exe_dir, + .Lib => self.lib_dir, }; return fs.path.resolve( self.allocator, @@ -1228,6 +1229,7 @@ pub const LibExeObjStep = struct { name_only_filename: []const u8, strip: bool, lib_paths: ArrayList([]const u8), + framework_dirs: ArrayList([]const u8), frameworks: BufSet, verbose_link: bool, verbose_cc: bool, @@ -1317,6 +1319,9 @@ pub const LibExeObjStep = struct { } fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, is_dynamic: bool, ver: Version) LibExeObjStep { + if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { + panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", name); + } var self = LibExeObjStep{ .strip = false, .builder = builder, @@ -1341,6 +1346,7 @@ pub const LibExeObjStep = struct { .include_dirs = ArrayList(IncludeDir).init(builder.allocator), .link_objects = ArrayList(LinkObject).init(builder.allocator), .lib_paths = ArrayList([]const u8).init(builder.allocator), + .framework_dirs = ArrayList([]const u8).init(builder.allocator), .object_src = undefined, .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, .c_std = Builder.CStd.C99, @@ -1614,6 +1620,10 @@ pub const LibExeObjStep = struct { self.lib_paths.append(path) catch unreachable; } + pub fn addFrameworkDir(self: *LibExeObjStep, dir_path: []const u8) void { + self.framework_dirs.append(dir_path) catch unreachable; + } + pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { self.packages.append(Pkg{ .name = name, @@ -1860,8 +1870,8 @@ pub const LibExeObjStep = struct { } for (self.lib_paths.toSliceConst()) |lib_path| { - zig_args.append("--library-path") catch unreachable; - zig_args.append(lib_path) catch unreachable; + try zig_args.append("-L"); + try zig_args.append(lib_path); } if (self.need_system_paths and self.target == Target.Native) { @@ -1882,6 +1892,11 @@ pub const LibExeObjStep = struct { } if (self.target.isDarwin()) { + for (self.framework_dirs.toSliceConst()) |dir| { + try zig_args.append("-F"); + try zig_args.append(dir); + } + var it = self.frameworks.iterator(); while (it.next()) |entry| { zig_args.append("-framework") catch unreachable; diff --git a/std/fmt/parse_float.zig b/std/fmt/parse_float.zig index ce2cc70768..d0a826a55e 100644 --- a/std/fmt/parse_float.zig +++ b/std/fmt/parse_float.zig @@ -110,9 +110,7 @@ fn convertRepr(comptime T: type, n: FloatRepr) T { q.shiftLeft1(s); // q = p << 1 r.shiftLeft1(q); // r = p << 2 s.shiftLeft1(r); // p = p << 3 - q.add(s); // p = (p << 3) + (p << 1) - - exp -= 1; + s.add(q); // p = (p << 3) + (p << 1) while (s.d2 & mask28 != 0) { q.shiftRight1(s); @@ -402,6 +400,13 @@ test "fmt.parseFloat" { expectEqual((try parseFloat(T, "+0")), 0.0); expectEqual((try parseFloat(T, "-0")), 0.0); + expectEqual((try parseFloat(T, "0e0")), 0); + expectEqual((try parseFloat(T, "2e3")), 2000.0); + expectEqual((try parseFloat(T, "1e0")), 1.0); + expectEqual((try parseFloat(T, "-2e3")), -2000.0); + expectEqual((try parseFloat(T, "-1e0")), -1.0); + expectEqual((try parseFloat(T, "1.234e3")), 1234); + expect(approxEq(T, try parseFloat(T, "3.141"), 3.141, epsilon)); expect(approxEq(T, try parseFloat(T, "-3.141"), -3.141, epsilon)); @@ -413,6 +418,9 @@ test "fmt.parseFloat" { expectEqual((try parseFloat(T, "-INF")), -std.math.inf(T)); if (T != f16) { + expect(approxEq(T, try parseFloat(T, "1e-2"), 0.01, epsilon)); + expect(approxEq(T, try parseFloat(T, "1234e-2"), 12.34, epsilon)); + expect(approxEq(T, try parseFloat(T, "123142.1"), 123142.1, epsilon)); expect(approxEq(T, try parseFloat(T, "-123142.1124"), T(-123142.1124), epsilon)); expect(approxEq(T, try parseFloat(T, "0.7062146892655368"), T(0.7062146892655368), epsilon)); diff --git a/std/hash_map.zig b/std/hash_map.zig index 5a852d4302..4ffe88067b 100644 --- a/std/hash_map.zig +++ b/std/hash_map.zig @@ -23,9 +23,7 @@ pub fn StringHashMap(comptime V: type) type { } pub fn eqlString(a: []const u8, b: []const u8) bool { - if (a.len != b.len) return false; - if (a.ptr == b.ptr) return true; - return mem.compare(u8, a, b) == .Equal; + return mem.eql(u8, a, b); } pub fn hashString(s: []const u8) u32 { diff --git a/std/json.zig b/std/json.zig index 8324c59e32..f562a672f8 100644 --- a/std/json.zig +++ b/std/json.zig @@ -989,7 +989,7 @@ test "json.validate" { const Allocator = std.mem.Allocator; const ArenaAllocator = std.heap.ArenaAllocator; const ArrayList = std.ArrayList; -const HashMap = std.HashMap; +const StringHashMap = std.StringHashMap; pub const ValueTree = struct { arena: ArenaAllocator, @@ -1000,7 +1000,7 @@ pub const ValueTree = struct { } }; -pub const ObjectMap = HashMap([]const u8, Value, mem.hash_slice_u8, mem.eql_slice_u8); +pub const ObjectMap = StringHashMap(Value); pub const Value = union(enum) { Null, diff --git a/std/mem.zig b/std/mem.zig index 61dc5c7a30..2091eb4804 100644 --- a/std/mem.zig +++ b/std/mem.zig @@ -339,6 +339,7 @@ test "mem.lessThan" { /// Compares two slices and returns whether they are equal. pub fn eql(comptime T: type, a: []const T, b: []const T) bool { if (a.len != b.len) return false; + if (a.ptr == b.ptr) return true; for (a) |item, index| { if (b[index] != item) return false; } @@ -738,47 +739,34 @@ test "writeIntBig and writeIntLittle" { var buf9: [9]u8 = undefined; writeIntBig(u0, &buf0, 0x0); - testing.expect(eql_slice_u8(buf0[0..], [_]u8{})); + testing.expect(eql(u8, buf0[0..], [_]u8{})); writeIntLittle(u0, &buf0, 0x0); - testing.expect(eql_slice_u8(buf0[0..], [_]u8{})); + testing.expect(eql(u8, buf0[0..], [_]u8{})); writeIntBig(u8, &buf1, 0x12); - testing.expect(eql_slice_u8(buf1[0..], [_]u8{0x12})); + testing.expect(eql(u8, buf1[0..], [_]u8{0x12})); writeIntLittle(u8, &buf1, 0x34); - testing.expect(eql_slice_u8(buf1[0..], [_]u8{0x34})); + testing.expect(eql(u8, buf1[0..], [_]u8{0x34})); writeIntBig(u16, &buf2, 0x1234); - testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0x12, 0x34 })); + testing.expect(eql(u8, buf2[0..], [_]u8{ 0x12, 0x34 })); writeIntLittle(u16, &buf2, 0x5678); - testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0x78, 0x56 })); + testing.expect(eql(u8, buf2[0..], [_]u8{ 0x78, 0x56 })); writeIntBig(u72, &buf9, 0x123456789abcdef024); - testing.expect(eql_slice_u8(buf9[0..], [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); + testing.expect(eql(u8, buf9[0..], [_]u8{ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x24 })); writeIntLittle(u72, &buf9, 0xfedcba9876543210ec); - testing.expect(eql_slice_u8(buf9[0..], [_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); + testing.expect(eql(u8, buf9[0..], [_]u8{ 0xec, 0x10, 0x32, 0x54, 0x76, 0x98, 0xba, 0xdc, 0xfe })); writeIntBig(i8, &buf1, -1); - testing.expect(eql_slice_u8(buf1[0..], [_]u8{0xff})); + testing.expect(eql(u8, buf1[0..], [_]u8{0xff})); writeIntLittle(i8, &buf1, -2); - testing.expect(eql_slice_u8(buf1[0..], [_]u8{0xfe})); + testing.expect(eql(u8, buf1[0..], [_]u8{0xfe})); writeIntBig(i16, &buf2, -3); - testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0xff, 0xfd })); + testing.expect(eql(u8, buf2[0..], [_]u8{ 0xff, 0xfd })); writeIntLittle(i16, &buf2, -4); - testing.expect(eql_slice_u8(buf2[0..], [_]u8{ 0xfc, 0xff })); -} - -pub fn hash_slice_u8(k: []const u8) u32 { - // FNV 32-bit hash - var h: u32 = 2166136261; - for (k) |b| { - h = (h ^ b) *% 16777619; - } - return h; -} - -pub fn eql_slice_u8(a: []const u8, b: []const u8) bool { - return eql(u8, a, b); + testing.expect(eql(u8, buf2[0..], [_]u8{ 0xfc, 0xff })); } /// Returns an iterator that iterates over the slices of `buffer` that are not diff --git a/std/special/build_runner.zig b/std/special/build_runner.zig index 4933faca8a..01e307d46e 100644 --- a/std/special/build_runner.zig +++ b/std/special/build_runner.zig @@ -123,6 +123,7 @@ pub fn main() !void { } } + builder.resolveInstallPrefix(); try runBuild(builder); if (builder.validateUserInputDidItFail()) @@ -151,6 +152,7 @@ fn usage(builder: *Builder, already_ran_build: bool, out_stream: var) !void { // run the build script to collect the options if (!already_ran_build) { builder.setInstallPrefix(null); + builder.resolveInstallPrefix(); try runBuild(builder); } diff --git a/std/zig/parser_test.zig b/std/zig/parser_test.zig index b0c7b9135e..821ead9db5 100644 --- a/std/zig/parser_test.zig +++ b/std/zig/parser_test.zig @@ -2419,6 +2419,94 @@ test "zig fmt: comment after empty comment" { ); } +test "zig fmt: line comment in array" { + try testTransform( + \\test "a" { + \\ var arr = [_]u32{ + \\ 0 + \\ // 1, + \\ // 2, + \\ }; + \\} + \\ + , + \\test "a" { + \\ var arr = [_]u32{ + \\ 0, // 1, + \\ // 2, + \\ }; + \\} + \\ + ); + try testCanonical( + \\test "a" { + \\ var arr = [_]u32{ + \\ 0, + \\ // 1, + \\ // 2, + \\ }; + \\} + \\ + ); +} + +test "zig fmt: comment after params" { + try testTransform( + \\fn a( + \\ b: u32 + \\ // c: u32, + \\ // d: u32, + \\) void {} + \\ + , + \\fn a( + \\ b: u32, // c: u32, + \\ // d: u32, + \\) void {} + \\ + ); + try testCanonical( + \\fn a( + \\ b: u32, + \\ // c: u32, + \\ // d: u32, + \\) void {} + \\ + ); +} + +test "zig fmt: comment in array initializer/access" { + try testCanonical( + \\test "a" { + \\ var a = x{ //aa + \\ //bb + \\ }; + \\ var a = []x{ //aa + \\ //bb + \\ }; + \\ var b = [ //aa + \\ _ + \\ ]x{ //aa + \\ //bb + \\ 9, + \\ }; + \\ var c = b[ //aa + \\ 0 + \\ ]; + \\ var d = [_ + \\ //aa + \\ ]x{ //aa + \\ //bb + \\ 9, + \\ }; + \\ var e = d[0 + \\ //aa + \\ ]; + \\} + \\ + ); +} + test "zig fmt: comments at several places in struct init" { try testTransform( \\var bar = Bar{ diff --git a/std/zig/render.zig b/std/zig/render.zig index 9db7939a22..6268a056f5 100644 --- a/std/zig/render.zig +++ b/std/zig/render.zig @@ -483,9 +483,23 @@ fn renderExpression( }, ast.Node.PrefixOp.Op.ArrayType => |array_index| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, start_col, array_index, Space.None); - try renderToken(tree, stream, tree.nextToken(array_index.lastToken()), indent, start_col, Space.None); // ] + const lbracket = prefix_op_node.op_token; + const rbracket = tree.nextToken(array_index.lastToken()); + + try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ + + const starts_with_comment = tree.tokens.at(lbracket + 1).id == .LineComment; + const ends_with_comment = tree.tokens.at(rbracket - 1).id == .LineComment; + const new_indent = if (ends_with_comment) indent + indent_delta else indent; + const new_space = if (ends_with_comment) Space.Newline else Space.None; + try renderExpression(allocator, stream, tree, new_indent, start_col, array_index, new_space); + if (starts_with_comment) { + try stream.writeByte('\n'); + } + if (ends_with_comment or starts_with_comment) { + try stream.writeByteNTimes(' ', indent); + } + try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ] }, ast.Node.PrefixOp.Op.BitNot, ast.Node.PrefixOp.Op.BoolNot, @@ -580,7 +594,18 @@ fn renderExpression( try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ - try renderExpression(allocator, stream, tree, indent, start_col, index_expr, Space.None); + + const starts_with_comment = tree.tokens.at(lbracket + 1).id == .LineComment; + const ends_with_comment = tree.tokens.at(rbracket - 1).id == .LineComment; + const new_indent = if (ends_with_comment) indent + indent_delta else indent; + const new_space = if (ends_with_comment) Space.Newline else Space.None; + try renderExpression(allocator, stream, tree, new_indent, start_col, index_expr, new_space); + if (starts_with_comment) { + try stream.writeByte('\n'); + } + if (ends_with_comment or starts_with_comment) { + try stream.writeByteNTimes(' ', indent); + } return renderToken(tree, stream, rbracket, indent, start_col, space); // ] }, @@ -615,7 +640,7 @@ fn renderExpression( if (field_inits.len == 0) { try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); - try renderToken(tree, stream, lbrace, indent, start_col, Space.None); + try renderToken(tree, stream, lbrace, indent + indent_delta, start_col, Space.None); return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } @@ -714,7 +739,7 @@ fn renderExpression( try renderToken(tree, stream, lbrace, indent, start_col, Space.None); return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); } - if (exprs.len == 1) { + if (exprs.len == 1 and tree.tokens.at(exprs.at(0).*.lastToken() + 1).id == .RBrace) { const expr = exprs.at(0).*; try renderExpression(allocator, stream, tree, indent, start_col, suffix_op.lhs, Space.None); @@ -775,7 +800,7 @@ fn renderExpression( while (it.next()) |expr| : (i += 1) { counting_stream.bytes_written = 0; var dummy_col: usize = 0; - try renderExpression(allocator, &counting_stream.stream, tree, 0, &dummy_col, expr.*, Space.None); + try renderExpression(allocator, &counting_stream.stream, tree, indent, &dummy_col, expr.*, Space.None); const width = @intCast(usize, counting_stream.bytes_written); const col = i % row_size; column_widths[col] = std.math.max(column_widths[col], width); @@ -1191,8 +1216,8 @@ fn renderExpression( }); const src_params_trailing_comma = blk: { - const maybe_comma = tree.prevToken(rparen); - break :blk tree.tokens.at(maybe_comma).id == Token.Id.Comma; + const maybe_comma = tree.tokens.at(rparen - 1).id; + break :blk maybe_comma == .Comma or maybe_comma == .LineComment; }; if (!src_params_trailing_comma) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 12f17ec790..871ff63e23 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -6462,4 +6462,13 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:5:30: error: expression value is ignored", "tmp.zig:9:30: error: expression value is ignored", ); + + cases.add( + "aligned variable of zero-bit type", + \\export fn f() void { + \\ var s: struct {} align(4) = undefined; + \\} + , + "tmp.zig:2:5: error: variable 's' of zero-bit type 'struct:2:12' has no in-memory representation, it cannot be aligned", + ); } diff --git a/test/stack_traces.zig b/test/stack_traces.zig new file mode 100644 index 0000000000..1a014f07cc --- /dev/null +++ b/test/stack_traces.zig @@ -0,0 +1,274 @@ +const builtin = @import("builtin"); +const std = @import("std"); +const os = std.os; +const tests = @import("tests.zig"); + +pub fn addCases(cases: *tests.StackTracesContext) void { + const source_return = + \\const std = @import("std"); + \\ + \\pub fn main() !void { + \\ return error.TheSkyIsFalling; + \\} + ; + const source_try_return = + \\const std = @import("std"); + \\ + \\fn foo() !void { + \\ return error.TheSkyIsFalling; + \\} + \\ + \\pub fn main() !void { + \\ try foo(); + \\} + ; + const source_try_try_return_return = + \\const std = @import("std"); + \\ + \\fn foo() !void { + \\ try bar(); + \\} + \\ + \\fn bar() !void { + \\ return make_error(); + \\} + \\ + \\fn make_error() !void { + \\ return error.TheSkyIsFalling; + \\} + \\ + \\pub fn main() !void { + \\ try foo(); + \\} + ; + switch (builtin.os) { + .linux => { + cases.addCase( + "return", + source_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in main (test) + \\ + , + // release-safe + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test) + \\ + , + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + cases.addCase( + "try return", + source_try_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in foo (test) + \\source.zig:8:5: [address] in main (test) + \\ + , + // release-safe + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test) + \\ + , + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + cases.addCase( + "try try return return", + source_try_try_return_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:12:5: [address] in make_error (test) + \\source.zig:8:5: [address] in bar (test) + \\source.zig:4:5: [address] in foo (test) + \\source.zig:16:5: [address] in main (test) + \\ + , + // release-safe + \\error: TheSkyIsFalling + \\source.zig:12:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:8:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:4:5: [address] in std.special.posixCallMainAndExit (test) + \\source.zig:16:5: [address] in std.special.posixCallMainAndExit (test) + \\ + , + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + }, + .macosx => { + cases.addCase( + "return", + source_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in _main.0 (test.o) + \\ + , + // release-safe + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in _main (test.o) + \\ + , + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + cases.addCase( + "try return", + source_try_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in _foo (test.o) + \\source.zig:8:5: [address] in _main.0 (test.o) + \\ + , + // release-safe + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in _main (test.o) + \\source.zig:8:5: [address] in _main (test.o) + \\ + , + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + cases.addCase( + "try try return return", + source_try_try_return_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:12:5: [address] in _make_error (test.o) + \\source.zig:8:5: [address] in _bar (test.o) + \\source.zig:4:5: [address] in _foo (test.o) + \\source.zig:16:5: [address] in _main.0 (test.o) + \\ + , + // release-safe + \\error: TheSkyIsFalling + \\source.zig:12:5: [address] in _main (test.o) + \\source.zig:8:5: [address] in _main (test.o) + \\source.zig:4:5: [address] in _main (test.o) + \\source.zig:16:5: [address] in _main (test.o) + \\ + , + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + }, + .windows => { + cases.addCase( + "return", + source_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in main (test.obj) + \\ + , + // release-safe + // --disabled-- results in segmenetation fault + "", + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + cases.addCase( + "try return", + source_try_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:4:5: [address] in foo (test.obj) + \\source.zig:8:5: [address] in main (test.obj) + \\ + , + // release-safe + // --disabled-- results in segmenetation fault + "", + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + cases.addCase( + "try try return return", + source_try_try_return_return, + [_][]const u8{ + // debug + \\error: TheSkyIsFalling + \\source.zig:12:5: [address] in make_error (test.obj) + \\source.zig:8:5: [address] in bar (test.obj) + \\source.zig:4:5: [address] in foo (test.obj) + \\source.zig:16:5: [address] in main (test.obj) + \\ + , + // release-safe + // --disabled-- results in segmenetation fault + "", + // release-fast + \\error: TheSkyIsFalling + \\ + , + // release-small + \\error: TheSkyIsFalling + \\ + }, + ); + }, + else => {}, + } +} diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index f7152971a4..23ec3e53ce 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -30,6 +30,7 @@ comptime { _ = @import("behavior/bugs/2114.zig"); _ = @import("behavior/bugs/2346.zig"); _ = @import("behavior/bugs/2578.zig"); + _ = @import("behavior/bugs/2692.zig"); _ = @import("behavior/bugs/3112.zig"); _ = @import("behavior/bugs/394.zig"); _ = @import("behavior/bugs/421.zig"); diff --git a/test/stage1/behavior/async_fn.zig b/test/stage1/behavior/async_fn.zig index 005a790f5f..ad8e949f8b 100644 --- a/test/stage1/behavior/async_fn.zig +++ b/test/stage1/behavior/async_fn.zig @@ -1031,3 +1031,64 @@ test "@typeOf an async function call of generic fn with error union type" { }; _ = async S.func(i32); } + +test "using @typeOf on a generic function call" { + const S = struct { + var global_frame: anyframe = undefined; + var global_ok = false; + + var buf: [100]u8 align(16) = undefined; + + fn amain(x: var) void { + if (x == 0) { + global_ok = true; + return; + } + suspend { + global_frame = @frame(); + } + const F = @typeOf(async amain(x - 1)); + const frame = @intToPtr(*F, @ptrToInt(&buf)); + return await @asyncCall(frame, {}, amain, x - 1); + } + }; + _ = async S.amain(u32(1)); + resume S.global_frame; + expect(S.global_ok); +} + +test "recursive call of await @asyncCall with struct return type" { + const S = struct { + var global_frame: anyframe = undefined; + var global_ok = false; + + var buf: [100]u8 align(16) = undefined; + + fn amain(x: var) Foo { + if (x == 0) { + global_ok = true; + return Foo{ .x = 1, .y = 2, .z = 3 }; + } + suspend { + global_frame = @frame(); + } + const F = @typeOf(async amain(x - 1)); + const frame = @intToPtr(*F, @ptrToInt(&buf)); + return await @asyncCall(frame, {}, amain, x - 1); + } + + const Foo = struct { + x: u64, + y: u64, + z: u64, + }; + }; + var res: S.Foo = undefined; + var frame: @typeOf(async S.amain(u32(1))) = undefined; + _ = @asyncCall(&frame, &res, S.amain, u32(1)); + resume S.global_frame; + expect(S.global_ok); + expect(res.x == 1); + expect(res.y == 2); + expect(res.z == 3); +} diff --git a/test/stage1/behavior/bugs/2692.zig b/test/stage1/behavior/bugs/2692.zig new file mode 100644 index 0000000000..267c3a131a --- /dev/null +++ b/test/stage1/behavior/bugs/2692.zig @@ -0,0 +1,6 @@ +fn foo(a: []u8) void {} + +test "address of 0 length array" { + var pt: [0]u8 = undefined; + foo(&pt); +} diff --git a/test/stage1/behavior/if.zig b/test/stage1/behavior/if.zig index 5f92962957..70712ea85a 100644 --- a/test/stage1/behavior/if.zig +++ b/test/stage1/behavior/if.zig @@ -63,3 +63,14 @@ test "labeled break inside comptime if inside runtime if" { } expect(answer == 42); } + +test "const result loc, runtime if cond, else unreachable" { + const Num = enum { + One, + Two, + }; + + var t = true; + const x = if (t) Num.Two else unreachable; + if (x != .Two) @compileError("bad"); +} diff --git a/test/stage1/behavior/struct.zig b/test/stage1/behavior/struct.zig index b86b171daf..0befe4f620 100644 --- a/test/stage1/behavior/struct.zig +++ b/test/stage1/behavior/struct.zig @@ -599,3 +599,36 @@ test "extern fn returns struct by value" { S.entry(); comptime S.entry(); } + +test "for loop over pointers to struct, getting field from struct pointer" { + const S = struct { + const Foo = struct { + name: []const u8, + }; + + var ok = true; + + fn eql(a: []const u8) bool { + return true; + } + + const ArrayList = struct { + fn toSlice(self: *ArrayList) []*Foo { + return ([*]*Foo)(undefined)[0..0]; + } + }; + + fn doTheTest() void { + var objects: ArrayList = undefined; + + for (objects.toSlice()) |obj| { + if (eql(obj.name)) { + ok = false; + } + } + + expect(ok); + } + }; + S.doTheTest(); +} diff --git a/test/stage1/behavior/union.zig b/test/stage1/behavior/union.zig index 7d6a8154ea..d28a0f8ea4 100644 --- a/test/stage1/behavior/union.zig +++ b/test/stage1/behavior/union.zig @@ -457,3 +457,13 @@ test "@unionInit can modify a pointer value" { value_ptr.* = @unionInit(UnionInitEnum, "Byte", 2); expect(value.Byte == 2); } + +test "union no tag with struct member" { + const Struct = struct {}; + const Union = union { + s: Struct, + pub fn foo(self: *@This()) void {} + }; + var u = Union{ .s = Struct{} }; + u.foo(); +} diff --git a/test/tests.zig b/test/tests.zig index f7ef05d3cd..99ee5949c3 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -16,6 +16,7 @@ const LibExeObjStep = build.LibExeObjStep; const compare_output = @import("compare_output.zig"); const standalone = @import("standalone.zig"); +const stack_traces = @import("stack_traces.zig"); const compile_errors = @import("compile_errors.zig"); const assemble_and_link = @import("assemble_and_link.zig"); const runtime_safety = @import("runtime_safety.zig"); @@ -57,6 +58,21 @@ pub fn addCompareOutputTests(b: *build.Builder, test_filter: ?[]const u8, modes: return cases.step; } +pub fn addStackTraceTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { + const cases = b.allocator.create(StackTracesContext) catch unreachable; + cases.* = StackTracesContext{ + .b = b, + .step = b.step("test-stack-traces", "Run the stack trace tests"), + .test_index = 0, + .test_filter = test_filter, + .modes = modes, + }; + + stack_traces.addCases(cases); + + return cases.step; +} + pub fn addRuntimeSafetyTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { const cases = b.allocator.create(CompareOutputContext) catch unreachable; cases.* = CompareOutputContext{ @@ -549,6 +565,200 @@ pub const CompareOutputContext = struct { } }; +pub const StackTracesContext = struct { + b: *build.Builder, + step: *build.Step, + test_index: usize, + test_filter: ?[]const u8, + modes: []const Mode, + + const Expect = [@typeInfo(Mode).Enum.fields.len][]const u8; + + pub fn addCase( + self: *StackTracesContext, + name: []const u8, + source: []const u8, + expect: Expect, + ) void { + const b = self.b; + + const source_pathname = fs.path.join( + b.allocator, + [_][]const u8{ b.cache_root, "source.zig" }, + ) catch unreachable; + + for (self.modes) |mode| { + const expect_for_mode = expect[@enumToInt(mode)]; + if (expect_for_mode.len == 0) continue; + + const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", "stack-trace", name, @tagName(mode)) catch unreachable; + if (self.test_filter) |filter| { + if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; + } + + const exe = b.addExecutable("test", source_pathname); + exe.setBuildMode(mode); + + const write_source = b.addWriteFile(source_pathname, source); + exe.step.dependOn(&write_source.step); + + const run_and_compare = RunAndCompareStep.create( + self, + exe, + annotated_case_name, + mode, + expect_for_mode, + ); + + self.step.dependOn(&run_and_compare.step); + } + } + + const RunAndCompareStep = struct { + step: build.Step, + context: *StackTracesContext, + exe: *LibExeObjStep, + name: []const u8, + mode: Mode, + expect_output: []const u8, + test_index: usize, + + pub fn create( + context: *StackTracesContext, + exe: *LibExeObjStep, + name: []const u8, + mode: Mode, + expect_output: []const u8, + ) *RunAndCompareStep { + const allocator = context.b.allocator; + const ptr = allocator.create(RunAndCompareStep) catch unreachable; + ptr.* = RunAndCompareStep{ + .step = build.Step.init("StackTraceCompareOutputStep", allocator, make), + .context = context, + .exe = exe, + .name = name, + .mode = mode, + .expect_output = expect_output, + .test_index = context.test_index, + }; + ptr.step.dependOn(&exe.step); + context.test_index += 1; + return ptr; + } + + fn make(step: *build.Step) !void { + const self = @fieldParentPtr(RunAndCompareStep, "step", step); + const b = self.context.b; + + const full_exe_path = self.exe.getOutputPath(); + var args = ArrayList([]const u8).init(b.allocator); + defer args.deinit(); + args.append(full_exe_path) catch unreachable; + + warn("Test {}/{} {}...", self.test_index + 1, self.context.test_index, self.name); + + const child = std.ChildProcess.init(args.toSliceConst(), b.allocator) catch unreachable; + defer child.deinit(); + + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + child.env_map = b.env_map; + + child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); + + var stdout = Buffer.initNull(b.allocator); + var stderr = Buffer.initNull(b.allocator); + + var stdout_file_in_stream = child.stdout.?.inStream(); + var stderr_file_in_stream = child.stderr.?.inStream(); + + stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; + stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; + + const term = child.wait() catch |err| { + debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err)); + }; + + switch (term) { + .Exited => |code| { + const expect_code: u32 = 1; + if (code != expect_code) { + warn("Process {} exited with error code {} but expected code {}\n", full_exe_path, code, expect_code); + printInvocation(args.toSliceConst()); + return error.TestFailed; + } + }, + .Signal => |signum| { + warn("Process {} terminated on signal {}\n", full_exe_path, signum); + printInvocation(args.toSliceConst()); + return error.TestFailed; + }, + .Stopped => |signum| { + warn("Process {} stopped on signal {}\n", full_exe_path, signum); + printInvocation(args.toSliceConst()); + return error.TestFailed; + }, + .Unknown => |code| { + warn("Process {} terminated unexpectedly with error code {}\n", full_exe_path, code); + printInvocation(args.toSliceConst()); + return error.TestFailed; + }, + } + + // process result + // - keep only basename of source file path + // - replace address with symbolic string + // - skip empty lines + const got: []const u8 = got_result: { + var buf = try Buffer.initSize(b.allocator, 0); + defer buf.deinit(); + var bytes = stderr.toSliceConst(); + if (bytes.len != 0 and bytes[bytes.len - 1] == '\n') bytes = bytes[0 .. bytes.len - 1]; + var it = mem.separate(bytes, "\n"); + process_lines: while (it.next()) |line| { + if (line.len == 0) continue; + const delims = [_][]const u8{ ":", ":", ":", " in " }; + var marks = [_]usize{0} ** 4; + // offset search past `[drive]:` on windows + var pos: usize = if (builtin.os == .windows) 2 else 0; + for (delims) |delim, i| { + marks[i] = mem.indexOfPos(u8, line, pos, delim) orelse { + try buf.append(line); + try buf.append("\n"); + continue :process_lines; + }; + pos = marks[i] + delim.len; + } + pos = mem.lastIndexOfScalar(u8, line[0..marks[0]], fs.path.sep) orelse { + try buf.append(line); + try buf.append("\n"); + continue :process_lines; + }; + try buf.append(line[pos + 1 .. marks[2] + delims[2].len]); + try buf.append(" [address]"); + try buf.append(line[marks[3]..]); + try buf.append("\n"); + } + break :got_result buf.toOwnedSlice(); + }; + + if (!mem.eql(u8, self.expect_output, got)) { + warn( + \\ + \\========= Expected this output: ========= + \\{} + \\================================================ + \\{} + \\ + , self.expect_output, got); + return error.TestFailed; + } + warn("OK\n"); + } + }; +}; + pub const CompileErrorContext = struct { b: *build.Builder, step: *build.Step,