Commit f81651932a

Jacob Young <jacobly0@users.noreply.github.com>
2022-10-06 09:59:07
c: hacks to fix incompatible redeclaration of library function warnings
1 parent f8a8197
Changed files (2)
src
codegen
test
behavior
src/codegen/c.zig
@@ -1309,6 +1309,33 @@ pub const DeclGen = struct {
         return name;
     }
 
+    fn renderOpaqueTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
+        const opaque_ty = t.cast(Type.Payload.Opaque).?.data;
+        const unqualified_name = dg.module.declPtr(opaque_ty.owner_decl).name;
+        const fqn = try opaque_ty.getFullyQualifiedName(dg.module);
+        defer dg.typedefs.allocator.free(fqn);
+
+        var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
+        defer buffer.deinit();
+
+        try buffer.writer().print("typedef struct {} ", .{fmtIdent(std.mem.span(unqualified_name))});
+
+        const name_start = buffer.items.len;
+        try buffer.writer().print("zig_O_{};\n", .{fmtIdent(fqn)});
+
+        const rendered = buffer.toOwnedSlice();
+        errdefer dg.typedefs.allocator.free(rendered);
+        const name = rendered[name_start .. rendered.len - 2];
+
+        try dg.typedefs.ensureUnusedCapacity(1);
+        dg.typedefs.putAssumeCapacityNoClobber(
+            try t.copy(dg.typedefs_arena),
+            .{ .name = name, .rendered = rendered },
+        );
+
+        return name;
+    }
+
     /// Renders a type as a single identifier, generating intermediate typedefs
     /// if necessary.
     ///
@@ -1387,7 +1414,16 @@ pub const DeclGen = struct {
                     return w.writeAll(name);
                 }
 
-                try dg.renderType(w, t.elemType());
+                const child_ty = t.childType();
+                if (t.isCPtr() and child_ty.eql(Type.u8, dg.module) and dg.decl.val.tag() == .extern_fn) {
+                    // This is a hack, since the c compiler expects a lot of external
+                    // library functions to have char pointers in their signatures, but
+                    // u8 and i8 produce unsigned char and signed char respectively,
+                    // which in C are not very usefully different than char.
+                    try w.writeAll("char");
+                } else {
+                    try dg.renderType(w, child_ty);
+                }
                 if (t.isConstPtr()) {
                     try w.writeAll(" const");
                 }
@@ -1456,7 +1492,16 @@ pub const DeclGen = struct {
 
                 try dg.renderType(w, int_tag_ty);
             },
-            .Opaque => return w.writeAll("void"),
+            .Opaque => switch (t.tag()) {
+                .anyopaque => try w.writeAll("void"),
+                .@"opaque" => {
+                    const name = dg.getTypedefName(t) orelse
+                        try dg.renderOpaqueTypedef(t);
+
+                    try w.writeAll(name);
+                },
+                else => unreachable,
+            },
 
             .Frame,
             .AnyFrame,
@@ -2830,12 +2875,16 @@ fn airCall(
         }
     };
 
+    var is_extern = false;
     callee: {
         known: {
             const fn_decl = fn_decl: {
                 const callee_val = f.air.value(pl_op.operand) orelse break :known;
                 break :fn_decl switch (callee_val.tag()) {
-                    .extern_fn => callee_val.castTag(.extern_fn).?.data.owner_decl,
+                    .extern_fn => blk: {
+                        is_extern = true;
+                        break :blk callee_val.castTag(.extern_fn).?.data.owner_decl;
+                    },
                     .function => callee_val.castTag(.function).?.data.owner_decl,
                     .decl_ref => callee_val.castTag(.decl_ref).?.data,
                     else => break :known,
@@ -2857,6 +2906,13 @@ fn airCall(
         if (args_written != 0) {
             try writer.writeAll(", ");
         }
+        if (is_extern and ty.isCPtr() and ty.childType().tag() == .u8) {
+            // Corresponds with hack in renderType .Pointer case.
+            try writer.writeAll("(char");
+            if (ty.isConstPtr()) try writer.writeAll(" const");
+            if (ty.isVolatilePtr()) try writer.writeAll(" volatile");
+            try writer.writeAll(" *)");
+        }
         if (f.air.value(arg)) |val| {
             try f.object.dg.renderValue(writer, f.air.typeOf(arg), val, .FunctionArgument);
         } else {
test/behavior/bugs/4328.zig
@@ -5,10 +5,10 @@ const FILE = extern struct {
     dummy_field: u8,
 };
 
-extern fn printf([*c]const u8, ...) c_int;
-extern fn fputs([*c]const u8, noalias [*c]FILE) c_int;
-extern fn ftell([*c]FILE) c_long;
-extern fn fopen([*c]const u8, [*c]const u8) [*c]FILE;
+extern fn c_printf([*c]const u8, ...) c_int;
+extern fn c_fputs([*c]const u8, noalias [*c]FILE) c_int;
+extern fn c_ftell([*c]FILE) c_long;
+extern fn c_fopen([*c]const u8, [*c]const u8) [*c]FILE;
 
 const S = extern struct {
     state: c_short,
@@ -18,7 +18,7 @@ const S = extern struct {
 
 test "Extern function calls in @TypeOf" {
     const Test = struct {
-        fn test_fn_1(a: anytype, b: anytype) @TypeOf(printf("%d %s\n", a, b)) {
+        fn test_fn_1(a: anytype, b: anytype) @TypeOf(c_printf("%d %s\n", a, b)) {
             return 0;
         }
 
@@ -38,7 +38,7 @@ test "Extern function calls in @TypeOf" {
 
 test "Peer resolution of extern function calls in @TypeOf" {
     const Test = struct {
-        fn test_fn() @TypeOf(ftell(null), fputs(null, null)) {
+        fn test_fn() @TypeOf(c_ftell(null), c_fputs(null, null)) {
             return 0;
         }
 
@@ -55,12 +55,12 @@ test "Extern function calls, dereferences and field access in @TypeOf" {
     if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
 
     const Test = struct {
-        fn test_fn_1(a: c_long) @TypeOf(fopen("test", "r").*) {
+        fn test_fn_1(a: c_long) @TypeOf(c_fopen("test", "r").*) {
             _ = a;
             return .{ .dummy_field = 0 };
         }
 
-        fn test_fn_2(a: anytype) @TypeOf(fopen("test", "r").*.dummy_field) {
+        fn test_fn_2(a: anytype) @TypeOf(c_fopen("test", "r").*.dummy_field) {
             _ = a;
             return 255;
         }