| author | |
| committer | |
| log | 06fc600aec227f00d1edbb0b1577b26e490c7d8c |
| tree | 01e983ffab2f600d24292c9ea60612fdac770fcd |
| parent | 0b4176891c33c990e9d40523a8eaceea6d172c8e |
| signature | Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM |
* 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 |
| 1502 | 1502 | return scope.base.parent.?.getAlias(name); |
| 1503 | 1503 | } |
| 1504 | 1504 | |
| 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. | |
| 1506 | 1506 | /// |
| 1507 | 1507 | /// Block scoped extern declarations translate to: |
| 1508 | 1508 | /// const MangledStructName = struct {extern [qualifiers] original_extern_variable_name: [type]}; |
| 1509 | 1509 | /// This finds MangledStructName given original_extern_variable_name for referencing correctly in transDeclRefExpr() |
| 1510 | 1510 | pub fn getLocalExternAlias(scope: *Block, name: []const u8) ?[]const u8 { |
| 1511 | 1511 | 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 => {}, | |
| 1518 | 1528 | } |
| 1519 | 1529 | } |
| 1520 | 1530 | return null; |
lib/compiler/aro_translate_c/ast.zig+16-4| ... | ... | @@ -57,6 +57,8 @@ pub const Node = extern union { |
| 57 | 57 | static_local_var, |
| 58 | 58 | /// const ExternLocal_name = struct { init } |
| 59 | 59 | extern_local_var, |
| 60 | /// const ExternLocal_name = struct { init } | |
| 61 | extern_local_fn, | |
| 60 | 62 | /// var name = init.* |
| 61 | 63 | mut_str, |
| 62 | 64 | func, |
| ... | ... | @@ -367,7 +369,13 @@ pub const Node = extern union { |
| 367 | 369 | .c_pointer, .single_pointer => Payload.Pointer, |
| 368 | 370 | .array_type, .null_sentinel_array_type => Payload.Array, |
| 369 | 371 | .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, | |
| 371 | 379 | .enum_constant => Payload.EnumConstant, |
| 372 | 380 | .array_filler => Payload.ArrayFiller, |
| 373 | 381 | .pub_inline_fn => Payload.PubInlineFn, |
| ... | ... | @@ -1285,8 +1293,11 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1285 | 1293 | }, |
| 1286 | 1294 | }); |
| 1287 | 1295 | }, |
| 1288 | .extern_local_var => { | |
| 1289 | const payload = node.castTag(.extern_local_var).?.data; | |
| 1296 | .extern_local_var, .extern_local_fn => { | |
| 1297 | const payload = if (node.tag() == .extern_local_var) | |
| 1298 | node.castTag(.extern_local_var).?.data | |
| 1299 | else | |
| 1300 | node.castTag(.extern_local_fn).?.data; | |
| 1290 | 1301 | |
| 1291 | 1302 | const const_tok = try c.addToken(.keyword_const, "const"); |
| 1292 | 1303 | _ = try c.addIdentifier(payload.name); |
| ... | ... | @@ -2338,7 +2349,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn |
| 2338 | 2349 | fn addSemicolonIfNeeded(c: *Context, node: Node) !void { |
| 2339 | 2350 | switch (node.tag()) { |
| 2340 | 2351 | .warning => unreachable, |
| 2341 | .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .extern_local_var, .mut_str => {}, | |
| 2352 | .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .extern_local_var, .extern_local_fn, .mut_str => {}, | |
| 2342 | 2353 | .while_true => { |
| 2343 | 2354 | const payload = node.castTag(.while_true).?.data; |
| 2344 | 2355 | return addSemicolonIfNotBlock(c, payload); |
| ... | ... | @@ -2435,6 +2446,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2435 | 2446 | .builtin_extern, |
| 2436 | 2447 | .static_local_var, |
| 2437 | 2448 | .extern_local_var, |
| 2449 | .extern_local_fn, | |
| 2438 | 2450 | .mut_str, |
| 2439 | 2451 | .macro_arithmetic, |
| 2440 | 2452 | => { |
src/translate_c.zig+40-7| ... | ... | @@ -325,7 +325,7 @@ fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void { |
| 325 | 325 | fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void { |
| 326 | 326 | switch (decl.getKind()) { |
| 327 | 327 | .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))); | |
| 329 | 329 | }, |
| 330 | 330 | .Typedef => { |
| 331 | 331 | 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 |
| 367 | 367 | try scope.appendNode(comptime_node); |
| 368 | 368 | } |
| 369 | 369 | |
| 370 | fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { | |
| 370 | fn transFnDecl(c: *Context, scope: *Scope, fn_decl: *const clang.FunctionDecl) Error!void { | |
| 371 | 371 | const fn_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(fn_decl)).getName_bytes_begin()); |
| 372 | 372 | if (c.global_scope.sym_table.contains(fn_name)) |
| 373 | 373 | return; // Avoid processing this decl twice |
| ... | ... | @@ -375,7 +375,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 375 | 375 | // Skip this declaration if a proper definition exists |
| 376 | 376 | if (!fn_decl.isThisDeclarationADefinition()) { |
| 377 | 377 | if (fn_decl.getDefinition()) |def| |
| 378 | return visitFnDecl(c, def); | |
| 378 | return transFnDecl(c, scope, def); | |
| 379 | 379 | } |
| 380 | 380 | |
| 381 | 381 | const fn_decl_loc = fn_decl.getLocation(); |
| ... | ... | @@ -446,6 +446,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 446 | 446 | }; |
| 447 | 447 | |
| 448 | 448 | if (!decl_ctx.has_body) { |
| 449 | if (scope.id != .root) { | |
| 450 | return addLocalExternFnDecl(c, scope, fn_name, Node.initPayload(&proto_node.base)); | |
| 451 | } | |
| 449 | 452 | return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base)); |
| 450 | 453 | } |
| 451 | 454 | |
| ... | ... | @@ -455,7 +458,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 455 | 458 | block_scope.return_type = return_qt; |
| 456 | 459 | defer block_scope.deinit(); |
| 457 | 460 | |
| 458 | const scope = &block_scope.base; | |
| 461 | const top_scope = &block_scope.base; | |
| 459 | 462 | |
| 460 | 463 | var param_id: c_uint = 0; |
| 461 | 464 | for (proto_node.data.params) |*param| { |
| ... | ... | @@ -508,7 +511,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { |
| 508 | 511 | break :blk; |
| 509 | 512 | } |
| 510 | 513 | |
| 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) { | |
| 512 | 515 | error.OutOfMemory => |e| return e, |
| 513 | 516 | error.UnsupportedTranslation, |
| 514 | 517 | error.UnsupportedType, |
| ... | ... | @@ -1874,7 +1877,7 @@ fn transDeclStmtOne( |
| 1874 | 1877 | try transEnumDecl(c, scope, @as(*const clang.EnumDecl, @ptrCast(decl))); |
| 1875 | 1878 | }, |
| 1876 | 1879 | .Function => { |
| 1877 | try visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl))); | |
| 1880 | try transFnDecl(c, scope, @as(*const clang.FunctionDecl, @ptrCast(decl))); | |
| 1878 | 1881 | }, |
| 1879 | 1882 | else => { |
| 1880 | 1883 | const decl_name = try c.str(decl.getDeclKindName()); |
| ... | ... | @@ -1903,11 +1906,19 @@ fn transDeclRefExpr( |
| 1903 | 1906 | const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin()); |
| 1904 | 1907 | const mangled_name = scope.getAlias(name); |
| 1905 | 1908 | 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; | |
| 1907 | 1911 | |
| 1908 | 1912 | var confirmed_local_extern = false; |
| 1913 | var confirmed_local_extern_fn = false; | |
| 1909 | 1914 | var ref_expr = val: { |
| 1910 | 1915 | 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 | } | |
| 1911 | 1922 | break :val try Tag.fn_identifier.create(c.arena, mangled_name); |
| 1912 | 1923 | } else if (potential_local_extern) { |
| 1913 | 1924 | if (scope.getLocalExternAlias(name)) |v| { |
| ... | ... | @@ -1934,6 +1945,11 @@ fn transDeclRefExpr( |
| 1934 | 1945 | .field_name = name, // by necessity, name will always == mangled_name |
| 1935 | 1946 | }); |
| 1936 | 1947 | } |
| 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 | }); | |
| 1937 | 1953 | } |
| 1938 | 1954 | scope.skipVariableDiscard(mangled_name); |
| 1939 | 1955 | return ref_expr; |
| ... | ... | @@ -4213,6 +4229,23 @@ fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void { |
| 4213 | 4229 | } |
| 4214 | 4230 | } |
| 4215 | 4231 | |
| 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 | /// | |
| 4235 | fn 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 | ||
| 4216 | 4249 | fn transQualTypeInitializedStringLiteral(c: *Context, elem_ty: Node, string_lit: *const clang.StringLiteral) TypeError!Node { |
| 4217 | 4250 | const string_lit_size = string_lit.getLength(); |
| 4218 | 4251 | 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 { |
| 3537 | 3537 | \\ return bar(1, 2); |
| 3538 | 3538 | \\} |
| 3539 | 3539 | , &[_][]const u8{ |
| 3540 | \\pub extern fn bar(c_int, c_int) c_int; | |
| 3541 | 3540 | \\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)); | |
| 3543 | 3546 | \\} |
| 3544 | 3547 | }); |
| 3545 | 3548 |