| author | |
| committer | |
| log | 9cfcd0c29677e11f76846b006757ff49e05e3d6f |
| tree | 285973ffaab76ee3ca831a9663f15be62c44d32b |
| parent | 1ca49b92c650e9b8988a35983e202cdb9c7ba6e7 |
| parent | 717e2a365d9b5103f855668e4765cc154203a2bf |
| signature |
Disallow extern variables with initializers.13 files changed, 122 insertions(+), 33 deletions(-)
lib/std/c/darwin.zig+3-2| ... | @@ -46,10 +46,11 @@ const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header; | ... | @@ -46,10 +46,11 @@ const mach_hdr = if (@sizeOf(usize) == 8) mach_header_64 else mach_header; |
| 46 | /// on this operating system. However when building object files or libraries, | 46 | /// on this operating system. However when building object files or libraries, |
| 47 | /// the system libc won't be linked until the final executable. So we | 47 | /// the system libc won't be linked until the final executable. So we |
| 48 | /// export a weak symbol here, to be overridden by the real one. | 48 | /// export a weak symbol here, to be overridden by the real one. |
| 49 | pub extern "c" var _mh_execute_header: mach_hdr = undefined; | 49 | var dummy_execute_header: mach_hdr = undefined; |
| 50 | pub extern var _mh_execute_header: mach_hdr; | ||
| 50 | comptime { | 51 | comptime { |
| 51 | if (std.Target.current.isDarwin()) { | 52 | if (std.Target.current.isDarwin()) { |
| 52 | @export(_mh_execute_header, .{ .name = "_mh_execute_header", .linkage = .Weak }); | 53 | @export(dummy_execute_header, .{ .name = "_mh_execute_header", .linkage = .Weak }); |
| 53 | } | 54 | } |
| 54 | } | 55 | } |
| 55 | 56 |
lib/std/mem.zig+19-1| ... | @@ -226,9 +226,19 @@ pub fn zeroes(comptime T: type) T { | ... | @@ -226,9 +226,19 @@ pub fn zeroes(comptime T: type) T { |
| 226 | .Vector => |info| { | 226 | .Vector => |info| { |
| 227 | return @splat(info.len, zeroes(info.child)); | 227 | return @splat(info.len, zeroes(info.child)); |
| 228 | }, | 228 | }, |
| 229 | .Union => |info| { | ||
| 230 | if (comptime meta.containerLayout(T) == .Extern) { | ||
| 231 | // The C language specification states that (global) unions | ||
| 232 | // should be zero initialized to the first named member. | ||
| 233 | var item: T = undefined; | ||
| 234 | @field(item, info.fields[0].name) = zeroes(@TypeOf(@field(item, info.fields[0].name))); | ||
| 235 | return item; | ||
| 236 | } | ||
| 237 | |||
| 238 | @compileError("Can't set a " ++ @typeName(T) ++ " to zero."); | ||
| 239 | }, | ||
| 229 | .ErrorUnion, | 240 | .ErrorUnion, |
| 230 | .ErrorSet, | 241 | .ErrorSet, |
| 231 | .Union, | ||
| 232 | .Fn, | 242 | .Fn, |
| 233 | .BoundFn, | 243 | .BoundFn, |
| 234 | .Type, | 244 | .Type, |
| ... | @@ -317,6 +327,14 @@ test "mem.zeroes" { | ... | @@ -317,6 +327,14 @@ test "mem.zeroes" { |
| 317 | for (b.sentinel) |e| { | 327 | for (b.sentinel) |e| { |
| 318 | testing.expectEqual(@as(u8, 0), e); | 328 | testing.expectEqual(@as(u8, 0), e); |
| 319 | } | 329 | } |
| 330 | |||
| 331 | const C_union = extern union { | ||
| 332 | a: u8, | ||
| 333 | b: u32, | ||
| 334 | }; | ||
| 335 | |||
| 336 | var c = zeroes(C_union); | ||
| 337 | testing.expectEqual(@as(u8, 0), c.a); | ||
| 320 | } | 338 | } |
| 321 | 339 | ||
| 322 | /// Sets a slice to zeroes. | 340 | /// Sets a slice to zeroes. |
lib/std/special/c.zig+1-1| ... | @@ -40,7 +40,7 @@ comptime { | ... | @@ -40,7 +40,7 @@ comptime { |
| 40 | } | 40 | } |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | extern var _fltused: c_int = 1; | 43 | var _fltused: c_int = 1; |
| 44 | 44 | ||
| 45 | extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; | 45 | extern fn main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; |
| 46 | fn wasm_start() callconv(.C) void { | 46 | fn wasm_start() callconv(.C) void { |
lib/std/special/compiler_rt.zig+1-1| ... | @@ -340,7 +340,7 @@ fn __stack_chk_fail() callconv(.C) noreturn { | ... | @@ -340,7 +340,7 @@ fn __stack_chk_fail() callconv(.C) noreturn { |
| 340 | @panic("stack smashing detected"); | 340 | @panic("stack smashing detected"); |
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | extern var __stack_chk_guard: usize = blk: { | 343 | var __stack_chk_guard: usize = blk: { |
| 344 | var buf = [1]u8{0} ** @sizeOf(usize); | 344 | var buf = [1]u8{0} ** @sizeOf(usize); |
| 345 | buf[@sizeOf(usize) - 1] = 255; | 345 | buf[@sizeOf(usize) - 1] = 255; |
| 346 | buf[@sizeOf(usize) - 2] = '\n'; | 346 | buf[@sizeOf(usize) - 2] = '\n'; |
src-self-hosted/Module.zig+7-4| ... | @@ -324,10 +324,10 @@ pub const Fn = struct { | ... | @@ -324,10 +324,10 @@ pub const Fn = struct { |
| 324 | }; | 324 | }; |
| 325 | 325 | ||
| 326 | pub const Var = struct { | 326 | pub const Var = struct { |
| 327 | /// if is_extern == true this is undefined | ||
| 327 | init: Value, | 328 | init: Value, |
| 328 | owner_decl: *Decl, | 329 | owner_decl: *Decl, |
| 329 | 330 | ||
| 330 | has_init: bool, | ||
| 331 | is_extern: bool, | 331 | is_extern: bool, |
| 332 | is_mutable: bool, | 332 | is_mutable: bool, |
| 333 | is_threadlocal: bool, | 333 | is_threadlocal: bool, |
| ... | @@ -1456,7 +1456,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1456,7 +1456,11 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1456 | const is_extern = blk: { | 1456 | const is_extern = blk: { |
| 1457 | const maybe_extern_token = var_decl.getTrailer("extern_export_token") orelse | 1457 | const maybe_extern_token = var_decl.getTrailer("extern_export_token") orelse |
| 1458 | break :blk false; | 1458 | break :blk false; |
| 1459 | break :blk tree.token_ids[maybe_extern_token] == .Keyword_extern; | 1459 | if (tree.token_ids[maybe_extern_token] != .Keyword_extern) break :blk false; |
| 1460 | if (var_decl.getTrailer("init_node")) |some| { | ||
| 1461 | return self.failNode(&block_scope.base, some, "extern variables have no initializers", .{}); | ||
| 1462 | } | ||
| 1463 | break :blk true; | ||
| 1460 | }; | 1464 | }; |
| 1461 | if (var_decl.getTrailer("lib_name")) |lib_name| { | 1465 | if (var_decl.getTrailer("lib_name")) |lib_name| { |
| 1462 | assert(is_extern); | 1466 | assert(is_extern); |
| ... | @@ -1569,7 +1573,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { | ... | @@ -1569,7 +1573,6 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { |
| 1569 | new_variable.* = .{ | 1573 | new_variable.* = .{ |
| 1570 | .owner_decl = decl, | 1574 | .owner_decl = decl, |
| 1571 | .init = value orelse undefined, | 1575 | .init = value orelse undefined, |
| 1572 | .has_init = value != null, | ||
| 1573 | .is_extern = is_extern, | 1576 | .is_extern = is_extern, |
| 1574 | .is_mutable = is_mutable, | 1577 | .is_mutable = is_mutable, |
| 1575 | .is_threadlocal = is_threadlocal, | 1578 | .is_threadlocal = is_threadlocal, |
| ... | @@ -2440,7 +2443,7 @@ fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) Inner | ... | @@ -2440,7 +2443,7 @@ fn analyzeVarRef(self: *Module, scope: *Scope, src: usize, tv: TypedValue) Inner |
| 2440 | const variable = tv.val.cast(Value.Payload.Variable).?.variable; | 2443 | const variable = tv.val.cast(Value.Payload.Variable).?.variable; |
| 2441 | 2444 | ||
| 2442 | const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty); | 2445 | const ty = try self.singlePtrType(scope, src, variable.is_mutable, tv.ty); |
| 2443 | if (!variable.is_mutable and !variable.is_extern and variable.has_init) { | 2446 | if (!variable.is_mutable and !variable.is_extern) { |
| 2444 | const val_payload = try scope.arena().create(Value.Payload.RefVal); | 2447 | const val_payload = try scope.arena().create(Value.Payload.RefVal); |
| 2445 | val_payload.* = .{ .val = variable.init }; | 2448 | val_payload.* = .{ .val = variable.init }; |
| 2446 | return self.constInst(scope, src, .{ | 2449 | return self.constInst(scope, src, .{ |
src-self-hosted/translate_c.zig+41-13| ... | @@ -498,7 +498,7 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { | ... | @@ -498,7 +498,7 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { |
| 498 | _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl)); | 498 | _ = try transRecordDecl(c, @ptrCast(*const ZigClangRecordDecl, decl)); |
| 499 | }, | 499 | }, |
| 500 | .Var => { | 500 | .Var => { |
| 501 | return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl)); | 501 | return visitVarDecl(c, @ptrCast(*const ZigClangVarDecl, decl), null); |
| 502 | }, | 502 | }, |
| 503 | .Empty => { | 503 | .Empty => { |
| 504 | // Do nothing | 504 | // Do nothing |
| ... | @@ -679,12 +679,13 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { | ... | @@ -679,12 +679,13 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { |
| 679 | return addTopLevelDecl(c, fn_name, &proto_node.base); | 679 | return addTopLevelDecl(c, fn_name, &proto_node.base); |
| 680 | } | 680 | } |
| 681 | 681 | ||
| 682 | fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { | 682 | /// if mangled_name is not null, this var decl was declared in a block scope. |
| 683 | const var_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, var_decl))); | 683 | fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl, mangled_name: ?[]const u8) Error!void { |
| 684 | const var_name = mangled_name orelse try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, var_decl))); | ||
| 684 | if (c.global_scope.sym_table.contains(var_name)) | 685 | if (c.global_scope.sym_table.contains(var_name)) |
| 685 | return; // Avoid processing this decl twice | 686 | return; // Avoid processing this decl twice |
| 686 | const rp = makeRestorePoint(c); | 687 | const rp = makeRestorePoint(c); |
| 687 | const visib_tok = try appendToken(c, .Keyword_pub, "pub"); | 688 | const visib_tok = if (mangled_name) |_| null else try appendToken(c, .Keyword_pub, "pub"); |
| 688 | 689 | ||
| 689 | const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) | 690 | const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) |
| 690 | null | 691 | null |
| ... | @@ -701,8 +702,14 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { | ... | @@ -701,8 +702,14 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { |
| 701 | const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); | 702 | const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); |
| 702 | const storage_class = ZigClangVarDecl_getStorageClass(var_decl); | 703 | const storage_class = ZigClangVarDecl_getStorageClass(var_decl); |
| 703 | const is_const = ZigClangQualType_isConstQualified(qual_type); | 704 | const is_const = ZigClangQualType_isConstQualified(qual_type); |
| 704 | 705 | const has_init = ZigClangVarDecl_hasInit(var_decl); | |
| 705 | const extern_tok = if (storage_class == .Extern) | 706 | |
| 707 | // In C extern variables with initializers behave like Zig exports. | ||
| 708 | // extern int foo = 2; | ||
| 709 | // does the same as: | ||
| 710 | // extern int foo; | ||
| 711 | // int foo = 2; | ||
| 712 | const extern_tok = if (storage_class == .Extern and !has_init) | ||
| 706 | try appendToken(c, .Keyword_extern, "extern") | 713 | try appendToken(c, .Keyword_extern, "extern") |
| 707 | else if (storage_class != .Static) | 714 | else if (storage_class != .Static) |
| 708 | try appendToken(c, .Keyword_export, "export") | 715 | try appendToken(c, .Keyword_export, "export") |
| ... | @@ -730,7 +737,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { | ... | @@ -730,7 +737,7 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { |
| 730 | // If the initialization expression is not present, initialize with undefined. | 737 | // If the initialization expression is not present, initialize with undefined. |
| 731 | // If it is an integer literal, we can skip the @as since it will be redundant | 738 | // If it is an integer literal, we can skip the @as since it will be redundant |
| 732 | // with the variable type. | 739 | // with the variable type. |
| 733 | if (ZigClangVarDecl_hasInit(var_decl)) { | 740 | if (has_init) { |
| 734 | eq_tok = try appendToken(c, .Equal, "="); | 741 | eq_tok = try appendToken(c, .Equal, "="); |
| 735 | init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| | 742 | init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| |
| 736 | transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { | 743 | transExprCoercing(rp, &c.global_scope.base, expr, .used, .r_value) catch |err| switch (err) { |
| ... | @@ -745,7 +752,22 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { | ... | @@ -745,7 +752,22 @@ fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { |
| 745 | try transCreateNodeUndefinedLiteral(c); | 752 | try transCreateNodeUndefinedLiteral(c); |
| 746 | } else if (storage_class != .Extern) { | 753 | } else if (storage_class != .Extern) { |
| 747 | eq_tok = try appendToken(c, .Equal, "="); | 754 | eq_tok = try appendToken(c, .Equal, "="); |
| 748 | init_node = try transCreateNodeIdentifierUnchecked(c, "undefined"); | 755 | // The C language specification states that variables with static or threadlocal |
| 756 | // storage without an initializer are initialized to a zero value. | ||
| 757 | |||
| 758 | // @import("std").mem.zeroes(T) | ||
| 759 | const import_fn_call = try c.createBuiltinCall("@import", 1); | ||
| 760 | const std_node = try transCreateNodeStringLiteral(c, "\"std\""); | ||
| 761 | import_fn_call.params()[0] = std_node; | ||
| 762 | import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); | ||
| 763 | const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); | ||
| 764 | const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroes"); | ||
| 765 | |||
| 766 | const zero_init_call = try c.createCall(outer_field_access, 1); | ||
| 767 | zero_init_call.params()[0] = type_node; | ||
| 768 | zero_init_call.rtoken = try appendToken(c, .RParen, ")"); | ||
| 769 | |||
| 770 | init_node = &zero_init_call.base; | ||
| 749 | } | 771 | } |
| 750 | 772 | ||
| 751 | const linksection_expr = blk: { | 773 | const linksection_expr = blk: { |
| ... | @@ -1561,15 +1583,22 @@ fn transDeclStmtOne( | ... | @@ -1561,15 +1583,22 @@ fn transDeclStmtOne( |
| 1561 | .Var => { | 1583 | .Var => { |
| 1562 | const var_decl = @ptrCast(*const ZigClangVarDecl, decl); | 1584 | const var_decl = @ptrCast(*const ZigClangVarDecl, decl); |
| 1563 | 1585 | ||
| 1564 | const thread_local_token = if (ZigClangVarDecl_getTLSKind(var_decl) == .None) | ||
| 1565 | null | ||
| 1566 | else | ||
| 1567 | try appendToken(c, .Keyword_threadlocal, "threadlocal"); | ||
| 1568 | const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); | 1586 | const qual_type = ZigClangVarDecl_getTypeSourceInfo_getType(var_decl); |
| 1569 | const name = try c.str(ZigClangNamedDecl_getName_bytes_begin( | 1587 | const name = try c.str(ZigClangNamedDecl_getName_bytes_begin( |
| 1570 | @ptrCast(*const ZigClangNamedDecl, var_decl), | 1588 | @ptrCast(*const ZigClangNamedDecl, var_decl), |
| 1571 | )); | 1589 | )); |
| 1572 | const mangled_name = try block_scope.makeMangledName(c, name); | 1590 | const mangled_name = try block_scope.makeMangledName(c, name); |
| 1591 | |||
| 1592 | switch (ZigClangVarDecl_getStorageClass(var_decl)) { | ||
| 1593 | .Extern, .Static => { | ||
| 1594 | // This is actually a global variable, put it in the global scope and reference it. | ||
| 1595 | // `_ = mangled_name;` | ||
| 1596 | try visitVarDecl(rp.c, var_decl, mangled_name); | ||
| 1597 | return try maybeSuppressResult(rp, scope, .unused, try transCreateNodeIdentifier(rp.c, mangled_name)); | ||
| 1598 | }, | ||
| 1599 | else => {}, | ||
| 1600 | } | ||
| 1601 | |||
| 1573 | const mut_tok = if (ZigClangQualType_isConstQualified(qual_type)) | 1602 | const mut_tok = if (ZigClangQualType_isConstQualified(qual_type)) |
| 1574 | try appendToken(c, .Keyword_const, "const") | 1603 | try appendToken(c, .Keyword_const, "const") |
| 1575 | else | 1604 | else |
| ... | @@ -1597,7 +1626,6 @@ fn transDeclStmtOne( | ... | @@ -1597,7 +1626,6 @@ fn transDeclStmtOne( |
| 1597 | .mut_token = mut_tok, | 1626 | .mut_token = mut_tok, |
| 1598 | .semicolon_token = semicolon_token, | 1627 | .semicolon_token = semicolon_token, |
| 1599 | }, .{ | 1628 | }, .{ |
| 1600 | .thread_local_token = thread_local_token, | ||
| 1601 | .eq_token = eq_token, | 1629 | .eq_token = eq_token, |
| 1602 | .type_node = type_node, | 1630 | .type_node = type_node, |
| 1603 | .init_node = init_node, | 1631 | .init_node = init_node, |
src/codegen.cpp+5-4| ... | @@ -292,13 +292,14 @@ static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) { | ... | @@ -292,13 +292,14 @@ static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) { |
| 292 | } | 292 | } |
| 293 | } | 293 | } |
| 294 | 294 | ||
| 295 | static LLVMLinkage to_llvm_linkage(GlobalLinkageId id) { | 295 | static LLVMLinkage to_llvm_linkage(GlobalLinkageId id, bool is_extern) { |
| 296 | switch (id) { | 296 | switch (id) { |
| 297 | case GlobalLinkageIdInternal: | 297 | case GlobalLinkageIdInternal: |
| 298 | return LLVMInternalLinkage; | 298 | return LLVMInternalLinkage; |
| 299 | case GlobalLinkageIdStrong: | 299 | case GlobalLinkageIdStrong: |
| 300 | return LLVMExternalLinkage; | 300 | return LLVMExternalLinkage; |
| 301 | case GlobalLinkageIdWeak: | 301 | case GlobalLinkageIdWeak: |
| 302 | if (is_extern) return LLVMExternalWeakLinkage; | ||
| 302 | return LLVMWeakODRLinkage; | 303 | return LLVMWeakODRLinkage; |
| 303 | case GlobalLinkageIdLinkOnce: | 304 | case GlobalLinkageIdLinkOnce: |
| 304 | return LLVMLinkOnceODRLinkage; | 305 | return LLVMLinkOnceODRLinkage; |
| ... | @@ -521,7 +522,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { | ... | @@ -521,7 +522,7 @@ static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { |
| 521 | } | 522 | } |
| 522 | 523 | ||
| 523 | 524 | ||
| 524 | LLVMSetLinkage(llvm_fn, to_llvm_linkage(linkage)); | 525 | LLVMSetLinkage(llvm_fn, to_llvm_linkage(linkage, fn->body_node == nullptr)); |
| 525 | 526 | ||
| 526 | if (linkage == GlobalLinkageIdInternal) { | 527 | if (linkage == GlobalLinkageIdInternal) { |
| 527 | LLVMSetUnnamedAddr(llvm_fn, true); | 528 | LLVMSetUnnamedAddr(llvm_fn, true); |
| ... | @@ -7962,7 +7963,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -7962,7 +7963,7 @@ static void do_code_gen(CodeGen *g) { |
| 7962 | global_value = LLVMAddGlobal(g->module, get_llvm_type(g, var->var_type), symbol_name); | 7963 | global_value = LLVMAddGlobal(g->module, get_llvm_type(g, var->var_type), symbol_name); |
| 7963 | // TODO debug info for the extern variable | 7964 | // TODO debug info for the extern variable |
| 7964 | 7965 | ||
| 7965 | LLVMSetLinkage(global_value, to_llvm_linkage(linkage)); | 7966 | LLVMSetLinkage(global_value, to_llvm_linkage(linkage, true)); |
| 7966 | maybe_import_dll(g, global_value, GlobalLinkageIdStrong); | 7967 | maybe_import_dll(g, global_value, GlobalLinkageIdStrong); |
| 7967 | LLVMSetAlignment(global_value, var->align_bytes); | 7968 | LLVMSetAlignment(global_value, var->align_bytes); |
| 7968 | LLVMSetGlobalConstant(global_value, var->gen_is_const); | 7969 | LLVMSetGlobalConstant(global_value, var->gen_is_const); |
| ... | @@ -7975,7 +7976,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -7975,7 +7976,7 @@ static void do_code_gen(CodeGen *g) { |
| 7975 | global_value = var->const_value->llvm_global; | 7976 | global_value = var->const_value->llvm_global; |
| 7976 | 7977 | ||
| 7977 | if (exported) { | 7978 | if (exported) { |
| 7978 | LLVMSetLinkage(global_value, to_llvm_linkage(linkage)); | 7979 | LLVMSetLinkage(global_value, to_llvm_linkage(linkage, false)); |
| 7979 | maybe_export_dll(g, global_value, GlobalLinkageIdStrong); | 7980 | maybe_export_dll(g, global_value, GlobalLinkageIdStrong); |
| 7980 | } | 7981 | } |
| 7981 | if (var->section_name) { | 7982 | if (var->section_name) { |
src/parser.cpp+3| ... | @@ -680,6 +680,9 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B | ... | @@ -680,6 +680,9 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, B |
| 680 | AstNode *var_decl = ast_parse_var_decl(pc); | 680 | AstNode *var_decl = ast_parse_var_decl(pc); |
| 681 | if (var_decl != nullptr) { | 681 | if (var_decl != nullptr) { |
| 682 | assert(var_decl->type == NodeTypeVariableDeclaration); | 682 | assert(var_decl->type == NodeTypeVariableDeclaration); |
| 683 | if (first->id == TokenIdKeywordExtern && var_decl->data.variable_declaration.expr != nullptr) { | ||
| 684 | ast_error(pc, first, "extern variables have no initializers"); | ||
| 685 | } | ||
| 683 | var_decl->line = first->start_line; | 686 | var_decl->line = first->start_line; |
| 684 | var_decl->column = first->start_column; | 687 | var_decl->column = first->start_column; |
| 685 | var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; | 688 | var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; |
test/compile_errors.zig+6| ... | @@ -2,6 +2,12 @@ const tests = @import("tests.zig"); | ... | @@ -2,6 +2,12 @@ const tests = @import("tests.zig"); |
| 2 | const std = @import("std"); | 2 | const std = @import("std"); |
| 3 | 3 | ||
| 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.addTest("reject extern variables with initializers", | ||
| 6 | \\extern var foo: int = 2; | ||
| 7 | , &[_][]const u8{ | ||
| 8 | "tmp.zig:1:1: error: extern variables have no initializers", | ||
| 9 | }); | ||
| 10 | |||
| 5 | cases.addTest("duplicate/unused labels", | 11 | cases.addTest("duplicate/unused labels", |
| 6 | \\comptime { | 12 | \\comptime { |
| 7 | \\ blk: { blk: while (false) {} } | 13 | \\ blk: { blk: while (false) {} } |
test/run_translated_c.zig+14| ... | @@ -3,6 +3,20 @@ const tests = @import("tests.zig"); | ... | @@ -3,6 +3,20 @@ const tests = @import("tests.zig"); |
| 3 | const nl = std.cstr.line_sep; | 3 | const nl = std.cstr.line_sep; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.RunTranslatedCContext) void { | 5 | pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 6 | cases.add("static variable in block scope", | ||
| 7 | \\#include <stdlib.h> | ||
| 8 | \\int foo() { | ||
| 9 | \\ static int bar; | ||
| 10 | \\ bar += 1; | ||
| 11 | \\ return bar; | ||
| 12 | \\} | ||
| 13 | \\int main() { | ||
| 14 | \\ foo(); | ||
| 15 | \\ foo(); | ||
| 16 | \\ if (foo() != 3) abort(); | ||
| 17 | \\} | ||
| 18 | , ""); | ||
| 19 | |||
| 6 | cases.add("array initializer", | 20 | cases.add("array initializer", |
| 7 | \\#include <stdlib.h> | 21 | \\#include <stdlib.h> |
| 8 | \\int main(int argc, char **argv) { | 22 | \\int main(int argc, char **argv) { |
test/standalone/global_linkage/obj1.zig+2-2| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | extern var internal_integer: usize = 1; | 1 | var internal_integer: usize = 1; |
| 2 | extern var obj1_integer: usize = 421; | 2 | var obj1_integer: usize = 421; |
| 3 | 3 | ||
| 4 | comptime { | 4 | comptime { |
| 5 | @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal }); | 5 | @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal }); |
test/standalone/global_linkage/obj2.zig+2-2| ... | @@ -1,5 +1,5 @@ | ... | @@ -1,5 +1,5 @@ |
| 1 | extern var internal_integer: usize = 2; | 1 | var internal_integer: usize = 2; |
| 2 | extern var obj2_integer: usize = 422; | 2 | var obj2_integer: usize = 422; |
| 3 | 3 | ||
| 4 | comptime { | 4 | comptime { |
| 5 | @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal }); | 5 | @export(internal_integer, .{ .name = "internal_integer", .linkage = .Internal }); |
test/translate_c.zig+18-3| ... | @@ -3,6 +3,20 @@ const std = @import("std"); | ... | @@ -3,6 +3,20 @@ const std = @import("std"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.TranslateCContext) void { | 5 | pub fn addCases(cases: *tests.TranslateCContext) void { |
| 6 | cases.add("extern variable in block scope", | ||
| 7 | \\float bar; | ||
| 8 | \\int foo() { | ||
| 9 | \\ _Thread_local static int bar = 2; | ||
| 10 | \\} | ||
| 11 | , &[_][]const u8{ | ||
| 12 | \\pub export var bar: f32 = @import("std").mem.zeroes(f32); | ||
| 13 | \\threadlocal var bar_1: c_int = 2; | ||
| 14 | \\pub export fn foo() c_int { | ||
| 15 | \\ _ = bar_1; | ||
| 16 | \\ return 0; | ||
| 17 | \\} | ||
| 18 | }); | ||
| 19 | |||
| 6 | cases.add("missing return stmt", | 20 | cases.add("missing return stmt", |
| 7 | \\int foo() {} | 21 | \\int foo() {} |
| 8 | \\int bar() { | 22 | \\int bar() { |
| ... | @@ -466,7 +480,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -466,7 +480,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 466 | , &[_][]const u8{ | 480 | , &[_][]const u8{ |
| 467 | \\pub extern var extern_var: c_int; | 481 | \\pub extern var extern_var: c_int; |
| 468 | \\pub const int_var: c_int = 13; | 482 | \\pub const int_var: c_int = 13; |
| 469 | \\pub export var foo: c_int = undefined; | 483 | \\pub export var foo: c_int = @import("std").mem.zeroes(c_int); |
| 470 | }); | 484 | }); |
| 471 | 485 | ||
| 472 | cases.add("const ptr initializer", | 486 | cases.add("const ptr initializer", |
| ... | @@ -480,8 +494,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -480,8 +494,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 480 | \\ static const char v2[] = "2.2.2"; | 494 | \\ static const char v2[] = "2.2.2"; |
| 481 | \\} | 495 | \\} |
| 482 | , &[_][]const u8{ | 496 | , &[_][]const u8{ |
| 497 | \\const v2: [*c]const u8 = "2.2.2"; | ||
| 483 | \\pub export fn foo() void { | 498 | \\pub export fn foo() void { |
| 484 | \\ const v2: [*c]const u8 = "2.2.2"; | 499 | \\ _ = v2; |
| 485 | \\} | 500 | \\} |
| 486 | }); | 501 | }); |
| 487 | 502 | ||
| ... | @@ -1327,7 +1342,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { | ... | @@ -1327,7 +1342,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { |
| 1327 | \\static char arr1[] = "hello"; | 1342 | \\static char arr1[] = "hello"; |
| 1328 | \\char arr2[] = "hello"; | 1343 | \\char arr2[] = "hello"; |
| 1329 | , &[_][]const u8{ | 1344 | , &[_][]const u8{ |
| 1330 | \\pub extern var arr0: [*c]u8 = "hello"; | 1345 | \\pub export var arr0: [*c]u8 = "hello"; |
| 1331 | \\pub var arr1: [*c]u8 = "hello"; | 1346 | \\pub var arr1: [*c]u8 = "hello"; |
| 1332 | \\pub export var arr2: [*c]u8 = "hello"; | 1347 | \\pub export var arr2: [*c]u8 = "hello"; |
| 1333 | }); | 1348 | }); |