Skip to content

Commit fb1d499

Browse files
authored
Make translate-c more robust in handling macro functions.
Translate-c didn't properly account for C macro functions having parameter names that are C keywords. So something like `#define FOO(float) ((float) + 10)` would've been interpreted as casting `+10` to a `float` type, instead of adding `10` to the parameter `float`. An example of a real-world macro function like this is SDL3's `SDL_DEFINE_AUDIO_FORMAT` from `SDL_audio.h`, which uses `signed` as a parameter.
1 parent f2f36c4 commit fb1d499

File tree

2 files changed

+179
-68
lines changed

2 files changed

+179
-68
lines changed

src/translate_c.zig

Lines changed: 97 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5226,6 +5226,7 @@ const MacroCtx = struct {
52265226
loc: clang.SourceLocation,
52275227
name: []const u8,
52285228
refs_var_decl: bool = false,
5229+
fn_params: ?[]const ast.Payload.Param = null,
52295230

52305231
fn peek(self: *MacroCtx) ?CToken.Id {
52315232
if (self.i >= self.list.len) return null;
@@ -5298,6 +5299,15 @@ const MacroCtx = struct {
52985299
}
52995300
return null;
53005301
}
5302+
5303+
fn checkFnParam(self: *MacroCtx, str: []const u8) bool {
5304+
if (self.fn_params == null) return false;
5305+
5306+
for (self.fn_params.?) |param| {
5307+
if (mem.eql(u8, param.name.?, str)) return true;
5308+
}
5309+
return false;
5310+
}
53015311
};
53025312

53035313
fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) ![]const u8 {
@@ -5474,10 +5484,9 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
54745484
defer fn_params.deinit();
54755485

54765486
while (true) {
5477-
switch (m.peek().?) {
5478-
.identifier, .extended_identifier => _ = m.next(),
5479-
else => break,
5480-
}
5487+
if (!m.peek().?.isMacroIdentifier()) break;
5488+
5489+
_ = m.next();
54815490

54825491
const mangled_name = try block_scope.makeMangledName(c, m.slice());
54835492
try fn_params.append(.{
@@ -5490,6 +5499,8 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
54905499
_ = m.next();
54915500
}
54925501

5502+
m.fn_params = fn_params.items;
5503+
54935504
try m.skip(c, .r_paren);
54945505

54955506
if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) {
@@ -5905,38 +5916,41 @@ fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
59055916
.pp_num => {
59065917
return parseCNumLit(c, m);
59075918
},
5908-
.identifier, .extended_identifier => {
5909-
if (c.global_scope.blank_macros.contains(slice)) {
5910-
return parseCPrimaryExpr(c, m, scope);
5911-
}
5912-
const mangled_name = scope.getAlias(slice);
5913-
if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);
5914-
const identifier = try Tag.identifier.create(c.arena, mangled_name);
5915-
scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
5916-
refs_var: {
5917-
const ident_node = c.global_scope.sym_table.get(slice) orelse break :refs_var;
5918-
const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
5919-
if (!var_decl_node.data.is_const) m.refs_var_decl = true;
5920-
}
5921-
return identifier;
5922-
},
59235919
.l_paren => {
59245920
const inner_node = try parseCExpr(c, m, scope);
59255921

59265922
try m.skip(c, .r_paren);
59275923
return inner_node;
59285924
},
5929-
else => {
5930-
// for handling type macros (EVIL)
5931-
// TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
5932-
m.i -= 1;
5933-
if (try parseCTypeName(c, m, scope, true)) |type_name| {
5934-
return type_name;
5935-
}
5936-
try m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
5937-
return error.ParseError;
5938-
},
5925+
else => {},
5926+
}
5927+
5928+
// The C preprocessor has no knowledge of C, so C keywords aren't special in macros.
5929+
// Thus the current token should be treated like an identifier if its name matches a parameter.
5930+
if (tok == .identifier or tok == .extended_identifier or m.checkFnParam(slice)) {
5931+
if (c.global_scope.blank_macros.contains(slice)) {
5932+
return parseCPrimaryExpr(c, m, scope);
5933+
}
5934+
const mangled_name = scope.getAlias(slice);
5935+
if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);
5936+
const identifier = try Tag.identifier.create(c.arena, mangled_name);
5937+
scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
5938+
refs_var: {
5939+
const ident_node = c.global_scope.sym_table.get(slice) orelse break :refs_var;
5940+
const var_decl_node = ident_node.castTag(.var_decl) orelse break :refs_var;
5941+
if (!var_decl_node.data.is_const) m.refs_var_decl = true;
5942+
}
5943+
return identifier;
5944+
}
5945+
5946+
// for handling type macros (EVIL)
5947+
// TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
5948+
m.i -= 1;
5949+
if (try parseCTypeName(c, m, scope, true)) |type_name| {
5950+
return type_name;
59395951
}
5952+
try m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
5953+
return error.ParseError;
59405954
}
59415955

59425956
fn macroIntFromBool(c: *Context, node: Node) !Node {
@@ -6199,41 +6213,50 @@ fn parseCTypeName(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) Pa
61996213

62006214
fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) ParseError!?Node {
62016215
const tok = m.next().?;
6202-
switch (tok) {
6203-
.identifier, .extended_identifier => {
6204-
if (c.global_scope.blank_macros.contains(m.slice())) {
6205-
return try parseCSpecifierQualifierList(c, m, scope, allow_fail);
6206-
}
6207-
const mangled_name = scope.getAlias(m.slice());
6208-
if (!allow_fail or c.typedefs.contains(mangled_name)) {
6209-
if (builtin_typedef_map.get(mangled_name)) |ty| return try Tag.type.create(c.arena, ty);
6210-
return try Tag.identifier.create(c.arena, mangled_name);
6211-
}
6212-
},
6213-
.keyword_void => return try Tag.type.create(c.arena, "anyopaque"),
6214-
.keyword_bool => return try Tag.type.create(c.arena, "bool"),
6215-
.keyword_char,
6216-
.keyword_int,
6217-
.keyword_short,
6218-
.keyword_long,
6219-
.keyword_float,
6220-
.keyword_double,
6221-
.keyword_signed,
6222-
.keyword_unsigned,
6223-
.keyword_complex,
6224-
=> {
6225-
m.i -= 1;
6226-
return try parseCNumericType(c, m);
6227-
},
6228-
.keyword_enum, .keyword_struct, .keyword_union => {
6229-
// struct Foo will be declared as struct_Foo by transRecordDecl
6230-
const slice = m.slice();
6231-
try m.skip(c, .identifier);
6216+
const slice = m.slice();
6217+
const mangled_name = scope.getAlias(slice);
6218+
if (!m.checkFnParam(mangled_name)) {
6219+
switch (tok) {
6220+
.identifier, .extended_identifier => {
6221+
if (c.global_scope.blank_macros.contains(m.slice())) {
6222+
return try parseCSpecifierQualifierList(c, m, scope, allow_fail);
6223+
}
6224+
if (!allow_fail or c.typedefs.contains(mangled_name)) {
6225+
if (builtin_typedef_map.get(mangled_name)) |ty| return try Tag.type.create(c.arena, ty);
6226+
return try Tag.identifier.create(c.arena, mangled_name);
6227+
}
6228+
},
6229+
.keyword_void => return try Tag.type.create(c.arena, "anyopaque"),
6230+
.keyword_bool => return try Tag.type.create(c.arena, "bool"),
6231+
.keyword_char,
6232+
.keyword_int,
6233+
.keyword_short,
6234+
.keyword_long,
6235+
.keyword_float,
6236+
.keyword_double,
6237+
.keyword_signed,
6238+
.keyword_unsigned,
6239+
.keyword_complex,
6240+
=> {
6241+
m.i -= 1;
6242+
return try parseCNumericType(c, m);
6243+
},
6244+
.keyword_enum, .keyword_struct, .keyword_union => {
6245+
// struct Foo will be declared as struct_Foo by transRecordDecl
6246+
try m.skip(c, .identifier);
62326247

6233-
const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
6234-
return try Tag.identifier.create(c.arena, name);
6235-
},
6236-
else => {},
6248+
const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
6249+
return try Tag.identifier.create(c.arena, name);
6250+
},
6251+
else => {},
6252+
}
6253+
} else {
6254+
if (allow_fail) {
6255+
m.i -= 1;
6256+
return null;
6257+
} else {
6258+
return try Tag.identifier.create(c.arena, mangled_name);
6259+
}
62376260
}
62386261

62396262
if (allow_fail) {
@@ -6511,7 +6534,7 @@ fn parseCPostfixExprInner(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?
65116534
}
65126535

65136536
fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
6514-
switch (m.next().?) {
6537+
sw: switch (m.next().?) {
65156538
.bang => {
65166539
const operand = try macroIntToBool(c, try parseCCastExpr(c, m, scope));
65176540
return Tag.not.create(c.arena, operand);
@@ -6534,6 +6557,9 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
65346557
return Tag.address_of.create(c.arena, operand);
65356558
},
65366559
.keyword_sizeof => {
6560+
// 'sizeof' could be used as a parameter to a macro function.
6561+
if (m.checkFnParam(m.slice())) break :sw;
6562+
65376563
const operand = if (m.peek().? == .l_paren) blk: {
65386564
_ = m.next();
65396565
const inner = (try parseCTypeName(c, m, scope, false)).?;
@@ -6544,6 +6570,9 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
65446570
return Tag.helpers_sizeof.create(c.arena, operand);
65456571
},
65466572
.keyword_alignof => {
6573+
// 'alignof' could be used as a parameter to a macro function.
6574+
if (m.checkFnParam(m.slice())) break :sw;
6575+
65476576
// TODO this won't work if using <stdalign.h>'s
65486577
// #define alignof _Alignof
65496578
try m.skip(c, .l_paren);
@@ -6556,11 +6585,11 @@ fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
65566585
try m.fail(c, "TODO unary inc/dec expr", .{});
65576586
return error.ParseError;
65586587
},
6559-
else => {
6560-
m.i -= 1;
6561-
return try parseCPostfixExpr(c, m, scope, null);
6562-
},
6588+
else => {},
65636589
}
6590+
6591+
m.i -= 1;
6592+
return try parseCPostfixExpr(c, m, scope, null);
65646593
}
65656594

65666595
fn getContainer(c: *Context, node: Node) ?Node {
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#define GUARDED_INT_ADDITION(int) ((int) + 1)
2+
3+
#define UNGUARDED_INT_SUBTRACTION(int) (int - 2)
4+
5+
#define GUARDED_INT_MULTIPLY(int) ((int) * 3)
6+
7+
#define UNGUARDED_INT_DIVIDE(int) (int / 4)
8+
9+
#define WRAPPED_RETURN(return) ((return) % 2)
10+
11+
#define UNWRAPPED_RETURN(return) (return ^ 0x7F)
12+
13+
#define WITH_TWO_PARAMETERS(signed, x) ((signed) + (x) + 9)
14+
15+
#define GUARDED_ALIGNOF(_Alignof) ((_Alignof) & 0x55)
16+
17+
#define UNGUARDED_ALIGNOF(_Alignof) (_Alignof | 0x80)
18+
19+
#define GUARDED_SIZEOF(sizeof) ((sizeof) == 64)
20+
21+
#define UNGUARDED_SIZEOF(sizeof) (sizeof < 64)
22+
23+
#define SIZEOF(x) ((int)sizeof(x))
24+
25+
#define SIZEOF2(x) ((int)sizeof x)
26+
27+
// translate-c
28+
// c_frontend=clang
29+
//
30+
// pub inline fn GUARDED_INT_ADDITION(int: anytype) @TypeOf(int + @as(c_int, 1)) {
31+
// _ = &int;
32+
// return int + @as(c_int, 1);
33+
// }
34+
// pub inline fn UNGUARDED_INT_SUBTRACTION(int: anytype) @TypeOf(int - @as(c_int, 2)) {
35+
// _ = &int;
36+
// return int - @as(c_int, 2);
37+
// }
38+
// pub inline fn GUARDED_INT_MULTIPLY(int: anytype) @TypeOf(int * @as(c_int, 3)) {
39+
// _ = &int;
40+
// return int * @as(c_int, 3);
41+
// }
42+
// pub inline fn UNGUARDED_INT_DIVIDE(int: anytype) @TypeOf(@import("std").zig.c_translation.MacroArithmetic.div(int, @as(c_int, 4))) {
43+
// _ = &int;
44+
// return @import("std").zig.c_translation.MacroArithmetic.div(int, @as(c_int, 4));
45+
// }
46+
// pub inline fn WRAPPED_RETURN(@"return": anytype) @TypeOf(@import("std").zig.c_translation.MacroArithmetic.rem(@"return", @as(c_int, 2))) {
47+
// _ = &@"return";
48+
// return @import("std").zig.c_translation.MacroArithmetic.rem(@"return", @as(c_int, 2));
49+
// }
50+
// pub inline fn UNWRAPPED_RETURN(@"return": anytype) @TypeOf(@"return" ^ @as(c_int, 0x7F)) {
51+
// _ = &@"return";
52+
// return @"return" ^ @as(c_int, 0x7F);
53+
// }
54+
// pub inline fn WITH_TWO_PARAMETERS(signed: anytype, x: anytype) @TypeOf((signed + x) + @as(c_int, 9)) {
55+
// _ = &signed;
56+
// _ = &x;
57+
// return (signed + x) + @as(c_int, 9);
58+
// }
59+
// pub inline fn GUARDED_ALIGNOF(_Alignof: anytype) @TypeOf(_Alignof & @as(c_int, 0x55)) {
60+
// _ = &_Alignof;
61+
// return _Alignof & @as(c_int, 0x55);
62+
// }
63+
// pub inline fn UNGUARDED_ALIGNOF(_Alignof: anytype) @TypeOf(_Alignof | @as(c_int, 0x80)) {
64+
// _ = &_Alignof;
65+
// return _Alignof | @as(c_int, 0x80);
66+
// }
67+
// pub inline fn GUARDED_SIZEOF(sizeof: anytype) @TypeOf(sizeof == @as(c_int, 64)) {
68+
// _ = &sizeof;
69+
// return sizeof == @as(c_int, 64);
70+
// }
71+
// pub inline fn UNGUARDED_SIZEOF(sizeof: anytype) @TypeOf(sizeof < @as(c_int, 64)) {
72+
// _ = &sizeof;
73+
// return sizeof < @as(c_int, 64);
74+
// }
75+
// pub inline fn SIZEOF(x: anytype) c_int {
76+
// _ = &x;
77+
// return @import("std").zig.c_translation.cast(c_int, @import("std").zig.c_translation.sizeof(x));
78+
// }
79+
// pub inline fn SIZEOF2(x: anytype) c_int {
80+
// _ = &x;
81+
// return @import("std").zig.c_translation.cast(c_int, @import("std").zig.c_translation.sizeof(x));
82+
// }

0 commit comments

Comments
 (0)