Commit fac120bc3a
Changed files (2)
src/Module.zig
@@ -564,7 +564,23 @@ pub const Decl = struct {
}
};
- pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, void);
+ pub const DepsTable = std.AutoArrayHashMapUnmanaged(Decl.Index, DepType);
+
+ /// Later types take priority; e.g. if a dependent decl has both `normal`
+ /// and `function_body` dependencies on another decl, it will be marked as
+ /// having a `function_body` dependency.
+ pub const DepType = enum {
+ /// The dependent references or uses the dependency's value, so must be
+ /// updated whenever it is changed. However, if the dependency is a
+ /// function and its type is unchanged, the dependent does not need to
+ /// be updated.
+ normal,
+ /// The dependent performs an inline or comptime call to the dependency,
+ /// or is a generic instantiation of it. It must therefore be updated
+ /// whenever the dependency is updated, even if the function type
+ /// remained the same.
+ function_body,
+ };
pub fn clearName(decl: *Decl, gpa: Allocator) void {
gpa.free(mem.sliceTo(decl.name, 0));
@@ -4155,53 +4171,63 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
decl_prog_node.activate();
defer decl_prog_node.end();
- const type_changed = mod.semaDecl(decl_index) catch |err| switch (err) {
- error.AnalysisFail => {
- if (decl.analysis == .in_progress) {
- // If this decl caused the compile error, the analysis field would
- // be changed to indicate it was this Decl's fault. Because this
- // did not happen, we infer here that it was a dependency failure.
- decl.analysis = .dependency_failure;
- }
- return error.AnalysisFail;
- },
- error.NeededSourceLocation => unreachable,
- error.GenericPoison => unreachable,
- else => |e| {
- decl.analysis = .sema_failure_retryable;
- try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
- mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
- mod.gpa,
- decl.srcLoc(),
- "unable to analyze: {s}",
- .{@errorName(e)},
- ));
- return error.AnalysisFail;
- },
+ const type_changed = blk: {
+ if (decl.zir_decl_index == 0 and !mod.declIsRoot(decl_index)) {
+ // Anonymous decl. We don't semantically analyze these.
+ break :blk false; // tv unchanged
+ }
+
+ break :blk mod.semaDecl(decl_index) catch |err| switch (err) {
+ error.AnalysisFail => {
+ if (decl.analysis == .in_progress) {
+ // If this decl caused the compile error, the analysis field would
+ // be changed to indicate it was this Decl's fault. Because this
+ // did not happen, we infer here that it was a dependency failure.
+ decl.analysis = .dependency_failure;
+ }
+ return error.AnalysisFail;
+ },
+ error.NeededSourceLocation => unreachable,
+ error.GenericPoison => unreachable,
+ else => |e| {
+ decl.analysis = .sema_failure_retryable;
+ try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
+ mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
+ mod.gpa,
+ decl.srcLoc(),
+ "unable to analyze: {s}",
+ .{@errorName(e)},
+ ));
+ return error.AnalysisFail;
+ },
+ };
};
if (subsequent_analysis) {
- // We may need to chase the dependants and re-analyze them.
- // However, if the decl is a function, and the type is the same, we do not need to.
- if (type_changed or decl.ty.zigTypeTag() != .Fn) {
- for (decl.dependants.keys()) |dep_index| {
- const dep = mod.declPtr(dep_index);
- switch (dep.analysis) {
- .unreferenced => unreachable,
- .in_progress => continue, // already doing analysis, ok
- .outdated => continue, // already queued for update
-
- .file_failure,
- .dependency_failure,
- .sema_failure,
- .sema_failure_retryable,
- .codegen_failure,
- .codegen_failure_retryable,
- .complete,
- => if (dep.generation != mod.generation) {
- try mod.markOutdatedDecl(dep_index);
- },
- }
+ // Update all dependents which have at least this level of dependency.
+ // If our type remained the same and we're a function, only update
+ // decls which depend on our body; otherwise, update all dependents.
+ const update_level: Decl.DepType = if (!type_changed and decl.ty.zigTypeTag() == .Fn) .function_body else .normal;
+
+ for (decl.dependants.keys(), decl.dependants.values()) |dep_index, dep_type| {
+ if (@enumToInt(dep_type) < @enumToInt(update_level)) continue;
+
+ const dep = mod.declPtr(dep_index);
+ switch (dep.analysis) {
+ .unreferenced => unreachable,
+ .in_progress => continue, // already doing analysis, ok
+ .outdated => continue, // already queued for update
+
+ .file_failure,
+ .dependency_failure,
+ .sema_failure,
+ .sema_failure_retryable,
+ .codegen_failure,
+ .codegen_failure_retryable,
+ .complete,
+ => if (dep.generation != mod.generation) {
+ try mod.markOutdatedDecl(dep_index);
+ },
}
}
}
@@ -4742,25 +4768,37 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
/// Returns the depender's index of the dependee.
pub fn declareDeclDependency(mod: *Module, depender_index: Decl.Index, dependee_index: Decl.Index) !void {
+ return mod.declareDeclDependencyType(depender_index, dependee_index, .normal);
+}
+
+/// Returns the depender's index of the dependee.
+pub fn declareDeclDependencyType(mod: *Module, depender_index: Decl.Index, dependee_index: Decl.Index, dep_type: Decl.DepType) !void {
if (depender_index == dependee_index) return;
const depender = mod.declPtr(depender_index);
const dependee = mod.declPtr(dependee_index);
+ if (depender.dependencies.get(dependee_index)) |cur_type| {
+ if (@enumToInt(cur_type) >= @enumToInt(dep_type)) {
+ // We already have this dependency (or stricter) marked
+ return;
+ }
+ }
+
log.debug("{*} ({s}) depends on {*} ({s})", .{
depender, depender.name, dependee, dependee.name,
});
- try depender.dependencies.ensureUnusedCapacity(mod.gpa, 1);
- try dependee.dependants.ensureUnusedCapacity(mod.gpa, 1);
-
if (dependee.deletion_flag) {
dependee.deletion_flag = false;
assert(mod.deletion_set.swapRemove(dependee_index));
}
- dependee.dependants.putAssumeCapacity(depender_index, {});
- depender.dependencies.putAssumeCapacity(dependee_index, {});
+ try depender.dependencies.ensureUnusedCapacity(mod.gpa, 1);
+ try dependee.dependants.ensureUnusedCapacity(mod.gpa, 1);
+
+ dependee.dependants.putAssumeCapacity(depender_index, dep_type);
+ depender.dependencies.putAssumeCapacity(dependee_index, dep_type);
}
pub const ImportFileResult = struct {
@@ -6342,8 +6380,8 @@ pub fn populateTestFunctions(
try mod.declPtr(test_name_decl_index).finalizeNewArena(&name_decl_arena);
break :n test_name_decl_index;
};
- array_decl.dependencies.putAssumeCapacityNoClobber(test_decl_index, {});
- array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl_index, {});
+ array_decl.dependencies.putAssumeCapacityNoClobber(test_decl_index, .normal);
+ array_decl.dependencies.putAssumeCapacityNoClobber(test_name_decl_index, .normal);
try mod.linkerUpdateDecl(test_name_decl_index);
const field_vals = try arena.create([3]Value);
src/Sema.zig
@@ -6635,6 +6635,8 @@ fn analyzeCall(
sema.code = fn_owner_decl.getFileScope().zir;
defer sema.code = parent_zir;
+ try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body);
+
const parent_inst_map = sema.inst_map;
sema.inst_map = .{};
defer {
@@ -7331,7 +7333,7 @@ fn instantiateGenericCall(
// The generic function Decl is guaranteed to be the first dependency
// of each of its instantiations.
assert(new_decl.dependencies.keys().len == 0);
- try mod.declareDeclDependency(new_decl_index, module_fn.owner_decl);
+ try mod.declareDeclDependencyType(new_decl_index, module_fn.owner_decl, .function_body);
var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
const new_decl_arena_allocator = new_decl_arena.allocator();