authorgravatar for hayden@terrakaffe.comHayden Riddiford <hayden@terrakaffe.com> 2024-06-14 09:06:05-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2024-07-16 23:29:44+03:00
log20563d84577203891637477b2e475a9b279152ac
tree90bf82b109f4d7fb86153bdc0d9aaaa944b26309
parent88bb0fd288acb6a20abed57cdf459cc4fc788b89

- Added special handling for translating C extern variables declared within scoped blocks

- Added test/cases/run_translated_c/extern_typedef_variables_in_functions.c to test for issue 19687

4 files changed, 161 insertions(+), 12 deletions(-)

lib/compiler/aro_translate_c.zig+34
......@@ -1277,6 +1277,12 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
12771277 /// struct itself is given this name.
12781278 pub const static_inner_name = "static";
12791279
1280 /// C extern variables declared within a block are wrapped in a block-local
1281 /// struct. The struct is named ExternLocal_[variable_name], the Zig variable
1282 /// within the struct itself is [variable_name] by neccessity since it's an
1283 /// extern reference to an existing symbol.
1284 pub const extern_inner_prepend = "ExternLocal";
1285
12801286 pub fn init(c: *ScopeExtraContext, parent: *ScopeExtraScope, labeled: bool) !Block {
12811287 var blk = Block{
12821288 .base = .{
......@@ -1356,6 +1362,24 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
13561362 return scope.base.parent.?.getAlias(name);
13571363 }
13581364
1365 /// Finds the (potentially) mangled struct name for a locally scoped extern variable given the original declaration name.
1366 ///
1367 /// Block scoped extern declarations translate to:
1368 /// const MangledStructName = struct {extern [qualifiers] original_extern_variable_name: [type]};
1369 /// This finds MangledStructName given original_extern_variable_name for referencing correctly in transDeclRefExpr()
1370 pub fn getLocalExternAlias(scope: *Block, name: []const u8) ?[]const u8 {
1371 for (scope.statements.items) |node| {
1372 if (node.tag() == .extern_local_var) {
1373 const parent_node = node.castTag(.extern_local_var).?;
1374 const init_node = parent_node.data.init.castTag(.var_decl).?;
1375 if (std.mem.eql(u8, init_node.data.name, name)) {
1376 return parent_node.data.name;
1377 }
1378 }
1379 }
1380 return null;
1381 }
1382
13591383 pub fn localContains(scope: *Block, name: []const u8) bool {
13601384 for (scope.variables.items) |p| {
13611385 if (std.mem.eql(u8, p.alias, name))
......@@ -1451,6 +1475,16 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
14511475 };
14521476 }
14531477
1478 pub fn getLocalExternAlias(scope: *ScopeExtraScope, name: []const u8) ?[]const u8 {
1479 return switch (scope.id) {
1480 .block => ret: {
1481 const block = @as(*Block, @fieldParentPtr("base", scope));
1482 break :ret block.getLocalExternAlias(name);
1483 },
1484 .root, .loop, .do_loop, .condition => null,
1485 };
1486 }
1487
14541488 pub fn contains(scope: *ScopeExtraScope, name: []const u8) bool {
14551489 return switch (scope.id) {
14561490 .root => @as(*Root, @fieldParentPtr("base", scope)).contains(name),
lib/compiler/aro_translate_c/ast.zig+35-2
......@@ -55,6 +55,8 @@ pub const Node = extern union {
5555 var_decl,
5656 /// const name = struct { init }
5757 static_local_var,
58 /// const ExternLocal_name = struct { init }
59 extern_local_var,
5860 /// var name = init.*
5961 mut_str,
6062 func,
......@@ -365,7 +367,7 @@ pub const Node = extern union {
365367 .c_pointer, .single_pointer => Payload.Pointer,
366368 .array_type, .null_sentinel_array_type => Payload.Array,
367369 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,
368 .var_simple, .pub_var_simple, .static_local_var, .mut_str => Payload.SimpleVarDecl,
370 .var_simple, .pub_var_simple, .static_local_var, .extern_local_var, .mut_str => Payload.SimpleVarDecl,
369371 .enum_constant => Payload.EnumConstant,
370372 .array_filler => Payload.ArrayFiller,
371373 .pub_inline_fn => Payload.PubInlineFn,
......@@ -1269,6 +1271,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
12691271 },
12701272 });
12711273 },
1274 .extern_local_var => {
1275 const payload = node.castTag(.extern_local_var).?.data;
1276
1277 const const_tok = try c.addToken(.keyword_const, "const");
1278 _ = try c.addIdentifier(payload.name);
1279 _ = try c.addToken(.equal, "=");
1280
1281 const kind_tok = try c.addToken(.keyword_struct, "struct");
1282 _ = try c.addToken(.l_brace, "{");
1283
1284 const container_def = try c.addNode(.{
1285 .tag = .container_decl_two_trailing,
1286 .main_token = kind_tok,
1287 .data = .{
1288 .lhs = try renderNode(c, payload.init),
1289 .rhs = 0,
1290 },
1291 });
1292 _ = try c.addToken(.r_brace, "}");
1293 _ = try c.addToken(.semicolon, ";");
1294
1295 return c.addNode(.{
1296 .tag = .simple_var_decl,
1297 .main_token = const_tok,
1298 .data = .{
1299 .lhs = 0,
1300 .rhs = container_def,
1301 },
1302 });
1303 },
12721304 .mut_str => {
12731305 const payload = node.castTag(.mut_str).?.data;
12741306
......@@ -2292,7 +2324,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn
22922324fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
22932325 switch (node.tag()) {
22942326 .warning => unreachable,
2295 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .mut_str => {},
2327 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .static_local_var, .extern_local_var, .mut_str => {},
22962328 .while_true => {
22972329 const payload = node.castTag(.while_true).?.data;
22982330 return addSemicolonIfNotBlock(c, payload);
......@@ -2388,6 +2420,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
23882420 .shuffle,
23892421 .builtin_extern,
23902422 .static_local_var,
2423 .extern_local_var,
23912424 .mut_str,
23922425 .macro_arithmetic,
23932426 => {
src/translate_c.zig+74-10
......@@ -1734,6 +1734,48 @@ const ClangAlignment = struct {
17341734 }
17351735};
17361736
1737/// Translate an "extern" variable that's been declared within a scoped block.
1738/// Similar to static local variables, this will be wrapped in a struct to work with Zig's syntax requirements.
1739///
1740/// Assumptions made:
1741/// - No need to mangle the actual NamedDecl, as by definition this MUST be the same name as the external symbol it's referencing
1742/// - It's not valid C to have an initializer with this type of declaration, so we can safely operate assuming no initializer
1743/// - No need to look for any cleanup attributes with getCleanupAttribute(), not relevant for this type of decl
1744fn transLocalExternStmt(c: *Context, scope: *Scope, var_decl: *const clang.VarDecl, block_scope: *Scope.Block) TransError!void {
1745 const extern_var_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
1746
1747 // Special naming convention for local extern variable wrapper struct
1748 const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ Scope.Block.extern_inner_prepend, extern_var_name });
1749
1750 // On the off chance there's already a variable in scope named "ExternLocal_[extern_var_name]"
1751 const mangled_name = try block_scope.makeMangledName(c, name);
1752
1753 const qual_type = var_decl.getTypeSourceInfo_getType();
1754 const is_const = qual_type.isConstQualified();
1755 const loc = var_decl.getLocation();
1756 const type_node = try transQualType(c, scope, qual_type, loc);
1757
1758 // Inner Node for the extern variable declaration
1759 var node = try Tag.var_decl.create(c.arena, .{
1760 .is_pub = false,
1761 .is_const = is_const,
1762 .is_extern = true,
1763 .is_export = false,
1764 .is_threadlocal = var_decl.getTLSKind() != .None, // TODO: Neccessary?
1765 .linksection_string = null, // TODO: Neccessary?
1766 .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(),
1767 .name = extern_var_name,
1768 .type = type_node,
1769 .init = null,
1770 });
1771
1772 // Outer Node for the wrapper struct
1773 node = try Tag.extern_local_var.create(c.arena, .{ .name = mangled_name, .init = node });
1774
1775 try block_scope.statements.append(node);
1776 try block_scope.discardVariable(c, mangled_name);
1777}
1778
17371779fn transDeclStmtOne(
17381780 c: *Context,
17391781 scope: *Scope,
......@@ -1743,6 +1785,13 @@ fn transDeclStmtOne(
17431785 switch (decl.getKind()) {
17441786 .Var => {
17451787 const var_decl = @as(*const clang.VarDecl, @ptrCast(decl));
1788
1789 // Translation behavior for a block scope declared "extern" variable
1790 // is enough of an outlier that it needs it's own function
1791 if (var_decl.getStorageClass() == .Extern) {
1792 return transLocalExternStmt(c, scope, var_decl, block_scope);
1793 }
1794
17461795 const decl_init = var_decl.getInit();
17471796 const loc = decl.getLocation();
17481797
......@@ -1750,11 +1799,7 @@ fn transDeclStmtOne(
17501799 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
17511800 const mangled_name = try block_scope.makeMangledName(c, name);
17521801
1753 if (var_decl.getStorageClass() == .Extern) {
1754 // This is actually a global variable, put it in the global scope and reference it.
1755 // `_ = mangled_name;`
1756 return visitVarDecl(c, var_decl, mangled_name);
1757 } else if (qualTypeWasDemotedToOpaque(c, qual_type)) {
1802 if (qualTypeWasDemotedToOpaque(c, qual_type)) {
17581803 return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{});
17591804 }
17601805
......@@ -1851,18 +1896,37 @@ fn transDeclRefExpr(
18511896 const value_decl = expr.getDecl();
18521897 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin());
18531898 const mangled_name = scope.getAlias(name);
1854 var ref_expr = if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr))))
1855 try Tag.fn_identifier.create(c.arena, mangled_name)
1856 else
1857 try Tag.identifier.create(c.arena, mangled_name);
1899 const decl_is_var = @as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var;
1900 const potential_local_extern = if (decl_is_var) ((@as(*const clang.VarDecl, @ptrCast(value_decl)).getStorageClass() == .Extern) and (scope.id == .block)) else false;
1901
1902 var confirmed_local_extern = false;
1903 var ref_expr = val: {
1904 if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr)))) {
1905 break :val try Tag.fn_identifier.create(c.arena, mangled_name);
1906 } else if (potential_local_extern) {
1907 if (scope.getLocalExternAlias(name)) |v| {
1908 confirmed_local_extern = true;
1909 break :val try Tag.identifier.create(c.arena, v);
1910 } else {
1911 break :val try Tag.identifier.create(c.arena, mangled_name);
1912 }
1913 } else {
1914 break :val try Tag.identifier.create(c.arena, mangled_name);
1915 }
1916 };
18581917
1859 if (@as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var) {
1918 if (decl_is_var) {
18601919 const var_decl = @as(*const clang.VarDecl, @ptrCast(value_decl));
18611920 if (var_decl.isStaticLocal()) {
18621921 ref_expr = try Tag.field_access.create(c.arena, .{
18631922 .lhs = ref_expr,
18641923 .field_name = Scope.Block.static_inner_name,
18651924 });
1925 } else if (confirmed_local_extern) {
1926 ref_expr = try Tag.field_access.create(c.arena, .{
1927 .lhs = ref_expr,
1928 .field_name = name, // by necessity, name will always == mangled_name
1929 });
18661930 }
18671931 }
18681932 scope.skipVariableDiscard(mangled_name);
test/cases/run_translated_c/extern_typedef_variables_in_functions.c created+18
......@@ -0,0 +1,18 @@
1const int ev = 40;
2
3static int func(void)
4{
5 typedef int test_type_t;
6 extern const test_type_t ev;
7 return ev + 2;
8}
9
10int main()
11{
12 if (func() != 42)
13 return 1;
14 return 0;
15}
16
17// run-translated-c
18// c_frontend=clang