From adc5bce5e8487cf9507ab5dd2b1b60dd7f54cc3d Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 20 Aug 2020 10:08:27 +0300 Subject: [PATCH] translate-c: correct translation of global variables * externs with intializers are translated as exports * non extern without explicit initialization are zero initalized --- lib/std/mem.zig | 20 +++++++++++++++++++- src-self-hosted/translate_c.zig | 27 ++++++++++++++++++++++++--- test/translate_c.zig | 4 ++-- 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 1ba64f47fa..7f1b5ae618 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -221,9 +221,19 @@ pub fn zeroes(comptime T: type) T { .Vector => |info| { return @splat(info.len, zeroes(info.child)); }, + .Union => |info| { + if (comptime meta.containerLayout(T) == .Extern) { + // The C language specification states that (global) unions + // should be zero initialized to the first named member. + var item: T = undefined; + @field(item, info.fields[0].name) = zeroes(@TypeOf(@field(item, info.fields[0].name))); + return item; + } + + @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); + }, .ErrorUnion, .ErrorSet, - .Union, .Fn, .BoundFn, .Type, @@ -312,6 +322,14 @@ test "mem.zeroes" { for (b.sentinel) |e| { testing.expectEqual(@as(u8, 0), e); } + + const C_union = extern union { + a: u8, + b: u32, + }; + + var c = zeroes(C_union); + testing.expectEqual(@as(u8, 0), c.a); } /// Sets a slice to zeroes. diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 98cb68f059..6c6e3f0c7e 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -701,8 +701,14 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); const storage_class = ZigClangVarDecl_getStorageClass(var_decl); const is_const = ZigClangQualType_isConstQualified(qual_type); + const has_init = ZigClangVarDecl_hasInit(var_decl); - const extern_tok = if (storage_class == .Extern) + // In C extern variables with initializers behave like Zig exports. + // extern int foo = 2; + // does the same as: + // extern int foo; + // int foo = 2; + const extern_tok = if (storage_class == .Extern and !has_init) try appendToken(c, .Keyword_extern, "extern") else if (storage_class != .Static) try appendToken(c, .Keyword_export, "export") @@ -730,7 +736,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { // If the initialization expression is not present, initialize with undefined. // If it is an integer literal, we can skip the @as since it will be redundant // with the variable type. - if (ZigClangVarDecl_hasInit(var_decl)) { + if (has_init) { eq_tok = try appendToken(c, .Equal, "="); init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { @@ -745,7 +751,22 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { try transCreateNodeUndefinedLiteral(c); } else if (storage_class != .Extern) { eq_tok = try appendToken(c, .Equal, "="); - init_node = try transCreateNodeIdentifierUnchecked(c, "undefined"); + // The C language specification states that variables with static or threadlocal + // storage without an initializer are initialized to a zero value. + + // @import("std").mem.zeroes(T) + const import_fn_call = try c.createBuiltinCall("@import", 1); + const std_node = try transCreateNodeStringLiteral(c, "\"std\""); + import_fn_call.params()[0] = std_node; + import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); + const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); + const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes"); + + const zero_init_call = try c.createCall(outer_field_access, 1); + zero_init_call.params()[0] = type_node; + zero_init_call.rtoken = try appendToken(c, .RParen, ")"); + + init_node = &zero_init_call.base; } const linksection_expr = blk: { diff --git a/test/translate_c.zig b/test/translate_c.zig index f7e983276e..bfbae2b65e 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -466,7 +466,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { , &[_][]const u8{ \\pub extern var extern_var: c_int; \\pub const int_var: c_int = 13; - \\pub export var foo: c_int = undefined; + \\pub export var foo: c_int = @import("std").mem.zeroes(c_int); }); cases.add("const ptr initializer", @@ -1327,7 +1327,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\static char arr1[] = "hello"; \\char arr2[] = "hello"; , &[_][]const u8{ - \\pub extern var arr0: [*c]u8 = "hello"; + \\pub export var arr0: [*c]u8 = "hello"; \\pub var arr1: [*c]u8 = "hello"; \\pub export var arr2: [*c]u8 = "hello"; });