Commit 84c2ebd6c6
Changed files (5)
src/Zcu/PerThread.zig
@@ -485,10 +485,7 @@ 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) {
- // The namespace is already up-to-date thanks to the `updateFileNamespace` calls at the
- // start of this update. We just have to check whether the type itself is okay!
- const file_root_type_cau = pt.zcu.intern_pool.loadStructType(file_root_type).cau.unwrap().?;
- return pt.ensureCauAnalyzed(file_root_type_cau);
+ _ = try pt.ensureTypeUpToDate(file_root_type, false);
} else {
return pt.semaFile(file_index);
}
@@ -505,10 +502,10 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });
+ const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
const cau = ip.getCau(cau_index);
- //log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});
+ log.debug("ensureCauAnalyzed {d}", .{@intFromEnum(cau_index)});
assert(!zcu.analysis_in_progress.contains(anal_unit));
@@ -552,10 +549,12 @@ pub fn ensureCauAnalyzed(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) Zcu
// Since it does not, this must be a transitive failure.
try zcu.transitive_failed_analysis.put(gpa, anal_unit, {});
}
- // We treat errors as up-to-date, since those uses would just trigger a transitive error
+ // We treat errors as up-to-date, since those uses would just trigger a transitive error.
+ // The exception is types, since type declarations may require re-analysis if the type, e.g. its captures, changed.
+ const outdated = cau.owner.unwrap() == .type;
break :res .{ .{
- .invalidate_decl_val = false,
- .invalidate_decl_ref = false,
+ .invalidate_decl_val = outdated,
+ .invalidate_decl_ref = outdated,
}, true };
},
error.OutOfMemory => res: {
@@ -610,7 +609,7 @@ fn ensureCauAnalyzedInner(
const ip = &zcu.intern_pool;
const cau = ip.getCau(cau_index);
- const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });
+ const anal_unit = AnalUnit.wrap(.{ .cau = cau_index });
const inst_info = cau.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
@@ -626,7 +625,6 @@ fn ensureCauAnalyzedInner(
// * 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
- // * TODO: optimize this to make sure we only do it once a generation i guess?
// * so everyone lived happily ever after
if (zcu.fileByIndex(inst_info.file).status != .success_zir) {
@@ -646,17 +644,6 @@ fn ensureCauAnalyzedInner(
_ = zcu.transitive_failed_analysis.swapRemove(anal_unit);
}
- if (inst_info.inst == .main_struct_inst) {
- // Note that this is definitely a *recreation* due to outdated, because
- // this instruction indicates that `cau.owner` is a `type`, which only
- // reaches here if `cau_outdated`.
- try pt.recreateFileRoot(inst_info.file);
- return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
- }
-
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),
@@ -685,9 +672,9 @@ pub fn ensureFuncBodyAnalyzed(pt: Zcu.PerThread, maybe_coerced_func_index: Inter
const func = zcu.funcInfo(maybe_coerced_func_index);
- //log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});
+ log.debug("ensureFuncBodyAnalyzed {d}", .{@intFromEnum(func_index)});
- const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
+ const anal_unit = AnalUnit.wrap(.{ .func = func_index });
const func_outdated = zcu.outdated.swapRemove(anal_unit) or
zcu.potentially_outdated.swapRemove(anal_unit);
@@ -742,7 +729,7 @@ fn ensureFuncBodyAnalyzedInner(
const ip = &zcu.intern_pool;
const func = zcu.funcInfo(func_index);
- const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
+ const anal_unit = AnalUnit.wrap(.{ .func = func_index });
// Here's an interesting question: is this function actually valid?
// Maybe the signature changed, so we'll end up creating a whole different `func`
@@ -766,7 +753,7 @@ fn ensureFuncBodyAnalyzedInner(
if (func_outdated) {
try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index }); // IES
}
- ip.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));
+ ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .func = func_index }));
ip.remove(pt.tid, func_index);
@panic("TODO: remove orphaned function from binary");
}
@@ -901,7 +888,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
"unable to codegen: {s}",
.{@errorName(err)},
));
- try zcu.retryable_failures.append(zcu.gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));
+ try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
},
};
} else if (zcu.llvm_object) |llvm_object| {
@@ -982,7 +969,7 @@ fn createFileRootStruct(
if (zcu.comp.incremental) {
try ip.addDependency(
gpa,
- InternPool.AnalUnit.wrap(.{ .cau = new_cau_index }),
+ AnalUnit.wrap(.{ .cau = new_cau_index }),
.{ .src_hash = tracked_inst },
);
}
@@ -998,35 +985,6 @@ fn createFileRootStruct(
return wip_ty.finish(ip, new_cau_index.toOptional(), namespace_index);
}
-/// Recreate the root type of a file after it becomes outdated. A new struct type
-/// is constructed at a new InternPool index, reusing the namespace for efficiency.
-fn recreateFileRoot(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
- const file = zcu.fileByIndex(file_index);
- const file_root_type = zcu.fileRootType(file_index);
- const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu);
-
- assert(file_root_type != .none);
-
- log.debug("recreateFileRoot mod={s} sub_file_path={s}", .{
- file.mod.fully_qualified_name,
- file.sub_file_path,
- });
-
- if (file.status != .success_zir) {
- return error.AnalysisFail;
- }
-
- // Invalidate the existing type, reusing its namespace.
- const file_root_type_cau = ip.loadStructType(file_root_type).cau.unwrap().?;
- ip.removeDependenciesForDepender(
- zcu.gpa,
- InternPool.AnalUnit.wrap(.{ .cau = file_root_type_cau }),
- );
- _ = try pt.createFileRootStruct(file_index, namespace_index, true);
-}
-
/// 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.
@@ -1060,6 +1018,7 @@ fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.
break :decls file.zir.bodySlice(extra_index, decls_len);
};
try pt.scanNamespace(namespace_index, decls);
+ zcu.namespacePtr(namespace_index).generation = zcu.generation;
}
fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
@@ -1080,6 +1039,7 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
.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);
@@ -1131,7 +1091,7 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const anal_unit = InternPool.AnalUnit.wrap(.{ .cau = cau_index });
+ 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;
@@ -1151,10 +1111,12 @@ fn semaCau(pt: Zcu.PerThread, cau_index: InternPool.Cau.Index) !SemaCauResult {
// This declaration has no value so is definitely not a std.builtin type.
break :ip_index .none;
},
- .type => {
+ .type => |ty| {
// This is an incremental update, and this type is being re-analyzed because it is outdated.
- // The type must be recreated at a new `InternPool.Index`.
- // Mark it outdated so that creation sites are re-analyzed.
+ // 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,
@@ -2002,21 +1964,23 @@ const ScanDeclIter = struct {
try namespace.other_decls.append(gpa, cau);
- // For a `comptime` declaration, whether to re-analyze is based solely on whether the
- // `Cau` is outdated. So, add this one to `outdated` and `outdated_ready` if not already.
- const unit = InternPool.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
+ 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, {});
}
- } 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, {});
}
break :cau .{ cau, true };
@@ -2027,9 +1991,6 @@ const ScanDeclIter = struct {
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);
- if (nav.name != name) {
- std.debug.panic("'{}' vs '{}'", .{ nav.name.fmt(ip), name.fmt(ip) });
- }
assert(nav.name == name);
assert(nav.fqn == fqn);
break :cau_nav .{ cau_index, nav_index };
@@ -2078,7 +2039,7 @@ const ScanDeclIter = struct {
},
};
- if (want_analysis or declaration.flags.is_export) {
+ if (existing_cau == null and (want_analysis or declaration.flags.is_export)) {
log.debug(
"scanDecl queue analyze_cau file='{s}' cau_index={d}",
.{ namespace.fileScope(zcu).sub_file_path, cau },
@@ -2098,7 +2059,7 @@ fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!
const gpa = zcu.gpa;
const ip = &zcu.intern_pool;
- const anal_unit = InternPool.AnalUnit.wrap(.{ .func = func_index });
+ const anal_unit = AnalUnit.wrap(.{ .func = func_index });
const func = zcu.funcInfo(func_index);
const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail;
const file = zcu.fileByIndex(inst_info.file);
@@ -2484,7 +2445,7 @@ fn processExportsInner(
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 = InternPool.AnalUnit.wrap(.{ .cau = 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;
}
@@ -2494,7 +2455,7 @@ fn processExportsInner(
};
// If the value is a function, we also need to check if that function succeeded analysis.
if (val.typeOf(zcu).zigTypeTag(zcu) == .Fn) {
- const func_unit = InternPool.AnalUnit.wrap(.{ .func = val.toIntern() });
+ const func_unit = AnalUnit.wrap(.{ .func = val.toIntern() });
if (zcu.failed_analysis.contains(func_unit)) break :failed true;
if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true;
}
@@ -2669,7 +2630,7 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void
.{@errorName(err)},
));
if (nav.analysis_owner.unwrap()) |cau| {
- try zcu.retryable_failures.append(zcu.gpa, InternPool.AnalUnit.wrap(.{ .cau = cau }));
+ try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .cau = cau }));
} 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.
@@ -2782,7 +2743,7 @@ pub fn reportRetryableFileError(
gop.value_ptr.* = err_msg;
}
-/// Shortcut for calling `intern_pool.get`.
+///Shortcut for calling `intern_pool.get`.
pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index {
return pt.zcu.intern_pool.get(pt.zcu.gpa, pt.tid, key);
}
@@ -3367,6 +3328,532 @@ pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPo
return Value.fromInterned(r.val).typeOf(zcu).abiAlignment(pt);
}
+/// 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 {
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ 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.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.recreateStructType(ty, 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(ty, 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(ty, key, enum_obj);
+ },
+ .opaque_type => {
+ assert(!already_updating);
+ return ty;
+ },
+ else => unreachable,
+ }
+}
+
+fn recreateStructType(
+ pt: Zcu.PerThread,
+ ty: InternPool.Index,
+ full_key: InternPool.Key.NamespaceType,
+ struct_obj: InternPool.LoadedStructType,
+) Zcu.SemaError!InternPool.Index {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const key = switch (full_key) {
+ .reified => unreachable, // never outdated
+ .empty_struct => unreachable, // never outdated
+ .generated_tag => unreachable, // not a struct
+ .declared => |d| d,
+ };
+
+ if (@intFromEnum(ty) <= InternPool.static_len) {
+ @panic("TODO: recreate resolved builtin type");
+ }
+
+ const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_info.file);
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
+ const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
+ assert(extended.opcode == .struct_decl);
+ const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
+ var extra_index = extra.end;
+
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk fields_len;
+ } else 0;
+
+ if (captures_len != key.captures.owned.len) return error.AnalysisFail;
+ if (fields_len != struct_obj.field_types.len) return error.AnalysisFail;
+
+ // The old type will be unused, so drop its dependency information.
+ ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .cau = struct_obj.cau.unwrap().? }));
+
+ const namespace_index = struct_obj.namespace.unwrap().?;
+
+ const wip_ty = switch (try ip.getStructType(gpa, pt.tid, .{
+ .layout = small.layout,
+ .fields_len = fields_len,
+ .known_non_opv = small.known_non_opv,
+ .requires_comptime = if (small.known_comptime_only) .yes else .unknown,
+ .is_tuple = small.is_tuple,
+ .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_owned_captures = .{
+ .zir_index = key.zir_index,
+ .captures = key.captures.owned,
+ } },
+ }, true)) {
+ .wip => |wip| wip,
+ .existing => unreachable, // we passed `replace_existing`
+ };
+ 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, namespace_index, wip_ty.index);
+ try ip.addDependency(
+ gpa,
+ AnalUnit.wrap(.{ .cau = new_cau_index }),
+ .{ .src_hash = key.zir_index },
+ );
+ zcu.namespacePtr(namespace_index).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(), namespace_index);
+ 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);
+ }
+ return new_ty;
+}
+
+fn recreateUnionType(
+ pt: Zcu.PerThread,
+ ty: InternPool.Index,
+ full_key: InternPool.Key.NamespaceType,
+ union_obj: InternPool.LoadedUnionType,
+) Zcu.SemaError!InternPool.Index {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const key = switch (full_key) {
+ .reified => unreachable, // never outdated
+ .empty_struct => unreachable, // never outdated
+ .generated_tag => unreachable, // not a union
+ .declared => |d| d,
+ };
+
+ if (@intFromEnum(ty) <= InternPool.static_len) {
+ @panic("TODO: recreate resolved builtin type");
+ }
+
+ const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_info.file);
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
+ const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
+ assert(extended.opcode == .union_decl);
+ const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
+ var extra_index = extra.end;
+
+ extra_index += @intFromBool(small.has_tag_type);
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+ extra_index += @intFromBool(small.has_body_len);
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk fields_len;
+ } else 0;
+
+ if (captures_len != key.captures.owned.len) return error.AnalysisFail;
+ if (fields_len != union_obj.field_types.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 namespace_index = union_obj.namespace;
+
+ const wip_ty = switch (try ip.getUnionType(gpa, pt.tid, .{
+ .flags = .{
+ .layout = small.layout,
+ .status = .none,
+ .runtime_tag = if (small.has_tag_type or small.auto_enum_tag)
+ .tagged
+ else if (small.layout != .auto)
+ .none
+ else switch (true) { // TODO
+ true => .safety,
+ false => .none,
+ },
+ .any_aligned_fields = small.any_aligned_fields,
+ .requires_comptime = .unknown,
+ .assumed_runtime_bits = false,
+ .assumed_pointer_aligned = false,
+ .alignment = .none,
+ },
+ .fields_len = fields_len,
+ .enum_tag_ty = .none, // set later
+ .field_types = &.{}, // set later
+ .field_aligns = &.{}, // set later
+ .key = .{ .declared_owned_captures = .{
+ .zir_index = key.zir_index,
+ .captures = key.captures.owned,
+ } },
+ }, true)) {
+ .wip => |wip| wip,
+ .existing => unreachable, // we passed `replace_existing`
+ };
+ 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 }),
+ .{ .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);
+}
+
+fn recreateEnumType(
+ pt: Zcu.PerThread,
+ ty: InternPool.Index,
+ full_key: InternPool.Key.NamespaceType,
+ enum_obj: InternPool.LoadedEnumType,
+) Zcu.SemaError!InternPool.Index {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const key = switch (full_key) {
+ .reified => unreachable, // never outdated
+ .empty_struct => unreachable, // never outdated
+ .generated_tag => unreachable, // never outdated
+ .declared => |d| d,
+ };
+
+ if (@intFromEnum(ty) <= InternPool.static_len) {
+ @panic("TODO: recreate resolved builtin type");
+ }
+
+ const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_info.file);
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
+ const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
+ assert(extended.opcode == .enum_decl);
+ const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
+ var extra_index = extra.end;
+
+ const tag_type_ref = if (small.has_tag_type) blk: {
+ const tag_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
+ extra_index += 1;
+ break :blk tag_type_ref;
+ } else .none;
+
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+
+ const body_len = if (small.has_body_len) blk: {
+ const body_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk body_len;
+ } else 0;
+
+ const fields_len = if (small.has_fields_len) blk: {
+ const fields_len = 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 = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+
+ if (captures_len != key.captures.owned.len) return error.AnalysisFail;
+ if (fields_len != enum_obj.names.len) return error.AnalysisFail;
+
+ extra_index += captures_len;
+ extra_index += decls_len;
+
+ const body = zir.bodySlice(extra_index, body_len);
+ extra_index += body.len;
+
+ const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
+ const body_end = extra_index;
+ extra_index += bit_bags_count;
+
+ const any_values = for (zir.extra[body_end..][0..bit_bags_count]) |bag| {
+ 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 namespace_index = enum_obj.namespace;
+
+ const wip_ty = switch (try ip.getEnumType(gpa, pt.tid, .{
+ .has_values = any_values,
+ .tag_mode = if (small.nonexhaustive)
+ .nonexhaustive
+ else if (tag_type_ref == .none)
+ .auto
+ else
+ .explicit,
+ .fields_len = fields_len,
+ .key = .{ .declared_owned_captures = .{
+ .zir_index = key.zir_index,
+ .captures = key.captures.owned,
+ } },
+ }, true)) {
+ .wip => |wip| wip,
+ .existing => unreachable, // we passed `replace_existing`
+ };
+ var done = true;
+ errdefer if (!done) wip_ty.cancel(ip, pt.tid);
+
+ 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);
+ done = true;
+
+ Sema.resolveDeclaredEnum(
+ pt,
+ wip_ty,
+ inst_info.inst,
+ key.zir_index,
+ namespace_index,
+ enum_obj.name,
+ new_cau_index,
+ small,
+ body,
+ tag_type_ref,
+ any_values,
+ fields_len,
+ zir,
+ body_end,
+ ) catch |err| switch (err) {
+ error.GenericPoison => unreachable,
+ error.ComptimeBreak => unreachable,
+ error.ComptimeReturn => unreachable,
+ error.AnalysisFail, error.OutOfMemory => |e| return e,
+ };
+
+ return wip_ty.index;
+}
+
+/// Given a namespace, re-scan its declarations from the type definition if they have not
+/// yet been re-scanned on this update.
+/// If the type declaration instruction has been lost, returns `error.AnalysisFail`.
+/// This will effectively short-circuit the caller, which will be semantic analysis of a
+/// guaranteed-unreferenced `AnalUnit`, to trigger a transitive analysis error.
+pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) Zcu.SemaError!void {
+ const zcu = pt.zcu;
+ const ip = &zcu.intern_pool;
+ const namespace = zcu.namespacePtr(namespace_index);
+
+ if (namespace.generation == zcu.generation) return;
+
+ const Container = enum { @"struct", @"union", @"enum", @"opaque" };
+ const container: Container, const full_key = switch (ip.indexToKey(namespace.owner_type)) {
+ .struct_type => |k| .{ .@"struct", k },
+ .union_type => |k| .{ .@"union", k },
+ .enum_type => |k| .{ .@"enum", k },
+ .opaque_type => |k| .{ .@"opaque", k },
+ else => unreachable, // namespaces are owned by a container type
+ };
+
+ const key = switch (full_key) {
+ .reified, .empty_struct, .generated_tag => {
+ // Namespace always empty, so up-to-date.
+ namespace.generation = zcu.generation;
+ return;
+ },
+ .declared => |d| d,
+ };
+
+ // Namespace outdated -- re-scan the type if necessary.
+
+ const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail;
+ const file = zcu.fileByIndex(inst_info.file);
+ if (file.status != .success_zir) return error.AnalysisFail;
+ const zir = file.zir;
+
+ assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended);
+ const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended;
+
+ const decls = switch (container) {
+ .@"struct" => decls: {
+ assert(extended.opcode == .struct_decl);
+ const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
+ var extra_index = extra.end;
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+ extra_index += @intFromBool(small.has_fields_len);
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ extra_index += captures_len;
+ if (small.has_backing_int) {
+ const backing_int_body_len = zir.extra[extra_index];
+ extra_index += 1; // backing_int_body_len
+ if (backing_int_body_len == 0) {
+ extra_index += 1; // backing_int_ref
+ } else {
+ extra_index += backing_int_body_len; // backing_int_body_inst
+ }
+ }
+ break :decls zir.bodySlice(extra_index, decls_len);
+ },
+ .@"union" => decls: {
+ assert(extended.opcode == .union_decl);
+ const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
+ var extra_index = extra.end;
+ extra_index += @intFromBool(small.has_tag_type);
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+ extra_index += @intFromBool(small.has_body_len);
+ extra_index += @intFromBool(small.has_fields_len);
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ extra_index += captures_len;
+ break :decls zir.bodySlice(extra_index, decls_len);
+ },
+ .@"enum" => decls: {
+ assert(extended.opcode == .enum_decl);
+ const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
+ var extra_index = extra.end;
+ extra_index += @intFromBool(small.has_tag_type);
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+ extra_index += @intFromBool(small.has_body_len);
+ extra_index += @intFromBool(small.has_fields_len);
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ extra_index += captures_len;
+ break :decls zir.bodySlice(extra_index, decls_len);
+ },
+ .@"opaque" => decls: {
+ assert(extended.opcode == .opaque_decl);
+ const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small);
+ const extra = zir.extraData(Zir.Inst.OpaqueDecl, extended.operand);
+ var extra_index = extra.end;
+ const captures_len = if (small.has_captures_len) blk: {
+ const captures_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk captures_len;
+ } else 0;
+ const decls_len = if (small.has_decls_len) blk: {
+ const decls_len = zir.extra[extra_index];
+ extra_index += 1;
+ break :blk decls_len;
+ } else 0;
+ extra_index += captures_len;
+ break :decls zir.bodySlice(extra_index, decls_len);
+ },
+ };
+
+ try pt.scanNamespace(namespace_index, decls);
+ namespace.generation = zcu.generation;
+}
+
const Air = @import("../Air.zig");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
@@ -3379,6 +3866,7 @@ const builtin = @import("builtin");
const Cache = std.Build.Cache;
const dev = @import("../dev.zig");
const InternPool = @import("../InternPool.zig");
+const AnalUnit = InternPool.AnalUnit;
const isUpDir = @import("../introspect.zig").isUpDir;
const Liveness = @import("../Liveness.zig");
const log = std.log.scoped(.zcu);
src/Compilation.zig
@@ -3569,6 +3569,8 @@ pub fn performAllTheWork(
mod.sema_prog_node = std.Progress.Node.none;
mod.codegen_prog_node.end();
mod.codegen_prog_node = std.Progress.Node.none;
+
+ mod.generation += 1;
};
try comp.performAllTheWorkInner(main_progress_node);
if (!InternPool.single_threaded) if (comp.codegen_work.job_error) |job_error| return job_error;
src/InternPool.zig
@@ -684,10 +684,6 @@ pub fn dependencyIterator(ip: *const InternPool, dependee: Dependee) DependencyI
.ip = ip,
.next_entry = .none,
};
- if (ip.dep_entries.items[@intFromEnum(first_entry)].depender == .none) return .{
- .ip = ip,
- .next_entry = .none,
- };
return .{
.ip = ip,
.next_entry = first_entry.toOptional(),
@@ -724,7 +720,6 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
if (gop.found_existing and ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].depender == .none) {
// Dummy entry, so we can reuse it rather than allocating a new one!
- ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].next = .none;
break :new_index gop.value_ptr.*;
}
@@ -732,7 +727,12 @@ pub fn addDependency(ip: *InternPool, gpa: Allocator, depender: AnalUnit, depend
const new_index: DepEntry.Index, const ptr = if (ip.free_dep_entries.popOrNull()) |new_index| new: {
break :new .{ new_index, &ip.dep_entries.items[@intFromEnum(new_index)] };
} else .{ @enumFromInt(ip.dep_entries.items.len), ip.dep_entries.addOneAssumeCapacity() };
- ptr.next = if (gop.found_existing) gop.value_ptr.*.toOptional() else .none;
+ if (gop.found_existing) {
+ ptr.next = gop.value_ptr.*.toOptional();
+ ip.dep_entries.items[@intFromEnum(gop.value_ptr.*)].prev = new_index.toOptional();
+ } else {
+ ptr.next = .none;
+ }
gop.value_ptr.* = new_index;
break :new_index new_index;
},
@@ -754,10 +754,9 @@ pub const NamespaceNameKey = struct {
};
pub const DepEntry = extern struct {
- /// If null, this is a dummy entry - all other fields are `undefined`. It is
- /// the first and only entry in one of `intern_pool.*_deps`, and does not
- /// appear in any list by `first_dependency`, but is not in
- /// `free_dep_entries` since `*_deps` stores a reference to it.
+ /// If null, this is a dummy entry. `next_dependee` is undefined. This is the first
+ /// entry in one of `*_deps`, and does not appear in any list by `first_dependency`,
+ /// but is not in `free_dep_entries` since `*_deps` stores a reference to it.
depender: AnalUnit.Optional,
/// Index into `dep_entries` forming a doubly linked list of all dependencies on this dependee.
/// Used to iterate all dependers for a given dependee during an update.
@@ -2689,7 +2688,12 @@ pub const Key = union(enum) {
.variable => |a_info| {
const b_info = b.variable;
- return a_info.owner_nav == b_info.owner_nav;
+ return a_info.owner_nav == b_info.owner_nav and
+ a_info.ty == b_info.ty and
+ a_info.init == b_info.init and
+ a_info.lib_name == b_info.lib_name and
+ a_info.is_threadlocal == b_info.is_threadlocal and
+ a_info.is_weak_linkage == b_info.is_weak_linkage;
},
.@"extern" => |a_info| {
const b_info = b.@"extern";
@@ -8016,6 +8020,10 @@ pub const UnionTypeInit = struct {
zir_index: TrackedInst.Index,
captures: []const CaptureValue,
},
+ declared_owned_captures: struct {
+ zir_index: TrackedInst.Index,
+ captures: CaptureValue.Slice,
+ },
reified: struct {
zir_index: TrackedInst.Index,
type_hash: u64,
@@ -8037,6 +8045,10 @@ pub fn getUnionType(
.zir_index = d.zir_index,
.captures = .{ .external = d.captures },
} },
+ .declared_owned_captures => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .owned = d.captures },
+ } },
.reified => |r| .{ .reified = .{
.zir_index = r.zir_index,
.type_hash = r.type_hash,
@@ -8060,7 +8072,7 @@ pub fn getUnionType(
// TODO: fmt bug
// zig fmt: off
switch (ini.key) {
- .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
+ inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
@@ -8069,7 +8081,10 @@ pub fn getUnionType(
const extra_index = addExtraAssumeCapacity(extra, Tag.TypeUnion{
.flags = .{
- .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,
+ .any_captures = switch (ini.key) {
+ inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
+ .reified => false,
+ },
.runtime_tag = ini.flags.runtime_tag,
.any_aligned_fields = ini.flags.any_aligned_fields,
.layout = ini.flags.layout,
@@ -8078,7 +8093,10 @@ pub fn getUnionType(
.assumed_runtime_bits = ini.flags.assumed_runtime_bits,
.assumed_pointer_aligned = ini.flags.assumed_pointer_aligned,
.alignment = ini.flags.alignment,
- .is_reified = ini.key == .reified,
+ .is_reified = switch (ini.key) {
+ .declared, .declared_owned_captures => false,
+ .reified => true,
+ },
},
.fields_len = ini.fields_len,
.size = std.math.maxInt(u32),
@@ -8102,6 +8120,10 @@ pub fn getUnionType(
extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
},
+ .declared_owned_captures => |d| if (d.captures.len != 0) {
+ extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
+ extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
+ },
.reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
}
@@ -8199,6 +8221,10 @@ pub const StructTypeInit = struct {
zir_index: TrackedInst.Index,
captures: []const CaptureValue,
},
+ declared_owned_captures: struct {
+ zir_index: TrackedInst.Index,
+ captures: CaptureValue.Slice,
+ },
reified: struct {
zir_index: TrackedInst.Index,
type_hash: u64,
@@ -8220,6 +8246,10 @@ pub fn getStructType(
.zir_index = d.zir_index,
.captures = .{ .external = d.captures },
} },
+ .declared_owned_captures => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .owned = d.captures },
+ } },
.reified => |r| .{ .reified = .{
.zir_index = r.zir_index,
.type_hash = r.type_hash,
@@ -8251,7 +8281,7 @@ pub fn getStructType(
// TODO: fmt bug
// zig fmt: off
switch (ini.key) {
- .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
+ inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
@@ -8267,10 +8297,16 @@ pub fn getStructType(
.backing_int_ty = .none,
.names_map = names_map,
.flags = .{
- .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,
+ .any_captures = switch (ini.key) {
+ inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
+ .reified => false,
+ },
.field_inits_wip = false,
.inits_resolved = ini.inits_resolved,
- .is_reified = ini.key == .reified,
+ .is_reified = switch (ini.key) {
+ .declared, .declared_owned_captures => false,
+ .reified => true,
+ },
},
});
try items.append(.{
@@ -8282,6 +8318,10 @@ pub fn getStructType(
extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
},
+ .declared_owned_captures => |d| if (d.captures.len != 0) {
+ extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
+ extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
+ },
.reified => |r| {
_ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));
},
@@ -8309,7 +8349,7 @@ pub fn getStructType(
// TODO: fmt bug
// zig fmt: off
switch (ini.key) {
- .declared => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
+ inline .declared, .declared_owned_captures => |d| @intFromBool(d.captures.len != 0) + d.captures.len,
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
@@ -8324,7 +8364,10 @@ pub fn getStructType(
.fields_len = ini.fields_len,
.size = std.math.maxInt(u32),
.flags = .{
- .any_captures = ini.key == .declared and ini.key.declared.captures.len != 0,
+ .any_captures = switch (ini.key) {
+ inline .declared, .declared_owned_captures => |d| d.captures.len != 0,
+ .reified => false,
+ },
.is_extern = is_extern,
.known_non_opv = ini.known_non_opv,
.requires_comptime = ini.requires_comptime,
@@ -8342,7 +8385,10 @@ pub fn getStructType(
.field_inits_wip = false,
.inits_resolved = ini.inits_resolved,
.fully_resolved = false,
- .is_reified = ini.key == .reified,
+ .is_reified = switch (ini.key) {
+ .declared, .declared_owned_captures => false,
+ .reified => true,
+ },
},
});
try items.append(.{
@@ -8354,6 +8400,10 @@ pub fn getStructType(
extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures)});
},
+ .declared_owned_captures => |d| if (d.captures.len != 0) {
+ extra.appendAssumeCapacity(.{@intCast(d.captures.len)});
+ extra.appendSliceAssumeCapacity(.{@ptrCast(d.captures.get(ip))});
+ },
.reified => |r| {
_ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash));
},
@@ -9157,6 +9207,10 @@ pub const EnumTypeInit = struct {
zir_index: TrackedInst.Index,
captures: []const CaptureValue,
},
+ declared_owned_captures: struct {
+ zir_index: TrackedInst.Index,
+ captures: CaptureValue.Slice,
+ },
reified: struct {
zir_index: TrackedInst.Index,
type_hash: u64,
@@ -9261,6 +9315,10 @@ pub fn getEnumType(
.zir_index = d.zir_index,
.captures = .{ .external = d.captures },
} },
+ .declared_owned_captures => |d| .{ .declared = .{
+ .zir_index = d.zir_index,
+ .captures = .{ .owned = d.captures },
+ } },
.reified => |r| .{ .reified = .{
.zir_index = r.zir_index,
.type_hash = r.type_hash,
@@ -9288,7 +9346,7 @@ pub fn getEnumType(
// TODO: fmt bug
// zig fmt: off
switch (ini.key) {
- .declared => |d| d.captures.len,
+ inline .declared, .declared_owned_captures => |d| d.captures.len,
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
@@ -9298,7 +9356,7 @@ pub fn getEnumType(
const extra_index = addExtraAssumeCapacity(extra, EnumAuto{
.name = undefined, // set by `prepare`
.captures_len = switch (ini.key) {
- .declared => |d| @intCast(d.captures.len),
+ inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len),
.reified => std.math.maxInt(u32),
},
.namespace = undefined, // set by `prepare`
@@ -9317,6 +9375,7 @@ pub fn getEnumType(
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))}),
.reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
}
const names_start = extra.mutate.len;
@@ -9347,7 +9406,7 @@ pub fn getEnumType(
// TODO: fmt bug
// zig fmt: off
switch (ini.key) {
- .declared => |d| d.captures.len,
+ inline .declared, .declared_owned_captures => |d| d.captures.len,
.reified => 2, // type_hash: PackedU64
} +
// zig fmt: on
@@ -9358,7 +9417,7 @@ pub fn getEnumType(
const extra_index = addExtraAssumeCapacity(extra, EnumExplicit{
.name = undefined, // set by `prepare`
.captures_len = switch (ini.key) {
- .declared => |d| @intCast(d.captures.len),
+ inline .declared, .declared_owned_captures => |d| @intCast(d.captures.len),
.reified => std.math.maxInt(u32),
},
.namespace = undefined, // set by `prepare`
@@ -9382,6 +9441,7 @@ pub fn getEnumType(
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))}),
.reified => |r| _ = addExtraAssumeCapacity(extra, PackedU64.init(r.type_hash)),
}
const names_start = extra.mutate.len;
@@ -9445,10 +9505,12 @@ pub fn getGeneratedTagEnumType(
.tid = tid,
.index = items.mutate.len,
}, ip);
+ const parent_namespace = ip.namespacePtr(ini.parent_namespace);
const namespace = try ip.createNamespace(gpa, tid, .{
.parent = ini.parent_namespace.toOptional(),
.owner_type = enum_index,
- .file_scope = ip.namespacePtr(ini.parent_namespace).file_scope,
+ .file_scope = parent_namespace.file_scope,
+ .generation = parent_namespace.generation,
});
errdefer ip.destroyNamespace(tid, namespace);
@@ -11044,6 +11106,7 @@ pub fn destroyNamespace(
.parent = undefined,
.file_scope = undefined,
.owner_type = undefined,
+ .generation = undefined,
};
@field(namespace, Local.namespace_next_free_field) =
@enumFromInt(local.mutate.namespaces.free_list);
src/Sema.zig
@@ -2723,32 +2723,6 @@ fn wrapWipTy(sema: *Sema, wip_ty: anytype) @TypeOf(wip_ty) {
return new;
}
-/// Given a type just looked up in the `InternPool`, check whether it is
-/// considered outdated on this update. If so, returns `true`, and the
-/// caller must replace the outdated type with a fresh one.
-fn checkOutdatedType(sema: *Sema, ty: InternPool.Index) !bool {
- const pt = sema.pt;
- const zcu = pt.zcu;
- const ip = &zcu.intern_pool;
-
- if (!zcu.comp.incremental) return false;
-
- const cau_index = switch (ip.indexToKey(ty)) {
- .struct_type => ip.loadStructType(ty).cau.unwrap().?,
- .union_type => ip.loadUnionType(ty).cau,
- .enum_type => ip.loadEnumType(ty).cau.unwrap().?,
- else => unreachable,
- };
- const cau_unit = AnalUnit.wrap(.{ .cau = cau_index });
- const was_outdated = zcu.outdated.swapRemove(cau_unit) or
- zcu.potentially_outdated.swapRemove(cau_unit);
- if (!was_outdated) return false;
- _ = zcu.outdated_ready.swapRemove(cau_unit);
- zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, cau_unit);
- try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty });
- return true;
-}
-
fn zirStructDecl(
sema: *Sema,
block: *Block,
@@ -2815,13 +2789,16 @@ fn zirStructDecl(
} },
};
const wip_ty = sema.wrapWipTy(switch (try ip.getStructType(gpa, pt.tid, struct_init, false)) {
- .existing => |ty| wip: {
- if (!try sema.checkOutdatedType(ty)) {
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
- }
- break :wip (try ip.getStructType(gpa, pt.tid, struct_init, true)).wip;
+ .existing => |ty| {
+ const new_ty = try pt.ensureTypeUpToDate(ty, false);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(mod));
+
+ try sema.declareDependency(.{ .interned = new_ty });
+ try sema.addTypeReferenceEntry(src, new_ty);
+ return Air.internedToRef(new_ty);
},
.wip => |wip| wip,
});
@@ -2839,6 +2816,7 @@ fn zirStructDecl(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
errdefer pt.destroyNamespace(new_namespace_index);
@@ -2977,7 +2955,6 @@ fn zirEnumDecl(
const tracked_inst = try block.trackZir(inst);
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 tag_type_ref = if (small.has_tag_type) blk: {
const tag_type_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
@@ -3041,13 +3018,16 @@ fn zirEnumDecl(
} },
};
const wip_ty = sema.wrapWipTy(switch (try ip.getEnumType(gpa, pt.tid, enum_init, false)) {
- .existing => |ty| wip: {
- if (!try sema.checkOutdatedType(ty)) {
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
- }
- break :wip (try ip.getEnumType(gpa, pt.tid, enum_init, true)).wip;
+ .existing => |ty| {
+ const new_ty = try pt.ensureTypeUpToDate(ty, false);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(mod));
+
+ try sema.declareDependency(.{ .interned = new_ty });
+ try sema.addTypeReferenceEntry(src, new_ty);
+ return Air.internedToRef(new_ty);
},
.wip => |wip| wip,
});
@@ -3071,19 +3051,12 @@ fn zirEnumDecl(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
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);
- if (pt.zcu.comp.incremental) {
- try mod.intern_pool.addDependency(
- gpa,
- AnalUnit.wrap(.{ .cau = new_cau_index }),
- .{ .src_hash = try block.trackZir(inst) },
- );
- }
-
try pt.scanNamespace(new_namespace_index, decls);
try sema.declareDependency(.{ .interned = wip_ty.index });
@@ -3094,144 +3067,22 @@ fn zirEnumDecl(
wip_ty.prepare(ip, new_cau_index, new_namespace_index);
done = true;
- const int_tag_ty = ty: {
- // We create a block for the field type instructions because they
- // may need to reference Decls from inside the enum namespace.
- // Within the field type, default value, and alignment expressions, the owner should be the enum's `Cau`.
-
- const prev_owner = sema.owner;
- sema.owner = AnalUnit.wrap(.{ .cau = new_cau_index });
- defer sema.owner = prev_owner;
-
- const prev_func_index = sema.func_index;
- sema.func_index = .none;
- defer sema.func_index = prev_func_index;
-
- var enum_block: Block = .{
- .parent = null,
- .sema = sema,
- .namespace = new_namespace_index,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- .src_base_inst = tracked_inst,
- .type_name_ctx = type_name,
- };
- defer enum_block.instructions.deinit(sema.gpa);
-
- if (body.len != 0) {
- _ = try sema.analyzeInlineBody(&enum_block, body, inst);
- }
-
- if (tag_type_ref != .none) {
- const ty = try sema.resolveType(&enum_block, tag_ty_src, tag_type_ref);
- if (ty.zigTypeTag(mod) != .Int and ty.zigTypeTag(mod) != .ComptimeInt) {
- return sema.fail(&enum_block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(pt)});
- }
- break :ty ty;
- } else if (fields_len == 0) {
- break :ty try pt.intType(.unsigned, 0);
- } else {
- const bits = std.math.log2_int_ceil(usize, fields_len);
- break :ty try pt.intType(.unsigned, bits);
- }
- };
-
- wip_ty.setTagTy(ip, int_tag_ty.toIntern());
-
- if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
- if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(pt)) {
- return sema.fail(block, src, "non-exhaustive enum specifies every value", .{});
- }
- }
-
- var bit_bag_index: usize = body_end;
- var cur_bit_bag: u32 = undefined;
- var field_i: u32 = 0;
- var last_tag_val: ?Value = null;
- while (field_i < fields_len) : (field_i += 1) {
- if (field_i % 32 == 0) {
- cur_bit_bag = sema.code.extra[bit_bag_index];
- bit_bag_index += 1;
- }
- const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
- cur_bit_bag >>= 1;
-
- const field_name_index: Zir.NullTerminatedString = @enumFromInt(sema.code.extra[extra_index]);
- const field_name_zir = sema.code.nullTerminatedString(field_name_index);
- extra_index += 2; // field name, doc comment
-
- const field_name = try mod.intern_pool.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
-
- const value_src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_value = field_i },
- };
-
- const tag_overflow = if (has_tag_value) overflow: {
- const tag_val_ref: Zir.Inst.Ref = @enumFromInt(sema.code.extra[extra_index]);
- extra_index += 1;
- const tag_inst = try sema.resolveInst(tag_val_ref);
- last_tag_val = try sema.resolveConstDefinedValue(block, .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_name = field_i },
- }, tag_inst, .{
- .needed_comptime_reason = "enum tag value must be comptime-known",
- });
- if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
- last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
- if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| {
- assert(conflict.kind == .value); // AstGen validated names are unique
- const other_field_src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_value = conflict.prev_field_idx },
- };
- const msg = msg: {
- const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
- errdefer msg.destroy(gpa);
- try sema.errNote(other_field_src, msg, "other occurrence here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- break :overflow false;
- } else if (any_values) overflow: {
- var overflow: ?usize = null;
- last_tag_val = if (last_tag_val) |val|
- try sema.intAdd(val, try pt.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
- else
- try pt.intValue(int_tag_ty, 0);
- if (overflow != null) break :overflow true;
- if (wip_ty.nextField(&mod.intern_pool, field_name, last_tag_val.?.toIntern())) |conflict| {
- assert(conflict.kind == .value); // AstGen validated names are unique
- const other_field_src: LazySrcLoc = .{
- .base_node_inst = tracked_inst,
- .offset = .{ .container_field_value = conflict.prev_field_idx },
- };
- const msg = msg: {
- const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, sema)});
- errdefer msg.destroy(gpa);
- try sema.errNote(other_field_src, msg, "other occurrence here", .{});
- break :msg msg;
- };
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- break :overflow false;
- } else overflow: {
- assert(wip_ty.nextField(&mod.intern_pool, field_name, .none) == null);
- last_tag_val = try pt.intValue(Type.comptime_int, field_i);
- if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
- last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
- break :overflow false;
- };
-
- if (tag_overflow) {
- const msg = try sema.errMsg(value_src, "enumeration value '{}' too large for type '{}'", .{
- last_tag_val.?.fmtValueSema(pt, sema), int_tag_ty.fmt(pt),
- });
- return sema.failWithOwnedErrorMsg(block, msg);
- }
- }
+ try Sema.resolveDeclaredEnum(
+ pt,
+ wip_ty,
+ inst,
+ tracked_inst,
+ new_namespace_index,
+ type_name,
+ new_cau_index,
+ small,
+ body,
+ tag_type_ref,
+ any_values,
+ fields_len,
+ sema.code,
+ body_end,
+ );
codegen_type: {
if (mod.comp.config.use_llvm) break :codegen_type;
@@ -3311,13 +3162,16 @@ fn zirUnionDecl(
} },
};
const wip_ty = sema.wrapWipTy(switch (try ip.getUnionType(gpa, pt.tid, union_init, false)) {
- .existing => |ty| wip: {
- if (!try sema.checkOutdatedType(ty)) {
- try sema.declareDependency(.{ .interned = ty });
- try sema.addTypeReferenceEntry(src, ty);
- return Air.internedToRef(ty);
- }
- break :wip (try ip.getUnionType(gpa, pt.tid, union_init, true)).wip;
+ .existing => |ty| {
+ const new_ty = try pt.ensureTypeUpToDate(ty, false);
+
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ try pt.ensureNamespaceUpToDate(Type.fromInterned(new_ty).getNamespaceIndex(mod));
+
+ try sema.declareDependency(.{ .interned = new_ty });
+ try sema.addTypeReferenceEntry(src, new_ty);
+ return Air.internedToRef(new_ty);
},
.wip => |wip| wip,
});
@@ -3335,6 +3189,7 @@ fn zirUnionDecl(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
errdefer pt.destroyNamespace(new_namespace_index);
@@ -3344,7 +3199,7 @@ fn zirUnionDecl(
try mod.intern_pool.addDependency(
gpa,
AnalUnit.wrap(.{ .cau = new_cau_index }),
- .{ .src_hash = try block.trackZir(inst) },
+ .{ .src_hash = tracked_inst },
);
}
@@ -3406,8 +3261,12 @@ fn zirOpaqueDecl(
};
// No `wrapWipTy` needed as no std.builtin types are opaque.
const wip_ty = switch (try ip.getOpaqueType(gpa, pt.tid, opaque_init)) {
- // No `checkOutdatedType` as opaque types are never outdated.
.existing => |ty| {
+ // Make sure we update the namespace if the declaration is re-analyzed, to pick
+ // up on e.g. changed comptime decls.
+ try pt.ensureNamespaceUpToDate(Type.fromInterned(ty).getNamespaceIndex(mod));
+
+ try sema.declareDependency(.{ .interned = ty });
try sema.addTypeReferenceEntry(src, ty);
return Air.internedToRef(ty);
},
@@ -3427,6 +3286,7 @@ fn zirOpaqueDecl(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
errdefer pt.destroyNamespace(new_namespace_index);
@@ -6072,6 +5932,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
// trigger re-analysis later.
try pt.ensureFileAnalyzed(result.file_index);
const ty = zcu.fileRootType(result.file_index);
+ try sema.declareDependency(.{ .interned = ty });
try sema.addTypeReferenceEntry(src, ty);
return Air.internedToRef(ty);
}
@@ -6821,6 +6682,8 @@ fn lookupInNamespace(
const zcu = pt.zcu;
const ip = &zcu.intern_pool;
+ try pt.ensureNamespaceUpToDate(namespace_index);
+
const namespace = zcu.namespacePtr(namespace_index);
const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu };
@@ -14038,6 +13901,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
// trigger re-analysis later.
try pt.ensureFileAnalyzed(result.file_index);
const ty = zcu.fileRootType(result.file_index);
+ try sema.declareDependency(.{ .interned = ty });
try sema.addTypeReferenceEntry(operand_src, ty);
return Air.internedToRef(ty);
}
@@ -17703,7 +17567,13 @@ fn zirThis(
_ = extended;
const pt = sema.pt;
const namespace = pt.zcu.namespacePtr(block.namespace);
- return Air.internedToRef(namespace.owner_type);
+ const new_ty = try pt.ensureTypeUpToDate(namespace.owner_type, false);
+ switch (pt.zcu.intern_pool.indexToKey(new_ty)) {
+ .struct_type, .union_type, .enum_type => try sema.declareDependency(.{ .interned = new_ty }),
+ .opaque_type => {},
+ else => unreachable,
+ }
+ return Air.internedToRef(new_ty);
}
fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
@@ -19005,6 +18875,7 @@ fn typeInfoNamespaceDecls(
const ip = &zcu.intern_pool;
const namespace_index = opt_namespace_index.unwrap() orelse return;
+ try pt.ensureNamespaceUpToDate(namespace_index);
const namespace = zcu.namespacePtr(namespace_index);
const gop = try seen_namespaces.getOrPut(namespace);
@@ -21871,6 +21742,7 @@ fn zirReify(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
try sema.addTypeReferenceEntry(src, wip_ty.index);
@@ -22080,6 +21952,7 @@ fn reifyEnum(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
@@ -22384,6 +22257,7 @@ fn reifyUnion(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
@@ -22667,6 +22541,7 @@ fn reifyStruct(
.parent = block.namespace.toOptional(),
.owner_type = wip_ty.index,
.file_scope = block.getFileScopeIndex(mod),
+ .generation = mod.generation,
});
const new_cau_index = try ip.createTypeCau(gpa, pt.tid, tracked_inst, new_namespace_index, wip_ty.index);
@@ -35373,7 +35248,7 @@ pub fn resolveStructLayout(sema: *Sema, ty: Type) SemaError!void {
if (struct_type.haveLayout(ip))
return;
- try ty.resolveFields(pt);
+ try sema.resolveTypeFieldsStruct(ty.toIntern(), struct_type);
if (struct_type.layout == .@"packed") {
semaBackingIntType(pt, struct_type) catch |err| switch (err) {
@@ -38499,6 +38374,187 @@ fn getOwnerFuncDeclInst(sema: *Sema) InternPool.TrackedInst.Index {
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.
+pub fn resolveDeclaredEnum(
+ pt: Zcu.PerThread,
+ wip_ty: InternPool.WipEnumType,
+ inst: Zir.Inst.Index,
+ 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,
+ any_values: bool,
+ fields_len: u32,
+ zir: Zir,
+ body_end: usize,
+) Zcu.CompileError!void {
+ const zcu = pt.zcu;
+ const gpa = zcu.gpa;
+ const ip = &zcu.intern_pool;
+
+ const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
+
+ 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 });
+
+ var arena = std.heap.ArenaAllocator.init(gpa);
+ defer 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 = 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();
+
+ try sema.declareDependency(.{ .src_hash = tracked_inst });
+
+ var block: Block = .{
+ .parent = null,
+ .sema = &sema,
+ .namespace = namespace,
+ .instructions = .{},
+ .inlining = null,
+ .is_comptime = true,
+ .src_base_inst = tracked_inst,
+ .type_name_ctx = type_name,
+ };
+ defer block.instructions.deinit(gpa);
+
+ const int_tag_ty = ty: {
+ if (body.len != 0) {
+ _ = try sema.analyzeInlineBody(&block, body, inst);
+ }
+
+ if (tag_type_ref != .none) {
+ const ty = try sema.resolveType(&block, tag_ty_src, tag_type_ref);
+ if (ty.zigTypeTag(zcu) != .Int and ty.zigTypeTag(zcu) != .ComptimeInt) {
+ return sema.fail(&block, tag_ty_src, "expected integer tag type, found '{}'", .{ty.fmt(pt)});
+ }
+ break :ty ty;
+ } else if (fields_len == 0) {
+ break :ty try pt.intType(.unsigned, 0);
+ } else {
+ const bits = std.math.log2_int_ceil(usize, fields_len);
+ break :ty try pt.intType(.unsigned, bits);
+ }
+ };
+
+ wip_ty.setTagTy(ip, int_tag_ty.toIntern());
+
+ if (small.nonexhaustive and int_tag_ty.toIntern() != .comptime_int_type) {
+ if (fields_len > 1 and std.math.log2_int(u64, fields_len) == int_tag_ty.bitSize(pt)) {
+ return sema.fail(&block, src, "non-exhaustive enum specifies every value", .{});
+ }
+ }
+
+ var extra_index = body_end + bit_bags_count;
+ var bit_bag_index: usize = body_end;
+ var cur_bit_bag: u32 = undefined;
+ var last_tag_val: ?Value = null;
+ for (0..fields_len) |field_i_usize| {
+ const field_i: u32 = @intCast(field_i_usize);
+ if (field_i % 32 == 0) {
+ cur_bit_bag = zir.extra[bit_bag_index];
+ bit_bag_index += 1;
+ }
+ const has_tag_value = @as(u1, @truncate(cur_bit_bag)) != 0;
+ cur_bit_bag >>= 1;
+
+ const field_name_index: Zir.NullTerminatedString = @enumFromInt(zir.extra[extra_index]);
+ const field_name_zir = zir.nullTerminatedString(field_name_index);
+ extra_index += 2; // field name, doc comment
+
+ const field_name = try ip.getOrPutString(gpa, pt.tid, field_name_zir, .no_embedded_nulls);
+
+ const value_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_value = field_i },
+ };
+
+ const tag_overflow = if (has_tag_value) overflow: {
+ const tag_val_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
+ extra_index += 1;
+ const tag_inst = try sema.resolveInst(tag_val_ref);
+ last_tag_val = try sema.resolveConstDefinedValue(&block, .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_name = field_i },
+ }, tag_inst, .{
+ .needed_comptime_reason = "enum tag value must be comptime-known",
+ });
+ if (!(try sema.intFitsInType(last_tag_val.?, int_tag_ty, null))) break :overflow true;
+ last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
+ if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
+ assert(conflict.kind == .value); // AstGen validated names are unique
+ const other_field_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_value = conflict.prev_field_idx },
+ };
+ const msg = msg: {
+ const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, &sema)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(other_field_src, msg, "other occurrence here", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(&block, msg);
+ }
+ break :overflow false;
+ } else if (any_values) overflow: {
+ var overflow: ?usize = null;
+ last_tag_val = if (last_tag_val) |val|
+ try sema.intAdd(val, try pt.intValue(int_tag_ty, 1), int_tag_ty, &overflow)
+ else
+ try pt.intValue(int_tag_ty, 0);
+ if (overflow != null) break :overflow true;
+ if (wip_ty.nextField(ip, field_name, last_tag_val.?.toIntern())) |conflict| {
+ assert(conflict.kind == .value); // AstGen validated names are unique
+ const other_field_src: LazySrcLoc = .{
+ .base_node_inst = tracked_inst,
+ .offset = .{ .container_field_value = conflict.prev_field_idx },
+ };
+ const msg = msg: {
+ const msg = try sema.errMsg(value_src, "enum tag value {} already taken", .{last_tag_val.?.fmtValueSema(pt, &sema)});
+ errdefer msg.destroy(gpa);
+ try sema.errNote(other_field_src, msg, "other occurrence here", .{});
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(&block, msg);
+ }
+ break :overflow false;
+ } else overflow: {
+ assert(wip_ty.nextField(ip, field_name, .none) == null);
+ last_tag_val = try pt.intValue(Type.comptime_int, field_i);
+ if (!try sema.intFitsInType(last_tag_val.?, int_tag_ty, null)) break :overflow true;
+ last_tag_val = try pt.getCoerced(last_tag_val.?, int_tag_ty);
+ break :overflow false;
+ };
+
+ if (tag_overflow) {
+ const msg = try sema.errMsg(value_src, "enumeration value '{}' too large for type '{}'", .{
+ last_tag_val.?.fmtValueSema(pt, &sema), int_tag_ty.fmt(pt),
+ });
+ return sema.failWithOwnedErrorMsg(&block, msg);
+ }
+ }
+}
+
pub const bitCastVal = @import("Sema/bitcast.zig").bitCast;
pub const bitCastSpliceVal = @import("Sema/bitcast.zig").bitCastSplice;
src/Zcu.zig
@@ -215,6 +215,8 @@ panic_messages: [PanicId.len]InternPool.Nav.Index.Optional = .{.none} ** PanicId
panic_func_index: InternPool.Index = .none,
null_stack_trace: InternPool.Index = .none,
+generation: u32 = 0,
+
pub const PerThread = @import("Zcu/PerThread.zig");
pub const PanicId = enum {
@@ -332,6 +334,7 @@ pub const TypeReference = struct {
pub const Namespace = struct {
parent: OptionalIndex,
file_scope: File.Index,
+ generation: u32,
/// Will be a struct, enum, union, or opaque.
owner_type: InternPool.Index,
/// Members of the namespace which are marked `pub`.
@@ -2295,7 +2298,7 @@ pub fn markDependeeOutdated(
marked_po: enum { not_marked_po, marked_po },
dependee: InternPool.Dependee,
) !void {
- log.debug("outdated dependee: {}", .{fmtDependee(dependee, zcu)});
+ log.debug("outdated dependee: {}", .{zcu.fmtDependee(dependee)});
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
@@ -2303,9 +2306,9 @@ pub fn markDependeeOutdated(
.not_marked_po => {},
.marked_po => {
po_dep_count.* -= 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* });
+ log.debug("outdated {} => already outdated {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
if (po_dep_count.* == 0) {
- log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
},
@@ -2316,20 +2319,19 @@ pub fn markDependeeOutdated(
const new_po_dep_count = switch (marked_po) {
.not_marked_po => if (opt_po_entry) |e| e.value else 0,
.marked_po => if (opt_po_entry) |e| e.value - 1 else {
- // This dependency has been registered during in-progress analysis, but the unit is
- // not in `potentially_outdated` because analysis is in-progress. Nothing to do.
+ // This `AnalUnit` has already been re-analyzed this update, and registered a dependency
+ // on this thing, but already has sufficiently up-to-date information. Nothing to do.
continue;
},
};
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), new_po_dep_count });
try zcu.outdated.putNoClobber(
zcu.gpa,
depender,
new_po_dep_count,
);
- log.debug("outdated: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated {} => new outdated {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), new_po_dep_count });
if (new_po_dep_count == 0) {
- log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
// If this is a Decl and was not previously PO, we must recursively
@@ -2342,16 +2344,16 @@ pub fn markDependeeOutdated(
}
pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
- log.debug("up-to-date dependee: {}", .{fmtDependee(dependee, zcu)});
+ log.debug("up-to-date dependee: {}", .{zcu.fmtDependee(dependee)});
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
// This depender is already outdated, but it now has one
// less PO dependency!
po_dep_count.* -= 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), po_dep_count.* });
+ log.debug("up-to-date {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), po_dep_count.* });
if (po_dep_count.* == 0) {
- log.debug("outdated ready: {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("outdated ready: {}", .{zcu.fmtAnalUnit(depender)});
try zcu.outdated_ready.put(zcu.gpa, depender, {});
}
continue;
@@ -2365,11 +2367,11 @@ pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
};
if (ptr.* > 1) {
ptr.* -= 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(depender, zcu), ptr.* });
+ log.debug("up-to-date {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender), ptr.* });
continue;
}
- log.debug("up-to-date (po deps = 0): {}", .{fmtAnalUnit(depender, zcu)});
+ log.debug("up-to-date {} => {} po_deps=0 (up-to-date)", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(depender) });
// This dependency is no longer PO, i.e. is known to be up-to-date.
assert(zcu.potentially_outdated.swapRemove(depender));
@@ -2398,7 +2400,7 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
},
.func => |func_index| .{ .interned = func_index }, // IES
};
- log.debug("marking dependee po: {}", .{fmtDependee(dependee, zcu)});
+ log.debug("potentially outdated dependee: {}", .{zcu.fmtDependee(dependee)});
var it = ip.dependencyIterator(dependee);
while (it.next()) |po| {
if (zcu.outdated.getPtr(po)) |po_dep_count| {
@@ -2408,17 +2410,17 @@ fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUni
_ = zcu.outdated_ready.swapRemove(po);
}
po_dep_count.* += 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), po_dep_count.* });
+ log.debug("po {} => {} [outdated] po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), po_dep_count.* });
continue;
}
if (zcu.potentially_outdated.getPtr(po)) |n| {
// There is now one more PO dependency.
n.* += 1;
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), n.* });
+ log.debug("po {} => {} po_deps={}", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po), n.* });
continue;
}
try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1);
- log.debug("po dep count: {} = {}", .{ fmtAnalUnit(po, zcu), 1 });
+ log.debug("po {} => {} po_deps=1", .{ zcu.fmtDependee(dependee), zcu.fmtAnalUnit(po) });
// This AnalUnit was not already PO, so we must recursively mark its dependers as also PO.
try zcu.markTransitiveDependersPotentiallyOutdated(po);
}
@@ -2443,7 +2445,7 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
if (zcu.outdated_ready.count() > 0) {
const unit = zcu.outdated_ready.keys()[0];
- log.debug("findOutdatedToAnalyze: trivial {}", .{fmtAnalUnit(unit, zcu)});
+ log.debug("findOutdatedToAnalyze: trivial {}", .{zcu.fmtAnalUnit(unit)});
return unit;
}
@@ -2498,10 +2500,15 @@ pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit {
const nav = zcu.funcInfo(func).owner_nav;
std.io.getStdErr().writer().print("outdated: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
}
+ for (zcu.potentially_outdated.keys(), zcu.potentially_outdated.values()) |o, opod| {
+ const func = o.unwrap().func;
+ const nav = zcu.funcInfo(func).owner_nav;
+ std.io.getStdErr().writer().print("po: func {}, nav {}, name '{}', [p]o deps {}\n", .{ func, nav, ip.getNav(nav).fqn.fmt(ip), opod }) catch {};
+ }
}
log.debug("findOutdatedToAnalyze: heuristic returned '{}' ({d} dependers)", .{
- fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? }), zcu),
+ zcu.fmtAnalUnit(AnalUnit.wrap(.{ .cau = chosen_cau.? })),
chosen_cau_dependers,
});
@@ -2744,7 +2751,7 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
const gpa = zcu.gpa;
unit_refs: {
- const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;
+ const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse break :unit_refs;
var idx = kv.value;
while (idx != std.math.maxInt(u32)) {
@@ -2758,7 +2765,7 @@ pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
}
type_refs: {
- const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse return;
+ const kv = zcu.type_reference_table.fetchSwapRemove(anal_unit) orelse break :type_refs;
var idx = kv.value;
while (idx != std.math.maxInt(u32)) {
@@ -3280,7 +3287,7 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
const unit = kv.key;
try result.putNoClobber(gpa, unit, kv.value);
- log.debug("handle unit '{}'", .{fmtAnalUnit(unit, zcu)});
+ log.debug("handle unit '{}'", .{zcu.fmtAnalUnit(unit)});
if (zcu.reference_table.get(unit)) |first_ref_idx| {
assert(first_ref_idx != std.math.maxInt(u32));
@@ -3289,8 +3296,8 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
const ref = zcu.all_references.items[ref_idx];
if (!result.contains(ref.referenced)) {
log.debug("unit '{}': ref unit '{}'", .{
- fmtAnalUnit(unit, zcu),
- fmtAnalUnit(ref.referenced, zcu),
+ zcu.fmtAnalUnit(unit),
+ zcu.fmtAnalUnit(ref.referenced),
});
try unit_queue.put(gpa, ref.referenced, .{
.referencer = unit,
@@ -3307,7 +3314,7 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ?Resolve
const ref = zcu.all_type_references.items[ref_idx];
if (!checked_types.contains(ref.referenced)) {
log.debug("unit '{}': ref type '{}'", .{
- fmtAnalUnit(unit, zcu),
+ zcu.fmtAnalUnit(unit),
Type.fromInterned(ref.referenced).containerTypeName(ip).fmt(ip),
});
try type_queue.put(gpa, ref.referenced, .{
@@ -3389,10 +3396,10 @@ pub fn cauFileScope(zcu: *Zcu, cau: InternPool.Cau.Index) *File {
return zcu.fileByIndex(file_index);
}
-fn fmtAnalUnit(unit: AnalUnit, zcu: *Zcu) std.fmt.Formatter(formatAnalUnit) {
+pub fn fmtAnalUnit(zcu: *Zcu, unit: AnalUnit) std.fmt.Formatter(formatAnalUnit) {
return .{ .data = .{ .unit = unit, .zcu = zcu } };
}
-fn fmtDependee(d: InternPool.Dependee, zcu: *Zcu) std.fmt.Formatter(formatDependee) {
+pub fn fmtDependee(zcu: *Zcu, d: InternPool.Dependee) std.fmt.Formatter(formatDependee) {
return .{ .data = .{ .dependee = d, .zcu = zcu } };
}