From 2cd3989cb32d97c8a60d9cf4154e049815259b46 Mon Sep 17 00:00:00 2001 From: Veikka Tuominen Date: Thu, 1 Sep 2022 13:16:06 +0300 Subject: Sema: add more validation to coerceVarArgParam Closes #12706 --- src/Sema.zig | 36 ++++++++++++++++---- .../int_literal_passed_as_variadic_arg.zig | 11 ------ .../compile_errors/variadic_arg_validation.zig | 29 ++++++++++++++++ test/standalone.zig | 1 + test/standalone/issue_12706/build.zig | 39 ++++++++++++++++++++++ test/standalone/issue_12706/main.zig | 12 +++++++ test/standalone/issue_12706/test.c | 11 ++++++ 7 files changed, 122 insertions(+), 17 deletions(-) delete mode 100644 test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig create mode 100644 test/cases/compile_errors/variadic_arg_validation.zig create mode 100644 test/standalone/issue_12706/build.zig create mode 100644 test/standalone/issue_12706/main.zig create mode 100644 test/standalone/issue_12706/test.c diff --git a/src/Sema.zig b/src/Sema.zig index 7a96fd51cd..e643f73962 100644 --- a/src/Sema.zig +++ b/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. diff --git a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig b/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig deleted file mode 100644 index be9ffaa884..0000000000 --- a/test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig +++ /dev/null @@ -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 diff --git a/test/cases/compile_errors/variadic_arg_validation.zig b/test/cases/compile_errors/variadic_arg_validation.zig new file mode 100644 index 0000000000..830d3a0877 --- /dev/null +++ b/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' diff --git a/test/standalone.zig b/test/standalone.zig index bfd683ec4c..26af0d5da5 100644 --- a/test/standalone.zig +++ b/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. diff --git a/test/standalone/issue_12706/build.zig b/test/standalone/issue_12706/build.zig new file mode 100644 index 0000000000..d84160a4f4 --- /dev/null +++ b/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); + } +} diff --git a/test/standalone/issue_12706/main.zig b/test/standalone/issue_12706/main.zig new file mode 100644 index 0000000000..b40c997561 --- /dev/null +++ b/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); +} diff --git a/test/standalone/issue_12706/test.c b/test/standalone/issue_12706/test.c new file mode 100644 index 0000000000..30b5c62697 --- /dev/null +++ b/test/standalone/issue_12706/test.c @@ -0,0 +1,11 @@ +#include + +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 -- cgit v1.2.3