Commit b33efa3739

Evan Haas <evan@lagerdata.com>
2021-07-22 18:17:54
translate-c: Handle ambiguous cast or call macro
Fixes #9425
1 parent dc4fa83
Changed files (2)
lib/std/zig/c_translation.zig
@@ -390,6 +390,20 @@ pub const Macros = struct {
     pub fn WL_CONTAINER_OF(ptr: anytype, sample: anytype, comptime member: []const u8) @TypeOf(sample) {
         return @fieldParentPtr(@TypeOf(sample.*), member, ptr);
     }
+
+    /// A 2-argument function-like macro defined as #define FOO(A, B) (A)(B)
+    /// could be either: cast B to A, or call A with the value B.
+    pub fn CAST_OR_CALL(a: anytype, b: anytype) switch (@typeInfo(@TypeOf(a))) {
+        .Type => a,
+        .Fn => |fn_info| fn_info.return_type orelse void,
+        else => |info| @compileError("Unexpected argument type: " ++ @tagName(info)),
+    } {
+        switch (@typeInfo(@TypeOf(a))) {
+            .Type => return cast(a, b),
+            .Fn => return a(b),
+            else => unreachable, // return type will be a compile error otherwise
+        }
+    }
 };
 
 test "Macro suffix functions" {
@@ -430,3 +444,41 @@ test "WL_CONTAINER_OF" {
     var ptr = Macros.WL_CONTAINER_OF(&x.b, &y, "b");
     try testing.expectEqual(&x, ptr);
 }
+
+test "CAST_OR_CALL casting" {
+    var arg = @as(c_int, 1000);
+    var casted = Macros.CAST_OR_CALL(u8, arg);
+    try testing.expectEqual(cast(u8, arg), casted);
+
+    const S = struct {
+        x: u32 = 0,
+    };
+    var s = S{};
+    var casted_ptr = Macros.CAST_OR_CALL(*u8, &s);
+    try testing.expectEqual(cast(*u8, &s), casted_ptr);
+}
+
+test "CAST_OR_CALL calling" {
+    const Helper = struct {
+        var last_val: bool = false;
+        fn returnsVoid(val: bool) void {
+            last_val = val;
+        }
+        fn returnsBool(f: f32) bool {
+            return f > 0;
+        }
+        fn identity(self: c_uint) c_uint {
+            return self;
+        }
+    };
+
+    Macros.CAST_OR_CALL(Helper.returnsVoid, true);
+    try testing.expectEqual(true, Helper.last_val);
+    Macros.CAST_OR_CALL(Helper.returnsVoid, false);
+    try testing.expectEqual(false, Helper.last_val);
+
+    try testing.expectEqual(Helper.returnsBool(1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, 1)));
+    try testing.expectEqual(Helper.returnsBool(-1), Macros.CAST_OR_CALL(Helper.returnsBool, @as(f32, -1)));
+
+    try testing.expectEqual(Helper.identity(@as(c_uint, 100)), Macros.CAST_OR_CALL(Helper.identity, @as(c_uint, 100)));
+}
src/translate_c.zig
@@ -4868,6 +4868,8 @@ const PatternList = struct {
         [2][]const u8{ "Ull_SUFFIX(X) (X ## Ull)", "ULL_SUFFIX" },
         [2][]const u8{ "ULL_SUFFIX(X) (X ## ULL)", "ULL_SUFFIX" },
 
+        [2][]const u8{ "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL" },
+
         [2][]const u8{
             \\wl_container_of(ptr, sample, member)                     \
             \\(__typeof__(sample))((char *)(ptr) -                     \
@@ -5048,6 +5050,7 @@ test "Macro matching" {
     , "WL_CONTAINER_OF");
 
     try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
+    try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", "CAST_OR_CALL");
 }
 
 const MacroCtx = struct {