Commit 2cd3989cb3

Veikka Tuominen <git@vexu.eu>
2022-09-01 12:16:06
Sema: add more validation to coerceVarArgParam
Closes #12706
1 parent 2b92c5a
src/Sema.zig
@@ -24075,16 +24075,40 @@ fn coerceVarArgParam(
     inst: Air.Inst.Ref,
     inst_src: LazySrcLoc,
 ) !Air.Inst.Ref {
-    const inst_ty = sema.typeOf(inst);
     if (block.is_typeof) return inst;
 
-    switch (inst_ty.zigTypeTag()) {
+    const coerced = switch (sema.typeOf(inst).zigTypeTag()) {
         // TODO consider casting to c_int/f64 if they fit
-        .ComptimeInt, .ComptimeFloat => return sema.fail(block, inst_src, "integer and float literals in var args function must be casted", .{}),
-        else => {},
+        .ComptimeInt, .ComptimeFloat => return sema.fail(
+            block,
+            inst_src,
+            "integer and float literals passed variadic function must be casted to a fixed-size number type",
+            .{},
+        ),
+        .Fn => blk: {
+            const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
+            const fn_decl = fn_val.pointerDecl().?;
+            break :blk try sema.analyzeDeclRef(fn_decl);
+        },
+        .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
+        else => inst,
+    };
+
+    const coerced_ty = sema.typeOf(coerced);
+    if (!sema.validateExternType(coerced_ty, .other)) {
+        const msg = msg: {
+            const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
+            errdefer msg.destroy(sema.gpa);
+
+            const src_decl = sema.mod.declPtr(block.src_decl);
+            try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .other);
+
+            try sema.addDeclaredHereNote(msg, coerced_ty);
+            break :msg msg;
+        };
+        return sema.failWithOwnedErrorMsg(msg);
     }
-    // TODO implement more of this function.
-    return inst;
+    return coerced;
 }
 
 // TODO migrate callsites to use storePtr2 instead.
test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig
@@ -1,11 +0,0 @@
-extern fn printf([*:0]const u8, ...) c_int;
-
-pub export fn entry() void {
-    _ = printf("%d %d %d %d\n", 1, 2, 3, 4);
-}
-
-// error
-// backend=stage2
-// target=native
-//
-// :4:33: error: integer and float literals in var args function must be casted
test/cases/compile_errors/variadic_arg_validation.zig
@@ -0,0 +1,29 @@
+extern fn printf([*:0]const u8, ...) c_int;
+
+pub export fn entry() void {
+    _ = printf("%d %d %d %d\n", 1, 2, 3, 4);
+}
+
+pub export fn entry1() void {
+    var arr: [2]u8 = undefined;
+    _ = printf("%d\n", arr);
+}
+
+pub export fn entry2() void {
+    _ = printf("%d\n", @as(u48, 2));
+}
+
+pub export fn entry3() void {
+    _ = printf("%d\n", {});
+}
+
+// error
+// backend=stage2
+// target=native
+//
+// :4:33: error: integer and float literals passed variadic function must be casted to a fixed-size number type
+// :9:24: error: arrays must be passed by reference to variadic function
+// :13:24: error: cannot pass 'u48' to variadic function
+// :13:24: note: only integers with power of two bits are extern compatible
+// :17:24: error: cannot pass 'void' to variadic function
+// :17:24: note: 'void' is a zero bit type; for C 'void' use 'anyopaque'
test/standalone/issue_12706/build.zig
@@ -0,0 +1,39 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const Builder = std.build.Builder;
+const CrossTarget = std.zig.CrossTarget;
+
+// TODO integrate this with the std.build executor API
+fn isRunnableTarget(t: CrossTarget) bool {
+    if (t.isNative()) return true;
+
+    return (t.getOsTag() == builtin.os.tag and
+        t.getCpuArch() == builtin.cpu.arch);
+}
+
+pub fn build(b: *Builder) void {
+    const mode = b.standardReleaseOptions();
+    const target = b.standardTargetOptions(.{});
+
+    const exe = b.addExecutable("main", "main.zig");
+    exe.setBuildMode(mode);
+    exe.install();
+
+    const c_sources = [_][]const u8{
+        "test.c",
+    };
+
+    exe.addCSourceFiles(&c_sources, &.{});
+    exe.linkLibC();
+
+    exe.setTarget(target);
+    b.default_step.dependOn(&exe.step);
+
+    const test_step = b.step("test", "Test the program");
+    if (isRunnableTarget(target)) {
+        const run_cmd = exe.run();
+        test_step.dependOn(&run_cmd.step);
+    } else {
+        test_step.dependOn(&exe.step);
+    }
+}
test/standalone/issue_12706/main.zig
@@ -0,0 +1,12 @@
+const std = @import("std");
+extern fn testFnPtr(n: c_int, ...) void;
+
+const val: c_int = 123;
+
+fn func(a: c_int) callconv(.C) void {
+    std.debug.assert(a == val);
+}
+
+pub fn main() void {
+    testFnPtr(2, func, val);
+}
test/standalone/issue_12706/test.c
@@ -0,0 +1,11 @@
+#include <stdarg.h>
+
+void testFnPtr(int n, ...) {
+    va_list ap;
+    va_start(ap, n);
+
+    void (*fnPtr)(int) = va_arg(ap, void (*)(int));
+    int arg = va_arg(ap, int);
+    fnPtr(arg);
+    va_end(ap);
+}
\ No newline at end of file
test/standalone.zig
@@ -66,6 +66,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
     if (builtin.os.tag == .linux) {
         cases.addBuildFile("test/standalone/pie/build.zig", .{});
     }
+    cases.addBuildFile("test/standalone/issue_12706/build.zig", .{});
 
     // Ensure the development tools are buildable.