authorgravatar for flyfish30@users.noreply.github.comParker Liu <flyfish30@users.noreply.github.com> 2025-04-03 04:07:41+08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-04-02 20:07:41+00:00
logde62dc884edb1205e1aac799e0a3b4273be999a1
treea422d9b06ad72ce6d13cca7015fe4091b707faac
parent9dfdf3503242428f93c56c8f211834bd6b87ac76
signaturebadge-check Signed by PGP key B5690EEEBB952194

translate-c: fix function prototype decalared inside a function

* If a function prototype is declarated inside a function, do not translate it to a top-level extern function declaration. Similar to extern local variable, just wrapped it into a block-local struct. * Add a new extern_local_fn tag of aro_translate_c node for present extern local function declaration. * When a function body has a C function prototype declaration, it adds an extern local function declaration. Subsequent function references will look for this function declaration.

4 files changed, 78 insertions(+), 20 deletions(-)

lib/compiler/aro_translate_c.zig+17-7
......@@ -1502,19 +1502,29 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
15021502 return scope.base.parent.?.getAlias(name);
15031503 }
15041504
1505 /// Finds the (potentially) mangled struct name for a locally scoped extern variable given the original declaration name.
1505 /// Finds the (potentially) mangled struct name for a locally scoped extern variable or function given the original declaration name.
15061506 ///
15071507 /// Block scoped extern declarations translate to:
15081508 /// const MangledStructName = struct {extern [qualifiers] original_extern_variable_name: [type]};
15091509 /// This finds MangledStructName given original_extern_variable_name for referencing correctly in transDeclRefExpr()
15101510 pub fn getLocalExternAlias(scope: *Block, name: []const u8) ?[]const u8 {
15111511 for (scope.statements.items) |node| {
1512 if (node.tag() == .extern_local_var) {
1513 const parent_node = node.castTag(.extern_local_var).?;
1514 const init_node = parent_node.data.init.castTag(.var_decl).?;
1515 if (std.mem.eql(u8, init_node.data.name, name)) {
1516 return parent_node.data.name;
1517 }
1512 switch (node.tag()) {
1513 .extern_local_var => {
1514 const parent_node = node.castTag(.extern_local_var).?;
1515 const init_node = parent_node.data.init.castTag(.var_decl).?;
1516 if (std.mem.eql(u8, init_node.data.name, name)) {
1517 return parent_node.data.name;
1518 }
1519 },
1520 .extern_local_fn => {
1521 const parent_node = node.castTag(.extern_local_fn).?;
1522 const init_node = parent_node.data.init.castTag(.func).?;
1523 if (std.mem.eql(u8, init_node.data.name.?, name)) {
1524 return parent_node.data.name;
1525 }
1526 },
1527 else => {},
15181528 }
15191529 }
15201530 return null;
lib/compiler/aro_translate_c/ast.zig+16-4
......@@ -57,6 +57,8 @@ pub const Node = extern union {
5757 static_local_var,
5858 /// const ExternLocal_name = struct { init }
5959 extern_local_var,
60 /// const ExternLocal_name = struct { init }
61 extern_local_fn,
6062 /// var name = init.*
6163 mut_str,
6264 func,
......@@ -367,7 +369,13 @@ pub const Node = extern union {
367369 .c_pointer, .single_pointer => Payload.Pointer,
368370 .array_type, .null_sentinel_array_type => Payload.Array,
369371 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
370 .var_simple, .pub_var_simple, .static_local_var, .extern_local_var, .mut_str => Payload.SimpleVarDecl,
372 .var_simple,
373 .pub_var_simple,
374 .static_local_var,
375 .extern_local_var,
376 .extern_local_fn,
377 .mut_str,
378 => Payload.SimpleVarDecl,
371379 .enum_constant => Payload.EnumConstant,
372380 .array_filler => Payload.ArrayFiller,
373381 .pub_inline_fn => Payload.PubInlineFn,
......@@ -1265,8 +1273,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
12651273 } },
12661274 });
12671275 },
1268 .extern_local_var => {
1269 const payload = node.castTag(.extern_local_var).?.data;
1276 .extern_local_var, .extern_local_fn => {
1277 const payload = if (node.tag() == .extern_local_var)
1278 node.castTag(.extern_local_var).?.data
1279 else
1280 node.castTag(.extern_local_fn).?.data;
12701281
12711282 const const_tok = try c.addToken(.keyword_const, "const");
12721283 _ = try c.addIdentifier(payload.name);
......@@ -2293,7 +2304,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn
22932304fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
22942305 switch (node.tag()) {
22952306 .warning => unreachable,
2296 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .extern_local_var, .mut_str => {},
2307 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .extern_local_var, .extern_local_fn, .mut_str => {},
22972308 .while_true => {
22982309 const payload = node.castTag(.while_true).?.data;
22992310 return addSemicolonIfNotBlock(c, payload);
......@@ -2390,6 +2401,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
23902401 .builtin_extern,
23912402 .static_local_var,
23922403 .extern_local_var,
2404 .extern_local_fn,
23932405 .mut_str,
23942406 .macro_arithmetic,
23952407 => {
src/translate_c.zig+40-7
......@@ -325,7 +325,7 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
325325fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
326326 switch (decl.getKind()) {
327327 .Function => {
328 return visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl)));
328 return transFnDecl(c, &c.global_scope.base, @as(*const clang.FunctionDecl, @ptrCast(decl)));
329329 },
330330 .Typedef => {
331331 try transTypeDef(c, &c.global_scope.base, @as(*const clang.TypedefNameDecl, @ptrCast(decl)));
......@@ -367,7 +367,7 @@ fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.Fi
367367 try scope.appendNode(comptime_node);
368368}
369369
370fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
370fn transFnDecl(c: *Context, scope: *Scope, fn_decl: *const clang.FunctionDecl) Error!void {
371371 const fn_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(fn_decl)).getName_bytes_begin());
372372 if (c.global_scope.sym_table.contains(fn_name))
373373 return; // Avoid processing this decl twice
......@@ -375,7 +375,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
375375 // Skip this declaration if a proper definition exists
376376 if (!fn_decl.isThisDeclarationADefinition()) {
377377 if (fn_decl.getDefinition()) |def|
378 return visitFnDecl(c, def);
378 return transFnDecl(c, scope, def);
379379 }
380380
381381 const fn_decl_loc = fn_decl.getLocation();
......@@ -446,6 +446,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
446446 };
447447
448448 if (!decl_ctx.has_body) {
449 if (scope.id != .root) {
450 return addLocalExternFnDecl(c, scope, fn_name, Node.initPayload(&proto_node.base));
451 }
449452 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
450453 }
451454
......@@ -455,7 +458,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
455458 block_scope.return_type = return_qt;
456459 defer block_scope.deinit();
457460
458 const scope = &block_scope.base;
461 const top_scope = &block_scope.base;
459462
460463 var param_id: c_uint = 0;
461464 for (proto_node.data.params) |*param| {
......@@ -508,7 +511,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
508511 break :blk;
509512 }
510513
511 const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
514 const rhs = transZeroInitExpr(c, top_scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
512515 error.OutOfMemory => |e| return e,
513516 error.UnsupportedTranslation,
514517 error.UnsupportedType,
......@@ -1874,7 +1877,7 @@ fn transDeclStmtOne(
18741877 try transEnumDecl(c, scope, @as(*const clang.EnumDecl, @ptrCast(decl)));
18751878 },
18761879 .Function => {
1877 try visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl)));
1880 try transFnDecl(c, scope, @as(*const clang.FunctionDecl, @ptrCast(decl)));
18781881 },
18791882 else => {
18801883 const decl_name = try c.str(decl.getDeclKindName());
......@@ -1903,11 +1906,19 @@ fn transDeclRefExpr(
19031906 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin());
19041907 const mangled_name = scope.getAlias(name);
19051908 const decl_is_var = @as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var;
1906 const potential_local_extern = if (decl_is_var) ((@as(*const clang.VarDecl, @ptrCast(value_decl)).getStorageClass() == .Extern) and (scope.id != .root)) else false;
1909 const storage_class = @as(*const clang.VarDecl, @ptrCast(value_decl)).getStorageClass();
1910 const potential_local_extern = if (decl_is_var) ((storage_class == .Extern) and (scope.id != .root)) else false;
19071911
19081912 var confirmed_local_extern = false;
1913 var confirmed_local_extern_fn = false;
19091914 var ref_expr = val: {
19101915 if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr)))) {
1916 if (scope.id != .root) {
1917 if (scope.getLocalExternAlias(name)) |v| {
1918 confirmed_local_extern_fn = true;
1919 break :val try Tag.identifier.create(c.arena, v);
1920 }
1921 }
19111922 break :val try Tag.fn_identifier.create(c.arena, mangled_name);
19121923 } else if (potential_local_extern) {
19131924 if (scope.getLocalExternAlias(name)) |v| {
......@@ -1934,6 +1945,11 @@ fn transDeclRefExpr(
19341945 .field_name = name, // by necessity, name will always == mangled_name
19351946 });
19361947 }
1948 } else if (confirmed_local_extern_fn) {
1949 ref_expr = try Tag.field_access.create(c.arena, .{
1950 .lhs = ref_expr,
1951 .field_name = name, // by necessity, name will always == mangled_name
1952 });
19371953 }
19381954 scope.skipVariableDiscard(mangled_name);
19391955 return ref_expr;
......@@ -4213,6 +4229,23 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
42134229 }
42144230}
42154231
4232/// Add an "extern" function prototype declaration that's been declared within a scoped block.
4233/// Similar to static local variables, this will be wrapped in a struct to work with Zig's syntax requirements.
4234///
4235fn addLocalExternFnDecl(c: *Context, scope: *Scope, name: []const u8, decl_node: Node) !void {
4236 const bs: *Scope.Block = try scope.findBlockScope(c);
4237
4238 // Special naming convention for local extern function wrapper struct,
4239 // this named "ExternLocal_[name]".
4240 const struct_name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ Scope.Block.extern_inner_prepend, name });
4241
4242 // Outer Node for the wrapper struct
4243 const node = try Tag.extern_local_fn.create(c.arena, .{ .name = struct_name, .init = decl_node });
4244
4245 try bs.statements.append(node);
4246 try bs.discardVariable(c, struct_name);
4247}
4248
42164249fn transQualTypeInitializedStringLiteral(c: *Context, elem_ty: Node, string_lit: *const clang.StringLiteral) TypeError!Node {
42174250 const string_lit_size = string_lit.getLength();
42184251 const array_size = @as(usize, @intCast(string_lit_size));
test/translate_c.zig+5-2
......@@ -3537,9 +3537,12 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
35373537 \\ return bar(1, 2);
35383538 \\}
35393539 , &[_][]const u8{
3540 \\pub extern fn bar(c_int, c_int) c_int;
35413540 \\pub export fn foo() c_int {
3542 \\ return bar(@as(c_int, 1), @as(c_int, 2));
3541 \\ const ExternLocal_bar = struct {
3542 \\ pub extern fn bar(c_int, c_int) c_int;
3543 \\ };
3544 \\ _ = &ExternLocal_bar;
3545 \\ return ExternLocal_bar.bar(@as(c_int, 1), @as(c_int, 2));
35433546 \\}
35443547 });
35453548