Commit 40aafcd6a8
Changed files (7)
src/link/Dwarf.zig
@@ -2261,8 +2261,8 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst);
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
@@ -2292,8 +2292,8 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst);
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
@@ -2321,8 +2321,8 @@ pub fn initWipNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool.Nav.In
assert(file.zir_loaded);
const decl = file.zir.getDeclaration(inst_info.inst);
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
@@ -2563,8 +2563,8 @@ pub fn updateComptimeNav(dwarf: *Dwarf, pt: Zcu.PerThread, nav_index: InternPool
return;
}
- const parent_type, const accessibility: u8 = if (nav.analysis_owner.unwrap()) |cau| parent: {
- const parent_namespace_ptr = ip.namespacePtr(ip.getCau(cau).namespace);
+ const parent_type, const accessibility: u8 = if (nav.analysis) |a| parent: {
+ const parent_namespace_ptr = ip.namespacePtr(a.namespace);
break :parent .{
parent_namespace_ptr.owner_type,
if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private,
src/Zcu/PerThread.zig
@@ -545,144 +545,173 @@ pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void {
pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
const file_root_type = pt.zcu.fileRootType(file_index);
if (file_root_type != .none) {
- _ = try pt.ensureTypeUpToDate(file_root_type, false);
+ _ = try pt.ensureTypeUpToDate(file_root_type);
} else {
return pt.semaFile(file_index);
}
}
-/// This ensures that the state of the `Cau`, and of its corresponding `Nav` or type,
-/// is fully up-to-date. Note that the type of the `Nav` may not be fully resolved.
-/// Returns `error.AnalysisFail` if the `Cau` has an error.
-pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu.SemaError!void {
+/// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis
+/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
+/// free to ignore this, since the error is already registered.
+pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.SemaError!void {
const tracy = trace(@src());
defer tracy.end();
const zcu = pt.zcu;
const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
- const cau = ip.getCau(cau_index);
+ const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
- log.debug("ensureCauAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
+ log.debug("ensureComptimeUnitUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
assert(!zcu.analysis_in_progress.contains(anal_unit));
- // Determine whether or not this Cau is outdated, i.e. requires re-analysis
- // even if `complete`. If a Cau is PO, we pessismistically assume that it
- // *does* require re-analysis, to ensure that the Cau is definitely
- // up-to-date when this function returns.
-
- // If analysis occurs in a poor order, this could result in over-analysis.
- // We do our best to avoid this by the other dependency logic in this file
- // which tries to limit re-analysis to Caus whose previously listed
- // dependencies are all up-to-date.
+ // Determine whether or not this `ComptimeUnit` is outdated. For this kind of `AnalUnit`, that's
+ // the only indicator as to whether or not analysis is required; when a `ComptimeUnit` is first
+ // created, it's marked as outdated.
+ //
+ // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
+ // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
+ // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
+ // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
- const cau_outdated = zcu.outdated.swapRemove(anal_unit) or
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
- const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
-
- if (cau_outdated) {
+ if (was_outdated) {
_ = zcu.outdated_ready.swapRemove(anal_unit);
- } else {
- // We can trust the current information about this `Cau`.
- if (prev_failed) {
- return error.AnalysisFail;
- }
- // If it wasn't failed and wasn't marked outdated, then either...
- // * it is a type and is up-to-date, or
- // * it is a `comptime` decl and is up-to-date, or
- // * it is another decl and is EITHER up-to-date OR never-referenced (so unresolved)
- // We just need to check for that last case.
- switch (cau.owner.unwrap()) {
- .type, .none => return,
- .nav => |nav| if (ip.getNav(nav).status == .resolved) return,
+ // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`.
+ if (dev.env.supports(.incremental)) {
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
}
+ } else {
+ // We can trust the current information about this unit.
+ if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail;
+ return;
}
- const sema_result: SemaCauResult, const analysis_fail = if (pt.ensureCauAnalyzedInner(cau_index, cau_outdated)) |result|
- // This `Cau` has gone from failed to success, so even if the value of the owner `Nav` didn't actually
- // change, we need to invalidate the dependencies anyway.
- .{ .{
- .invalidate_decl_val = result.invalidate_decl_val or prev_failed,
- .invalidate_decl_ref = result.invalidate_decl_ref or prev_failed,
- }, false }
- else |err| switch (err) {
- error.AnalysisFail => res: {
+ const unit_prog_node = zcu.sema_prog_node.start("comptime", 0);
+ defer unit_prog_node.end();
+
+ return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) {
+ error.AnalysisFail => {
if (!zcu.failed_analysis.contains(anal_unit)) {
- // If this `Cau` caused the error, it would have an entry in `failed_analysis`.
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
// Since it does not, this must be a transitive failure.
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
}
- // We consider this `Cau` to be outdated if:
- // * Previous analysis succeeded; in this case, we need to re-analyze dependants to ensure
- // they hit a transitive error here, rather than reporting a different error later (which
- // may now be invalid).
- // * The `Cau` is a type; in this case, the declaration site may require re-analysis to
- // construct a valid type.
- const outdated = !prev_failed or cau.owner.unwrap() == .type;
- break :res .{ .{
- .invalidate_decl_val = outdated,
- .invalidate_decl_ref = outdated,
- }, true };
+ return error.AnalysisFail;
},
- error.OutOfMemory => res: {
- try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
- try zcu.retryable_failures.ensureUnusedCapacity(gpa, 1);
- const msg = try Zcu.ErrorMsg.create(
- gpa,
- .{ .base_node_inst = cau.zir_index, .offset = Zcu.LazySrcLoc.Offset.nodeOffset(0) },
- "unable to analyze: OutOfMemory",
- .{},
- );
- zcu.retryable_failures.appendAssumeCapacity(anal_unit);
- zcu.failed_analysis.putAssumeCapacityNoClobber(anal_unit, msg);
- break :res .{ .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- }, true };
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
},
+ error.GenericPoison => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
};
-
- if (cau_outdated) {
- // TODO: we do not yet have separate dependencies for decl values vs types.
- const invalidate = sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref;
- const dependee: InternPool.Dependee = switch (cau.owner.unwrap()) {
- .none => return, // there are no dependencies on a `comptime` decl!
- .nav => |nav_index| .{ .nav_val = nav_index },
- .type => |ty| .{ .interned = ty },
- };
-
- if (invalidate) {
- // This dependency was marked as PO, meaning dependees were waiting
- // on its analysis result, and it has turned out to be outdated.
- // Update dependees accordingly.
- try zcu.markDependeeOutdated(.marked_po, dependee);
- } else {
- // This dependency was previously PO, but turned out to be up-to-date.
- // We do not need to queue successive analysis.
- try zcu.markPoDependeeUpToDate(dependee);
- }
- }
-
- if (analysis_fail) return error.AnalysisFail;
}
-fn ensureCauAnalyzedInner(
- pt: Zcu.PerThread,
- cau_index: InternPool.Cau.Index,
- cau_outdated: bool,
-) Zcu.SemaError!SemaCauResult {
+/// Re-analyzes a `ComptimeUnit`. The unit has already been determined to be out-of-date, and old
+/// side effects (exports/references/etc) have been dropped. If semantic analysis fails, this
+/// function will return `error.AnalysisFail`, and it is the caller's reponsibility to add an entry
+/// to `transitive_failed_analysis` if necessary.
+fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.CompileError!void {
const zcu = pt.zcu;
+ const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const cau = ip.getCau(cau_index);
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
+ const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id });
+ const comptime_unit = ip.getComptimeUnit(cu_id);
+
+ log.debug("analyzeComptimeUnit {}", .{zcu.fmtAnalUnit(anal_unit)});
+
+ const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_resolved.file);
+ // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
+ // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
+ // in `ensureComptimeUnitUpToDate`.
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ // We are about to re-analyze this unit; drop its depenndencies.
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ defer assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
+
+ var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
+
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
+
+ // The comptime unit declares on the source of the corresponding `comptime` declaration.
+ try sema.declareDependency(.{ .src_hash = comptime_unit.zir_index });
+
+ var block: Sema.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = comptime_unit.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = comptime_unit.zir_index,
+ .type_name_ctx = try ip.getOrPutStringFmt(gpa, pt.tid, "{}.comptime", .{
+ Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip).fmt(ip),
+ }, .no_embedded_nulls),
+ };
+ defer block.instructions.deinit(gpa);
+
+ const zir_decl = zir.getDeclaration(inst_resolved.inst);
+ assert(zir_decl.kind == .@"comptime");
+ assert(zir_decl.type_body == null);
+ assert(zir_decl.align_body == null);
+ assert(zir_decl.linksection_body == null);
+ assert(zir_decl.addrspace_body == null);
+ const value_body = zir_decl.value_body.?;
+
+ const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ assert(result_ref == .void_value); // AstGen should always uphold this
+
+ // Nothing else to do -- for a comptime decl, all we care about are the side effects.
+ // Just make sure to `flushExports`.
+ try sema.flushExports();
+}
- const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+/// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis
+/// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is
+/// free to ignore this, since the error is already registered.
+pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
// TODO: document this elsewhere mlugg!
// For my own benefit, here's how a namespace update for a normal (non-file-root) type works:
@@ -692,821 +721,826 @@ fn ensureCauAnalyzedInner(
// * Any change to the `struct` body -- including changing a declaration -- invalidates this
// * `S` is re-analyzed, but notes:
// * there is an existing struct instance (at this `TrackedInst` with these captures)
- // * the struct's `Cau` is up-to-date (because nothing about the fields changed)
+ // * the struct's resolution is up-to-date (because nothing about the fields changed)
// * so, it uses the same `struct`
// * but this doesn't stop it from updating the namespace!
// * we basically do `scanDecls`, updating the namespace as needed
// * so everyone lived happily ever after
- if (zcu.fileByIndex(inst_info.file).status != .success_zir) {
- return error.AnalysisFail;
- }
-
- // `cau_outdated` can be true in the initial update for `comptime` declarations,
- // so this isn't a `dev.check`.
- if (cau_outdated and dev.env.supports(.incremental)) {
- // The exports this `Cau` performs will be re-discovered, so we remove them here
- // prior to re-analysis.
- zcu.deleteUnitExports(anal_unit);
- zcu.deleteUnitReferences(anal_unit);
- if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
- kv.value.destroy(zcu.gpa);
- }
- _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
- }
-
- const decl_prog_node = zcu.sema_prog_node.start(switch (cau.owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).fqn.toSlice(ip),
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
- .none => "comptime",
- }, 0);
- defer decl_prog_node.end();
-
- return pt.semaCau(cau_index) catch |err| switch (err) {
- error.GenericPoison, error.ComptimeBreak, error.ComptimeReturn => unreachable,
- error.AnalysisFail, error.OutOfMemory => |e| return e,
- };
-}
-
-pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
- dev.check(.sema);
-
- const tracy = trace(@src());
- defer tracy.end();
-
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- // We only care about the uncoerced function.
- const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
- const anal_unit = AnalUnit.wrap(.{ .func = func_index });
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
+ const nav = ip.getNav(nav_id);
- log.debug("ensureFuncBodyAnalyzed {}", .{zcu.fmtAnalUnit(anal_unit)});
+ log.debug("ensureNavUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
- const func = zcu.funcInfo(maybe_coerced_func_index);
+ // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the
+ // status is `.unresolved`, which indicates that the value is outdated because it has *never*
+ // been analyzed so far.
+ //
+ // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to
+ // ensure that the unit is definitely up-to-date when this function returns. This mechanism could
+ // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by
+ // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`.
- const func_outdated = zcu.outdated.swapRemove(anal_unit) or
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
- const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
+ const prev_failed = zcu.failed_analysis.contains(anal_unit) or
+ zcu.transitive_failed_analysis.contains(anal_unit);
- if (func_outdated) {
+ if (was_outdated) {
+ dev.check(.incremental);
_ = zcu.outdated_ready.swapRemove(anal_unit);
- } else {
- // We can trust the current information about this function.
- if (prev_failed) {
- return error.AnalysisFail;
- }
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // this is the first reference
- .queued => {}, // we're waiting on first-time analysis
- .analyzed => return, // up-to-date
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
}
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ } else {
+ // We can trust the current information about this unit.
+ if (prev_failed) return error.AnalysisFail;
+ if (nav.status == .resolved) return;
}
- const ies_outdated, const analysis_fail = if (pt.ensureFuncBodyAnalyzedInner(func_index, func_outdated)) |result|
- .{ result.ies_outdated, false }
- else |err| switch (err) {
+ const unit_prog_node = zcu.sema_prog_node.start(nav.fqn.toSlice(ip), 0);
+ defer unit_prog_node.end();
+
+ const sema_result: SemaNavResult, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: {
+ break :res .{
+ .{
+ // If the unit has gone from failed to success, we still need to invalidate the dependencies.
+ .invalidate_nav_val = result.invalidate_nav_val or prev_failed,
+ .invalidate_nav_ref = result.invalidate_nav_ref or prev_failed,
+ },
+ false,
+ };
+ } else |err| switch (err) {
error.AnalysisFail => res: {
if (!zcu.failed_analysis.contains(anal_unit)) {
- // If this function caused the error, it would have an entry in `failed_analysis`.
+ // If this unit caused the error, it would have an entry in `failed_analysis`.
// Since it does not, this must be a transitive failure.
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
}
- // We consider the IES to be outdated if the function previously succeeded analysis; in this case,
- // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting
- // a different error later (which may now be invalid).
- break :res .{ !prev_failed, true };
+ break :res .{ .{
+ .invalidate_nav_val = !prev_failed,
+ .invalidate_nav_ref = !prev_failed,
+ }, true };
},
- error.OutOfMemory => return error.OutOfMemory, // TODO: graceful handling like `ensureCauAnalyzed`
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
+ },
+ error.GenericPoison => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.ComptimeBreak => unreachable,
};
- if (func_outdated) {
- if (ies_outdated) {
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
+ if (was_outdated) {
+ // TODO: we do not yet have separate dependencies for Nav values vs types.
+ const invalidate = sema_result.invalidate_nav_val or sema_result.invalidate_nav_ref;
+ const dependee: InternPool.Dependee = .{ .nav_val = nav_id };
+ if (invalidate) {
+ // This dependency was marked as PO, meaning dependees were waiting
+ // on its analysis result, and it has turned out to be outdated.
+ // Update dependees accordingly.
+ try zcu.markDependeeOutdated(.marked_po, dependee);
} else {
- try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
+ // This dependency was previously PO, but turned out to be up-to-date.
+ // We do not need to queue successive analysis.
+ try zcu.markPoDependeeUpToDate(dependee);
}
}
- if (analysis_fail) return error.AnalysisFail;
+ if (new_failed) return error.AnalysisFail;
}
-fn ensureFuncBodyAnalyzedInner(
- pt: Zcu.PerThread,
- func_index: InternPool.Index,
- func_outdated: bool,
-) Zcu.SemaError!struct { ies_outdated: bool } {
+const SemaNavResult = packed struct {
+ /// Whether the value of a `decl_val` of the corresponding Nav changed.
+ invalidate_nav_val: bool,
+ /// Whether the type of a `decl_ref` of the corresponding Nav changed.
+ invalidate_nav_ref: bool,
+};
+
+fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!SemaNavResult {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const func = zcu.funcInfo(func_index);
- const anal_unit = AnalUnit.wrap(.{ .func = func_index });
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
+ const old_nav = ip.getNav(nav_id);
- // Make sure that this function is still owned by the same `Nav`. Otherwise, analyzing
- // it would be a waste of time in the best case, and could cause codegen to give bogus
- // results in the worst case.
+ log.debug("analyzeNavVal {}", .{zcu.fmtAnalUnit(anal_unit)});
- if (func.generic_owner == .none) {
- // Among another things, this ensures that the function's `zir_body_inst` is correct.
- try pt.ensureCauAnalyzed(ip.getNav(func.owner_nav).analysis_owner.unwrap().?);
- if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
- // This function is no longer referenced! There's no point in re-analyzing it.
- // Just mark a transitive failure and move on.
- return error.AnalysisFail;
- }
- } else {
- const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
- // Among another things, this ensures that the function's `zir_body_inst` is correct.
- try pt.ensureCauAnalyzed(ip.getNav(go_nav).analysis_owner.unwrap().?);
- if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
- // The generic owner is no longer referenced, so this function is also unreferenced.
- // There's no point in re-analyzing it. Just mark a transitive failure and move on.
- return error.AnalysisFail;
- }
- }
+ const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_resolved.file);
+ // TODO: stop the compiler ever reaching Sema if there are failed files. That way, this check is
+ // unnecessary, and we can move the below `removeDependenciesForDepender` call up with its friends
+ // in `ensureComptimeUnitUpToDate`.
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
- // We'll want to remember what the IES used to be before the update for
- // dependency invalidation purposes.
- const old_resolved_ies = if (func.analysisUnordered(ip).inferred_error_set)
- func.resolvedErrorSetUnordered(ip)
- else
- .none;
+ // We are about to re-analyze this unit; drop its depenndencies.
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
- if (func_outdated) {
- dev.check(.incremental);
- zcu.deleteUnitExports(anal_unit);
- zcu.deleteUnitReferences(anal_unit);
- if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
- kv.value.destroy(gpa);
- }
- _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
- }
+ try zcu.analysis_in_progress.put(gpa, anal_unit, {});
+ errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
- if (!func_outdated) {
- // We can trust the current information about this function.
- if (zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit)) {
- return error.AnalysisFail;
- }
- switch (func.analysisUnordered(ip).state) {
- .unreferenced => {}, // this is the first reference
- .queued => {}, // we're waiting on first-time analysis
- .analyzed => return .{ .ies_outdated = false }, // up-to-date
- }
- }
+ var analysis_arena: std.heap.ArenaAllocator = .init(gpa);
+ defer analysis_arena.deinit();
- log.debug("analyze and generate fn body {}; reason='{s}'", .{
- zcu.fmtAnalUnit(anal_unit),
- if (func_outdated) "outdated" else "never analyzed",
- });
+ var comptime_err_ret_trace: std.ArrayList(Zcu.LazySrcLoc) = .init(gpa);
+ defer comptime_err_ret_trace.deinit();
- var air = try pt.analyzeFnBody(func_index);
- errdefer air.deinit(gpa);
+ var sema: Sema = .{
+ .pt = pt,
+ .gpa = gpa,
+ .arena = analysis_arena.allocator(),
+ .code = zir,
+ .owner = anal_unit,
+ .func_index = .none,
+ .func_is_naked = false,
+ .fn_ret_ty = .void,
+ .fn_ret_ty_ies = null,
+ .comptime_err_ret_trace = &comptime_err_ret_trace,
+ };
+ defer sema.deinit();
- const ies_outdated = func_outdated and
- (!func.analysisUnordered(ip).inferred_error_set or func.resolvedErrorSetUnordered(ip) != old_resolved_ies);
+ // The comptime unit declares on the source of the corresponding declaration.
+ try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index });
- const comp = zcu.comp;
+ var block: Sema.Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = old_nav.analysis.?.namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = old_nav.analysis.?.zir_index,
+ .type_name_ctx = old_nav.fqn,
+ };
+ defer block.instructions.deinit(gpa);
- const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
- const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null);
+ const zir_decl = zir.getDeclaration(inst_resolved.inst);
- if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
- air.deinit(gpa);
- return .{ .ies_outdated = ies_outdated };
- }
+ assert(old_nav.is_usingnamespace == (zir_decl.kind == .@"usingnamespace"));
- // This job depends on any resolve_type_fully jobs queued up before it.
- try comp.queueJob(.{ .codegen_func = .{
- .func = func_index,
- .air = air,
- } });
+ const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
+ const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
+ const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
+ const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
+ const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
- return .{ .ies_outdated = ies_outdated };
-}
+ // First, we must resolve the declaration's type. To do this, we analyze the type body if available,
+ // or otherwise, we analyze the value body, populating `early_val` in the process.
-/// Takes ownership of `air`, even on error.
-/// If any types referenced by `air` are unresolved, marks the codegen as failed.
-pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Air) Allocator.Error!void {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const comp = zcu.comp;
+ const nav_ty: Type, const early_val: ?Value = if (zir_decl.type_body) |type_body| ty: {
+ // We evaluate only the type now; no need for the value yet.
+ const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst);
+ const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
+ break :ty .{ .fromInterned(type_ref.toInterned().?), null };
+ } else ty: {
+ // We don't have a type body, so we need to evaluate the value immediately.
+ const value_body = zir_decl.value_body.?;
+ const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ const val = try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ break :ty .{ val.typeOf(zcu), val };
+ };
- defer {
- var air_mut = air;
- air_mut.deinit(gpa);
+ switch (zir_decl.kind) {
+ .@"comptime" => unreachable, // this is not a Nav
+ .unnamed_test, .@"test", .decltest => assert(nav_ty.zigTypeTag(zcu) == .@"fn"),
+ .@"usingnamespace" => {},
+ .@"const" => {},
+ .@"var" => try sema.validateVarType(
+ &block,
+ if (zir_decl.type_body != null) ty_src else init_src,
+ nav_ty,
+ zir_decl.linkage == .@"extern",
+ ),
}
- const func = zcu.funcInfo(func_index);
- const nav_index = func.owner_nav;
- const nav = ip.getNav(nav_index);
-
- var liveness = try Liveness.analyze(gpa, air, ip);
- defer liveness.deinit(gpa);
+ // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
+ // the full pointer type of this declaration.
- if (build_options.enable_debug_extensions and comp.verbose_air) {
- std.debug.print("# Begin Function AIR: {}:\n", .{nav.fqn.fmt(ip)});
- @import("../print_air.zig").dump(pt, air, liveness);
- std.debug.print("# End Function AIR: {}\n\n", .{nav.fqn.fmt(ip)});
- }
+ const alignment: InternPool.Alignment = a: {
+ const align_body = zir_decl.align_body orelse break :a .none;
+ const align_ref = try sema.resolveInlineBody(&block, align_body, inst_resolved.inst);
+ break :a try sema.analyzeAsAlign(&block, align_src, align_ref);
+ };
- if (std.debug.runtime_safety) {
- var verify: Liveness.Verify = .{
- .gpa = gpa,
- .air = air,
- .liveness = liveness,
- .intern_pool = ip,
- };
- defer verify.deinit();
+ const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
+ const linksection_body = zir_decl.linksection_body orelse break :ls .none;
+ const linksection_ref = try sema.resolveInlineBody(&block, linksection_body, inst_resolved.inst);
+ const bytes = try sema.toConstString(&block, section_src, linksection_ref, .{
+ .needed_comptime_reason = "linksection must be comptime-known",
+ });
+ if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
+ return sema.fail(&block, section_src, "linksection cannot contain null bytes", .{});
+ } else if (bytes.len == 0) {
+ return sema.fail(&block, section_src, "linksection cannot be empty", .{});
+ }
+ break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
+ };
- verify.verify() catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
- gpa,
- zcu.navSrcLoc(nav_index),
- "invalid liveness: {s}",
- .{@errorName(err)},
- ));
- return;
+ const @"addrspace": std.builtin.AddressSpace = as: {
+ const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
+ .@"var" => .variable,
+ else => switch (nav_ty.zigTypeTag(zcu)) {
+ .@"fn" => .function,
+ else => .constant,
},
};
+ const target = zcu.getTarget();
+ const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
+ .function => target_util.defaultAddressSpace(target, .function),
+ .variable => target_util.defaultAddressSpace(target, .global_mutable),
+ .constant => target_util.defaultAddressSpace(target, .global_constant),
+ else => unreachable,
+ };
+ const addrspace_ref = try sema.resolveInlineBody(&block, addrspace_body, inst_resolved.inst);
+ break :as try sema.analyzeAsAddressSpace(&block, addrspace_src, addrspace_ref, addrspace_ctx);
+ };
+
+ // Lastly, we must evaluate the value if we have not already done so. Note, however, that extern declarations
+ // don't have an associated value body.
+
+ const final_val: ?Value = early_val orelse if (zir_decl.value_body) |value_body| val: {
+ // Put the resolved type into `inst_map` to be used as the result type of the init.
+ try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst});
+ sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(nav_ty.toIntern()));
+ const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst);
+ assert(sema.inst_map.remove(inst_resolved.inst));
+
+ const result_ref = try sema.coerce(&block, nav_ty, uncoerced_result_ref, init_src);
+ break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
+ } else null;
+
+ const nav_val: Value = switch (zir_decl.linkage) {
+ .normal, .@"export" => switch (zir_decl.kind) {
+ .@"var" => .fromInterned(try pt.intern(.{ .variable = .{
+ .ty = nav_ty.toIntern(),
+ .init = final_val.?.toIntern(),
+ .owner_nav = nav_id,
+ .is_threadlocal = zir_decl.is_threadlocal,
+ .is_weak_linkage = false,
+ } })),
+ else => final_val.?,
+ },
+ .@"extern" => val: {
+ assert(final_val == null); // extern decls do not have a value body
+ const lib_name: ?[]const u8 = if (zir_decl.lib_name != .empty) l: {
+ break :l zir.nullTerminatedString(zir_decl.lib_name);
+ } else null;
+ if (lib_name) |l| {
+ const lib_name_src = block.src(.{ .node_offset_lib_name = 0 });
+ try sema.handleExternLibName(&block, lib_name_src, l);
+ }
+ break :val .fromInterned(try pt.getExtern(.{
+ .name = old_nav.name,
+ .ty = nav_ty.toIntern(),
+ .lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls),
+ .is_const = zir_decl.kind == .@"const",
+ .is_threadlocal = zir_decl.is_threadlocal,
+ .is_weak_linkage = false,
+ .is_dll_import = false,
+ .alignment = alignment,
+ .@"addrspace" = @"addrspace",
+ .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction
+ .owner_nav = undefined, // ignored by `getExtern`
+ }));
+ },
+ };
+
+ switch (nav_val.toIntern()) {
+ .generic_poison => unreachable, // assertion failure
+ .unreachable_value => unreachable, // assertion failure
+ else => {},
}
- const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(ip), 0);
- defer codegen_prog_node.end();
+ // This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type,
+ // this resolves the type `type` (which needs no resolution), not the struct itself.
+ try nav_ty.resolveLayout(pt);
- if (!air.typesFullyResolved(zcu)) {
- // A type we depend on failed to resolve. This is a transitive failure.
- // Correcting this failure will involve changing a type this function
- // depends on, hence triggering re-analysis of this function, so this
- // interacts correctly with incremental compilation.
- // TODO: do we need to mark this failure anywhere? I don't think so, since compilation
- // will fail due to the type error anyway.
- } else if (comp.bin_file) |lf| {
- lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {
- assert(zcu.failed_codegen.contains(nav_index));
- },
- else => {
- try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
- gpa,
- zcu.navSrcLoc(nav_index),
- "unable to codegen: {s}",
- .{@errorName(err)},
- ));
- try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
- },
- };
- } else if (zcu.llvm_object) |llvm_object| {
- llvm_object.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
+ // TODO: this is jank. If #20663 is rejected, let's think about how to better model `usingnamespace`.
+ if (zir_decl.kind == .@"usingnamespace") {
+ if (nav_ty.toIntern() != .type_type) {
+ return sema.fail(&block, ty_src, "expected type, found {}", .{nav_ty.fmt(pt)});
+ }
+ if (nav_val.toType().getNamespace(zcu) == .none) {
+ return sema.fail(&block, ty_src, "type {} has no namespace", .{nav_val.toType().fmt(pt)});
+ }
+ ip.resolveNavValue(nav_id, .{
+ .val = nav_val.toIntern(),
+ .alignment = .none,
+ .@"linksection" = .none,
+ .@"addrspace" = .generic,
+ });
+ // TODO: usingnamespace cannot participate in incremental compilation
+ assert(zcu.analysis_in_progress.swapRemove(anal_unit));
+ return .{
+ .invalidate_nav_val = true,
+ .invalidate_nav_ref = true,
};
}
-}
-/// https://github.com/ziglang/zig/issues/14307
-pub fn semaPkg(pt: Zcu.PerThread, pkg: *Module) !void {
- dev.check(.sema);
- const import_file_result = try pt.importPkg(pkg);
- const root_type = pt.zcu.fileRootType(import_file_result.file_index);
- if (root_type == .none) {
- return pt.semaFile(import_file_result.file_index);
- }
-}
+ const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) {
+ .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen
+ .variable => |v| .{ v.owner_nav == nav_id, false },
+ .@"extern" => |e| .{
+ false,
+ Type.fromInterned(e.ty).zigTypeTag(zcu) == .@"fn" and zir_decl.linkage == .@"extern",
+ },
+ else => .{ true, false },
+ };
-fn createFileRootStruct(
- pt: Zcu.PerThread,
- file_index: Zcu.File.Index,
- namespace_index: Zcu.Namespace.Index,
- replace_existing: bool,
-) Allocator.Error!InternPool.Index {
- const zcu = pt.zcu;
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const file = zcu.fileByIndex(file_index);
- const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- assert(!small.has_captures_len);
- assert(!small.has_backing_int);
- assert(small.layout == .auto);
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = file.zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = file.zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- const decls = file.zir.bodySlice(extra_index, decls_len);
- extra_index += decls_len;
+ if (is_owned_fn) {
+ // linksection etc are legal, except some targets do not support function alignment.
+ if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) {
+ return sema.fail(&block, align_src, "target does not support function alignment", .{});
+ }
+ } else if (try nav_ty.comptimeOnlySema(pt)) {
+ // alignment, linksection, addrspace annotations are not allowed for comptime-only types.
+ const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) {
+ .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
+ else => "comptime-only type",
+ };
+ if (zir_decl.align_body != null) {
+ return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
+ }
+ if (zir_decl.linksection_body != null) {
+ return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
+ }
+ if (zir_decl.addrspace_body != null) {
+ return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
+ }
+ }
- const tracked_inst = try ip.trackZir(gpa, pt.tid, .{
- .file = file_index,
- .inst = .main_struct_inst,
+ ip.resolveNavValue(nav_id, .{
+ .val = nav_val.toIntern(),
+ .alignment = alignment,
+ .@"linksection" = @"linksection",
+ .@"addrspace" = @"addrspace",
});
- const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
- .layout = .auto,
- .fields_len = fields_len,
- .known_non_opv = small.known_non_opv,
- .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
- .any_comptime_fields = small.any_comptime_fields,
- .any_default_inits = small.any_default_inits,
- .inits_resolved = false,
- .any_aligned_fields = small.any_aligned_fields,
- .key = .{ .declared = .{
- .zir_index = tracked_inst,
- .captures = &.{},
- } },
- }, replace_existing)) {
- .existing => unreachable, // we wouldn't be analysing the file root if this type existed
- .wip => |wip| wip,
- };
- errdefer wip_ty.cancel(ip, pt.tid);
-
- wip_ty.setName(ip, try file.internFullyQualifiedName(pt));
- ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, namespace_index, wip_ty.index);
- if (zcu.comp.incremental) {
- try ip.addDependency(
- gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
- .{ .src_hash = tracked_inst },
- );
- }
+ // Mark the unit as completed before evaluating the export!
+ assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- try pt.scanNamespace(namespace_index, decls);
- try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- codegen_type: {
- if (zcu.comp.config.use_llvm) break :codegen_type;
- if (file.mod.strip) break :codegen_type;
- // This job depends on any resolve_type_fully jobs queued up before it.
- try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
+ if (zir_decl.linkage == .@"export") {
+ const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.is_pub) });
+ const name_slice = zir.nullTerminatedString(zir_decl.name);
+ const name_ip = try ip.getOrPutString(gpa, pt.tid, name_slice, .no_embedded_nulls);
+ try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id);
}
- zcu.setFileRootType(file_index, wip_ty.index);
- return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
-}
-/// Re-scan the namespace of a file's root struct type on an incremental update.
-/// The file must have successfully populated ZIR.
-/// If the file's root struct type is not populated (the file is unreferenced), nothing is done.
-/// This is called by `updateZirRefs` for all updated files before the main work loop.
-/// This function does not perform any semantic analysis.
-fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
- const zcu = pt.zcu;
+ try sema.flushExports();
- const file = zcu.fileByIndex(file_index);
- assert(file.status == .success_zir);
- const file_root_type = zcu.fileRootType(file_index);
- if (file_root_type == .none) return;
+ queue_codegen: {
+ if (!queue_linker_work) break :queue_codegen;
- log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{
- file.mod.fully_qualified_name,
- file.sub_file_path,
- });
+ if (!try nav_ty.hasRuntimeBitsSema(pt)) {
+ if (zcu.comp.config.use_llvm) break :queue_codegen;
+ if (file.mod.strip) break :queue_codegen;
+ }
- const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
- const decls = decls: {
- const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
+ // This job depends on any resolve_type_fully jobs queued up before it.
+ try zcu.comp.queueJob(.{ .codegen_nav = nav_id });
+ }
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
- extra_index += @intFromBool(small.has_fields_len);
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = file.zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- break :decls file.zir.bodySlice(extra_index, decls_len);
- };
- try pt.scanNamespace(namespace_index, decls);
- zcu.namespacePtr(namespace_index).generation = zcu.generation;
+ switch (old_nav.status) {
+ .unresolved => return .{
+ .invalidate_nav_val = true,
+ .invalidate_nav_ref = true,
+ },
+ .resolved => |old| {
+ const new = ip.getNav(nav_id).status.resolved;
+ return .{
+ .invalidate_nav_val = new.val != old.val,
+ .invalidate_nav_ref = ip.typeOf(new.val) != ip.typeOf(old.val) or
+ new.alignment != old.alignment or
+ new.@"linksection" != old.@"linksection" or
+ new.@"addrspace" != old.@"addrspace",
+ };
+ },
+ }
}
-fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
+pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, maybe_coerced_func_index: InternPool.Index) Zcu.SemaError!void {
+ dev.check(.sema);
+
const tracy = trace(@src());
defer tracy.end();
const zcu = pt.zcu;
const gpa = zcu.gpa;
- const file = zcu.fileByIndex(file_index);
- assert(zcu.fileRootType(file_index) == .none);
+ const ip = &zcu.intern_pool;
- if (file.status != .success_zir) {
- return error.AnalysisFail;
- }
- assert(file.zir_loaded);
+ // We only care about the uncoerced function.
+ const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
+ const anal_unit: AnalUnit = .wrap(.{ .func = func_index });
- const new_namespace_index = try pt.createNamespace(.{
- .parent = .none,
- .owner_type = undefined, // set in `createFileRootStruct`
- .file_scope = file_index,
- .generation = zcu.generation,
- });
- const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
- errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
+ log.debug("ensureFuncBodyUpToDate {}", .{zcu.fmtAnalUnit(anal_unit)});
- switch (zcu.comp.cache_use) {
- .whole => |whole| if (whole.cache_manifest) |man| {
- const source = file.getSource(gpa) catch |err| {
- try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)});
- return error.AnalysisFail;
- };
+ const func = zcu.funcInfo(maybe_coerced_func_index);
- const resolved_path = std.fs.path.resolve(gpa, &.{
- file.mod.root.root_dir.path orelse ".",
- file.mod.root.sub_path,
- file.sub_file_path,
- }) catch |err| {
- try pt.reportRetryableFileError(file_index, "unable to resolve path: {s}", .{@errorName(err)});
- return error.AnalysisFail;
- };
- errdefer gpa.free(resolved_path);
+ const was_outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
- whole.cache_manifest_mutex.lock();
- defer whole.cache_manifest_mutex.unlock();
- man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) {
- error.OutOfMemory => |e| return e,
- else => {
- try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
- return error.AnalysisFail;
- },
- };
+ const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit);
+
+ if (was_outdated) {
+ dev.check(.incremental);
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ } else {
+ // We can trust the current information about this function.
+ if (prev_failed) {
+ return error.AnalysisFail;
+ }
+ switch (func.analysisUnordered(ip).state) {
+ .unreferenced => {}, // this is the first reference
+ .queued => {}, // we're waiting on first-time analysis
+ .analyzed => return, // up-to-date
+ }
+ }
+
+ const func_prog_node = zcu.sema_prog_node.start(ip.getNav(func.owner_nav).fqn.toSlice(ip), 0);
+ defer func_prog_node.end();
+
+ const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result|
+ .{ prev_failed or result.ies_outdated, false }
+ else |err| switch (err) {
+ error.AnalysisFail => res: {
+ if (!zcu.failed_analysis.contains(anal_unit)) {
+ // If this function caused the error, it would have an entry in `failed_analysis`.
+ // Since it does not, this must be a transitive failure.
+ try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
+ log.debug("mark transitive analysis failure for {}", .{zcu.fmtAnalUnit(anal_unit)});
+ }
+ // We consider the IES to be outdated if the function previously succeeded analysis; in this case,
+ // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting
+ // a different error later (which may now be invalid).
+ break :res .{ !prev_failed, true };
},
- .incremental => {},
+ error.OutOfMemory => {
+ // TODO: it's unclear how to gracefully handle this.
+ // To report the error cleanly, we need to add a message to `failed_analysis` and a
+ // corresponding entry to `retryable_failures`; but either of these things is quite
+ // likely to OOM at this point.
+ // If that happens, what do we do? Perhaps we could have a special field on `Zcu`
+ // for reporting OOM errors without allocating.
+ return error.OutOfMemory;
+ },
+ };
+
+ if (was_outdated) {
+ if (ies_outdated) {
+ try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index });
+ } else {
+ try zcu.markPoDependeeUpToDate(.{ .interned = func_index });
+ }
}
-}
-const SemaCauResult = packed struct {
- /// Whether the value of a `decl_val` of the corresponding Nav changed.
- invalidate_decl_val: bool,
- /// Whether the type of a `decl_ref` of the corresponding Nav changed.
- invalidate_decl_ref: bool,
-};
+ if (new_failed) return error.AnalysisFail;
+}
-/// Performs semantic analysis on the given `Cau`, storing results to its owner `Nav` if needed.
-/// If analysis fails, returns `error.AnalysisFail`, storing an error in `zcu.failed_analysis` unless
-/// the error is transitive.
-/// On success, returns information about whether the `Nav` value changed.
-fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
+fn analyzeFuncBody(
+ pt: Zcu.PerThread,
+ func_index: InternPool.Index,
+) Zcu.SemaError!struct { ies_outdated: bool } {
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
-
- const cau = ip.getCau(cau_index);
- const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
- const file = zcu.fileByIndex(inst_info.file);
- const zir = file.zir;
-
- if (file.status != .success_zir) {
- return error.AnalysisFail;
- }
+ const func = zcu.funcInfo(func_index);
+ const anal_unit = AnalUnit.wrap(.{ .func = func_index });
- // We are about to re-analyze this `Cau`; drop its depenndencies.
- zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+ // Make sure that this function is still owned by the same `Nav`. Otherwise, analyzing
+ // it would be a waste of time in the best case, and could cause codegen to give bogus
+ // results in the worst case.
- switch (cau.owner.unwrap()) {
- .none => {}, // `comptime` decl -- we will re-analyze its body.
- .nav => {}, // Other decl -- we will re-analyze its value.
- .type => |ty| {
- // This is an incremental update, and this type is being re-analyzed because it is outdated.
- // Create a new type in its place, and mark the old one as outdated so that use sites will
- // be re-analyzed and discover an up-to-date type.
- const new_ty = try pt.ensureTypeUpToDate(ty, true);
- assert(new_ty != ty);
- return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
- },
+ if (func.generic_owner == .none) {
+ // Among another things, this ensures that the function's `zir_body_inst` is correct.
+ try pt.ensureNavValUpToDate(func.owner_nav);
+ if (ip.getNav(func.owner_nav).status.resolved.val != func_index) {
+ // This function is no longer referenced! There's no point in re-analyzing it.
+ // Just mark a transitive failure and move on.
+ return error.AnalysisFail;
+ }
+ } else {
+ const go_nav = zcu.funcInfo(func.generic_owner).owner_nav;
+ // Among another things, this ensures that the function's `zir_body_inst` is correct.
+ try pt.ensureNavValUpToDate(go_nav);
+ if (ip.getNav(go_nav).status.resolved.val != func.generic_owner) {
+ // The generic owner is no longer referenced, so this function is also unreferenced.
+ // There's no point in re-analyzing it. Just mark a transitive failure and move on.
+ return error.AnalysisFail;
+ }
}
- const is_usingnamespace = switch (cau.owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).is_usingnamespace,
- .none, .type => false,
- };
+ // We'll want to remember what the IES used to be before the update for
+ // dependency invalidation purposes.
+ const old_resolved_ies = if (func.analysisUnordered(ip).inferred_error_set)
+ func.resolvedErrorSetUnordered(ip)
+ else
+ .none;
- log.debug("semaCau {}", .{zcu.fmtAnalUnit(anal_unit)});
+ log.debug("analyze and generate fn body {}", .{zcu.fmtAnalUnit(anal_unit)});
- try zcu.analysis_in_progress.put(gpa, anal_unit, {});
- errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit);
+ var air = try pt.analyzeFnBodyInner(func_index);
+ errdefer air.deinit(gpa);
- var analysis_arena = std.heap.ArenaAllocator.init(gpa);
- defer analysis_arena.deinit();
+ const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or
+ func.resolvedErrorSetUnordered(ip) != old_resolved_ies;
- var comptime_err_ret_trace = std.ArrayList(Zcu.LazySrcLoc).init(gpa);
- defer comptime_err_ret_trace.deinit();
+ const comp = zcu.comp;
- var sema: Sema = .{
- .pt = pt,
- .gpa = gpa,
- .arena = analysis_arena.allocator(),
- .code = zir,
- .owner = anal_unit,
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = Type.void,
- .fn_ret_ty_ies = null,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
+ const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
+ const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null);
- // Every `Cau` has a dependency on the source of its own ZIR instruction.
- try sema.declareDependency(.{ .src_hash = cau.zir_index });
+ if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
+ air.deinit(gpa);
+ return .{ .ies_outdated = ies_outdated };
+ }
- var block: Sema.Block = .{
- .parent = null,
- .sema = &sema,
- .namespace = cau.namespace,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- .src_base_inst = cau.zir_index,
- .type_name_ctx = switch (cau.owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).fqn,
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip),
- .none => try ip.getOrPutStringFmt(gpa, pt.tid, "{}.comptime", .{
- Type.fromInterned(zcu.namespacePtr(cau.namespace).owner_type).containerTypeName(ip).fmt(ip),
- }, .no_embedded_nulls),
- },
- };
- defer block.instructions.deinit(gpa);
+ // This job depends on any resolve_type_fully jobs queued up before it.
+ try comp.queueJob(.{ .codegen_func = .{
+ .func = func_index,
+ .air = air,
+ } });
- const zir_decl = zir.getDeclaration(inst_info.inst);
+ return .{ .ies_outdated = ies_outdated };
+}
- // We have to fetch this state before resolving the body because of the `nav_already_populated`
- // case below. We might change the language in future so that align/linksection/etc for functions
- // work in a way more in line with other declarations, in which case that logic will go away.
- const old_nav_info = switch (cau.owner.unwrap()) {
- .none, .type => undefined, // we'll never use `old_nav_info`
- .nav => |nav| ip.getNav(nav),
- };
+/// Takes ownership of `air`, even on error.
+/// If any types referenced by `air` are unresolved, marks the codegen as failed.
+pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Air) Allocator.Error!void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+ const comp = zcu.comp;
- const align_src = block.src(.{ .node_offset_var_decl_align = 0 });
- const section_src = block.src(.{ .node_offset_var_decl_section = 0 });
- const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = 0 });
- const ty_src = block.src(.{ .node_offset_var_decl_ty = 0 });
- const init_src = block.src(.{ .node_offset_var_decl_init = 0 });
+ defer {
+ var air_mut = air;
+ air_mut.deinit(gpa);
+ }
- // First, we must resolve the declaration's type. To do this, we analyze the type body if available,
- // or otherwise, we analyze the value body, populating `early_val` in the process.
+ const func = zcu.funcInfo(func_index);
+ const nav_index = func.owner_nav;
+ const nav = ip.getNav(nav_index);
- const decl_ty: Type, const early_val: ?Value = if (zir_decl.type_body) |type_body| ty: {
- // We evaluate only the type now; no need for the value yet.
- const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_info.inst);
- const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src);
- break :ty .{ .fromInterned(type_ref.toInterned().?), null };
- } else ty: {
- // We don't have a type body, so we need to evaluate the value immediately.
- const value_body = zir_decl.value_body.?;
- const result_ref = try sema.resolveInlineBody(&block, value_body, inst_info.inst);
- const val = try sema.resolveFinalDeclValue(&block, init_src, result_ref);
- break :ty .{ val.typeOf(zcu), val };
- };
+ var liveness = try Liveness.analyze(gpa, air, ip);
+ defer liveness.deinit(gpa);
- switch (zir_decl.kind) {
- .unnamed_test, .@"test", .decltest => assert(decl_ty.zigTypeTag(zcu) == .@"fn"),
- .@"comptime" => assert(decl_ty.toIntern() == .void_type),
- .@"usingnamespace" => {},
- .@"const" => {},
- .@"var" => try sema.validateVarType(
- &block,
- if (zir_decl.type_body != null) ty_src else init_src,
- decl_ty,
- zir_decl.linkage == .@"extern",
- ),
+ if (build_options.enable_debug_extensions and comp.verbose_air) {
+ std.debug.print("# Begin Function AIR: {}:\n", .{nav.fqn.fmt(ip)});
+ @import("../print_air.zig").dump(pt, air, liveness);
+ std.debug.print("# End Function AIR: {}\n\n", .{nav.fqn.fmt(ip)});
}
- // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
- // the full pointer type of this declaration.
+ if (std.debug.runtime_safety) {
+ var verify: Liveness.Verify = .{
+ .gpa = gpa,
+ .air = air,
+ .liveness = liveness,
+ .intern_pool = ip,
+ };
+ defer verify.deinit();
- const alignment: InternPool.Alignment = a: {
- const align_body = zir_decl.align_body orelse break :a .none;
- const align_ref = try sema.resolveInlineBody(&block, align_body, inst_info.inst);
- break :a try sema.analyzeAsAlign(&block, align_src, align_ref);
- };
+ verify.verify() catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ else => {
+ try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
+ gpa,
+ zcu.navSrcLoc(nav_index),
+ "invalid liveness: {s}",
+ .{@errorName(err)},
+ ));
+ return;
+ },
+ };
+ }
- const @"linksection": InternPool.OptionalNullTerminatedString = ls: {
- const linksection_body = zir_decl.linksection_body orelse break :ls .none;
- const linksection_ref = try sema.resolveInlineBody(&block, linksection_body, inst_info.inst);
- const bytes = try sema.toConstString(&block, section_src, linksection_ref, .{
- .needed_comptime_reason = "linksection must be comptime-known",
- });
- if (std.mem.indexOfScalar(u8, bytes, 0) != null) {
- return sema.fail(&block, section_src, "linksection cannot contain null bytes", .{});
- } else if (bytes.len == 0) {
- return sema.fail(&block, section_src, "linksection cannot be empty", .{});
- }
- break :ls try ip.getOrPutStringOpt(gpa, pt.tid, bytes, .no_embedded_nulls);
- };
+ const codegen_prog_node = zcu.codegen_prog_node.start(nav.fqn.toSlice(ip), 0);
+ defer codegen_prog_node.end();
- const @"addrspace": std.builtin.AddressSpace = as: {
- const addrspace_ctx: Sema.AddressSpaceContext = switch (zir_decl.kind) {
- .@"var" => .variable,
- else => switch (decl_ty.zigTypeTag(zcu)) {
- .@"fn" => .function,
- else => .constant,
+ if (!air.typesFullyResolved(zcu)) {
+ // A type we depend on failed to resolve. This is a transitive failure.
+ // Correcting this failure will involve changing a type this function
+ // depends on, hence triggering re-analysis of this function, so this
+ // interacts correctly with incremental compilation.
+ // TODO: do we need to mark this failure anywhere? I don't think so, since compilation
+ // will fail due to the type error anyway.
+ } else if (comp.bin_file) |lf| {
+ lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.AnalysisFail => {
+ assert(zcu.failed_codegen.contains(nav_index));
+ },
+ else => {
+ try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
+ gpa,
+ zcu.navSrcLoc(nav_index),
+ "unable to codegen: {s}",
+ .{@errorName(err)},
+ ));
+ try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
},
};
- const target = zcu.getTarget();
- const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) {
- .function => target_util.defaultAddressSpace(target, .function),
- .variable => target_util.defaultAddressSpace(target, .global_mutable),
- .constant => target_util.defaultAddressSpace(target, .global_constant),
- else => unreachable,
+ } else if (zcu.llvm_object) |llvm_object| {
+ llvm_object.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
};
- const addrspace_ref = try sema.resolveInlineBody(&block, addrspace_body, inst_info.inst);
- break :as try sema.analyzeAsAddressSpace(&block, addrspace_src, addrspace_ref, addrspace_ctx);
- };
-
- // Lastly, we must evaluate the value if we have not already done so. Note, however, that extern declarations
- // don't have an associated value body.
-
- const final_val: ?Value = early_val orelse if (zir_decl.value_body) |value_body| val: {
- // Put the resolved type into `inst_map` to be used as the result type of the init.
- try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_info.inst});
- sema.inst_map.putAssumeCapacity(inst_info.inst, Air.internedToRef(decl_ty.toIntern()));
- const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_info.inst);
- assert(sema.inst_map.remove(inst_info.inst));
+ }
+}
- const result_ref = try sema.coerce(&block, decl_ty, uncoerced_result_ref, init_src);
- break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref);
- } else null;
+/// https://github.com/ziglang/zig/issues/14307
+pub fn semaPkg(pt: Zcu.PerThread, pkg: *Module) !void {
+ dev.check(.sema);
+ const import_file_result = try pt.importPkg(pkg);
+ const root_type = pt.zcu.fileRootType(import_file_result.file_index);
+ if (root_type == .none) {
+ return pt.semaFile(import_file_result.file_index);
+ }
+}
- // TODO: missing validation?
+fn createFileRootStruct(
+ pt: Zcu.PerThread,
+ file_index: Zcu.File.Index,
+ namespace_index: Zcu.Namespace.Index,
+ replace_existing: bool,
+) Allocator.Error!InternPool.Index {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+ const file = zcu.fileByIndex(file_index);
+ const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
+ assert(extended.opcode == .struct_decl);
+ const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
+ assert(!small.has_captures_len);
+ assert(!small.has_backing_int);
+ assert(small.layout == .auto);
+ var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = file.zir.extra[extra_index];
+ extra_index += 1;
+ break :blk fields_len;
+ } else 0;
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = file.zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ const decls = file.zir.bodySlice(extra_index, decls_len);
+ extra_index += decls_len;
- const decl_val: Value = switch (zir_decl.linkage) {
- .normal, .@"export" => switch (zir_decl.kind) {
- .@"var" => .fromInterned(try pt.intern(.{ .variable = .{
- .ty = decl_ty.toIntern(),
- .init = final_val.?.toIntern(),
- .owner_nav = cau.owner.unwrap().nav,
- .is_threadlocal = zir_decl.is_threadlocal,
- .is_weak_linkage = false,
- } })),
- else => final_val.?,
- },
- .@"extern" => val: {
- assert(final_val == null); // extern decls do not have a value body
- const lib_name: ?[]const u8 = if (zir_decl.lib_name != .empty) l: {
- break :l zir.nullTerminatedString(zir_decl.lib_name);
- } else null;
- if (lib_name) |l| {
- const lib_name_src = block.src(.{ .node_offset_lib_name = 0 });
- try sema.handleExternLibName(&block, lib_name_src, l);
- }
- break :val .fromInterned(try pt.getExtern(.{
- .name = old_nav_info.name,
- .ty = decl_ty.toIntern(),
- .lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls),
- .is_const = zir_decl.kind == .@"const",
- .is_threadlocal = zir_decl.is_threadlocal,
- .is_weak_linkage = false,
- .is_dll_import = false,
- .alignment = alignment,
- .@"addrspace" = @"addrspace",
- .zir_index = cau.zir_index, // `declaration` instruction
- .owner_nav = undefined, // ignored by `getExtern`
- }));
- },
+ const tracked_inst = try ip.trackZir(gpa, pt.tid, .{
+ .file = file_index,
+ .inst = .main_struct_inst,
+ });
+ const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
+ .layout = .auto,
+ .fields_len = fields_len,
+ .known_non_opv = small.known_non_opv,
+ .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
+ .any_comptime_fields = small.any_comptime_fields,
+ .any_default_inits = small.any_default_inits,
+ .inits_resolved = false,
+ .any_aligned_fields = small.any_aligned_fields,
+ .key = .{ .declared = .{
+ .zir_index = tracked_inst,
+ .captures = &.{},
+ } },
+ }, replace_existing)) {
+ .existing => unreachable, // we wouldn't be analysing the file root if this type existed
+ .wip => |wip| wip,
};
+ errdefer wip_ty.cancel(ip, pt.tid);
- const nav_index = switch (cau.owner.unwrap()) {
- .none => {
- // This is a `comptime` decl, so we are done -- the side effects are all we care about.
- // Just make sure to `flushExports`.
- try sema.flushExports();
- assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- return .{
- .invalidate_decl_val = false,
- .invalidate_decl_ref = false,
- };
- },
- .nav => |nav| nav, // We will resolve this `Nav` below.
- .type => unreachable, // Handled at top of function.
- };
+ wip_ty.setName(ip, try file.internFullyQualifiedName(pt));
+ ip.namespacePtr(namespace_index).owner_type = wip_ty.index;
- switch (decl_val.toIntern()) {
- .generic_poison => unreachable, // assertion failure
- .unreachable_value => unreachable, // assertion failure
- else => {},
+ if (zcu.comp.incremental) {
+ try ip.addDependency(
+ gpa,
+ .wrap(.{ .type = wip_ty.index }),
+ .{ .src_hash = tracked_inst },
+ );
}
- // This resolves the type of the resolved value, not that value itself. If `decl_val` is a struct type,
- // this resolves the type `type` (which needs no resolution), not the struct itself.
- try decl_ty.resolveLayout(pt);
-
- // TODO: this is jank. If #20663 is rejected, let's think about how to better model `usingnamespace`.
- if (is_usingnamespace) {
- if (decl_ty.toIntern() != .type_type) {
- return sema.fail(&block, ty_src, "expected type, found {}", .{decl_ty.fmt(pt)});
- }
- if (decl_val.toType().getNamespace(zcu) == .none) {
- return sema.fail(&block, ty_src, "type {} has no namespace", .{decl_val.toType().fmt(pt)});
- }
- ip.resolveNavValue(nav_index, .{
- .val = decl_val.toIntern(),
- .alignment = .none,
- .@"linksection" = .none,
- .@"addrspace" = .generic,
- });
- // TODO: usingnamespace cannot participate in incremental compilation
- assert(zcu.analysis_in_progress.swapRemove(anal_unit));
- return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
+ try pt.scanNamespace(namespace_index, decls);
+ try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
+ codegen_type: {
+ if (zcu.comp.config.use_llvm) break :codegen_type;
+ if (file.mod.strip) break :codegen_type;
+ // This job depends on any resolve_type_fully jobs queued up before it.
+ try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
}
+ zcu.setFileRootType(file_index, wip_ty.index);
+ return wip_ty.finish(ip, namespace_index);
+}
- const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(decl_val.toIntern())) {
- .func => |f| .{ true, f.owner_nav == nav_index }, // note that this lets function aliases reach codegen
- .variable => |v| .{ v.owner_nav == nav_index, false },
- .@"extern" => |e| .{ false, Type.fromInterned(e.ty).zigTypeTag(zcu) == .@"fn" },
- else => .{ true, false },
- };
-
- // Keep in sync with logic in `Sema.zirVarExtended`.
+/// Re-scan the namespace of a file's root struct type on an incremental update.
+/// The file must have successfully populated ZIR.
+/// If the file's root struct type is not populated (the file is unreferenced), nothing is done.
+/// This is called by `updateZirRefs` for all updated files before the main work loop.
+/// This function does not perform any semantic analysis.
+fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void {
+ const zcu = pt.zcu;
- if (is_owned_fn) {
- // linksection etc are legal, except some targets do not support function alignment.
- if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) {
- return sema.fail(&block, align_src, "target does not support function alignment", .{});
- }
- } else if (try decl_ty.comptimeOnlySema(pt)) {
- // alignment, linksection, addrspace annotations are not allowed for comptime-only types.
- const reason: []const u8 = switch (ip.indexToKey(decl_val.toIntern())) {
- .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations*
- else => "comptime-only type",
- };
- if (zir_decl.align_body != null) {
- return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason});
- }
- if (zir_decl.linksection_body != null) {
- return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason});
- }
- if (zir_decl.addrspace_body != null) {
- return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason});
- }
- }
+ const file = zcu.fileByIndex(file_index);
+ assert(file.status == .success_zir);
+ const file_root_type = zcu.fileRootType(file_index);
+ if (file_root_type == .none) return;
- ip.resolveNavValue(nav_index, .{
- .val = decl_val.toIntern(),
- .alignment = alignment,
- .@"linksection" = @"linksection",
- .@"addrspace" = @"addrspace",
+ log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{
+ file.mod.fully_qualified_name,
+ file.sub_file_path,
});
- // Mark the `Cau` as completed before evaluating the export!
- assert(zcu.analysis_in_progress.swapRemove(anal_unit));
-
- if (zir_decl.linkage == .@"export") {
- const export_src = block.src(.{ .token_offset = @intFromBool(zir_decl.is_pub) });
- const name_slice = zir.nullTerminatedString(zir_decl.name);
- const name_ip = try ip.getOrPutString(gpa, pt.tid, name_slice, .no_embedded_nulls);
- try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_index);
- }
+ const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
+ const decls = decls: {
+ const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
+ const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- try sema.flushExports();
+ var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len;
+ extra_index += @intFromBool(small.has_fields_len);
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = file.zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ break :decls file.zir.bodySlice(extra_index, decls_len);
+ };
+ try pt.scanNamespace(namespace_index, decls);
+ zcu.namespacePtr(namespace_index).generation = zcu.generation;
+}
- queue_codegen: {
- if (!queue_linker_work) break :queue_codegen;
+fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
+ const tracy = trace(@src());
+ defer tracy.end();
- if (!try decl_ty.hasRuntimeBitsSema(pt)) {
- if (zcu.comp.config.use_llvm) break :queue_codegen;
- if (file.mod.strip) break :queue_codegen;
- }
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const file = zcu.fileByIndex(file_index);
+ assert(zcu.fileRootType(file_index) == .none);
- // This job depends on any resolve_type_fully jobs queued up before it.
- try zcu.comp.queueJob(.{ .codegen_nav = nav_index });
+ if (file.status != .success_zir) {
+ return error.AnalysisFail;
}
+ assert(file.zir_loaded);
- switch (old_nav_info.status) {
- .unresolved => return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- },
- .resolved => |old| {
- const new = ip.getNav(nav_index).status.resolved;
- return .{
- .invalidate_decl_val = new.val != old.val,
- .invalidate_decl_ref = ip.typeOf(new.val) != ip.typeOf(old.val) or
- new.alignment != old.alignment or
- new.@"linksection" != old.@"linksection" or
- new.@"addrspace" != old.@"addrspace",
+ const new_namespace_index = try pt.createNamespace(.{
+ .parent = .none,
+ .owner_type = undefined, // set in `createFileRootStruct`
+ .file_scope = file_index,
+ .generation = zcu.generation,
+ });
+ const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false);
+ errdefer zcu.intern_pool.remove(pt.tid, struct_ty);
+
+ switch (zcu.comp.cache_use) {
+ .whole => |whole| if (whole.cache_manifest) |man| {
+ const source = file.getSource(gpa) catch |err| {
+ try pt.reportRetryableFileError(file_index, "unable to load source: {s}", .{@errorName(err)});
+ return error.AnalysisFail;
+ };
+
+ const resolved_path = std.fs.path.resolve(gpa, &.{
+ file.mod.root.root_dir.path orelse ".",
+ file.mod.root.sub_path,
+ file.sub_file_path,
+ }) catch |err| {
+ try pt.reportRetryableFileError(file_index, "unable to resolve path: {s}", .{@errorName(err)});
+ return error.AnalysisFail;
+ };
+ errdefer gpa.free(resolved_path);
+
+ whole.cache_manifest_mutex.lock();
+ defer whole.cache_manifest_mutex.unlock();
+ man.addFilePostContents(resolved_path, source.bytes, source.stat) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
+ else => {
+ try pt.reportRetryableFileError(file_index, "unable to update cache: {s}", .{@errorName(err)});
+ return error.AnalysisFail;
+ },
};
},
+ .incremental => {},
}
}
@@ -1880,45 +1914,42 @@ pub fn scanNamespace(
// For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather
// than their name. We'll build an efficient mapping now, then discard the current `decls`.
- // We map to the `Cau`, since not every declaration has a `Nav`.
- var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.Cau.Index) = .empty;
+ // We map to the `AnalUnit`, since not every declaration has a `Nav`.
+ var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit) = .empty;
defer existing_by_inst.deinit(gpa);
try existing_by_inst.ensureTotalCapacity(gpa, @intCast(
namespace.pub_decls.count() + namespace.priv_decls.count() +
namespace.pub_usingnamespace.items.len + namespace.priv_usingnamespace.items.len +
- namespace.other_decls.items.len,
+ namespace.comptime_decls.items.len +
+ namespace.test_decls.items.len,
));
for (namespace.pub_decls.keys()) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
}
for (namespace.priv_decls.keys()) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
}
for (namespace.pub_usingnamespace.items) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
}
for (namespace.priv_usingnamespace.items) |nav| {
- const cau_index = ip.getNav(nav).analysis_owner.unwrap().?;
- const zir_index = ip.getCau(cau_index).zir_index;
- existing_by_inst.putAssumeCapacityNoClobber(zir_index, cau_index);
- }
- for (namespace.other_decls.items) |cau_index| {
- const cau = ip.getCau(cau_index);
- existing_by_inst.putAssumeCapacityNoClobber(cau.zir_index, cau_index);
- // If this is a test, it'll be re-added to `test_functions` later on
- // if still alive. Remove it for now.
- switch (cau.owner.unwrap()) {
- .none, .type => {},
- .nav => |nav| _ = zcu.test_functions.swapRemove(nav),
- }
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
+ }
+ for (namespace.comptime_decls.items) |cu| {
+ const zir_index = ip.getComptimeUnit(cu).zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu }));
+ }
+ for (namespace.test_decls.items) |nav| {
+ const zir_index = ip.getNav(nav).analysis.?.zir_index;
+ existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav }));
+ // This test will be re-added to `test_functions` later on if it's still alive. Remove it for now.
+ _ = zcu.test_functions.swapRemove(nav);
}
var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty;
@@ -1928,7 +1959,8 @@ pub fn scanNamespace(
namespace.priv_decls.clearRetainingCapacity();
namespace.pub_usingnamespace.clearRetainingCapacity();
namespace.priv_usingnamespace.clearRetainingCapacity();
- namespace.other_decls.clearRetainingCapacity();
+ namespace.comptime_decls.clearRetainingCapacity();
+ namespace.test_decls.clearRetainingCapacity();
var scan_decl_iter: ScanDeclIter = .{
.pt = pt,
@@ -1950,7 +1982,7 @@ const ScanDeclIter = struct {
pt: Zcu.PerThread,
namespace_index: Zcu.Namespace.Index,
seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void),
- existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.Cau.Index),
+ existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit),
/// Decl scanning is run in two passes, so that we can detect when a generated
/// name would clash with an explicit name and use a different one.
pass: enum { named, unnamed },
@@ -1988,48 +2020,30 @@ const ScanDeclIter = struct {
const decl = zir.getDeclaration(decl_inst);
- const Kind = enum { @"comptime", @"usingnamespace", @"test", named };
-
- const maybe_name: InternPool.OptionalNullTerminatedString, const kind: Kind, const is_named_test: bool = switch (decl.kind) {
- .@"comptime" => info: {
+ const maybe_name: InternPool.OptionalNullTerminatedString = switch (decl.kind) {
+ .@"comptime" => name: {
if (iter.pass != .unnamed) return;
- break :info .{
- .none,
- .@"comptime",
- false,
- };
+ break :name .none;
},
- .@"usingnamespace" => info: {
+ .@"usingnamespace" => name: {
if (iter.pass != .unnamed) return;
const i = iter.usingnamespace_index;
iter.usingnamespace_index += 1;
- break :info .{
- (try iter.avoidNameConflict("usingnamespace_{d}", .{i})).toOptional(),
- .@"usingnamespace",
- false,
- };
+ break :name (try iter.avoidNameConflict("usingnamespace_{d}", .{i})).toOptional();
},
- .unnamed_test => info: {
+ .unnamed_test => name: {
if (iter.pass != .unnamed) return;
const i = iter.unnamed_test_index;
iter.unnamed_test_index += 1;
- break :info .{
- (try iter.avoidNameConflict("test_{d}", .{i})).toOptional(),
- .@"test",
- false,
- };
+ break :name (try iter.avoidNameConflict("test_{d}", .{i})).toOptional();
},
- .@"test", .decltest => |kind| info: {
+ .@"test", .decltest => |kind| name: {
// We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary.
if (iter.pass != .unnamed) return;
const prefix = @tagName(kind);
- break :info .{
- (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(decl.name) })).toOptional(),
- .@"test",
- true,
- };
+ break :name (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(decl.name) })).toOptional();
},
- .@"const", .@"var" => info: {
+ .@"const", .@"var" => name: {
if (iter.pass != .named) return;
const name = try ip.getOrPutString(
gpa,
@@ -2038,11 +2052,7 @@ const ScanDeclIter = struct {
.no_embedded_nulls,
);
try iter.seen_decls.putNoClobber(gpa, name, {});
- break :info .{
- name.toOptional(),
- .named,
- false,
- };
+ break :name name.toOptional();
},
};
@@ -2051,46 +2061,44 @@ const ScanDeclIter = struct {
.inst = decl_inst,
});
- const existing_cau = iter.existing_by_inst.get(tracked_inst);
+ const existing_unit = iter.existing_by_inst.get(tracked_inst);
- const cau, const want_analysis = switch (kind) {
- .@"comptime" => cau: {
- const cau = existing_cau orelse try ip.createComptimeCau(gpa, pt.tid, tracked_inst, namespace_index);
+ const unit, const want_analysis = switch (decl.kind) {
+ .@"comptime" => unit: {
+ const cu = if (existing_unit) |eu|
+ eu.unwrap().@"comptime"
+ else
+ try ip.createComptimeUnit(gpa, pt.tid, tracked_inst, namespace_index);
- try namespace.other_decls.append(gpa, cau);
+ const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
- if (existing_cau == null) {
- // For a `comptime` declaration, whether to analyze is based solely on whether the
- // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.
- const unit = AnalUnit.wrap(.{ .cau = cau });
- if (zcu.potentially_outdated.fetchSwapRemove(unit)) |kv| {
- try zcu.outdated.ensureUnusedCapacity(gpa, 1);
- try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
- zcu.outdated.putAssumeCapacityNoClobber(unit, kv.value);
- if (kv.value == 0) { // no PO deps
- zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
- }
- } else if (!zcu.outdated.contains(unit)) {
- try zcu.outdated.ensureUnusedCapacity(gpa, 1);
- try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
- zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
- zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
- }
+ try namespace.comptime_decls.append(gpa, cu);
+
+ if (existing_unit == null) {
+ // For a `comptime` declaration, whether to analyze is based solely on whether the unit
+ // is outdated. So, add this fresh one to `outdated` and `outdated_ready`.
+ try zcu.outdated.ensureUnusedCapacity(gpa, 1);
+ try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1);
+ zcu.outdated.putAssumeCapacityNoClobber(unit, 0);
+ zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {});
}
- break :cau .{ cau, true };
+ break :unit .{ unit, true };
},
- else => cau: {
+ else => unit: {
const name = maybe_name.unwrap().?;
const fqn = try namespace.internFullyQualifiedName(ip, gpa, pt.tid, name);
- const cau, const nav = if (existing_cau) |cau_index| cau_nav: {
- const nav_index = ip.getCau(cau_index).owner.unwrap().nav;
- const nav = ip.getNav(nav_index);
- assert(nav.name == name);
- assert(nav.fqn == fqn);
- break :cau_nav .{ cau_index, nav_index };
- } else try ip.createPairedCauNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, kind == .@"usingnamespace");
- const want_analysis = switch (kind) {
+ const nav = if (existing_unit) |eu|
+ eu.unwrap().nav_val
+ else
+ try ip.createDeclNav(gpa, pt.tid, name, fqn, tracked_inst, namespace_index, decl.kind == .@"usingnamespace");
+
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
+
+ assert(ip.getNav(nav).name == name);
+ assert(ip.getNav(nav).fqn == fqn);
+
+ const want_analysis = switch (decl.kind) {
.@"comptime" => unreachable,
.@"usingnamespace" => a: {
if (comp.incremental) {
@@ -2103,8 +2111,9 @@ const ScanDeclIter = struct {
}
break :a true;
},
- .@"test" => a: {
- try namespace.other_decls.append(gpa, cau);
+ .unnamed_test, .@"test", .decltest => a: {
+ const is_named = decl.kind != .unnamed_test;
+ try namespace.test_decls.append(gpa, nav);
// TODO: incremental compilation!
// * remove from `test_functions` if no longer matching filter
// * add to `test_functions` if newly passing filter
@@ -2112,7 +2121,7 @@ const ScanDeclIter = struct {
// Perhaps we should add all test indiscriminately and filter at the end of the update.
if (!comp.config.is_test) break :a false;
if (file.mod != zcu.main_mod) break :a false;
- if (is_named_test and comp.test_filters.len > 0) {
+ if (is_named and comp.test_filters.len > 0) {
const fqn_slice = fqn.toSlice(ip);
for (comp.test_filters) |test_filter| {
if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
@@ -2121,7 +2130,7 @@ const ScanDeclIter = struct {
try zcu.test_functions.put(gpa, nav, {});
break :a true;
},
- .named => a: {
+ .@"const", .@"var" => a: {
if (decl.is_pub) {
try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu });
} else {
@@ -2130,23 +2139,23 @@ const ScanDeclIter = struct {
break :a false;
},
};
- break :cau .{ cau, want_analysis };
+ break :unit .{ unit, want_analysis };
},
};
- if (existing_cau == null and (want_analysis or decl.linkage == .@"export")) {
+ if (existing_unit == null and (want_analysis or decl.linkage == .@"export")) {
log.debug(
- "scanDecl queue analyze_cau file='{s}' cau_index={d}",
- .{ namespace.fileScope(zcu).sub_file_path, cau },
+ "scanDecl queue analyze_comptime_unit file='{s}' unit={}",
+ .{ namespace.fileScope(zcu).sub_file_path, zcu.fmtAnalUnit(unit) },
);
- try comp.queueJob(.{ .analyze_cau = cau });
+ try comp.queueJob(.{ .analyze_comptime_unit = unit });
}
// TODO: we used to do line number updates here, but this is an inappropriate place for this logic to live.
}
};
-fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
+fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air {
const tracy = trace(@src());
defer tracy.end();
@@ -2168,21 +2177,14 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
func.setResolvedErrorSet(ip, .none);
}
- // This is the `Cau` corresponding to the `declaration` instruction which the function or its generic owner originates from.
- const decl_cau = ip.getCau(cau: {
- const orig_nav = if (func.generic_owner == .none)
- func.owner_nav
- else
- zcu.funcInfo(func.generic_owner).owner_nav;
-
- break :cau ip.getNav(orig_nav).analysis_owner.unwrap().?;
- });
+ // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from.
+ const decl_nav = ip.getNav(if (func.generic_owner == .none)
+ func.owner_nav
+ else
+ zcu.funcInfo(func.generic_owner).owner_nav);
const func_nav = ip.getNav(func.owner_nav);
- const decl_prog_node = zcu.sema_prog_node.start(func_nav.fqn.toSlice(ip), 0);
- defer decl_prog_node.end();
-
zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
@@ -2216,7 +2218,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
// Every runtime function has a dependency on the source of the Decl it originates from.
// It also depends on the value of its owner Decl.
- try sema.declareDependency(.{ .src_hash = decl_cau.zir_index });
+ try sema.declareDependency(.{ .src_hash = decl_nav.analysis.?.zir_index });
try sema.declareDependency(.{ .nav_val = func.owner_nav });
if (func.analysisUnordered(ip).inferred_error_set) {
@@ -2236,11 +2238,11 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
var inner_block: Sema.Block = .{
.parent = null,
.sema = &sema,
- .namespace = decl_cau.namespace,
+ .namespace = decl_nav.analysis.?.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = false,
- .src_base_inst = decl_cau.zir_index,
+ .src_base_inst = decl_nav.analysis.?.zir_index,
.type_name_ctx = func_nav.fqn,
};
defer inner_block.instructions.deinit(gpa);
@@ -2542,10 +2544,10 @@ fn processExportsInner(
.nav => |nav_index| if (failed: {
const nav = ip.getNav(nav_index);
if (zcu.failed_codegen.contains(nav_index)) break :failed true;
- if (nav.analysis_owner.unwrap()) |cau| {
- const cau_unit = AnalUnit.wrap(.{ .cau = cau });
- if (zcu.failed_analysis.contains(cau_unit)) break :failed true;
- if (zcu.transitive_failed_analysis.contains(cau_unit)) break :failed true;
+ if (nav.analysis != null) {
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
+ if (zcu.failed_analysis.contains(unit)) break :failed true;
+ if (zcu.transitive_failed_analysis.contains(unit)) break :failed true;
}
const val = switch (nav.status) {
.unresolved => break :failed true,
@@ -2593,15 +2595,14 @@ pub fn populateTestFunctions(
Zcu.Namespace.NameAdapter{ .zcu = zcu },
).?;
{
- // We have to call `ensureCauAnalyzed` here in case `builtin.test_functions`
+ // We have to call `ensureNavValUpToDate` here in case `builtin.test_functions`
// was not referenced by start code.
zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
defer {
zcu.sema_prog_node.end();
zcu.sema_prog_node = std.Progress.Node.none;
}
- const cau_index = ip.getNav(nav_index).analysis_owner.unwrap().?;
- pt.ensureCauAnalyzed(cau_index) catch |err| switch (err) {
+ pt.ensureNavValUpToDate(nav_index) catch |err| switch (err) {
error.AnalysisFail => return,
error.OutOfMemory => return error.OutOfMemory,
};
@@ -2622,8 +2623,7 @@ pub fn populateTestFunctions(
{
// The test declaration might have failed; if that's the case, just return, as we'll
// be emitting a compile error anyway.
- const cau = test_nav.analysis_owner.unwrap().?;
- const anal_unit: AnalUnit = .wrap(.{ .cau = cau });
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = test_nav_index });
if (zcu.failed_analysis.contains(anal_unit) or
zcu.transitive_failed_analysis.contains(anal_unit))
{
@@ -2748,8 +2748,8 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
"unable to codegen: {s}",
.{@errorName(err)},
));
- if (nav.analysis_owner.unwrap()) |cau| {
- try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .cau = cau }));
+ if (nav.analysis != null) {
+ try zcu.retryable_failures.append(zcu.gpa, .wrap(.{ .nav_val = nav_index }));
} else {
// TODO: we don't have a way to indicate that this failure is retryable!
// Since these are really rare, we could as a cop-out retry the whole build next update.
@@ -3255,7 +3255,7 @@ pub fn getBuiltinNav(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Intern
const builtin_str = try ip.getOrPutString(gpa, pt.tid, "builtin", .no_embedded_nulls);
const builtin_nav = std_namespace.pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse
@panic("lib/std.zig is corrupt and missing 'builtin'");
- pt.ensureCauAnalyzed(ip.getNav(builtin_nav).analysis_owner.unwrap().?) catch @panic("std.builtin is corrupt");
+ pt.ensureNavValUpToDate(builtin_nav) catch @panic("std.builtin is corrupt");
const builtin_type = Type.fromInterned(ip.getNav(builtin_nav).status.resolved.val);
const builtin_namespace = zcu.namespacePtr(builtin_type.getNamespace(zcu).unwrap() orelse @panic("std.builtin is corrupt"));
const name_str = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls);
@@ -3307,68 +3307,45 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo
/// Given a container type requiring resolution, ensures that it is up-to-date.
/// If not, the type is recreated at a new `InternPool.Index`.
/// The new index is returned. This is the same as the old index if the fields were up-to-date.
-/// If `already_updating` is set, assumes the type is already outdated and undergoing re-analysis rather than checking `zcu.outdated`.
-pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index, already_updating: bool) Zcu.SemaError!InternPool.Index {
+pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
+ const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
+
+ const anal_unit: AnalUnit = .wrap(.{ .type = ty });
+ const outdated = zcu.outdated.swapRemove(anal_unit) or
+ zcu.potentially_outdated.swapRemove(anal_unit);
+
+ if (!outdated) return ty;
+
+ // We will recreate the type at a new `InternPool.Index`.
+
+ _ = zcu.outdated_ready.swapRemove(anal_unit);
+ try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
+
+ // Delete old state which is no longer in use. Technically, this is not necessary: these exports,
+ // references, etc, will be ignored because the type itself is unreferenced. However, it allows
+ // reusing the memory which is currently being used to track this state.
+ zcu.deleteUnitExports(anal_unit);
+ zcu.deleteUnitReferences(anal_unit);
+ if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| {
+ kv.value.destroy(gpa);
+ }
+ _ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
+ zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit);
+
switch (ip.indexToKey(ty)) {
- .struct_type => |key| {
- const struct_obj = ip.loadStructType(ty);
- const outdated = already_updating or o: {
- const anal_unit = AnalUnit.wrap(.{ .cau = struct_obj.cau });
- const o = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
- if (o) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
- break :o o;
- };
- if (!outdated) return ty;
- return pt.recreateStructType(key, struct_obj);
- },
- .union_type => |key| {
- const union_obj = ip.loadUnionType(ty);
- const outdated = already_updating or o: {
- const anal_unit = AnalUnit.wrap(.{ .cau = union_obj.cau });
- const o = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
- if (o) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
- break :o o;
- };
- if (!outdated) return ty;
- return pt.recreateUnionType(key, union_obj);
- },
- .enum_type => |key| {
- const enum_obj = ip.loadEnumType(ty);
- const outdated = already_updating or o: {
- const anal_unit = AnalUnit.wrap(.{ .cau = enum_obj.cau.unwrap().? });
- const o = zcu.outdated.swapRemove(anal_unit) or
- zcu.potentially_outdated.swapRemove(anal_unit);
- if (o) {
- _ = zcu.outdated_ready.swapRemove(anal_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- }
- break :o o;
- };
- if (!outdated) return ty;
- return pt.recreateEnumType(key, enum_obj);
- },
- .opaque_type => {
- assert(!already_updating);
- return ty;
- },
+ .struct_type => |key| return pt.recreateStructType(ty, key),
+ .union_type => |key| return pt.recreateUnionType(ty, key),
+ .enum_type => |key| return pt.recreateEnumType(ty, key),
else => unreachable,
}
}
fn recreateStructType(
pt: Zcu.PerThread,
+ old_ty: InternPool.Index,
full_key: InternPool.Key.NamespaceType,
- struct_obj: InternPool.LoadedStructType,
) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3405,8 +3382,7 @@ fn recreateStructType(
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- // The old type will be unused, so drop its dependency information.
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = struct_obj.cau }));
+ const struct_obj = ip.loadStructType(old_ty);
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
.layout = small.layout,
@@ -3428,17 +3404,16 @@ fn recreateStructType(
errdefer wip_ty.cancel(ip, pt.tid);
wip_ty.setName(ip, struct_obj.name);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, struct_obj.namespace, wip_ty.index);
try ip.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ .wrap(.{ .type = wip_ty.index }),
.{ .src_hash = key.zir_index },
);
zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index;
// No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive.
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- const new_ty = wip_ty.finish(ip, new_cau_index.toOptional(), struct_obj.namespace);
+ const new_ty = wip_ty.finish(ip, struct_obj.namespace);
if (inst_info.inst == .main_struct_inst) {
// This is the root type of a file! Update the reference.
zcu.setFileRootType(inst_info.file, new_ty);
@@ -3448,8 +3423,8 @@ fn recreateStructType(
fn recreateUnionType(
pt: Zcu.PerThread,
+ old_ty: InternPool.Index,
full_key: InternPool.Key.NamespaceType,
- union_obj: InternPool.LoadedUnionType,
) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3488,8 +3463,7 @@ fn recreateUnionType(
if (captures_len != key.captures.owned.len) return error.AnalysisFail;
- // The old type will be unused, so drop its dependency information.
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = union_obj.cau }));
+ const union_obj = ip.loadUnionType(old_ty);
const namespace_index = union_obj.namespace;
@@ -3526,22 +3500,21 @@ fn recreateUnionType(
errdefer wip_ty.cancel(ip, pt.tid);
wip_ty.setName(ip, union_obj.name);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
try ip.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ .wrap(.{ .type = wip_ty.index }),
.{ .src_hash = key.zir_index },
);
zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
// No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive.
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
- return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
+ return wip_ty.finish(ip, namespace_index);
}
fn recreateEnumType(
pt: Zcu.PerThread,
+ old_ty: InternPool.Index,
full_key: InternPool.Key.NamespaceType,
- enum_obj: InternPool.LoadedEnumType,
) Zcu.SemaError!InternPool.Index {
const zcu = pt.zcu;
const gpa = zcu.gpa;
@@ -3610,8 +3583,7 @@ fn recreateEnumType(
if (bag != 0) break true;
} else false;
- // The old type will be unused, so drop its dependency information.
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = enum_obj.cau.unwrap().? }));
+ const enum_obj = ip.loadEnumType(old_ty);
const namespace_index = enum_obj.namespace;
@@ -3637,12 +3609,10 @@ fn recreateEnumType(
wip_ty.setName(ip, enum_obj.name);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, key.zir_index, namespace_index, wip_ty.index);
-
zcu.namespacePtr(namespace_index).owner_type = wip_ty.index;
// No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive.
- wip_ty.prepare(ip, new_cau_index, namespace_index);
+ wip_ty.prepare(ip, namespace_index);
done = true;
Sema.resolveDeclaredEnum(
@@ -3652,7 +3622,6 @@ fn recreateEnumType(
key.zir_index,
namespace_index,
enum_obj.name,
- new_cau_index,
small,
body,
tag_type_ref,
src/Compilation.zig
@@ -348,12 +348,15 @@ const Job = union(enum) {
/// Corresponds to the task in `link.Task`.
/// Only needed for backends that haven't yet been updated to not race against Sema.
codegen_type: InternPool.Index,
- /// The `Cau` must be semantically analyzed (and possibly export itself).
+ /// The `AnalUnit`, which is *not* a `func`, must be semantically analyzed.
+ /// This may be its first time being analyzed, or it may be outdated.
+ /// If the unit is a function, a `codegen_func` job will then be queued.
+ analyze_comptime_unit: InternPool.AnalUnit,
+ /// This function must be semantically analyzed.
/// This may be its first time being analyzed, or it may be outdated.
- analyze_cau: InternPool.Cau.Index,
- /// Analyze the body of a runtime function.
/// After analysis, a `codegen_func` job will be queued.
/// These must be separate jobs to ensure any needed type resolution occurs *before* codegen.
+ /// This job is separate from `analyze_comptime_unit` because it has a different priority.
analyze_func: InternPool.Index,
/// The main source file for the module needs to be analyzed.
analyze_mod: *Package.Module,
@@ -3141,8 +3144,10 @@ pub fn getAllErrorsAlloc(comp: *Compilation) !ErrorBundle {
}
const file_index = switch (anal_unit.unwrap()) {
- .cau => |cau| zcu.namespacePtr(ip.getCau(cau).namespace).file_scope,
- .func => |ip_index| (zcu.funcInfo(ip_index).zir_body_inst.resolveFull(ip) orelse continue).file,
+ .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index.resolveFile(ip),
+ .nav_val => |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),
};
// Skip errors for AnalUnits within files that had a parse failure.
@@ -3374,11 +3379,9 @@ pub fn addModuleErrorMsg(
const rt_file_path = try src.file_scope.fullPath(gpa);
defer gpa.free(rt_file_path);
const name = switch (ref.referencer.unwrap()) {
- .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
- .nav => |nav| ip.getNav(nav).name.toSlice(ip),
- .type => |ty| Type.fromInterned(ty).containerTypeName(ip).toSlice(ip),
- .none => "comptime",
- },
+ .@"comptime" => "comptime",
+ .nav_val => |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),
};
try ref_traces.append(gpa, .{
@@ -3641,10 +3644,13 @@ fn performAllTheWorkInner(
// If there's no work queued, check if there's anything outdated
// which we need to work on, and queue it if so.
if (try zcu.findOutdatedToAnalyze()) |outdated| {
- switch (outdated.unwrap()) {
- .cau => |cau| try comp.queueJob(.{ .analyze_cau = cau }),
- .func => |func| try comp.queueJob(.{ .analyze_func = func }),
- }
+ try comp.queueJob(switch (outdated.unwrap()) {
+ .func => |f| .{ .analyze_func = f },
+ .@"comptime",
+ .nav_val,
+ .type,
+ => .{ .analyze_comptime_unit = outdated },
+ });
continue;
}
}
@@ -3667,8 +3673,8 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
.codegen_nav => |nav_index| {
const zcu = comp.zcu.?;
const nav = zcu.intern_pool.getNav(nav_index);
- if (nav.analysis_owner.unwrap()) |cau| {
- const unit = InternPool.AnalUnit.wrap(.{ .cau = cau });
+ if (nav.analysis != null) {
+ const unit: InternPool.AnalUnit = .wrap(.{ .nav_val = nav_index });
if (zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit)) {
return;
}
@@ -3688,36 +3694,47 @@ fn processOneJob(tid: usize, comp: *Compilation, job: Job, prog_node: std.Progre
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
defer pt.deactivate();
- pt.ensureFuncBodyAnalyzed(func) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
+
+ pt.ensureFuncBodyUpToDate(func) catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
error.AnalysisFail => return,
};
},
- .analyze_cau => |cau_index| {
+ .analyze_comptime_unit => |unit| {
+ const named_frame = tracy.namedFrame("analyze_comptime_unit");
+ defer named_frame.end();
+
const pt: Zcu.PerThread = .activate(comp.zcu.?, @enumFromInt(tid));
defer pt.deactivate();
- pt.ensureCauAnalyzed(cau_index) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
+
+ const maybe_err: Zcu.SemaError!void = switch (unit.unwrap()) {
+ .@"comptime" => |cu| pt.ensureComptimeUnitUpToDate(cu),
+ .nav_val => |nav| pt.ensureNavValUpToDate(nav),
+ .type => |ty| if (pt.ensureTypeUpToDate(ty)) |_| {} else |err| err,
+ .func => unreachable,
+ };
+ maybe_err catch |err| switch (err) {
+ error.OutOfMemory => |e| return e,
error.AnalysisFail => return,
};
+
queue_test_analysis: {
if (!comp.config.is_test) break :queue_test_analysis;
+ const nav = switch (unit.unwrap()) {
+ .nav_val => |nav| nav,
+ else => break :queue_test_analysis,
+ };
// Check if this is a test function.
const ip = &pt.zcu.intern_pool;
- const cau = ip.getCau(cau_index);
- const nav_index = switch (cau.owner.unwrap()) {
- .none, .type => break :queue_test_analysis,
- .nav => |nav| nav,
- };
- if (!pt.zcu.test_functions.contains(nav_index)) {
+ if (!pt.zcu.test_functions.contains(nav)) {
break :queue_test_analysis;
}
// Tests are always emitted in test binaries. The decl_refs are created by
// Zcu.populateTestFunctions, but this will not queue body analysis, so do
// that now.
- try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav_index).status.resolved.val);
+ try pt.zcu.ensureFuncBodyAnalysisQueued(ip.getNav(nav).status.resolved.val);
}
},
.resolve_type_fully => |ty| {
src/InternPool.zig
@@ -363,33 +363,53 @@ pub fn rehashTrackedInsts(
}
/// Analysis Unit. Represents a single entity which undergoes semantic analysis.
-/// This is either a `Cau` or a runtime function.
-/// The LSB is used as a tag bit.
/// This is the "source" of an incremental dependency edge.
-pub const AnalUnit = packed struct(u32) {
- kind: enum(u1) { cau, func },
- index: u31,
- pub const Unwrapped = union(enum) {
- cau: Cau.Index,
+pub const AnalUnit = packed struct(u64) {
+ kind: Kind,
+ id: u32,
+
+ pub const Kind = enum(u32) {
+ @"comptime",
+ nav_val,
+ type,
+ func,
+ };
+
+ pub const Unwrapped = union(Kind) {
+ /// This `AnalUnit` analyzes the body of the given `comptime` declaration.
+ @"comptime": ComptimeUnit.Id,
+ /// This `AnalUnit` resolves the value of the given `Nav`.
+ nav_val: Nav.Index,
+ /// This `AnalUnit` resolves the given `struct`/`union`/`enum` type.
+ /// Generated tag enums are never used here (they do not undergo type resolution).
+ type: InternPool.Index,
+ /// This `AnalUnit` analyzes the body of the given runtime function.
func: InternPool.Index,
};
- pub fn unwrap(as: AnalUnit) Unwrapped {
- return switch (as.kind) {
- .cau => .{ .cau = @enumFromInt(as.index) },
- .func => .{ .func = @enumFromInt(as.index) },
+
+ pub fn unwrap(au: AnalUnit) Unwrapped {
+ return switch (au.kind) {
+ inline else => |tag| @unionInit(
+ Unwrapped,
+ @tagName(tag),
+ @enumFromInt(au.id),
+ ),
};
}
pub fn wrap(raw: Unwrapped) AnalUnit {
return switch (raw) {
- .cau => |cau| .{ .kind = .cau, .index = @intCast(@intFromEnum(cau)) },
- .func => |func| .{ .kind = .func, .index = @intCast(@intFromEnum(func)) },
+ inline else => |id, tag| .{
+ .kind = tag,
+ .id = @intFromEnum(id),
+ },
};
}
+
pub fn toOptional(as: AnalUnit) Optional {
- return @enumFromInt(@as(u32, @bitCast(as)));
+ return @enumFromInt(@as(u64, @bitCast(as)));
}
- pub const Optional = enum(u32) {
- none = std.math.maxInt(u32),
+ pub const Optional = enum(u64) {
+ none = std.math.maxInt(u64),
_,
pub fn unwrap(opt: Optional) ?AnalUnit {
return switch (opt) {
@@ -400,97 +420,30 @@ pub const AnalUnit = packed struct(u32) {
};
};
-/// Comptime Analysis Unit. This is the "subject" of semantic analysis where the root context is
-/// comptime; every `Sema` is owned by either a `Cau` or a runtime function (see `AnalUnit`).
-/// The state stored here is immutable.
-///
-/// * Every ZIR `declaration` has a `Cau` (post-instantiation) to analyze the declaration body.
-/// * Every `struct`, `union`, and `enum` has a `Cau` for type resolution.
-///
-/// The analysis status of a `Cau` is known only from state in `Zcu`.
-/// An entry in `Zcu.failed_analysis` indicates an analysis failure with associated error message.
-/// An entry in `Zcu.transitive_failed_analysis` indicates a transitive analysis failure.
-///
-/// 12 bytes.
-pub const Cau = struct {
- /// The `declaration`, `struct_decl`, `enum_decl`, or `union_decl` instruction which this `Cau` analyzes.
+pub const ComptimeUnit = extern struct {
zir_index: TrackedInst.Index,
- /// The namespace which this `Cau` should be analyzed within.
namespace: NamespaceIndex,
- /// This field essentially tells us what to do with the information resulting from
- /// semantic analysis. See `Owner.Unwrapped` for details.
- owner: Owner,
-
- /// See `Owner.Unwrapped` for details. In terms of representation, the `InternPool.Index`
- /// or `Nav.Index` is cast to a `u31` and stored in `index`. As a special case, if
- /// `@as(u32, @bitCast(owner)) == 0xFFFF_FFFF`, then the value is treated as `.none`.
- pub const Owner = packed struct(u32) {
- kind: enum(u1) { type, nav },
- index: u31,
-
- pub const Unwrapped = union(enum) {
- /// This `Cau` exists in isolation. It is a global `comptime` declaration, or (TODO ANYTHING ELSE?).
- /// After semantic analysis completes, the result is discarded.
- none,
- /// This `Cau` is owned by the given type for type resolution.
- /// This is a `struct`, `union`, or `enum` type.
- type: InternPool.Index,
- /// This `Cau` is owned by the given `Nav` to resolve its value.
- /// When analyzing the `Cau`, the resulting value is stored as the value of this `Nav`.
- nav: Nav.Index,
- };
- pub fn unwrap(owner: Owner) Unwrapped {
- if (@as(u32, @bitCast(owner)) == std.math.maxInt(u32)) {
- return .none;
- }
- return switch (owner.kind) {
- .type => .{ .type = @enumFromInt(owner.index) },
- .nav => .{ .nav = @enumFromInt(owner.index) },
- };
- }
-
- fn wrap(raw: Unwrapped) Owner {
- return switch (raw) {
- .none => @bitCast(@as(u32, std.math.maxInt(u32))),
- .type => |ty| .{ .kind = .type, .index = @intCast(@intFromEnum(ty)) },
- .nav => |nav| .{ .kind = .nav, .index = @intCast(@intFromEnum(nav)) },
- };
- }
- };
+ comptime {
+ assert(std.meta.hasUniqueRepresentation(ComptimeUnit));
+ }
- pub const Index = enum(u32) {
+ pub const Id = enum(u32) {
_,
- pub const Optional = enum(u32) {
- none = std.math.maxInt(u32),
- _,
- pub fn unwrap(opt: Optional) ?Cau.Index {
- return switch (opt) {
- .none => null,
- _ => @enumFromInt(@intFromEnum(opt)),
- };
- }
-
- const debug_state = InternPool.debug_state;
- };
- pub fn toOptional(i: Cau.Index) Optional {
- return @enumFromInt(@intFromEnum(i));
- }
const Unwrapped = struct {
tid: Zcu.PerThread.Id,
index: u32,
-
- fn wrap(unwrapped: Unwrapped, ip: *const InternPool) Cau.Index {
+ fn wrap(unwrapped: Unwrapped, ip: *const InternPool) ComptimeUnit.Id {
assert(@intFromEnum(unwrapped.tid) <= ip.getTidMask());
- assert(unwrapped.index <= ip.getIndexMask(u31));
- return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_31 |
+ assert(unwrapped.index <= ip.getIndexMask(u32));
+ return @enumFromInt(@as(u32, @intFromEnum(unwrapped.tid)) << ip.tid_shift_32 |
unwrapped.index);
}
};
- fn unwrap(cau_index: Cau.Index, ip: *const InternPool) Unwrapped {
+ fn unwrap(id: Id, ip: *const InternPool) Unwrapped {
return .{
- .tid = @enumFromInt(@intFromEnum(cau_index) >> ip.tid_shift_31 & ip.getTidMask()),
- .index = @intFromEnum(cau_index) & ip.getIndexMask(u31),
+ .tid = @enumFromInt(@intFromEnum(id) >> ip.tid_shift_32 & ip.getTidMask()),
+ .index = @intFromEnum(id) & ip.getIndexMask(u31),
};
}
@@ -507,6 +460,11 @@ pub const Cau = struct {
/// * Generic instances have a `Nav` corresponding to the instantiated function.
/// * `@extern` calls create a `Nav` whose value is a `.@"extern"`.
///
+/// This data structure is optimized for the `analysis_info != null` case, because this is much more
+/// common in practice; the other case is used only for externs and for generic instances. At the time
+/// of writing, in the compiler itself, around 74% of all `Nav`s have `analysis_info != null`.
+/// (Specifically, 104225 / 140923)
+///
/// `Nav.Repr` is the in-memory representation.
pub const Nav = struct {
/// The unqualified name of this `Nav`. Namespace lookups use this name, and error messages may use it.
@@ -514,13 +472,16 @@ pub const Nav = struct {
name: NullTerminatedString,
/// The fully-qualified name of this `Nav`.
fqn: NullTerminatedString,
- /// If the value of this `Nav` is resolved by semantic analysis, it is within this `Cau`.
- /// If this is `.none`, then `status == .resolved` always.
- analysis_owner: Cau.Index.Optional,
+ /// This field is populated iff this `Nav` is resolved by semantic analysis.
+ /// If this is `null`, then `status == .resolved` always.
+ analysis: ?struct {
+ namespace: NamespaceIndex,
+ zir_index: TrackedInst.Index,
+ },
/// TODO: this is a hack! If #20663 isn't accepted, let's figure out something a bit better.
is_usingnamespace: bool,
status: union(enum) {
- /// This `Nav` is pending semantic analysis through `analysis_owner`.
+ /// This `Nav` is pending semantic analysis.
unresolved,
/// The value of this `Nav` is resolved.
resolved: struct {
@@ -544,17 +505,16 @@ pub const Nav = struct {
/// Get the ZIR instruction corresponding to this `Nav`, used to resolve source locations.
/// This is a `declaration`.
pub fn srcInst(nav: Nav, ip: *const InternPool) TrackedInst.Index {
- if (nav.analysis_owner.unwrap()) |cau| {
- return ip.getCau(cau).zir_index;
+ if (nav.analysis) |a| {
+ return a.zir_index;
}
- // A `Nav` with no corresponding `Cau` always has a resolved value.
+ // A `Nav` which does not undergo analysis always has a resolved value.
return switch (ip.indexToKey(nav.status.resolved.val)) {
.func => |func| {
- // Since there was no `analysis_owner`, this must be an instantiation.
- // Go up to the generic owner and consult *its* `analysis_owner`.
+ // Since `analysis` was not populated, this must be an instantiation.
+ // Go up to the generic owner and consult *its* `analysis` field.
const go_nav = ip.getNav(ip.indexToKey(func.generic_owner).func.owner_nav);
- const go_cau = ip.getCau(go_nav.analysis_owner.unwrap().?);
- return go_cau.zir_index;
+ return go_nav.analysis.?.zir_index;
},
.@"extern" => |@"extern"| @"extern".zir_index, // extern / @extern
else => unreachable,
@@ -600,11 +560,13 @@ pub const Nav = struct {
};
/// The compact in-memory representation of a `Nav`.
- /// 18 bytes.
+ /// 26 bytes.
const Repr = struct {
name: NullTerminatedString,
fqn: NullTerminatedString,
- analysis_owner: Cau.Index.Optional,
+ // The following 1 fields are either both populated, or both `.none`.
+ analysis_namespace: OptionalNamespaceIndex,
+ analysis_zir_index: TrackedInst.Index.Optional,
/// Populated only if `bits.status == .resolved`.
val: InternPool.Index,
/// Populated only if `bits.status == .resolved`.
@@ -625,7 +587,13 @@ pub const Nav = struct {
return .{
.name = repr.name,
.fqn = repr.fqn,
- .analysis_owner = repr.analysis_owner,
+ .analysis = if (repr.analysis_namespace.unwrap()) |namespace| .{
+ .namespace = namespace,
+ .zir_index = repr.analysis_zir_index.unwrap().?,
+ } else a: {
+ assert(repr.analysis_zir_index == .none);
+ break :a null;
+ },
.is_usingnamespace = repr.bits.is_usingnamespace,
.status = switch (repr.bits.status) {
.unresolved => .unresolved,
@@ -646,7 +614,8 @@ pub const Nav = struct {
return .{
.name = nav.name,
.fqn = nav.fqn,
- .analysis_owner = nav.analysis_owner,
+ .analysis_namespace = if (nav.analysis) |a| a.namespace.toOptional() else .none,
+ .analysis_zir_index = if (nav.analysis) |a| a.zir_index.toOptional() else .none,
.val = switch (nav.status) {
.unresolved => .none,
.resolved => |r| r.val,
@@ -862,8 +831,8 @@ const Local = struct {
tracked_insts: ListMutate,
files: ListMutate,
maps: ListMutate,
- caus: ListMutate,
navs: ListMutate,
+ comptime_units: ListMutate,
namespaces: BucketListMutate,
} align(std.atomic.cache_line),
@@ -876,8 +845,8 @@ const Local = struct {
tracked_insts: TrackedInsts,
files: List(File),
maps: Maps,
- caus: Caus,
navs: Navs,
+ comptime_units: ComptimeUnits,
namespaces: Namespaces,
@@ -899,8 +868,8 @@ const Local = struct {
const Strings = List(struct { u8 });
const TrackedInsts = List(struct { TrackedInst.MaybeLost });
const Maps = List(struct { FieldMap });
- const Caus = List(struct { Cau });
const Navs = List(Nav.Repr);
+ const ComptimeUnits = List(struct { ComptimeUnit });
const namespaces_bucket_width = 8;
const namespaces_bucket_mask = (1 << namespaces_bucket_width) - 1;
@@ -1275,21 +1244,21 @@ const Local = struct {
};
}
- pub fn getMutableCaus(local: *Local, gpa: Allocator) Caus.Mutable {
+ pub fn getMutableNavs(local: *Local, gpa: Allocator) Navs.Mutable {
return .{
.gpa = gpa,
.arena = &local.mutate.arena,
- .mutate = &local.mutate.caus,
- .list = &local.shared.caus,
+ .mutate = &local.mutate.navs,
+ .list = &local.shared.navs,
};
}
- pub fn getMutableNavs(local: *Local, gpa: Allocator) Navs.Mutable {
+ pub fn getMutableComptimeUnits(local: *Local, gpa: Allocator) ComptimeUnits.Mutable {
return .{
.gpa = gpa,
.arena = &local.mutate.arena,
- .mutate = &local.mutate.navs,
- .list = &local.shared.navs,
+ .mutate = &local.mutate.comptime_units,
+ .list = &local.shared.comptime_units,
};
}
@@ -3052,8 +3021,6 @@ pub const LoadedUnionType = struct {
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this union type.
name: NullTerminatedString,
- /// The `Cau` within which type resolution occurs.
- cau: Cau.Index,
/// Represents the declarations inside this union.
namespace: NamespaceIndex,
/// The enum tag type.
@@ -3370,7 +3337,6 @@ pub fn loadUnionType(ip: *const InternPool, index: Index) LoadedUnionType {
.tid = unwrapped_index.tid,
.extra_index = data,
.name = type_union.data.name,
- .cau = type_union.data.cau,
.namespace = type_union.data.namespace,
.enum_tag_ty = type_union.data.tag_ty,
.field_types = field_types,
@@ -3387,8 +3353,6 @@ pub const LoadedStructType = struct {
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this struct type.
name: NullTerminatedString,
- /// The `Cau` within which type resolution occurs.
- cau: Cau.Index,
namespace: NamespaceIndex,
/// Index of the `struct_decl` or `reify` ZIR instruction.
zir_index: TrackedInst.Index,
@@ -3979,7 +3943,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
switch (item.tag) {
.type_struct => {
const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "name").?]);
- const cau: Cau.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "cau").?]);
const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?]);
const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?]);
const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStruct, "fields_len").?];
@@ -4066,7 +4029,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.tid = unwrapped_index.tid,
.extra_index = item.data,
.name = name,
- .cau = cau,
.namespace = namespace,
.zir_index = zir_index,
.layout = if (flags.is_extern) .@"extern" else .auto,
@@ -4083,7 +4045,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
},
.type_struct_packed, .type_struct_packed_inits => {
const name: NullTerminatedString = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?]);
- const cau: Cau.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "cau").?]);
const zir_index: TrackedInst.Index = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "zir_index").?]);
const fields_len = extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "fields_len").?];
const namespace: NamespaceIndex = @enumFromInt(extra_items[item.data + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?]);
@@ -4130,7 +4091,6 @@ pub fn loadStructType(ip: *const InternPool, index: Index) LoadedStructType {
.tid = unwrapped_index.tid,
.extra_index = item.data,
.name = name,
- .cau = cau,
.namespace = namespace,
.zir_index = zir_index,
.layout = .@"packed",
@@ -4153,9 +4113,6 @@ pub const LoadedEnumType = struct {
// TODO: the non-fqn will be needed by the new dwarf structure
/// The name of this enum type.
name: NullTerminatedString,
- /// The `Cau` within which type resolution occurs.
- /// `null` if this is a generated tag type.
- cau: Cau.Index.Optional,
/// Represents the declarations inside this enum.
namespace: NamespaceIndex,
/// An integer type which is used for the numerical value of the enum.
@@ -4232,21 +4189,15 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
.type_enum_auto => {
const extra = extraDataTrail(extra_list, EnumAuto, item.data);
var extra_index: u32 = @intCast(extra.end);
- const cau: Cau.Index.Optional = if (extra.data.zir_index == .none) cau: {
+ if (extra.data.zir_index == .none) {
extra_index += 1; // owner_union
- break :cau .none;
- } else cau: {
- const cau: Cau.Index = @enumFromInt(extra_list.view().items(.@"0")[extra_index]);
- extra_index += 1; // cau
- break :cau cau.toOptional();
- };
+ }
const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
extra_index += 2; // type_hash: PackedU64
break :c 0;
} else extra.data.captures_len;
return .{
.name = extra.data.name,
- .cau = cau,
.namespace = extra.data.namespace,
.tag_ty = extra.data.int_tag_type,
.names = .{
@@ -4272,21 +4223,15 @@ pub fn loadEnumType(ip: *const InternPool, index: Index) LoadedEnumType {
};
const extra = extraDataTrail(extra_list, EnumExplicit, item.data);
var extra_index: u32 = @intCast(extra.end);
- const cau: Cau.Index.Optional = if (extra.data.zir_index == .none) cau: {
+ if (extra.data.zir_index == .none) {
extra_index += 1; // owner_union
- break :cau .none;
- } else cau: {
- const cau: Cau.Index = @enumFromInt(extra_list.view().items(.@"0")[extra_index]);
- extra_index += 1; // cau
- break :cau cau.toOptional();
- };
+ }
const captures_len = if (extra.data.captures_len == std.math.maxInt(u32)) c: {
extra_index += 2; // type_hash: PackedU64
break :c 0;
} else extra.data.captures_len;
return .{
.name = extra.data.name,
- .cau = cau,
.namespace = extra.data.namespace,
.tag_ty = extra.data.int_tag_type,
.names = .{
@@ -5256,7 +5201,6 @@ pub const Tag = enum(u8) {
.payload = EnumExplicit,
.trailing = struct {
owner_union: Index,
- cau: ?Cau.Index,
captures: ?[]CaptureValue,
type_hash: ?u64,
field_names: []NullTerminatedString,
@@ -5302,7 +5246,6 @@ pub const Tag = enum(u8) {
.payload = EnumAuto,
.trailing = struct {
owner_union: ?Index,
- cau: ?Cau.Index,
captures: ?[]CaptureValue,
type_hash: ?u64,
field_names: []NullTerminatedString,
@@ -5679,7 +5622,6 @@ pub const Tag = enum(u8) {
size: u32,
/// Only valid after .have_layout
padding: u32,
- cau: Cau.Index,
namespace: NamespaceIndex,
/// The enum that provides the list of field names and values.
tag_ty: Index,
@@ -5710,7 +5652,6 @@ pub const Tag = enum(u8) {
/// 5. init: Index for each fields_len // if tag is type_struct_packed_inits
pub const TypeStructPacked = struct {
name: NullTerminatedString,
- cau: Cau.Index,
zir_index: TrackedInst.Index,
fields_len: u32,
namespace: NamespaceIndex,
@@ -5758,7 +5699,6 @@ pub const Tag = enum(u8) {
/// 8. field_offset: u32 // for each field in declared order, undef until layout_resolved
pub const TypeStruct = struct {
name: NullTerminatedString,
- cau: Cau.Index,
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
fields_len: u32,
@@ -6088,11 +6028,10 @@ pub const Array = struct {
/// Trailing:
/// 0. owner_union: Index // if `zir_index == .none`
-/// 1. cau: Cau.Index // if `zir_index != .none`
-/// 2. capture: CaptureValue // for each `captures_len`
-/// 3. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
-/// 4. field name: NullTerminatedString for each fields_len; declaration order
-/// 5. tag value: Index for each fields_len; declaration order
+/// 1. capture: CaptureValue // for each `captures_len`
+/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
+/// 3. field name: NullTerminatedString for each fields_len; declaration order
+/// 4. tag value: Index for each fields_len; declaration order
pub const EnumExplicit = struct {
name: NullTerminatedString,
/// `std.math.maxInt(u32)` indicates this type is reified.
@@ -6115,10 +6054,9 @@ pub const EnumExplicit = struct {
/// Trailing:
/// 0. owner_union: Index // if `zir_index == .none`
-/// 1. cau: Cau.Index // if `zir_index != .none`
-/// 2. capture: CaptureValue // for each `captures_len`
-/// 3. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
-/// 4. field name: NullTerminatedString for each fields_len; declaration order
+/// 1. capture: CaptureValue // for each `captures_len`
+/// 2. type_hash: PackedU64 // if reified (`captures_len == std.math.maxInt(u32)`)
+/// 3. field name: NullTerminatedString for each fields_len; declaration order
pub const EnumAuto = struct {
name: NullTerminatedString,
/// `std.math.maxInt(u32)` indicates this type is reified.
@@ -6408,32 +6346,32 @@ pub fn init(ip: *InternPool, gpa: Allocator, available_threads: usize) !void {
ip.locals = try gpa.alloc(Local, used_threads);
@memset(ip.locals, .{
.shared = .{
- .items = Local.List(Item).empty,
- .extra = Local.Extra.empty,
- .limbs = Local.Limbs.empty,
- .strings = Local.Strings.empty,
- .tracked_insts = Local.TrackedInsts.empty,
- .files = Local.List(File).empty,
- .maps = Local.Maps.empty,
- .caus = Local.Caus.empty,
- .navs = Local.Navs.empty,
-
- .namespaces = Local.Namespaces.empty,
+ .items = .empty,
+ .extra = .empty,
+ .limbs = .empty,
+ .strings = .empty,
+ .tracked_insts = .empty,
+ .files = .empty,
+ .maps = .empty,
+ .navs = .empty,
+ .comptime_units = .empty,
+
+ .namespaces = .empty,
},
.mutate = .{
.arena = .{},
- .items = Local.ListMutate.empty,
- .extra = Local.ListMutate.empty,
- .limbs = Local.ListMutate.empty,
- .strings = Local.ListMutate.empty,
- .tracked_insts = Local.ListMutate.empty,
- .files = Local.ListMutate.empty,
- .maps = Local.ListMutate.empty,
- .caus = Local.ListMutate.empty,
- .navs = Local.ListMutate.empty,
+ .items = .empty,
+ .extra = .empty,
+ .limbs = .empty,
+ .strings = .empty,
+ .tracked_insts = .empty,
+ .files = .empty,
+ .maps = .empty,
+ .navs = .empty,
+ .comptime_units = .empty,
- .namespaces = Local.BucketListMutate.empty,
+ .namespaces = .empty,
},
});
@@ -6506,7 +6444,8 @@ pub fn deinit(ip: *InternPool, gpa: Allocator) void {
namespace.priv_decls.deinit(gpa);
namespace.pub_usingnamespace.deinit(gpa);
namespace.priv_usingnamespace.deinit(gpa);
- namespace.other_decls.deinit(gpa);
+ namespace.comptime_decls.deinit(gpa);
+ namespace.test_decls.deinit(gpa);
}
};
const maps = local.getMutableMaps(gpa);
@@ -6525,8 +6464,6 @@ pub fn activate(ip: *const InternPool) void {
_ = OptionalString.debug_state;
_ = NullTerminatedString.debug_state;
_ = OptionalNullTerminatedString.debug_state;
- _ = Cau.Index.debug_state;
- _ = Cau.Index.Optional.debug_state;
_ = Nav.Index.debug_state;
_ = Nav.Index.Optional.debug_state;
std.debug.assert(debug_state.intern_pool == null);
@@ -6711,14 +6648,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
if (extra.data.captures_len == std.math.maxInt(u32)) {
break :ns .{ .reified = .{
.zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(),
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
} };
}
break :ns .{ .declared = .{
.zir_index = zir_index,
.captures = .{ .owned = .{
.tid = unwrapped_index.tid,
- .start = extra.end + 1,
+ .start = extra.end,
.len = extra.data.captures_len,
} },
} };
@@ -6735,14 +6672,14 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
if (extra.data.captures_len == std.math.maxInt(u32)) {
break :ns .{ .reified = .{
.zir_index = zir_index,
- .type_hash = extraData(extra_list, PackedU64, extra.end + 1).get(),
+ .type_hash = extraData(extra_list, PackedU64, extra.end).get(),
} };
}
break :ns .{ .declared = .{
.zir_index = zir_index,
.captures = .{ .owned = .{
.tid = unwrapped_index.tid,
- .start = extra.end + 1,
+ .start = extra.end,
.len = extra.data.captures_len,
} },
} };
@@ -8323,7 +8260,6 @@ pub fn getUnionType(
.size = std.math.maxInt(u32),
.padding = std.math.maxInt(u32),
.name = undefined, // set by `finish`
- .cau = undefined, // set by `finish`
.namespace = undefined, // set by `finish`
.tag_ty = ini.enum_tag_ty,
.zir_index = switch (ini.key) {
@@ -8375,7 +8311,6 @@ pub fn getUnionType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "name").?,
- .cau_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "cau").?,
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeUnion, "namespace").?,
} };
}
@@ -8384,7 +8319,6 @@ pub const WipNamespaceType = struct {
tid: Zcu.PerThread.Id,
index: Index,
type_name_extra_index: u32,
- cau_extra_index: ?u32,
namespace_extra_index: u32,
pub fn setName(
@@ -8400,18 +8334,11 @@ pub const WipNamespaceType = struct {
pub fn finish(
wip: WipNamespaceType,
ip: *InternPool,
- analysis_owner: Cau.Index.Optional,
namespace: NamespaceIndex,
) Index {
const extra = ip.getLocalShared(wip.tid).extra.acquire();
const extra_items = extra.view().items(.@"0");
- if (wip.cau_extra_index) |i| {
- extra_items[i] = @intFromEnum(analysis_owner.unwrap().?);
- } else {
- assert(analysis_owner == .none);
- }
-
extra_items[wip.namespace_extra_index] = @intFromEnum(namespace);
return wip.index;
@@ -8510,7 +8437,6 @@ pub fn getStructType(
ini.fields_len); // inits
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStructPacked{
.name = undefined, // set by `finish`
- .cau = undefined, // set by `finish`
.zir_index = zir_index,
.fields_len = ini.fields_len,
.namespace = undefined, // set by `finish`
@@ -8555,7 +8481,6 @@ pub fn getStructType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "name").?,
- .cau_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "cau").?,
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStructPacked, "namespace").?,
} };
},
@@ -8578,7 +8503,6 @@ pub fn getStructType(
1); // names_map
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeStruct{
.name = undefined, // set by `finish`
- .cau = undefined, // set by `finish`
.zir_index = zir_index,
.namespace = undefined, // set by `finish`
.fields_len = ini.fields_len,
@@ -8647,7 +8571,6 @@ pub fn getStructType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "name").?,
- .cau_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "cau").?,
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeStruct, "namespace").?,
} };
}
@@ -9383,7 +9306,7 @@ fn finishFuncInstance(
func_extra_index: u32,
) Allocator.Error!void {
const fn_owner_nav = ip.getNav(ip.funcDeclInfo(generic_owner).owner_nav);
- const fn_namespace = ip.getCau(fn_owner_nav.analysis_owner.unwrap().?).namespace;
+ const fn_namespace = fn_owner_nav.analysis.?.namespace;
// TODO: improve this name
const nav_name = try ip.getOrPutStringFmt(gpa, tid, "{}__anon_{d}", .{
@@ -9429,7 +9352,6 @@ pub const WipEnumType = struct {
index: Index,
tag_ty_index: u32,
type_name_extra_index: u32,
- cau_extra_index: u32,
namespace_extra_index: u32,
names_map: MapIndex,
names_start: u32,
@@ -9449,13 +9371,11 @@ pub const WipEnumType = struct {
pub fn prepare(
wip: WipEnumType,
ip: *InternPool,
- analysis_owner: Cau.Index,
namespace: NamespaceIndex,
) void {
const extra = ip.getLocalShared(wip.tid).extra.acquire();
const extra_items = extra.view().items(.@"0");
- extra_items[wip.cau_extra_index] = @intFromEnum(analysis_owner);
extra_items[wip.namespace_extra_index] = @intFromEnum(namespace);
}
@@ -9556,7 +9476,6 @@ pub fn getEnumType(
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
- 1 + // cau
ini.fields_len); // field types
const extra_index = addExtraAssumeCapacity(extra, EnumAuto{
@@ -9577,8 +9496,6 @@ pub fn getEnumType(
.tag = .type_enum_auto,
.data = extra_index,
});
- const cau_extra_index = extra.view().len;
- extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`
switch (ini.key) {
.declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
.declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
@@ -9591,7 +9508,6 @@ pub fn getEnumType(
.index = gop.put(),
.tag_ty_index = extra_index + std.meta.fieldIndex(EnumAuto, "int_tag_type").?,
.type_name_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "name").?,
- .cau_extra_index = @intCast(cau_extra_index),
.namespace_extra_index = extra_index + std.meta.fieldIndex(EnumAuto, "namespace").?,
.names_map = names_map,
.names_start = @intCast(names_start),
@@ -9616,7 +9532,6 @@ pub fn getEnumType(
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
- 1 + // cau
ini.fields_len + // field types
ini.fields_len * @intFromBool(ini.has_values)); // field values
@@ -9643,8 +9558,6 @@ pub fn getEnumType(
},
.data = extra_index,
});
- const cau_extra_index = extra.view().len;
- extra.appendAssumeCapacity(undefined); // `cau` will be set by `finish`
switch (ini.key) {
.declared => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)}),
.declared_owned_captures => |d| extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))}),
@@ -9661,7 +9574,6 @@ pub fn getEnumType(
.index = gop.put(),
.tag_ty_index = extra_index + std.meta.fieldIndex(EnumExplicit, "int_tag_type").?,
.type_name_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "name").?,
- .cau_extra_index = @intCast(cau_extra_index),
.namespace_extra_index = extra_index + std.meta.fieldIndex(EnumExplicit, "namespace").?,
.names_map = names_map,
.names_start = @intCast(names_start),
@@ -9858,7 +9770,6 @@ pub fn getOpaqueType(
.tid = tid,
.index = gop.put(),
.type_name_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "name").?,
- .cau_extra_index = null, // opaques do not undergo type resolution
.namespace_extra_index = extra_index + std.meta.fieldIndex(Tag.TypeOpaque, "namespace").?,
},
};
@@ -9974,7 +9885,6 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
inline for (@typeInfo(@TypeOf(item)).@"struct".fields) |field| {
extra.appendAssumeCapacity(.{switch (field.type) {
Index,
- Cau.Index,
Nav.Index,
NamespaceIndex,
OptionalNamespaceIndex,
@@ -10037,7 +9947,6 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
const extra_item = extra_items[extra_index];
@field(result, field.name) = switch (field.type) {
Index,
- Cau.Index,
Nav.Index,
NamespaceIndex,
OptionalNamespaceIndex,
@@ -11058,12 +10967,6 @@ pub fn dumpGenericInstancesFallible(ip: *const InternPool, allocator: Allocator)
try bw.flush();
}
-pub fn getCau(ip: *const InternPool, index: Cau.Index) Cau {
- const unwrapped = index.unwrap(ip);
- const caus = ip.getLocalShared(unwrapped.tid).caus.acquire();
- return caus.view().items(.@"0")[unwrapped.index];
-}
-
pub fn getNav(ip: *const InternPool, index: Nav.Index) Nav {
const unwrapped = index.unwrap(ip);
const navs = ip.getLocalShared(unwrapped.tid).navs.acquire();
@@ -11077,51 +10980,34 @@ pub fn namespacePtr(ip: *InternPool, namespace_index: NamespaceIndex) *Zcu.Names
return &namespaces_bucket[unwrapped_namespace_index.index];
}
-/// Create a `Cau` associated with the type at the given `InternPool.Index`.
-pub fn createTypeCau(
+/// Create a `ComptimeUnit`, forming an `AnalUnit` for a `comptime` declaration.
+pub fn createComptimeUnit(
ip: *InternPool,
gpa: Allocator,
tid: Zcu.PerThread.Id,
zir_index: TrackedInst.Index,
namespace: NamespaceIndex,
- owner_type: InternPool.Index,
-) Allocator.Error!Cau.Index {
- const caus = ip.getLocal(tid).getMutableCaus(gpa);
- const index_unwrapped: Cau.Index.Unwrapped = .{
+) Allocator.Error!ComptimeUnit.Id {
+ const comptime_units = ip.getLocal(tid).getMutableComptimeUnits(gpa);
+ const id_unwrapped: ComptimeUnit.Id.Unwrapped = .{
.tid = tid,
- .index = caus.mutate.len,
+ .index = comptime_units.mutate.len,
};
- try caus.append(.{.{
+ try comptime_units.append(.{.{
.zir_index = zir_index,
.namespace = namespace,
- .owner = Cau.Owner.wrap(.{ .type = owner_type }),
}});
- return index_unwrapped.wrap(ip);
+ return id_unwrapped.wrap(ip);
}
-/// Create a `Cau` for a `comptime` declaration.
-pub fn createComptimeCau(
- ip: *InternPool,
- gpa: Allocator,
- tid: Zcu.PerThread.Id,
- zir_index: TrackedInst.Index,
- namespace: NamespaceIndex,
-) Allocator.Error!Cau.Index {
- const caus = ip.getLocal(tid).getMutableCaus(gpa);
- const index_unwrapped: Cau.Index.Unwrapped = .{
- .tid = tid,
- .index = caus.mutate.len,
- };
- try caus.append(.{.{
- .zir_index = zir_index,
- .namespace = namespace,
- .owner = Cau.Owner.wrap(.none),
- }});
- return index_unwrapped.wrap(ip);
+pub fn getComptimeUnit(ip: *const InternPool, id: ComptimeUnit.Id) ComptimeUnit {
+ const unwrapped = id.unwrap(ip);
+ const comptime_units = ip.getLocalShared(unwrapped.tid).comptime_units.acquire();
+ return comptime_units.view().items(.@"0")[unwrapped.index];
}
-/// Create a `Nav` not associated with any `Cau`.
-/// Since there is no analysis owner, the `Nav`'s value must be known at creation time.
+/// Create a `Nav` which does not undergo semantic analysis.
+/// Since it is never analyzed, the `Nav`'s value must be known at creation time.
pub fn createNav(
ip: *InternPool,
gpa: Allocator,
@@ -11143,7 +11029,7 @@ pub fn createNav(
try navs.append(Nav.pack(.{
.name = opts.name,
.fqn = opts.fqn,
- .analysis_owner = .none,
+ .analysis = null,
.status = .{ .resolved = .{
.val = opts.val,
.alignment = opts.alignment,
@@ -11155,10 +11041,9 @@ pub fn createNav(
return index_unwrapped.wrap(ip);
}
-/// Create a `Cau` and `Nav` which are paired. The value of the `Nav` is
-/// determined by semantic analysis of the `Cau`. The value of the `Nav`
-/// is initially unresolved.
-pub fn createPairedCauNav(
+/// Create a `Nav` which undergoes semantic analysis because it corresponds to a source declaration.
+/// The value of the `Nav` is initially unresolved.
+pub fn createDeclNav(
ip: *InternPool,
gpa: Allocator,
tid: Zcu.PerThread.Id,
@@ -11168,36 +11053,28 @@ pub fn createPairedCauNav(
namespace: NamespaceIndex,
/// TODO: this is hacky! See `Nav.is_usingnamespace`.
is_usingnamespace: bool,
-) Allocator.Error!struct { Cau.Index, Nav.Index } {
- const caus = ip.getLocal(tid).getMutableCaus(gpa);
+) Allocator.Error!Nav.Index {
const navs = ip.getLocal(tid).getMutableNavs(gpa);
- try caus.ensureUnusedCapacity(1);
try navs.ensureUnusedCapacity(1);
- const cau = Cau.Index.Unwrapped.wrap(.{
- .tid = tid,
- .index = caus.mutate.len,
- }, ip);
const nav = Nav.Index.Unwrapped.wrap(.{
.tid = tid,
.index = navs.mutate.len,
}, ip);
- caus.appendAssumeCapacity(.{.{
- .zir_index = zir_index,
- .namespace = namespace,
- .owner = Cau.Owner.wrap(.{ .nav = nav }),
- }});
navs.appendAssumeCapacity(Nav.pack(.{
.name = name,
.fqn = fqn,
- .analysis_owner = cau.toOptional(),
+ .analysis = .{
+ .namespace = namespace,
+ .zir_index = zir_index,
+ },
.status = .unresolved,
.is_usingnamespace = is_usingnamespace,
}));
- return .{ cau, nav };
+ return nav;
}
/// Resolve the value of a `Nav` with an analysis owner.
@@ -11220,12 +11097,14 @@ pub fn resolveNavValue(
const navs = local.shared.navs.view();
- const nav_analysis_owners = navs.items(.analysis_owner);
+ const nav_analysis_namespace = navs.items(.analysis_namespace);
+ const nav_analysis_zir_index = navs.items(.analysis_zir_index);
const nav_vals = navs.items(.val);
const nav_linksections = navs.items(.@"linksection");
const nav_bits = navs.items(.bits);
- assert(nav_analysis_owners[unwrapped.index] != .none);
+ assert(nav_analysis_namespace[unwrapped.index] != .none);
+ assert(nav_analysis_zir_index[unwrapped.index] != .none);
@atomicStore(InternPool.Index, &nav_vals[unwrapped.index], resolved.val, .release);
@atomicStore(OptionalNullTerminatedString, &nav_linksections[unwrapped.index], resolved.@"linksection", .release);
src/Sema.zig
@@ -2870,7 +2870,7 @@ fn zirStructDecl(
};
const wip_ty = switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
.existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty, false);
+ const new_ty = try pt.ensureTypeUpToDate(ty);
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -2900,12 +2900,10 @@ fn zirStructDecl(
});
errdefer pt.destroyNamespace(new_namespace_index);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
if (pt.zcu.comp.incremental) {
try ip.addDependency(
sema.gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ AnalUnit.wrap(.{ .type = wip_ty.index }),
.{ .src_hash = tracked_inst },
);
}
@@ -2922,7 +2920,7 @@ fn zirStructDecl(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn createTypeName(
@@ -3100,7 +3098,7 @@ fn zirEnumDecl(
};
const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
.existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty, false);
+ const new_ty = try pt.ensureTypeUpToDate(ty);
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -3136,16 +3134,14 @@ fn zirEnumDecl(
});
errdefer if (!done) pt.destroyNamespace(new_namespace_index);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try pt.scanNamespace(new_namespace_index, decls);
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
// We've finished the initial construction of this type, and are about to perform analysis.
- // Set the Cau and namespace appropriately, and don't destroy anything on failure.
- wip_ty.prepare(ip, new_cau_index, new_namespace_index);
+ // Set the namespace appropriately, and don't destroy anything on failure.
+ wip_ty.prepare(ip, new_namespace_index);
done = true;
try Sema.resolveDeclaredEnum(
@@ -3155,7 +3151,6 @@ fn zirEnumDecl(
tracked_inst,
new_namespace_index,
type_name,
- new_cau_index,
small,
body,
tag_type_ref,
@@ -3245,7 +3240,7 @@ fn zirUnionDecl(
};
const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
.existing => |ty| {
- const new_ty = try pt.ensureTypeUpToDate(ty, false);
+ const new_ty = try pt.ensureTypeUpToDate(ty);
// Make sure we update the namespace if the declaration is re-analyzed, to pick
// up on e.g. changed comptime decls.
@@ -3275,12 +3270,10 @@ fn zirUnionDecl(
});
errdefer pt.destroyNamespace(new_namespace_index);
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
if (pt.zcu.comp.incremental) {
try zcu.intern_pool.addDependency(
gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
+ AnalUnit.wrap(.{ .type = wip_ty.index }),
.{ .src_hash = tracked_inst },
);
}
@@ -3297,7 +3290,7 @@ fn zirUnionDecl(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn zirOpaqueDecl(
@@ -3382,7 +3375,7 @@ fn zirOpaqueDecl(
try zcu.comp.queueJob(.{ .codegen_type = wip_ty.index });
}
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn zirErrorSetDecl(
@@ -6547,7 +6540,10 @@ fn zirDisableInstrumentation(sema: *Sema) CompileError!void {
const ip = &zcu.intern_pool;
const func = switch (sema.owner.unwrap()) {
.func => |func| func,
- .cau => return, // does nothing outside a function
+ .@"comptime",
+ .nav_val,
+ .type,
+ => return, // does nothing outside a function
};
ip.funcSetDisableInstrumentation(func);
sema.allow_memoize = false;
@@ -6868,11 +6864,8 @@ fn lookupInNamespace(
ignore_self: {
const skip_nav = switch (sema.owner.unwrap()) {
- .func => break :ignore_self,
- .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
- .none, .type => break :ignore_self,
- .nav => |nav| nav,
- },
+ .@"comptime", .type, .func => break :ignore_self,
+ .nav_val => |nav| nav,
};
var i: usize = 0;
while (i < candidates.items.len) {
@@ -7132,7 +7125,7 @@ fn zirCall(
const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
switch (sema.owner.unwrap()) {
- .cau => input_is_error = false,
+ .@"comptime", .type, .nav_val => input_is_error = false,
.func => |owner_func| if (!zcu.intern_pool.funcAnalysisUnordered(owner_func).calls_or_awaits_errorable_fn) {
// No errorable fn actually called; we have no error return trace
input_is_error = false;
@@ -7747,11 +7740,9 @@ fn analyzeCall(
// The call site definitely depends on the function's signature.
try sema.declareDependency(.{ .src_hash = module_fn.zir_body_inst });
- // This is not a function instance, so the function's `Nav` has a
- // `Cau` -- we don't need to check `generic_owner`.
+ // This is not a function instance, so the function's `Nav` has analysis
+ // state -- we don't need to check `generic_owner`.
const fn_nav = ip.getNav(module_fn.owner_nav);
- const fn_cau_index = fn_nav.analysis_owner.unwrap().?;
- const fn_cau = ip.getCau(fn_cau_index);
// We effectively want a child Sema here, but can't literally do that, because we need AIR
// to be shared. InlineCallSema is a wrapper which handles this for us. While `ics` is in
@@ -7759,7 +7750,7 @@ fn analyzeCall(
// whenever performing an operation where the difference matters.
var ics = InlineCallSema.init(
sema,
- zcu.cauFileScope(fn_cau_index).zir,
+ zcu.navFileScope(module_fn.owner_nav).zir,
module_fn_index,
block.error_return_trace_index,
);
@@ -7769,7 +7760,7 @@ fn analyzeCall(
.parent = null,
.sema = sema,
// The function body exists in the same namespace as the corresponding function declaration.
- .namespace = fn_cau.namespace,
+ .namespace = fn_nav.analysis.?.namespace,
.instructions = .{},
.label = null,
.inlining = &inlining,
@@ -7780,7 +7771,7 @@ fn analyzeCall(
.runtime_cond = block.runtime_cond,
.runtime_loop = block.runtime_loop,
.runtime_index = block.runtime_index,
- .src_base_inst = fn_cau.zir_index,
+ .src_base_inst = fn_nav.analysis.?.zir_index,
.type_name_ctx = fn_nav.fqn,
};
@@ -7795,7 +7786,7 @@ fn analyzeCall(
// mutate comptime state.
// TODO: comptime call memoization is currently not supported under incremental compilation
// since dependencies are not marked on callers. If we want to keep this around (we should
- // check that it's worthwhile first!), each memoized call needs a `Cau`.
+ // check that it's worthwhile first!), each memoized call needs an `AnalUnit`.
var should_memoize = !zcu.comp.incremental;
// If it's a comptime function call, we need to memoize it as long as no external
@@ -7904,7 +7895,7 @@ fn analyzeCall(
// Since we're doing an inline call, we depend on the source code of the whole
// function declaration.
- try sema.declareDependency(.{ .src_hash = fn_cau.zir_index });
+ try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index });
new_fn_info.return_type = sema.fn_ret_ty.toIntern();
if (!is_comptime_call and !block.is_typeof) {
@@ -8016,7 +8007,7 @@ fn analyzeCall(
if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
switch (sema.owner.unwrap()) {
- .cau => {},
+ .@"comptime", .nav_val, .type => {},
.func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
},
@@ -8268,10 +8259,9 @@ fn instantiateGenericCall(
// The actual monomorphization happens via adding `func_instance` to
// `InternPool`.
- // Since we are looking at the generic owner here, it has a `Cau`.
+ // Since we are looking at the generic owner here, it has analysis state.
const fn_nav = ip.getNav(generic_owner_func.owner_nav);
- const fn_cau = ip.getCau(fn_nav.analysis_owner.unwrap().?);
- const fn_zir = zcu.namespacePtr(fn_cau.namespace).fileScope(zcu).zir;
+ const fn_zir = zcu.navFileScope(generic_owner_func.owner_nav).zir;
const fn_info = fn_zir.getFnInfo(generic_owner_func.zir_body_inst.resolve(ip) orelse return error.AnalysisFail);
const comptime_args = try sema.arena.alloc(InternPool.Index, args_info.count());
@@ -8312,11 +8302,11 @@ fn instantiateGenericCall(
var child_block: Block = .{
.parent = null,
.sema = &child_sema,
- .namespace = fn_cau.namespace,
+ .namespace = fn_nav.analysis.?.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = true,
- .src_base_inst = fn_cau.zir_index,
+ .src_base_inst = fn_nav.analysis.?.zir_index,
.type_name_ctx = fn_nav.fqn,
};
defer child_block.instructions.deinit(gpa);
@@ -8481,7 +8471,7 @@ fn instantiateGenericCall(
if (call_dbg_node) |some| try sema.zirDbgStmt(block, some);
switch (sema.owner.unwrap()) {
- .cau => {},
+ .@"comptime", .nav_val, .type => {},
.func => |owner_func| if (Type.fromInterned(func_ty_info.return_type).isError(zcu)) {
ip.funcSetCallsOrAwaitsErrorableFn(owner_func);
},
@@ -9510,14 +9500,11 @@ fn zirFunc(
// the callconv based on whether it is exported. Otherwise, the callconv defaults
// to `.auto`.
const cc: std.builtin.CallingConvention = if (has_body) cc: {
- const func_decl_cau = if (sema.generic_owner != .none) cau: {
- const generic_owner_fn = zcu.funcInfo(sema.generic_owner);
- // The generic owner definitely has a `Cau` for the corresponding function declaration.
- const generic_owner_nav = ip.getNav(generic_owner_fn.owner_nav);
- break :cau generic_owner_nav.analysis_owner.unwrap().?;
- } else sema.owner.unwrap().cau;
+ const func_decl_nav = if (sema.generic_owner != .none) nav: {
+ break :nav zcu.funcInfo(sema.generic_owner).owner_nav;
+ } else sema.owner.unwrap().nav_val;
const fn_is_exported = exported: {
- const decl_inst = ip.getCau(func_decl_cau).zir_index.resolve(ip) orelse return error.AnalysisFail;
+ const decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(ip) orelse return error.AnalysisFail;
const zir_decl = sema.code.getDeclaration(decl_inst);
break :exported zir_decl.linkage == .@"export";
};
@@ -9991,7 +9978,7 @@ fn funcCommon(
if (!ret_poison)
try sema.validateErrorUnionPayloadType(block, bare_return_type, ret_ty_src);
const func_index = try ip.getFuncDeclIes(gpa, pt.tid, .{
- .owner_nav = sema.getOwnerCauNav(),
+ .owner_nav = sema.owner.unwrap().nav_val,
.param_types = param_types,
.noalias_bits = noalias_bits,
@@ -10040,7 +10027,7 @@ fn funcCommon(
if (has_body) {
const func_index = try ip.getFuncDecl(gpa, pt.tid, .{
- .owner_nav = sema.getOwnerCauNav(),
+ .owner_nav = sema.owner.unwrap().nav_val,
.ty = func_ty,
.cc = cc,
.is_noinline = is_noinline,
@@ -17664,7 +17651,7 @@ fn zirAsm(
if (is_volatile) {
return sema.fail(block, src, "volatile keyword is redundant on module-level assembly", .{});
}
- try zcu.addGlobalAssembly(sema.owner.unwrap().cau, asm_source);
+ try zcu.addGlobalAssembly(sema.owner, asm_source);
return .void_value;
}
@@ -18155,7 +18142,7 @@ fn zirThis(
_ = extended;
const pt = sema.pt;
const namespace = pt.zcu.namespacePtr(block.namespace);
- const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type, false);
+ const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type);
switch (pt.zcu.intern_pool.indexToKey(new_ty)) {
.struct_type, .union_type, .enum_type => try sema.declareDependency(.{ .interned = new_ty }),
.opaque_type => {},
@@ -19321,10 +19308,8 @@ fn typeInfoNamespaceDecls(
}
for (namespace.pub_usingnamespace.items) |nav| {
- if (ip.getNav(nav).analysis_owner.unwrap()) |cau| {
- if (zcu.analysis_in_progress.contains(AnalUnit.wrap(.{ .cau = cau }))) {
- continue;
- }
+ if (zcu.analysis_in_progress.contains(.wrap(.{ .nav_val = nav }))) {
+ continue;
}
try sema.ensureNavResolved(src, nav);
const namespace_ty = Type.fromInterned(ip.getNav(nav).status.resolved.val);
@@ -21187,14 +21172,13 @@ fn structInitAnon(
.file_scope = block.getFileScopeIndex(zcu),
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip.index);
try zcu.comp.queueJob(.{ .resolve_type_fully = wip.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
if (block.ownerModule().strip) break :codegen_type;
try zcu.comp.queueJob(.{ .codegen_type = wip.index });
}
- break :ty wip.finish(ip, new_cau_index.toOptional(), new_namespace_index);
+ break :ty wip.finish(ip, new_namespace_index);
},
.existing => |ty| ty,
};
@@ -21618,7 +21602,7 @@ fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref {
.func => |func| if (ip.funcAnalysisUnordered(func).calls_or_awaits_errorable_fn and block.ownerModule().error_tracing) {
return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty);
},
- .cau => {},
+ .@"comptime", .nav_val, .type => {},
}
return Air.internedToRef(try pt.intern(.{ .opt = .{
.ty = opt_ptr_stack_trace_ty.toIntern(),
@@ -22296,7 +22280,7 @@ fn zirReify(
});
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, .none, new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
},
.@"union" => {
const struct_type = ip.loadStructType(ip.typeOf(union_val.val));
@@ -22505,11 +22489,9 @@ fn reifyEnum(
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- wip_ty.prepare(ip, new_cau_index, new_namespace_index);
+ wip_ty.prepare(ip, new_namespace_index);
wip_ty.setTagTy(ip, tag_ty.toIntern());
done = true;
@@ -22811,8 +22793,6 @@ fn reifyUnion(
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
@@ -22822,7 +22802,7 @@ fn reifyUnion(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn reifyTuple(
@@ -23170,8 +23150,6 @@ fn reifyStruct(
.generation = zcu.generation,
});
- const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
-
try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index });
codegen_type: {
if (zcu.comp.config.use_llvm) break :codegen_type;
@@ -23181,7 +23159,7 @@ fn reifyStruct(
}
try sema.declareDependency(.{ .interned = wip_ty.index });
try sema.addTypeReferenceEntry(src, wip_ty.index);
- return Air.internedToRef(wip_ty.finish(ip, new_cau_index.toOptional(), new_namespace_index));
+ return Air.internedToRef(wip_ty.finish(ip, new_namespace_index));
}
fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref {
@@ -26713,15 +26691,13 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val);
} else cc: {
if (has_body) {
- const decl_inst = if (sema.generic_owner != .none) decl_inst: {
+ const func_decl_nav = if (sema.generic_owner != .none) nav: {
// Generic instance -- use the original function declaration to
// look for the `export` syntax.
- const nav = zcu.intern_pool.getNav(zcu.funcInfo(sema.generic_owner).owner_nav);
- const cau = zcu.intern_pool.getCau(nav.analysis_owner.unwrap().?);
- break :decl_inst cau.zir_index;
- } else sema.getOwnerCauDeclInst(); // not an instantiation so we're analyzing a function declaration Cau
-
- const zir_decl = sema.code.getDeclaration(decl_inst.resolve(&zcu.intern_pool) orelse return error.AnalysisFail);
+ break :nav zcu.funcInfo(sema.generic_owner).owner_nav;
+ } else sema.owner.unwrap().nav_val;
+ const func_decl_inst = ip.getNav(func_decl_nav).analysis.?.zir_index.resolve(&zcu.intern_pool) orelse return error.AnalysisFail;
+ const zir_decl = sema.code.getDeclaration(func_decl_inst);
if (zir_decl.linkage == .@"export") {
break :cc target.cCallingConvention() orelse {
// This target has no default C calling convention. We sometimes trigger a similar
@@ -27108,8 +27084,16 @@ fn zirBuiltinExtern(
// `builtin_extern` doesn't provide enough information, and isn't currently tracked.
// So, for now, just use our containing `declaration`.
.zir_index = switch (sema.owner.unwrap()) {
- .cau => sema.getOwnerCauDeclInst(),
- .func => sema.getOwnerFuncDeclInst(),
+ .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index,
+ .type => |owner_ty| Type.fromInterned(owner_ty).typeDeclInst(zcu).?,
+ .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index,
+ .func => |func| zir_index: {
+ const func_info = zcu.funcInfo(func);
+ const owner_func_info = if (func_info.generic_owner != .none) owner: {
+ break :owner zcu.funcInfo(func_info.generic_owner);
+ } else func_info;
+ break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index;
+ },
},
.owner_nav = undefined, // ignored by `getExtern`
});
@@ -32670,28 +32654,25 @@ pub fn ensureNavResolved(sema: *Sema, src: LazySrcLoc, nav_index: InternPool.Nav
const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index);
-
- const cau_index = nav.analysis_owner.unwrap() orelse {
+ if (nav.analysis == null) {
assert(nav.status == .resolved);
return;
- };
+ }
- // Note that even if `nav.status == .resolved`, we must still trigger `ensureCauAnalyzed`
+ // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate`
// to make sure the value is up-to-date on incremental updates.
- assert(ip.getCau(cau_index).owner.unwrap().nav == nav_index);
-
- const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
+ const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_index });
try sema.addReferenceEntry(src, anal_unit);
if (zcu.analysis_in_progress.contains(anal_unit)) {
return sema.failWithOwnedErrorMsg(null, try sema.errMsg(.{
- .base_node_inst = ip.getCau(cau_index).zir_index,
+ .base_node_inst = nav.analysis.?.zir_index,
.offset = LazySrcLoc.Offset.nodeOffset(0),
}, "dependency loop detected", .{}));
}
- return pt.ensureCauAnalyzed(cau_index);
+ return pt.ensureNavValUpToDate(nav_index);
}
fn optRefValue(sema: *Sema, opt_val: ?Value) !Value {
@@ -35641,7 +35622,7 @@ pub fn resolveStructAlignment(
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty);
assert(struct_type.layout != .@"packed");
assert(struct_type.flagsUnordered(ip).alignment == .none);
@@ -35684,7 +35665,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty) orelse return;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
if (struct_type.haveLayout(ip))
return;
@@ -35831,15 +35812,13 @@ fn backingIntType(
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const cau_index = struct_type.cau;
-
var analysis_arena = std.heap.ArenaAllocator.init(gpa);
defer analysis_arena.deinit();
var block: Block = .{
.parent = null,
.sema = sema,
- .namespace = ip.getCau(cau_index).namespace,
+ .namespace = struct_type.namespace,
.instructions = .{},
.inlining = null,
.is_comptime = true,
@@ -35971,7 +35950,7 @@ pub fn resolveUnionAlignment(
const ip = &zcu.intern_pool;
const target = zcu.getTarget();
- assert(sema.owner.unwrap().cau == union_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
assert(!union_type.haveLayout(ip));
@@ -36011,7 +35990,7 @@ pub fn resolveUnionLayout(sema: *Sema, ty: Type) SemaError!void {
// Load again, since the tag type might have changed due to resolution.
const union_type = ip.loadUnionType(ty.ip_index);
- assert(sema.owner.unwrap().cau == union_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
const old_flags = union_type.flagsUnordered(ip);
switch (old_flags.status) {
@@ -36126,7 +36105,7 @@ pub fn resolveStructFully(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty).?;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
if (struct_type.setFullyResolved(ip)) return;
errdefer struct_type.clearFullyResolved(ip);
@@ -36149,7 +36128,7 @@ pub fn resolveUnionFully(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const union_obj = zcu.typeToUnion(ty).?;
- assert(sema.owner.unwrap().cau == union_obj.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
switch (union_obj.flagsUnordered(ip).status) {
.none, .have_field_types, .field_types_wip, .layout_wip, .have_layout => {},
@@ -36184,7 +36163,7 @@ pub fn resolveStructFieldTypes(
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty);
if (struct_type.haveFieldTypes(ip)) return;
@@ -36210,7 +36189,7 @@ pub fn resolveStructFieldInits(sema: *Sema, ty: Type) SemaError!void {
const ip = &zcu.intern_pool;
const struct_type = zcu.typeToStruct(ty) orelse return;
- assert(sema.owner.unwrap().cau == struct_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
// Inits can start as resolved
if (struct_type.haveFieldInits(ip)) return;
@@ -36239,7 +36218,7 @@ pub fn resolveUnionFieldTypes(sema: *Sema, ty: Type, union_type: InternPool.Load
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
- assert(sema.owner.unwrap().cau == union_type.cau);
+ assert(sema.owner.unwrap().type == ty.toIntern());
switch (union_type.flagsUnordered(ip).status) {
.none => {},
@@ -36315,7 +36294,7 @@ fn resolveInferredErrorSet(
// In this case we are dealing with the actual InferredErrorSet object that
// corresponds to the function, not one created to track an inline/comptime call.
try sema.addReferenceEntry(src, AnalUnit.wrap(.{ .func = func_index }));
- try pt.ensureFuncBodyAnalyzed(func_index);
+ try pt.ensureFuncBodyUpToDate(func_index);
}
// This will now have been resolved by the logic at the end of `Zcu.analyzeFnBody`
@@ -36472,8 +36451,7 @@ fn structFields(
const zcu = pt.zcu;
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const cau_index = struct_type.cau;
- const namespace_index = ip.getCau(cau_index).namespace;
+ const namespace_index = struct_type.namespace;
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
@@ -36671,8 +36649,7 @@ fn structFieldInits(
assert(!struct_type.haveFieldInits(ip));
- const cau_index = struct_type.cau;
- const namespace_index = ip.getCau(cau_index).namespace;
+ const namespace_index = struct_type.namespace;
const zir = zcu.namespacePtr(namespace_index).fileScope(zcu).zir;
const zir_index = struct_type.zir_index.resolve(ip) orelse return error.AnalysisFail;
const fields_len, _, var extra_index = structZirInfo(zir, zir_index);
@@ -38474,13 +38451,11 @@ pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void {
// just result in over-analysis since `Zcu.findOutdatedToAnalyze` would never be able to resolve
// the loop.
switch (sema.owner.unwrap()) {
- .cau => |cau| switch (dependee) {
- .nav_val => |nav| if (zcu.intern_pool.getNav(nav).analysis_owner == cau.toOptional()) {
- return;
- },
+ .nav_val => |this_nav| switch (dependee) {
+ .nav_val => |other_nav| if (this_nav == other_nav) return,
else => {},
},
- .func => {},
+ else => {},
}
try zcu.intern_pool.addDependency(sema.gpa, sema.owner, dependee);
@@ -38659,38 +38634,6 @@ pub fn flushExports(sema: *Sema) !void {
}
}
-/// Given that this `Sema` is owned by the `Cau` of a `declaration`, fetches
-/// the corresponding `Nav`.
-fn getOwnerCauNav(sema: *Sema) InternPool.Nav.Index {
- const cau = sema.owner.unwrap().cau;
- return sema.pt.zcu.intern_pool.getCau(cau).owner.unwrap().nav;
-}
-
-/// Given that this `Sema` is owned by the `Cau` of a `declaration`, fetches
-/// the `TrackedInst` corresponding to this `declaration` instruction.
-fn getOwnerCauDeclInst(sema: *Sema) InternPool.TrackedInst.Index {
- const ip = &sema.pt.zcu.intern_pool;
- const cau = ip.getCau(sema.owner.unwrap().cau);
- assert(cau.owner.unwrap() == .nav);
- return cau.zir_index;
-}
-
-/// Given that this `Sema` is owned by a runtime function, fetches the
-/// `TrackedInst` corresponding to its `declaration` instruction.
-fn getOwnerFuncDeclInst(sema: *Sema) InternPool.TrackedInst.Index {
- const zcu = sema.pt.zcu;
- const ip = &zcu.intern_pool;
- const func = sema.owner.unwrap().func;
- const func_info = zcu.funcInfo(func);
- const cau = if (func_info.generic_owner == .none) cau: {
- break :cau ip.getNav(func_info.owner_nav).analysis_owner.unwrap().?;
- } else cau: {
- const generic_owner = zcu.funcInfo(func_info.generic_owner);
- break :cau ip.getNav(generic_owner.owner_nav).analysis_owner.unwrap().?;
- };
- return ip.getCau(cau).zir_index;
-}
-
/// Called as soon as a `declared` enum type is created.
/// Resolves the tag type and field inits.
/// Marks the `src_inst` dependency on the enum's declaration, so call sites need not do this.
@@ -38701,7 +38644,6 @@ pub fn resolveDeclaredEnum(
tracked_inst: InternPool.TrackedInst.Index,
namespace: InternPool.NamespaceIndex,
type_name: InternPool.NullTerminatedString,
- enum_cau: InternPool.Cau.Index,
small: Zir.Inst.EnumDecl.Small,
body: []const Zir.Inst.Index,
tag_type_ref: Zir.Inst.Ref,
@@ -38719,7 +38661,7 @@ pub fn resolveDeclaredEnum(
const src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = LazySrcLoc.Offset.nodeOffset(0) };
const tag_ty_src: LazySrcLoc = .{ .base_node_inst = tracked_inst, .offset = .{ .node_offset_container_tag = 0 } };
- const anal_unit = AnalUnit.wrap(.{ .cau = enum_cau });
+ const anal_unit = AnalUnit.wrap(.{ .type = wip_ty.index });
var arena = std.heap.ArenaAllocator.init(gpa);
defer arena.deinit();
@@ -38943,6 +38885,6 @@ fn getBuiltin(sema: *Sema, name: []const u8) SemaError!Air.Inst.Ref {
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
const nav = try pt.getBuiltinNav(name);
- try pt.ensureCauAnalyzed(ip.getNav(nav).analysis_owner.unwrap().?);
+ try pt.ensureNavValUpToDate(nav);
return Air.internedToRef(ip.getNav(nav).status.resolved.val);
}
src/Type.zig
@@ -3851,7 +3851,7 @@ fn resolveStructInner(
const gpa = zcu.gpa;
const struct_obj = zcu.typeToStruct(ty).?;
- const owner = InternPool.AnalUnit.wrap(.{ .cau = struct_obj.cau });
+ const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
return error.AnalysisFail;
@@ -3905,7 +3905,7 @@ fn resolveUnionInner(
const gpa = zcu.gpa;
const union_obj = zcu.typeToUnion(ty).?;
- const owner = InternPool.AnalUnit.wrap(.{ .cau = union_obj.cau });
+ const owner: InternPool.AnalUnit = .wrap(.{ .type = ty.toIntern() });
if (zcu.failed_analysis.contains(owner) or zcu.transitive_failed_analysis.contains(owner)) {
return error.AnalysisFail;
src/Zcu.zig
@@ -192,7 +192,7 @@ compile_log_text: std.ArrayListUnmanaged(u8) = .empty,
test_functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, void) = .empty,
-global_assembly: std.AutoArrayHashMapUnmanaged(InternPool.Cau.Index, []u8) = .empty,
+global_assembly: std.AutoArrayHashMapUnmanaged(AnalUnit, []u8) = .empty,
/// Key is the `AnalUnit` *performing* the reference. This representation allows
/// incremental updates to quickly delete references caused by a specific `AnalUnit`.
@@ -344,9 +344,12 @@ pub const Namespace = struct {
pub_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
/// All `usingnamespace` declarations in this namespace which are *not* marked `pub`.
priv_usingnamespace: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
- /// All `comptime` and `test` declarations in this namespace. We store these purely so that
- /// incremental compilation can re-use the existing `Cau`s when a namespace changes.
- other_decls: std.ArrayListUnmanaged(InternPool.Cau.Index) = .empty,
+ /// All `comptime` declarations in this namespace. We store these purely so that incremental
+ /// compilation can re-use the existing `ComptimeUnit`s when a namespace changes.
+ comptime_decls: std.ArrayListUnmanaged(InternPool.ComptimeUnit.Id) = .empty,
+ /// All `test` declarations in this namespace. We store these purely so that incremental
+ /// compilation can re-use the existing `Nav`s when a namespace changes.
+ test_decls: std.ArrayListUnmanaged(InternPool.Nav.Index) = .empty,
pub const Index = InternPool.NamespaceIndex;
pub const OptionalIndex = InternPool.OptionalNamespaceIndex;
@@ -2436,11 +2439,9 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
// If this is a Decl, we must recursively mark dependencies on its tyval
// as no longer PO.
switch (depender.unwrap()) {
- .cau => |cau| switch (zcu.intern_pool.getCau(cau).owner.unwrap()) {
- .nav => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
- .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
- .none => {},
- },
+ .@"comptime" => {},
+ .nav_val => |nav| try zcu.markPoDependeeUpToDate(.{ .nav_val = nav }),
+ .type => |ty| try zcu.markPoDependeeUpToDate(.{ .interned = ty }),
.func => |func| try zcu.markPoDependeeUpToDate(.{ .interned = func }),
}
}
@@ -2451,11 +2452,9 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void {
const ip = &zcu.intern_pool;
const dependee: InternPool.Dependee = switch (maybe_outdated.unwrap()) {
- .cau => |cau| switch (ip.getCau(cau).owner.unwrap()) {
- .nav => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
- .type => |ty| .{ .interned = ty },
- .none => return, // analysis of this `Cau` can't outdate any dependencies
- },
+ .@"comptime" => return, // analysis of a comptime decl can't outdate any dependencies
+ .nav_val => |nav| .{ .nav_val = nav }, // TODO: also `nav_ref` deps when introduced
+ .type => |ty| .{ .interned = ty },
.func => |func_index| .{ .interned = func_index }, // IES
};
log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)});
@@ -2512,14 +2511,14 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
}
// There is no single AnalUnit which is ready for re-analysis. Instead, we must assume that some
- // Cau with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
- // A or B. We should select a Cau, since a Cau is definitely responsible for the loop in the
- // dependency graph (since IES dependencies can't have loops). We should also, of course, not
- // select a Cau owned by a `comptime` declaration, since you can't depend on those!
+ // AnalUnit with PO dependencies is outdated -- e.g. in the above example we arbitrarily pick one of
+ // A or B. We should definitely not select a function, since a function can't be responsible for the
+ // loop (IES dependencies can't have loops). We should also, of course, not select a `comptime`
+ // declaration, since you can't depend on those!
- // The choice of this Cau could have a big impact on how much total analysis we perform, since
+ // The choice of this unit could have a big impact on how much total analysis we perform, since
// if analysis concludes any dependencies on its result are up-to-date, then other PO AnalUnit
- // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a Decl
+ // may be resolved as up-to-date. To hopefully avoid doing too much work, let's find a unit
// which the most things depend on - the idea is that this will resolve a lot of loops (but this
// is only a heuristic).
@@ -2530,33 +2529,28 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
const ip = &zcu.intern_pool;
- var chosen_cau: ?InternPool.Cau.Index = null;
- var chosen_cau_dependers: u32 = undefined;
+ var chosen_unit: ?AnalUnit = null;
+ var chosen_unit_dependers: u32 = undefined;
inline for (.{ zcu.outdated.keys(), zcu.potentially_outdated.keys() }) |outdated_units| {
for (outdated_units) |unit| {
- const cau = switch (unit.unwrap()) {
- .cau => |cau| cau,
- .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
- };
- const cau_owner = ip.getCau(cau).owner;
-
var n: u32 = 0;
- var it = ip.dependencyIterator(switch (cau_owner.unwrap()) {
- .none => continue, // there can be no dependencies on this `Cau` so it is a terrible choice
+ var it = ip.dependencyIterator(switch (unit.unwrap()) {
+ .func => continue, // a `func` definitely can't be causing the loop so it is a bad choice
+ .@"comptime" => continue, // a `comptime` block can't even be depended on so it is a terrible choice
.type => |ty| .{ .interned = ty },
- .nav => |nav| .{ .nav_val = nav },
+ .nav_val => |nav| .{ .nav_val = nav },
});
while (it.next()) |_| n += 1;
- if (chosen_cau == null or n > chosen_cau_dependers) {
- chosen_cau = cau;
- chosen_cau_dependers = n;
+ if (chosen_unit == null or n > chosen_unit_dependers) {
+ chosen_unit = unit;
+ chosen_unit_dependers = n;
}
}
}
- if (chosen_cau == null) {
+ if (chosen_unit == null) {
for (zcu.outdated.keys(), zcu.outdated.values()) |o, opod| {
const func = o.unwrap().func;
const nav = zcu.funcInfo(func).owner_nav;
@@ -2570,11 +2564,11 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
}
log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
- zcu.fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? })),
- chosen_cau_dependers,
+ zcu.fmtAnalUnit(chosen_unit.?),
+ chosen_unit_dependers,
});
- return AnalUnit.wrap(.{ .cau = chosen_cau.? });
+ return chosen_unit.?;
}
/// During an incremental update, before semantic analysis, call this to flush all values from
@@ -3019,9 +3013,9 @@ pub fn handleUpdateExports(
};
}
-pub fn addGlobalAssembly(zcu: *Zcu, cau: InternPool.Cau.Index, source: []const u8) !void {
+pub fn addGlobalAssembly(zcu: *Zcu, unit: AnalUnit, source: []const u8) !void {
const gpa = zcu.gpa;
- const gop = try zcu.global_assembly.getOrPut(gpa, cau);
+ const gop = try zcu.global_assembly.getOrPut(gpa, unit);
if (gop.found_existing) {
const new_value = try std.fmt.allocPrint(gpa, "{s}\n{s}", .{ gop.value_ptr.*, source });
gpa.free(gop.value_ptr.*);
@@ -3304,23 +3298,22 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
log.debug("handle type '{}'", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)});
- // If this type has a `Cau` for resolution, it's automatically referenced.
- const resolution_cau: InternPool.Cau.Index.Optional = switch (ip.indexToKey(ty)) {
- .struct_type => ip.loadStructType(ty).cau.toOptional(),
- .union_type => ip.loadUnionType(ty).cau.toOptional(),
- .enum_type => ip.loadEnumType(ty).cau,
- .opaque_type => .none,
+ // If this type undergoes type resolution, the corresponding `AnalUnit` is automatically referenced.
+ const has_resolution: bool = switch (ip.indexToKey(ty)) {
+ .struct_type, .union_type => true,
+ .enum_type => |k| k != .generated_tag,
+ .opaque_type => false,
else => unreachable,
};
- if (resolution_cau.unwrap()) |cau| {
+ if (has_resolution) {
// this should only be referenced by the type
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .type = ty });
assert(!result.contains(unit));
try unit_queue.putNoClobber(gpa, unit, referencer);
}
// If this is a union with a generated tag, its tag type is automatically referenced.
- // We don't add this reference for non-generated tags, as those will already be referenced via the union's `Cau`, with a better source location.
+ // We don't add this reference for non-generated tags, as those will already be referenced via the union's type resolution, with a better source location.
if (zcu.typeToUnion(Type.fromInterned(ty))) |union_obj| {
const tag_ty = union_obj.enum_tag_ty;
if (tag_ty != .none) {
@@ -3335,24 +3328,35 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
// Queue any decls within this type which would be automatically analyzed.
// Keep in sync with analysis queueing logic in `Zcu.PerThread.ScanDeclIter.scanDecl`.
const ns = Type.fromInterned(ty).getNamespace(zcu).unwrap().?;
- for (zcu.namespacePtr(ns).other_decls.items) |cau| {
- // These are `comptime` and `test` declarations.
- // `comptime` decls are always analyzed; `test` declarations are analyzed depending on the test filter.
- const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
+ for (zcu.namespacePtr(ns).comptime_decls.items) |cu| {
+ // `comptime` decls are always analyzed.
+ const unit: AnalUnit = .wrap(.{ .@"comptime" = cu });
+ if (!result.contains(unit)) {
+ log.debug("type '{}': ref comptime %{}", .{
+ Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
+ @intFromEnum(ip.getComptimeUnit(cu).zir_index.resolve(ip) orelse continue),
+ });
+ try unit_queue.put(gpa, unit, referencer);
+ }
+ }
+ for (zcu.namespacePtr(ns).test_decls.items) |nav_id| {
+ const nav = ip.getNav(nav_id);
+ // `test` declarations are analyzed depending on the test filter.
+ const inst_info = nav.analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
const decl = zir.getDeclaration(inst_info.inst);
+
+ if (!comp.config.is_test or file.mod != zcu.main_mod) continue;
+
const want_analysis = switch (decl.kind) {
.@"usingnamespace" => unreachable,
.@"const", .@"var" => unreachable,
- .@"comptime" => true,
- .unnamed_test => comp.config.is_test and file.mod == zcu.main_mod,
+ .@"comptime" => unreachable,
+ .unnamed_test => true,
.@"test", .decltest => a: {
- if (!comp.config.is_test) break :a false;
- if (file.mod != zcu.main_mod) break :a false;
- const nav = ip.getCau(cau).owner.unwrap().nav;
- const fqn_slice = ip.getNav(nav).fqn.toSlice(ip);
+ const fqn_slice = nav.fqn.toSlice(ip);
for (comp.test_filters) |test_filter| {
if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break;
} else break :a false;
@@ -3360,28 +3364,25 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
},
};
if (want_analysis) {
- const unit = AnalUnit.wrap(.{ .cau = cau });
- if (!result.contains(unit)) {
- log.debug("type '{}': ref cau %{}", .{
- Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
- @intFromEnum(inst_info.inst),
- });
- try unit_queue.put(gpa, unit, referencer);
- }
+ log.debug("type '{}': ref test %{}", .{
+ Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
+ @intFromEnum(inst_info.inst),
+ });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav_id });
+ try unit_queue.put(gpa, unit, referencer);
}
}
for (zcu.namespacePtr(ns).pub_decls.keys()) |nav| {
// These are named declarations. They are analyzed only if marked `export`.
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
+ const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
const decl = zir.getDeclaration(inst_info.inst);
if (decl.linkage == .@"export") {
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) {
- log.debug("type '{}': ref cau %{}", .{
+ log.debug("type '{}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -3391,16 +3392,15 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
}
for (zcu.namespacePtr(ns).priv_decls.keys()) |nav| {
// These are named declarations. They are analyzed only if marked `export`.
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const inst_info = ip.getCau(cau).zir_index.resolveFull(ip) orelse continue;
+ const inst_info = ip.getNav(nav).analysis.?.zir_index.resolveFull(ip) orelse continue;
const file = zcu.fileByIndex(inst_info.file);
// If the file failed AstGen, the TrackedInst refers to the old ZIR.
const zir = if (file.status == .success_zir) file.zir else file.prev_zir.?.*;
const decl = zir.getDeclaration(inst_info.inst);
if (decl.linkage == .@"export") {
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) {
- log.debug("type '{}': ref cau %{}", .{
+ log.debug("type '{}': ref named %{}", .{
Type.fromInterned(ty).containerTypeName(ip).fmt(ip),
@intFromEnum(inst_info.inst),
});
@@ -3411,13 +3411,11 @@ fn resolveReferencesInner(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolv
// Incremental compilation does not support `usingnamespace`.
// These are only included to keep good reference traces in non-incremental updates.
for (zcu.namespacePtr(ns).pub_usingnamespace.items) |nav| {
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
}
for (zcu.namespacePtr(ns).priv_usingnamespace.items) |nav| {
- const cau = ip.getNav(nav).analysis_owner.unwrap().?;
- const unit = AnalUnit.wrap(.{ .cau = cau });
+ const unit: AnalUnit = .wrap(.{ .nav_val = nav });
if (!result.contains(unit)) try unit_queue.put(gpa, unit, referencer);
}
continue;
@@ -3527,12 +3525,6 @@ pub fn navFileScope(zcu: *Zcu, nav: InternPool.Nav.Index) *File {
return zcu.fileByIndex(zcu.navFileScopeIndex(nav));
}
-pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
- const ip = &zcu.intern_pool;
- const file_index = ip.getCau(cau).zir_index.resolveFile(ip);
- return zcu.fileByIndex(file_index);
-}
-
pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(formatAnalUnit) {
return .{ .data = .{ .unit = unit, .zcu = zcu } };
}
@@ -3545,19 +3537,17 @@ fn formatAnalUnit(data: struct { unit: AnalUnit, zcu: *Zcu }, comptime fmt: []co
const zcu = data.zcu;
const ip = &zcu.intern_pool;
switch (data.unit.unwrap()) {
- .cau => |cau_index| {
- const cau = ip.getCau(cau_index);
- switch (cau.owner.unwrap()) {
- .nav => |nav| return writer.print("cau(decl='{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
- .type => |ty| return writer.print("cau(ty='{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
- .none => if (cau.zir_index.resolveFull(ip)) |resolved| {
- const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
- return writer.print("cau(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
- } else {
- return writer.writeAll("cau(inst=<lost>)");
- },
+ .@"comptime" => |cu_id| {
+ const cu = ip.getComptimeUnit(cu_id);
+ if (cu.zir_index.resolveFull(ip)) |resolved| {
+ const file_path = zcu.fileByIndex(resolved.file).sub_file_path;
+ return writer.print("comptime(inst=('{s}', %{}))", .{ file_path, @intFromEnum(resolved.inst) });
+ } else {
+ return writer.writeAll("comptime(inst=<list>)");
}
},
+ .nav_val => |nav| return writer.print("nav_val('{}')", .{ip.getNav(nav).fqn.fmt(ip)}),
+ .type => |ty| return writer.print("ty('{}')", .{Type.fromInterned(ty).containerTypeName(ip).fmt(ip)}),
.func => |func| {
const nav = zcu.funcInfo(func).owner_nav;
return writer.print("func('{}')", .{ip.getNav(nav).fqn.fmt(ip)});