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...@@ -1277,6 +1277,12 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
1277 /// struct itself is given this name.1277 /// struct itself is given this name.
1278 pub const static_inner_name = "static";1278 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
1280 pub fn init(c: *ScopeExtraContext, parent: *ScopeExtraScope, labeled: bool) !Block {1286 pub fn init(c: *ScopeExtraContext, parent: *ScopeExtraScope, labeled: bool) !Block {
1281 var blk = Block{1287 var blk = Block{
1282 .base = .{1288 .base = .{
...@@ -1356,6 +1362,24 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ...@@ -1356,6 +1362,24 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
1356 return scope.base.parent.?.getAlias(name);1362 return scope.base.parent.?.getAlias(name);
1357 }1363 }
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
1359 pub fn localContains(scope: *Block, name: []const u8) bool {1383 pub fn localContains(scope: *Block, name: []const u8) bool {
1360 for (scope.variables.items) |p| {1384 for (scope.variables.items) |p| {
1361 if (std.mem.eql(u8, p.alias, name))1385 if (std.mem.eql(u8, p.alias, name))
...@@ -1451,6 +1475,16 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ...@@ -1451,6 +1475,16 @@ pub fn ScopeExtra(comptime ScopeExtraContext: type, comptime ScopeExtraType: typ
1451 };1475 };
1452 }1476 }
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
1454 pub fn contains(scope: *ScopeExtraScope, name: []const u8) bool {1488 pub fn contains(scope: *ScopeExtraScope, name: []const u8) bool {
1455 return switch (scope.id) {1489 return switch (scope.id) {
1456 .root => @as(*Root, @fieldParentPtr("base", scope)).contains(name),1490 .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 {...@@ -55,6 +55,8 @@ pub const Node = extern union {
55 var_decl,55 var_decl,
56 /// const name = struct { init }56 /// const name = struct { init }
57 static_local_var,57 static_local_var,
58 /// const ExternLocal_name = struct { init }
59 extern_local_var,
58 /// var name = init.*60 /// var name = init.*
59 mut_str,61 mut_str,
60 func,62 func,
...@@ -365,7 +367,7 @@ pub const Node = extern union {...@@ -365,7 +367,7 @@ pub const Node = extern union {
365 .c_pointer, .single_pointer => Payload.Pointer,367 .c_pointer, .single_pointer => Payload.Pointer,
366 .array_type, .null_sentinel_array_type => Payload.Array,368 .array_type, .null_sentinel_array_type => Payload.Array,
367 .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl,369 .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,
369 .enum_constant => Payload.EnumConstant,371 .enum_constant => Payload.EnumConstant,
370 .array_filler => Payload.ArrayFiller,372 .array_filler => Payload.ArrayFiller,
371 .pub_inline_fn => Payload.PubInlineFn,373 .pub_inline_fn => Payload.PubInlineFn,
...@@ -1269,6 +1271,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -1269,6 +1271,36 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
1269 },1271 },
1270 });1272 });
1271 },1273 },
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 },
1272 .mut_str => {1304 .mut_str => {
1273 const payload = node.castTag(.mut_str).?.data;1305 const payload = node.castTag(.mut_str).?.data;
12741306
...@@ -2292,7 +2324,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn...@@ -2292,7 +2324,7 @@ fn renderNullSentinelArrayType(c: *Context, len: usize, elem_type: Node) !NodeIn
2292fn addSemicolonIfNeeded(c: *Context, node: Node) !void {2324fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
2293 switch (node.tag()) {2325 switch (node.tag()) {
2294 .warning => unreachable,2326 .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 => {},
2296 .while_true => {2328 .while_true => {
2297 const payload = node.castTag(.while_true).?.data;2329 const payload = node.castTag(.while_true).?.data;
2298 return addSemicolonIfNotBlock(c, payload);2330 return addSemicolonIfNotBlock(c, payload);
...@@ -2388,6 +2420,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2388,6 +2420,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2388 .shuffle,2420 .shuffle,
2389 .builtin_extern,2421 .builtin_extern,
2390 .static_local_var,2422 .static_local_var,
2423 .extern_local_var,
2391 .mut_str,2424 .mut_str,
2392 .macro_arithmetic,2425 .macro_arithmetic,
2393 => {2426 => {
src/translate_c.zig+74-10
...@@ -1734,6 +1734,48 @@ const ClangAlignment = struct {...@@ -1734,6 +1734,48 @@ const ClangAlignment = struct {
1734 }1734 }
1735};1735};
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
1737fn transDeclStmtOne(1779fn transDeclStmtOne(
1738 c: *Context,1780 c: *Context,
1739 scope: *Scope,1781 scope: *Scope,
...@@ -1743,6 +1785,13 @@ fn transDeclStmtOne(...@@ -1743,6 +1785,13 @@ fn transDeclStmtOne(
1743 switch (decl.getKind()) {1785 switch (decl.getKind()) {
1744 .Var => {1786 .Var => {
1745 const var_decl = @as(*const clang.VarDecl, @ptrCast(decl));1787 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
1746 const decl_init = var_decl.getInit();1795 const decl_init = var_decl.getInit();
1747 const loc = decl.getLocation();1796 const loc = decl.getLocation();
17481797
...@@ -1750,11 +1799,7 @@ fn transDeclStmtOne(...@@ -1750,11 +1799,7 @@ fn transDeclStmtOne(
1750 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());1799 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
1751 const mangled_name = try block_scope.makeMangledName(c, name);1800 const mangled_name = try block_scope.makeMangledName(c, name);
17521801
1753 if (var_decl.getStorageClass() == .Extern) {1802 if (qualTypeWasDemotedToOpaque(c, qual_type)) {
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)) {
1758 return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{});1803 return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{});
1759 }1804 }
17601805
...@@ -1851,18 +1896,37 @@ fn transDeclRefExpr(...@@ -1851,18 +1896,37 @@ fn transDeclRefExpr(
1851 const value_decl = expr.getDecl();1896 const value_decl = expr.getDecl();
1852 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin());1897 const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin());
1853 const mangled_name = scope.getAlias(name);1898 const mangled_name = scope.getAlias(name);
1854 var ref_expr = if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr))))1899 const decl_is_var = @as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var;
1855 try Tag.fn_identifier.create(c.arena, mangled_name)1900 const potential_local_extern = if (decl_is_var) ((@as(*const clang.VarDecl, @ptrCast(value_decl)).getStorageClass() == .Extern) and (scope.id == .block)) else false;
1856 else1901
1857 try Tag.identifier.create(c.arena, mangled_name);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) {
1860 const var_decl = @as(*const clang.VarDecl, @ptrCast(value_decl));1919 const var_decl = @as(*const clang.VarDecl, @ptrCast(value_decl));
1861 if (var_decl.isStaticLocal()) {1920 if (var_decl.isStaticLocal()) {
1862 ref_expr = try Tag.field_access.create(c.arena, .{1921 ref_expr = try Tag.field_access.create(c.arena, .{
1863 .lhs = ref_expr,1922 .lhs = ref_expr,
1864 .field_name = Scope.Block.static_inner_name,1923 .field_name = Scope.Block.static_inner_name,
1865 });1924 });
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 });
1866 }1930 }
1867 }1931 }
1868 scope.skipVariableDiscard(mangled_name);1932 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