authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-06-08 17:09:42-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-12 23:12:37+03:00
logea4a25287eb65e639fd75d425f320663e6e8dca1
treeb207a5d47fc1b3b42bca6ad8aa10b2283779255c
parent44cdafd9d46bed36f08effb99aa0bb7fc2145efa

translate-c: better support for static local variables

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,6 +1002,9 @@ pub const VarDecl = opaque {
10021002
1003 pub const getTypeSourceInfo_getType = ZigClangVarDecl_getTypeSourceInfo_getType;1003 pub const getTypeSourceInfo_getType = ZigClangVarDecl_getTypeSourceInfo_getType;
1004 extern fn ZigClangVarDecl_getTypeSourceInfo_getType(*const VarDecl) QualType;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};
10061009
1007pub const VectorType = opaque {1010pub const VectorType = opaque {
src/translate_c.zig+31-11
...@@ -72,6 +72,11 @@ const Scope = struct {...@@ -72,6 +72,11 @@ const Scope = struct {
72 /// so that the return expression can be cast, if necessary72 /// so that the return expression can be cast, if necessary
73 return_type: ?clang.QualType = null,73 return_type: ?clang.QualType = null,
7474
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 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {80 fn init(c: *Context, parent: *Scope, labeled: bool) !Block {
76 var blk = Block{81 var blk = Block{
77 .base = .{82 .base = .{
...@@ -1738,15 +1743,13 @@ fn transDeclStmtOne(...@@ -1738,15 +1743,13 @@ fn transDeclStmtOne(
1738 const name = try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());1743 const name = try c.str(@ptrCast(*const clang.NamedDecl, var_decl).getName_bytes_begin());
1739 const mangled_name = try block_scope.makeMangledName(c, name);1744 const mangled_name = try block_scope.makeMangledName(c, name);
17401745
1741 switch (var_decl.getStorageClass()) {1746 if (var_decl.getStorageClass() == .Extern) {
1742 .Extern, .Static => {1747 // This is actually a global variable, put it in the global scope and reference it.
1743 // This is actually a global variable, put it in the global scope and reference it.1748 // `_ = mangled_name;`
1744 // `_ = mangled_name;`1749 return visitVarDecl(c, var_decl, mangled_name);
1745 return visitVarDecl(c, var_decl, mangled_name);
1746 },
1747 else => {},
1748 }1750 }
17491751
1752 const is_static_local = var_decl.isStaticLocal();
1750 const is_const = qual_type.isConstQualified();1753 const is_const = qual_type.isConstQualified();
17511754
1752 const loc = decl.getLocation();1755 const loc = decl.getLocation();
...@@ -1757,24 +1760,30 @@ fn transDeclStmtOne(...@@ -1757,24 +1760,30 @@ fn transDeclStmtOne(
1757 try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)1760 try transStringLiteralInitializer(c, scope, @ptrCast(*const clang.StringLiteral, expr), type_node)
1758 else1761 else
1759 try transExprCoercing(c, scope, expr, .used)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 else1765 else
1761 Tag.undefined_literal.init();1766 Tag.undefined_literal.init();
1762 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {1767 if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
1763 init_node = try Tag.bool_to_int.create(c.arena, init_node);1768 init_node = try Tag.bool_to_int.create(c.arena, init_node);
1764 }1769 }
17651770
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 .is_pub = false,1773 .is_pub = false,
1768 .is_const = is_const,1774 .is_const = is_const,
1769 .is_extern = false,1775 .is_extern = false,
1770 .is_export = false,1776 .is_export = false,
1771 .is_threadlocal = false,1777 .is_threadlocal = var_decl.getTLSKind() != .None,
1772 .linksection_string = null,1778 .linksection_string = null,
1773 .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)),1779 .alignment = zigAlignment(var_decl.getAlignedAttribute(c.clang_context)),
1774 .name = mangled_name,1780 .name = var_name,
1775 .type = type_node,1781 .type = type_node,
1776 .init = init_node,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 try block_scope.statements.append(node);1787 try block_scope.statements.append(node);
17791788
1780 const cleanup_attr = var_decl.getCleanupAttribute();1789 const cleanup_attr = var_decl.getCleanupAttribute();
...@@ -1834,7 +1843,18 @@ fn transDeclRefExpr(...@@ -1834,7 +1843,18 @@ fn transDeclRefExpr(
1834 const value_decl = expr.getDecl();1843 const value_decl = expr.getDecl();
1835 const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());1844 const name = try c.str(@ptrCast(*const clang.NamedDecl, value_decl).getName_bytes_begin());
1836 const mangled_name = scope.getAlias(name);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}
18391859
1840fn transImplicitCastExpr(1860fn transImplicitCastExpr(
src/translate_c/ast.zig+33-1
...@@ -60,6 +60,8 @@ pub const Node = extern union {...@@ -60,6 +60,8 @@ pub const Node = extern union {
60 array_access,60 array_access,
61 call,61 call,
62 var_decl,62 var_decl,
63 /// const name = struct { init }
64 static_local_var,
63 func,65 func,
64 warning,66 warning,
65 /// All enums are non-exhaustive67 /// All enums are non-exhaustive
...@@ -366,7 +368,7 @@ pub const Node = extern union {...@@ -366,7 +368,7 @@ pub const Node = extern union {
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 .log2_int_type => Payload.Log2IntType,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 .pub_enum_redecl, .enum_redecl => Payload.EnumRedecl,372 .pub_enum_redecl, .enum_redecl => Payload.EnumRedecl,
371 .array_filler => Payload.ArrayFiller,373 .array_filler => Payload.ArrayFiller,
372 .pub_inline_fn => Payload.PubInlineFn,374 .pub_inline_fn => Payload.PubInlineFn,
...@@ -1209,6 +1211,35 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -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 .var_decl => return renderVar(c, node),1243 .var_decl => return renderVar(c, node),
1213 .arg_redecl, .alias => {1244 .arg_redecl, .alias => {
1214 const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data;1245 const payload = @fieldParentPtr(Payload.ArgRedecl, "base", node.ptr_otherwise).data;
...@@ -2276,6 +2307,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2276,6 +2307,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2276 .div_exact,2307 .div_exact,
2277 .offset_of,2308 .offset_of,
2278 .shuffle,2309 .shuffle,
2310 .static_local_var,
2279 => {2311 => {
2280 // no grouping needed2312 // no grouping needed
2281 return renderNode(c, node);2313 return renderNode(c, node);
src/zig_clang.cpp+5
...@@ -2458,6 +2458,11 @@ enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangV...@@ -2458,6 +2458,11 @@ enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangV
2458 return (ZigClangStorageClass)casted->getStorageClass();2458 return (ZigClangStorageClass)casted->getStorageClass();
2459}2459}
24602460
2461bool ZigClangVarDecl_isStaticLocal(const struct ZigClangVarDecl *self) {
2462 auto casted = reinterpret_cast<const clang::VarDecl *>(self);
2463 return casted->isStaticLocal();
2464}
2465
2461enum ZigClangBuiltinTypeKind ZigClangBuiltinType_getKind(const struct ZigClangBuiltinType *self) {2466enum ZigClangBuiltinTypeKind ZigClangBuiltinType_getKind(const struct ZigClangBuiltinType *self) {
2462 auto casted = reinterpret_cast<const clang::BuiltinType *>(self);2467 auto casted = reinterpret_cast<const clang::BuiltinType *>(self);
2463 return (ZigClangBuiltinTypeKind)casted->getKind();2468 return (ZigClangBuiltinTypeKind)casted->getKind();
src/zig_clang.h+1
...@@ -1072,6 +1072,7 @@ ZIG_EXTERN_C bool ZigClangVarDecl_hasInit(const struct ZigClangVarDecl *);...@@ -1072,6 +1072,7 @@ ZIG_EXTERN_C bool ZigClangVarDecl_hasInit(const struct ZigClangVarDecl *);
1072ZIG_EXTERN_C const struct ZigClangAPValue *ZigClangVarDecl_evaluateValue(const struct ZigClangVarDecl *);1072ZIG_EXTERN_C const struct ZigClangAPValue *ZigClangVarDecl_evaluateValue(const struct ZigClangVarDecl *);
1073ZIG_EXTERN_C struct ZigClangQualType ZigClangVarDecl_getTypeSourceInfo_getType(const struct ZigClangVarDecl *);1073ZIG_EXTERN_C struct ZigClangQualType ZigClangVarDecl_getTypeSourceInfo_getType(const struct ZigClangVarDecl *);
1074ZIG_EXTERN_C enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangVarDecl *self);1074ZIG_EXTERN_C enum ZigClangStorageClass ZigClangVarDecl_getStorageClass(const struct ZigClangVarDecl *self);
1075ZIG_EXTERN_C bool ZigClangVarDecl_isStaticLocal(const struct ZigClangVarDecl *self);
10751076
1076ZIG_EXTERN_C bool ZigClangSourceLocation_eq(struct ZigClangSourceLocation a, struct ZigClangSourceLocation b);1077ZIG_EXTERN_C bool ZigClangSourceLocation_eq(struct ZigClangSourceLocation a, struct ZigClangSourceLocation b);
10771078
test/run_translated_c.zig+38
...@@ -1550,4 +1550,42 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1550,4 +1550,42 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1550 \\ if(FORCE_UINT != 0xffffffff) abort();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,15 +286,17 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
286 \\}286 \\}
287 });287 });
288288
289 cases.add("extern variable in block scope",289 cases.add("static variable in block scope",
290 \\float bar;290 \\float bar;
291 \\int foo() {291 \\int foo() {
292 \\ _Thread_local static int bar = 2;292 \\ _Thread_local static int bar = 2;
293 \\}293 \\}
294 , &[_][]const u8{294 , &[_][]const u8{
295 \\pub export var bar: f32 = @import("std").mem.zeroes(f32);295 \\pub export var bar: f32 = @import("std").mem.zeroes(f32);
296 \\threadlocal var bar_1: c_int = 2;
297 \\pub export fn foo() c_int {296 \\pub export fn foo() c_int {
297 \\ const bar_1 = struct {
298 \\ threadlocal var static: c_int = 2;
299 \\ };
298 \\ return 0;300 \\ return 0;
299 \\}301 \\}
300 });302 });
...@@ -816,8 +818,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -816,8 +818,11 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
816 \\ static const char v2[] = "2.2.2";818 \\ static const char v2[] = "2.2.2";
817 \\}819 \\}
818 , &[_][]const u8{820 , &[_][]const u8{
819 \\const v2: [5:0]u8 = "2.2.2".*;821 \\pub export fn foo() void {
820 \\pub export fn foo() void {}822 \\ const v2 = struct {
823 \\ const static: [5:0]u8 = "2.2.2".*;
824 \\ };
825 \\}
821 });826 });
822827
823 cases.add("simple function definition",828 cases.add("simple function definition",
...@@ -3595,4 +3600,23 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3595,4 +3600,23 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3595 \\ return bar(@as(c_int, 1), @as(c_int, 2));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}