diff --git a/lib/compiler/aro_translate_c.zig b/lib/compiler/aro_translate_c.zig index c4126e9ff4..8f8498592b 100644 --- a/lib/compiler/aro_translate_c.zig +++ b/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. diff --git a/lib/compiler/aro_translate_c/ast.zig b/lib/compiler/aro_translate_c/ast.zig index b74d50c659..74594af8fe 100644 --- a/lib/compiler/aro_translate_c/ast.zig +++ b/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' diff --git a/src/translate_c.zig b/src/translate_c.zig index a7de9a02a5..148b73955b 100644 --- a/src/translate_c.zig +++ b/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.*); - } - } -} diff --git a/test/cases/translate_c/macro_referencing_var.c b/test/cases/translate_c/macro_referencing_var.c new file mode 100644 index 0000000000..5675d06eda --- /dev/null +++ b/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); diff --git a/test/translate_c.zig b/test/translate_c.zig index 890f93a106..c07b29f772 100644 --- a/test/translate_c.zig +++ b/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; });