| author | |
| committer | |
| log | 20563d84577203891637477b2e475a9b279152ac |
| tree | 90bf82b109f4d7fb86153bdc0d9aaaa944b26309 |
| parent | 88bb0fd288acb6a20abed57cdf459cc4fc788b89 |
- Added test/cases/run_translated_c/extern_typedef_variables_in_functions.c to test for issue 196874 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"; |
| 1279 | 1279 | ||
| 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 | } |
| 1358 | 1364 | ||
| 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 | } |
| 1453 | 1477 | ||
| 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; |
| 1274 | 1306 | ||
| ... | @@ -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 |
| 2292 | fn addSemicolonIfNeeded(c: *Context, node: Node) !void { | 2324 | fn 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 | }; |
| 1736 | 1736 | ||
| 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 | ||
| 1744 | fn 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 | |||
| 1737 | fn transDeclStmtOne( | 1779 | fn 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(); |
| 1748 | 1797 | ||
| ... | @@ -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); |
| 1752 | 1801 | ||
| 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 | } |
| 1760 | 1805 | ||
| ... | @@ -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 | else | 1901 | |
| 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 | }; | ||
| 1858 | 1917 | ||
| 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 @@ | ||
| 1 | const int ev = 40; | ||
| 2 | |||
| 3 | static int func(void) | ||
| 4 | { | ||
| 5 | typedef int test_type_t; | ||
| 6 | extern const test_type_t ev; | ||
| 7 | return ev + 2; | ||
| 8 | } | ||
| 9 | |||
| 10 | int main() | ||
| 11 | { | ||
| 12 | if (func() != 42) | ||
| 13 | return 1; | ||
| 14 | return 0; | ||
| 15 | } | ||
| 16 | |||
| 17 | // run-translated-c | ||
| 18 | // c_frontend=clang | ||