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
32 changes: 32 additions & 0 deletions lib/std/zig/Zir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -5066,3 +5066,35 @@ pub fn assertTrackable(zir: Zir, inst_idx: Zir.Inst.Index) void {
else => unreachable, // assertion failure; not trackable
}
}

pub fn typeCapturesLen(zir: Zir, type_decl: Inst.Index) u32 {
const inst = zir.instructions.get(@intFromEnum(type_decl));
assert(inst.tag == .extended);
switch (inst.data.extended.opcode) {
.struct_decl => {
const small: Inst.StructDecl.Small = @bitCast(inst.data.extended.small);
if (!small.has_captures_len) return 0;
const extra = zir.extraData(Inst.StructDecl, inst.data.extended.operand);
return zir.extra[extra.end];
},
.union_decl => {
const small: Inst.UnionDecl.Small = @bitCast(inst.data.extended.small);
if (!small.has_captures_len) return 0;
const extra = zir.extraData(Inst.UnionDecl, inst.data.extended.operand);
return zir.extra[extra.end + @intFromBool(small.has_tag_type)];
},
.enum_decl => {
const small: Inst.EnumDecl.Small = @bitCast(inst.data.extended.small);
if (!small.has_captures_len) return 0;
const extra = zir.extraData(Inst.EnumDecl, inst.data.extended.operand);
return zir.extra[extra.end + @intFromBool(small.has_tag_type)];
},
.opaque_decl => {
const small: Inst.OpaqueDecl.Small = @bitCast(inst.data.extended.small);
if (!small.has_captures_len) return 0;
const extra = zir.extraData(Inst.OpaqueDecl, inst.data.extended.operand);
return zir.extra[extra.end];
},
else => unreachable,
}
}
26 changes: 16 additions & 10 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3158,16 +3158,19 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
if (!refs.contains(anal_unit)) continue;
}

const file_index = switch (anal_unit.unwrap()) {
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
.nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
};
report_ok: {
const file_index = switch (anal_unit.unwrap()) {
.@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
.nav_val, .nav_ty => |nav| ip.getNav(nav).analysis.?.zir_index.resolveFile(ip),
.type => |ty| Type.fromInterned(ty).typeDeclInst(zcu).?.resolveFile(ip),
.func => |ip_index| zcu.funcInfo(ip_index).zir_body_inst.resolveFile(ip),
.memoized_state => break :report_ok, // always report std.builtin errors
};

// Skip errors for AnalUnits within files that had a parse failure.
// We'll try again once parsing succeeds.
if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
// Skip errors for AnalUnits within files that had a parse failure.
// We'll try again once parsing succeeds.
if (!zcu.fileByIndex(file_index).okToReportErrors()) continue;
}

std.log.scoped(.zcu).debug("analysis error '{s}' reported from unit '{}'", .{
error_msg.msg,
Expand Down Expand Up @@ -3391,7 +3394,7 @@ pub fn addModuleErrorMsg(
const ref = maybe_ref orelse break;
const gop = try seen.getOrPut(gpa, ref.referencer);
if (gop.found_existing) break;
if (ref_traces.items.len < max_references) {
if (ref_traces.items.len < max_references) skip: {
const src = ref.src.upgrade(zcu);
const source = try src.file_scope.getSource(gpa);
const span = try src.span(gpa);
Expand All @@ -3403,6 +3406,7 @@ pub fn addModuleErrorMsg(
.nav_val, .nav_ty => |nav| ip.getNav(nav).name.toSlice(ip),
.type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
.func => |f| ip.getNav(zcu.funcInfo(f).owner_nav).name.toSlice(ip),
.memoized_state => break :skip,
};
try ref_traces.append(gpa, .{
.decl_name = try eb.addString(name),
Expand Down Expand Up @@ -3670,6 +3674,7 @@ fn performAllTheWorkInner(
if (try zcu.findOutdatedToAnalyze()) |outdated| {
try comp.queueJob(switch (outdated.unwrap()) {
.func => |f| .{ .analyze_func = f },
.memoized_state,
.@"comptime",
.nav_ty,
.nav_val,
Expand Down Expand Up @@ -3737,6 +3742,7 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
.nav_ty => |nav| pt.ensureNavTypeUpToDate(nav),
.nav_val => |nav| pt.ensureNavValUpToDate(nav),
.type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
.memoized_state => |stage| pt.ensureMemoizedStateUpToDate(stage),
.func => unreachable,
};
maybe_err catch |err| switch (err) {
Expand Down
75 changes: 66 additions & 9 deletions src/InternPool.zig
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ namespace_deps: std.AutoArrayHashMapUnmanaged(TrackedInst.Index, DepEntry.Index)
/// Dependencies on the (non-)existence of some name in a namespace.
/// Value is index into `dep_entries` of the first dependency on this name.
namespace_name_deps: std.AutoArrayHashMapUnmanaged(NamespaceNameKey, DepEntry.Index),
// Dependencies on the value of fields memoized on `Zcu` (`panic_messages` etc).
// If set, these are indices into `dep_entries` of the first dependency on this state.
memoized_state_main_deps: DepEntry.Index.Optional,
memoized_state_panic_deps: DepEntry.Index.Optional,
memoized_state_va_list_deps: DepEntry.Index.Optional,

/// Given a `Depender`, points to an entry in `dep_entries` whose `depender`
/// matches. The `next_dependee` field can be used to iterate all such entries
Expand Down Expand Up @@ -87,6 +92,9 @@ pub const empty: InternPool = .{
.interned_deps = .empty,
.namespace_deps = .empty,
.namespace_name_deps = .empty,
.memoized_state_main_deps = .none,
.memoized_state_panic_deps = .none,
.memoized_state_va_list_deps = .none,
.first_dependency = .empty,
.dep_entries = .empty,
.free_dep_entries = .empty,
Expand Down Expand Up @@ -385,6 +393,7 @@ pub const AnalUnit = packed struct(u64) {
nav_ty,
type,
func,
memoized_state,
};

pub const Unwrapped = union(Kind) {
Expand All @@ -399,6 +408,8 @@ pub const AnalUnit = packed struct(u64) {
type: InternPool.Index,
/// This `AnalUnit` analyzes the body of the given runtime function.
func: InternPool.Index,
/// This `AnalUnit` resolves all state which is memoized in fields on `Zcu`.
memoized_state: MemoizedStateStage,
};

pub fn unwrap(au: AnalUnit) Unwrapped {
Expand Down Expand Up @@ -434,6 +445,16 @@ pub const AnalUnit = packed struct(u64) {
};
};

pub const MemoizedStateStage = enum(u32) {
/// Everything other than panics and `VaList`.
main,
/// Everything within `std.builtin.Panic`.
/// Since the panic handler is user-provided, this must be able to reference the other memoized state.
panic,
/// Specifically `std.builtin.VaList`. See `Zcu.BuiltinDecl.stage`.
va_list,
};

pub const ComptimeUnit = extern struct {
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
Expand Down Expand Up @@ -769,6 +790,7 @@ pub const Dependee = union(enum) {
interned: Index,
namespace: TrackedInst.Index,
namespace_name: NamespaceNameKey,
memoized_state: MemoizedStateStage,
};

pub fn removeDependenciesForDepender(ip: *InternPool, gpa: Allocator, depender: AnalUnit) void {
Expand Down Expand Up @@ -819,6 +841,11 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
.interned => |x| ip.interned_deps.get(x),
.namespace => |x| ip.namespace_deps.get(x),
.namespace_name => |x| ip.namespace_name_deps.get(x),
.memoized_state => |stage| switch (stage) {
.main => ip.memoized_state_main_deps.unwrap(),
.panic => ip.memoized_state_panic_deps.unwrap(),
.va_list => ip.memoized_state_va_list_deps.unwrap(),
},
} orelse return .{
.ip = ip,
.next_entry = .none,
Expand Down Expand Up @@ -848,6 +875,33 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
// This block should allocate an entry and prepend it to the relevant `*_deps` list.
// The `next` field should be correctly initialized; all other fields may be undefined.
const new_index: DepEntry.Index = switch (dependee) {
.memoized_state => |stage| new_index: {
const deps = switch (stage) {
.main => &ip.memoized_state_main_deps,
.panic => &ip.memoized_state_panic_deps,
.va_list => &ip.memoized_state_va_list_deps,
};

if (deps.unwrap()) |first| {
if (ip.dep_entries.items[@intFromEnum(first)].depender == .none) {
// Dummy entry, so we can reuse it rather than allocating a new one!
break :new_index first;
}
}

// Prepend a new dependency.
const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {
break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };
} else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };
if (deps.unwrap()) |old_first| {
ptr.next = old_first.toOptional();
ip.dep_entries.items[@intFromEnum(old_first)].prev = new_index.toOptional();
} else {
ptr.next = .none;
}
deps.* = new_index.toOptional();
break :new_index new_index;
},
inline else => |dependee_payload, tag| new_index: {
const gop = try switch (tag) {
.file => ip.file_deps,
Expand All @@ -857,6 +911,7 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
.interned => ip.interned_deps,
.namespace => ip.namespace_deps,
.namespace_name => ip.namespace_name_deps,
.memoized_state => comptime unreachable,
}.getOrPut(gpa, dependee_payload);

if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {
Expand Down Expand Up @@ -2029,15 +2084,7 @@ pub const Key = union(enum) {
pub const NamespaceType = union(enum) {
/// This type corresponds to an actual source declaration, e.g. `struct { ... }`.
/// It is hashed based on its ZIR instruction index and set of captures.
declared: struct {
/// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction.
zir_index: TrackedInst.Index,
/// The captured values of this type. These values must be fully resolved per the language spec.
captures: union(enum) {
owned: CaptureValue.Slice,
external: []const CaptureValue,
},
},
declared: Declared,
/// This type is an automatically-generated enum tag type for a union.
/// It is hashed based on the index of the union type it corresponds to.
generated_tag: struct {
Expand All @@ -2053,6 +2100,16 @@ pub const Key = union(enum) {
/// A hash of this type's attributes, fields, etc, generated by Sema.
type_hash: u64,
},

pub const Declared = struct {
/// A `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl` instruction.
zir_index: TrackedInst.Index,
/// The captured values of this type. These values must be fully resolved per the language spec.
captures: union(enum) {
owned: CaptureValue.Slice,
external: []const CaptureValue,
},
};
};

pub const FuncType = struct {
Expand Down
Loading