Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Add `is_dll_import` to @extern, to support `__declspec(dllimport)` with the MSVC ABI by kcbanner · Pull Request #21758 · ziglang/zig · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/std/builtin.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -683,6 +683,7 @@ pub const ExternOptions = struct {
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
};

/// This data structure is used by the Zig language code generation and
Expand Down
11 changes: 9 additions & 2 deletions src/InternPool.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2054,6 +2054,7 @@ pub const Key = union(enum) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
alignment: Alignment,
@"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations.
Expand DownExpand Up@@ -2675,7 +2676,8 @@ pub const Key = union(enum) {
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++
asBytes(&e.@"addrspace") ++ asBytes(&e.zir_index)),
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)),
};
}

Expand DownExpand Up@@ -2771,6 +2773,7 @@ pub const Key = union(enum) {
a_info.is_const == b_info.is_const and
a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and
a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index;
Expand DownExpand Up@@ -5370,7 +5373,8 @@ pub const Tag = enum(u8) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
_: u29 = 0,
is_dll_import: bool,
_: u28 = 0,
};
};

Expand DownExpand Up@@ -6715,6 +6719,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.is_const = extra.flags.is_const,
.is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import,
.alignment = nav.status.resolved.alignment,
.@"addrspace" = nav.status.resolved.@"addrspace",
.zir_index = extra.zir_index,
Expand DownExpand Up@@ -7381,6 +7386,7 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}),
});
Expand DownExpand Up@@ -8644,6 +8650,7 @@ pub fn getExtern(
.is_const = key.is_const,
.is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import,
},
.zir_index = key.zir_index,
.owner_nav = owner_nav,
Expand Down
23 changes: 22 additions & 1 deletion src/Sema.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -876,6 +876,7 @@ const InferredAlloc = struct {

const NeededComptimeReason = struct {
needed_comptime_reason: []const u8,
value_comptime_reason: ?[]const u8 = null,
block_comptime_reason: ?*const Block.ComptimeReason = null,
};

Expand DownExpand Up@@ -2251,7 +2252,7 @@ fn resolveValueAllowVariables(sema: *Sema, inst: Air.Inst.Ref) CompileError!?Val
}
};
const val = Value.fromInterned(ip_index);
if (val.isPtrToThreadLocal(pt.zcu)) return null;
if (val.isPtrRuntimeValue(pt.zcu)) return null;
return val;
}

Expand All@@ -2277,8 +2278,14 @@ pub fn resolveFinalDeclValue(
const zcu = sema.pt.zcu;

const val = try sema.resolveValueAllowVariables(air_ref) orelse {
const value_comptime_reason: ?[]const u8 = if (air_ref.toInterned()) |_|
"thread local and dll imported variables have runtime-known addresses"
else
null;

return sema.failWithNeededComptime(block, src, .{
.needed_comptime_reason = "global variable initializer must be comptime-known",
.value_comptime_reason = value_comptime_reason,
});
};
if (val.isGenericPoison()) return error.GenericPoison;
Expand All@@ -2296,6 +2303,9 @@ fn failWithNeededComptime(sema: *Sema, block: *Block, src: LazySrcLoc, reason: N
const msg = try sema.errMsg(src, "unable to resolve comptime value", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(src, msg, "{s}", .{reason.needed_comptime_reason});
if (reason.value_comptime_reason) |value_comptime_reason| {
try sema.errNote(src, msg, "{s}", .{value_comptime_reason});
}

if (reason.block_comptime_reason) |block_comptime_reason| {
try block_comptime_reason.explain(sema, msg);
Expand DownExpand Up@@ -9960,6 +9970,7 @@ fn funcCommon(
.is_const = true,
.is_threadlocal = false,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment orelse .none,
.@"addrspace" = address_space orelse .generic,
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26505,6 +26516,7 @@ fn zirVarExtended(
.is_const = small.is_const,
.is_threadlocal = small.is_threadlocal,
.is_weak_linkage = false,
.is_dll_import = false,
.alignment = alignment,
.@"addrspace" = @"addrspace",
.zir_index = sema.getOwnerCauDeclInst(), // `declaration` instruction
Expand DownExpand Up@@ -26958,6 +26970,7 @@ fn resolveExternOptions(
library_name: InternPool.OptionalNullTerminatedString = .none,
linkage: std.builtin.GlobalLinkage = .strong,
is_thread_local: bool = false,
is_dll_import: bool = false,
} {
const pt = sema.pt;
const zcu = pt.zcu;
Expand All@@ -26971,6 +26984,7 @@ fn resolveExternOptions(
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });

const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{
Expand DownExpand Up@@ -27004,6 +27018,11 @@ fn resolveExternOptions(
break :library_name library_name;
} else null;

const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{
.needed_comptime_reason = "it must be comptime-known if the symbol is imported from a dll",
});

if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
}
Expand All@@ -27017,6 +27036,7 @@ fn resolveExternOptions(
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage,
.is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(),
};
}

Expand DownExpand Up@@ -27062,6 +27082,7 @@ fn zirBuiltinExtern(
.is_const = ptr_info.flags.is_const,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import,
.alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations.
Expand Down
4 changes: 2 additions & 2 deletions src/Value.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1340,11 +1340,11 @@ pub fn isLazySize(val: Value, zcu: *Zcu) bool {
};
}

pub fn isPtrToThreadLocal(val: Value, zcu: *Zcu) bool {
pub fn isPtrRuntimeValue(val: Value, zcu: *Zcu) bool {
const ip = &zcu.intern_pool;
const nav = ip.getBackingNav(val.toIntern()).unwrap() orelse return false;
return switch (ip.indexToKey(ip.getNav(nav).status.resolved.val)) {
.@"extern" => |e| e.is_threadlocal,
.@"extern" => |e| e.is_threadlocal or e.is_dll_import,
.variable => |v| v.is_threadlocal,
else => false,
};
Expand Down
3 changes: 3 additions & 0 deletions src/Zcu.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -1522,6 +1522,7 @@ pub const SrcLoc = struct {
.init_field_cache,
.init_field_library,
.init_field_thread_local,
.init_field_dll_import,
=> |builtin_call_node| {
const wanted = switch (src_loc.lazy) {
.init_field_name => "name",
Expand All@@ -1533,6 +1534,7 @@ pub const SrcLoc = struct {
.init_field_cache => "cache",
.init_field_library => "library",
.init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import",
else => unreachable,
};
const tree = try src_loc.file_scope.getTree(gpa);
Expand DownExpand Up@@ -1959,6 +1961,7 @@ pub const LazySrcLoc = struct {
init_field_cache: i32,
init_field_library: i32,
init_field_thread_local: i32,
init_field_dll_import: i32,
/// The source location points to the value of an item in a specific
/// case of a `switch`.
switch_case_item: SwitchItem,
Expand Down
1 change: 1 addition & 0 deletions src/Zcu/PerThread.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2763,6 +2763,7 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.is_const = e.is_const,
.is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage,
.is_dll_import = e.is_dll_import,
.alignment = e.alignment,
.@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index,
Expand Down
22 changes: 13 additions & 9 deletions src/codegen/llvm.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -3237,10 +3237,10 @@ pub const Object = struct {
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;
const is_extern, const is_threadlocal, const is_weak_linkage = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage },
else => .{ false, false, false },
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import },
else => .{ false, false, false, false },
};

const variable_index = try o.builder.addVariable(
Expand All@@ -3257,6 +3257,7 @@ pub const Object = struct {
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
} else {
variable_index.setLinkage(.internal, &o.builder);
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
Expand DownExpand Up@@ -4782,10 +4783,10 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index);
const resolved = nav.status.resolved;

const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, true, resolved.val, nav_index },
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, variable.lib_name, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index },
};
const ty = Type.fromInterned(nav.typeOf(ip));

Expand DownExpand Up@@ -4860,8 +4861,11 @@ pub const NavGen = struct {
try global_index.rename(decl_name, &o.builder);
global_index.setLinkage(.external, &o.builder);
global_index.setUnnamedAddr(.default, &o.builder);
if (zcu.comp.config.dll_export_fns)
if (is_dll_import) {
global_index.setDllStorageClass(.dllimport, &o.builder);
} else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
}

if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
}
Expand Down
4 changes: 4 additions & 0 deletions src/codegen/llvm/Builder.zig
Original file line numberDiff line numberDiff line change
Expand Up@@ -2528,6 +2528,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder);
}

pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder);
}

pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void {
return self.ptrConst(builder).global.setUnnamedAddr(unnamed_addr, builder);
}
Expand Down
18 changes: 18 additions & 0 deletions test/cases/compile_errors/builtin_extern_in_comptime_scope.zig
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
const foo_tl = @extern(*i32, .{ .name = "foo", .is_thread_local = true });
const foo_dll = @extern(*i32, .{ .name = "foo", .is_dll_import = true });
pub export fn entry() void {
_ = foo_tl;
}
pub export fn entry2() void {
_ = foo_dll;
}
// error
// backend=stage2
// target=native
//
// :1:16: error: unable to resolve comptime value
// :1:16: note: global variable initializer must be comptime-known
// :1:16: note: thread local and dll imported variables have runtime-known addresses
// :2:17: error: unable to resolve comptime value
// :2:17: note: global variable initializer must be comptime-known
// :2:17: note: thread local and dll imported variables have runtime-known addresses
19 changes: 15 additions & 4 deletions test/standalone/extern/build.zig
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const test_step = b.step("test", "Test it");
b.default_step = test_step;

const optimize: std.builtin.OptimizeMode = .Debug;

const obj = b.addObject(.{
Expand All@@ -9,12 +12,20 @@ pub fn build(b: *std.Build) void {
.target = b.graph.host,
.optimize = optimize,
});
const main = b.addTest(.{
const shared = b.addSharedLibrary(.{
.name = "shared",
.target = b.graph.host,
.optimize = optimize,
.link_libc = true,
});
if (b.graph.host.result.abi == .msvc) shared.defineCMacro("API", "__declspec(dllexport)");
shared.addCSourceFile(.{ .file = b.path("shared.c"), .flags = &.{} });
const test_exe = b.addTest(.{
.root_source_file = b.path("main.zig"),
.optimize = optimize,
});
main.addObject(obj);
test_exe.addObject(obj);
test_exe.linkLibrary(shared);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
test_step.dependOn(&b.addRunArtifact(test_exe).step);
}
7 changes: 6 additions & 1 deletion test/standalone/extern/main.zig
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const testing = @import("std").testing;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });
Expand All@@ -8,14 +9,18 @@ const T = extern struct { x: u32 };
test {
const mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
const const_val_ptr = @extern(*const T, .{ .name = "const_val" });
const shared_val_ptr = @extern(*c_int, .{ .name = "shared_val", .is_dll_import = true });

assert(getHidden() == 0);
updateHidden(123);
assert(getHidden() == 123);

assert(mut_val_ptr.* == 1.23);
mut_val_ptr.* = 10.0;
assert(mut_val_ptr.* == 10.0);

assert(const_val_ptr.x == 42);

assert(shared_val_ptr.* == 1234);
shared_val_ptr.* = 1235;
assert(shared_val_ptr.* == 1235);
}
5 changes: 5 additions & 0 deletions test/standalone/extern/shared.c
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
#ifndef API
#define API
#endif

API int shared_val = 1234;