mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
compiler: rework capture scopes in-memory layout
* Use 32-bit integers instead of pointers for compactness and serialization friendliness. * Use a separate hash map for runtime and comptime capture scopes, avoiding the 1-bit union tag. * Use a compact array representation instead of a tree of hash maps. * Eliminate the only instance of ref-counting in the compiler, instead relying on garbage collection (not implemented yet but is the plan for almost all long-lived objects related to incremental compilation). Because a code modification may need to access capture scope data, this makes capture scope data long-lived state. My goal is to get incremental compilation state serialization down to a single pwritev syscall, by unifying the on-disk representation with the in-memory representation. This commit eliminates the last remaining pointer field of `Module.Decl`.
This commit is contained in:
parent
4f952c7e0e
commit
8592c5cdac
131
src/Module.zig
131
src/Module.zig
@ -92,6 +92,17 @@ embed_table: std.StringHashMapUnmanaged(*EmbedFile) = .{},
|
||||
/// is not yet implemented.
|
||||
intern_pool: InternPool = .{},
|
||||
|
||||
/// The index type for this array is `CaptureScope.Index` and the elements here are
|
||||
/// the indexes of the parent capture scopes.
|
||||
/// Memory is owned by gpa; garbage collected.
|
||||
capture_scope_parents: std.ArrayListUnmanaged(CaptureScope.Index) = .{},
|
||||
/// Value is index of type
|
||||
/// Memory is owned by gpa; garbage collected.
|
||||
runtime_capture_scopes: std.AutoArrayHashMapUnmanaged(CaptureScope.Key, InternPool.Index) = .{},
|
||||
/// Value is index of value
|
||||
/// Memory is owned by gpa; garbage collected.
|
||||
comptime_capture_scopes: std.AutoArrayHashMapUnmanaged(CaptureScope.Key, InternPool.Index) = .{},
|
||||
|
||||
/// To be eliminated in a future commit by moving more data into InternPool.
|
||||
/// Current uses that must be eliminated:
|
||||
/// * Struct comptime_args
|
||||
@ -272,83 +283,26 @@ pub const Export = struct {
|
||||
};
|
||||
|
||||
pub const CaptureScope = struct {
|
||||
refs: u32,
|
||||
parent: ?*CaptureScope,
|
||||
|
||||
/// Values from this decl's evaluation that will be closed over in
|
||||
/// child decls. This map is backed by the gpa, and deinited when
|
||||
/// the refcount reaches 0.
|
||||
captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Capture) = .{},
|
||||
|
||||
pub const Capture = union(enum) {
|
||||
comptime_val: InternPool.Index, // index of value
|
||||
runtime_val: InternPool.Index, // index of type
|
||||
pub const Key = extern struct {
|
||||
zir_index: Zir.Inst.Index,
|
||||
index: Index,
|
||||
};
|
||||
|
||||
pub fn failed(noalias self: *const CaptureScope) bool {
|
||||
return self.captures.available == 0 and self.captures.size == std.math.maxInt(u32);
|
||||
}
|
||||
/// Index into `capture_scope_parents` which uniquely identifies a capture scope.
|
||||
pub const Index = enum(u32) {
|
||||
none = std.math.maxInt(u32),
|
||||
_,
|
||||
|
||||
pub fn fail(noalias self: *CaptureScope, gpa: Allocator) void {
|
||||
self.captures.deinit(gpa);
|
||||
self.captures.available = 0;
|
||||
self.captures.size = std.math.maxInt(u32);
|
||||
}
|
||||
|
||||
pub fn incRef(self: *CaptureScope) void {
|
||||
// TODO: wtf is reference counting doing in my beautiful codebase? 😠
|
||||
// seriously though, let's change this to rely on InternPool garbage
|
||||
// collection instead.
|
||||
self.refs += 1;
|
||||
}
|
||||
|
||||
pub fn decRef(self: *CaptureScope, gpa: Allocator) void {
|
||||
self.refs -= 1;
|
||||
if (self.refs > 0) return;
|
||||
if (self.parent) |p| p.decRef(gpa);
|
||||
if (!self.failed()) {
|
||||
self.captures.deinit(gpa);
|
||||
pub fn parent(i: Index, mod: *Module) Index {
|
||||
return mod.capture_scope_parents.items[@intFromEnum(i)];
|
||||
}
|
||||
gpa.destroy(self);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
pub const WipCaptureScope = struct {
|
||||
scope: *CaptureScope,
|
||||
finalized: bool,
|
||||
gpa: Allocator,
|
||||
|
||||
pub fn init(gpa: Allocator, parent: ?*CaptureScope) !WipCaptureScope {
|
||||
const scope = try gpa.create(CaptureScope);
|
||||
if (parent) |p| p.incRef();
|
||||
scope.* = .{ .refs = 1, .parent = parent };
|
||||
return .{
|
||||
.scope = scope,
|
||||
.finalized = false,
|
||||
.gpa = gpa,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn finalize(noalias self: *WipCaptureScope) !void {
|
||||
self.finalized = true;
|
||||
}
|
||||
|
||||
pub fn reset(noalias self: *WipCaptureScope, parent: ?*CaptureScope) !void {
|
||||
self.scope.decRef(self.gpa);
|
||||
self.scope = try self.gpa.create(CaptureScope);
|
||||
if (parent) |p| p.incRef();
|
||||
self.scope.* = .{ .refs = 1, .parent = parent };
|
||||
}
|
||||
|
||||
pub fn deinit(noalias self: *WipCaptureScope) void {
|
||||
if (self.finalized) {
|
||||
self.scope.decRef(self.gpa);
|
||||
} else {
|
||||
self.scope.fail(self.gpa);
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
};
|
||||
pub fn createCaptureScope(mod: *Module, parent: CaptureScope.Index) error{OutOfMemory}!CaptureScope.Index {
|
||||
try mod.capture_scope_parents.append(mod.gpa, parent);
|
||||
return @enumFromInt(mod.capture_scope_parents.items.len - 1);
|
||||
}
|
||||
|
||||
const ValueArena = struct {
|
||||
state: std.heap.ArenaAllocator.State,
|
||||
@ -413,7 +367,7 @@ pub const Decl = struct {
|
||||
/// The scope which lexically contains this decl. A decl must depend
|
||||
/// on its lexical parent, in order to ensure that this pointer is valid.
|
||||
/// This scope is allocated out of the arena of the parent decl.
|
||||
src_scope: ?*CaptureScope,
|
||||
src_scope: CaptureScope.Index,
|
||||
|
||||
/// An integer that can be checked against the corresponding incrementing
|
||||
/// generation field of Module. This is used to determine whether `complete` status
|
||||
@ -2893,6 +2847,10 @@ pub fn deinit(mod: *Module) void {
|
||||
mod.memoized_decls.deinit(gpa);
|
||||
mod.intern_pool.deinit(gpa);
|
||||
mod.tmp_hack_arena.deinit();
|
||||
|
||||
mod.capture_scope_parents.deinit(gpa);
|
||||
mod.runtime_capture_scopes.deinit(gpa);
|
||||
mod.comptime_capture_scopes.deinit(gpa);
|
||||
}
|
||||
|
||||
pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
||||
@ -2914,7 +2872,6 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
|
||||
mod.destroyNamespace(i);
|
||||
}
|
||||
}
|
||||
if (decl.src_scope) |scope| scope.decRef(gpa);
|
||||
decl.dependants.deinit(gpa);
|
||||
decl.dependencies.deinit(gpa);
|
||||
}
|
||||
@ -3909,7 +3866,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
|
||||
const new_namespace = mod.namespacePtr(new_namespace_index);
|
||||
errdefer mod.destroyNamespace(new_namespace_index);
|
||||
|
||||
const new_decl_index = try mod.allocateNewDecl(new_namespace_index, 0, null);
|
||||
const new_decl_index = try mod.allocateNewDecl(new_namespace_index, 0, .none);
|
||||
const new_decl = mod.declPtr(new_decl_index);
|
||||
errdefer @panic("TODO error handling");
|
||||
|
||||
@ -3984,11 +3941,7 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
|
||||
};
|
||||
defer sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, null);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
if (sema.analyzeStructDecl(new_decl, main_struct_inst, struct_index)) |_| {
|
||||
try wip_captures.finalize();
|
||||
for (comptime_mutable_decls.items) |decl_index| {
|
||||
const decl = mod.declPtr(decl_index);
|
||||
_ = try decl.internValue(mod);
|
||||
@ -4115,15 +4068,12 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var block_scope: Sema.Block = .{
|
||||
.parent = null,
|
||||
.sema = &sema,
|
||||
.src_decl = decl_index,
|
||||
.namespace = decl.src_namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -4137,7 +4087,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
|
||||
// We'll do some other bits with the Sema. Clear the type target index just in case they analyze any type.
|
||||
sema.builtin_type_target_index = .none;
|
||||
try wip_captures.finalize();
|
||||
for (comptime_mutable_decls.items) |ct_decl_index| {
|
||||
const ct_decl = mod.declPtr(ct_decl_index);
|
||||
_ = try ct_decl.internValue(mod);
|
||||
@ -5069,15 +5018,12 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
|
||||
try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
|
||||
sema.air_extra.items.len += reserved_count;
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var inner_block: Sema.Block = .{
|
||||
.parent = null,
|
||||
.sema = &sema,
|
||||
.src_decl = decl_index,
|
||||
.namespace = decl.src_namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = false,
|
||||
@ -5189,7 +5135,6 @@ pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocato
|
||||
};
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
for (comptime_mutable_decls.items) |ct_decl_index| {
|
||||
const ct_decl = mod.declPtr(ct_decl_index);
|
||||
_ = try ct_decl.internValue(mod);
|
||||
@ -5308,7 +5253,7 @@ pub fn allocateNewDecl(
|
||||
mod: *Module,
|
||||
namespace: Namespace.Index,
|
||||
src_node: Ast.Node.Index,
|
||||
src_scope: ?*CaptureScope,
|
||||
src_scope: CaptureScope.Index,
|
||||
) !Decl.Index {
|
||||
const ip = &mod.intern_pool;
|
||||
const gpa = mod.gpa;
|
||||
@ -5344,8 +5289,6 @@ pub fn allocateNewDecl(
|
||||
}
|
||||
}
|
||||
|
||||
if (src_scope) |scope| scope.incRef();
|
||||
|
||||
return decl_index;
|
||||
}
|
||||
|
||||
@ -5374,7 +5317,7 @@ pub fn createAnonymousDeclFromDecl(
|
||||
mod: *Module,
|
||||
src_decl: *Decl,
|
||||
namespace: Namespace.Index,
|
||||
src_scope: ?*CaptureScope,
|
||||
src_scope: CaptureScope.Index,
|
||||
tv: TypedValue,
|
||||
) !Decl.Index {
|
||||
const new_decl_index = try mod.allocateNewDecl(namespace, src_decl.src_node, src_scope);
|
||||
@ -5968,7 +5911,7 @@ pub fn populateTestFunctions(
|
||||
.len = test_decl_name.len,
|
||||
.child = .u8_type,
|
||||
});
|
||||
const test_name_decl_index = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, null, .{
|
||||
const test_name_decl_index = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, .none, .{
|
||||
.ty = test_name_decl_ty,
|
||||
.val = (try mod.intern(.{ .aggregate = .{
|
||||
.ty = test_name_decl_ty.toIntern(),
|
||||
@ -6015,7 +5958,7 @@ pub fn populateTestFunctions(
|
||||
.child = test_fn_ty.toIntern(),
|
||||
.sentinel = .none,
|
||||
});
|
||||
const array_decl_index = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, null, .{
|
||||
const array_decl_index = try mod.createAnonymousDeclFromDecl(decl, decl.src_namespace, .none, .{
|
||||
.ty = array_decl_ty,
|
||||
.val = (try mod.intern(.{ .aggregate = .{
|
||||
.ty = array_decl_ty.toIntern(),
|
||||
|
||||
194
src/Sema.zig
194
src/Sema.zig
@ -131,7 +131,6 @@ const CompileError = Module.CompileError;
|
||||
const SemaError = Module.SemaError;
|
||||
const Decl = Module.Decl;
|
||||
const CaptureScope = Module.CaptureScope;
|
||||
const WipCaptureScope = Module.WipCaptureScope;
|
||||
const LazySrcLoc = Module.LazySrcLoc;
|
||||
const RangeSet = @import("RangeSet.zig");
|
||||
const target_util = @import("target.zig");
|
||||
@ -308,7 +307,7 @@ pub const Block = struct {
|
||||
/// used to add a `func_instance` into the `InternPool`.
|
||||
params: std.MultiArrayList(Param) = .{},
|
||||
|
||||
wip_capture_scope: *CaptureScope,
|
||||
wip_capture_scope: CaptureScope.Index,
|
||||
|
||||
label: ?*Label = null,
|
||||
inlining: ?*Inlining,
|
||||
@ -951,21 +950,12 @@ fn analyzeBodyInner(
|
||||
// different values for the same Zir.Inst.Index, so in those cases, we will
|
||||
// have to create nested capture scopes; see the `.repeat` case below.
|
||||
const parent_capture_scope = block.wip_capture_scope;
|
||||
parent_capture_scope.incRef();
|
||||
var wip_captures: WipCaptureScope = .{
|
||||
.scope = parent_capture_scope,
|
||||
.gpa = sema.gpa,
|
||||
.finalized = true, // don't finalize the parent scope
|
||||
};
|
||||
defer wip_captures.deinit();
|
||||
|
||||
const mod = sema.mod;
|
||||
const map = &sema.inst_map;
|
||||
const tags = sema.code.instructions.items(.tag);
|
||||
const datas = sema.code.instructions.items(.data);
|
||||
|
||||
var orig_captures: usize = parent_capture_scope.captures.count();
|
||||
|
||||
var crash_info = crash_report.prepAnalyzeBody(sema, block, body);
|
||||
crash_info.push();
|
||||
defer crash_info.pop();
|
||||
@ -1500,16 +1490,11 @@ fn analyzeBodyInner(
|
||||
// Send comptime control flow back to the beginning of this block.
|
||||
const src = LazySrcLoc.nodeOffset(datas[inst].node);
|
||||
try sema.emitBackwardBranch(block, src);
|
||||
if (wip_captures.scope.captures.count() != orig_captures) {
|
||||
// We need to construct new capture scopes for the next loop iteration so it
|
||||
// can capture values without clobbering the earlier iteration's captures.
|
||||
// At first, we reused the parent capture scope as an optimization, but for
|
||||
// successive scopes we have to create new ones as children of the parent
|
||||
// scope.
|
||||
try wip_captures.reset(parent_capture_scope);
|
||||
block.wip_capture_scope = wip_captures.scope;
|
||||
orig_captures = 0;
|
||||
}
|
||||
|
||||
// We need to construct new capture scopes for the next loop iteration so it
|
||||
// can capture values without clobbering the earlier iteration's captures.
|
||||
block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope);
|
||||
|
||||
i = 0;
|
||||
continue;
|
||||
} else {
|
||||
@ -1520,16 +1505,11 @@ fn analyzeBodyInner(
|
||||
// Send comptime control flow back to the beginning of this block.
|
||||
const src = LazySrcLoc.nodeOffset(datas[inst].node);
|
||||
try sema.emitBackwardBranch(block, src);
|
||||
if (wip_captures.scope.captures.count() != orig_captures) {
|
||||
// We need to construct new capture scopes for the next loop iteration so it
|
||||
// can capture values without clobbering the earlier iteration's captures.
|
||||
// At first, we reused the parent capture scope as an optimization, but for
|
||||
// successive scopes we have to create new ones as children of the parent
|
||||
// scope.
|
||||
try wip_captures.reset(parent_capture_scope);
|
||||
block.wip_capture_scope = wip_captures.scope;
|
||||
orig_captures = 0;
|
||||
}
|
||||
|
||||
// We need to construct new capture scopes for the next loop iteration so it
|
||||
// can capture values without clobbering the earlier iteration's captures.
|
||||
block.wip_capture_scope = try mod.createCaptureScope(parent_capture_scope);
|
||||
|
||||
i = 0;
|
||||
continue;
|
||||
},
|
||||
@ -1803,12 +1783,9 @@ fn analyzeBodyInner(
|
||||
}
|
||||
if (noreturn_inst) |some| try block.instructions.append(sema.gpa, some);
|
||||
|
||||
if (!wip_captures.finalized) {
|
||||
// We've updated the capture scope due to a `repeat` instruction where
|
||||
// the body had a capture; finalize our child scope and reset
|
||||
try wip_captures.finalize();
|
||||
block.wip_capture_scope = parent_capture_scope;
|
||||
}
|
||||
// We may have overwritten the capture scope due to a `repeat` instruction where
|
||||
// the body had a capture; restore it now.
|
||||
block.wip_capture_scope = parent_capture_scope;
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -3157,15 +3134,12 @@ fn zirEnumDecl(
|
||||
sema.func_index = .none;
|
||||
defer sema.func_index = prev_func_index;
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, new_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var enum_block: Block = .{
|
||||
.parent = null,
|
||||
.sema = sema,
|
||||
.src_decl = new_decl_index,
|
||||
.namespace = new_namespace_index,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(new_decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -3176,8 +3150,6 @@ fn zirEnumDecl(
|
||||
try sema.analyzeBody(&enum_block, body);
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
if (tag_type_ref != .none) {
|
||||
const ty = try sema.resolveType(block, tag_ty_src, tag_type_ref);
|
||||
if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) {
|
||||
@ -7298,15 +7270,12 @@ fn analyzeCall(
|
||||
|
||||
try mod.declareDeclDependencyType(ics.callee().owner_decl_index, module_fn.owner_decl, .function_body);
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, fn_owner_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var child_block: Block = .{
|
||||
.parent = null,
|
||||
.sema = sema,
|
||||
.src_decl = module_fn.owner_decl,
|
||||
.namespace = fn_owner_decl.src_namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(fn_owner_decl.src_scope),
|
||||
.instructions = .{},
|
||||
.label = null,
|
||||
.inlining = &inlining,
|
||||
@ -7514,8 +7483,6 @@ fn analyzeCall(
|
||||
break :res2 result;
|
||||
};
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
break :res res2;
|
||||
} else res: {
|
||||
assert(!func_ty_info.is_generic);
|
||||
@ -7840,15 +7807,12 @@ fn instantiateGenericCall(
|
||||
};
|
||||
defer child_sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var child_block: Block = .{
|
||||
.parent = null,
|
||||
.sema = &child_sema,
|
||||
.src_decl = generic_owner_func.owner_decl,
|
||||
.namespace = namespace_index,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -8000,8 +7964,6 @@ fn instantiateGenericCall(
|
||||
const func_ty = callee.ty.toType();
|
||||
const func_ty_info = mod.typeToFunc(func_ty).?;
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
// If the call evaluated to a return type that requires comptime, never mind
|
||||
// our generic instantiation. Instead we need to perform a comptime call.
|
||||
if (try sema.typeRequiresComptime(func_ty_info.return_type.toType())) {
|
||||
@ -11897,11 +11859,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
||||
const body = sema.code.extra[extra_index..][0..info.body_len];
|
||||
extra_index += info.body_len;
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.wip_capture_scope = wip_captures.scope;
|
||||
case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
|
||||
|
||||
const item = case_vals.items[scalar_i];
|
||||
// `item` is already guaranteed to be constant known.
|
||||
@ -11929,8 +11888,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
||||
_ = try case_block.addNoOp(.unreach);
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
try cases_extra.ensureUnusedCapacity(gpa, 3 + case_block.instructions.items.len);
|
||||
cases_extra.appendAssumeCapacity(1); // items_len
|
||||
cases_extra.appendAssumeCapacity(@intCast(case_block.instructions.items.len));
|
||||
@ -12177,11 +12134,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
||||
var cond_body = try case_block.instructions.toOwnedSlice(gpa);
|
||||
defer gpa.free(cond_body);
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.wip_capture_scope = wip_captures.scope;
|
||||
case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
|
||||
|
||||
const body = sema.code.extra[extra_index..][0..info.body_len];
|
||||
extra_index += info.body_len;
|
||||
@ -12200,8 +12154,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
||||
);
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
if (is_first) {
|
||||
is_first = false;
|
||||
first_else_body = cond_body;
|
||||
@ -12407,11 +12359,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
||||
}),
|
||||
};
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, child_block.wip_capture_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
case_block.instructions.shrinkRetainingCapacity(0);
|
||||
case_block.wip_capture_scope = wip_captures.scope;
|
||||
case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
|
||||
|
||||
if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
|
||||
operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
|
||||
@ -12456,8 +12405,6 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
|
||||
}
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
|
||||
if (is_first) {
|
||||
final_else_body = case_block.instructions.items;
|
||||
} else {
|
||||
@ -16557,51 +16504,53 @@ fn zirThis(
|
||||
}
|
||||
|
||||
fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
|
||||
// Closures are not necessarily constant values. For example, the
|
||||
// code might do something like this:
|
||||
// fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; }
|
||||
// ...in which case the closure_capture instruction has access to a runtime
|
||||
// value only. In such case we preserve the type and use a dummy runtime value.
|
||||
// value only. In such case only the type is saved into the scope.
|
||||
const operand = try sema.resolveInst(inst_data.operand);
|
||||
const ty = sema.typeOf(operand);
|
||||
const capture: CaptureScope.Capture = blk: {
|
||||
if (try sema.resolveMaybeUndefValAllowVariables(operand)) |val| {
|
||||
const ip_index = try val.intern(ty, sema.mod);
|
||||
break :blk .{ .comptime_val = ip_index };
|
||||
}
|
||||
break :blk .{ .runtime_val = ty.toIntern() };
|
||||
const key: CaptureScope.Key = .{
|
||||
.zir_index = inst,
|
||||
.index = block.wip_capture_scope,
|
||||
};
|
||||
try block.wip_capture_scope.captures.putNoClobber(sema.gpa, inst, capture);
|
||||
if (try sema.resolveMaybeUndefValAllowVariables(operand)) |val| {
|
||||
try mod.comptime_capture_scopes.put(gpa, key, try val.intern(ty, mod));
|
||||
} else {
|
||||
try mod.runtime_capture_scopes.put(gpa, key, ty.toIntern());
|
||||
}
|
||||
}
|
||||
|
||||
fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const mod = sema.mod;
|
||||
const ip = &mod.intern_pool;
|
||||
//const ip = &mod.intern_pool;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].inst_node;
|
||||
var scope: *CaptureScope = mod.declPtr(block.src_decl).src_scope.?;
|
||||
var scope: CaptureScope.Index = mod.declPtr(block.src_decl).src_scope;
|
||||
assert(scope != .none);
|
||||
// Note: The target closure must be in this scope list.
|
||||
// If it's not here, the zir is invalid, or the list is broken.
|
||||
const capture = while (true) {
|
||||
const capture_ty = while (true) {
|
||||
// Note: We don't need to add a dependency here, because
|
||||
// decls always depend on their lexical parents.
|
||||
|
||||
// Fail this decl if a scope it depended on failed.
|
||||
if (scope.failed()) {
|
||||
if (sema.owner_func_index != .none) {
|
||||
ip.funcAnalysis(sema.owner_func_index).state = .dependency_failure;
|
||||
} else {
|
||||
sema.owner_decl.analysis = .dependency_failure;
|
||||
}
|
||||
return error.AnalysisFail;
|
||||
}
|
||||
if (scope.captures.get(inst_data.inst)) |capture| {
|
||||
break capture;
|
||||
}
|
||||
scope = scope.parent.?;
|
||||
const key: CaptureScope.Key = .{
|
||||
.zir_index = inst_data.inst,
|
||||
.index = scope,
|
||||
};
|
||||
if (mod.comptime_capture_scopes.get(key)) |val|
|
||||
return Air.internedToRef(val);
|
||||
if (mod.runtime_capture_scopes.get(key)) |ty|
|
||||
break ty;
|
||||
scope = scope.parent(mod);
|
||||
assert(scope != .none);
|
||||
};
|
||||
|
||||
if (capture == .runtime_val and !block.is_typeof and sema.func_index == .none) {
|
||||
// The comptime case is handled already above. Runtime case below.
|
||||
|
||||
if (!block.is_typeof and sema.func_index == .none) {
|
||||
const msg = msg: {
|
||||
const name = name: {
|
||||
const file = sema.owner_decl.getFileScope(mod);
|
||||
@ -16629,7 +16578,7 @@ fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
if (capture == .runtime_val and !block.is_typeof and !block.is_comptime and sema.func_index != .none) {
|
||||
if (!block.is_typeof and !block.is_comptime and sema.func_index != .none) {
|
||||
const msg = msg: {
|
||||
const name = name: {
|
||||
const file = sema.owner_decl.getFileScope(mod);
|
||||
@ -16659,16 +16608,9 @@ fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
switch (capture) {
|
||||
.runtime_val => |ty_ip_index| {
|
||||
assert(block.is_typeof);
|
||||
// We need a dummy runtime instruction with the correct type.
|
||||
return block.addTy(.alloc, ty_ip_index.toType());
|
||||
},
|
||||
.comptime_val => |val_ip_index| {
|
||||
return Air.internedToRef(val_ip_index);
|
||||
},
|
||||
}
|
||||
assert(block.is_typeof);
|
||||
// We need a dummy runtime instruction with the correct type.
|
||||
return block.addTy(.alloc, capture_ty.toType());
|
||||
}
|
||||
|
||||
fn zirRetAddr(
|
||||
@ -24988,7 +24930,7 @@ fn zirBuiltinExtern(
|
||||
|
||||
// TODO check duplicate extern
|
||||
|
||||
const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
|
||||
const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, .none);
|
||||
errdefer mod.destroyDecl(new_decl_index);
|
||||
const new_decl = mod.declPtr(new_decl_index);
|
||||
new_decl.name = options.name;
|
||||
@ -34327,15 +34269,12 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
|
||||
};
|
||||
defer sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var block: Block = .{
|
||||
.parent = null,
|
||||
.sema = &sema,
|
||||
.src_decl = decl_index,
|
||||
.namespace = struct_obj.namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -34356,7 +34295,6 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
|
||||
|
||||
try sema.checkBackingIntType(&block, backing_int_src, backing_int_ty, fields_bit_sum);
|
||||
struct_obj.backing_int_ty = backing_int_ty;
|
||||
try wip_captures.finalize();
|
||||
for (comptime_mutable_decls.items) |ct_decl_index| {
|
||||
const ct_decl = mod.declPtr(ct_decl_index);
|
||||
_ = try ct_decl.internValue(mod);
|
||||
@ -35018,15 +34956,12 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
};
|
||||
defer sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var block_scope: Block = .{
|
||||
.parent = null,
|
||||
.sema = &sema,
|
||||
.src_decl = decl_index,
|
||||
.namespace = struct_obj.namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -35283,7 +35218,6 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
|
||||
}
|
||||
}
|
||||
}
|
||||
try wip_captures.finalize();
|
||||
for (comptime_mutable_decls.items) |ct_decl_index| {
|
||||
const ct_decl = mod.declPtr(ct_decl_index);
|
||||
_ = try ct_decl.internValue(mod);
|
||||
@ -35361,15 +35295,12 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
|
||||
};
|
||||
defer sema.deinit();
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var block_scope: Block = .{
|
||||
.parent = null,
|
||||
.sema = &sema,
|
||||
.src_decl = decl_index,
|
||||
.namespace = union_type.namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -35380,7 +35311,6 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
|
||||
try sema.analyzeBody(&block_scope, body);
|
||||
}
|
||||
|
||||
try wip_captures.finalize();
|
||||
for (comptime_mutable_decls.items) |ct_decl_index| {
|
||||
const ct_decl = mod.declPtr(ct_decl_index);
|
||||
_ = try ct_decl.internValue(mod);
|
||||
@ -35823,18 +35753,16 @@ fn generateUnionTagTypeSimple(
|
||||
}
|
||||
|
||||
fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref {
|
||||
const mod = sema.mod;
|
||||
const gpa = sema.gpa;
|
||||
const src = LazySrcLoc.nodeOffset(0);
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(gpa, sema.owner_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var block: Block = .{
|
||||
.parent = null,
|
||||
.sema = sema,
|
||||
.src_decl = sema.owner_decl_index,
|
||||
.namespace = sema.owner_decl.src_namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
@ -35875,17 +35803,15 @@ fn getBuiltinDecl(sema: *Sema, block: *Block, name: []const u8) CompileError!Mod
|
||||
}
|
||||
|
||||
fn getBuiltinType(sema: *Sema, name: []const u8) CompileError!Type {
|
||||
const mod = sema.mod;
|
||||
const ty_inst = try sema.getBuiltin(name);
|
||||
|
||||
var wip_captures = try WipCaptureScope.init(sema.gpa, sema.owner_decl.src_scope);
|
||||
defer wip_captures.deinit();
|
||||
|
||||
var block: Block = .{
|
||||
.parent = null,
|
||||
.sema = sema,
|
||||
.src_decl = sema.owner_decl_index,
|
||||
.namespace = sema.owner_decl.src_namespace,
|
||||
.wip_capture_scope = wip_captures.scope,
|
||||
.wip_capture_scope = try mod.createCaptureScope(sema.owner_decl.src_scope),
|
||||
.instructions = .{},
|
||||
.inlining = null,
|
||||
.is_comptime = true,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user