aboutsummaryrefslogtreecommitdiff
path: root/src/Module.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-07-03 22:09:30 -0700
committerAndrew Kelley <andrew@ziglang.org>2023-07-18 19:02:05 -0700
commitdb33ee45b7261c9ec62a1087cfc9377bc4e7aa8f (patch)
tree81e1f629c296a8a9c9573879382586dac3784737 /src/Module.zig
parent70c71935c7c9f20353dc2a50b497b752d70d3452 (diff)
downloadzig-db33ee45b7261c9ec62a1087cfc9377bc4e7aa8f.tar.gz
zig-db33ee45b7261c9ec62a1087cfc9377bc4e7aa8f.zip
rework generic function calls
Abridged summary: * Move `Module.Fn` into `InternPool`. * Delete a lot of confusing and problematic `Sema` logic related to generic function calls. This commit removes `Module.Fn` and replaces it with two new `InternPool.Tag` values: * `func_decl` - corresponding to a function declared in the source code. This one contains line/column numbers, zir_body_inst, etc. * `func_instance` - one for each monomorphization of a generic function. Contains a reference to the `func_decl` from whence the instantiation came, along with the `comptime` parameter values (or types in the case of `anytype`) Since `InternPool` provides deduplication on these values, these fields are now deleted from `Module`: * `monomorphed_func_keys` * `monomorphed_funcs` * `align_stack_fns` Instead of these, Sema logic for generic function instantiation now unconditionally evaluates the function prototype expression for every generic callsite. This is technically required in order for type coercions to work. The previous code had some dubious, probably wrong hacks to make things work, such as `hashUncoerced`. I'm not 100% sure how we were able to eliminate that function and still pass all the behavior tests, but I'm pretty sure things were still broken without doing type coercion for every generic function call argument. After the function prototype is evaluated, it produces a deduplicated `func_instance` `InternPool.Index` which can then be used for the generic function call. Some other nice things made by this simplification are the removal of `comptime_args_fn_inst` and `preallocated_new_func` from `Sema`, and the messy logic associated with them. I have not yet been able to measure the perf of this against master branch. On one hand, it reduces memory usage and pointer chasing of the most heavily used `InternPool` Tag - function bodies - but on the other hand, it does evaluate function prototype expressions more than before. We will soon find out.
Diffstat (limited to 'src/Module.zig')
-rw-r--r--src/Module.zig686
1 files changed, 272 insertions, 414 deletions
diff --git a/src/Module.zig b/src/Module.zig
index 96be13e768..a91a24987d 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -101,16 +101,6 @@ tmp_hack_arena: std.heap.ArenaAllocator,
/// This is currently only used for string literals.
memoized_decls: std.AutoHashMapUnmanaged(InternPool.Index, Decl.Index) = .{},
-monomorphed_func_keys: std.ArrayListUnmanaged(InternPool.Index) = .{},
-/// The set of all the generic function instantiations. This is used so that when a generic
-/// function is called twice with the same comptime parameter arguments, both calls dispatch
-/// to the same function.
-monomorphed_funcs: MonomorphedFuncsSet = .{},
-/// Contains the values from `@setAlignStack`. A sparse table is used here
-/// instead of a field of `Fn` because usage of `@setAlignStack` is rare, while
-/// functions are many.
-align_stack_fns: std.AutoHashMapUnmanaged(Fn.Index, SetAlignStack) = .{},
-
/// We optimize memory usage for a compilation with no compile errors by storing the
/// error messages and mapping outside of `Decl`.
/// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator.
@@ -189,7 +179,8 @@ reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {
}) = .{},
panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len,
-panic_func_index: Fn.OptionalIndex = .none,
+/// The panic function body.
+panic_func_index: InternPool.Index = .none,
null_stack_trace: InternPool.Index = .none,
pub const PanicId = enum {
@@ -239,50 +230,6 @@ pub const CImportError = struct {
}
};
-pub const MonomorphedFuncKey = struct { func: Fn.Index, args_index: u32, args_len: u32 };
-
-pub const MonomorphedFuncAdaptedKey = struct { func: Fn.Index, args: []const InternPool.Index };
-
-pub const MonomorphedFuncsSet = std.HashMapUnmanaged(
- MonomorphedFuncKey,
- InternPool.Index,
- MonomorphedFuncsContext,
- std.hash_map.default_max_load_percentage,
-);
-
-pub const MonomorphedFuncsContext = struct {
- mod: *Module,
-
- pub fn eql(_: @This(), a: MonomorphedFuncKey, b: MonomorphedFuncKey) bool {
- return std.meta.eql(a, b);
- }
-
- pub fn hash(ctx: @This(), key: MonomorphedFuncKey) u64 {
- const key_args = ctx.mod.monomorphed_func_keys.items[key.args_index..][0..key.args_len];
- return std.hash.Wyhash.hash(@intFromEnum(key.func), std.mem.sliceAsBytes(key_args));
- }
-};
-
-pub const MonomorphedFuncsAdaptedContext = struct {
- mod: *Module,
-
- pub fn eql(ctx: @This(), adapted_key: MonomorphedFuncAdaptedKey, other_key: MonomorphedFuncKey) bool {
- const other_key_args = ctx.mod.monomorphed_func_keys.items[other_key.args_index..][0..other_key.args_len];
- return adapted_key.func == other_key.func and std.mem.eql(InternPool.Index, adapted_key.args, other_key_args);
- }
-
- pub fn hash(_: @This(), adapted_key: MonomorphedFuncAdaptedKey) u64 {
- return std.hash.Wyhash.hash(@intFromEnum(adapted_key.func), std.mem.sliceAsBytes(adapted_key.args));
- }
-};
-
-pub const SetAlignStack = struct {
- alignment: Alignment,
- /// TODO: This needs to store a non-lazy source location for the case of an inline function
- /// which does `@setAlignStack` (applying it to the caller).
- src: LazySrcLoc,
-};
-
/// A `Module` has zero or one of these depending on whether `-femit-h` is enabled.
pub const GlobalEmitH = struct {
/// Where to put the output.
@@ -625,13 +572,6 @@ pub const Decl = struct {
function_body,
};
- pub fn clearValues(decl: *Decl, mod: *Module) void {
- if (decl.getOwnedFunctionIndex(mod).unwrap()) |func| {
- _ = mod.align_stack_fns.remove(func);
- mod.destroyFunc(func);
- }
- }
-
/// This name is relative to the containing namespace of the decl.
/// The memory is owned by the containing File ZIR.
pub fn getName(decl: Decl, mod: *Module) ?[:0]const u8 {
@@ -816,14 +756,17 @@ pub const Decl = struct {
return mod.typeToUnion(decl.val.toType());
}
- /// If the Decl owns its value and it is a function, return it,
- /// otherwise null.
- pub fn getOwnedFunction(decl: Decl, mod: *Module) ?*Fn {
- return mod.funcPtrUnwrap(decl.getOwnedFunctionIndex(mod));
+ pub fn getOwnedFunction(decl: Decl, mod: *Module) ?InternPool.Key.Func {
+ const i = decl.getOwnedFunctionIndex();
+ if (i == .none) return null;
+ return switch (mod.intern_pool.indexToKey(i)) {
+ .func => |func| func,
+ else => null,
+ };
}
- pub fn getOwnedFunctionIndex(decl: Decl, mod: *Module) Fn.OptionalIndex {
- return if (decl.owns_tv) decl.val.getFunctionIndex(mod) else .none;
+ pub fn getOwnedFunctionIndex(decl: Decl) InternPool.Index {
+ return if (decl.owns_tv) decl.val.toIntern() else .none;
}
/// If the Decl owns its value and it is an extern function, returns it,
@@ -1385,71 +1328,39 @@ pub const ExternFn = struct {
}
};
-/// Some Fn struct memory is owned by the Decl's TypedValue.Managed arena allocator.
-/// Extern functions do not have this data structure; they are represented by `ExternFn`
-/// instead.
-pub const Fn = struct {
- /// The Decl that corresponds to the function itself.
- owner_decl: Decl.Index,
- /// The ZIR instruction that is a function instruction. Use this to find
- /// the body. We store this rather than the body directly so that when ZIR
- /// is regenerated on update(), we can map this to the new corresponding
- /// ZIR instruction.
- zir_body_inst: Zir.Inst.Index,
- /// If this is not null, this function is a generic function instantiation, and
- /// there is a `TypedValue` here for each parameter of the function.
- /// Non-comptime parameters are marked with a `generic_poison` for the value.
- /// Non-anytype parameters are marked with a `generic_poison` for the type.
- /// These never have .generic_poison for the Type
- /// because the Type is needed to pass to `Type.eql` and for inserting comptime arguments
- /// into the inst_map when analyzing the body of a generic function instantiation.
- /// Instead, the is_anytype knowledge is communicated via `isAnytypeParam`.
- comptime_args: ?[*]TypedValue,
-
- /// Precomputed hash for monomorphed_funcs.
- /// This is important because it may be accessed when resizing monomorphed_funcs
- /// while this Fn has already been added to the set, but does not have the
- /// owner_decl, comptime_args, or other fields populated yet.
- /// This field is undefined if comptime_args == null.
- hash: u64,
-
- /// Relative to owner Decl.
- lbrace_line: u32,
- /// Relative to owner Decl.
- rbrace_line: u32,
- lbrace_column: u16,
- rbrace_column: u16,
-
- /// When a generic function is instantiated, this value is inherited from the
- /// active Sema context. Importantly, this value is also updated when an existing
- /// generic function instantiation is found and called.
- branch_quota: u32,
-
- /// If this is not none, this function is a generic function instantiation, and
- /// this is the generic function decl from which the instance was derived.
- /// This information is redundant with a combination of checking if comptime_args is
- /// not null and looking at the first decl dependency of owner_decl. This redundant
- /// information is useful for three reasons:
- /// 1. Improved perf of monomorphed_funcs when checking the eql() function because it
- /// can do two fewer pointer chases by grabbing the info from this field directly
- /// instead of accessing the decl and then the dependencies set.
- /// 2. While a generic function instantiation is being initialized, we need hash()
- /// and eql() to work before the initialization is complete. Completing the
- /// insertion into the decl dependency set has more fallible operations than simply
- /// setting this field.
- /// 3. I forgot what the third thing was while typing up the other two.
- generic_owner_decl: Decl.OptionalIndex,
-
- state: Analysis,
- is_cold: bool = false,
- is_noinline: bool,
- calls_or_awaits_errorable_fn: bool = false,
+/// This struct is used to keep track of any dependencies related to functions instances
+/// that return inferred error sets. Note that a function may be associated to
+/// multiple different error sets, for example an inferred error set which
+/// this function returns, but also any inferred error sets of called inline
+/// or comptime functions.
+pub const InferredErrorSet = struct {
+ /// The function from which this error set originates.
+ func: InternPool.Index,
+
+ /// All currently known errors that this error set contains. This includes
+ /// direct additions via `return error.Foo;`, and possibly also errors that
+ /// are returned from any dependent functions. When the inferred error set is
+ /// fully resolved, this map contains all the errors that the function might return.
+ errors: NameMap = .{},
+
+ /// Other inferred error sets which this inferred error set should include.
+ inferred_error_sets: std.AutoArrayHashMapUnmanaged(InferredErrorSet.Index, void) = .{},
+
+ /// Whether the function returned anyerror. This is true if either of
+ /// the dependent functions returns anyerror.
+ is_anyerror: bool = false,
+
+ /// Whether this error set is already fully resolved. If true, resolving
+ /// can skip resolving any dependents of this inferred error set.
+ is_resolved: bool = false,
+
+ pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
pub const Index = enum(u32) {
_,
- pub fn toOptional(i: Index) OptionalIndex {
- return @as(OptionalIndex, @enumFromInt(@intFromEnum(i)));
+ pub fn toOptional(i: InferredErrorSet.Index) InferredErrorSet.OptionalIndex {
+ return @as(InferredErrorSet.OptionalIndex, @enumFromInt(@intFromEnum(i)));
}
};
@@ -1457,159 +1368,37 @@ pub const Fn = struct {
none = std.math.maxInt(u32),
_,
- pub fn init(oi: ?Index) OptionalIndex {
- return @as(OptionalIndex, @enumFromInt(@intFromEnum(oi orelse return .none)));
+ pub fn init(oi: ?InferredErrorSet.Index) InferredErrorSet.OptionalIndex {
+ return @as(InferredErrorSet.OptionalIndex, @enumFromInt(@intFromEnum(oi orelse return .none)));
}
- pub fn unwrap(oi: OptionalIndex) ?Index {
+ pub fn unwrap(oi: InferredErrorSet.OptionalIndex) ?InferredErrorSet.Index {
if (oi == .none) return null;
- return @as(Index, @enumFromInt(@intFromEnum(oi)));
+ return @as(InferredErrorSet.Index, @enumFromInt(@intFromEnum(oi)));
}
};
- pub const Analysis = enum {
- /// This function has not yet undergone analysis, because we have not
- /// seen a potential runtime call. It may be analyzed in future.
- none,
- /// Analysis for this function has been queued, but not yet completed.
- queued,
- /// This function intentionally only has ZIR generated because it is marked
- /// inline, which means no runtime version of the function will be generated.
- inline_only,
- in_progress,
- /// There will be a corresponding ErrorMsg in Module.failed_decls
- sema_failure,
- /// This Fn might be OK but it depends on another Decl which did not
- /// successfully complete semantic analysis.
- dependency_failure,
- success,
- };
-
- /// This struct is used to keep track of any dependencies related to functions instances
- /// that return inferred error sets. Note that a function may be associated to
- /// multiple different error sets, for example an inferred error set which
- /// this function returns, but also any inferred error sets of called inline
- /// or comptime functions.
- pub const InferredErrorSet = struct {
- /// The function from which this error set originates.
- func: Fn.Index,
-
- /// All currently known errors that this error set contains. This includes
- /// direct additions via `return error.Foo;`, and possibly also errors that
- /// are returned from any dependent functions. When the inferred error set is
- /// fully resolved, this map contains all the errors that the function might return.
- errors: NameMap = .{},
-
- /// Other inferred error sets which this inferred error set should include.
- inferred_error_sets: std.AutoArrayHashMapUnmanaged(InferredErrorSet.Index, void) = .{},
-
- /// Whether the function returned anyerror. This is true if either of
- /// the dependent functions returns anyerror.
- is_anyerror: bool = false,
-
- /// Whether this error set is already fully resolved. If true, resolving
- /// can skip resolving any dependents of this inferred error set.
- is_resolved: bool = false,
-
- pub const NameMap = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void);
-
- pub const Index = enum(u32) {
- _,
-
- pub fn toOptional(i: InferredErrorSet.Index) InferredErrorSet.OptionalIndex {
- return @as(InferredErrorSet.OptionalIndex, @enumFromInt(@intFromEnum(i)));
- }
- };
-
- pub const OptionalIndex = enum(u32) {
- none = std.math.maxInt(u32),
- _,
-
- pub fn init(oi: ?InferredErrorSet.Index) InferredErrorSet.OptionalIndex {
- return @as(InferredErrorSet.OptionalIndex, @enumFromInt(@intFromEnum(oi orelse return .none)));
- }
-
- pub fn unwrap(oi: InferredErrorSet.OptionalIndex) ?InferredErrorSet.Index {
- if (oi == .none) return null;
- return @as(InferredErrorSet.Index, @enumFromInt(@intFromEnum(oi)));
- }
- };
-
- pub fn addErrorSet(
- self: *InferredErrorSet,
- err_set_ty: Type,
- ip: *InternPool,
- gpa: Allocator,
- ) !void {
- switch (err_set_ty.toIntern()) {
- .anyerror_type => {
- self.is_anyerror = true;
+ pub fn addErrorSet(
+ self: *InferredErrorSet,
+ err_set_ty: Type,
+ ip: *InternPool,
+ gpa: Allocator,
+ ) !void {
+ switch (err_set_ty.toIntern()) {
+ .anyerror_type => {
+ self.is_anyerror = true;
+ },
+ else => switch (ip.indexToKey(err_set_ty.toIntern())) {
+ .error_set_type => |error_set_type| {
+ for (error_set_type.names) |name| {
+ try self.errors.put(gpa, name, {});
+ }
},
- else => switch (ip.indexToKey(err_set_ty.toIntern())) {
- .error_set_type => |error_set_type| {
- for (error_set_type.names) |name| {
- try self.errors.put(gpa, name, {});
- }
- },
- .inferred_error_set_type => |ies_index| {
- try self.inferred_error_sets.put(gpa, ies_index, {});
- },
- else => unreachable,
+ .inferred_error_set_type => |ies_index| {
+ try self.inferred_error_sets.put(gpa, ies_index, {});
},
- }
- }
- };
-
- pub fn isAnytypeParam(func: Fn, mod: *Module, index: u32) bool {
- const file = mod.declPtr(func.owner_decl).getFileScope(mod);
-
- const tags = file.zir.instructions.items(.tag);
-
- const param_body = file.zir.getParamBody(func.zir_body_inst);
- const param = param_body[index];
-
- return switch (tags[param]) {
- .param, .param_comptime => false,
- .param_anytype, .param_anytype_comptime => true,
- else => unreachable,
- };
- }
-
- pub fn getParamName(func: Fn, mod: *Module, index: u32) [:0]const u8 {
- const file = mod.declPtr(func.owner_decl).getFileScope(mod);
-
- const tags = file.zir.instructions.items(.tag);
- const data = file.zir.instructions.items(.data);
-
- const param_body = file.zir.getParamBody(func.zir_body_inst);
- const param = param_body[index];
-
- return switch (tags[param]) {
- .param, .param_comptime => blk: {
- const extra = file.zir.extraData(Zir.Inst.Param, data[param].pl_tok.payload_index);
- break :blk file.zir.nullTerminatedString(extra.data.name);
- },
- .param_anytype, .param_anytype_comptime => blk: {
- const param_data = data[param].str_tok;
- break :blk param_data.get(file.zir);
- },
- else => unreachable,
- };
- }
-
- pub fn hasInferredErrorSet(func: Fn, mod: *Module) bool {
- const owner_decl = mod.declPtr(func.owner_decl);
- const zir = owner_decl.getFileScope(mod).zir;
- const zir_tags = zir.instructions.items(.tag);
- switch (zir_tags[func.zir_body_inst]) {
- .func => return false,
- .func_inferred => return true,
- .func_fancy => {
- const inst_data = zir.instructions.items(.data)[func.zir_body_inst].pl_node;
- const extra = zir.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
- return extra.data.bits.is_inferred_error;
+ else => unreachable,
},
- else => unreachable,
}
}
};
@@ -2468,6 +2257,22 @@ pub const SrcLoc = struct {
}
} else unreachable;
},
+ .call_arg => |call_arg| {
+ const tree = try src_loc.file_scope.getTree(gpa);
+ const node = src_loc.declRelativeToNodeIndex(call_arg.call_node_offset);
+ var buf: [1]Ast.Node.Index = undefined;
+ const call_full = tree.fullCall(&buf, node).?;
+ const src_node = call_full.ast.params[call_arg.arg_index];
+ return nodeToSpan(tree, src_node);
+ },
+ .fn_proto_param => |fn_proto_param| {
+ const tree = try src_loc.file_scope.getTree(gpa);
+ const node = src_loc.declRelativeToNodeIndex(fn_proto_param.fn_proto_node_offset);
+ var buf: [1]Ast.Node.Index = undefined;
+ const fn_proto_full = tree.fullFnProto(&buf, node).?;
+ const src_node = fn_proto_full.ast.params[fn_proto_param.param_index];
+ return nodeToSpan(tree, src_node);
+ },
.node_offset_bin_lhs => |node_off| {
const tree = try src_loc.file_scope.getTree(gpa);
const node = src_loc.declRelativeToNodeIndex(node_off);
@@ -3146,6 +2951,20 @@ pub const LazySrcLoc = union(enum) {
/// Next, navigate to the corresponding capture.
/// The Decl is determined contextually.
for_capture_from_input: i32,
+ /// The source location points to the argument node of a function call.
+ /// The Decl is determined contextually.
+ call_arg: struct {
+ /// Points to the function call AST node.
+ call_node_offset: i32,
+ /// The index of the argument the source location points to.
+ arg_index: u32,
+ },
+ fn_proto_param: struct {
+ /// Points to the function prototype AST node.
+ fn_proto_node_offset: i32,
+ /// The index of the parameter the source location points to.
+ param_index: u32,
+ },
pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
@@ -3235,6 +3054,8 @@ pub const LazySrcLoc = union(enum) {
.node_offset_store_operand,
.for_input,
.for_capture_from_input,
+ .call_arg,
+ .fn_proto_param,
=> .{
.file_scope = decl.getFileScope(mod),
.parent_decl_node = decl.src_node,
@@ -3373,8 +3194,6 @@ pub fn deinit(mod: *Module) void {
mod.global_error_set.deinit(gpa);
mod.test_functions.deinit(gpa);
- mod.align_stack_fns.deinit(gpa);
- mod.monomorphed_funcs.deinit(gpa);
mod.decls_free_list.deinit(gpa);
mod.allocated_decls.deinit(gpa);
@@ -3407,7 +3226,6 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
}
}
if (decl.src_scope) |scope| scope.decRef(gpa);
- decl.clearValues(mod);
decl.dependants.deinit(gpa);
decl.dependencies.deinit(gpa);
decl.* = undefined;
@@ -3439,11 +3257,7 @@ pub fn structPtr(mod: *Module, index: Struct.Index) *Struct {
return mod.intern_pool.structPtr(index);
}
-pub fn funcPtr(mod: *Module, index: Fn.Index) *Fn {
- return mod.intern_pool.funcPtr(index);
-}
-
-pub fn inferredErrorSetPtr(mod: *Module, index: Fn.InferredErrorSet.Index) *Fn.InferredErrorSet {
+pub fn inferredErrorSetPtr(mod: *Module, index: InferredErrorSet.Index) *InferredErrorSet {
return mod.intern_pool.inferredErrorSetPtr(index);
}
@@ -3457,10 +3271,6 @@ pub fn structPtrUnwrap(mod: *Module, index: Struct.OptionalIndex) ?*Struct {
return mod.structPtr(index.unwrap() orelse return null);
}
-pub fn funcPtrUnwrap(mod: *Module, index: Fn.OptionalIndex) ?*Fn {
- return mod.funcPtr(index.unwrap() orelse return null);
-}
-
/// Returns true if and only if the Decl is the top level struct associated with a File.
pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
const decl = mod.declPtr(decl_index);
@@ -3881,6 +3691,8 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
// to re-generate ZIR for the File.
try file.outdated_decls.append(gpa, root_decl);
+ const ip = &mod.intern_pool;
+
while (decl_stack.popOrNull()) |decl_index| {
const decl = mod.declPtr(decl_index);
// Anonymous decls and the root decl have this set to 0. We still need
@@ -3918,7 +3730,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
}
if (decl.getOwnedFunction(mod)) |func| {
- func.zir_body_inst = inst_map.get(func.zir_body_inst) orelse {
+ func.zirBodyInst(ip).* = inst_map.get(func.zir_body_inst) orelse {
try file.deleted_decls.append(gpa, decl_index);
continue;
};
@@ -4101,11 +3913,6 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
// prior to re-analysis.
try mod.deleteDeclExports(decl_index);
- // Similarly, `@setAlignStack` invocations will be re-discovered.
- if (decl.getOwnedFunctionIndex(mod).unwrap()) |func| {
- _ = mod.align_stack_fns.remove(func);
- }
-
// Dependencies will be re-discovered, so we remove them here prior to re-analysis.
for (decl.dependencies.keys()) |dep_index| {
const dep = mod.declPtr(dep_index);
@@ -4189,11 +3996,12 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
}
}
-pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: Fn.Index) SemaError!void {
+pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: InternPool.Index) SemaError!void {
const tracy = trace(@src());
defer tracy.end();
- const func = mod.funcPtr(func_index);
+ const ip = &mod.intern_pool;
+ const func = mod.funcInfo(func_index);
const decl_index = func.owner_decl;
const decl = mod.declPtr(decl_index);
@@ -4211,7 +4019,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: Fn.Index) SemaError!void
=> return error.AnalysisFail,
.complete, .codegen_failure_retryable => {
- switch (func.state) {
+ switch (func.analysis(ip).state) {
.sema_failure, .dependency_failure => return error.AnalysisFail,
.none, .queued => {},
.in_progress => unreachable,
@@ -4227,11 +4035,11 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: Fn.Index) SemaError!void
var air = mod.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) {
error.AnalysisFail => {
- if (func.state == .in_progress) {
+ if (func.analysis(ip).state == .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.
- func.state = .dependency_failure;
+ func.analysis(ip).state = .dependency_failure;
}
return error.AnalysisFail;
},
@@ -4251,14 +4059,14 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: Fn.Index) SemaError!void
if (no_bin_file and !dump_air and !dump_llvm_ir) return;
- var liveness = try Liveness.analyze(gpa, air, &mod.intern_pool);
+ var liveness = try Liveness.analyze(gpa, air, ip);
defer liveness.deinit(gpa);
if (dump_air) {
const fqn = try decl.getFullyQualifiedName(mod);
- std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(&mod.intern_pool)});
+ std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)});
@import("print_air.zig").dump(mod, air, liveness);
- std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(&mod.intern_pool)});
+ std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)});
}
if (std.debug.runtime_safety) {
@@ -4266,7 +4074,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: Fn.Index) SemaError!void
.gpa = gpa,
.air = air,
.liveness = liveness,
- .intern_pool = &mod.intern_pool,
+ .intern_pool = ip,
};
defer verify.deinit();
@@ -4321,8 +4129,9 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func_index: Fn.Index) SemaError!void
/// analyzed, and for ensuring it can exist at runtime (see
/// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body
/// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`.
-pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: Fn.Index) !void {
- const func = mod.funcPtr(func_index);
+pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) !void {
+ const ip = &mod.intern_pool;
+ const func = mod.funcInfo(func_index);
const decl_index = func.owner_decl;
const decl = mod.declPtr(decl_index);
@@ -4348,7 +4157,7 @@ pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: Fn.Index) !void {
assert(decl.has_tv);
- switch (func.state) {
+ switch (func.analysis(ip).state) {
.none => {},
.queued => return,
// As above, we don't need to forward errors here.
@@ -4366,7 +4175,7 @@ pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: Fn.Index) !void {
// since the last update
try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
}
- func.state = .queued;
+ func.analysis(ip).state = .queued;
}
pub fn updateEmbedFile(mod: *Module, embed_file: *EmbedFile) SemaError!void {
@@ -4490,10 +4299,8 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
.code = file.zir,
.owner_decl = new_decl,
.owner_decl_index = new_decl_index,
- .func = null,
.func_index = .none,
.fn_ret_ty = Type.void,
- .owner_func = null,
.owner_func_index = .none,
.comptime_mutable_decls = &comptime_mutable_decls,
};
@@ -4573,10 +4380,8 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
.code = zir,
.owner_decl = decl,
.owner_decl_index = decl_index,
- .func = null,
.func_index = .none,
.fn_ret_ty = Type.void,
- .owner_func = null,
.owner_func_index = .none,
.comptime_mutable_decls = &comptime_mutable_decls,
};
@@ -4658,48 +4463,49 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
return true;
}
- if (mod.intern_pool.indexToFunc(decl_tv.val.toIntern()).unwrap()) |func_index| {
- const func = mod.funcPtr(func_index);
- const owns_tv = func.owner_decl == decl_index;
- if (owns_tv) {
- var prev_type_has_bits = false;
- var prev_is_inline = false;
- var type_changed = true;
-
- if (decl.has_tv) {
- prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits(mod);
- type_changed = !decl.ty.eql(decl_tv.ty, mod);
- if (decl.getOwnedFunction(mod)) |prev_func| {
- prev_is_inline = prev_func.state == .inline_only;
+ const ip = &mod.intern_pool;
+ switch (ip.indexToKey(decl_tv.val.toIntern())) {
+ .func => |func| {
+ const owns_tv = func.owner_decl == decl_index;
+ if (owns_tv) {
+ var prev_type_has_bits = false;
+ var prev_is_inline = false;
+ var type_changed = true;
+
+ if (decl.has_tv) {
+ prev_type_has_bits = decl.ty.isFnOrHasRuntimeBits(mod);
+ type_changed = !decl.ty.eql(decl_tv.ty, mod);
+ if (decl.getOwnedFunction(mod)) |prev_func| {
+ prev_is_inline = prev_func.analysis(ip).state == .inline_only;
+ }
}
- }
- decl.clearValues(mod);
-
- decl.ty = decl_tv.ty;
- decl.val = (try decl_tv.val.intern(decl_tv.ty, mod)).toValue();
- // linksection, align, and addrspace were already set by Sema
- decl.has_tv = true;
- decl.owns_tv = owns_tv;
- decl.analysis = .complete;
- decl.generation = mod.generation;
-
- const is_inline = decl.ty.fnCallingConvention(mod) == .Inline;
- if (decl.is_exported) {
- const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) };
- if (is_inline) {
- return sema.fail(&block_scope, export_src, "export of inline function", .{});
+
+ decl.ty = decl_tv.ty;
+ decl.val = (try decl_tv.val.intern(decl_tv.ty, mod)).toValue();
+ // linksection, align, and addrspace were already set by Sema
+ decl.has_tv = true;
+ decl.owns_tv = owns_tv;
+ decl.analysis = .complete;
+ decl.generation = mod.generation;
+
+ const is_inline = decl.ty.fnCallingConvention(mod) == .Inline;
+ if (decl.is_exported) {
+ const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) };
+ if (is_inline) {
+ return sema.fail(&block_scope, export_src, "export of inline function", .{});
+ }
+ // The scope needs to have the decl in it.
+ try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index);
}
- // The scope needs to have the decl in it.
- try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index);
+ return type_changed or is_inline != prev_is_inline;
}
- return type_changed or is_inline != prev_is_inline;
- }
+ },
+ else => {},
}
var type_changed = true;
if (decl.has_tv) {
type_changed = !decl.ty.eql(decl_tv.ty, mod);
}
- decl.clearValues(mod);
decl.owns_tv = false;
var queue_linker_work = false;
@@ -4707,7 +4513,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
switch (decl_tv.val.toIntern()) {
.generic_poison => unreachable,
.unreachable_value => unreachable,
- else => switch (mod.intern_pool.indexToKey(decl_tv.val.toIntern())) {
+ else => switch (ip.indexToKey(decl_tv.val.toIntern())) {
.variable => |variable| if (variable.decl == decl_index) {
decl.owns_tv = true;
queue_linker_work = true;
@@ -4743,11 +4549,11 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
} else if (bytes.len == 0) {
return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
}
- const section = try mod.intern_pool.getOrPutString(gpa, bytes);
+ const section = try ip.getOrPutString(gpa, bytes);
break :blk section.toOptional();
};
decl.@"addrspace" = blk: {
- const addrspace_ctx: Sema.AddressSpaceContext = switch (mod.intern_pool.indexToKey(decl_tv.val.toIntern())) {
+ const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_tv.val.toIntern())) {
.variable => .variable,
.extern_func, .func => .function,
else => .constant,
@@ -5309,7 +5115,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
decl.has_align = has_align;
decl.has_linksection_or_addrspace = has_linksection_or_addrspace;
decl.zir_decl_index = @as(u32, @intCast(decl_sub_index));
- if (decl.getOwnedFunctionIndex(mod) != .none) {
+ if (decl.getOwnedFunctionIndex() != .none) {
switch (comp.bin_file.tag) {
.coff, .elf, .macho, .plan9 => {
// TODO Look into detecting when this would be unnecessary by storing enough state
@@ -5386,7 +5192,6 @@ pub fn clearDecl(
try namespace.deleteAllDecls(mod, outdated_decls);
}
}
- decl.clearValues(mod);
if (decl.deletion_flag) {
decl.deletion_flag = false;
@@ -5497,19 +5302,26 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void
export_owners.deinit(mod.gpa);
}
-pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaError!Air {
+pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocator) SemaError!Air {
const tracy = trace(@src());
defer tracy.end();
const gpa = mod.gpa;
- const func = mod.funcPtr(func_index);
+ const ip = &mod.intern_pool;
+ const func = mod.funcInfo(func_index);
const decl_index = func.owner_decl;
const decl = mod.declPtr(decl_index);
var comptime_mutable_decls = std.ArrayList(Decl.Index).init(gpa);
defer comptime_mutable_decls.deinit();
+ // In the case of a generic function instance, this is the type of the
+ // instance, which has comptime parameters elided. In other words, it is
+ // the runtime-known parameters only, not to be confused with the
+ // generic_owner function type, which potentially has more parameters,
+ // including comptime parameters.
const fn_ty = decl.ty;
+ const fn_ty_info = mod.typeToFunc(fn_ty).?;
var sema: Sema = .{
.mod = mod,
@@ -5518,18 +5330,16 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
.code = decl.getFileScope(mod).zir,
.owner_decl = decl,
.owner_decl_index = decl_index,
- .func = func,
- .func_index = func_index.toOptional(),
- .fn_ret_ty = mod.typeToFunc(fn_ty).?.return_type.toType(),
- .owner_func = func,
- .owner_func_index = func_index.toOptional(),
- .branch_quota = @max(func.branch_quota, Sema.default_branch_quota),
+ .func_index = func_index,
+ .fn_ret_ty = fn_ty_info.return_type.toType(),
+ .owner_func_index = func_index,
+ .branch_quota = @max(func.branchQuota(ip).*, Sema.default_branch_quota),
.comptime_mutable_decls = &comptime_mutable_decls,
};
defer sema.deinit();
// reset in case calls to errorable functions are removed.
- func.calls_or_awaits_errorable_fn = false;
+ func.analysis(ip).calls_or_awaits_errorable_fn = false;
// First few indexes of extra are reserved and set at the end.
const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len;
@@ -5551,8 +5361,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
};
defer inner_block.instructions.deinit(gpa);
- const fn_info = sema.code.getFnInfo(func.zir_body_inst);
- const zir_tags = sema.code.instructions.items(.tag);
+ const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).*);
// Here we are performing "runtime semantic analysis" for a function body, which means
// we must map the parameter ZIR instructions to `arg` AIR instructions.
@@ -5560,35 +5369,36 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
// This could be a generic function instantiation, however, in which case we need to
// map the comptime parameters to constant values and only emit arg AIR instructions
// for the runtime ones.
- const runtime_params_len = @as(u32, @intCast(mod.typeToFunc(fn_ty).?.param_types.len));
+ const runtime_params_len = fn_ty_info.param_types.len;
try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len);
- try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len * 2); // * 2 for the `addType`
+ try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len);
try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
- var runtime_param_index: usize = 0;
- var total_param_index: usize = 0;
- for (fn_info.param_body) |inst| {
- switch (zir_tags[inst]) {
- .param, .param_comptime, .param_anytype, .param_anytype_comptime => {},
- else => continue,
+ // In the case of a generic function instance, pre-populate all the comptime args.
+ if (func.comptime_args.len != 0) {
+ for (
+ fn_info.param_body[0..func.comptime_args.len],
+ func.comptime_args.get(ip),
+ ) |inst, comptime_arg| {
+ if (comptime_arg == .none) continue;
+ sema.inst_map.putAssumeCapacityNoClobber(inst, Air.internedToRef(comptime_arg));
}
- const param_ty = if (func.comptime_args) |comptime_args| t: {
- const arg_tv = comptime_args[total_param_index];
-
- const arg_val = if (!arg_tv.val.isGenericPoison())
- arg_tv.val
- else if (try arg_tv.ty.onePossibleValue(mod)) |opv|
- opv
- else
- break :t arg_tv.ty;
-
- const arg = try sema.addConstant(arg_val);
- sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
- total_param_index += 1;
- continue;
- } else mod.typeToFunc(fn_ty).?.param_types[runtime_param_index].toType();
+ }
+
+ const src_params_len = if (func.comptime_args.len != 0)
+ func.comptime_args.len
+ else
+ runtime_params_len;
+
+ var runtime_param_index: usize = 0;
+ for (fn_info.param_body[0..src_params_len], 0..) |inst, src_param_index| {
+ const gop = sema.inst_map.getOrPutAssumeCapacity(inst);
+ if (gop.found_existing) continue; // provided above by comptime arg
- const opt_opv = sema.typeHasOnePossibleValue(param_ty) catch |err| switch (err) {
+ const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
+ runtime_param_index += 1;
+
+ const opt_opv = sema.typeHasOnePossibleValue(param_ty.toType()) catch |err| switch (err) {
error.NeededSourceLocation => unreachable,
error.GenericPoison => unreachable,
error.ComptimeReturn => unreachable,
@@ -5596,28 +5406,22 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
else => |e| return e,
};
if (opt_opv) |opv| {
- const arg = try sema.addConstant(opv);
- sema.inst_map.putAssumeCapacityNoClobber(inst, arg);
- total_param_index += 1;
- runtime_param_index += 1;
+ gop.value_ptr.* = Air.internedToRef(opv.toIntern());
continue;
}
- const air_ty = try sema.addType(param_ty);
- const arg_index = @as(u32, @intCast(sema.air_instructions.len));
+ const arg_index: u32 = @intCast(sema.air_instructions.len);
+ gop.value_ptr.* = Air.indexToRef(arg_index);
inner_block.instructions.appendAssumeCapacity(arg_index);
sema.air_instructions.appendAssumeCapacity(.{
.tag = .arg,
.data = .{ .arg = .{
- .ty = air_ty,
- .src_index = @as(u32, @intCast(total_param_index)),
+ .ty = Air.internedToRef(param_ty),
+ .src_index = @intCast(src_param_index),
} },
});
- sema.inst_map.putAssumeCapacityNoClobber(inst, Air.indexToRef(arg_index));
- total_param_index += 1;
- runtime_param_index += 1;
}
- func.state = .in_progress;
+ func.analysis(ip).state = .in_progress;
const last_arg_index = inner_block.instructions.items.len;
@@ -5648,7 +5452,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
}
// If we don't get an error return trace from a caller, create our own.
- if (func.calls_or_awaits_errorable_fn and
+ if (func.analysis(ip).calls_or_awaits_errorable_fn and
mod.comp.bin_file.options.error_return_tracing and
!sema.fn_ret_ty.isError(mod))
{
@@ -5677,7 +5481,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
sema.air_extra.appendSliceAssumeCapacity(inner_block.instructions.items);
sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;
- func.state = .success;
+ func.analysis(ip).state = .success;
// Finally we must resolve the return type and parameter types so that backends
// have full access to type information.
@@ -5716,7 +5520,7 @@ pub fn analyzeFnBody(mod: *Module, func_index: Fn.Index, arena: Allocator) SemaE
};
}
- return Air{
+ return .{
.instructions = sema.air_instructions.toOwnedSlice(),
.extra = try sema.air_extra.toOwnedSlice(gpa),
};
@@ -5731,9 +5535,6 @@ fn markOutdatedDecl(mod: *Module, decl_index: Decl.Index) !void {
if (mod.cimport_errors.fetchSwapRemove(decl_index)) |kv| {
for (kv.value) |err| err.deinit(mod.gpa);
}
- if (decl.getOwnedFunctionIndex(mod).unwrap()) |func| {
- _ = mod.align_stack_fns.remove(func);
- }
if (mod.emit_h) |emit_h| {
if (emit_h.failed_decls.fetchSwapRemove(decl_index)) |kv| {
kv.value.destroy(mod.gpa);
@@ -5777,14 +5578,6 @@ pub fn destroyUnion(mod: *Module, index: Union.Index) void {
return mod.intern_pool.destroyUnion(mod.gpa, index);
}
-pub fn createFunc(mod: *Module, initialization: Fn) Allocator.Error!Fn.Index {
- return mod.intern_pool.createFunc(mod.gpa, initialization);
-}
-
-pub fn destroyFunc(mod: *Module, index: Fn.Index) void {
- return mod.intern_pool.destroyFunc(mod.gpa, index);
-}
-
pub fn allocateNewDecl(
mod: *Module,
namespace: Namespace.Index,
@@ -6578,7 +6371,6 @@ pub fn populateTestFunctions(
// Since we are replacing the Decl's value we must perform cleanup on the
// previous value.
- decl.clearValues(mod);
decl.ty = new_ty;
decl.val = new_val;
decl.has_tv = true;
@@ -6657,7 +6449,7 @@ pub fn markReferencedDeclsAlive(mod: *Module, val: Value) Allocator.Error!void {
switch (mod.intern_pool.indexToKey(val.toIntern())) {
.variable => |variable| try mod.markDeclIndexAlive(variable.decl),
.extern_func => |extern_func| try mod.markDeclIndexAlive(extern_func.decl),
- .func => |func| try mod.markDeclIndexAlive(mod.funcPtr(func.index).owner_decl),
+ .func => |func| try mod.markDeclIndexAlive(func.owner_decl),
.error_union => |error_union| switch (error_union.val) {
.err_name => {},
.payload => |payload| try mod.markReferencedDeclsAlive(payload.toValue()),
@@ -6851,8 +6643,8 @@ pub fn adjustPtrTypeChild(mod: *Module, ptr_ty: Type, new_child: Type) Allocator
return mod.ptrType(info);
}
-pub fn funcType(mod: *Module, info: InternPool.Key.FuncType) Allocator.Error!Type {
- return (try intern(mod, .{ .func_type = info })).toType();
+pub fn funcType(mod: *Module, key: InternPool.GetFuncTypeKey) Allocator.Error!Type {
+ return (try mod.intern_pool.getFuncType(mod.gpa, key)).toType();
}
/// Use this for `anyframe->T` only.
@@ -7231,16 +7023,28 @@ pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType {
return mod.intern_pool.indexToFuncType(ty.toIntern());
}
-pub fn typeToInferredErrorSet(mod: *Module, ty: Type) ?*Fn.InferredErrorSet {
+pub fn typeToInferredErrorSet(mod: *Module, ty: Type) ?*InferredErrorSet {
const index = typeToInferredErrorSetIndex(mod, ty).unwrap() orelse return null;
return mod.inferredErrorSetPtr(index);
}
-pub fn typeToInferredErrorSetIndex(mod: *Module, ty: Type) Fn.InferredErrorSet.OptionalIndex {
+pub fn typeToInferredErrorSetIndex(mod: *Module, ty: Type) InferredErrorSet.OptionalIndex {
if (ty.ip_index == .none) return .none;
return mod.intern_pool.indexToInferredErrorSetType(ty.toIntern());
}
+pub fn funcOwnerDeclPtr(mod: *Module, func_index: InternPool.Index) *Decl {
+ return mod.declPtr(mod.funcOwnerDeclIndex(func_index));
+}
+
+pub fn funcOwnerDeclIndex(mod: *Module, func_index: InternPool.Index) Decl.Index {
+ return mod.funcInfo(func_index).owner_decl;
+}
+
+pub fn funcInfo(mod: *Module, func_index: InternPool.Index) InternPool.Key.Func {
+ return mod.intern_pool.indexToKey(func_index).func;
+}
+
pub fn fieldSrcLoc(mod: *Module, owner_decl_index: Decl.Index, query: FieldSrcQuery) SrcLoc {
@setCold(true);
const owner_decl = mod.declPtr(owner_decl_index);
@@ -7265,3 +7069,57 @@ pub fn fieldSrcLoc(mod: *Module, owner_decl_index: Decl.Index, query: FieldSrcQu
pub fn toEnum(mod: *Module, comptime E: type, val: Value) E {
return mod.intern_pool.toEnum(E, val.toIntern());
}
+
+pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool {
+ const file = mod.declPtr(func.owner_decl).getFileScope(mod);
+
+ const tags = file.zir.instructions.items(.tag);
+
+ const param_body = file.zir.getParamBody(func.zir_body_inst);
+ const param = param_body[index];
+
+ return switch (tags[param]) {
+ .param, .param_comptime => false,
+ .param_anytype, .param_anytype_comptime => true,
+ else => unreachable,
+ };
+}
+
+pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]const u8 {
+ const func = mod.funcInfo(func_index);
+ const file = mod.declPtr(func.owner_decl).getFileScope(mod);
+
+ const tags = file.zir.instructions.items(.tag);
+ const data = file.zir.instructions.items(.data);
+
+ const param_body = file.zir.getParamBody(func.zir_body_inst);
+ const param = param_body[index];
+
+ return switch (tags[param]) {
+ .param, .param_comptime => blk: {
+ const extra = file.zir.extraData(Zir.Inst.Param, data[param].pl_tok.payload_index);
+ break :blk file.zir.nullTerminatedString(extra.data.name);
+ },
+ .param_anytype, .param_anytype_comptime => blk: {
+ const param_data = data[param].str_tok;
+ break :blk param_data.get(file.zir);
+ },
+ else => unreachable,
+ };
+}
+
+pub fn hasInferredErrorSet(mod: *Module, func: InternPool.Key.Func) bool {
+ const owner_decl = mod.declPtr(func.owner_decl);
+ const zir = owner_decl.getFileScope(mod).zir;
+ const zir_tags = zir.instructions.items(.tag);
+ switch (zir_tags[func.zir_body_inst]) {
+ .func => return false,
+ .func_inferred => return true,
+ .func_fancy => {
+ const inst_data = zir.instructions.items(.data)[func.zir_body_inst].pl_node;
+ const extra = zir.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
+ return extra.data.bits.is_inferred_error;
+ },
+ else => unreachable,
+ }
+}