From 17a0458e530242254f21fc0b6825e1303ec06028 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Mon, 27 May 2024 13:24:43 +0300 Subject: [PATCH 1/2] Sema: add missing error for runtime `@ptrFromInt` to comptime-only type Closes #20083 --- src/Sema.zig | 9 +++++++++ ...runtime_@ptrFromInt_to_comptime_only_type.zig | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig diff --git a/src/Sema.zig b/src/Sema.zig index ca6d562eef..ba18869cdf 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -22722,7 +22722,16 @@ fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! .storage = .{ .elems = new_elems }, } })); } + if (try sema.typeRequiresComptime(ptr_ty)) { + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(block, src, "pointer to comptime-only type '{}' must be comptime-known, but operand is runtime-known", .{ptr_ty.fmt(mod)}); + errdefer msg.destroy(sema.gpa); + const src_decl = mod.declPtr(block.src_decl); + try sema.explainWhyTypeIsComptime(msg, src_decl.toSrcLoc(src, mod), ptr_ty); + break :msg msg; + }); + } try sema.requireRuntimeBlock(block, src, operand_src); if (!is_vector) { if (block.wantSafety() and (try sema.typeHasRuntimeBits(elem_ty) or elem_ty.zigTypeTag(mod) == .Fn)) { diff --git a/test/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig b/test/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig new file mode 100644 index 0000000000..0c4e844e76 --- /dev/null +++ b/test/cases/compile_errors/runtime_@ptrFromInt_to_comptime_only_type.zig @@ -0,0 +1,16 @@ +const GuSettings = struct { + fin: ?fn (c_int) callconv(.C) void, +}; +pub export fn callbackFin(id: c_int, arg: ?*anyopaque) void { + const settings: ?*GuSettings = @as(?*GuSettings, @ptrFromInt(@intFromPtr(arg))); + if (settings.?.fin != null) { + settings.?.fin.?(id & 0xffff); + } +} + +// error +// target=native +// +// :5:54: error: pointer to comptime-only type '?*tmp.GuSettings' must be comptime-known, but operand is runtime-known +// :2:10: note: struct requires comptime because of this field +// :2:10: note: use '*const fn (c_int) callconv(.C) void' for a function pointer type From 15791b8b1a426ebb524bb2c6312f2da66a540dbe Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Fri, 10 May 2024 18:09:51 +0300 Subject: [PATCH 2/2] Sema: validate function signature for Signal calling convention --- src/Sema.zig | 41 +++++++++---------- .../invalid_func_for_callconv.zig | 20 +++++++++ 2 files changed, 39 insertions(+), 22 deletions(-) create mode 100644 test/cases/compile_errors/invalid_func_for_callconv.zig diff --git a/src/Sema.zig b/src/Sema.zig index ba18869cdf..4007044269 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9714,18 +9714,18 @@ fn funcCommon( { return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); } - - if (cc_resolved == .Interrupt) switch (target.cpu.arch) { - .x86, .x86_64 => { + switch (cc_resolved) { + .Interrupt => if (target.cpu.arch.isX86()) { const err_code_size = target.ptrBitWidth(); switch (i) { - 0 => if (param_ty.zigTypeTag(mod) != .Pointer) return sema.fail(block, param_src, "parameter must be a pointer type", .{}), - 1 => if (param_ty.bitSize(mod) != err_code_size) return sema.fail(block, param_src, "parameter must be a {d}-bit integer", .{err_code_size}), - else => return sema.fail(block, param_src, "Interrupt calling convention supports up to 2 parameters, found {d}", .{i + 1}), + 0 => if (param_ty.zigTypeTag(mod) != .Pointer) return sema.fail(block, param_src, "first parameter of function with 'Interrupt' calling convention must be a pointer type", .{}), + 1 => if (param_ty.bitSize(mod) != err_code_size) return sema.fail(block, param_src, "second parameter of function with 'Interrupt' calling convention must be a {d}-bit integer", .{err_code_size}), + else => return sema.fail(block, param_src, "'Interrupt' calling convention supports up to 2 parameters, found {d}", .{i + 1}), } - }, - else => return sema.fail(block, param_src, "parameters are not allowed with Interrupt calling convention", .{}), - }; + } else return sema.fail(block, param_src, "parameters are not allowed with 'Interrupt' calling convention", .{}), + .Signal => return sema.fail(block, param_src, "parameters are not allowed with 'Signal' calling convention", .{}), + else => {}, + } } var ret_ty_requires_comptime = false; @@ -10031,6 +10031,16 @@ fn finishFunc( return sema.failWithOwnedErrorMsg(block, msg); } + switch (cc_resolved) { + .Interrupt, .Signal => if (return_type.zigTypeTag(mod) != .Void and return_type.zigTypeTag(mod) != .NoReturn) { + return sema.fail(block, ret_ty_src, "function with calling convention '{s}' must return 'void' or 'noreturn'", .{@tagName(cc_resolved)}); + }, + .Inline => if (is_noinline) { + return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{}); + }, + else => {}, + } + const arch = target.cpu.arch; if (@as(?[]const u8, switch (cc_resolved) { .Unspecified, .C, .Naked, .Async, .Inline => null, @@ -10074,20 +10084,7 @@ fn finishFunc( }); } - if (cc_resolved == .Interrupt and return_type.zigTypeTag(mod) != .Void) { - return sema.fail( - block, - cc_src, - "non-void return type '{}' not allowed in function with calling convention 'Interrupt'", - .{return_type.fmt(mod)}, - ); - } - - if (cc_resolved == .Inline and is_noinline) { - return sema.fail(block, cc_src, "'noinline' function cannot have callconv 'Inline'", .{}); - } if (is_generic and sema.no_partial_func_ty) return error.GenericPoison; - if (!final_is_generic and sema.wantErrorReturnTracing(return_type)) { // Make sure that StackTrace's fields are resolved so that the backend can // lower this fn type. diff --git a/test/cases/compile_errors/invalid_func_for_callconv.zig b/test/cases/compile_errors/invalid_func_for_callconv.zig new file mode 100644 index 0000000000..490b87bc4b --- /dev/null +++ b/test/cases/compile_errors/invalid_func_for_callconv.zig @@ -0,0 +1,20 @@ +export fn interrupt_param1(_: u32) callconv(.Interrupt) void {} +export fn interrupt_param2(_: *anyopaque, _: u32) callconv(.Interrupt) void {} +export fn interrupt_param3(_: *anyopaque, _: u64, _: u32) callconv(.Interrupt) void {} +export fn interrupt_ret(_: *anyopaque, _: u64) callconv(.Interrupt) u32 { + return 0; +} + +export fn signal_param(_: u32) callconv(.Signal) void {} +export fn signal_ret() callconv(.Signal) noreturn {} + +// error +// backend=stage2 +// target=x86_64-linux +// +// :1:28: error: first parameter of function with 'Interrupt' calling convention must be a pointer type +// :2:43: error: second parameter of function with 'Interrupt' calling convention must be a 64-bit integer +// :3:51: error: 'Interrupt' calling convention supports up to 2 parameters, found 3 +// :4:69: error: function with calling convention 'Interrupt' must return 'void' or 'noreturn' +// :8:24: error: parameters are not allowed with 'Signal' calling convention +// :9:34: error: callconv 'Signal' is only available on AVR, not x86_64