Skip to content

Commit

Permalink
Sema: @extern fixes
Browse files Browse the repository at this point in the history
* There was an edge case where the arena could be destroyed twice on
  error: once from the arena itself and once from the decl destruction.

* The type of the created decl was incorrect (it should have been the
  pointer child type), but it's not required anyway, so it's now just
  initialized to anyopaque (which more accurately reflects what's
  actually at that memory, since e.g. [*]T may correspond to nothing).

* A runtime bitcast of the pointer was performed, meaning @extern didn't
  work at comptime. This is unnecessary: the decl_ref can just be
  initialized with the correct pointer type.
  • Loading branch information
mlugg authored and Vexu committed Mar 12, 2023
1 parent 948926c commit c93e0d8
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 29 deletions.
59 changes: 30 additions & 29 deletions src/Sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -22287,7 +22287,6 @@ fn zirBuiltinExtern(
extended: Zir.Inst.Extended.InstData,
) CompileError!Air.Inst.Ref {
const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
const src = LazySrcLoc.nodeOffset(extra.node);
const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };

Expand Down Expand Up @@ -22315,39 +22314,41 @@ fn zirBuiltinExtern(
const new_decl = sema.mod.declPtr(new_decl_index);
new_decl.name = try sema.gpa.dupeZ(u8, options.name);

var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
errdefer new_decl_arena.deinit();
const new_decl_arena_allocator = new_decl_arena.allocator();
{
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
errdefer new_decl_arena.deinit();
const new_decl_arena_allocator = new_decl_arena.allocator();

const new_var = try new_decl_arena_allocator.create(Module.Var);
errdefer new_decl_arena_allocator.destroy(new_var);
const new_var = try new_decl_arena_allocator.create(Module.Var);
new_var.* = .{
.owner_decl = sema.owner_decl_index,
.init = Value.initTag(.unreachable_value),
.is_extern = true,
.is_mutable = false,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .Weak,
.lib_name = null,
};

new_var.* = .{
.owner_decl = sema.owner_decl_index,
.init = Value.initTag(.unreachable_value),
.is_extern = true,
.is_mutable = false,
.is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .Weak,
.lib_name = null,
};
new_decl.src_line = sema.owner_decl.src_line;
// We only access this decl through the decl_ref with the correct type created
// below, so this type doesn't matter
new_decl.ty = Type.Tag.init(.anyopaque);
new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
new_decl.@"align" = 0;
new_decl.@"linksection" = null;
new_decl.has_tv = true;
new_decl.analysis = .complete;
new_decl.generation = sema.mod.generation;

new_decl.src_line = sema.owner_decl.src_line;
new_decl.ty = try ty.copy(new_decl_arena_allocator);
new_decl.val = try Value.Tag.variable.create(new_decl_arena_allocator, new_var);
new_decl.@"align" = 0;
new_decl.@"linksection" = null;
new_decl.has_tv = true;
new_decl.analysis = .complete;
new_decl.generation = sema.mod.generation;
try new_decl.finalizeNewArena(&new_decl_arena);
}

const arena_state = try new_decl_arena_allocator.create(std.heap.ArenaAllocator.State);
arena_state.* = new_decl_arena.state;
new_decl.value_arena = arena_state;
try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
try sema.ensureDeclAnalyzed(new_decl_index);

const ref = try sema.analyzeDeclRef(new_decl_index);
try sema.requireRuntimeBlock(block, src, null);
return block.addBitCast(ty, ref);
const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
return sema.addConstant(ty, ref);
}

fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void {
Expand Down
1 change: 1 addition & 0 deletions test/standalone.zig
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
cases.addBuildFile("test/standalone/emit_asm_and_bin/build.zig", .{});
cases.addBuildFile("test/standalone/issue_12588/build.zig", .{});
cases.addBuildFile("test/standalone/embed_generated_file/build.zig", .{});
cases.addBuildFile("test/standalone/extern/build.zig", .{});

cases.addBuildFile("test/standalone/dep_diamond/build.zig", .{});
cases.addBuildFile("test/standalone/dep_triangle/build.zig", .{});
Expand Down
20 changes: 20 additions & 0 deletions test/standalone/extern/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const optimize = b.standardOptimizeOption(.{});

const obj = b.addObject(.{
.name = "exports",
.root_source_file = .{ .path = "exports.zig" },
.target = .{},
.optimize = optimize,
});
const main = b.addTest(.{
.root_source_file = .{ .path = "main.zig" },
.optimize = optimize,
});
main.addObject(obj);

const test_step = b.step("test", "Test it");
test_step.dependOn(&main.step);
}
12 changes: 12 additions & 0 deletions test/standalone/extern/exports.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
var hidden: u32 = 0;
export fn updateHidden(val: u32) void {
hidden = val;
}
export fn getHidden() u32 {
return hidden;
}

const T = extern struct { x: u32 };

export var mut_val: f64 = 1.23;
export const const_val: T = .{ .x = 42 };
21 changes: 21 additions & 0 deletions test/standalone/extern/main.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const assert = @import("std").debug.assert;

const updateHidden = @extern(*const fn (u32) callconv(.C) void, .{ .name = "updateHidden" });
const getHidden = @extern(*const fn () callconv(.C) u32, .{ .name = "getHidden" });

const T = extern struct { x: u32 };

test {
var mut_val_ptr = @extern(*f64, .{ .name = "mut_val" });
var const_val_ptr = @extern(*const T, .{ .name = "const_val" });

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);
}

0 comments on commit c93e0d8

Please sign in to comment.