Commit 9bbfb09fc3

freakmangd <53349189+freakmangd@users.noreply.github.com>
2024-06-05 22:06:51
translate-c: promote macros that reference var decls to inline functions
1 parent 8f27fdb
Changed files (5)
lib
compiler
src
test
lib/compiler/aro_translate_c/ast.zig
@@ -875,6 +875,7 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
         .declaration => unreachable,
         .warning => {
             const payload = node.castTag(.warning).?.data;
+            try c.buf.append('\n');
             try c.buf.appendSlice(payload);
             try c.buf.append('\n');
             return @as(NodeIndex, 0); // error: integer value 0 cannot be coerced to type 'std.mem.Allocator.Error!u32'
lib/compiler/aro_translate_c.zig
@@ -1381,7 +1381,6 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
         pub const Root = struct {
             base: ScopeExtraScope,
             sym_table: SymbolTable,
-            macro_table: SymbolTable,
             blank_macros: std.StringArrayHashMap(void),
             context: *ScopeExtraContext,
             nodes: std.ArrayList(ast.Node),
@@ -1393,7 +1392,6 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
                         .parent = null,
                     },
                     .sym_table = SymbolTable.init(c.gpa),
-                    .macro_table = SymbolTable.init(c.gpa),
                     .blank_macros = std.StringArrayHashMap(void).init(c.gpa),
                     .context = c,
                     .nodes = std.ArrayList(ast.Node).init(c.gpa),
@@ -1402,7 +1400,6 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
 
             pub fn deinit(scope: *Root) void {
                 scope.sym_table.deinit();
-                scope.macro_table.deinit();
                 scope.blank_macros.deinit();
                 scope.nodes.deinit();
             }
@@ -1410,7 +1407,7 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
             /// Check if the global scope contains this name, without looking into the "future", e.g.
             /// ignore the preprocessed decl and macro names.
             pub fn containsNow(scope: *Root, name: []const u8) bool {
-                return scope.sym_table.contains(name) or scope.macro_table.contains(name);
+                return scope.sym_table.contains(name);
             }
 
             /// Check if the global scope contains the name, includes all decls that haven't been translated yet.
src/translate_c.zig
@@ -177,7 +177,6 @@ pub fn translate(
 
     try transPreprocessorEntities(&context, ast_unit);
 
-    try addMacros(&context);
     for (context.alias_list.items) |alias| {
         const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
         try addTopLevelDecl(&context, alias.alias, node);
@@ -5105,6 +5104,7 @@ const MacroCtx = struct {
     i: usize = 0,
     loc: clang.SourceLocation,
     name: []const u8,
+    refs_var_decl: bool = false,
 
     fn peek(self: *MacroCtx) ?CToken.Id {
         if (self.i >= self.list.len) return null;
@@ -5244,7 +5244,7 @@ fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
                         // We define it as an empty string so that it can still be used with ++
                         const str_node = try Tag.string_literal.create(c.arena, "\"\"");
                         const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = name, .init = str_node });
-                        try c.global_scope.macro_table.put(name, var_decl);
+                        try addTopLevelDecl(c, name, var_decl);
                         try c.global_scope.blank_macros.put(name, {});
                         continue;
                     },
@@ -5291,7 +5291,7 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
                 try c.global_scope.blank_macros.put(m.name, {});
                 const init_node = try Tag.string_literal.create(c.arena, "\"\"");
                 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
-                try c.global_scope.macro_table.put(m.name, var_decl);
+                try addTopLevelDecl(c, m.name, var_decl);
                 return;
             },
             else => {},
@@ -5304,8 +5304,32 @@ fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
     if (last != .eof and last != .nl)
         return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
 
-    const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
-    try c.global_scope.macro_table.put(m.name, var_decl);
+    const node = node: {
+        const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
+
+        if (getFnProto(c, var_decl)) |proto_node| {
+            // If a macro aliases a global variable which is a function pointer, we conclude that
+            // the macro is intended to represent a function that assumes the function pointer
+            // variable is non-null and calls it.
+            break :node try transCreateNodeMacroFn(c, m.name, var_decl, proto_node);
+        } else if (m.refs_var_decl) {
+            const return_type = try Tag.typeof.create(c.arena, init_node);
+            const return_expr = try Tag.@"return".create(c.arena, init_node);
+            const block = try Tag.block_single.create(c.arena, return_expr);
+            try warn(c, scope, m.loc, "macro '{s}' contains a runtime value, translated to function", .{m.name});
+
+            break :node try Tag.pub_inline_fn.create(c.arena, .{
+                .name = m.name,
+                .params = &.{},
+                .return_type = return_type,
+                .body = block,
+            });
+        }
+
+        break :node var_decl;
+    };
+
+    try addTopLevelDecl(c, m.name, node);
 }
 
 fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
@@ -5315,7 +5339,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
             .name = m.name,
             .init = try Tag.helpers_macro.create(c.arena, pattern.impl),
         });
-        try c.global_scope.macro_table.put(m.name, decl);
+        try addTopLevelDecl(c, m.name, decl);
         return;
     }
 
@@ -5380,7 +5404,7 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
         .return_type = return_type,
         .body = try block_scope.complete(c),
     });
-    try c.global_scope.macro_table.put(m.name, fn_decl);
+    try addTopLevelDecl(c, m.name, fn_decl);
 }
 
 const ParseError = Error || error{ParseError};
@@ -5768,6 +5792,11 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
             if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);
             const identifier = try Tag.identifier.create(c.arena, mangled_name);
             scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
+            refs_var: {
+                const ident_node = c.global_scope.sym_table.get(slice) orelse break :refs_var;
+                const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
+                if (!var_decl_node.data.is_const) m.refs_var_decl = true;
+            }
             return identifier;
         },
         .l_paren => {
@@ -6496,17 +6525,3 @@ fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
     }
     return null;
 }
-
-fn addMacros(c: *Context) !void {
-    var it = c.global_scope.macro_table.iterator();
-    while (it.next()) |entry| {
-        if (getFnProto(c, entry.value_ptr.*)) |proto_node| {
-            // If a macro aliases a global variable which is a function pointer, we conclude that
-            // the macro is intended to represent a function that assumes the function pointer
-            // variable is non-null and calls it.
-            try addTopLevelDecl(c, entry.key_ptr.*, try transCreateNodeMacroFn(c, entry.key_ptr.*, entry.value_ptr.*, proto_node));
-        } else {
-            try addTopLevelDecl(c, entry.key_ptr.*, entry.value_ptr.*);
-        }
-    }
-}
test/cases/translate_c/macro_referencing_var.c
@@ -0,0 +1,21 @@
+extern float foo;
+#define FOO_TWICE foo * 2.0f
+#define FOO_NEGATIVE -foo
+
+#define BAR 10.0f
+#define BAR_TWICE BAR * 2.0f
+
+// translate-c
+// c_frontend=clang
+//
+// pub extern var foo: f32;
+//
+// pub inline fn FOO_TWICE() @TypeOf(foo * @as(f32, 2.0)) {
+//     return foo * @as(f32, 2.0);
+// }
+//
+// pub inline fn FOO_NEGATIVE() @TypeOf(-foo) {
+//     return -foo;
+// }
+// pub const BAR = @as(f32, 10.0);
+// pub const BAR_TWICE = BAR * @as(f32, 2.0);
test/translate_c.zig
@@ -223,7 +223,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\                             | (*((unsigned char *)(p) + 1) << 8)  \
         \\                             | (*((unsigned char *)(p) + 2) << 16))
     , &[_][]const u8{
-        \\pub const FOO = (foo + @as(c_int, 2)).*;
+        \\pub inline fn FOO() @TypeOf((foo + @as(c_int, 2)).*) {
+        \\    return (foo + @as(c_int, 2)).*;
+        \\}
         ,
         \\pub const VALUE = ((((@as(c_int, 1) + (@as(c_int, 2) * @as(c_int, 3))) + (@as(c_int, 4) * @as(c_int, 5))) + @as(c_int, 6)) << @as(c_int, 7)) | @intFromBool(@as(c_int, 8) == @as(c_int, 9));
         ,
@@ -452,7 +454,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\#define FOO -\
         \\BAR
     , &[_][]const u8{
-        \\pub const FOO = -BAR;
+        \\pub inline fn FOO() @TypeOf(-BAR) {
+        \\    return -BAR;
+        \\}
     });
 
     cases.add("struct with atomic field",
@@ -2453,9 +2457,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    _ = c.*.b;
         \\}
         ,
-        \\pub const DOT = a.b;
+        \\pub inline fn ARROW() @TypeOf(a.*.b) {
+        \\    return a.*.b;
+        \\}
         ,
-        \\pub const ARROW = a.*.b;
+        \\pub inline fn DOT() @TypeOf(a.b) {
+        \\    return a.b;
+        \\}
     });
 
     cases.add("array access",
@@ -2472,7 +2480,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\    return array[@as(c_uint, @intCast(index))];
         \\}
         ,
-        \\pub const ACCESS = array[@as(usize, @intCast(@as(c_int, 2)))];
+        \\pub inline fn ACCESS() @TypeOf(array[@as(usize, @intCast(@as(c_int, 2)))]) {
+        \\    return array[@as(usize, @intCast(@as(c_int, 2)))];
+        \\}
     });
 
     cases.add("cast signed array index to unsigned",
@@ -3130,7 +3140,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\ int a, b, c;
         \\#define FOO a ? b : c
     , &[_][]const u8{
-        \\pub const FOO = if (a) b else c;
+        \\pub inline fn FOO() @TypeOf(if (a) b else c) {
+        \\    return if (a) b else c;
+        \\}
     });
 
     cases.add("do while as expr",
@@ -3624,7 +3636,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
         \\#define FOO _
         \\int _ = 42;
     , &[_][]const u8{
-        \\pub const FOO = @"_";
+        \\pub inline fn FOO() @TypeOf(@"_") {
+        \\    return @"_";
+        \\}
         ,
         \\pub export var @"_": c_int = 42;
     });