aboutsummaryrefslogtreecommitdiff
path: root/src/Zcu.zig
diff options
context:
space:
mode:
authorJacob Young <jacobly0@users.noreply.github.com>2024-06-15 16:10:53 -0400
committerJacob Young <jacobly0@users.noreply.github.com>2024-07-07 22:59:52 -0400
commit525f341f33af9b8aad53931fd5511f00a82cb090 (patch)
treecec3280498c1122858580946ac5e31f8feb807ce /src/Zcu.zig
parent8f20e81b8816aadd8ceb1b04bd3727cc1d124464 (diff)
downloadzig-525f341f33af9b8aad53931fd5511f00a82cb090.tar.gz
zig-525f341f33af9b8aad53931fd5511f00a82cb090.zip
Zcu: introduce `PerThread` and pass to all the functions
Diffstat (limited to 'src/Zcu.zig')
-rw-r--r--src/Zcu.zig2102
1 files changed, 22 insertions, 2080 deletions
diff --git a/src/Zcu.zig b/src/Zcu.zig
index 203238c633..bfc70815df 100644
--- a/src/Zcu.zig
+++ b/src/Zcu.zig
@@ -6,7 +6,6 @@ const std = @import("std");
const builtin = @import("builtin");
const mem = std.mem;
const Allocator = std.mem.Allocator;
-const ArrayListUnmanaged = std.ArrayListUnmanaged;
const assert = std.debug.assert;
const log = std.log.scoped(.module);
const BigIntConst = std.math.big.int.Const;
@@ -75,10 +74,10 @@ local_zir_cache: Compilation.Directory,
/// This is where all `Export` values are stored. Not all values here are necessarily valid exports;
/// to enumerate all exports, `single_exports` and `multi_exports` must be consulted.
-all_exports: ArrayListUnmanaged(Export) = .{},
+all_exports: std.ArrayListUnmanaged(Export) = .{},
/// This is a list of free indices in `all_exports`. These indices may be reused by exports from
/// future semantic analysis.
-free_exports: ArrayListUnmanaged(u32) = .{},
+free_exports: std.ArrayListUnmanaged(u32) = .{},
/// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of
/// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit`
/// whose analysis triggered the export.
@@ -179,7 +178,7 @@ stage1_flags: packed struct {
reserved: u2 = 0,
} = .{},
-compile_log_text: ArrayListUnmanaged(u8) = .{},
+compile_log_text: std.ArrayListUnmanaged(u8) = .{},
emit_h: ?*GlobalEmitH,
@@ -203,6 +202,8 @@ panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len,
panic_func_index: InternPool.Index = .none,
null_stack_trace: InternPool.Index = .none,
+pub const PerThread = @import("Zcu/PerThread.zig");
+
pub const PanicId = enum {
unreach,
unwrap_null,
@@ -519,24 +520,24 @@ pub const Decl = struct {
return decl.getExternDecl(zcu) != .none;
}
- pub fn getAlignment(decl: Decl, zcu: *Zcu) Alignment {
+ pub fn getAlignment(decl: Decl, pt: Zcu.PerThread) Alignment {
assert(decl.has_tv);
if (decl.alignment != .none) return decl.alignment;
- return decl.typeOf(zcu).abiAlignment(zcu);
+ return decl.typeOf(pt.zcu).abiAlignment(pt);
}
- pub fn declPtrType(decl: Decl, zcu: *Zcu) !Type {
+ pub fn declPtrType(decl: Decl, pt: Zcu.PerThread) !Type {
assert(decl.has_tv);
- const decl_ty = decl.typeOf(zcu);
- return zcu.ptrType(.{
+ const decl_ty = decl.typeOf(pt.zcu);
+ return pt.ptrType(.{
.child = decl_ty.toIntern(),
.flags = .{
- .alignment = if (decl.alignment == decl_ty.abiAlignment(zcu))
+ .alignment = if (decl.alignment == decl_ty.abiAlignment(pt))
.none
else
decl.alignment,
.address_space = decl.@"addrspace",
- .is_const = decl.getOwnedVariable(zcu) == null,
+ .is_const = decl.getOwnedVariable(pt.zcu) == null,
},
});
}
@@ -589,7 +590,7 @@ pub const Decl = struct {
/// This state is attached to every Decl when Module emit_h is non-null.
pub const EmitH = struct {
- fwd_decl: ArrayListUnmanaged(u8) = .{},
+ fwd_decl: std.ArrayListUnmanaged(u8) = .{},
};
pub const DeclAdapter = struct {
@@ -622,8 +623,8 @@ pub const Namespace = struct {
/// Value is whether the usingnamespace decl is marked `pub`.
usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{},
- const Index = InternPool.NamespaceIndex;
- const OptionalIndex = InternPool.OptionalNamespaceIndex;
+ pub const Index = InternPool.NamespaceIndex;
+ pub const OptionalIndex = InternPool.OptionalNamespaceIndex;
const DeclContext = struct {
zcu: *Zcu,
@@ -3079,7 +3080,7 @@ pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void {
}
}
-fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
+pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void {
var it = zcu.intern_pool.dependencyIterator(dependee);
while (it.next()) |depender| {
if (zcu.outdated.getPtr(depender)) |po_dep_count| {
@@ -3279,7 +3280,7 @@ pub fn mapOldZirToNew(
old_inst: Zir.Inst.Index,
new_inst: Zir.Inst.Index,
};
- var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{};
+ var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{};
defer match_stack.deinit(gpa);
// Main struct inst is always matched
@@ -3394,357 +3395,6 @@ pub fn mapOldZirToNew(
}
}
-/// Like `ensureDeclAnalyzed`, but the Decl is a file's root Decl.
-pub fn ensureFileAnalyzed(zcu: *Zcu, file_index: File.Index) SemaError!void {
- if (zcu.fileRootDecl(file_index).unwrap()) |existing_root| {
- return zcu.ensureDeclAnalyzed(existing_root);
- } else {
- return zcu.semaFile(file_index);
- }
-}
-
-/// This ensures that the Decl will have an up-to-date Type and Value populated.
-/// However the resolution status of the Type may not be fully resolved.
-/// For example an inferred error set is not resolved until after `analyzeFnBody`.
-/// is called.
-pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const ip = &mod.intern_pool;
- const decl = mod.declPtr(decl_index);
-
- log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{
- @intFromEnum(decl_index),
- decl.name.fmt(ip),
- });
-
- // Determine whether or not this Decl is outdated, i.e. requires re-analysis
- // even if `complete`. If a Decl is PO, we pessismistically assume that it
- // *does* require re-analysis, to ensure that the Decl is definitely
- // up-to-date when this function returns.
-
- // If analysis occurs in a poor order, this could result in over-analysis.
- // We do our best to avoid this by the other dependency logic in this file
- // which tries to limit re-analysis to Decls whose previously listed
- // dependencies are all up-to-date.
-
- const decl_as_depender = AnalUnit.wrap(.{ .decl = decl_index });
- const decl_was_outdated = mod.outdated.swapRemove(decl_as_depender) or
- mod.potentially_outdated.swapRemove(decl_as_depender);
-
- if (decl_was_outdated) {
- _ = mod.outdated_ready.swapRemove(decl_as_depender);
- }
-
- const was_outdated = mod.outdated_file_root.swapRemove(decl_index) or decl_was_outdated;
-
- switch (decl.analysis) {
- .in_progress => unreachable,
-
- .file_failure => return error.AnalysisFail,
-
- .sema_failure,
- .dependency_failure,
- .codegen_failure,
- => if (!was_outdated) return error.AnalysisFail,
-
- .complete => if (!was_outdated) return,
-
- .unreferenced => {},
- }
-
- if (was_outdated) {
- // The exports this Decl performs will be re-discovered, so we remove them here
- // prior to re-analysis.
- if (build_options.only_c) unreachable;
- mod.deleteUnitExports(decl_as_depender);
- mod.deleteUnitReferences(decl_as_depender);
- }
-
- const sema_result: SemaDeclResult = blk: {
- if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) {
- // Anonymous decl. We don't semantically analyze these.
- break :blk .{
- .invalidate_decl_val = false,
- .invalidate_decl_ref = false,
- };
- }
-
- if (mod.declIsRoot(decl_index)) {
- const changed = try mod.semaFileUpdate(decl.getFileScopeIndex(mod), decl_was_outdated);
- break :blk .{
- .invalidate_decl_val = changed,
- .invalidate_decl_ref = changed,
- };
- }
-
- const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
- defer decl_prog_node.end();
-
- 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.GenericPoison => unreachable,
- else => |e| {
- decl.analysis = .sema_failure;
- try mod.failed_analysis.ensureUnusedCapacity(mod.gpa, 1);
- try mod.retryable_failures.append(mod.gpa, AnalUnit.wrap(.{ .decl = decl_index }));
- mod.failed_analysis.putAssumeCapacityNoClobber(AnalUnit.wrap(.{ .decl = decl_index }), try ErrorMsg.create(
- mod.gpa,
- decl.navSrcLoc(mod),
- "unable to analyze: {s}",
- .{@errorName(e)},
- ));
- return error.AnalysisFail;
- },
- };
- };
-
- // TODO: we do not yet have separate dependencies for decl values vs types.
- if (decl_was_outdated) {
- if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) {
- log.debug("Decl tv invalidated ('{d}')", .{@intFromEnum(decl_index)});
- // This dependency was marked as PO, meaning dependees were waiting
- // on its analysis result, and it has turned out to be outdated.
- // Update dependees accordingly.
- try mod.markDependeeOutdated(.{ .decl_val = decl_index });
- } else {
- log.debug("Decl tv up-to-date ('{d}')", .{@intFromEnum(decl_index)});
- // This dependency was previously PO, but turned out to be up-to-date.
- // We do not need to queue successive analysis.
- try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index });
- }
- }
-}
-
-pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.Index) SemaError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
-
- // We only care about the uncoerced function.
- // We need to do this for the "orphaned function" check below to be valid.
- const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index);
-
- const func = zcu.funcInfo(maybe_coerced_func_index);
- const decl_index = func.owner_decl;
- const decl = zcu.declPtr(decl_index);
-
- log.debug("ensureFuncBodyAnalyzed '{d}' (instance of '{}')", .{
- @intFromEnum(func_index),
- decl.name.fmt(ip),
- });
-
- // First, our owner decl must be up-to-date. This will always be the case
- // during the first update, but may not on successive updates if we happen
- // to get analyzed before our parent decl.
- try zcu.ensureDeclAnalyzed(decl_index);
-
- // On an update, it's possible this function changed such that our owner
- // decl now refers to a different function, making this one orphaned. If
- // that's the case, we should remove this function from the binary.
- if (decl.val.ip_index != func_index) {
- try zcu.markDependeeOutdated(.{ .func_ies = func_index });
- ip.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .func = func_index }));
- ip.remove(func_index);
- @panic("TODO: remove orphaned function from binary");
- }
-
- // We'll want to remember what the IES used to be before the update for
- // dependency invalidation purposes.
- const old_resolved_ies = if (func.analysis(ip).inferred_error_set)
- func.resolvedErrorSet(ip).*
- else
- .none;
-
- switch (decl.analysis) {
- .unreferenced => unreachable,
- .in_progress => unreachable,
-
- .codegen_failure => unreachable, // functions do not perform constant value generation
-
- .file_failure,
- .sema_failure,
- .dependency_failure,
- => return error.AnalysisFail,
-
- .complete => {},
- }
-
- const func_as_depender = AnalUnit.wrap(.{ .func = func_index });
- const was_outdated = zcu.outdated.swapRemove(func_as_depender) or
- zcu.potentially_outdated.swapRemove(func_as_depender);
-
- if (was_outdated) {
- if (build_options.only_c) unreachable;
- _ = zcu.outdated_ready.swapRemove(func_as_depender);
- zcu.deleteUnitExports(func_as_depender);
- zcu.deleteUnitReferences(func_as_depender);
- }
-
- switch (func.analysis(ip).state) {
- .success => if (!was_outdated) return,
- .sema_failure,
- .dependency_failure,
- .codegen_failure,
- => if (!was_outdated) return error.AnalysisFail,
- .none, .queued => {},
- .in_progress => unreachable,
- .inline_only => unreachable, // don't queue work for this
- }
-
- log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{
- @intFromEnum(func_index),
- if (was_outdated) "outdated" else "never analyzed",
- });
-
- var tmp_arena = std.heap.ArenaAllocator.init(gpa);
- defer tmp_arena.deinit();
- const sema_arena = tmp_arena.allocator();
-
- var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) {
- error.AnalysisFail => {
- 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.analysis(ip).state = .dependency_failure;
- }
- return error.AnalysisFail;
- },
- error.OutOfMemory => return error.OutOfMemory,
- };
- errdefer air.deinit(gpa);
-
- const invalidate_ies_deps = i: {
- if (!was_outdated) break :i false;
- if (!func.analysis(ip).inferred_error_set) break :i true;
- const new_resolved_ies = func.resolvedErrorSet(ip).*;
- break :i new_resolved_ies != old_resolved_ies;
- };
- if (invalidate_ies_deps) {
- log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)});
- try zcu.markDependeeOutdated(.{ .func_ies = func_index });
- } else if (was_outdated) {
- log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)});
- try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index });
- }
-
- const comp = zcu.comp;
-
- const dump_air = build_options.enable_debug_extensions and comp.verbose_air;
- const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null);
-
- if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) {
- air.deinit(gpa);
- return;
- }
-
- try comp.work_queue.writeItem(.{ .codegen_func = .{
- .func = func_index,
- .air = air,
- } });
-}
-
-/// Takes ownership of `air`, even on error.
-/// If any types referenced by `air` are unresolved, marks the codegen as failed.
-pub fn linkerUpdateFunc(zcu: *Zcu, func_index: InternPool.Index, air: Air) Allocator.Error!void {
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const comp = zcu.comp;
-
- defer {
- var air_mut = air;
- air_mut.deinit(gpa);
- }
-
- const func = zcu.funcInfo(func_index);
- const decl_index = func.owner_decl;
- const decl = zcu.declPtr(decl_index);
-
- var liveness = try Liveness.analyze(gpa, air, ip);
- defer liveness.deinit(gpa);
-
- if (build_options.enable_debug_extensions and comp.verbose_air) {
- const fqn = try decl.fullyQualifiedName(zcu);
- std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)});
- @import("print_air.zig").dump(zcu, air, liveness);
- std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)});
- }
-
- if (std.debug.runtime_safety) {
- var verify: Liveness.Verify = .{
- .gpa = gpa,
- .air = air,
- .liveness = liveness,
- .intern_pool = ip,
- };
- defer verify.deinit();
-
- verify.verify() catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- else => {
- try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
- zcu.failed_analysis.putAssumeCapacityNoClobber(
- AnalUnit.wrap(.{ .func = func_index }),
- try Module.ErrorMsg.create(
- gpa,
- decl.navSrcLoc(zcu),
- "invalid liveness: {s}",
- .{@errorName(err)},
- ),
- );
- func.analysis(ip).state = .codegen_failure;
- return;
- },
- };
- }
-
- const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(ip), 0);
- defer codegen_prog_node.end();
-
- if (!air.typesFullyResolved(zcu)) {
- // A type we depend on failed to resolve. This is a transitive failure.
- // Correcting this failure will involve changing a type this function
- // depends on, hence triggering re-analysis of this function, so this
- // interacts correctly with incremental compilation.
- func.analysis(ip).state = .codegen_failure;
- } else if (comp.bin_file) |lf| {
- lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {
- func.analysis(ip).state = .codegen_failure;
- },
- else => {
- try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
- zcu.failed_analysis.putAssumeCapacityNoClobber(AnalUnit.wrap(.{ .func = func_index }), try Module.ErrorMsg.create(
- gpa,
- decl.navSrcLoc(zcu),
- "unable to codegen: {s}",
- .{@errorName(err)},
- ));
- func.analysis(ip).state = .codegen_failure;
- try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .func = func_index }));
- },
- };
- } else if (zcu.llvm_object) |llvm_object| {
- if (build_options.only_c) unreachable;
- llvm_object.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- };
- }
-}
-
/// Ensure this function's body is or will be analyzed and emitted. This should
/// be called whenever a potential runtime call of a function is seen.
///
@@ -3804,517 +3454,14 @@ pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index)
func.analysis(ip).state = .queued;
}
-pub fn semaPkg(zcu: *Zcu, pkg: *Package.Module) !void {
- const import_file_result = try zcu.importPkg(pkg);
- const root_decl_index = zcu.fileRootDecl(import_file_result.file_index);
- if (root_decl_index == .none) {
- return zcu.semaFile(import_file_result.file_index);
- }
-}
-
-fn getFileRootStruct(
- zcu: *Zcu,
- decl_index: Decl.Index,
- namespace_index: Namespace.Index,
- file_index: File.Index,
-) Allocator.Error!InternPool.Index {
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const file = zcu.fileByIndex(file_index);
- const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
- assert(extended.opcode == .struct_decl);
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
- assert(!small.has_captures_len);
- assert(!small.has_backing_int);
- assert(small.layout == .auto);
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
- const fields_len = if (small.has_fields_len) blk: {
- const fields_len = file.zir.extra[extra_index];
- extra_index += 1;
- break :blk fields_len;
- } else 0;
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = file.zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- const decls = file.zir.bodySlice(extra_index, decls_len);
- extra_index += decls_len;
-
- const tracked_inst = try ip.trackZir(gpa, file_index, .main_struct_inst);
- const wip_ty = switch (try ip.getStructType(gpa, .{
- .layout = .auto,
- .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,
- .has_namespace = true,
- .key = .{ .declared = .{
- .zir_index = tracked_inst,
- .captures = &.{},
- } },
- })) {
- .existing => unreachable, // we wouldn't be analysing the file root if this type existed
- .wip => |wip| wip,
- };
- errdefer wip_ty.cancel(ip);
-
- if (zcu.comp.debug_incremental) {
- try ip.addDependency(
- gpa,
- AnalUnit.wrap(.{ .decl = decl_index }),
- .{ .src_hash = tracked_inst },
- );
- }
-
- const decl = zcu.declPtr(decl_index);
- decl.val = Value.fromInterned(wip_ty.index);
- decl.has_tv = true;
- decl.owns_tv = true;
- decl.analysis = .complete;
-
- try zcu.scanNamespace(namespace_index, decls, decl);
- try zcu.comp.work_queue.writeItem(.{ .resolve_type_fully = wip_ty.index });
- return wip_ty.finish(ip, decl_index, namespace_index.toOptional());
-}
-
-/// Re-analyze the root Decl of a file on an incremental update.
-/// If `type_outdated`, the struct type itself is considered outdated and is
-/// reconstructed at a new InternPool index. Otherwise, the namespace is just
-/// re-analyzed. Returns whether the decl's tyval was invalidated.
-fn semaFileUpdate(zcu: *Zcu, file_index: File.Index, type_outdated: bool) SemaError!bool {
- const file = zcu.fileByIndex(file_index);
- const decl = zcu.declPtr(zcu.fileRootDecl(file_index).unwrap().?);
-
- log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{
- file.mod.fully_qualified_name,
- file.sub_file_path,
- type_outdated,
- });
-
- if (file.status != .success_zir) {
- if (decl.analysis == .file_failure) {
- return false;
- } else {
- decl.analysis = .file_failure;
- return true;
- }
- }
-
- if (decl.analysis == .file_failure) {
- // No struct type currently exists. Create one!
- const root_decl = zcu.fileRootDecl(file_index);
- _ = try zcu.getFileRootStruct(root_decl.unwrap().?, decl.src_namespace, file_index);
- return true;
- }
-
- assert(decl.has_tv);
- assert(decl.owns_tv);
-
- if (type_outdated) {
- // Invalidate the existing type, reusing the decl and namespace.
- const file_root_decl = zcu.fileRootDecl(file_index).unwrap().?;
- zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, AnalUnit.wrap(.{
- .decl = file_root_decl,
- }));
- zcu.intern_pool.remove(decl.val.toIntern());
- decl.val = undefined;
- _ = try zcu.getFileRootStruct(file_root_decl, decl.src_namespace, file_index);
- return true;
- }
-
- // Only the struct's namespace is outdated.
- // Preserve the type - just scan the namespace again.
-
- const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended;
- const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
-
- var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len;
- extra_index += @intFromBool(small.has_fields_len);
- const decls_len = if (small.has_decls_len) blk: {
- const decls_len = file.zir.extra[extra_index];
- extra_index += 1;
- break :blk decls_len;
- } else 0;
- const decls = file.zir.bodySlice(extra_index, decls_len);
-
- if (!type_outdated) {
- try zcu.scanNamespace(decl.src_namespace, decls, decl);
- }
-
- return false;
-}
-
-/// Regardless of the file status, will create a `Decl` if none exists so that we can track
-/// dependencies and re-analyze when the file becomes outdated.
-fn semaFile(zcu: *Zcu, file_index: File.Index) SemaError!void {
- const tracy = trace(@src());
- defer tracy.end();
-
- const file = zcu.fileByIndex(file_index);
- assert(zcu.fileRootDecl(file_index) == .none);
-
- const gpa = zcu.gpa;
- log.debug("semaFile zcu={s} sub_file_path={s}", .{
- file.mod.fully_qualified_name, file.sub_file_path,
- });
-
- // Because these three things each reference each other, `undefined`
- // placeholders are used before being set after the struct type gains an
- // InternPool index.
- const new_namespace_index = try zcu.createNamespace(.{
- .parent = .none,
- .decl_index = undefined,
- .file_scope = file_index,
- });
- errdefer zcu.destroyNamespace(new_namespace_index);
-
- const new_decl_index = try zcu.allocateNewDecl(new_namespace_index);
- const new_decl = zcu.declPtr(new_decl_index);
- errdefer @panic("TODO error handling");
-
- zcu.setFileRootDecl(file_index, new_decl_index.toOptional());
- zcu.namespacePtr(new_namespace_index).decl_index = new_decl_index;
-
- new_decl.name = try file.fullyQualifiedName(zcu);
- new_decl.name_fully_qualified = true;
- new_decl.is_pub = true;
- new_decl.is_exported = false;
- new_decl.alignment = .none;
- new_decl.@"linksection" = .none;
- new_decl.analysis = .in_progress;
-
- if (file.status != .success_zir) {
- new_decl.analysis = .file_failure;
- return;
- }
- assert(file.zir_loaded);
-
- const struct_ty = try zcu.getFileRootStruct(new_decl_index, new_namespace_index, file_index);
- errdefer zcu.intern_pool.remove(struct_ty);
-
- switch (zcu.comp.cache_use) {
- .whole => |whole| if (whole.cache_manifest) |man| {
- const source = file.getSource(gpa) catch |err| {
- try reportRetryableFileError(zcu, file_index, "unable to load source: {s}", .{@errorName(err)});
- return error.AnalysisFail;
- };
-
- const resolved_path = std.fs.path.resolve(gpa, &.{
- file.mod.root.root_dir.path orelse ".",
- file.mod.root.sub_path,
- file.sub_file_path,
- }) catch |err| {
- try reportRetryableFileError(zcu, file_index, "unable to resolve path: {s}", .{@errorName(err)});
- return error.AnalysisFail;
- };
- errdefer gpa.free(resolved_path);
-
- whole.cache_manifest_mutex.lock();
- defer whole.cache_manifest_mutex.unlock();
- try man.addFilePostContents(resolved_path, source.bytes, source.stat);
- },
- .incremental => {},
- }
-}
-
-const SemaDeclResult = packed struct {
+pub const SemaDeclResult = packed struct {
/// Whether the value of a `decl_val` of this Decl changed.
invalidate_decl_val: bool,
/// Whether the type of a `decl_ref` of this Decl changed.
invalidate_decl_ref: bool,
};
-fn semaDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult {
- const tracy = trace(@src());
- defer tracy.end();
-
- const decl = zcu.declPtr(decl_index);
- const ip = &zcu.intern_pool;
-
- if (decl.getFileScope(zcu).status != .success_zir) {
- return error.AnalysisFail;
- }
-
- assert(!zcu.declIsRoot(decl_index));
-
- if (decl.zir_decl_index == .none and decl.owns_tv) {
- // We are re-analyzing an anonymous owner Decl (for a function or a namespace type).
- return zcu.semaAnonOwnerDecl(decl_index);
- }
-
- log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)});
- log.debug("decl name '{}'", .{(try decl.fullyQualifiedName(zcu)).fmt(ip)});
- defer blk: {
- log.debug("finish decl name '{}'", .{(decl.fullyQualifiedName(zcu) catch break :blk).fmt(ip)});
- }
-
- const old_has_tv = decl.has_tv;
- // The following values are ignored if `!old_has_tv`
- const old_ty = if (old_has_tv) decl.typeOf(zcu) else undefined;
- const old_val = decl.val;
- const old_align = decl.alignment;
- const old_linksection = decl.@"linksection";
- const old_addrspace = decl.@"addrspace";
- const old_is_inline = if (decl.getOwnedFunction(zcu)) |prev_func|
- prev_func.analysis(ip).state == .inline_only
- else
- false;
-
- const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip);
-
- const gpa = zcu.gpa;
- const zir = decl.getFileScope(zcu).zir;
-
- const builtin_type_target_index: InternPool.Index = ip_index: {
- const std_mod = zcu.std_mod;
- if (decl.getFileScope(zcu).mod != std_mod) break :ip_index .none;
- // We're in the std module.
- const std_file_imported = try zcu.importPkg(std_mod);
- const std_file_root_decl_index = zcu.fileRootDecl(std_file_imported.file_index);
- const std_decl = zcu.declPtr(std_file_root_decl_index.unwrap().?);
- const std_namespace = std_decl.getInnerNamespace(zcu).?;
- const builtin_str = try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls);
- const builtin_decl = zcu.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .zcu = zcu }) orelse break :ip_index .none);
- const builtin_namespace = builtin_decl.getInnerNamespaceIndex(zcu).unwrap() orelse break :ip_index .none;
- if (decl.src_namespace != builtin_namespace) break :ip_index .none;
- // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index.
- for ([_][]const u8{
- "AtomicOrder",
- "AtomicRmwOp",
- "CallingConvention",
- "AddressSpace",
- "FloatMode",
- "ReduceOp",
- "CallModifier",
- "PrefetchOptions",
- "ExportOptions",
- "ExternOptions",
- "Type",
- }, [_]InternPool.Index{
- .atomic_order_type,
- .atomic_rmw_op_type,
- .calling_convention_type,
- .address_space_type,
- .float_mode_type,
- .reduce_op_type,
- .call_modifier_type,
- .prefetch_options_type,
- .export_options_type,
- .extern_options_type,
- .type_info_type,
- }) |type_name, type_ip| {
- if (decl.name.eqlSlice(type_name, ip)) break :ip_index type_ip;
- }
- break :ip_index .none;
- };
-
- zcu.intern_pool.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .decl = decl_index }));
-
- decl.analysis = .in_progress;
-
- var analysis_arena = std.heap.ArenaAllocator.init(gpa);
- defer analysis_arena.deinit();
-
- var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
- defer comptime_err_ret_trace.deinit();
-
- var sema: Sema = .{
- .mod = zcu,
- .gpa = gpa,
- .arena = analysis_arena.allocator(),
- .code = zir,
- .owner_decl = decl,
- .owner_decl_index = decl_index,
- .func_index = .none,
- .func_is_naked = false,
- .fn_ret_ty = Type.void,
- .fn_ret_ty_ies = null,
- .owner_func_index = .none,
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- .builtin_type_target_index = builtin_type_target_index,
- };
- defer sema.deinit();
-
- // Every Decl (other than file root Decls, which do not have a ZIR index) has a dependency on its own source.
- try sema.declareDependency(.{ .src_hash = try ip.trackZir(
- gpa,
- decl.getFileScopeIndex(zcu),
- decl_inst,
- ) });
-
- var block_scope: Sema.Block = .{
- .parent = null,
- .sema = &sema,
- .namespace = decl.src_namespace,
- .instructions = .{},
- .inlining = null,
- .is_comptime = true,
- .src_base_inst = decl.zir_decl_index.unwrap().?,
- .type_name_ctx = decl.name,
- };
- defer block_scope.instructions.deinit(gpa);
-
- const decl_bodies = decl.zirBodies(zcu);
-
- const result_ref = try sema.resolveInlineBody(&block_scope, decl_bodies.value_body, decl_inst);
- // We'll do some other bits with the Sema. Clear the type target index just
- // in case they analyze any type.
- sema.builtin_type_target_index = .none;
- const align_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_align = 0 });
- const section_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_section = 0 });
- const address_space_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_addrspace = 0 });
- const ty_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_ty = 0 });
- const init_src: LazySrcLoc = block_scope.src(.{ .node_offset_var_decl_init = 0 });
- const decl_val = try sema.resolveFinalDeclValue(&block_scope, init_src, result_ref);
- const decl_ty = decl_val.typeOf(zcu);
-
- // Note this resolves the type of the Decl, not the value; if this Decl
- // is a struct, for example, this resolves `type` (which needs no resolution),
- // not the struct itself.
- try decl_ty.resolveLayout(zcu);
-
- if (decl.kind == .@"usingnamespace") {
- if (!decl_ty.eql(Type.type, zcu)) {
- return sema.fail(&block_scope, ty_src, "expected type, found {}", .{
- decl_ty.fmt(zcu),
- });
- }
- const ty = decl_val.toType();
- if (ty.getNamespace(zcu) == null) {
- return sema.fail(&block_scope, ty_src, "type {} has no namespace", .{ty.fmt(zcu)});
- }
-
- decl.val = ty.toValue();
- decl.alignment = .none;
- decl.@"linksection" = .none;
- decl.has_tv = true;
- decl.owns_tv = false;
- decl.analysis = .complete;
-
- // TODO: usingnamespace cannot currently participate in incremental compilation
- return .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
- }
-
- var queue_linker_work = true;
- var is_func = false;
- var is_inline = false;
- switch (decl_val.toIntern()) {
- .generic_poison => unreachable,
- .unreachable_value => unreachable,
- else => switch (ip.indexToKey(decl_val.toIntern())) {
- .variable => |variable| {
- decl.owns_tv = variable.decl == decl_index;
- queue_linker_work = decl.owns_tv;
- },
-
- .extern_func => |extern_func| {
- decl.owns_tv = extern_func.decl == decl_index;
- queue_linker_work = decl.owns_tv;
- is_func = decl.owns_tv;
- },
-
- .func => |func| {
- decl.owns_tv = func.owner_decl == decl_index;
- queue_linker_work = false;
- is_inline = decl.owns_tv and decl_ty.fnCallingConvention(zcu) == .Inline;
- is_func = decl.owns_tv;
- },
-
- else => {},
- },
- }
-
- decl.val = decl_val;
- // Function linksection, align, and addrspace were already set by Sema
- if (!is_func) {
- decl.alignment = blk: {
- const align_body = decl_bodies.align_body orelse break :blk .none;
- const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst);
- break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref);
- };
- decl.@"linksection" = blk: {
- const linksection_body = decl_bodies.linksection_body orelse break :blk .none;
- const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst);
- const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{
- .needed_comptime_reason = "linksection must be comptime-known",
- });
- if (mem.indexOfScalar(u8, bytes, 0) != null) {
- return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{});
- } else if (bytes.len == 0) {
- return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
- }
- break :blk try ip.getOrPutStringOpt(gpa, bytes, .no_embedded_nulls);
- };
- decl.@"addrspace" = blk: {
- const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_val.toIntern())) {
- .variable => .variable,
- .extern_func, .func => .function,
- else => .constant,
- };
-
- const target = sema.mod.getTarget();
-
- const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) {
- .function => target_util.defaultAddressSpace(target, .function),
- .variable => target_util.defaultAddressSpace(target, .global_mutable),
- .constant => target_util.defaultAddressSpace(target, .global_constant),
- else => unreachable,
- };
- const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst);
- break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx);
- };
- }
- decl.has_tv = true;
- decl.analysis = .complete;
-
- const result: SemaDeclResult = if (old_has_tv) .{
- .invalidate_decl_val = !decl_ty.eql(old_ty, zcu) or
- !decl.val.eql(old_val, decl_ty, zcu) or
- is_inline != old_is_inline,
- .invalidate_decl_ref = !decl_ty.eql(old_ty, zcu) or
- decl.alignment != old_align or
- decl.@"linksection" != old_linksection or
- decl.@"addrspace" != old_addrspace or
- is_inline != old_is_inline,
- } else .{
- .invalidate_decl_val = true,
- .invalidate_decl_ref = true,
- };
-
- const has_runtime_bits = queue_linker_work and (is_func or try sema.typeHasRuntimeBits(decl_ty));
- if (has_runtime_bits) {
- // Needed for codegen_decl which will call updateDecl and then the
- // codegen backend wants full access to the Decl Type.
- try decl_ty.resolveFully(zcu);
-
- try zcu.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
-
- if (result.invalidate_decl_ref and zcu.emit_h != null) {
- try zcu.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index });
- }
- }
-
- if (decl.is_exported) {
- const export_src: LazySrcLoc = block_scope.src(.{ .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);
- }
-
- try sema.flushExports();
-
- return result;
-}
-
-fn semaAnonOwnerDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult {
+pub fn semaAnonOwnerDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult {
const decl = zcu.declPtr(decl_index);
assert(decl.has_tv);
@@ -4533,78 +3680,6 @@ pub fn importFile(
};
}
-pub fn embedFile(
- mod: *Module,
- cur_file: *File,
- import_string: []const u8,
- src_loc: LazySrcLoc,
-) !InternPool.Index {
- const gpa = mod.gpa;
-
- if (cur_file.mod.deps.get(import_string)) |pkg| {
- const resolved_path = try std.fs.path.resolve(gpa, &.{
- pkg.root.root_dir.path orelse ".",
- pkg.root.sub_path,
- pkg.root_src_path,
- });
- var keep_resolved_path = false;
- defer if (!keep_resolved_path) gpa.free(resolved_path);
-
- const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
- errdefer {
- assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path));
- keep_resolved_path = false;
- }
- if (gop.found_existing) return gop.value_ptr.*.val;
- keep_resolved_path = true;
-
- const sub_file_path = try gpa.dupe(u8, pkg.root_src_path);
- errdefer gpa.free(sub_file_path);
-
- return newEmbedFile(mod, pkg, sub_file_path, resolved_path, gop.value_ptr, src_loc);
- }
-
- // The resolved path is used as the key in the table, to detect if a file
- // refers to the same as another, despite different relative paths.
- const resolved_path = try std.fs.path.resolve(gpa, &.{
- cur_file.mod.root.root_dir.path orelse ".",
- cur_file.mod.root.sub_path,
- cur_file.sub_file_path,
- "..",
- import_string,
- });
-
- var keep_resolved_path = false;
- defer if (!keep_resolved_path) gpa.free(resolved_path);
-
- const gop = try mod.embed_table.getOrPut(gpa, resolved_path);
- errdefer {
- assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path));
- keep_resolved_path = false;
- }
- if (gop.found_existing) return gop.value_ptr.*.val;
- keep_resolved_path = true;
-
- const resolved_root_path = try std.fs.path.resolve(gpa, &.{
- cur_file.mod.root.root_dir.path orelse ".",
- cur_file.mod.root.sub_path,
- });
- defer gpa.free(resolved_root_path);
-
- const sub_file_path = p: {
- const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path);
- errdefer gpa.free(relative);
-
- if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) {
- break :p relative;
- }
- return error.ImportOutsideModulePath;
- };
- defer gpa.free(sub_file_path);
-
- return newEmbedFile(mod, cur_file.mod, sub_file_path, resolved_path, gop.value_ptr, src_loc);
-}
-
fn computePathDigest(zcu: *Zcu, mod: *Package.Module, sub_file_path: []const u8) Cache.BinDigest {
const want_local_cache = mod == zcu.main_mod;
var path_hash: Cache.HashHelper = .{};
@@ -4620,87 +3695,6 @@ fn computePathDigest(zcu: *Zcu, mod: *Package.Module, sub_file_path: []const u8)
return bin;
}
-/// https://github.com/ziglang/zig/issues/14307
-fn newEmbedFile(
- mod: *Module,
- pkg: *Package.Module,
- sub_file_path: []const u8,
- resolved_path: []const u8,
- result: **EmbedFile,
- src_loc: LazySrcLoc,
-) !InternPool.Index {
- const gpa = mod.gpa;
- const ip = &mod.intern_pool;
-
- const new_file = try gpa.create(EmbedFile);
- errdefer gpa.destroy(new_file);
-
- var file = try pkg.root.openFile(sub_file_path, .{});
- defer file.close();
-
- const actual_stat = try file.stat();
- const stat: Cache.File.Stat = .{
- .size = actual_stat.size,
- .inode = actual_stat.inode,
- .mtime = actual_stat.mtime,
- };
- const size = std.math.cast(usize, actual_stat.size) orelse return error.Overflow;
-
- const bytes = try ip.string_bytes.addManyAsSlice(gpa, try std.math.add(usize, size, 1));
- const actual_read = try file.readAll(bytes[0..size]);
- if (actual_read != size) return error.UnexpectedEndOfFile;
- bytes[size] = 0;
-
- const comp = mod.comp;
- switch (comp.cache_use) {
- .whole => |whole| if (whole.cache_manifest) |man| {
- const copied_resolved_path = try gpa.dupe(u8, resolved_path);
- errdefer gpa.free(copied_resolved_path);
- whole.cache_manifest_mutex.lock();
- defer whole.cache_manifest_mutex.unlock();
- try man.addFilePostContents(copied_resolved_path, bytes[0..size], stat);
- },
- .incremental => {},
- }
-
- const array_ty = try ip.get(gpa, .{ .array_type = .{
- .len = size,
- .sentinel = .zero_u8,
- .child = .u8_type,
- } });
- const array_val = try ip.get(gpa, .{ .aggregate = .{
- .ty = array_ty,
- .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, bytes.len, .maybe_embedded_nulls) },
- } });
-
- const ptr_ty = (try mod.ptrType(.{
- .child = array_ty,
- .flags = .{
- .alignment = .none,
- .is_const = true,
- .address_space = .generic,
- },
- })).toIntern();
- const ptr_val = try ip.get(gpa, .{ .ptr = .{
- .ty = ptr_ty,
- .base_addr = .{ .anon_decl = .{
- .val = array_val,
- .orig_ty = ptr_ty,
- } },
- .byte_offset = 0,
- } });
-
- result.* = new_file;
- new_file.* = .{
- .sub_file_path = try ip.getOrPutString(gpa, sub_file_path, .no_embedded_nulls),
- .owner = pkg,
- .stat = stat,
- .val = ptr_val,
- .src_loc = src_loc,
- };
- return ptr_val;
-}
-
pub fn scanNamespace(
zcu: *Zcu,
namespace_index: Namespace.Index,
@@ -4970,13 +3964,6 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
mod.destroyDecl(decl_index);
}
-/// Finalize the creation of an anon decl.
-pub fn finalizeAnonDecl(mod: *Module, decl_index: Decl.Index) Allocator.Error!void {
- if (mod.declPtr(decl_index).typeOf(mod).isFnOrHasRuntimeBits(mod)) {
- try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index });
- }
-}
-
/// Delete all the Export objects that are caused by this `AnalUnit`. Re-analysis of
/// this `AnalUnit` will cause them to be re-created (or not).
pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
@@ -5019,7 +4006,7 @@ pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void {
/// Delete all references in `reference_table` which are caused by this `AnalUnit`.
/// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated.
-fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
+pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void {
const gpa = zcu.gpa;
const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return;
@@ -5058,251 +4045,6 @@ pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit
gop.value_ptr.* = @intCast(ref_idx);
}
-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 ip = &mod.intern_pool;
- const func = mod.funcInfo(func_index);
- const decl_index = func.owner_decl;
- const decl = mod.declPtr(decl_index);
-
- log.debug("func name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)});
- defer blk: {
- log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)});
- }
-
- const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(mod)).toSlice(ip), 0);
- defer decl_prog_node.end();
-
- mod.intern_pool.removeDependenciesForDepender(gpa, AnalUnit.wrap(.{ .func = func_index }));
-
- var comptime_err_ret_trace = std.ArrayList(LazySrcLoc).init(gpa);
- defer comptime_err_ret_trace.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.typeOf(mod);
- const fn_ty_info = mod.typeToFunc(fn_ty).?;
-
- var sema: Sema = .{
- .mod = mod,
- .gpa = gpa,
- .arena = arena,
- .code = decl.getFileScope(mod).zir,
- .owner_decl = decl,
- .owner_decl_index = decl_index,
- .func_index = func_index,
- .func_is_naked = fn_ty_info.cc == .Naked,
- .fn_ret_ty = Type.fromInterned(fn_ty_info.return_type),
- .fn_ret_ty_ies = null,
- .owner_func_index = func_index,
- .branch_quota = @max(func.branchQuota(ip).*, Sema.default_branch_quota),
- .comptime_err_ret_trace = &comptime_err_ret_trace,
- };
- defer sema.deinit();
-
- // Every runtime function has a dependency on the source of the Decl it originates from.
- // It also depends on the value of its owner Decl.
- try sema.declareDependency(.{ .src_hash = decl.zir_decl_index.unwrap().? });
- try sema.declareDependency(.{ .decl_val = decl_index });
-
- if (func.analysis(ip).inferred_error_set) {
- const ies = try arena.create(Sema.InferredErrorSet);
- ies.* = .{ .func = func_index };
- sema.fn_ret_ty_ies = ies;
- }
-
- // reset in case calls to errorable functions are removed.
- 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;
- try sema.air_extra.ensureTotalCapacity(gpa, reserved_count);
- sema.air_extra.items.len += reserved_count;
-
- var inner_block: Sema.Block = .{
- .parent = null,
- .sema = &sema,
- .namespace = decl.src_namespace,
- .instructions = .{},
- .inlining = null,
- .is_comptime = false,
- .src_base_inst = inst: {
- const owner_info = if (func.generic_owner == .none)
- func
- else
- mod.funcInfo(func.generic_owner);
- const orig_decl = mod.declPtr(owner_info.owner_decl);
- break :inst orig_decl.zir_decl_index.unwrap().?;
- },
- .type_name_ctx = decl.name,
- };
- defer inner_block.instructions.deinit(gpa);
-
- const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).resolve(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.
- // AIR requires the `arg` parameters to be the first N instructions.
- // 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 = 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);
- try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
-
- // 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 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 param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index];
- runtime_param_index += 1;
-
- const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) {
- error.GenericPoison => unreachable,
- error.ComptimeReturn => unreachable,
- error.ComptimeBreak => unreachable,
- else => |e| return e,
- };
- if (opt_opv) |opv| {
- gop.value_ptr.* = Air.internedToRef(opv.toIntern());
- continue;
- }
- const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len);
- gop.value_ptr.* = arg_index.toRef();
- inner_block.instructions.appendAssumeCapacity(arg_index);
- sema.air_instructions.appendAssumeCapacity(.{
- .tag = .arg,
- .data = .{ .arg = .{
- .ty = Air.internedToRef(param_ty),
- .src_index = @intCast(src_param_index),
- } },
- });
- }
-
- func.analysis(ip).state = .in_progress;
-
- const last_arg_index = inner_block.instructions.items.len;
-
- // Save the error trace as our first action in the function.
- // If this is unnecessary after all, Liveness will clean it up for us.
- const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&inner_block);
- sema.error_return_trace_index_on_fn_entry = error_return_trace_index;
- inner_block.error_return_trace_index = error_return_trace_index;
-
- sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) {
- // TODO make these unreachable instead of @panic
- error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
- error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
- else => |e| return e,
- };
-
- for (sema.unresolved_inferred_allocs.keys()) |ptr_inst| {
- // The lack of a resolve_inferred_alloc means that this instruction
- // is unused so it just has to be a no-op.
- sema.air_instructions.set(@intFromEnum(ptr_inst), .{
- .tag = .alloc,
- .data = .{ .ty = Type.single_const_pointer_to_comptime_int },
- });
- }
-
- // If we don't get an error return trace from a caller, create our own.
- if (func.analysis(ip).calls_or_awaits_errorable_fn and
- mod.comp.config.any_error_tracing and
- !sema.fn_ret_ty.isError(mod))
- {
- sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) {
- // TODO make these unreachable instead of @panic
- error.GenericPoison => @panic("zig compiler bug: GenericPoison"),
- error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"),
- error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"),
- else => |e| return e,
- };
- }
-
- // Copy the block into place and mark that as the main block.
- try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len +
- inner_block.instructions.items.len);
- const main_block_index = sema.addExtraAssumeCapacity(Air.Block{
- .body_len = @intCast(inner_block.instructions.items.len),
- });
- sema.air_extra.appendSliceAssumeCapacity(@ptrCast(inner_block.instructions.items));
- sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index;
-
- // Resolving inferred error sets is done *before* setting the function
- // state to success, so that "unable to resolve inferred error set" errors
- // can be emitted here.
- if (sema.fn_ret_ty_ies) |ies| {
- sema.resolveInferredErrorSetPtr(&inner_block, .{
- .base_node_inst = inner_block.src_base_inst,
- .offset = LazySrcLoc.Offset.nodeOffset(0),
- }, ies) catch |err| switch (err) {
- error.GenericPoison => unreachable,
- error.ComptimeReturn => unreachable,
- error.ComptimeBreak => unreachable,
- error.AnalysisFail => {
- // In this case our function depends on a type that had a compile error.
- // We should not try to lower this function.
- decl.analysis = .dependency_failure;
- return error.AnalysisFail;
- },
- else => |e| return e,
- };
- assert(ies.resolved != .none);
- ip.funcIesResolved(func_index).* = ies.resolved;
- }
-
- func.analysis(ip).state = .success;
-
- // Finally we must resolve the return type and parameter types so that backends
- // have full access to type information.
- // Crucially, this happens *after* we set the function state to success above,
- // so that dependencies on the function body will now be satisfied rather than
- // result in circular dependency errors.
- sema.resolveFnTypes(fn_ty) catch |err| switch (err) {
- error.GenericPoison => unreachable,
- error.ComptimeReturn => unreachable,
- error.ComptimeBreak => unreachable,
- error.AnalysisFail => {
- // In this case our function depends on a type that had a compile error.
- // We should not try to lower this function.
- decl.analysis = .dependency_failure;
- return error.AnalysisFail;
- },
- else => |e| return e,
- };
-
- try sema.flushExports();
-
- return .{
- .instructions = sema.air_instructions.toOwnedSlice(),
- .extra = try sema.air_extra.toOwnedSlice(gpa),
- };
-}
-
pub fn createNamespace(mod: *Module, initialization: Namespace) !Namespace.Index {
return mod.intern_pool.createNamespace(mod.gpa, initialization);
}
@@ -5420,117 +4162,7 @@ fn lockAndClearFileCompileError(mod: *Module, file: *File) void {
}
}
-/// Called from `Compilation.update`, after everything is done, just before
-/// reporting compile errors. In this function we emit exported symbol collision
-/// errors and communicate exported symbols to the linker backend.
-pub fn processExports(zcu: *Zcu) !void {
- const gpa = zcu.gpa;
-
- // First, construct a mapping of every exported value and Decl to the indices of all its different exports.
- var decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(u32)) = .{};
- var value_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, ArrayListUnmanaged(u32)) = .{};
- defer {
- for (decl_exports.values()) |*exports| {
- exports.deinit(gpa);
- }
- decl_exports.deinit(gpa);
- for (value_exports.values()) |*exports| {
- exports.deinit(gpa);
- }
- value_exports.deinit(gpa);
- }
-
- // We note as a heuristic:
- // * It is rare to export a value.
- // * It is rare for one Decl to be exported multiple times.
- // So, this ensureTotalCapacity serves as a reasonable (albeit very approximate) optimization.
- try decl_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count());
-
- for (zcu.single_exports.values()) |export_idx| {
- const exp = zcu.all_exports.items[export_idx];
- const value_ptr, const found_existing = switch (exp.exported) {
- .decl_index => |i| gop: {
- const gop = try decl_exports.getOrPut(gpa, i);
- break :gop .{ gop.value_ptr, gop.found_existing };
- },
- .value => |i| gop: {
- const gop = try value_exports.getOrPut(gpa, i);
- break :gop .{ gop.value_ptr, gop.found_existing };
- },
- };
- if (!found_existing) value_ptr.* = .{};
- try value_ptr.append(gpa, export_idx);
- }
-
- for (zcu.multi_exports.values()) |info| {
- for (zcu.all_exports.items[info.index..][0..info.len], info.index..) |exp, export_idx| {
- const value_ptr, const found_existing = switch (exp.exported) {
- .decl_index => |i| gop: {
- const gop = try decl_exports.getOrPut(gpa, i);
- break :gop .{ gop.value_ptr, gop.found_existing };
- },
- .value => |i| gop: {
- const gop = try value_exports.getOrPut(gpa, i);
- break :gop .{ gop.value_ptr, gop.found_existing };
- },
- };
- if (!found_existing) value_ptr.* = .{};
- try value_ptr.append(gpa, @intCast(export_idx));
- }
- }
-
- // Map symbol names to `Export` for name collision detection.
- var symbol_exports: SymbolExports = .{};
- defer symbol_exports.deinit(gpa);
-
- for (decl_exports.keys(), decl_exports.values()) |exported_decl, exports_list| {
- const exported: Exported = .{ .decl_index = exported_decl };
- try processExportsInner(zcu, &symbol_exports, exported, exports_list.items);
- }
-
- for (value_exports.keys(), value_exports.values()) |exported_value, exports_list| {
- const exported: Exported = .{ .value = exported_value };
- try processExportsInner(zcu, &symbol_exports, exported, exports_list.items);
- }
-}
-
-const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, u32);
-
-fn processExportsInner(
- zcu: *Zcu,
- symbol_exports: *SymbolExports,
- exported: Exported,
- export_indices: []const u32,
-) error{OutOfMemory}!void {
- const gpa = zcu.gpa;
-
- for (export_indices) |export_idx| {
- const new_export = &zcu.all_exports.items[export_idx];
- const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
- if (gop.found_existing) {
- new_export.status = .failed_retryable;
- try zcu.failed_exports.ensureUnusedCapacity(gpa, 1);
- const msg = try ErrorMsg.create(gpa, new_export.src, "exported symbol collision: {}", .{
- new_export.opts.name.fmt(&zcu.intern_pool),
- });
- errdefer msg.destroy(gpa);
- const other_export = zcu.all_exports.items[gop.value_ptr.*];
- try zcu.errNote(other_export.src, msg, "other symbol here", .{});
- zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg);
- new_export.status = .failed;
- } else {
- gop.value_ptr.* = export_idx;
- }
- }
- if (zcu.comp.bin_file) |lf| {
- try handleUpdateExports(zcu, export_indices, lf.updateExports(zcu, exported, export_indices));
- } else if (zcu.llvm_object) |llvm_object| {
- if (build_options.only_c) unreachable;
- try handleUpdateExports(zcu, export_indices, llvm_object.updateExports(zcu, exported, export_indices));
- }
-}
-
-fn handleUpdateExports(
+pub fn handleUpdateExports(
zcu: *Zcu,
export_indices: []const u32,
result: link.File.UpdateExportsError!void,
@@ -5551,180 +4183,7 @@ fn handleUpdateExports(
};
}
-pub fn populateTestFunctions(
- zcu: *Zcu,
- main_progress_node: std.Progress.Node,
-) !void {
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const builtin_mod = zcu.root_mod.getBuiltinDependency();
- const builtin_file_index = (zcu.importPkg(builtin_mod) catch unreachable).file_index;
- const root_decl_index = zcu.fileRootDecl(builtin_file_index);
- const root_decl = zcu.declPtr(root_decl_index.unwrap().?);
- const builtin_namespace = zcu.namespacePtr(root_decl.src_namespace);
- const test_functions_str = try ip.getOrPutString(gpa, "test_functions", .no_embedded_nulls);
- const decl_index = builtin_namespace.decls.getKeyAdapted(
- test_functions_str,
- DeclAdapter{ .zcu = zcu },
- ).?;
- {
- // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions`
- // was not referenced by start code.
- zcu.sema_prog_node = main_progress_node.start("Semantic Analysis", 0);
- defer {
- zcu.sema_prog_node.end();
- zcu.sema_prog_node = undefined;
- }
- try zcu.ensureDeclAnalyzed(decl_index);
- }
-
- const decl = zcu.declPtr(decl_index);
- const test_fn_ty = decl.typeOf(zcu).slicePtrFieldType(zcu).childType(zcu);
-
- const array_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = array: {
- // Add zcu.test_functions to an array decl then make the test_functions
- // decl reference it as a slice.
- const test_fn_vals = try gpa.alloc(InternPool.Index, zcu.test_functions.count());
- defer gpa.free(test_fn_vals);
-
- for (test_fn_vals, zcu.test_functions.keys()) |*test_fn_val, test_decl_index| {
- const test_decl = zcu.declPtr(test_decl_index);
- const test_decl_name = try test_decl.fullyQualifiedName(zcu);
- const test_decl_name_len = test_decl_name.length(ip);
- const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = n: {
- const test_name_ty = try zcu.arrayType(.{
- .len = test_decl_name_len,
- .child = .u8_type,
- });
- const test_name_val = try zcu.intern(.{ .aggregate = .{
- .ty = test_name_ty.toIntern(),
- .storage = .{ .bytes = test_decl_name.toString() },
- } });
- break :n .{
- .orig_ty = (try zcu.singleConstPtrType(test_name_ty)).toIntern(),
- .val = test_name_val,
- };
- };
-
- const test_fn_fields = .{
- // name
- try zcu.intern(.{ .slice = .{
- .ty = .slice_const_u8_type,
- .ptr = try zcu.intern(.{ .ptr = .{
- .ty = .manyptr_const_u8_type,
- .base_addr = .{ .anon_decl = test_name_anon_decl },
- .byte_offset = 0,
- } }),
- .len = try zcu.intern(.{ .int = .{
- .ty = .usize_type,
- .storage = .{ .u64 = test_decl_name_len },
- } }),
- } }),
- // func
- try zcu.intern(.{ .ptr = .{
- .ty = try zcu.intern(.{ .ptr_type = .{
- .child = test_decl.typeOf(zcu).toIntern(),
- .flags = .{
- .is_const = true,
- },
- } }),
- .base_addr = .{ .decl = test_decl_index },
- .byte_offset = 0,
- } }),
- };
- test_fn_val.* = try zcu.intern(.{ .aggregate = .{
- .ty = test_fn_ty.toIntern(),
- .storage = .{ .elems = &test_fn_fields },
- } });
- }
-
- const array_ty = try zcu.arrayType(.{
- .len = test_fn_vals.len,
- .child = test_fn_ty.toIntern(),
- .sentinel = .none,
- });
- const array_val = try zcu.intern(.{ .aggregate = .{
- .ty = array_ty.toIntern(),
- .storage = .{ .elems = test_fn_vals },
- } });
- break :array .{
- .orig_ty = (try zcu.singleConstPtrType(array_ty)).toIntern(),
- .val = array_val,
- };
- };
-
- {
- const new_ty = try zcu.ptrType(.{
- .child = test_fn_ty.toIntern(),
- .flags = .{
- .is_const = true,
- .size = .Slice,
- },
- });
- const new_val = decl.val;
- const new_init = try zcu.intern(.{ .slice = .{
- .ty = new_ty.toIntern(),
- .ptr = try zcu.intern(.{ .ptr = .{
- .ty = new_ty.slicePtrFieldType(zcu).toIntern(),
- .base_addr = .{ .anon_decl = array_anon_decl },
- .byte_offset = 0,
- } }),
- .len = (try zcu.intValue(Type.usize, zcu.test_functions.count())).toIntern(),
- } });
- ip.mutateVarInit(decl.val.toIntern(), new_init);
-
- // Since we are replacing the Decl's value we must perform cleanup on the
- // previous value.
- decl.val = new_val;
- decl.has_tv = true;
- }
- {
- zcu.codegen_prog_node = main_progress_node.start("Code Generation", 0);
- defer {
- zcu.codegen_prog_node.end();
- zcu.codegen_prog_node = undefined;
- }
-
- try zcu.linkerUpdateDecl(decl_index);
- }
-}
-
-pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void {
- const comp = zcu.comp;
-
- const decl = zcu.declPtr(decl_index);
-
- const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(zcu)).toSlice(&zcu.intern_pool), 0);
- defer codegen_prog_node.end();
-
- if (comp.bin_file) |lf| {
- lf.updateDecl(zcu, decl_index) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- error.AnalysisFail => {
- decl.analysis = .codegen_failure;
- },
- else => {
- const gpa = zcu.gpa;
- try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1);
- zcu.failed_analysis.putAssumeCapacityNoClobber(AnalUnit.wrap(.{ .decl = decl_index }), try ErrorMsg.create(
- gpa,
- decl.navSrcLoc(zcu),
- "unable to codegen: {s}",
- .{@errorName(err)},
- ));
- decl.analysis = .codegen_failure;
- try zcu.retryable_failures.append(zcu.gpa, AnalUnit.wrap(.{ .decl = decl_index }));
- },
- };
- } else if (zcu.llvm_object) |llvm_object| {
- if (build_options.only_c) unreachable;
- llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) {
- error.OutOfMemory => return error.OutOfMemory,
- };
- }
-}
-
-fn reportRetryableFileError(
+pub fn reportRetryableFileError(
zcu: *Zcu,
file_index: File.Index,
comptime format: []const u8,
@@ -5795,344 +4254,6 @@ pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool {
return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature);
}
-/// Shortcut for calling `intern_pool.get`.
-pub fn intern(mod: *Module, key: InternPool.Key) Allocator.Error!InternPool.Index {
- return mod.intern_pool.get(mod.gpa, key);
-}
-
-/// Shortcut for calling `intern_pool.getCoerced`.
-pub fn getCoerced(mod: *Module, val: Value, new_ty: Type) Allocator.Error!Value {
- return Value.fromInterned((try mod.intern_pool.getCoerced(mod.gpa, val.toIntern(), new_ty.toIntern())));
-}
-
-pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type {
- return Type.fromInterned((try intern(mod, .{ .int_type = .{
- .signedness = signedness,
- .bits = bits,
- } })));
-}
-
-pub fn errorIntType(mod: *Module) std.mem.Allocator.Error!Type {
- return mod.intType(.unsigned, mod.errorSetBits());
-}
-
-pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type {
- const i = try intern(mod, .{ .array_type = info });
- return Type.fromInterned(i);
-}
-
-pub fn vectorType(mod: *Module, info: InternPool.Key.VectorType) Allocator.Error!Type {
- const i = try intern(mod, .{ .vector_type = info });
- return Type.fromInterned(i);
-}
-
-pub fn optionalType(mod: *Module, child_type: InternPool.Index) Allocator.Error!Type {
- const i = try intern(mod, .{ .opt_type = child_type });
- return Type.fromInterned(i);
-}
-
-pub fn ptrType(mod: *Module, info: InternPool.Key.PtrType) Allocator.Error!Type {
- var canon_info = info;
-
- if (info.flags.size == .C) canon_info.flags.is_allowzero = true;
-
- // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee
- // type, we change it to 0 here. If this causes an assertion trip because the
- // pointee type needs to be resolved more, that needs to be done before calling
- // this ptr() function.
- if (info.flags.alignment != .none and
- info.flags.alignment == Type.fromInterned(info.child).abiAlignment(mod))
- {
- canon_info.flags.alignment = .none;
- }
-
- switch (info.flags.vector_index) {
- // Canonicalize host_size. If it matches the bit size of the pointee type,
- // we change it to 0 here. If this causes an assertion trip, the pointee type
- // needs to be resolved before calling this ptr() function.
- .none => if (info.packed_offset.host_size != 0) {
- const elem_bit_size = Type.fromInterned(info.child).bitSize(mod);
- assert(info.packed_offset.bit_offset + elem_bit_size <= info.packed_offset.host_size * 8);
- if (info.packed_offset.host_size * 8 == elem_bit_size) {
- canon_info.packed_offset.host_size = 0;
- }
- },
- .runtime => {},
- _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size),
- }
-
- return Type.fromInterned((try intern(mod, .{ .ptr_type = canon_info })));
-}
-
-/// Like `ptrType`, but if `info` specifies an `alignment`, first ensures the pointer
-/// child type's alignment is resolved so that an invalid alignment is not used.
-/// In general, prefer this function during semantic analysis.
-pub fn ptrTypeSema(zcu: *Zcu, info: InternPool.Key.PtrType) SemaError!Type {
- if (info.flags.alignment != .none) {
- _ = try Type.fromInterned(info.child).abiAlignmentAdvanced(zcu, .sema);
- }
- return zcu.ptrType(info);
-}
-
-pub fn singleMutPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
- return ptrType(mod, .{ .child = child_type.toIntern() });
-}
-
-pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
- return ptrType(mod, .{
- .child = child_type.toIntern(),
- .flags = .{
- .is_const = true,
- },
- });
-}
-
-pub fn manyConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type {
- return ptrType(mod, .{
- .child = child_type.toIntern(),
- .flags = .{
- .size = .Many,
- .is_const = true,
- },
- });
-}
-
-pub fn adjustPtrTypeChild(mod: *Module, ptr_ty: Type, new_child: Type) Allocator.Error!Type {
- var info = ptr_ty.ptrInfo(mod);
- info.child = new_child.toIntern();
- return mod.ptrType(info);
-}
-
-pub fn funcType(mod: *Module, key: InternPool.GetFuncTypeKey) Allocator.Error!Type {
- return Type.fromInterned((try mod.intern_pool.getFuncType(mod.gpa, key)));
-}
-
-/// Use this for `anyframe->T` only.
-/// For `anyframe`, use the `InternPool.Index.anyframe` tag directly.
-pub fn anyframeType(mod: *Module, payload_ty: Type) Allocator.Error!Type {
- return Type.fromInterned((try intern(mod, .{ .anyframe_type = payload_ty.toIntern() })));
-}
-
-pub fn errorUnionType(mod: *Module, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type {
- return Type.fromInterned((try intern(mod, .{ .error_union_type = .{
- .error_set_type = error_set_ty.toIntern(),
- .payload_type = payload_ty.toIntern(),
- } })));
-}
-
-pub fn singleErrorSetType(mod: *Module, name: InternPool.NullTerminatedString) Allocator.Error!Type {
- const names: *const [1]InternPool.NullTerminatedString = &name;
- const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names);
- return Type.fromInterned(new_ty);
-}
-
-/// Sorts `names` in place.
-pub fn errorSetFromUnsortedNames(
- mod: *Module,
- names: []InternPool.NullTerminatedString,
-) Allocator.Error!Type {
- std.mem.sort(
- InternPool.NullTerminatedString,
- names,
- {},
- InternPool.NullTerminatedString.indexLessThan,
- );
- const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names);
- return Type.fromInterned(new_ty);
-}
-
-/// Supports only pointers, not pointer-like optionals.
-pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
- assert(ty.zigTypeTag(mod) == .Pointer and !ty.isSlice(mod));
- assert(x != 0 or ty.isAllowzeroPtr(mod));
- const i = try intern(mod, .{ .ptr = .{
- .ty = ty.toIntern(),
- .base_addr = .int,
- .byte_offset = x,
- } });
- return Value.fromInterned(i);
-}
-
-/// Creates an enum tag value based on the integer tag value.
-pub fn enumValue(mod: *Module, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value {
- if (std.debug.runtime_safety) {
- const tag = ty.zigTypeTag(mod);
- assert(tag == .Enum);
- }
- const i = try intern(mod, .{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = tag_int,
- } });
- return Value.fromInterned(i);
-}
-
-/// Creates an enum tag value based on the field index according to source code
-/// declaration order.
-pub fn enumValueFieldIndex(mod: *Module, ty: Type, field_index: u32) Allocator.Error!Value {
- const ip = &mod.intern_pool;
- const gpa = mod.gpa;
- const enum_type = ip.loadEnumType(ty.toIntern());
-
- if (enum_type.values.len == 0) {
- // Auto-numbered fields.
- return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = try ip.get(gpa, .{ .int = .{
- .ty = enum_type.tag_ty,
- .storage = .{ .u64 = field_index },
- } }),
- } })));
- }
-
- return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{
- .ty = ty.toIntern(),
- .int = enum_type.values.get(ip)[field_index],
- } })));
-}
-
-pub fn undefValue(mod: *Module, ty: Type) Allocator.Error!Value {
- return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() })));
-}
-
-pub fn undefRef(mod: *Module, ty: Type) Allocator.Error!Air.Inst.Ref {
- return Air.internedToRef((try mod.undefValue(ty)).toIntern());
-}
-
-pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
- if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted);
- if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted);
- var limbs_buffer: [4]usize = undefined;
- var big_int = BigIntMutable.init(&limbs_buffer, x);
- return intValue_big(mod, ty, big_int.toConst());
-}
-
-pub fn intRef(mod: *Module, ty: Type, x: anytype) Allocator.Error!Air.Inst.Ref {
- return Air.internedToRef((try mod.intValue(ty, x)).toIntern());
-}
-
-pub fn intValue_big(mod: *Module, ty: Type, x: BigIntConst) Allocator.Error!Value {
- const i = try intern(mod, .{ .int = .{
- .ty = ty.toIntern(),
- .storage = .{ .big_int = x },
- } });
- return Value.fromInterned(i);
-}
-
-pub fn intValue_u64(mod: *Module, ty: Type, x: u64) Allocator.Error!Value {
- const i = try intern(mod, .{ .int = .{
- .ty = ty.toIntern(),
- .storage = .{ .u64 = x },
- } });
- return Value.fromInterned(i);
-}
-
-pub fn intValue_i64(mod: *Module, ty: Type, x: i64) Allocator.Error!Value {
- const i = try intern(mod, .{ .int = .{
- .ty = ty.toIntern(),
- .storage = .{ .i64 = x },
- } });
- return Value.fromInterned(i);
-}
-
-pub fn unionValue(mod: *Module, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value {
- const i = try intern(mod, .{ .un = .{
- .ty = union_ty.toIntern(),
- .tag = tag.toIntern(),
- .val = val.toIntern(),
- } });
- return Value.fromInterned(i);
-}
-
-/// This function casts the float representation down to the representation of the type, potentially
-/// losing data if the representation wasn't correct.
-pub fn floatValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value {
- const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(mod.getTarget())) {
- 16 => .{ .f16 = @as(f16, @floatCast(x)) },
- 32 => .{ .f32 = @as(f32, @floatCast(x)) },
- 64 => .{ .f64 = @as(f64, @floatCast(x)) },
- 80 => .{ .f80 = @as(f80, @floatCast(x)) },
- 128 => .{ .f128 = @as(f128, @floatCast(x)) },
- else => unreachable,
- };
- const i = try intern(mod, .{ .float = .{
- .ty = ty.toIntern(),
- .storage = storage,
- } });
- return Value.fromInterned(i);
-}
-
-pub fn nullValue(mod: *Module, opt_ty: Type) Allocator.Error!Value {
- const ip = &mod.intern_pool;
- assert(ip.isOptionalType(opt_ty.toIntern()));
- const result = try ip.get(mod.gpa, .{ .opt = .{
- .ty = opt_ty.toIntern(),
- .val = .none,
- } });
- return Value.fromInterned(result);
-}
-
-pub fn smallestUnsignedInt(mod: *Module, max: u64) Allocator.Error!Type {
- return intType(mod, .unsigned, Type.smallestUnsignedBits(max));
-}
-
-/// Returns the smallest possible integer type containing both `min` and
-/// `max`. Asserts that neither value is undef.
-/// TODO: if #3806 is implemented, this becomes trivial
-pub fn intFittingRange(mod: *Module, min: Value, max: Value) !Type {
- assert(!min.isUndef(mod));
- assert(!max.isUndef(mod));
-
- if (std.debug.runtime_safety) {
- assert(Value.order(min, max, mod).compare(.lte));
- }
-
- const sign = min.orderAgainstZero(mod) == .lt;
-
- const min_val_bits = intBitsForValue(mod, min, sign);
- const max_val_bits = intBitsForValue(mod, max, sign);
-
- return mod.intType(
- if (sign) .signed else .unsigned,
- @max(min_val_bits, max_val_bits),
- );
-}
-
-/// Given a value representing an integer, returns the number of bits necessary to represent
-/// this value in an integer. If `sign` is true, returns the number of bits necessary in a
-/// twos-complement integer; otherwise in an unsigned integer.
-/// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true.
-pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 {
- assert(!val.isUndef(mod));
-
- const key = mod.intern_pool.indexToKey(val.toIntern());
- switch (key.int.storage) {
- .i64 => |x| {
- if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted) + @intFromBool(sign);
- assert(sign);
- // Protect against overflow in the following negation.
- if (x == std.math.minInt(i64)) return 64;
- return Type.smallestUnsignedBits(@as(u64, @intCast(-(x + 1)))) + 1;
- },
- .u64 => |x| {
- return Type.smallestUnsignedBits(x) + @intFromBool(sign);
- },
- .big_int => |big| {
- if (big.positive) return @as(u16, @intCast(big.bitCountAbs() + @intFromBool(sign)));
-
- // Zero is still a possibility, in which case unsigned is fine
- if (big.eqlZero()) return 0;
-
- return @as(u16, @intCast(big.bitCountTwosComp()));
- },
- .lazy_align => |lazy_ty| {
- return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(mod).toByteUnits() orelse 0) + @intFromBool(sign);
- },
- .lazy_size => |lazy_ty| {
- return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(mod)) + @intFromBool(sign);
- },
- }
-}
-
pub const AtomicPtrAlignmentError = error{
FloatTooBig,
IntTooBig,
@@ -6371,101 +4492,6 @@ pub const UnionLayout = struct {
padding: u32,
};
-pub fn getUnionLayout(mod: *Module, loaded_union: InternPool.LoadedUnionType) UnionLayout {
- const ip = &mod.intern_pool;
- assert(loaded_union.haveLayout(ip));
- var most_aligned_field: u32 = undefined;
- var most_aligned_field_size: u64 = undefined;
- var biggest_field: u32 = undefined;
- var payload_size: u64 = 0;
- var payload_align: Alignment = .@"1";
- for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
- if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue;
-
- const explicit_align = loaded_union.fieldAlign(ip, field_index);
- const field_align = if (explicit_align != .none)
- explicit_align
- else
- Type.fromInterned(field_ty).abiAlignment(mod);
- const field_size = Type.fromInterned(field_ty).abiSize(mod);
- if (field_size > payload_size) {
- payload_size = field_size;
- biggest_field = @intCast(field_index);
- }
- if (field_align.compare(.gte, payload_align)) {
- payload_align = field_align;
- most_aligned_field = @intCast(field_index);
- most_aligned_field_size = field_size;
- }
- }
- const have_tag = loaded_union.flagsPtr(ip).runtime_tag.hasTag();
- if (!have_tag or !Type.fromInterned(loaded_union.enum_tag_ty).hasRuntimeBits(mod)) {
- return .{
- .abi_size = payload_align.forward(payload_size),
- .abi_align = payload_align,
- .most_aligned_field = most_aligned_field,
- .most_aligned_field_size = most_aligned_field_size,
- .biggest_field = biggest_field,
- .payload_size = payload_size,
- .payload_align = payload_align,
- .tag_align = .none,
- .tag_size = 0,
- .padding = 0,
- };
- }
-
- const tag_size = Type.fromInterned(loaded_union.enum_tag_ty).abiSize(mod);
- const tag_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(mod).max(.@"1");
- return .{
- .abi_size = loaded_union.size(ip).*,
- .abi_align = tag_align.max(payload_align),
- .most_aligned_field = most_aligned_field,
- .most_aligned_field_size = most_aligned_field_size,
- .biggest_field = biggest_field,
- .payload_size = payload_size,
- .payload_align = payload_align,
- .tag_align = tag_align,
- .tag_size = tag_size,
- .padding = loaded_union.padding(ip).*,
- };
-}
-
-pub fn unionAbiSize(mod: *Module, loaded_union: InternPool.LoadedUnionType) u64 {
- return mod.getUnionLayout(loaded_union).abi_size;
-}
-
-/// Returns 0 if the union is represented with 0 bits at runtime.
-pub fn unionAbiAlignment(mod: *Module, loaded_union: InternPool.LoadedUnionType) Alignment {
- const ip = &mod.intern_pool;
- const have_tag = loaded_union.flagsPtr(ip).runtime_tag.hasTag();
- var max_align: Alignment = .none;
- if (have_tag) max_align = Type.fromInterned(loaded_union.enum_tag_ty).abiAlignment(mod);
- for (loaded_union.field_types.get(ip), 0..) |field_ty, field_index| {
- if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue;
-
- const field_align = mod.unionFieldNormalAlignment(loaded_union, @intCast(field_index));
- max_align = max_align.max(field_align);
- }
- return max_align;
-}
-
-/// Returns the field alignment of a non-packed union. Asserts the layout is not packed.
-pub fn unionFieldNormalAlignment(zcu: *Zcu, loaded_union: InternPool.LoadedUnionType, field_index: u32) Alignment {
- return zcu.unionFieldNormalAlignmentAdvanced(loaded_union, field_index, .normal) catch unreachable;
-}
-
-/// Returns the field alignment of a non-packed union. Asserts the layout is not packed.
-/// If `strat` is `.sema`, may perform type resolution.
-pub fn unionFieldNormalAlignmentAdvanced(zcu: *Zcu, loaded_union: InternPool.LoadedUnionType, field_index: u32, strat: Type.ResolveStrat) SemaError!Alignment {
- const ip = &zcu.intern_pool;
- assert(loaded_union.flagsPtr(ip).layout != .@"packed");
- const field_align = loaded_union.fieldAlign(ip, field_index);
- if (field_align != .none) return field_align;
- const field_ty = Type.fromInterned(loaded_union.field_types.get(ip)[field_index]);
- if (field_ty.isNoReturn(zcu)) return .none;
- return (try field_ty.abiAlignmentAdvanced(zcu, strat.toLazy())).scalar;
-}
-
/// Returns the index of the active field, given the current tag value
pub fn unionTagFieldIndex(mod: *Module, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 {
const ip = &mod.intern_pool;
@@ -6474,63 +4500,6 @@ pub fn unionTagFieldIndex(mod: *Module, loaded_union: InternPool.LoadedUnionType
return loaded_union.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern());
}
-/// Returns the field alignment of a non-packed struct. Asserts the layout is not packed.
-pub fn structFieldAlignment(
- zcu: *Zcu,
- explicit_alignment: InternPool.Alignment,
- field_ty: Type,
- layout: std.builtin.Type.ContainerLayout,
-) Alignment {
- return zcu.structFieldAlignmentAdvanced(explicit_alignment, field_ty, layout, .normal) catch unreachable;
-}
-
-/// Returns the field alignment of a non-packed struct. Asserts the layout is not packed.
-/// If `strat` is `.sema`, may perform type resolution.
-pub fn structFieldAlignmentAdvanced(
- zcu: *Zcu,
- explicit_alignment: InternPool.Alignment,
- field_ty: Type,
- layout: std.builtin.Type.ContainerLayout,
- strat: Type.ResolveStrat,
-) SemaError!Alignment {
- assert(layout != .@"packed");
- if (explicit_alignment != .none) return explicit_alignment;
- const ty_abi_align = (try field_ty.abiAlignmentAdvanced(zcu, strat.toLazy())).scalar;
- switch (layout) {
- .@"packed" => unreachable,
- .auto => if (zcu.getTarget().ofmt != .c) return ty_abi_align,
- .@"extern" => {},
- }
- // extern
- if (field_ty.isAbiInt(zcu) and field_ty.intInfo(zcu).bits >= 128) {
- return ty_abi_align.maxStrict(.@"16");
- }
- return ty_abi_align;
-}
-
-/// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets
-/// into the packed struct InternPool data rather than computing this on the
-/// fly, however it was found to perform worse when measured on real world
-/// projects.
-pub fn structPackedFieldBitOffset(
- mod: *Module,
- struct_type: InternPool.LoadedStructType,
- field_index: u32,
-) u16 {
- const ip = &mod.intern_pool;
- assert(struct_type.layout == .@"packed");
- assert(struct_type.haveLayout(ip));
- var bit_sum: u64 = 0;
- for (0..struct_type.field_types.len) |i| {
- if (i == field_index) {
- return @intCast(bit_sum);
- }
- const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]);
- bit_sum += field_ty.bitSize(mod);
- }
- unreachable; // index out of bounds
-}
-
pub const ResolvedReference = struct {
referencer: AnalUnit,
src: LazySrcLoc,
@@ -6564,33 +4533,6 @@ pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, Resolved
return result;
}
-pub fn getBuiltin(zcu: *Zcu, name: []const u8) Allocator.Error!Air.Inst.Ref {
- const decl_index = try zcu.getBuiltinDecl(name);
- zcu.ensureDeclAnalyzed(decl_index) catch @panic("std.builtin is corrupt");
- return Air.internedToRef(zcu.declPtr(decl_index).val.toIntern());
-}
-
-pub fn getBuiltinDecl(zcu: *Zcu, name: []const u8) Allocator.Error!InternPool.DeclIndex {
- const gpa = zcu.gpa;
- const ip = &zcu.intern_pool;
- const std_file_imported = zcu.importPkg(zcu.std_mod) catch @panic("failed to import lib/std.zig");
- const std_file_root_decl = zcu.fileRootDecl(std_file_imported.file_index).unwrap().?;
- const std_namespace = zcu.declPtr(std_file_root_decl).getOwnedInnerNamespace(zcu).?;
- const builtin_str = try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls);
- const builtin_decl = std_namespace.decls.getKeyAdapted(builtin_str, Zcu.DeclAdapter{ .zcu = zcu }) orelse @panic("lib/std.zig is corrupt and missing 'builtin'");
- zcu.ensureDeclAnalyzed(builtin_decl) catch @panic("std.builtin is corrupt");
- const builtin_namespace = zcu.declPtr(builtin_decl).getInnerNamespace(zcu) orelse @panic("std.builtin is corrupt");
- const name_str = try ip.getOrPutString(gpa, name, .no_embedded_nulls);
- return builtin_namespace.decls.getKeyAdapted(name_str, Zcu.DeclAdapter{ .zcu = zcu }) orelse @panic("lib/std/builtin.zig is corrupt");
-}
-
-pub fn getBuiltinType(zcu: *Zcu, name: []const u8) Allocator.Error!Type {
- const ty_inst = try zcu.getBuiltin(name);
- const ty = Type.fromInterned(ty_inst.toInterned() orelse @panic("std.builtin is corrupt"));
- ty.resolveFully(zcu) catch @panic("std.builtin is corrupt");
- return ty;
-}
-
pub fn fileByIndex(zcu: *const Zcu, i: File.Index) *File {
return zcu.import_table.values()[@intFromEnum(i)];
}