Commit a8bd55e085

mlugg <mlugg@mlugg.co.uk>
2023-03-07 23:24:50
translate-c: translate extern unknown-length arrays using @extern
Resolves: #14743
1 parent c93e0d8
Changed files (3)
src/translate_c/ast.zig
@@ -158,6 +158,8 @@ pub const Node = extern union {
         vector_zero_init,
         /// @shuffle(type, a, b, mask)
         shuffle,
+        /// @extern(ty, .{ .name = n })
+        builtin_extern,
 
         /// @import("std").zig.c_translation.MacroArithmetic.<op>(lhs, rhs)
         macro_arithmetic,
@@ -373,6 +375,7 @@ pub const Node = extern union {
                 .field_access => Payload.FieldAccess,
                 .string_slice => Payload.StringSlice,
                 .shuffle => Payload.Shuffle,
+                .builtin_extern => Payload.Extern,
                 .macro_arithmetic => Payload.MacroArithmetic,
             };
         }
@@ -718,6 +721,14 @@ pub const Payload = struct {
         },
     };
 
+    pub const Extern = struct {
+        base: Payload,
+        data: struct {
+            type: Node,
+            name: Node,
+        },
+    };
+
     pub const MacroArithmetic = struct {
         base: Payload,
         data: struct {
@@ -1409,6 +1420,22 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
                 payload.mask_vector,
             });
         },
+        .builtin_extern => {
+            const payload = node.castTag(.builtin_extern).?.data;
+
+            var info_inits: [1]Payload.ContainerInitDot.Initializer = .{
+                .{ .name = "name", .value = payload.name },
+            };
+            var info_payload: Payload.ContainerInitDot = .{
+                .base = .{ .tag = .container_init_dot },
+                .data = &info_inits,
+            };
+
+            return renderBuiltinCall(c, "@extern", &.{
+                payload.type,
+                .{ .ptr_otherwise = &info_payload.base },
+            });
+        },
         .macro_arithmetic => {
             const payload = node.castTag(.macro_arithmetic).?.data;
             const op = @tagName(payload.op);
@@ -2348,6 +2375,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
         .div_exact,
         .offset_of,
         .shuffle,
+        .builtin_extern,
         .static_local_var,
         .mut_str,
         .macro_arithmetic,
src/translate_c.zig
@@ -784,9 +784,9 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
 
     const qual_type = var_decl.getTypeSourceInfo_getType();
     const storage_class = var_decl.getStorageClass();
-    const is_const = qual_type.isConstQualified();
     const has_init = var_decl.hasInit();
     const decl_init = var_decl.getInit();
+    var is_const = qual_type.isConstQualified();
 
     // In C extern variables with initializers behave like Zig exports.
     // extern int foo = 2;
@@ -843,6 +843,20 @@ fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]co
 
         // std.mem.zeroes(T)
         init_node = try Tag.std_mem_zeroes.create(c.arena, type_node);
+    } else if (qual_type.getTypeClass() == .IncompleteArray) {
+        // Oh no, an extern array of unknown size! These are really fun because there's no
+        // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
+        // to the data initialized via @extern.
+
+        const name_str = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{var_name});
+        init_node = try Tag.builtin_extern.create(c.arena, .{
+            .type = type_node,
+            .name = try Tag.string_literal.create(c.arena, name_str),
+        });
+
+        // Since this is really a pointer to the underlying data, we tweak a few properties.
+        is_extern = false;
+        is_const = true;
     }
 
     const linksection_string = blk: {
test/translate_c.zig
@@ -3948,4 +3948,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
             \\}
         });
     }
+
+    cases.add("extern array of unknown length",
+        \\extern int foo[];
+    , &[_][]const u8{
+        \\const foo: [*c]c_int = @extern([*c]c_int, .{
+        \\    .name = "foo",
+        \\});
+    });
 }