From 4d8a6f6fea1b6922e7904b33c5b575249213fe53 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 17 Aug 2019 12:48:48 -0400 Subject: fix compiler not checking alignment of function frames closes #3086 --- test/compile_errors.zig | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'test/compile_errors.zig') diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 5eec78fa7f..2088b32d98 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,28 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "bad alignment in @asyncCall", + \\export fn entry() void { + \\ var ptr: async fn () void = func; + \\ var bytes: [64]u8 = undefined; + \\ _ = @asyncCall(&bytes, {}, ptr); + \\} + \\async fn func() void {} + , + "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'", + ); + + cases.add( + "bad alignment in implicit cast from array pointer to slice", + \\export fn a() void { + \\ var x: [10]u8 = undefined; + \\ var y: []align(16) u8 = &x; + \\} + , + "tmp.zig:3:30: error: expected type '[]align(16) u8', found '*[10]u8'", + ); + cases.add( "result location incompatibility mismatching handle_is_ptr (generic call)", \\export fn entry() void { @@ -164,7 +186,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "non async function pointer passed to @asyncCall", \\export fn entry() void { \\ var ptr = afunc; - \\ var bytes: [100]u8 = undefined; + \\ var bytes: [100]u8 align(16) = undefined; \\ _ = @asyncCall(&bytes, {}, ptr); \\} \\fn afunc() void { } -- cgit v1.2.3 From 0ff396c34f93b60a000e1ee50e881a8c25122b79 Mon Sep 17 00:00:00 2001 From: Vexu <15308111+Vexu@users.noreply.github.com> Date: Sat, 17 Aug 2019 22:51:25 +0300 Subject: add compile error for incorrect atomic ordering in fence #3082 --- src/ir.cpp | 6 ++++++ test/compile_errors.zig | 9 +++++++++ 2 files changed, 15 insertions(+) (limited to 'test/compile_errors.zig') diff --git a/src/ir.cpp b/src/ir.cpp index 15c570ddd9..51f849ce19 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -20860,6 +20860,12 @@ static IrInstruction *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstruction if (!ir_resolve_atomic_order(ira, order_value, &order)) return ira->codegen->invalid_instruction; + if (order < AtomicOrderAcquire) { + ir_add_error(ira, order_value, + buf_sprintf("atomic ordering must be Acquire or stricter")); + return ira->codegen->invalid_instruction; + } + IrInstruction *result = ir_build_fence(&ira->new_irb, instruction->base.scope, instruction->base.source_node, order_value, order); result->value.type = ira->codegen->builtin_types.entry_void; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 2088b32d98..d0c12e7cf6 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -14,6 +14,15 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { "tmp.zig:4:21: error: expected type '[]align(16) u8', found '*[64]u8'", ); + cases.add( + "atomic orderings of fence Acquire or stricter", + \\export fn entry() void { + \\ @fence(.Monotonic); + \\} + , + "tmp.zig:2:12: error: atomic ordering must be Acquire or stricter", + ); + cases.add( "bad alignment in implicit cast from array pointer to slice", \\export fn a() void { -- cgit v1.2.3 From 66a490c27c01c958d8d20dbc289c6b2b934a724e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 17 Aug 2019 16:49:23 -0400 Subject: detect non-async function pointer of inferred async function closes #3075 --- src/all_types.hpp | 1 + src/analyze.cpp | 11 +++++++++-- src/ir.cpp | 18 ++++++++++++++++++ test/compile_errors.zig | 15 +++++++++++++++ 4 files changed, 43 insertions(+), 2 deletions(-) (limited to 'test/compile_errors.zig') diff --git a/src/all_types.hpp b/src/all_types.hpp index f1c699ba10..f075dd7c58 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1396,6 +1396,7 @@ struct ZigFn { AstNode *set_cold_node; const AstNode *inferred_async_node; ZigFn *inferred_async_fn; + AstNode *non_async_node; ZigList export_list; ZigList call_list; diff --git a/src/analyze.cpp b/src/analyze.cpp index 4aff6da8e9..16e66a5906 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -4144,8 +4144,15 @@ void semantic_analyze(CodeGen *g) { // second pass over functions for detecting async for (g->fn_defs_index = 0; g->fn_defs_index < g->fn_defs.length; g->fn_defs_index += 1) { - ZigFn *fn_entry = g->fn_defs.at(g->fn_defs_index); - analyze_fn_async(g, fn_entry, true); + ZigFn *fn = g->fn_defs.at(g->fn_defs_index); + analyze_fn_async(g, fn, true); + if (fn_is_async(fn) && fn->non_async_node != nullptr) { + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("'%s' cannot be async", buf_ptr(&fn->symbol_name))); + add_error_note(g, msg, fn->non_async_node, + buf_sprintf("required to be non-async here")); + add_async_error_notes(g, msg, fn); + } } } diff --git a/src/ir.cpp b/src/ir.cpp index 51f849ce19..c37fac2f52 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15160,6 +15160,20 @@ no_mem_slot: return var_ptr_instruction; } +// This function is called when a comptime value becomes accessible at runtime. +static void mark_comptime_value_escape(IrAnalyze *ira, IrInstruction *source_instr, ConstExprValue *val) { + ir_assert(value_is_comptime(val), source_instr); + if (val->special == ConstValSpecialUndef) + return; + + if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) { + ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); + if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) { + val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node; + } + } +} + static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source_instr, IrInstruction *ptr, IrInstruction *uncasted_value, bool allow_write_through_const) { @@ -15256,6 +15270,10 @@ static IrInstruction *ir_analyze_store_ptr(IrAnalyze *ira, IrInstruction *source break; } + if (instr_is_comptime(value)) { + mark_comptime_value_escape(ira, source_instr, &value->value); + } + IrInstructionStorePtr *store_ptr = ir_build_store_ptr(&ira->new_irb, source_instr->scope, source_instr->source_node, ptr, value); return &store_ptr->base; diff --git a/test/compile_errors.zig b/test/compile_errors.zig index d0c12e7cf6..654171f553 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,21 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "non-async function pointer eventually is inferred to become async", + \\export fn a() void { + \\ var non_async_fn: fn () void = undefined; + \\ non_async_fn = func; + \\} + \\fn func() void { + \\ suspend; + \\} + , + "tmp.zig:5:1: error: 'func' cannot be async", + "tmp.zig:3:20: note: required to be non-async here", + "tmp.zig:6:5: note: suspends here", + ); + cases.add( "bad alignment in @asyncCall", \\export fn entry() void { -- cgit v1.2.3 From ea1734773ba9913e32318aba963cdcb9f51128d4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sat, 17 Aug 2019 19:47:49 -0400 Subject: add compile error for async frames depending on themselves --- src/all_types.hpp | 6 ++++++ src/analyze.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++-- test/compile_errors.zig | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) (limited to 'test/compile_errors.zig') diff --git a/src/all_types.hpp b/src/all_types.hpp index f075dd7c58..559ebe8cda 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1279,6 +1279,12 @@ struct ZigTypeOpaque { struct ZigTypeFnFrame { ZigFn *fn; ZigType *locals_struct; + + // This is set to the type that resolving the frame currently depends on, null if none. + // It's for generating a helpful error message. + ZigType *resolve_loop_type; + AstNode *resolve_loop_src_node; + bool reported_loop_err; }; struct ZigTypeAnyFrame { diff --git a/src/analyze.cpp b/src/analyze.cpp index 16e66a5906..ab994d07e8 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5197,6 +5197,27 @@ static ZigType *get_async_fn_type(CodeGen *g, ZigType *orig_fn_type) { return fn_type; } +static void emit_error_notes_for_type_loop(CodeGen *g, ErrorMsg *msg, ZigType *stop_type, + ZigType *ty, AstNode *src_node) +{ + ErrorMsg *note = add_error_note(g, msg, src_node, + buf_sprintf("when analyzing type '%s' here", buf_ptr(&ty->name))); + if (ty == stop_type) + return; + switch (ty->id) { + case ZigTypeIdFnFrame: { + ty->data.frame.reported_loop_err = true; + ZigType *depending_type = ty->data.frame.resolve_loop_type; + if (depending_type == nullptr) + return; + emit_error_notes_for_type_loop(g, note, stop_type, + depending_type, ty->data.frame.resolve_loop_src_node); + } + default: + return; + } +} + static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { Error err; @@ -5206,6 +5227,20 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { ZigFn *fn = frame_type->data.frame.fn; assert(!fn->type_entry->data.fn.is_generic); + if (frame_type->data.frame.resolve_loop_type != nullptr) { + if (!frame_type->data.frame.reported_loop_err) { + frame_type->data.frame.reported_loop_err = true; + ErrorMsg *msg = add_node_error(g, fn->proto_node, + buf_sprintf("'%s' depends on itself", buf_ptr(&frame_type->name))); + emit_error_notes_for_type_loop(g, msg, + frame_type, + frame_type->data.frame.resolve_loop_type, + frame_type->data.frame.resolve_loop_src_node); + emit_error_notes_for_ref_stack(g, msg); + } + return ErrorSemanticAnalyzeFail; + } + switch (fn->anal_state) { case FnAnalStateInvalid: return ErrorSemanticAnalyzeFail; @@ -5299,6 +5334,10 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { return ErrorSemanticAnalyzeFail; } + ZigType *callee_frame_type = get_fn_frame_type(g, callee); + frame_type->data.frame.resolve_loop_type = callee_frame_type; + frame_type->data.frame.resolve_loop_src_node = call->base.source_node; + analyze_fn_body(g, callee); if (callee->anal_state == FnAnalStateInvalid) { frame_type->data.frame.locals_struct = g->builtin_types.entry_invalid; @@ -5308,8 +5347,6 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { if (!fn_is_async(callee)) continue; - ZigType *callee_frame_type = get_fn_frame_type(g, callee); - IrInstructionAllocaGen *alloca_gen = allocate(1); alloca_gen->base.id = IrInstructionIdAllocaGen; alloca_gen->base.source_node = call->base.source_node; @@ -5378,9 +5415,13 @@ static Error resolve_async_frame(CodeGen *g, ZigType *frame_type) { continue; } } + + frame_type->data.frame.resolve_loop_type = child_type; + frame_type->data.frame.resolve_loop_src_node = instruction->base.source_node; if ((err = type_resolve(g, child_type, ResolveStatusSizeKnown))) { return err; } + const char *name; if (*instruction->name_hint == 0) { name = buf_ptr(buf_sprintf("@local%" ZIG_PRI_usize, alloca_i)); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 654171f553..cecb37620c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,42 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "indirect recursion of async functions detected", + \\var frame: ?anyframe = null; + \\ + \\export fn a() void { + \\ _ = async rangeSum(10); + \\ while (frame) |f| resume f; + \\} + \\ + \\fn rangeSum(x: i32) i32 { + \\ suspend { + \\ frame = @frame(); + \\ } + \\ frame = null; + \\ + \\ if (x == 0) return 0; + \\ var child = rangeSumIndirect(x - 1); + \\ return child + 1; + \\} + \\ + \\fn rangeSumIndirect(x: i32) i32 { + \\ suspend { + \\ frame = @frame(); + \\ } + \\ frame = null; + \\ + \\ if (x == 0) return 0; + \\ var child = rangeSum(x - 1); + \\ return child + 1; + \\} + , + "tmp.zig:8:1: error: '@Frame(rangeSum)' depends on itself", + "tmp.zig:15:33: note: when analyzing type '@Frame(rangeSumIndirect)' here", + "tmp.zig:26:25: note: when analyzing type '@Frame(rangeSum)' here", + ); + cases.add( "non-async function pointer eventually is inferred to become async", \\export fn a() void { -- cgit v1.2.3 From 2addec8ea1f35241e9399c5da6855c427481985f Mon Sep 17 00:00:00 2001 From: Timon Kruiper Date: Sat, 17 Aug 2019 14:16:51 +0200 Subject: compiler error when variable in asm template cannot be found --- src/ir.cpp | 35 +++++++++++++++++++++++++++++++++++ test/compile_errors.zig | 12 ++++++++++++ 2 files changed, 47 insertions(+) (limited to 'test/compile_errors.zig') diff --git a/src/ir.cpp b/src/ir.cpp index 8f27c9ea2c..cdddce1b18 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -6743,6 +6743,25 @@ static Error parse_asm_template(IrBuilder *irb, AstNode *source_node, Buf *asm_t return ErrorNone; } +static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) { + const char *ptr = buf_ptr(src_template) + tok->start + 2; + size_t len = tok->end - tok->start - 2; + size_t result = 0; + for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) { + return result; + } + } + for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) { + return result; + } + } + return SIZE_MAX; +} + static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *node) { Error err; assert(node->type == NodeTypeAsmExpr); @@ -6830,6 +6849,22 @@ static IrInstruction *ir_gen_asm_expr(IrBuilder *irb, Scope *scope, AstNode *nod input_list[i] = input_value; } + for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) { + AsmToken asm_token = tok_list.at(token_i); + if (asm_token.id == AsmTokenIdVar) { + size_t index = find_asm_index(irb->codegen, node, &asm_token, template_buf); + if (index == SIZE_MAX) { + const char *ptr = buf_ptr(template_buf) + asm_token.start + 2; + uint32_t len = asm_token.end - asm_token.start - 2; + + add_node_error(irb->codegen, node, + buf_sprintf("could not find '%.*s' in the inputs or outputs.", + len, ptr)); + return irb->codegen->invalid_instruction; + } + } + } + return ir_build_asm(irb, scope, node, template_buf, tok_list.items, tok_list.length, input_list, output_types, output_vars, return_count, is_volatile); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index cecb37620c..690db2c4eb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,6 +2,18 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "variable in inline assembly template cannot be found", + \\export fn entry() void { + \\ var sp = asm volatile ( + \\ "mov %[foo], sp" + \\ : [bar] "=r" (-> usize) + \\ ); + \\} + , + "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs." + ); + cases.add( "indirect recursion of async functions detected", \\var frame: ?anyframe = null; -- cgit v1.2.3 From 276eb4402b3c54adef10033d1ec54bf2053cc563 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 20 Aug 2019 14:40:57 -0400 Subject: specify the target for the newest test case --- test/compile_errors.zig | 29 ++++++++++++++++++----------- test/tests.zig | 25 +++++++++++++++---------- 2 files changed, 33 insertions(+), 21 deletions(-) (limited to 'test/compile_errors.zig') diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 690db2c4eb..a6a1d0219b 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2,17 +2,24 @@ const tests = @import("tests.zig"); const builtin = @import("builtin"); pub fn addCases(cases: *tests.CompileErrorContext) void { - cases.add( - "variable in inline assembly template cannot be found", - \\export fn entry() void { - \\ var sp = asm volatile ( - \\ "mov %[foo], sp" - \\ : [bar] "=r" (-> usize) - \\ ); - \\} - , - "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs." - ); + cases.addCase(x: { + var tc = cases.create("variable in inline assembly template cannot be found", + \\export fn entry() void { + \\ var sp = asm volatile ( + \\ "mov %[foo], sp" + \\ : [bar] "=r" (-> usize) + \\ ); + \\} + , "tmp.zig:2:14: error: could not find 'foo' in the inputs or outputs."); + tc.target = tests.Target{ + .Cross = tests.CrossTarget{ + .arch = .x86_64, + .os = .linux, + .abi = .gnu, + }, + }; + break :x tc; + }); cases.add( "indirect recursion of async functions detected", diff --git a/test/tests.zig b/test/tests.zig index c8aea33591..f7ef05d3cd 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -2,6 +2,8 @@ const std = @import("std"); const debug = std.debug; const warn = debug.warn; const build = std.build; +pub const Target = build.Target; +pub const CrossTarget = build.CrossTarget; const Buffer = std.Buffer; const io = std.io; const fs = std.fs; @@ -20,24 +22,18 @@ const runtime_safety = @import("runtime_safety.zig"); const translate_c = @import("translate_c.zig"); const gen_h = @import("gen_h.zig"); -const TestTarget = struct { - os: builtin.Os, - arch: builtin.Arch, - abi: builtin.Abi, -}; - -const test_targets = [_]TestTarget{ - TestTarget{ +const test_targets = [_]CrossTarget{ + CrossTarget{ .os = .linux, .arch = .x86_64, .abi = .gnu, }, - TestTarget{ + CrossTarget{ .os = .macosx, .arch = .x86_64, .abi = .gnu, }, - TestTarget{ + CrossTarget{ .os = .windows, .arch = .x86_64, .abi = .msvc, @@ -568,6 +564,7 @@ pub const CompileErrorContext = struct { link_libc: bool, is_exe: bool, is_test: bool, + target: Target = .Native, const SourceFile = struct { filename: []const u8, @@ -655,6 +652,14 @@ pub const CompileErrorContext = struct { zig_args.append("--output-dir") catch unreachable; zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable; + switch (self.case.target) { + .Native => {}, + .Cross => { + try zig_args.append("-target"); + try zig_args.append(try self.case.target.zigTriple(b.allocator)); + }, + } + switch (self.build_mode) { Mode.Debug => {}, Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, -- cgit v1.2.3