| author | |
| committer | |
| log | ea4a25287eb65e639fd75d425f320663e6e8dca1 |
| tree | b207a5d47fc1b3b42bca6ad8aa10b2283779255c |
| parent | 44cdafd9d46bed36f08effb99aa0bb7fc2145efa |
Don't move static local variables into the top-level scope since this
can cause name clashes if subsequently-defined variables or parameters
in different scopes share the name.
Instead, use a variable within a struct so that the variable's lexical
scope does not change. This solution was suggested by @LemonBoy
Note that a similar name-shadowing problem exists with `extern` variables
declared within block scope, but a different solution will be needed since
they do need to be moved to the top-level scope and we can't rename them.7 files changed, 139 insertions(+), 16 deletions(-)
src/clang.zig+3| ... | ... | @@ -1002,6 +1002,9 @@ pub const VarDecl = opaque { |
| 1002 | 1002 | |
| 1003 | 1003 | pub const getTypeSourceInfo_getType = ZigClangVarDecl_getTypeSourceInfo_getType; |
| 1004 | 1004 | extern fn ZigClangVarDecl_getTypeSourceInfo_getType(*const VarDecl) QualType; |
| 1005 | ||
| 1006 | pub const isStaticLocal = ZigClangVarDecl_isStaticLocal; | |
| 1007 | extern fn ZigClangVarDecl_isStaticLocal(*const VarDecl) bool; | |
| 1005 | 1008 | }; |
| 1006 | 1009 | |
| 1007 | 1010 | pub const VectorType = opaque { |
src/translate_c.zig+31-11| ... | ... | @@ -72,6 +72,11 @@ const Scope = struct { |
| 72 | 72 | /// so that the return expression can be cast, if necessary |
| 73 | 73 | return_type: ?clang.QualType = null, |
| 74 | 74 | |
| 75 | /// C static local variables are wrapped in a block-local struct. The struct | |
| 76 | /// is named after the (mangled) variable name, the Zig variable within the | |
| 77 | /// struct itself is given this name. | |
| 78 | const StaticInnerName = "static"; | |
| 79 | ||
| 75 | 80 | fn init(c: *Context, parent: *Scope, labeled: bool) !Block { |
| 76 | 81 | var blk = Block{ |
| 77 | 82 | .base = .{ |
| ... | ... | @@ -1738,15 +1743,13 @@ fn transDeclStmtOne( |
| 1738 | 1743 | const name = try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin()); |
| 1739 | 1744 | const mangled_name = try block_scope.makeMangledName(c, name); |
| 1740 | 1745 | |
| 1741 | switch (var_decl.getStorageClass()) { | |
| 1742 | .Extern, .Static => { | |
| 1743 | // This is actually a global variable, put it in the global scope and reference it. | |
| 1744 | // `_ = mangled_name;` | |
| 1745 | return visitVarDecl(c, var_decl, mangled_name); | |
| 1746 | }, | |
| 1747 | else => {}, | |
| 1746 | if (var_decl.getStorageClass() == .Extern) { | |
| 1747 | // This is actually a global variable, put it in the global scope and reference it. | |
| 1748 | // `_ = mangled_name;` | |
| 1749 | return visitVarDecl(c, var_decl, mangled_name); | |
| 1748 | 1750 | } |
| 1749 | 1751 | |
| 1752 | const is_static_local = var_decl.isStaticLocal(); | |
| 1750 | 1753 | const is_const = qual_type.isConstQualified(); |
| 1751 | 1754 | |
| 1752 | 1755 | const loc = decl.getLocation(); |
| ... | ... | @@ -1757,24 +1760,30 @@ fn transDeclStmtOne( |
| 1757 | 1760 | try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node) |
| 1758 | 1761 | else |
| 1759 | 1762 | try transExprCoercing(c, scope, expr, .used) |
| 1763 | else if (is_static_local) | |
| 1764 | try Tag.std_mem_zeroes.create(c.arena, type_node) | |
| 1760 | 1765 | else |
| 1761 | 1766 | Tag.undefined_literal.init(); |
| 1762 | 1767 | if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) { |
| 1763 | 1768 | init_node = try Tag.bool_to_int.create(c.arena, init_node); |
| 1764 | 1769 | } |
| 1765 | 1770 | |
| 1766 | const node = try Tag.var_decl.create(c.arena, .{ | |
| 1771 | const var_name: []const u8 = if (is_static_local) Scope.Block.StaticInnerName else mangled_name; | |
| 1772 | var node = try Tag.var_decl.create(c.arena, .{ | |
| 1767 | 1773 | .is_pub = false, |
| 1768 | 1774 | .is_const = is_const, |
| 1769 | 1775 | .is_extern = false, |
| 1770 | 1776 | .is_export = false, |
| 1771 | .is_threadlocal = false, | |
| 1777 | .is_threadlocal = var_decl.getTLSKind() != .None, | |
| 1772 | 1778 | .linksection_string = null, |
| 1773 | 1779 | .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)), |
| 1774 | .name = mangled_name, | |
| 1780 | .name = var_name, | |
| 1775 | 1781 | .type = type_node, |
| 1776 | 1782 | .init = init_node, |
| 1777 | 1783 | }); |
| 1784 | if (is_static_local) { | |
| 1785 | node = try Tag.static_local_var.create(c.arena, .{ .name = mangled_name, .init = node }); | |
| 1786 | } | |
| 1778 | 1787 | try block_scope.statements.append(node); |
| 1779 | 1788 | |
| 1780 | 1789 | const cleanup_attr = var_decl.getCleanupAttribute(); |
| ... | ... | @@ -1834,7 +1843,18 @@ fn transDeclRefExpr( |
| 1834 | 1843 | const value_decl = expr.getDecl(); |
| 1835 | 1844 | const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin()); |
| 1836 | 1845 | const mangled_name = scope.getAlias(name); |
| 1837 | return Tag.identifier.create(c.arena, mangled_name); | |
| 1846 | var ref_expr = try Tag.identifier.create(c.arena, mangled_name); | |
| 1847 | ||
| 1848 | if (@ptrCast(*const clang.Decl, value_decl).getKind() == .Var) { | |
| 1849 | const var_decl = @ptrCast(*const clang.VarDecl, value_decl); | |
| 1850 | if (var_decl.isStaticLocal()) { | |
| 1851 | ref_expr = try Tag.field_access.create(c.arena, .{ | |
| 1852 | .lhs = ref_expr, | |
| 1853 | .field_name = Scope.Block.StaticInnerName, | |
| 1854 | }); | |
| 1855 | } | |
| 1856 | } | |
| 1857 | return ref_expr; | |
| 1838 | 1858 | } |
| 1839 | 1859 | |
| 1840 | 1860 | fn transImplicitCastExpr( |
src/translate_c/ast.zig+33-1| ... | ... | @@ -60,6 +60,8 @@ pub const Node = extern union { |
| 60 | 60 | array_access, |
| 61 | 61 | call, |
| 62 | 62 | var_decl, |
| 63 | /// const name = struct { init } | |
| 64 | static_local_var, | |
| 63 | 65 | func, |
| 64 | 66 | warning, |
| 65 | 67 | /// All enums are non-exhaustive |
| ... | ... | @@ -366,7 +368,7 @@ pub const Node = extern union { |
| 366 | 368 | .array_type, .null_sentinel_array_type => Payload.Array, |
| 367 | 369 | .arg_redecl, .alias, .fail_decl => Payload.ArgRedecl, |
| 368 | 370 | .log2_int_type => Payload.Log2IntType, |
| 369 | .var_simple, .pub_var_simple => Payload.SimpleVarDecl, | |
| 371 | .var_simple, .pub_var_simple, .static_local_var => Payload.SimpleVarDecl, | |
| 370 | 372 | .pub_enum_redecl, .enum_redecl => Payload.EnumRedecl, |
| 371 | 373 | .array_filler => Payload.ArrayFiller, |
| 372 | 374 | .pub_inline_fn => Payload.PubInlineFn, |
| ... | ... | @@ -1209,6 +1211,35 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex { |
| 1209 | 1211 | }, |
| 1210 | 1212 | }); |
| 1211 | 1213 | }, |
| 1214 | .static_local_var => { | |
| 1215 | const payload = node.castTag(.static_local_var).?.data; | |
| 1216 | ||
| 1217 | const const_tok = try c.addToken(.keyword_const, "const"); | |
| 1218 | _ = try c.addIdentifier(payload.name); | |
| 1219 | _ = try c.addToken(.equal, "="); | |
| 1220 | ||
| 1221 | const kind_tok = try c.addToken(.keyword_struct, "struct"); | |
| 1222 | _ = try c.addToken(.l_brace, "{"); | |
| 1223 | ||
| 1224 | const container_def = try c.addNode(.{ | |
| 1225 | .tag = .container_decl_two_trailing, | |
| 1226 | .main_token = kind_tok, | |
| 1227 | .data = .{ | |
| 1228 | .lhs = try renderNode(c, payload.init), | |
| 1229 | .rhs = 0, | |
| 1230 | }, | |
| 1231 | }); | |
| 1232 | _ = try c.addToken(.r_brace, "}"); | |
| 1233 | ||
| 1234 | return c.addNode(.{ | |
| 1235 | .tag = .simple_var_decl, | |
| 1236 | .main_token = const_tok, | |
| 1237 | .data = .{ | |
| 1238 | .lhs = 0, | |
| 1239 | .rhs = container_def, | |
| 1240 | }, | |
| 1241 | }); | |
| 1242 | }, | |
| 1212 | 1243 | .var_decl => return renderVar(c, node), |
| 1213 | 1244 | .arg_redecl, .alias => { |
| 1214 | 1245 | const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data; |
| ... | ... | @@ -2276,6 +2307,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex { |
| 2276 | 2307 | .div_exact, |
| 2277 | 2308 | .offset_of, |
| 2278 | 2309 | .shuffle, |
| 2310 | .static_local_var, | |
| 2279 | 2311 | => { |
| 2280 | 2312 | // no grouping needed |
| 2281 | 2313 | return renderNode(c, node); |
src/zig_clang.cpp+5| ... | ... | @@ -2458,6 +2458,11 @@ enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangV |
| 2458 | 2458 | return (ZigClangStorageClass)casted->getStorageClass(); |
| 2459 | 2459 | } |
| 2460 | 2460 | |
| 2461 | bool ZigClangVarDecl_isStaticLocal(const struct ZigClangVarDecl *self) { | |
| 2462 | auto casted = reinterpret_cast<const clang::VarDecl *>(self); | |
| 2463 | return casted->isStaticLocal(); | |
| 2464 | } | |
| 2465 | ||
| 2461 | 2466 | enum ZigClangBuiltinTypeKind ZigClangBuiltinType_getKind(const struct ZigClangBuiltinType *self) { |
| 2462 | 2467 | auto casted = reinterpret_cast<const clang::BuiltinType *>(self); |
| 2463 | 2468 | return (ZigClangBuiltinTypeKind)casted->getKind(); |
src/zig_clang.h+1| ... | ... | @@ -1072,6 +1072,7 @@ ZIG_EXTERN_C bool ZigClangVarDecl_hasInit(const struct ZigClangVarDecl *); |
| 1072 | 1072 | ZIG_EXTERN_C const struct ZigClangAPValue *ZigClangVarDecl_evaluateValue(const struct ZigClangVarDecl *); |
| 1073 | 1073 | ZIG_EXTERN_C struct ZigClangQualType ZigClangVarDecl_getTypeSourceInfo_getType(const struct ZigClangVarDecl *); |
| 1074 | 1074 | ZIG_EXTERN_C enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangVarDecl *self); |
| 1075 | ZIG_EXTERN_C bool ZigClangVarDecl_isStaticLocal(const struct ZigClangVarDecl *self); | |
| 1075 | 1076 | |
| 1076 | 1077 | ZIG_EXTERN_C bool ZigClangSourceLocation_eq(struct ZigClangSourceLocation a, struct ZigClangSourceLocation b); |
| 1077 | 1078 |
test/run_translated_c.zig+38| ... | ... | @@ -1550,4 +1550,42 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1550 | 1550 | \\ if(FORCE_UINT != 0xffffffff) abort(); |
| 1551 | 1551 | \\} |
| 1552 | 1552 | , ""); |
| 1553 | ||
| 1554 | cases.add("block-scope static variable shadows function parameter. Issue #8208", | |
| 1555 | \\#include <stdlib.h> | |
| 1556 | \\int func1(int foo) { return foo + 1; } | |
| 1557 | \\int func2(void) { | |
| 1558 | \\ static int foo = 5; | |
| 1559 | \\ return foo++; | |
| 1560 | \\} | |
| 1561 | \\int main(void) { | |
| 1562 | \\ if (func1(42) != 43) abort(); | |
| 1563 | \\ if (func2() != 5) abort(); | |
| 1564 | \\ if (func2() != 6) abort(); | |
| 1565 | \\ return 0; | |
| 1566 | \\} | |
| 1567 | , ""); | |
| 1568 | ||
| 1569 | cases.add("nested same-name static locals", | |
| 1570 | \\#include <stdlib.h> | |
| 1571 | \\int func(int val) { | |
| 1572 | \\ static int foo; | |
| 1573 | \\ if (foo != val) abort(); | |
| 1574 | \\ { | |
| 1575 | \\ foo += 1; | |
| 1576 | \\ static int foo = 2; | |
| 1577 | \\ if (foo != val + 2) abort(); | |
| 1578 | \\ foo += 1; | |
| 1579 | \\ } | |
| 1580 | \\ return foo; | |
| 1581 | \\} | |
| 1582 | \\int main(void) { | |
| 1583 | \\ int foo = 1; | |
| 1584 | \\ if (func(0) != 1) abort(); | |
| 1585 | \\ if (func(1) != 2) abort(); | |
| 1586 | \\ if (func(2) != 3) abort(); | |
| 1587 | \\ if (foo != 1) abort(); | |
| 1588 | \\ return 0; | |
| 1589 | \\} | |
| 1590 | , ""); | |
| 1553 | 1591 | } |
test/translate_c.zig+28-4| ... | ... | @@ -286,15 +286,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 286 | 286 | \\} |
| 287 | 287 | }); |
| 288 | 288 | |
| 289 | cases.add("extern variable in block scope", | |
| 289 | cases.add("static variable in block scope", | |
| 290 | 290 | \\float bar; |
| 291 | 291 | \\int foo() { |
| 292 | 292 | \\ _Thread_local static int bar = 2; |
| 293 | 293 | \\} |
| 294 | 294 | , &[_][]const u8{ |
| 295 | 295 | \\pub export var bar: f32 = @import("std").mem.zeroes(f32); |
| 296 | \\threadlocal var bar_1: c_int = 2; | |
| 297 | 296 | \\pub export fn foo() c_int { |
| 297 | \\ const bar_1 = struct { | |
| 298 | \\ threadlocal var static: c_int = 2; | |
| 299 | \\ }; | |
| 298 | 300 | \\ return 0; |
| 299 | 301 | \\} |
| 300 | 302 | }); |
| ... | ... | @@ -816,8 +818,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 816 | 818 | \\ static const char v2[] = "2.2.2"; |
| 817 | 819 | \\} |
| 818 | 820 | , &[_][]const u8{ |
| 819 | \\const v2: [5:0]u8 = "2.2.2".*; | |
| 820 | \\pub export fn foo() void {} | |
| 821 | \\pub export fn foo() void { | |
| 822 | \\ const v2 = struct { | |
| 823 | \\ const static: [5:0]u8 = "2.2.2".*; | |
| 824 | \\ }; | |
| 825 | \\} | |
| 821 | 826 | }); |
| 822 | 827 | |
| 823 | 828 | cases.add("simple function definition", |
| ... | ... | @@ -3595,4 +3600,23 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 3595 | 3600 | \\ return bar(@as(c_int, 1), @as(c_int, 2)); |
| 3596 | 3601 | \\} |
| 3597 | 3602 | }); |
| 3603 | ||
| 3604 | cases.add("static local variable zero-initialized if no initializer", | |
| 3605 | \\struct FOO {int x; int y;}; | |
| 3606 | \\int bar(void) { | |
| 3607 | \\ static struct FOO foo; | |
| 3608 | \\ return foo.x; | |
| 3609 | \\} | |
| 3610 | , &[_][]const u8{ | |
| 3611 | \\pub const struct_FOO = extern struct { | |
| 3612 | \\ x: c_int, | |
| 3613 | \\ y: c_int, | |
| 3614 | \\}; | |
| 3615 | \\pub export fn bar() c_int { | |
| 3616 | \\ const foo = struct { | |
| 3617 | \\ var static: struct_FOO = @import("std").mem.zeroes(struct_FOO); | |
| 3618 | \\ }; | |
| 3619 | \\ return foo.static.x; | |
| 3620 | \\} | |
| 3621 | }); | |
| 3598 | 3622 | } |