Commit 15791b8b1a

Veikka Tuominen <git@vexu.eu>
2024-05-10 17:09:51
Sema: validate function signature for Signal calling convention
1 parent 17a0458
Changed files (2)
src
test
cases
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.
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