authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-01 13:16:06+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-01 13:16:33+03:00
log2cd3989cb32d97c8a60d9cf4154e049815259b46
tree989dd82927529f4133259e26c36d1c0ef0e19a44
parent2b92c5a23e912df56885ff10d690ff7bfd1e3f47

Sema: add more validation to coerceVarArgParam

Closes #12706

7 files changed, 122 insertions(+), 17 deletions(-)

src/Sema.zig+30-6
...@@ -24075,16 +24075,40 @@ fn coerceVarArgParam(...@@ -24075,16 +24075,40 @@ fn coerceVarArgParam(
24075 inst: Air.Inst.Ref,24075 inst: Air.Inst.Ref,
24076 inst_src: LazySrcLoc,24076 inst_src: LazySrcLoc,
24077) !Air.Inst.Ref {24077) !Air.Inst.Ref {
24078 const inst_ty = sema.typeOf(inst);
24079 if (block.is_typeof) return inst;24078 if (block.is_typeof) return inst;
2408024079
24081 switch (inst_ty.zigTypeTag()) {24080 const coerced = switch (sema.typeOf(inst).zigTypeTag()) {
24082 // TODO consider casting to c_int/f64 if they fit24081 // TODO consider casting to c_int/f64 if they fit
24083 .ComptimeInt, .ComptimeFloat => return sema.fail(block, inst_src, "integer and float literals in var args function must be casted", .{}),24082 .ComptimeInt, .ComptimeFloat => return sema.fail(
24084 else => {},24083 block,
24084 inst_src,
24085 "integer and float literals passed variadic function must be casted to a fixed-size number type",
24086 .{},
24087 ),
24088 .Fn => blk: {
24089 const fn_val = try sema.resolveConstValue(block, .unneeded, inst, undefined);
24090 const fn_decl = fn_val.pointerDecl().?;
24091 break :blk try sema.analyzeDeclRef(fn_decl);
24092 },
24093 .Array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}),
24094 else => inst,
24095 };
24096
24097 const coerced_ty = sema.typeOf(coerced);
24098 if (!sema.validateExternType(coerced_ty, .other)) {
24099 const msg = msg: {
24100 const msg = try sema.errMsg(block, inst_src, "cannot pass '{}' to variadic function", .{coerced_ty.fmt(sema.mod)});
24101 errdefer msg.destroy(sema.gpa);
24102
24103 const src_decl = sema.mod.declPtr(block.src_decl);
24104 try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .other);
24105
24106 try sema.addDeclaredHereNote(msg, coerced_ty);
24107 break :msg msg;
24108 };
24109 return sema.failWithOwnedErrorMsg(msg);
24085 }24110 }
24086 // TODO implement more of this function.24111 return coerced;
24087 return inst;
24088}24112}
2408924113
24090// TODO migrate callsites to use storePtr2 instead.24114// TODO migrate callsites to use storePtr2 instead.
test/cases/compile_errors/int_literal_passed_as_variadic_arg.zig deleted-11
...@@ -1,11 +0,0 @@
1extern fn printf([*:0]const u8, ...) c_int;
2
3pub export fn entry() void {
4 _ = printf("%d %d %d %d\n", 1, 2, 3, 4);
5}
6
7// error
8// backend=stage2
9// target=native
10//
11// :4:33: error: integer and float literals in var args function must be casted
test/cases/compile_errors/variadic_arg_validation.zig created+29
...@@ -0,0 +1,29 @@
1extern fn printf([*:0]const u8, ...) c_int;
2
3pub export fn entry() void {
4 _ = printf("%d %d %d %d\n", 1, 2, 3, 4);
5}
6
7pub export fn entry1() void {
8 var arr: [2]u8 = undefined;
9 _ = printf("%d\n", arr);
10}
11
12pub export fn entry2() void {
13 _ = printf("%d\n", @as(u48, 2));
14}
15
16pub export fn entry3() void {
17 _ = printf("%d\n", {});
18}
19
20// error
21// backend=stage2
22// target=native
23//
24// :4:33: error: integer and float literals passed variadic function must be casted to a fixed-size number type
25// :9:24: error: arrays must be passed by reference to variadic function
26// :13:24: error: cannot pass 'u48' to variadic function
27// :13:24: note: only integers with power of two bits are extern compatible
28// :17:24: error: cannot pass 'void' to variadic function
29// :17:24: note: 'void' is a zero bit type; for C 'void' use 'anyopaque'
test/standalone.zig+1
...@@ -66,6 +66,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -66,6 +66,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
66 if (builtin.os.tag == .linux) {66 if (builtin.os.tag == .linux) {
67 cases.addBuildFile("test/standalone/pie/build.zig", .{});67 cases.addBuildFile("test/standalone/pie/build.zig", .{});
68 }68 }
69 cases.addBuildFile("test/standalone/issue_12706/build.zig", .{});
6970
70 // Ensure the development tools are buildable.71 // Ensure the development tools are buildable.
7172
test/standalone/issue_12706/build.zig created+39
...@@ -0,0 +1,39 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;
5
6// TODO integrate this with the std.build executor API
7fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;
9
10 return (t.getOsTag() == builtin.os.tag and
11 t.getCpuArch() == builtin.cpu.arch);
12}
13
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
16 const target = b.standardTargetOptions(.{});
17
18 const exe = b.addExecutable("main", "main.zig");
19 exe.setBuildMode(mode);
20 exe.install();
21
22 const c_sources = [_][]const u8{
23 "test.c",
24 };
25
26 exe.addCSourceFiles(&c_sources, &.{});
27 exe.linkLibC();
28
29 exe.setTarget(target);
30 b.default_step.dependOn(&exe.step);
31
32 const test_step = b.step("test", "Test the program");
33 if (isRunnableTarget(target)) {
34 const run_cmd = exe.run();
35 test_step.dependOn(&run_cmd.step);
36 } else {
37 test_step.dependOn(&exe.step);
38 }
39}
test/standalone/issue_12706/main.zig created+12
...@@ -0,0 +1,12 @@
1const std = @import("std");
2extern fn testFnPtr(n: c_int, ...) void;
3
4const val: c_int = 123;
5
6fn func(a: c_int) callconv(.C) void {
7 std.debug.assert(a == val);
8}
9
10pub fn main() void {
11 testFnPtr(2, func, val);
12}
test/standalone/issue_12706/test.c created+11
...@@ -0,0 +1,11 @@
1#include <stdarg.h>
2
3void testFnPtr(int n, ...) {
4 va_list ap;
5 va_start(ap, n);
6
7 void (*fnPtr)(int) = va_arg(ap, void (*)(int));
8 int arg = va_arg(ap, int);
9 fnPtr(arg);
10 va_end(ap);
11}
\ No newline at end of file