blob 103cbaaa (189114B) - Raw
1 //! This type provides a wrapper around a `*Zcu` for uses which require a thread `Id`. 2 //! Any operation which mutates `InternPool` state lives here rather than on `Zcu`. 3 4 const std = @import("std"); 5 const Allocator = std.mem.Allocator; 6 const assert = std.debug.assert; 7 const Ast = std.zig.Ast; 8 const AstGen = std.zig.AstGen; 9 const BigIntConst = std.math.big.int.Const; 10 const BigIntMutable = std.math.big.int.Mutable; 11 const Cache = std.Build.Cache; 12 const log = std.log.scoped(.zcu); 13 const mem = std.mem; 14 const Zir = std.zig.Zir; 15 const Zoir = std.zig.Zoir; 16 const ZonGen = std.zig.ZonGen; 17 const Io = std.Io; 18 19 const Air = @import("../Air.zig"); 20 const Builtin = @import("../Builtin.zig"); 21 const build_options = @import("build_options"); 22 const builtin = @import("builtin"); 23 const dev = @import("../dev.zig"); 24 const InternPool = @import("../InternPool.zig"); 25 const AnalUnit = InternPool.AnalUnit; 26 const introspect = @import("../introspect.zig"); 27 const Module = @import("../Package.zig").Module; 28 const Sema = @import("../Sema.zig"); 29 const target_util = @import("../target.zig"); 30 const trace = @import("../tracy.zig").trace; 31 const Type = @import("../Type.zig"); 32 const Value = @import("../Value.zig"); 33 const Zcu = @import("../Zcu.zig"); 34 const Compilation = @import("../Compilation.zig"); 35 const codegen = @import("../codegen.zig"); 36 const crash_report = @import("../crash_report.zig"); 37 38 zcu: *Zcu, 39 40 /// Dense, per-thread unique index. 41 tid: Id, 42 43 pub const IdBacking = u7; 44 pub const Id = if (InternPool.single_threaded) enum { main } else enum(IdBacking) { main, _ }; 45 46 pub fn activate(zcu: *Zcu, tid: Id) Zcu.PerThread { 47 zcu.intern_pool.activate(); 48 return .{ .zcu = zcu, .tid = tid }; 49 } 50 51 pub fn deactivate(pt: Zcu.PerThread) void { 52 pt.zcu.intern_pool.deactivate(); 53 } 54 55 fn deinitFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void { 56 const zcu = pt.zcu; 57 const gpa = zcu.gpa; 58 const file = zcu.fileByIndex(file_index); 59 log.debug("deinit File {f}", .{file.path.fmt(zcu.comp)}); 60 file.path.deinit(gpa); 61 file.unload(gpa); 62 if (file.prev_zir) |prev_zir| { 63 prev_zir.deinit(gpa); 64 gpa.destroy(prev_zir); 65 } 66 file.* = undefined; 67 } 68 69 pub fn destroyFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) void { 70 const gpa = pt.zcu.gpa; 71 const file = pt.zcu.fileByIndex(file_index); 72 pt.deinitFile(file_index); 73 gpa.destroy(file); 74 } 75 76 /// Ensures that `file` has up-to-date ZIR. If not, loads the ZIR cache or runs 77 /// AstGen as needed. Also updates `file.status`. Does not assume that `file.mod` 78 /// is populated. Does not return `error.AnalysisFail` on AstGen failures. 79 pub fn updateFile( 80 pt: Zcu.PerThread, 81 file_index: Zcu.File.Index, 82 file: *Zcu.File, 83 ) !void { 84 dev.check(.ast_gen); 85 86 const tracy = trace(@src()); 87 defer tracy.end(); 88 89 const zcu = pt.zcu; 90 const comp = zcu.comp; 91 const gpa = zcu.gpa; 92 const io = comp.io; 93 94 // In any case we need to examine the stat of the file to determine the course of action. 95 var source_file = f: { 96 const dir, const sub_path = file.path.openInfo(comp.dirs); 97 break :f try dir.openFile(io, sub_path, .{}); 98 }; 99 defer source_file.close(io); 100 101 const stat = try source_file.stat(io); 102 103 const want_local_cache = switch (file.path.root) { 104 .none, .local_cache => true, 105 .global_cache, .zig_lib => false, 106 }; 107 108 const hex_digest: Cache.HexDigest = d: { 109 var h: Cache.HashHelper = .{}; 110 // As well as the file path, we also include the compiler version in case of backwards-incompatible ZIR changes. 111 file.path.addToHasher(&h.hasher); 112 h.addBytes(build_options.version); 113 h.add(builtin.zig_backend); 114 break :d h.final(); 115 }; 116 117 const cache_directory = if (want_local_cache) zcu.local_zir_cache else zcu.global_zir_cache; 118 const zir_dir = cache_directory.handle; 119 120 // Determine whether we need to reload the file from disk and redo parsing and AstGen. 121 var lock: Io.File.Lock = switch (file.status) { 122 .never_loaded, .retryable_failure => lock: { 123 // First, load the cached ZIR code, if any. 124 log.debug("AstGen checking cache: {f} (local={}, digest={s})", .{ 125 file.path.fmt(comp), want_local_cache, &hex_digest, 126 }); 127 128 break :lock .shared; 129 }, 130 .astgen_failure, .success => lock: { 131 const unchanged_metadata = 132 stat.size == file.stat.size and 133 stat.mtime.nanoseconds == file.stat.mtime.nanoseconds and 134 stat.inode == file.stat.inode; 135 136 if (unchanged_metadata) { 137 log.debug("unmodified metadata of file: {f}", .{file.path.fmt(comp)}); 138 return; 139 } 140 141 log.debug("metadata changed: {f}", .{file.path.fmt(comp)}); 142 143 break :lock .exclusive; 144 }, 145 }; 146 147 // The old compile error, if any, is no longer relevant. 148 pt.lockAndClearFileCompileError(file_index, file); 149 150 // If `zir` is not null, and `prev_zir` is null, then `TrackedInst`s are associated with `zir`. 151 // We need to keep it around! 152 // As an optimization, also check `loweringFailed`; if true, but `prev_zir == null`, then this 153 // file has never passed AstGen, so we actually need not cache the old ZIR. 154 if (file.zir != null and file.prev_zir == null and !file.zir.?.loweringFailed()) { 155 assert(file.prev_zir == null); 156 const prev_zir_ptr = try gpa.create(Zir); 157 file.prev_zir = prev_zir_ptr; 158 prev_zir_ptr.* = file.zir.?; 159 file.zir = null; 160 } 161 162 // If ZOIR is changing, then we need to invalidate dependencies on it 163 if (file.zoir != null) file.zoir_invalidated = true; 164 165 // We're going to re-load everything, so unload source, AST, ZIR, ZOIR. 166 file.unload(gpa); 167 168 // We ask for a lock in order to coordinate with other zig processes. 169 // If another process is already working on this file, we will get the cached 170 // version. Likewise if we're working on AstGen and another process asks for 171 // the cached file, they'll get it. 172 const cache_file = while (true) { 173 break zir_dir.createFile(io, &hex_digest, .{ 174 .read = true, 175 .truncate = false, 176 .lock = lock, 177 }) catch |err| switch (err) { 178 error.NotDir => unreachable, // no dir components 179 error.BadPathName => unreachable, // it's a hex encoded name 180 error.NameTooLong => unreachable, // it's a fixed size name 181 error.PipeBusy => unreachable, // it's not a pipe 182 error.NoDevice => unreachable, // it's not a pipe 183 error.WouldBlock => unreachable, // not asking for non-blocking I/O 184 error.FileNotFound => { 185 // There are no dir components, so the only possibility should 186 // be that the directory behind the handle has been deleted, 187 // however we have observed on macOS two processes racing to do 188 // openat() with O_CREAT manifest in ENOENT. 189 // 190 // As a workaround, we retry with exclusive=true which 191 // disambiguates by returning EEXIST, indicating original 192 // failure was a race, or ENOENT, indicating deletion of the 193 // directory of our open handle. 194 if (!builtin.os.tag.isDarwin()) { 195 std.process.fatal("cache directory '{f}' unexpectedly removed during compiler execution", .{ 196 cache_directory, 197 }); 198 } 199 break zir_dir.createFile(io, &hex_digest, .{ 200 .read = true, 201 .truncate = false, 202 .lock = lock, 203 .exclusive = true, 204 }) catch |excl_err| switch (excl_err) { 205 error.PathAlreadyExists => continue, 206 error.FileNotFound => { 207 std.process.fatal("cache directory '{f}' unexpectedly removed during compiler execution", .{ 208 cache_directory, 209 }); 210 }, 211 else => |e| return e, 212 }; 213 }, 214 215 else => |e| return e, // Retryable errors are handled at callsite. 216 }; 217 }; 218 defer cache_file.close(io); 219 220 // Under `--time-report`, ignore cache hits; do the work anyway for those juicy numbers. 221 const ignore_hit = comp.time_report != null; 222 223 const need_update = while (true) { 224 const result = switch (file.getMode()) { 225 inline else => |mode| try loadZirZoirCache(zcu, cache_file, stat, file, mode), 226 }; 227 switch (result) { 228 .success => if (!ignore_hit) { 229 log.debug("AstGen cached success: {f}", .{file.path.fmt(comp)}); 230 break false; 231 }, 232 .invalid => {}, 233 .truncated => log.warn("unexpected EOF reading cached ZIR for {f}", .{file.path.fmt(comp)}), 234 .stale => log.debug("AstGen cache stale: {f}", .{file.path.fmt(comp)}), 235 } 236 237 // If we already have the exclusive lock then it is our job to update. 238 if (builtin.os.tag == .wasi or lock == .exclusive) break true; 239 // Otherwise, unlock to give someone a chance to get the exclusive lock 240 // and then upgrade to an exclusive lock. 241 cache_file.unlock(io); 242 lock = .exclusive; 243 try cache_file.lock(io, lock); 244 }; 245 246 if (need_update) { 247 var cache_file_writer: Io.File.Writer = .init(cache_file, io, &.{}); 248 249 if (stat.size > std.math.maxInt(u32)) 250 return error.FileTooBig; 251 252 const source = try gpa.allocSentinel(u8, @intCast(stat.size), 0); 253 defer if (file.source == null) gpa.free(source); 254 var source_fr = source_file.reader(io, &.{}); 255 source_fr.size = stat.size; 256 source_fr.interface.readSliceAll(source) catch |err| switch (err) { 257 error.ReadFailed => return source_fr.err.?, 258 error.EndOfStream => return error.UnexpectedEndOfFile, 259 }; 260 261 file.source = source; 262 263 var timer = comp.startTimer(); 264 // Any potential AST errors are converted to ZIR errors when we run AstGen/ZonGen. 265 file.tree = try Ast.parse(gpa, source, file.getMode()); 266 if (timer.finish()) |ns_parse| { 267 comp.mutex.lockUncancelable(io); 268 defer comp.mutex.unlock(io); 269 comp.time_report.?.stats.cpu_ns_parse += ns_parse; 270 } 271 272 timer = comp.startTimer(); 273 switch (file.getMode()) { 274 .zig => { 275 file.zir = try AstGen.generate(gpa, file.tree.?); 276 Zcu.saveZirCache(gpa, &cache_file_writer, stat, file.zir.?) catch |err| switch (err) { 277 error.OutOfMemory => |e| return e, 278 else => log.warn("unable to write cached ZIR code for {f} to {f}{s}: {t}", .{ 279 file.path.fmt(comp), cache_directory, &hex_digest, err, 280 }), 281 }; 282 }, 283 .zon => { 284 file.zoir = try ZonGen.generate(gpa, file.tree.?, .{}); 285 Zcu.saveZoirCache(&cache_file_writer, stat, file.zoir.?) catch |err| { 286 log.warn("unable to write cached ZOIR code for {f} to {f}{s}: {t}", .{ 287 file.path.fmt(comp), cache_directory, &hex_digest, err, 288 }); 289 }; 290 }, 291 } 292 293 cache_file_writer.end() catch |err| switch (err) { 294 error.WriteFailed => return cache_file_writer.err.?, 295 else => |e| return e, 296 }; 297 298 if (timer.finish()) |ns_astgen| { 299 comp.mutex.lockUncancelable(io); 300 defer comp.mutex.unlock(io); 301 comp.time_report.?.stats.cpu_ns_astgen += ns_astgen; 302 } 303 304 log.debug("AstGen fresh success: {f}", .{file.path.fmt(comp)}); 305 } 306 307 file.stat = .{ 308 .size = stat.size, 309 .inode = stat.inode, 310 .mtime = stat.mtime, 311 }; 312 313 // Now, `zir` or `zoir` is definitely populated and up-to-date. 314 // Mark file successes/failures as needed. 315 316 switch (file.getMode()) { 317 .zig => { 318 if (file.zir.?.hasCompileErrors()) { 319 comp.mutex.lockUncancelable(io); 320 defer comp.mutex.unlock(io); 321 try zcu.failed_files.putNoClobber(gpa, file_index, null); 322 } 323 if (file.zir.?.loweringFailed()) { 324 file.status = .astgen_failure; 325 } else { 326 file.status = .success; 327 } 328 }, 329 .zon => { 330 if (file.zoir.?.hasCompileErrors()) { 331 file.status = .astgen_failure; 332 comp.mutex.lockUncancelable(io); 333 defer comp.mutex.unlock(io); 334 try zcu.failed_files.putNoClobber(gpa, file_index, null); 335 } else { 336 file.status = .success; 337 } 338 }, 339 } 340 341 switch (file.status) { 342 .never_loaded => unreachable, 343 .retryable_failure => unreachable, 344 .astgen_failure, .success => {}, 345 } 346 } 347 348 fn loadZirZoirCache( 349 zcu: *Zcu, 350 cache_file: Io.File, 351 stat: Io.File.Stat, 352 file: *Zcu.File, 353 comptime mode: Ast.Mode, 354 ) !enum { success, invalid, truncated, stale } { 355 assert(file.getMode() == mode); 356 357 const gpa = zcu.gpa; 358 const io = zcu.comp.io; 359 360 const Header = switch (mode) { 361 .zig => Zir.Header, 362 .zon => Zoir.Header, 363 }; 364 365 var buffer: [2000]u8 = undefined; 366 var cache_fr = cache_file.reader(io, &buffer); 367 cache_fr.size = stat.size; 368 const cache_br = &cache_fr.interface; 369 370 // First we read the header to determine the lengths of arrays. 371 const header = (cache_br.takeStructPointer(Header) catch |err| switch (err) { 372 error.ReadFailed => return cache_fr.err.?, 373 // This can happen if Zig bails out of this function between creating 374 // the cached file and writing it. 375 error.EndOfStream => return .invalid, 376 else => |e| return e, 377 }).*; 378 379 const unchanged_metadata = 380 stat.size == header.stat_size and 381 stat.mtime.nanoseconds == header.stat_mtime and 382 stat.inode == header.stat_inode; 383 384 if (!unchanged_metadata) { 385 return .stale; 386 } 387 388 switch (mode) { 389 .zig => file.zir = Zcu.loadZirCacheBody(gpa, header, cache_br) catch |err| switch (err) { 390 error.ReadFailed => return cache_fr.err.?, 391 error.EndOfStream => return .truncated, 392 else => |e| return e, 393 }, 394 .zon => file.zoir = Zcu.loadZoirCacheBody(gpa, header, cache_br) catch |err| switch (err) { 395 error.ReadFailed => return cache_fr.err.?, 396 error.EndOfStream => return .truncated, 397 else => |e| return e, 398 }, 399 } 400 401 return .success; 402 } 403 404 const UpdatedFile = struct { 405 file: *Zcu.File, 406 inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), 407 }; 408 409 fn cleanupUpdatedFiles(gpa: Allocator, updated_files: *std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile)) void { 410 for (updated_files.values()) |*elem| elem.inst_map.deinit(gpa); 411 updated_files.deinit(gpa); 412 } 413 414 pub fn updateZirRefs(pt: Zcu.PerThread) Allocator.Error!void { 415 assert(pt.tid == .main); 416 const zcu = pt.zcu; 417 const comp = zcu.comp; 418 const ip = &zcu.intern_pool; 419 const gpa = comp.gpa; 420 const io = comp.io; 421 422 // We need to visit every updated File for every TrackedInst in InternPool. 423 // This only includes Zig files; ZON files are omitted. 424 var updated_files: std.AutoArrayHashMapUnmanaged(Zcu.File.Index, UpdatedFile) = .empty; 425 defer cleanupUpdatedFiles(gpa, &updated_files); 426 427 for (zcu.import_table.keys()) |file_index| { 428 if (!zcu.alive_files.contains(file_index)) continue; 429 const file = zcu.fileByIndex(file_index); 430 assert(file.status == .success); 431 if (file.module_changed) { 432 try updated_files.putNoClobber(gpa, file_index, .{ 433 .file = file, 434 // We intentionally don't map any instructions here; that's the point, the whole file is outdated! 435 .inst_map = .{}, 436 }); 437 continue; 438 } 439 switch (file.getMode()) { 440 .zig => {}, // logic below 441 .zon => { 442 if (file.zoir_invalidated) { 443 try zcu.markDependeeOutdated(.not_marked_po, .{ .zon_file = file_index }); 444 file.zoir_invalidated = false; 445 } 446 continue; 447 }, 448 } 449 const old_zir = file.prev_zir orelse continue; 450 const new_zir = file.zir.?; 451 const gop = try updated_files.getOrPut(gpa, file_index); 452 assert(!gop.found_existing); 453 gop.value_ptr.* = .{ 454 .file = file, 455 .inst_map = .{}, 456 }; 457 try Zcu.mapOldZirToNew(gpa, old_zir.*, new_zir, &gop.value_ptr.inst_map); 458 } 459 460 if (updated_files.count() == 0) 461 return; 462 463 for (ip.locals, 0..) |*local, tid| { 464 const tracked_insts_list = local.getMutableTrackedInsts(gpa, io); 465 for (tracked_insts_list.viewAllowEmpty().items(.@"0"), 0..) |*tracked_inst, tracked_inst_unwrapped_index| { 466 const file_index = tracked_inst.file; 467 const updated_file = updated_files.get(file_index) orelse continue; 468 469 const file = updated_file.file; 470 471 const old_inst = tracked_inst.inst.unwrap() orelse continue; // we can't continue tracking lost insts 472 const tracked_inst_index = (InternPool.TrackedInst.Index.Unwrapped{ 473 .tid = @enumFromInt(tid), 474 .index = @intCast(tracked_inst_unwrapped_index), 475 }).wrap(ip); 476 const new_inst = updated_file.inst_map.get(old_inst) orelse { 477 // Tracking failed for this instruction due to changes in the ZIR. 478 // Invalidate associated `src_hash` deps. 479 log.debug("tracking failed for %{d}", .{old_inst}); 480 tracked_inst.inst = .lost; 481 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index }); 482 continue; 483 }; 484 tracked_inst.inst = InternPool.TrackedInst.MaybeLost.ZirIndex.wrap(new_inst); 485 486 const old_zir = file.prev_zir.?.*; 487 const new_zir = file.zir.?; 488 const old_tag = old_zir.instructions.items(.tag)[@intFromEnum(old_inst)]; 489 const old_data = old_zir.instructions.items(.data)[@intFromEnum(old_inst)]; 490 491 switch (old_tag) { 492 .declaration => { 493 const old_line = old_zir.getDeclaration(old_inst).src_line; 494 const new_line = new_zir.getDeclaration(new_inst).src_line; 495 if (old_line != new_line) { 496 try comp.queueJob(.{ .update_line_number = tracked_inst_index }); 497 } 498 }, 499 else => {}, 500 } 501 502 if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { 503 if (new_zir.getAssociatedSrcHash(new_inst)) |new_hash| { 504 if (std.zig.srcHashEql(old_hash, new_hash)) { 505 break :hash_changed; 506 } 507 log.debug("hash for (%{d} -> %{d}) changed: {x} -> {x}", .{ 508 old_inst, new_inst, &old_hash, &new_hash, 509 }); 510 } 511 // The source hash associated with this instruction changed - invalidate relevant dependencies. 512 try zcu.markDependeeOutdated(.not_marked_po, .{ .src_hash = tracked_inst_index }); 513 } 514 515 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies. 516 const has_namespace = switch (old_tag) { 517 .extended => switch (old_data.extended.opcode) { 518 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true, 519 else => false, 520 }, 521 else => false, 522 }; 523 if (!has_namespace) continue; 524 525 // Value is whether the declaration is `pub`. 526 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, bool) = .empty; 527 defer old_names.deinit(zcu.gpa); 528 { 529 var it = old_zir.declIterator(old_inst); 530 while (it.next()) |decl_inst| { 531 const old_decl = old_zir.getDeclaration(decl_inst); 532 if (old_decl.name == .empty) continue; 533 const name_ip = try zcu.intern_pool.getOrPutString( 534 zcu.gpa, 535 io, 536 pt.tid, 537 old_zir.nullTerminatedString(old_decl.name), 538 .no_embedded_nulls, 539 ); 540 try old_names.put(zcu.gpa, name_ip, old_decl.is_pub); 541 } 542 } 543 var any_change = false; 544 { 545 var it = new_zir.declIterator(new_inst); 546 while (it.next()) |decl_inst| { 547 const new_decl = new_zir.getDeclaration(decl_inst); 548 if (new_decl.name == .empty) continue; 549 const name_ip = try zcu.intern_pool.getOrPutString( 550 zcu.gpa, 551 io, 552 pt.tid, 553 new_zir.nullTerminatedString(new_decl.name), 554 .no_embedded_nulls, 555 ); 556 if (old_names.fetchSwapRemove(name_ip)) |kv| { 557 if (kv.value == new_decl.is_pub) continue; 558 } 559 // Name added, or changed whether it's pub 560 any_change = true; 561 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{ 562 .namespace = tracked_inst_index, 563 .name = name_ip, 564 } }); 565 } 566 } 567 // The only elements remaining in `old_names` now are any names which were removed. 568 for (old_names.keys()) |name_ip| { 569 any_change = true; 570 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace_name = .{ 571 .namespace = tracked_inst_index, 572 .name = name_ip, 573 } }); 574 } 575 576 if (any_change) { 577 try zcu.markDependeeOutdated(.not_marked_po, .{ .namespace = tracked_inst_index }); 578 } 579 } 580 } 581 582 try ip.rehashTrackedInsts(gpa, io, pt.tid); 583 584 for (updated_files.keys(), updated_files.values()) |file_index, updated_file| { 585 const file = updated_file.file; 586 587 if (file.prev_zir) |prev_zir| { 588 prev_zir.deinit(gpa); 589 gpa.destroy(prev_zir); 590 file.prev_zir = null; 591 } 592 file.module_changed = false; 593 594 // For every file which has changed, re-scan the namespace of the file's root struct type. 595 // These types are special-cased because they don't have an enclosing declaration which will 596 // be re-analyzed (causing the struct's namespace to be re-scanned). It's fine to do this 597 // now because this work is fast (no actual Sema work is happening, we're just updating the 598 // namespace contents). We must do this after updating ZIR refs above, since `scanNamespace` 599 // will track some instructions. 600 try pt.updateFileNamespace(file_index); 601 } 602 } 603 604 /// Ensures that `zcu.fileRootType` on this `file_index` gives an up-to-date answer. 605 /// Returns `error.AnalysisFail` if the file has an error. 606 pub fn ensureFileAnalyzed(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { 607 const file_root_type = pt.zcu.fileRootType(file_index); 608 if (file_root_type != .none) { 609 if (pt.ensureTypeUpToDate(file_root_type)) |_| { 610 return; 611 } else |err| switch (err) { 612 error.AnalysisFail => { 613 // The file's root `struct_decl` has, at some point, been lost, because the file failed AstGen. 614 // Clear `file_root_type`, and try the `semaFile` call below, in case the instruction has since 615 // been discovered under a new `TrackedInst.Index`. 616 pt.zcu.setFileRootType(file_index, .none); 617 }, 618 else => |e| return e, 619 } 620 } 621 return pt.semaFile(file_index); 622 } 623 624 /// Ensures that all memoized state on `Zcu` is up-to-date, performing re-analysis if necessary. 625 /// Returns `error.AnalysisFail` if an analysis error is encountered; the caller is free to ignore 626 /// this, since the error is already registered, but it must not use the value of memoized fields. 627 pub fn ensureMemoizedStateUpToDate(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.SemaError!void { 628 const tracy = trace(@src()); 629 defer tracy.end(); 630 631 const zcu = pt.zcu; 632 const gpa = zcu.gpa; 633 634 const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); 635 636 log.debug("ensureMemoizedStateUpToDate", .{}); 637 638 assert(!zcu.analysis_in_progress.contains(unit)); 639 640 const was_outdated = zcu.outdated.swapRemove(unit) or zcu.potentially_outdated.swapRemove(unit); 641 const prev_failed = zcu.failed_analysis.contains(unit) or zcu.transitive_failed_analysis.contains(unit); 642 643 if (was_outdated) { 644 dev.check(.incremental); 645 _ = zcu.outdated_ready.swapRemove(unit); 646 // No need for `deleteUnitExports` because we never export anything. 647 zcu.deleteUnitReferences(unit); 648 zcu.deleteUnitCompileLogs(unit); 649 if (zcu.failed_analysis.fetchSwapRemove(unit)) |kv| { 650 kv.value.destroy(gpa); 651 } 652 _ = zcu.transitive_failed_analysis.swapRemove(unit); 653 } else { 654 if (prev_failed) return error.AnalysisFail; 655 // We use an arbitrary element to check if the state has been resolved yet. 656 const to_check: Zcu.BuiltinDecl = switch (stage) { 657 .main => .Type, 658 .panic => .panic, 659 .va_list => .VaList, 660 .assembly => .assembly, 661 }; 662 if (zcu.builtin_decl_values.get(to_check) != .none) return; 663 } 664 665 if (zcu.comp.debugIncremental()) { 666 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit); 667 info.last_update_gen = zcu.generation; 668 info.deps.clearRetainingCapacity(); 669 } 670 671 const any_changed: bool, const new_failed: bool = if (pt.analyzeMemoizedState(stage)) |any_changed| 672 .{ any_changed or prev_failed, false } 673 else |err| switch (err) { 674 error.AnalysisFail => res: { 675 if (!zcu.failed_analysis.contains(unit)) { 676 // If this unit caused the error, it would have an entry in `failed_analysis`. 677 // Since it does not, this must be a transitive failure. 678 try zcu.transitive_failed_analysis.put(gpa, unit, {}); 679 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(unit)}); 680 } 681 break :res .{ !prev_failed, true }; 682 }, 683 error.OutOfMemory => { 684 // TODO: same as for `ensureComptimeUnitUpToDate` etc 685 return error.OutOfMemory; 686 }, 687 error.Canceled => |e| return e, 688 error.ComptimeReturn => unreachable, 689 error.ComptimeBreak => unreachable, 690 }; 691 692 if (was_outdated) { 693 const dependee: InternPool.Dependee = .{ .memoized_state = stage }; 694 if (any_changed) { 695 try zcu.markDependeeOutdated(.marked_po, dependee); 696 } else { 697 try zcu.markPoDependeeUpToDate(dependee); 698 } 699 } 700 701 if (new_failed) return error.AnalysisFail; 702 } 703 704 fn analyzeMemoizedState(pt: Zcu.PerThread, stage: InternPool.MemoizedStateStage) Zcu.CompileError!bool { 705 const zcu = pt.zcu; 706 const ip = &zcu.intern_pool; 707 const comp = zcu.comp; 708 const gpa = comp.gpa; 709 const io = comp.io; 710 711 const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); 712 713 try zcu.analysis_in_progress.putNoClobber(gpa, unit, {}); 714 defer assert(zcu.analysis_in_progress.swapRemove(unit)); 715 716 // Before we begin, collect: 717 // * The type `std`, and its namespace 718 // * The type `std.builtin`, and its namespace 719 // * A semi-reasonable source location 720 const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?; 721 try pt.ensureFileAnalyzed(std_file_index); 722 const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index)); 723 const std_namespace = std_type.getNamespaceIndex(zcu); 724 try pt.ensureNamespaceUpToDate(std_namespace); 725 const builtin_str = try ip.getOrPutString(gpa, io, pt.tid, "builtin", .no_embedded_nulls); 726 const builtin_nav = zcu.namespacePtr(std_namespace).pub_decls.getKeyAdapted(builtin_str, Zcu.Namespace.NameAdapter{ .zcu = zcu }) orelse 727 @panic("lib/std.zig is corrupt and missing 'builtin'"); 728 try pt.ensureNavValUpToDate(builtin_nav); 729 const builtin_type: Type = .fromInterned(ip.getNav(builtin_nav).status.fully_resolved.val); 730 const builtin_namespace = builtin_type.getNamespaceIndex(zcu); 731 try pt.ensureNamespaceUpToDate(builtin_namespace); 732 const src: Zcu.LazySrcLoc = .{ 733 .base_node_inst = builtin_type.typeDeclInst(zcu).?, 734 .offset = .{ .byte_abs = 0 }, 735 }; 736 737 var analysis_arena: std.heap.ArenaAllocator = .init(gpa); 738 defer analysis_arena.deinit(); 739 740 var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); 741 defer comptime_err_ret_trace.deinit(); 742 743 var sema: Sema = .{ 744 .pt = pt, 745 .gpa = gpa, 746 .arena = analysis_arena.allocator(), 747 .code = .{ .instructions = .empty, .string_bytes = &.{}, .extra = &.{} }, 748 .owner = unit, 749 .func_index = .none, 750 .func_is_naked = false, 751 .fn_ret_ty = .void, 752 .fn_ret_ty_ies = null, 753 .comptime_err_ret_trace = &comptime_err_ret_trace, 754 }; 755 defer sema.deinit(); 756 757 var block: Sema.Block = .{ 758 .parent = null, 759 .sema = &sema, 760 .namespace = std_namespace, 761 .instructions = .{}, 762 .inlining = null, 763 .comptime_reason = .{ .reason = .{ 764 .src = src, 765 .r = .{ .simple = .type }, 766 } }, 767 .src_base_inst = src.base_node_inst, 768 .type_name_ctx = .empty, 769 }; 770 defer block.instructions.deinit(gpa); 771 772 return sema.analyzeMemoizedState(&block, src, builtin_namespace, stage); 773 } 774 775 /// Ensures that the state of the given `ComptimeUnit` is fully up-to-date, performing re-analysis 776 /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is 777 /// free to ignore this, since the error is already registered. 778 pub fn ensureComptimeUnitUpToDate(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.SemaError!void { 779 const tracy = trace(@src()); 780 defer tracy.end(); 781 782 const zcu = pt.zcu; 783 const gpa = zcu.gpa; 784 785 const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id }); 786 787 log.debug("ensureComptimeUnitUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); 788 789 assert(!zcu.analysis_in_progress.contains(anal_unit)); 790 791 // Determine whether or not this `ComptimeUnit` is outdated. For this kind of `AnalUnit`, that's 792 // the only indicator as to whether or not analysis is required; when a `ComptimeUnit` is first 793 // created, it's marked as outdated. 794 // 795 // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to 796 // ensure that the unit is definitely up-to-date when this function returns. This mechanism could 797 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by 798 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`. 799 800 const was_outdated = zcu.outdated.swapRemove(anal_unit) or 801 zcu.potentially_outdated.swapRemove(anal_unit); 802 803 if (was_outdated) { 804 _ = zcu.outdated_ready.swapRemove(anal_unit); 805 // `was_outdated` can be true in the initial update for comptime units, so this isn't a `dev.check`. 806 if (dev.env.supports(.incremental)) { 807 zcu.deleteUnitExports(anal_unit); 808 zcu.deleteUnitReferences(anal_unit); 809 zcu.deleteUnitCompileLogs(anal_unit); 810 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { 811 kv.value.destroy(gpa); 812 } 813 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); 814 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); 815 } 816 } else { 817 // We can trust the current information about this unit. 818 if (zcu.failed_analysis.contains(anal_unit)) return error.AnalysisFail; 819 if (zcu.transitive_failed_analysis.contains(anal_unit)) return error.AnalysisFail; 820 return; 821 } 822 823 if (zcu.comp.debugIncremental()) { 824 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); 825 info.last_update_gen = zcu.generation; 826 info.deps.clearRetainingCapacity(); 827 } 828 829 const unit_tracking = zcu.trackUnitSema( 830 "comptime", 831 zcu.intern_pool.getComptimeUnit(cu_id).zir_index, 832 ); 833 defer unit_tracking.end(zcu); 834 835 return pt.analyzeComptimeUnit(cu_id) catch |err| switch (err) { 836 error.AnalysisFail => { 837 if (!zcu.failed_analysis.contains(anal_unit)) { 838 // If this unit caused the error, it would have an entry in `failed_analysis`. 839 // Since it does not, this must be a transitive failure. 840 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); 841 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); 842 } 843 return error.AnalysisFail; 844 }, 845 error.OutOfMemory => { 846 // TODO: it's unclear how to gracefully handle this. 847 // To report the error cleanly, we need to add a message to `failed_analysis` and a 848 // corresponding entry to `retryable_failures`; but either of these things is quite 849 // likely to OOM at this point. 850 // If that happens, what do we do? Perhaps we could have a special field on `Zcu` 851 // for reporting OOM errors without allocating. 852 return error.OutOfMemory; 853 }, 854 error.Canceled => |e| return e, 855 error.ComptimeReturn => unreachable, 856 error.ComptimeBreak => unreachable, 857 }; 858 } 859 860 /// Re-analyzes a `ComptimeUnit`. The unit has already been determined to be out-of-date, and old 861 /// side effects (exports/references/etc) have been dropped. If semantic analysis fails, this 862 /// function will return `error.AnalysisFail`, and it is the caller's reponsibility to add an entry 863 /// to `transitive_failed_analysis` if necessary. 864 fn analyzeComptimeUnit(pt: Zcu.PerThread, cu_id: InternPool.ComptimeUnit.Id) Zcu.CompileError!void { 865 const zcu = pt.zcu; 866 const ip = &zcu.intern_pool; 867 const comp = zcu.comp; 868 const gpa = comp.gpa; 869 const io = comp.io; 870 871 const anal_unit: AnalUnit = .wrap(.{ .@"comptime" = cu_id }); 872 const comptime_unit = ip.getComptimeUnit(cu_id); 873 874 log.debug("analyzeComptimeUnit {f}", .{zcu.fmtAnalUnit(anal_unit)}); 875 876 const inst_resolved = comptime_unit.zir_index.resolveFull(ip) orelse return error.AnalysisFail; 877 const file = zcu.fileByIndex(inst_resolved.file); 878 const zir = file.zir.?; 879 880 try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); 881 defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); 882 883 var analysis_arena: std.heap.ArenaAllocator = .init(gpa); 884 defer analysis_arena.deinit(); 885 886 var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); 887 defer comptime_err_ret_trace.deinit(); 888 889 var sema: Sema = .{ 890 .pt = pt, 891 .gpa = gpa, 892 .arena = analysis_arena.allocator(), 893 .code = zir, 894 .owner = anal_unit, 895 .func_index = .none, 896 .func_is_naked = false, 897 .fn_ret_ty = .void, 898 .fn_ret_ty_ies = null, 899 .comptime_err_ret_trace = &comptime_err_ret_trace, 900 }; 901 defer sema.deinit(); 902 903 // The comptime unit declares on the source of the corresponding `comptime` declaration. 904 try sema.declareDependency(.{ .src_hash = comptime_unit.zir_index }); 905 906 var block: Sema.Block = .{ 907 .parent = null, 908 .sema = &sema, 909 .namespace = comptime_unit.namespace, 910 .instructions = .{}, 911 .inlining = null, 912 .comptime_reason = .{ .reason = .{ 913 .src = .{ 914 .base_node_inst = comptime_unit.zir_index, 915 .offset = .{ .token_offset = .zero }, 916 }, 917 .r = .{ .simple = .comptime_keyword }, 918 } }, 919 .src_base_inst = comptime_unit.zir_index, 920 .type_name_ctx = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.comptime", .{ 921 Type.fromInterned(zcu.namespacePtr(comptime_unit.namespace).owner_type).containerTypeName(ip).fmt(ip), 922 }, .no_embedded_nulls), 923 }; 924 defer block.instructions.deinit(gpa); 925 926 const zir_decl = zir.getDeclaration(inst_resolved.inst); 927 assert(zir_decl.kind == .@"comptime"); 928 assert(zir_decl.type_body == null); 929 assert(zir_decl.align_body == null); 930 assert(zir_decl.linksection_body == null); 931 assert(zir_decl.addrspace_body == null); 932 const value_body = zir_decl.value_body.?; 933 934 const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst); 935 assert(result_ref == .void_value); // AstGen should always uphold this 936 937 // Nothing else to do -- for a comptime decl, all we care about are the side effects. 938 // Just make sure to `flushExports`. 939 try sema.flushExports(); 940 } 941 942 /// Ensures that the resolved value of the given `Nav` is fully up-to-date, performing re-analysis 943 /// if necessary. Returns `error.AnalysisFail` if an analysis error is encountered; the caller is 944 /// free to ignore this, since the error is already registered. 945 pub fn ensureNavValUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void { 946 const tracy = trace(@src()); 947 defer tracy.end(); 948 949 // TODO: document this elsewhere mlugg! 950 // For my own benefit, here's how a namespace update for a normal (non-file-root) type works: 951 // `const S = struct { ... };` 952 // We are adding or removing a declaration within this `struct`. 953 // * `S` registers a dependency on `.{ .src_hash = (declaration of S) }` 954 // * Any change to the `struct` body -- including changing a declaration -- invalidates this 955 // * `S` is re-analyzed, but notes: 956 // * there is an existing struct instance (at this `TrackedInst` with these captures) 957 // * the struct's resolution is up-to-date (because nothing about the fields changed) 958 // * so, it uses the same `struct` 959 // * but this doesn't stop it from updating the namespace! 960 // * we basically do `scanDecls`, updating the namespace as needed 961 // * so everyone lived happily ever after 962 963 const zcu = pt.zcu; 964 const gpa = zcu.gpa; 965 const ip = &zcu.intern_pool; 966 967 _ = zcu.nav_val_analysis_queued.swapRemove(nav_id); 968 969 const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id }); 970 const nav = ip.getNav(nav_id); 971 972 log.debug("ensureNavValUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); 973 974 assert(!zcu.analysis_in_progress.contains(anal_unit)); 975 976 // Determine whether or not this `Nav`'s value is outdated. This also includes checking if the 977 // status is `.unresolved`, which indicates that the value is outdated because it has *never* 978 // been analyzed so far. 979 // 980 // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to 981 // ensure that the unit is definitely up-to-date when this function returns. This mechanism could 982 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by 983 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`. 984 985 const was_outdated = zcu.outdated.swapRemove(anal_unit) or 986 zcu.potentially_outdated.swapRemove(anal_unit); 987 988 const prev_failed = zcu.failed_analysis.contains(anal_unit) or 989 zcu.transitive_failed_analysis.contains(anal_unit); 990 991 if (was_outdated) { 992 dev.check(.incremental); 993 _ = zcu.outdated_ready.swapRemove(anal_unit); 994 zcu.deleteUnitExports(anal_unit); 995 zcu.deleteUnitReferences(anal_unit); 996 zcu.deleteUnitCompileLogs(anal_unit); 997 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { 998 kv.value.destroy(gpa); 999 } 1000 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); 1001 ip.removeDependenciesForDepender(gpa, anal_unit); 1002 } else { 1003 // We can trust the current information about this unit. 1004 if (prev_failed) return error.AnalysisFail; 1005 switch (nav.status) { 1006 .unresolved, .type_resolved => {}, 1007 .fully_resolved => return, 1008 } 1009 } 1010 1011 if (zcu.comp.debugIncremental()) { 1012 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); 1013 info.last_update_gen = zcu.generation; 1014 info.deps.clearRetainingCapacity(); 1015 } 1016 1017 const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip)); 1018 defer unit_tracking.end(zcu); 1019 1020 const invalidate_value: bool, const new_failed: bool = if (pt.analyzeNavVal(nav_id)) |result| res: { 1021 break :res .{ 1022 // If the unit has gone from failed to success, we still need to invalidate the dependencies. 1023 result.val_changed or prev_failed, 1024 false, 1025 }; 1026 } else |err| switch (err) { 1027 error.AnalysisFail => res: { 1028 if (!zcu.failed_analysis.contains(anal_unit)) { 1029 // If this unit caused the error, it would have an entry in `failed_analysis`. 1030 // Since it does not, this must be a transitive failure. 1031 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); 1032 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1033 } 1034 break :res .{ !prev_failed, true }; 1035 }, 1036 error.OutOfMemory => { 1037 // TODO: it's unclear how to gracefully handle this. 1038 // To report the error cleanly, we need to add a message to `failed_analysis` and a 1039 // corresponding entry to `retryable_failures`; but either of these things is quite 1040 // likely to OOM at this point. 1041 // If that happens, what do we do? Perhaps we could have a special field on `Zcu` 1042 // for reporting OOM errors without allocating. 1043 return error.OutOfMemory; 1044 }, 1045 error.Canceled => |e| return e, 1046 error.ComptimeReturn => unreachable, 1047 error.ComptimeBreak => unreachable, 1048 }; 1049 1050 if (was_outdated) { 1051 const dependee: InternPool.Dependee = .{ .nav_val = nav_id }; 1052 if (invalidate_value) { 1053 // This dependency was marked as PO, meaning dependees were waiting 1054 // on its analysis result, and it has turned out to be outdated. 1055 // Update dependees accordingly. 1056 try zcu.markDependeeOutdated(.marked_po, dependee); 1057 } else { 1058 // This dependency was previously PO, but turned out to be up-to-date. 1059 // We do not need to queue successive analysis. 1060 try zcu.markPoDependeeUpToDate(dependee); 1061 } 1062 } 1063 1064 // If there isn't a type annotation, then we have also just resolved the type. That means the 1065 // the type is up-to-date, so it won't have the chance to mark its own dependency on the value; 1066 // we must do that ourselves. 1067 type_deps_on_val: { 1068 const inst_resolved = nav.analysis.?.zir_index.resolveFull(ip) orelse break :type_deps_on_val; 1069 const file = zcu.fileByIndex(inst_resolved.file); 1070 const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst); 1071 if (zir_decl.type_body != null) break :type_deps_on_val; 1072 // The type does indeed depend on the value. We are responsible for populating all state of 1073 // the `nav_ty`, including exports, references, errors, and dependencies. 1074 const ty_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id }); 1075 const ty_was_outdated = zcu.outdated.swapRemove(ty_unit) or 1076 zcu.potentially_outdated.swapRemove(ty_unit); 1077 if (ty_was_outdated) { 1078 _ = zcu.outdated_ready.swapRemove(ty_unit); 1079 zcu.deleteUnitExports(ty_unit); 1080 zcu.deleteUnitReferences(ty_unit); 1081 zcu.deleteUnitCompileLogs(ty_unit); 1082 if (zcu.failed_analysis.fetchSwapRemove(ty_unit)) |kv| { 1083 kv.value.destroy(gpa); 1084 } 1085 _ = zcu.transitive_failed_analysis.swapRemove(ty_unit); 1086 ip.removeDependenciesForDepender(gpa, ty_unit); 1087 } 1088 try pt.addDependency(ty_unit, .{ .nav_val = nav_id }); 1089 if (new_failed) try zcu.transitive_failed_analysis.put(gpa, ty_unit, {}); 1090 if (ty_was_outdated) try zcu.markDependeeOutdated(.marked_po, .{ .nav_ty = nav_id }); 1091 } 1092 1093 if (new_failed) return error.AnalysisFail; 1094 } 1095 1096 fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { val_changed: bool } { 1097 const zcu = pt.zcu; 1098 const ip = &zcu.intern_pool; 1099 const comp = zcu.comp; 1100 const gpa = comp.gpa; 1101 const io = comp.io; 1102 1103 const anal_unit: AnalUnit = .wrap(.{ .nav_val = nav_id }); 1104 const old_nav = ip.getNav(nav_id); 1105 1106 log.debug("analyzeNavVal {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1107 1108 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail; 1109 const file = zcu.fileByIndex(inst_resolved.file); 1110 const zir = file.zir.?; 1111 const zir_decl = zir.getDeclaration(inst_resolved.inst); 1112 1113 try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); 1114 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); 1115 1116 // If there's no type body, we are also resolving the type here. 1117 if (zir_decl.type_body == null) { 1118 try zcu.analysis_in_progress.putNoClobber(gpa, .wrap(.{ .nav_ty = nav_id }), {}); 1119 } 1120 errdefer if (zir_decl.type_body == null) { 1121 _ = zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id })); 1122 }; 1123 1124 var analysis_arena: std.heap.ArenaAllocator = .init(gpa); 1125 defer analysis_arena.deinit(); 1126 1127 var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); 1128 defer comptime_err_ret_trace.deinit(); 1129 1130 var sema: Sema = .{ 1131 .pt = pt, 1132 .gpa = gpa, 1133 .arena = analysis_arena.allocator(), 1134 .code = zir, 1135 .owner = anal_unit, 1136 .func_index = .none, 1137 .func_is_naked = false, 1138 .fn_ret_ty = .void, 1139 .fn_ret_ty_ies = null, 1140 .comptime_err_ret_trace = &comptime_err_ret_trace, 1141 }; 1142 defer sema.deinit(); 1143 1144 // Every `Nav` declares a dependency on the source of the corresponding declaration. 1145 try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index }); 1146 1147 // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are 1148 // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory, 1149 // these references are known implicitly. See logic in `Zcu.resolveReferences`. 1150 1151 var block: Sema.Block = .{ 1152 .parent = null, 1153 .sema = &sema, 1154 .namespace = old_nav.analysis.?.namespace, 1155 .instructions = .{}, 1156 .inlining = null, 1157 .comptime_reason = undefined, // set below 1158 .src_base_inst = old_nav.analysis.?.zir_index, 1159 .type_name_ctx = old_nav.fqn, 1160 }; 1161 defer block.instructions.deinit(gpa); 1162 1163 const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); 1164 const init_src = block.src(.{ .node_offset_var_decl_init = .zero }); 1165 const align_src = block.src(.{ .node_offset_var_decl_align = .zero }); 1166 const section_src = block.src(.{ .node_offset_var_decl_section = .zero }); 1167 const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero }); 1168 1169 block.comptime_reason = .{ .reason = .{ 1170 .src = init_src, 1171 .r = .{ .simple = .container_var_init }, 1172 } }; 1173 1174 const maybe_ty: ?Type = if (zir_decl.type_body != null) ty: { 1175 // Since we have a type body, the type is resolved separately! 1176 // Of course, we need to make sure we depend on it properly. 1177 try sema.declareDependency(.{ .nav_ty = nav_id }); 1178 try pt.ensureNavTypeUpToDate(nav_id); 1179 break :ty .fromInterned(ip.getNav(nav_id).typeOf(ip)); 1180 } else null; 1181 1182 const final_val: ?Value = if (zir_decl.value_body) |value_body| val: { 1183 if (maybe_ty) |ty| { 1184 // Put the resolved type into `inst_map` to be used as the result type of the init. 1185 try sema.inst_map.ensureSpaceForInstructions(gpa, &.{inst_resolved.inst}); 1186 sema.inst_map.putAssumeCapacity(inst_resolved.inst, Air.internedToRef(ty.toIntern())); 1187 const uncoerced_result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst); 1188 assert(sema.inst_map.remove(inst_resolved.inst)); 1189 1190 const result_ref = try sema.coerce(&block, ty, uncoerced_result_ref, init_src); 1191 break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref); 1192 } else { 1193 // Just analyze the value; we have no type to offer. 1194 const result_ref = try sema.resolveInlineBody(&block, value_body, inst_resolved.inst); 1195 break :val try sema.resolveFinalDeclValue(&block, init_src, result_ref); 1196 } 1197 } else null; 1198 1199 const nav_ty: Type = maybe_ty orelse final_val.?.typeOf(zcu); 1200 1201 // First, we must resolve the declaration's type. To do this, we analyze the type body if available, 1202 // or otherwise, we analyze the value body, populating `early_val` in the process. 1203 1204 const is_const = is_const: switch (zir_decl.kind) { 1205 .@"comptime" => unreachable, // this is not a Nav 1206 .unnamed_test, .@"test", .decltest => { 1207 assert(nav_ty.zigTypeTag(zcu) == .@"fn"); 1208 break :is_const true; 1209 }, 1210 .@"const" => true, 1211 .@"var" => { 1212 try sema.validateVarType( 1213 &block, 1214 if (zir_decl.type_body != null) ty_src else init_src, 1215 nav_ty, 1216 zir_decl.linkage == .@"extern", 1217 ); 1218 break :is_const false; 1219 }, 1220 }; 1221 1222 // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine 1223 // the full pointer type of this declaration. 1224 1225 const modifiers: Sema.NavPtrModifiers = if (zir_decl.type_body != null) m: { 1226 // `analyzeNavType` (from the `ensureNavTypeUpToDate` call above) has already populated this data into 1227 // the `Nav`. Load the new one, and pull the modifiers out. 1228 switch (ip.getNav(nav_id).status) { 1229 .unresolved => unreachable, // `analyzeNavType` will never leave us in this state 1230 inline .type_resolved, .fully_resolved => |r| break :m .{ 1231 .alignment = r.alignment, 1232 .@"linksection" = r.@"linksection", 1233 .@"addrspace" = r.@"addrspace", 1234 }, 1235 } 1236 } else m: { 1237 // `analyzeNavType` is essentially a stub which calls us. We are responsible for resolving this data. 1238 break :m try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, nav_ty); 1239 }; 1240 1241 // Lastly, we must figure out the actual interned value to store to the `Nav`. 1242 // This isn't necessarily the same as `final_val`! 1243 1244 const nav_val: Value = switch (zir_decl.linkage) { 1245 .normal, .@"export" => switch (zir_decl.kind) { 1246 .@"var" => .fromInterned(try pt.intern(.{ .variable = .{ 1247 .ty = nav_ty.toIntern(), 1248 .init = final_val.?.toIntern(), 1249 .owner_nav = nav_id, 1250 .is_threadlocal = zir_decl.is_threadlocal, 1251 } })), 1252 else => final_val.?, 1253 }, 1254 .@"extern" => val: { 1255 assert(final_val == null); // extern decls do not have a value body 1256 const lib_name: ?[]const u8 = if (zir_decl.lib_name != .empty) l: { 1257 break :l zir.nullTerminatedString(zir_decl.lib_name); 1258 } else null; 1259 if (lib_name) |l| { 1260 const lib_name_src = block.src(.{ .node_offset_lib_name = .zero }); 1261 try sema.handleExternLibName(&block, lib_name_src, l); 1262 } 1263 break :val .fromInterned(try pt.getExtern(.{ 1264 .name = old_nav.name, 1265 .ty = nav_ty.toIntern(), 1266 .lib_name = try ip.getOrPutStringOpt(gpa, io, pt.tid, lib_name, .no_embedded_nulls), 1267 .is_threadlocal = zir_decl.is_threadlocal, 1268 .linkage = .strong, 1269 .visibility = .default, 1270 .is_dll_import = false, 1271 .relocation = .any, 1272 .is_const = is_const, 1273 .alignment = modifiers.alignment, 1274 .@"addrspace" = modifiers.@"addrspace", 1275 .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction 1276 .owner_nav = undefined, // ignored by `getExtern` 1277 .source = .syntax, 1278 })); 1279 }, 1280 }; 1281 1282 switch (nav_val.toIntern()) { 1283 .unreachable_value => unreachable, // assertion failure 1284 else => {}, 1285 } 1286 1287 // This resolves the type of the resolved value, not that value itself. If `nav_val` is a struct type, 1288 // this resolves the type `type` (which needs no resolution), not the struct itself. 1289 try nav_ty.resolveLayout(pt); 1290 1291 const queue_linker_work, const is_owned_fn = switch (ip.indexToKey(nav_val.toIntern())) { 1292 .func => |f| .{ true, f.owner_nav == nav_id }, // note that this lets function aliases reach codegen 1293 .variable => |v| .{ v.owner_nav == nav_id, false }, 1294 .@"extern" => |e| .{ 1295 false, 1296 Type.fromInterned(e.ty).zigTypeTag(zcu) == .@"fn" and zir_decl.linkage == .@"extern", 1297 }, 1298 else => .{ true, false }, 1299 }; 1300 1301 if (is_owned_fn) { 1302 // linksection etc are legal, except some targets do not support function alignment. 1303 if (zir_decl.align_body != null and !target_util.supportsFunctionAlignment(zcu.getTarget())) { 1304 return sema.fail(&block, align_src, "target does not support function alignment", .{}); 1305 } 1306 } else if (try nav_ty.comptimeOnlySema(pt)) { 1307 // alignment, linksection, addrspace annotations are not allowed for comptime-only types. 1308 const reason: []const u8 = switch (ip.indexToKey(nav_val.toIntern())) { 1309 .func => "function alias", // slightly clearer message, since you *can* specify these on function *declarations* 1310 else => "comptime-only type", 1311 }; 1312 if (zir_decl.align_body != null) { 1313 return sema.fail(&block, align_src, "cannot specify alignment of {s}", .{reason}); 1314 } 1315 if (zir_decl.linksection_body != null) { 1316 return sema.fail(&block, section_src, "cannot specify linksection of {s}", .{reason}); 1317 } 1318 if (zir_decl.addrspace_body != null) { 1319 return sema.fail(&block, addrspace_src, "cannot specify addrspace of {s}", .{reason}); 1320 } 1321 } 1322 1323 ip.resolveNavValue(io, nav_id, .{ 1324 .val = nav_val.toIntern(), 1325 .is_const = is_const, 1326 .alignment = modifiers.alignment, 1327 .@"linksection" = modifiers.@"linksection", 1328 .@"addrspace" = modifiers.@"addrspace", 1329 }); 1330 1331 // Mark the unit as completed before evaluating the export! 1332 assert(zcu.analysis_in_progress.swapRemove(anal_unit)); 1333 if (zir_decl.type_body == null) { 1334 assert(zcu.analysis_in_progress.swapRemove(.wrap(.{ .nav_ty = nav_id }))); 1335 } 1336 1337 if (zir_decl.linkage == .@"export") { 1338 const export_src = block.src(.{ .token_offset = @enumFromInt(@intFromBool(zir_decl.is_pub)) }); 1339 const name_slice = zir.nullTerminatedString(zir_decl.name); 1340 const name_ip = try ip.getOrPutString(gpa, io, pt.tid, name_slice, .no_embedded_nulls); 1341 try sema.analyzeExport(&block, export_src, .{ .name = name_ip }, nav_id); 1342 } 1343 1344 try sema.flushExports(); 1345 1346 queue_codegen: { 1347 if (!queue_linker_work) break :queue_codegen; 1348 1349 if (!try nav_ty.hasRuntimeBitsSema(pt)) { 1350 if (zcu.comp.config.use_llvm) break :queue_codegen; 1351 if (file.mod.?.strip) break :queue_codegen; 1352 } 1353 1354 // This job depends on any resolve_type_fully jobs queued up before it. 1355 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 1356 try zcu.comp.queueJob(.{ .link_nav = nav_id }); 1357 } 1358 1359 switch (old_nav.status) { 1360 .unresolved, .type_resolved => return .{ .val_changed = true }, 1361 .fully_resolved => |old| return .{ .val_changed = old.val != nav_val.toIntern() }, 1362 } 1363 } 1364 1365 pub fn ensureNavTypeUpToDate(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.SemaError!void { 1366 const tracy = trace(@src()); 1367 defer tracy.end(); 1368 1369 const zcu = pt.zcu; 1370 const gpa = zcu.gpa; 1371 const ip = &zcu.intern_pool; 1372 1373 const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id }); 1374 const nav = ip.getNav(nav_id); 1375 1376 log.debug("ensureNavTypeUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1377 1378 assert(!zcu.analysis_in_progress.contains(anal_unit)); 1379 1380 const type_resolved_by_value: bool = from_val: { 1381 const analysis = nav.analysis orelse break :from_val false; 1382 const inst_resolved = analysis.zir_index.resolveFull(ip) orelse break :from_val false; 1383 const file = zcu.fileByIndex(inst_resolved.file); 1384 const zir_decl = file.zir.?.getDeclaration(inst_resolved.inst); 1385 break :from_val zir_decl.type_body == null; 1386 }; 1387 if (type_resolved_by_value) { 1388 // Logic at the end of `ensureNavValUpToDate` is directly responsible for populating our state. 1389 return pt.ensureNavValUpToDate(nav_id); 1390 } 1391 1392 // Determine whether or not this `Nav`'s type is outdated. This also includes checking if the 1393 // status is `.unresolved`, which indicates that the value is outdated because it has *never* 1394 // been analyzed so far. 1395 // 1396 // Note that if the unit is PO, we pessimistically assume that it *does* require re-analysis, to 1397 // ensure that the unit is definitely up-to-date when this function returns. This mechanism could 1398 // result in over-analysis if analysis occurs in a poor order; we do our best to avoid this by 1399 // carefully choosing which units to re-analyze. See `Zcu.findOutdatedToAnalyze`. 1400 1401 const was_outdated = zcu.outdated.swapRemove(anal_unit) or 1402 zcu.potentially_outdated.swapRemove(anal_unit); 1403 1404 const prev_failed = zcu.failed_analysis.contains(anal_unit) or 1405 zcu.transitive_failed_analysis.contains(anal_unit); 1406 1407 if (was_outdated) { 1408 dev.check(.incremental); 1409 _ = zcu.outdated_ready.swapRemove(anal_unit); 1410 zcu.deleteUnitExports(anal_unit); 1411 zcu.deleteUnitReferences(anal_unit); 1412 zcu.deleteUnitCompileLogs(anal_unit); 1413 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { 1414 kv.value.destroy(gpa); 1415 } 1416 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); 1417 ip.removeDependenciesForDepender(gpa, anal_unit); 1418 } else { 1419 // We can trust the current information about this unit. 1420 if (prev_failed) return error.AnalysisFail; 1421 switch (nav.status) { 1422 .unresolved => {}, 1423 .type_resolved, .fully_resolved => return, 1424 } 1425 } 1426 1427 if (zcu.comp.debugIncremental()) { 1428 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); 1429 info.last_update_gen = zcu.generation; 1430 info.deps.clearRetainingCapacity(); 1431 } 1432 1433 const unit_tracking = zcu.trackUnitSema(nav.fqn.toSlice(ip), nav.srcInst(ip)); 1434 defer unit_tracking.end(zcu); 1435 1436 const invalidate_type: bool, const new_failed: bool = if (pt.analyzeNavType(nav_id)) |result| res: { 1437 break :res .{ 1438 // If the unit has gone from failed to success, we still need to invalidate the dependencies. 1439 result.type_changed or prev_failed, 1440 false, 1441 }; 1442 } else |err| switch (err) { 1443 error.AnalysisFail => res: { 1444 if (!zcu.failed_analysis.contains(anal_unit)) { 1445 // If this unit caused the error, it would have an entry in `failed_analysis`. 1446 // Since it does not, this must be a transitive failure. 1447 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); 1448 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1449 } 1450 break :res .{ !prev_failed, true }; 1451 }, 1452 error.OutOfMemory => { 1453 // TODO: it's unclear how to gracefully handle this. 1454 // To report the error cleanly, we need to add a message to `failed_analysis` and a 1455 // corresponding entry to `retryable_failures`; but either of these things is quite 1456 // likely to OOM at this point. 1457 // If that happens, what do we do? Perhaps we could have a special field on `Zcu` 1458 // for reporting OOM errors without allocating. 1459 return error.OutOfMemory; 1460 }, 1461 error.Canceled => |e| return e, 1462 error.ComptimeReturn => unreachable, 1463 error.ComptimeBreak => unreachable, 1464 }; 1465 1466 if (was_outdated) { 1467 const dependee: InternPool.Dependee = .{ .nav_ty = nav_id }; 1468 if (invalidate_type) { 1469 // This dependency was marked as PO, meaning dependees were waiting 1470 // on its analysis result, and it has turned out to be outdated. 1471 // Update dependees accordingly. 1472 try zcu.markDependeeOutdated(.marked_po, dependee); 1473 } else { 1474 // This dependency was previously PO, but turned out to be up-to-date. 1475 // We do not need to queue successive analysis. 1476 try zcu.markPoDependeeUpToDate(dependee); 1477 } 1478 } 1479 1480 if (new_failed) return error.AnalysisFail; 1481 } 1482 1483 fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileError!struct { type_changed: bool } { 1484 const zcu = pt.zcu; 1485 const comp = zcu.comp; 1486 const gpa = comp.gpa; 1487 const io = comp.io; 1488 const ip = &zcu.intern_pool; 1489 1490 const anal_unit: AnalUnit = .wrap(.{ .nav_ty = nav_id }); 1491 const old_nav = ip.getNav(nav_id); 1492 1493 log.debug("analyzeNavType {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1494 1495 const inst_resolved = old_nav.analysis.?.zir_index.resolveFull(ip) orelse return error.AnalysisFail; 1496 const file = zcu.fileByIndex(inst_resolved.file); 1497 const zir = file.zir.?; 1498 1499 try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); 1500 defer assert(zcu.analysis_in_progress.swapRemove(anal_unit)); 1501 1502 const zir_decl = zir.getDeclaration(inst_resolved.inst); 1503 const type_body = zir_decl.type_body.?; 1504 1505 var analysis_arena: std.heap.ArenaAllocator = .init(gpa); 1506 defer analysis_arena.deinit(); 1507 1508 var comptime_err_ret_trace: std.array_list.Managed(Zcu.LazySrcLoc) = .init(gpa); 1509 defer comptime_err_ret_trace.deinit(); 1510 1511 var sema: Sema = .{ 1512 .pt = pt, 1513 .gpa = gpa, 1514 .arena = analysis_arena.allocator(), 1515 .code = zir, 1516 .owner = anal_unit, 1517 .func_index = .none, 1518 .func_is_naked = false, 1519 .fn_ret_ty = .void, 1520 .fn_ret_ty_ies = null, 1521 .comptime_err_ret_trace = &comptime_err_ret_trace, 1522 }; 1523 defer sema.deinit(); 1524 1525 // Every `Nav` declares a dependency on the source of the corresponding declaration. 1526 try sema.declareDependency(.{ .src_hash = old_nav.analysis.?.zir_index }); 1527 1528 // In theory, we would also add a reference to the corresponding `nav_val` unit here: there are 1529 // always references in both directions between a `nav_val` and `nav_ty`. However, to save memory, 1530 // these references are known implicitly. See logic in `Zcu.resolveReferences`. 1531 1532 var block: Sema.Block = .{ 1533 .parent = null, 1534 .sema = &sema, 1535 .namespace = old_nav.analysis.?.namespace, 1536 .instructions = .{}, 1537 .inlining = null, 1538 .comptime_reason = undefined, // set below 1539 .src_base_inst = old_nav.analysis.?.zir_index, 1540 .type_name_ctx = old_nav.fqn, 1541 }; 1542 defer block.instructions.deinit(gpa); 1543 1544 const ty_src = block.src(.{ .node_offset_var_decl_ty = .zero }); 1545 1546 block.comptime_reason = .{ .reason = .{ 1547 .src = ty_src, 1548 .r = .{ .simple = .type }, 1549 } }; 1550 1551 const resolved_ty: Type = ty: { 1552 const uncoerced_type_ref = try sema.resolveInlineBody(&block, type_body, inst_resolved.inst); 1553 const type_ref = try sema.coerce(&block, .type, uncoerced_type_ref, ty_src); 1554 break :ty .fromInterned(type_ref.toInterned().?); 1555 }; 1556 1557 try resolved_ty.resolveLayout(pt); 1558 1559 // In the case where the type is specified, this function is also responsible for resolving 1560 // the pointer modifiers, i.e. alignment, linksection, addrspace. 1561 const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty); 1562 1563 const is_const = switch (zir_decl.kind) { 1564 .@"comptime" => unreachable, 1565 .unnamed_test, .@"test", .decltest, .@"const" => true, 1566 .@"var" => false, 1567 }; 1568 1569 const is_extern_decl = zir_decl.linkage == .@"extern"; 1570 1571 // Now for the question of the day: are the type and modifiers the same as before? 1572 // If they are, then we should actually keep the `Nav` as `fully_resolved` if it currently is. 1573 // That's because `analyzeNavVal` will later want to look at the resolved value to figure out 1574 // whether it's changed: if we threw that data away now, it would have to assume that the value 1575 // had changed, potentially spinning off loads of unnecessary re-analysis! 1576 const changed = switch (old_nav.status) { 1577 .unresolved => true, 1578 .type_resolved => |r| r.type != resolved_ty.toIntern() or 1579 r.alignment != modifiers.alignment or 1580 r.@"linksection" != modifiers.@"linksection" or 1581 r.@"addrspace" != modifiers.@"addrspace" or 1582 r.is_const != is_const or 1583 r.is_extern_decl != is_extern_decl, 1584 .fully_resolved => |r| ip.typeOf(r.val) != resolved_ty.toIntern() or 1585 r.alignment != modifiers.alignment or 1586 r.@"linksection" != modifiers.@"linksection" or 1587 r.@"addrspace" != modifiers.@"addrspace" or 1588 r.is_const != is_const or 1589 (old_nav.getExtern(ip) != null) != is_extern_decl, 1590 }; 1591 1592 if (!changed) return .{ .type_changed = false }; 1593 1594 ip.resolveNavType(io, nav_id, .{ 1595 .type = resolved_ty.toIntern(), 1596 .is_const = is_const, 1597 .alignment = modifiers.alignment, 1598 .@"linksection" = modifiers.@"linksection", 1599 .@"addrspace" = modifiers.@"addrspace", 1600 .is_threadlocal = zir_decl.is_threadlocal, 1601 .is_extern_decl = is_extern_decl, 1602 }); 1603 1604 return .{ .type_changed = true }; 1605 } 1606 1607 pub fn ensureFuncBodyUpToDate(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!void { 1608 dev.check(.sema); 1609 1610 const tracy = trace(@src()); 1611 defer tracy.end(); 1612 1613 const zcu = pt.zcu; 1614 const gpa = zcu.gpa; 1615 const ip = &zcu.intern_pool; 1616 1617 _ = zcu.func_body_analysis_queued.swapRemove(func_index); 1618 1619 const anal_unit: AnalUnit = .wrap(.{ .func = func_index }); 1620 1621 log.debug("ensureFuncBodyUpToDate {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1622 1623 assert(!zcu.analysis_in_progress.contains(anal_unit)); 1624 1625 const func = zcu.funcInfo(func_index); 1626 1627 assert(func.ty == func.uncoerced_ty); // analyze the body of the original function, not a coerced one 1628 1629 const was_outdated = zcu.outdated.swapRemove(anal_unit) or 1630 zcu.potentially_outdated.swapRemove(anal_unit); 1631 1632 const prev_failed = zcu.failed_analysis.contains(anal_unit) or zcu.transitive_failed_analysis.contains(anal_unit); 1633 1634 if (was_outdated) { 1635 dev.check(.incremental); 1636 _ = zcu.outdated_ready.swapRemove(anal_unit); 1637 zcu.deleteUnitExports(anal_unit); 1638 zcu.deleteUnitReferences(anal_unit); 1639 zcu.deleteUnitCompileLogs(anal_unit); 1640 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { 1641 kv.value.destroy(gpa); 1642 } 1643 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); 1644 } else { 1645 // We can trust the current information about this function. 1646 if (prev_failed) { 1647 return error.AnalysisFail; 1648 } 1649 if (func.analysisUnordered(ip).is_analyzed) return; 1650 } 1651 1652 if (zcu.comp.debugIncremental()) { 1653 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); 1654 info.last_update_gen = zcu.generation; 1655 info.deps.clearRetainingCapacity(); 1656 } 1657 1658 const owner_nav = ip.getNav(func.owner_nav); 1659 const unit_tracking = zcu.trackUnitSema( 1660 owner_nav.fqn.toSlice(ip), 1661 owner_nav.srcInst(ip), 1662 ); 1663 defer unit_tracking.end(zcu); 1664 1665 const ies_outdated, const new_failed = if (pt.analyzeFuncBody(func_index)) |result| 1666 .{ prev_failed or result.ies_outdated, false } 1667 else |err| switch (err) { 1668 error.AnalysisFail => res: { 1669 if (!zcu.failed_analysis.contains(anal_unit)) { 1670 // If this function caused the error, it would have an entry in `failed_analysis`. 1671 // Since it does not, this must be a transitive failure. 1672 try zcu.transitive_failed_analysis.put(gpa, anal_unit, {}); 1673 log.debug("mark transitive analysis failure for {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1674 } 1675 // We consider the IES to be outdated if the function previously succeeded analysis; in this case, 1676 // we need to re-analyze dependants to ensure they hit a transitive error here, rather than reporting 1677 // a different error later (which may now be invalid). 1678 break :res .{ !prev_failed, true }; 1679 }, 1680 error.OutOfMemory => { 1681 // TODO: it's unclear how to gracefully handle this. 1682 // To report the error cleanly, we need to add a message to `failed_analysis` and a 1683 // corresponding entry to `retryable_failures`; but either of these things is quite 1684 // likely to OOM at this point. 1685 // If that happens, what do we do? Perhaps we could have a special field on `Zcu` 1686 // for reporting OOM errors without allocating. 1687 return error.OutOfMemory; 1688 }, 1689 error.Canceled => |e| return e, 1690 }; 1691 1692 if (was_outdated) { 1693 if (ies_outdated) { 1694 try zcu.markDependeeOutdated(.marked_po, .{ .interned = func_index }); 1695 } else { 1696 try zcu.markPoDependeeUpToDate(.{ .interned = func_index }); 1697 } 1698 } 1699 1700 if (new_failed) return error.AnalysisFail; 1701 } 1702 1703 fn analyzeFuncBody( 1704 pt: Zcu.PerThread, 1705 func_index: InternPool.Index, 1706 ) Zcu.SemaError!struct { ies_outdated: bool } { 1707 const zcu = pt.zcu; 1708 const gpa = zcu.gpa; 1709 const ip = &zcu.intern_pool; 1710 1711 const func = zcu.funcInfo(func_index); 1712 const anal_unit = AnalUnit.wrap(.{ .func = func_index }); 1713 1714 // Make sure that this function is still owned by the same `Nav`. Otherwise, analyzing 1715 // it would be a waste of time in the best case, and could cause codegen to give bogus 1716 // results in the worst case. 1717 1718 if (func.generic_owner == .none) { 1719 // Among another things, this ensures that the function's `zir_body_inst` is correct. 1720 try pt.ensureNavValUpToDate(func.owner_nav); 1721 if (ip.getNav(func.owner_nav).status.fully_resolved.val != func_index) { 1722 // This function is no longer referenced! There's no point in re-analyzing it. 1723 // Just mark a transitive failure and move on. 1724 return error.AnalysisFail; 1725 } 1726 } else { 1727 const go_nav = zcu.funcInfo(func.generic_owner).owner_nav; 1728 // Among another things, this ensures that the function's `zir_body_inst` is correct. 1729 try pt.ensureNavValUpToDate(go_nav); 1730 if (ip.getNav(go_nav).status.fully_resolved.val != func.generic_owner) { 1731 // The generic owner is no longer referenced, so this function is also unreferenced. 1732 // There's no point in re-analyzing it. Just mark a transitive failure and move on. 1733 return error.AnalysisFail; 1734 } 1735 } 1736 1737 // We'll want to remember what the IES used to be before the update for 1738 // dependency invalidation purposes. 1739 const old_resolved_ies = if (func.analysisUnordered(ip).inferred_error_set) 1740 func.resolvedErrorSetUnordered(ip) 1741 else 1742 .none; 1743 1744 log.debug("analyze and generate fn body {f}", .{zcu.fmtAnalUnit(anal_unit)}); 1745 1746 var air = try pt.analyzeFnBodyInner(func_index); 1747 errdefer air.deinit(gpa); 1748 1749 const ies_outdated = !func.analysisUnordered(ip).inferred_error_set or 1750 func.resolvedErrorSetUnordered(ip) != old_resolved_ies; 1751 1752 const comp = zcu.comp; 1753 1754 const dump_air = build_options.enable_debug_extensions and comp.verbose_air; 1755 const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); 1756 1757 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) { 1758 air.deinit(gpa); 1759 return .{ .ies_outdated = ies_outdated }; 1760 } 1761 1762 // This job depends on any resolve_type_fully jobs queued up before it. 1763 zcu.codegen_prog_node.increaseEstimatedTotalItems(1); 1764 comp.link_prog_node.increaseEstimatedTotalItems(1); 1765 try comp.queueJob(.{ .codegen_func = .{ 1766 .func = func_index, 1767 .air = air, 1768 } }); 1769 1770 return .{ .ies_outdated = ies_outdated }; 1771 } 1772 1773 pub fn semaMod(pt: Zcu.PerThread, mod: *Module) !void { 1774 dev.check(.sema); 1775 const file_index = pt.zcu.module_roots.get(mod).?.unwrap().?; 1776 const root_type = pt.zcu.fileRootType(file_index); 1777 if (root_type == .none) { 1778 return pt.semaFile(file_index); 1779 } 1780 } 1781 1782 fn createFileRootStruct( 1783 pt: Zcu.PerThread, 1784 file_index: Zcu.File.Index, 1785 namespace_index: Zcu.Namespace.Index, 1786 replace_existing: bool, 1787 ) Allocator.Error!InternPool.Index { 1788 const zcu = pt.zcu; 1789 const gpa = zcu.gpa; 1790 const io = zcu.comp.io; 1791 const ip = &zcu.intern_pool; 1792 const file = zcu.fileByIndex(file_index); 1793 const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; 1794 assert(extended.opcode == .struct_decl); 1795 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 1796 assert(!small.has_captures_len); 1797 assert(!small.has_backing_int); 1798 assert(small.layout == .auto); 1799 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; 1800 const fields_len = if (small.has_fields_len) blk: { 1801 const fields_len = file.zir.?.extra[extra_index]; 1802 extra_index += 1; 1803 break :blk fields_len; 1804 } else 0; 1805 const decls_len = if (small.has_decls_len) blk: { 1806 const decls_len = file.zir.?.extra[extra_index]; 1807 extra_index += 1; 1808 break :blk decls_len; 1809 } else 0; 1810 const decls = file.zir.?.bodySlice(extra_index, decls_len); 1811 extra_index += decls_len; 1812 1813 const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{ 1814 .file = file_index, 1815 .inst = .main_struct_inst, 1816 }); 1817 const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{ 1818 .layout = .auto, 1819 .fields_len = fields_len, 1820 .known_non_opv = small.known_non_opv, 1821 .requires_comptime = if (small.known_comptime_only) .yes else .unknown, 1822 .any_comptime_fields = small.any_comptime_fields, 1823 .any_default_inits = small.any_default_inits, 1824 .inits_resolved = false, 1825 .any_aligned_fields = small.any_aligned_fields, 1826 .key = .{ .declared = .{ 1827 .zir_index = tracked_inst, 1828 .captures = &.{}, 1829 } }, 1830 }, replace_existing)) { 1831 .existing => unreachable, // we wouldn't be analysing the file root if this type existed 1832 .wip => |wip| wip, 1833 }; 1834 errdefer wip_ty.cancel(ip, pt.tid); 1835 1836 wip_ty.setName(ip, try file.internFullyQualifiedName(pt), .none); 1837 ip.namespacePtr(namespace_index).owner_type = wip_ty.index; 1838 1839 if (zcu.comp.config.incremental) { 1840 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = tracked_inst }); 1841 } 1842 1843 try pt.scanNamespace(namespace_index, decls); 1844 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 1845 codegen_type: { 1846 if (file.mod.?.strip) break :codegen_type; 1847 // This job depends on any resolve_type_fully jobs queued up before it. 1848 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 1849 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 1850 } 1851 zcu.setFileRootType(file_index, wip_ty.index); 1852 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 1853 return wip_ty.finish(ip, namespace_index); 1854 } 1855 1856 /// Re-scan the namespace of a file's root struct type on an incremental update. 1857 /// The file must have successfully populated ZIR. 1858 /// If the file's root struct type is not populated (the file is unreferenced), nothing is done. 1859 /// This is called by `updateZirRefs` for all updated files before the main work loop. 1860 /// This function does not perform any semantic analysis. 1861 fn updateFileNamespace(pt: Zcu.PerThread, file_index: Zcu.File.Index) Allocator.Error!void { 1862 const zcu = pt.zcu; 1863 1864 const file = zcu.fileByIndex(file_index); 1865 const file_root_type = zcu.fileRootType(file_index); 1866 if (file_root_type == .none) return; 1867 1868 log.debug("updateFileNamespace mod={s} sub_file_path={s}", .{ 1869 file.mod.?.fully_qualified_name, 1870 file.sub_file_path, 1871 }); 1872 1873 const namespace_index = Type.fromInterned(file_root_type).getNamespaceIndex(zcu); 1874 const decls = decls: { 1875 const extended = file.zir.?.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; 1876 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 1877 1878 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len; 1879 extra_index += @intFromBool(small.has_fields_len); 1880 const decls_len = if (small.has_decls_len) blk: { 1881 const decls_len = file.zir.?.extra[extra_index]; 1882 extra_index += 1; 1883 break :blk decls_len; 1884 } else 0; 1885 break :decls file.zir.?.bodySlice(extra_index, decls_len); 1886 }; 1887 try pt.scanNamespace(namespace_index, decls); 1888 zcu.namespacePtr(namespace_index).generation = zcu.generation; 1889 } 1890 1891 fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void { 1892 const tracy = trace(@src()); 1893 defer tracy.end(); 1894 1895 const zcu = pt.zcu; 1896 const file = zcu.fileByIndex(file_index); 1897 assert(file.getMode() == .zig); 1898 assert(zcu.fileRootType(file_index) == .none); 1899 1900 assert(file.zir != null); 1901 1902 const new_namespace_index = try pt.createNamespace(.{ 1903 .parent = .none, 1904 .owner_type = undefined, // set in `createFileRootStruct` 1905 .file_scope = file_index, 1906 .generation = zcu.generation, 1907 }); 1908 const struct_ty = try pt.createFileRootStruct(file_index, new_namespace_index, false); 1909 errdefer zcu.intern_pool.remove(pt.tid, struct_ty); 1910 1911 if (zcu.comp.time_report) |*tr| { 1912 tr.stats.n_imported_files += 1; 1913 } 1914 } 1915 1916 /// Called by AstGen worker threads when an import is seen. If `new_file` is returned, the caller is 1917 /// then responsible for queueing a new AstGen job for the new file. 1918 /// Assumes that `comp.mutex` is NOT locked. It will be locked by this function where necessary. 1919 pub fn discoverImport( 1920 pt: Zcu.PerThread, 1921 importer_path: Compilation.Path, 1922 import_string: []const u8, 1923 ) Allocator.Error!union(enum) { 1924 module, 1925 existing_file: Zcu.File.Index, 1926 new_file: struct { 1927 index: Zcu.File.Index, 1928 file: *Zcu.File, 1929 }, 1930 } { 1931 const zcu = pt.zcu; 1932 const comp = zcu.comp; 1933 const io = comp.io; 1934 const gpa = comp.gpa; 1935 1936 if (!mem.endsWith(u8, import_string, ".zig") and !mem.endsWith(u8, import_string, ".zon")) { 1937 return .module; 1938 } 1939 1940 const new_path = try importer_path.upJoin(gpa, zcu.comp.dirs, import_string); 1941 errdefer new_path.deinit(gpa); 1942 1943 // We're about to do a GOP on `import_table`, so we need the mutex. 1944 comp.mutex.lockUncancelable(io); 1945 defer comp.mutex.unlock(io); 1946 1947 const gop = try zcu.import_table.getOrPutAdapted(gpa, new_path, Zcu.ImportTableAdapter{ .zcu = zcu }); 1948 errdefer _ = zcu.import_table.pop(); 1949 if (gop.found_existing) { 1950 new_path.deinit(gpa); // we didn't need it for `File.path` 1951 return .{ .existing_file = gop.key_ptr.* }; 1952 } 1953 1954 zcu.import_table.lockPointers(); 1955 defer zcu.import_table.unlockPointers(); 1956 1957 const new_file = try gpa.create(Zcu.File); 1958 errdefer gpa.destroy(new_file); 1959 1960 const new_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{ 1961 .bin_digest = new_path.digest(), 1962 .file = new_file, 1963 .root_type = .none, 1964 }); 1965 errdefer comptime unreachable; // because we don't remove the file from the internpool 1966 1967 gop.key_ptr.* = new_file_index; 1968 new_file.* = .{ 1969 .status = .never_loaded, 1970 .path = new_path, 1971 .stat = undefined, 1972 .is_builtin = false, 1973 .source = null, 1974 .tree = null, 1975 .zir = null, 1976 .zoir = null, 1977 .mod = null, 1978 .sub_file_path = undefined, 1979 .module_changed = false, 1980 .prev_zir = null, 1981 .zoir_invalidated = false, 1982 }; 1983 1984 return .{ .new_file = .{ 1985 .index = new_file_index, 1986 .file = new_file, 1987 } }; 1988 } 1989 1990 pub fn doImport( 1991 pt: Zcu.PerThread, 1992 /// This file must have its `mod` populated. 1993 importer: *Zcu.File, 1994 import_string: []const u8, 1995 ) error{ 1996 OutOfMemory, 1997 ModuleNotFound, 1998 IllegalZigImport, 1999 }!struct { 2000 file: Zcu.File.Index, 2001 module_root: ?*Module, 2002 } { 2003 const zcu = pt.zcu; 2004 const gpa = zcu.gpa; 2005 const imported_mod: ?*Module = m: { 2006 if (mem.eql(u8, import_string, "std")) break :m zcu.std_mod; 2007 if (mem.eql(u8, import_string, "root")) break :m zcu.root_mod; 2008 if (mem.eql(u8, import_string, "builtin")) { 2009 const opts = importer.mod.?.getBuiltinOptions(zcu.comp.config); 2010 break :m zcu.builtin_modules.get(opts.hash()).?; 2011 } 2012 break :m importer.mod.?.deps.get(import_string); 2013 }; 2014 if (imported_mod) |mod| { 2015 if (zcu.module_roots.get(mod).?.unwrap()) |file_index| { 2016 return .{ 2017 .file = file_index, 2018 .module_root = mod, 2019 }; 2020 } 2021 } 2022 if (!std.mem.endsWith(u8, import_string, ".zig") and 2023 !std.mem.endsWith(u8, import_string, ".zon")) 2024 { 2025 return error.ModuleNotFound; 2026 } 2027 const path = try importer.path.upJoin(gpa, zcu.comp.dirs, import_string); 2028 defer path.deinit(gpa); 2029 if (try path.isIllegalZigImport(gpa, zcu.comp.dirs)) { 2030 return error.IllegalZigImport; 2031 } 2032 return .{ 2033 .file = zcu.import_table.getKeyAdapted(path, Zcu.ImportTableAdapter{ .zcu = zcu }).?, 2034 .module_root = null, 2035 }; 2036 } 2037 /// This is called once during `Compilation.create` and never again. "builtin" modules don't yet 2038 /// exist, so are not added to `module_roots` here. They must be added when they are created. 2039 pub fn populateModuleRootTable(pt: Zcu.PerThread) error{ 2040 OutOfMemory, 2041 /// One of the specified modules had its root source file at an illegal path. 2042 IllegalZigImport, 2043 }!void { 2044 const zcu = pt.zcu; 2045 const comp = zcu.comp; 2046 const gpa = comp.gpa; 2047 const io = comp.io; 2048 2049 // We'll initially add [mod, undefined] pairs, and when we reach the pair while 2050 // iterating, rewrite the undefined value. 2051 const roots = &zcu.module_roots; 2052 roots.clearRetainingCapacity(); 2053 2054 // Start with: 2055 // * `std_mod`, which is the main root of analysis 2056 // * `root_mod`, which is `@import("root")` 2057 // * `main_mod`, which is a special analysis root in tests (and otherwise equal to `root_mod`) 2058 // All other modules will be found by traversing their dependency tables. 2059 try roots.ensureTotalCapacity(gpa, 3); 2060 roots.putAssumeCapacity(zcu.std_mod, undefined); 2061 roots.putAssumeCapacity(zcu.root_mod, undefined); 2062 roots.putAssumeCapacity(zcu.main_mod, undefined); 2063 var i: usize = 0; 2064 while (i < roots.count()) { 2065 const mod = roots.keys()[i]; 2066 try roots.ensureUnusedCapacity(gpa, mod.deps.count()); 2067 for (mod.deps.values()) |dep| { 2068 const gop = roots.getOrPutAssumeCapacity(dep); 2069 _ = gop; // we want to leave the value undefined if it was added 2070 } 2071 2072 const root_file_out = &roots.values()[i]; 2073 roots.lockPointers(); 2074 defer roots.unlockPointers(); 2075 2076 i += 1; 2077 2078 if (Zcu.File.modeFromPath(mod.root_src_path) == null) { 2079 root_file_out.* = .none; 2080 continue; 2081 } 2082 2083 const path = try mod.root.join(gpa, zcu.comp.dirs, mod.root_src_path); 2084 errdefer path.deinit(gpa); 2085 2086 if (try path.isIllegalZigImport(gpa, zcu.comp.dirs)) { 2087 return error.IllegalZigImport; 2088 } 2089 2090 const gop = try zcu.import_table.getOrPutAdapted(gpa, path, Zcu.ImportTableAdapter{ .zcu = zcu }); 2091 errdefer _ = zcu.import_table.pop(); 2092 2093 if (gop.found_existing) { 2094 path.deinit(gpa); 2095 root_file_out.* = gop.key_ptr.*.toOptional(); 2096 continue; 2097 } 2098 2099 zcu.import_table.lockPointers(); 2100 defer zcu.import_table.unlockPointers(); 2101 2102 const new_file = try gpa.create(Zcu.File); 2103 errdefer gpa.destroy(new_file); 2104 2105 const new_file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{ 2106 .bin_digest = path.digest(), 2107 .file = new_file, 2108 .root_type = .none, 2109 }); 2110 errdefer comptime unreachable; // because we don't remove the file from the internpool 2111 2112 gop.key_ptr.* = new_file_index; 2113 root_file_out.* = new_file_index.toOptional(); 2114 new_file.* = .{ 2115 .status = .never_loaded, 2116 .path = path, 2117 .stat = undefined, 2118 .is_builtin = false, 2119 .source = null, 2120 .tree = null, 2121 .zir = null, 2122 .zoir = null, 2123 .mod = null, 2124 .sub_file_path = undefined, 2125 .module_changed = false, 2126 .prev_zir = null, 2127 .zoir_invalidated = false, 2128 }; 2129 } 2130 } 2131 2132 /// Clears and re-populates `pt.zcu.alive_files`, and determines the module identity of every alive 2133 /// file. If a file's module changes, its `module_changed` flag is set for `updateZirRefs` to see. 2134 /// Also clears and re-populates `failed_imports` and `multi_module_err` based on the set of alive 2135 /// files. 2136 /// 2137 /// Live files are also added as file system inputs if necessary. 2138 /// 2139 /// Returns whether there is any live file which is failed. Howewver, this function does *not* 2140 /// modify `pt.zcu.skip_analysis_this_update`. 2141 /// 2142 /// If an error is returned, `pt.zcu.alive_files` might contain undefined values. 2143 pub fn computeAliveFiles(pt: Zcu.PerThread) Allocator.Error!bool { 2144 const zcu = pt.zcu; 2145 const comp = zcu.comp; 2146 const gpa = zcu.gpa; 2147 2148 var any_fatal_files = false; 2149 zcu.multi_module_err = null; 2150 zcu.failed_imports.clearRetainingCapacity(); 2151 zcu.alive_files.clearRetainingCapacity(); 2152 2153 // This function will iterate the keys of `alive_files`, adding new entries as it discovers 2154 // imports. Once a file is in `alive_files`, it has its `mod` field up-to-date. If conflicting 2155 // imports are discovered for a file, we will set `multi_module_err`. Crucially, this traversal 2156 // is single-threaded, and depends only on the order of the imports map from AstGen, which makes 2157 // its behavior (in terms of which multi module errors are discovered) entirely consistent in a 2158 // multi-threaded environment (where things like file indices could differ between compiler runs). 2159 2160 // The roots of our file liveness analysis will be the analysis roots. 2161 const analysis_roots = zcu.analysisRoots(); 2162 try zcu.alive_files.ensureTotalCapacity(gpa, analysis_roots.len); 2163 for (analysis_roots) |mod| { 2164 const file_index = zcu.module_roots.get(mod).?.unwrap() orelse continue; 2165 const file = zcu.fileByIndex(file_index); 2166 2167 file.mod = mod; 2168 file.sub_file_path = mod.root_src_path; 2169 2170 zcu.alive_files.putAssumeCapacityNoClobber(file_index, .{ .analysis_root = mod }); 2171 } 2172 2173 var live_check_idx: usize = 0; 2174 while (live_check_idx < zcu.alive_files.count()) { 2175 const file_idx = zcu.alive_files.keys()[live_check_idx]; 2176 const file = zcu.fileByIndex(file_idx); 2177 live_check_idx += 1; 2178 2179 switch (file.status) { 2180 .never_loaded => unreachable, // everything reachable is loaded by the AstGen workers 2181 .retryable_failure, .astgen_failure => any_fatal_files = true, 2182 .success => {}, 2183 } 2184 2185 try comp.appendFileSystemInput(file.path); 2186 2187 switch (file.getMode()) { 2188 .zig => {}, // continue to logic below 2189 .zon => continue, // ZON can't import anything 2190 } 2191 2192 if (file.status != .success) continue; // ZIR not valid if there was a file failure 2193 2194 const zir = file.zir.?; 2195 const imports_index = zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; 2196 if (imports_index == 0) continue; // this Zig file has no imports 2197 const extra = zir.extraData(Zir.Inst.Imports, imports_index); 2198 var extra_index = extra.end; 2199 try zcu.alive_files.ensureUnusedCapacity(gpa, extra.data.imports_len); 2200 for (0..extra.data.imports_len) |_| { 2201 const item = zir.extraData(Zir.Inst.Imports.Item, extra_index); 2202 extra_index = item.end; 2203 const import_path = zir.nullTerminatedString(item.data.name); 2204 2205 if (std.mem.eql(u8, import_path, "builtin")) { 2206 // We've not necessarily generated builtin modules yet, so `doImport` could fail. Instead, 2207 // create the module here. Then, since we know that `builtin.zig` doesn't have an error and 2208 // has no imports other than 'std', we can just continue onto the next import. 2209 try pt.updateBuiltinModule(file.mod.?.getBuiltinOptions(comp.config)); 2210 continue; 2211 } 2212 2213 const res = pt.doImport(file, import_path) catch |err| switch (err) { 2214 error.OutOfMemory => |e| return e, 2215 error.ModuleNotFound => { 2216 // It'd be nice if this were a file-level error, but allowing this turns out to 2217 // be quite important in practice, e.g. for optional dependencies whose import 2218 // is behind a comptime condition. So, the error here happens in `Sema` instead. 2219 continue; 2220 }, 2221 error.IllegalZigImport => { 2222 try zcu.failed_imports.append(gpa, .{ 2223 .file_index = file_idx, 2224 .import_string = item.data.name, 2225 .import_token = item.data.token, 2226 .kind = .illegal_zig_import, 2227 }); 2228 continue; 2229 }, 2230 }; 2231 2232 // If the import was not of a module, we propagate our own module. 2233 const imported_mod = res.module_root orelse file.mod.?; 2234 const imported_file = zcu.fileByIndex(res.file); 2235 2236 const imported_ref: Zcu.File.Reference = .{ .import = .{ 2237 .importer = file_idx, 2238 .tok = item.data.token, 2239 .module = res.module_root, 2240 } }; 2241 2242 const gop = zcu.alive_files.getOrPutAssumeCapacity(res.file); 2243 if (gop.found_existing) { 2244 // This means `imported_file.mod` is already populated. If it doesn't match 2245 // `imported_mod`, then this file exists in multiple modules. 2246 if (imported_file.mod.? != imported_mod) { 2247 // We only report the first multi-module error we see. Thanks to this traversal 2248 // being deterministic, this doesn't raise consistency issues. Moreover, it's a 2249 // useful behavior; we know that this error can be reached *without* realising 2250 // that any other files are multi-module, so it's probably approximately where 2251 // the problem "begins". Any compilation with a multi-module file is likely to 2252 // have a huge number of them by transitive imports, so just reporting this one 2253 // hopefully keeps the error focused. 2254 zcu.multi_module_err = .{ 2255 .file = file_idx, 2256 .modules = .{ imported_file.mod.?, imported_mod }, 2257 .refs = .{ gop.value_ptr.*, imported_ref }, 2258 }; 2259 // If we discover a multi-module error, it's the only error which matters, and we 2260 // can't discern any useful information about the file's own imports; so just do 2261 // an early exit now we've populated `zcu.multi_module_err`. 2262 return any_fatal_files; 2263 } 2264 continue; 2265 } 2266 // We're the first thing we've found referencing `res.file`. 2267 gop.value_ptr.* = imported_ref; 2268 if (imported_file.mod) |m| { 2269 if (m == imported_mod) { 2270 // Great, the module and sub path are already populated correctly. 2271 continue; 2272 } 2273 } 2274 // We need to set the file's module, meaning we also need to compute its sub path. 2275 // This string is externally managed and has a lifetime at least equal to the 2276 // lifetime of `imported_file`. `null` means the file is outside its module root. 2277 switch (imported_file.path.isNested(imported_mod.root)) { 2278 .yes => |sub_path| { 2279 if (imported_file.mod != null) { 2280 // There was a module from a previous update; instruct `updateZirRefs` to 2281 // invalidate everything. 2282 imported_file.module_changed = true; 2283 } 2284 imported_file.mod = imported_mod; 2285 imported_file.sub_file_path = sub_path; 2286 }, 2287 .different_roots, .no => { 2288 try zcu.failed_imports.append(gpa, .{ 2289 .file_index = file_idx, 2290 .import_string = item.data.name, 2291 .import_token = item.data.token, 2292 .kind = .file_outside_module_root, 2293 }); 2294 _ = zcu.alive_files.pop(); // we failed to populate `mod`/`sub_file_path` 2295 }, 2296 } 2297 } 2298 } 2299 2300 return any_fatal_files; 2301 } 2302 2303 /// Ensures that the `@import("builtin")` module corresponding to `opts` is available in 2304 /// `builtin_modules`, and that its file is populated. Also ensures the file on disk is 2305 /// up-to-date, setting a misc failure if updating it fails. 2306 /// Asserts that the imported `builtin.zig` has no ZIR errors, and that it has only one 2307 /// import, which is 'std'. 2308 pub fn updateBuiltinModule(pt: Zcu.PerThread, opts: Builtin) Allocator.Error!void { 2309 const zcu = pt.zcu; 2310 const comp = zcu.comp; 2311 const gpa = comp.gpa; 2312 const io = comp.io; 2313 2314 const gop = try zcu.builtin_modules.getOrPut(gpa, opts.hash()); 2315 if (gop.found_existing) return; // the `File` is up-to-date 2316 errdefer _ = zcu.builtin_modules.pop(); 2317 2318 const mod: *Module = try .createBuiltin(comp.arena, opts, comp.dirs); 2319 assert(std.mem.eql(u8, &mod.getBuiltinOptions(comp.config).hash(), gop.key_ptr)); // builtin is its own builtin 2320 2321 const path = try mod.root.join(gpa, comp.dirs, "builtin.zig"); 2322 errdefer path.deinit(gpa); 2323 2324 const file_gop = try zcu.import_table.getOrPutAdapted(gpa, path, Zcu.ImportTableAdapter{ .zcu = zcu }); 2325 // `Compilation.Path.isIllegalZigImport` checks guard file creation, so 2326 // there isn't an `import_table` entry for this path yet. 2327 assert(!file_gop.found_existing); 2328 errdefer _ = zcu.import_table.pop(); 2329 2330 try zcu.module_roots.ensureUnusedCapacity(gpa, 1); 2331 2332 const file = try gpa.create(Zcu.File); 2333 errdefer gpa.destroy(file); 2334 2335 file.* = .{ 2336 .status = .never_loaded, 2337 .stat = undefined, 2338 .path = path, 2339 .is_builtin = true, 2340 .source = null, 2341 .tree = null, 2342 .zir = null, 2343 .zoir = null, 2344 .mod = mod, 2345 .sub_file_path = "builtin.zig", 2346 .module_changed = false, 2347 .prev_zir = null, 2348 .zoir_invalidated = false, 2349 }; 2350 2351 const file_index = try zcu.intern_pool.createFile(gpa, io, pt.tid, .{ 2352 .bin_digest = path.digest(), 2353 .file = file, 2354 .root_type = .none, 2355 }); 2356 2357 gop.value_ptr.* = mod; 2358 file_gop.key_ptr.* = file_index; 2359 zcu.module_roots.putAssumeCapacityNoClobber(mod, file_index.toOptional()); 2360 try opts.populateFile(gpa, file); 2361 2362 assert(file.status == .success); 2363 assert(!file.zir.?.hasCompileErrors()); 2364 { 2365 // Check that it has only one import, which is 'std'. 2366 const imports_idx = file.zir.?.extra[@intFromEnum(Zir.ExtraIndex.imports)]; 2367 assert(imports_idx != 0); // there is an import 2368 const extra = file.zir.?.extraData(Zir.Inst.Imports, imports_idx); 2369 assert(extra.data.imports_len == 1); // there is exactly one import 2370 const item = file.zir.?.extraData(Zir.Inst.Imports.Item, extra.end); 2371 const import_path = file.zir.?.nullTerminatedString(item.data.name); 2372 assert(mem.eql(u8, import_path, "std")); // the single import is of 'std' 2373 } 2374 2375 Builtin.updateFileOnDisk(file, comp) catch |err| comp.setMiscFailure( 2376 .write_builtin_zig, 2377 "unable to write '{f}': {s}", 2378 .{ file.path.fmt(comp), @errorName(err) }, 2379 ); 2380 } 2381 2382 pub fn embedFile( 2383 pt: Zcu.PerThread, 2384 cur_file: *Zcu.File, 2385 import_string: []const u8, 2386 ) error{ 2387 OutOfMemory, 2388 Canceled, 2389 ImportOutsideModulePath, 2390 CurrentWorkingDirectoryUnlinked, 2391 }!Zcu.EmbedFile.Index { 2392 const zcu = pt.zcu; 2393 const gpa = zcu.gpa; 2394 2395 const opt_mod: ?*Module = m: { 2396 if (mem.eql(u8, import_string, "std")) break :m zcu.std_mod; 2397 if (mem.eql(u8, import_string, "root")) break :m zcu.root_mod; 2398 if (mem.eql(u8, import_string, "builtin")) { 2399 const opts = cur_file.mod.?.getBuiltinOptions(zcu.comp.config); 2400 break :m zcu.builtin_modules.get(opts.hash()).?; 2401 } 2402 break :m cur_file.mod.?.deps.get(import_string); 2403 }; 2404 if (opt_mod) |mod| { 2405 const path = try mod.root.join(gpa, zcu.comp.dirs, mod.root_src_path); 2406 errdefer path.deinit(gpa); 2407 2408 const gop = try zcu.embed_table.getOrPutAdapted(gpa, path, Zcu.EmbedTableAdapter{}); 2409 if (gop.found_existing) { 2410 path.deinit(gpa); // we're not using this key 2411 return @enumFromInt(gop.index); 2412 } 2413 errdefer _ = zcu.embed_table.pop(); 2414 gop.key_ptr.* = try pt.newEmbedFile(path); 2415 return @enumFromInt(gop.index); 2416 } 2417 2418 const embed_file: *Zcu.EmbedFile, const embed_file_idx: Zcu.EmbedFile.Index = ef: { 2419 const path = try cur_file.path.upJoin(gpa, zcu.comp.dirs, import_string); 2420 errdefer path.deinit(gpa); 2421 const gop = try zcu.embed_table.getOrPutAdapted(gpa, path, Zcu.EmbedTableAdapter{}); 2422 if (gop.found_existing) { 2423 path.deinit(gpa); // we're not using this key 2424 break :ef .{ gop.key_ptr.*, @enumFromInt(gop.index) }; 2425 } else { 2426 errdefer _ = zcu.embed_table.pop(); 2427 gop.key_ptr.* = try pt.newEmbedFile(path); 2428 break :ef .{ gop.key_ptr.*, @enumFromInt(gop.index) }; 2429 } 2430 }; 2431 2432 switch (embed_file.path.isNested(cur_file.mod.?.root)) { 2433 .yes => {}, 2434 .different_roots, .no => return error.ImportOutsideModulePath, 2435 } 2436 2437 return embed_file_idx; 2438 } 2439 2440 pub fn updateEmbedFile( 2441 pt: Zcu.PerThread, 2442 ef: *Zcu.EmbedFile, 2443 /// If not `null`, the interned file data is stored here, if it was loaded. 2444 /// `newEmbedFile` uses this to add the file to the `whole` cache manifest. 2445 ip_str_out: ?*?InternPool.String, 2446 ) Allocator.Error!void { 2447 pt.updateEmbedFileInner(ef, ip_str_out) catch |err| switch (err) { 2448 error.OutOfMemory => |e| return e, 2449 else => |e| { 2450 ef.val = .none; 2451 ef.err = e; 2452 ef.stat = undefined; 2453 }, 2454 }; 2455 } 2456 2457 fn updateEmbedFileInner( 2458 pt: Zcu.PerThread, 2459 ef: *Zcu.EmbedFile, 2460 ip_str_out: ?*?InternPool.String, 2461 ) !void { 2462 const tid = pt.tid; 2463 const zcu = pt.zcu; 2464 const gpa = zcu.gpa; 2465 const io = zcu.comp.io; 2466 const ip = &zcu.intern_pool; 2467 2468 var file = f: { 2469 const dir, const sub_path = ef.path.openInfo(zcu.comp.dirs); 2470 break :f try dir.openFile(io, sub_path, .{}); 2471 }; 2472 defer file.close(io); 2473 2474 const stat: Cache.File.Stat = .fromFs(try file.stat(io)); 2475 2476 if (ef.val != .none) { 2477 const old_stat = ef.stat; 2478 const unchanged_metadata = 2479 stat.size == old_stat.size and 2480 stat.mtime.nanoseconds == old_stat.mtime.nanoseconds and 2481 stat.inode == old_stat.inode; 2482 if (unchanged_metadata) return; 2483 } 2484 2485 const size = std.math.cast(usize, stat.size) orelse return error.FileTooBig; 2486 const size_plus_one = std.math.add(usize, size, 1) catch return error.FileTooBig; 2487 2488 // The loaded bytes of the file, including a sentinel 0 byte. 2489 const ip_str: InternPool.String = str: { 2490 const string_bytes = ip.getLocal(tid).getMutableStringBytes(gpa, io); 2491 const old_len = string_bytes.mutate.len; 2492 errdefer string_bytes.shrinkRetainingCapacity(old_len); 2493 const bytes = (try string_bytes.addManyAsSlice(size_plus_one))[0]; 2494 var fr = file.reader(io, &.{}); 2495 fr.size = stat.size; 2496 fr.interface.readSliceAll(bytes[0..size]) catch |err| switch (err) { 2497 error.ReadFailed => return fr.err.?, 2498 error.EndOfStream => return error.UnexpectedEof, 2499 }; 2500 bytes[size] = 0; 2501 break :str try ip.getOrPutTrailingString(gpa, io, tid, @intCast(bytes.len), .maybe_embedded_nulls); 2502 }; 2503 if (ip_str_out) |p| p.* = ip_str; 2504 2505 const array_ty = try pt.arrayType(.{ 2506 .len = size, 2507 .sentinel = .zero_u8, 2508 .child = .u8_type, 2509 }); 2510 const ptr_ty = try pt.singleConstPtrType(array_ty); 2511 2512 const array_val = try pt.intern(.{ .aggregate = .{ 2513 .ty = array_ty.toIntern(), 2514 .storage = .{ .bytes = ip_str }, 2515 } }); 2516 const ptr_val = try pt.intern(.{ .ptr = .{ 2517 .ty = ptr_ty.toIntern(), 2518 .base_addr = .{ .uav = .{ 2519 .val = array_val, 2520 .orig_ty = ptr_ty.toIntern(), 2521 } }, 2522 .byte_offset = 0, 2523 } }); 2524 2525 ef.val = ptr_val; 2526 ef.err = null; 2527 ef.stat = stat; 2528 } 2529 2530 /// Assumes that `path` is allocated into `gpa`. Takes ownership of `path` on success. 2531 fn newEmbedFile( 2532 pt: Zcu.PerThread, 2533 path: Compilation.Path, 2534 ) !*Zcu.EmbedFile { 2535 const zcu = pt.zcu; 2536 const comp = zcu.comp; 2537 const io = comp.io; 2538 const gpa = comp.gpa; 2539 const ip = &zcu.intern_pool; 2540 2541 const new_file = try gpa.create(Zcu.EmbedFile); 2542 errdefer gpa.destroy(new_file); 2543 2544 new_file.* = .{ 2545 .path = path, 2546 .val = .none, 2547 .err = null, 2548 .stat = undefined, 2549 }; 2550 2551 var opt_ip_str: ?InternPool.String = null; 2552 try pt.updateEmbedFile(new_file, &opt_ip_str); 2553 2554 try comp.appendFileSystemInput(path); 2555 2556 // Add the file contents to the `whole` cache manifest if necessary. 2557 cache: { 2558 const whole = switch (zcu.comp.cache_use) { 2559 .whole => |whole| whole, 2560 .incremental, .none => break :cache, 2561 }; 2562 const man = whole.cache_manifest orelse break :cache; 2563 const ip_str = opt_ip_str orelse break :cache; // this will be a compile error 2564 2565 const array_len = Value.fromInterned(new_file.val).typeOf(zcu).childType(zcu).arrayLen(zcu); 2566 const contents = ip_str.toSlice(array_len, ip); 2567 2568 const path_str = try path.toAbsolute(comp.dirs, gpa); 2569 defer gpa.free(path_str); 2570 2571 try whole.cache_manifest_mutex.lock(io); 2572 defer whole.cache_manifest_mutex.unlock(io); 2573 2574 man.addFilePostContents(path_str, contents, new_file.stat) catch |err| switch (err) { 2575 error.Unexpected => unreachable, 2576 else => |e| return e, 2577 }; 2578 } 2579 2580 return new_file; 2581 } 2582 2583 pub fn scanNamespace( 2584 pt: Zcu.PerThread, 2585 namespace_index: Zcu.Namespace.Index, 2586 decls: []const Zir.Inst.Index, 2587 ) Allocator.Error!void { 2588 const tracy = trace(@src()); 2589 defer tracy.end(); 2590 2591 const zcu = pt.zcu; 2592 const ip = &zcu.intern_pool; 2593 const gpa = zcu.gpa; 2594 const namespace = zcu.namespacePtr(namespace_index); 2595 2596 const tracked_unit = zcu.trackUnitSema( 2597 Type.fromInterned(namespace.owner_type).containerTypeName(ip).toSlice(ip), 2598 null, 2599 ); 2600 defer tracked_unit.end(zcu); 2601 2602 // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather 2603 // than their name. We'll build an efficient mapping now, then discard the current `decls`. 2604 // We map to the `AnalUnit`, since not every declaration has a `Nav`. 2605 var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit) = .empty; 2606 defer existing_by_inst.deinit(gpa); 2607 2608 try existing_by_inst.ensureTotalCapacity(gpa, @intCast( 2609 namespace.pub_decls.count() + namespace.priv_decls.count() + 2610 namespace.comptime_decls.items.len + 2611 namespace.test_decls.items.len, 2612 )); 2613 2614 for (namespace.pub_decls.keys()) |nav| { 2615 const zir_index = ip.getNav(nav).analysis.?.zir_index; 2616 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav })); 2617 } 2618 for (namespace.priv_decls.keys()) |nav| { 2619 const zir_index = ip.getNav(nav).analysis.?.zir_index; 2620 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav })); 2621 } 2622 for (namespace.comptime_decls.items) |cu| { 2623 const zir_index = ip.getComptimeUnit(cu).zir_index; 2624 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .@"comptime" = cu })); 2625 } 2626 for (namespace.test_decls.items) |nav| { 2627 const zir_index = ip.getNav(nav).analysis.?.zir_index; 2628 existing_by_inst.putAssumeCapacityNoClobber(zir_index, .wrap(.{ .nav_val = nav })); 2629 // This test will be re-added to `test_functions` later on if it's still alive. Remove it for now. 2630 _ = zcu.test_functions.swapRemove(nav); 2631 } 2632 2633 var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .empty; 2634 defer seen_decls.deinit(gpa); 2635 2636 namespace.pub_decls.clearRetainingCapacity(); 2637 namespace.priv_decls.clearRetainingCapacity(); 2638 namespace.comptime_decls.clearRetainingCapacity(); 2639 namespace.test_decls.clearRetainingCapacity(); 2640 2641 var scan_decl_iter: ScanDeclIter = .{ 2642 .pt = pt, 2643 .namespace_index = namespace_index, 2644 .seen_decls = &seen_decls, 2645 .existing_by_inst = &existing_by_inst, 2646 .pass = .named, 2647 }; 2648 for (decls) |decl_inst| { 2649 try scan_decl_iter.scanDecl(decl_inst); 2650 } 2651 scan_decl_iter.pass = .unnamed; 2652 for (decls) |decl_inst| { 2653 try scan_decl_iter.scanDecl(decl_inst); 2654 } 2655 } 2656 2657 const ScanDeclIter = struct { 2658 pt: Zcu.PerThread, 2659 namespace_index: Zcu.Namespace.Index, 2660 seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), 2661 existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, InternPool.AnalUnit), 2662 /// Decl scanning is run in two passes, so that we can detect when a generated 2663 /// name would clash with an explicit name and use a different one. 2664 pass: enum { named, unnamed }, 2665 unnamed_test_index: usize = 0, 2666 2667 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString { 2668 const pt = iter.pt; 2669 const ip = &pt.zcu.intern_pool; 2670 const comp = pt.zcu.comp; 2671 const gpa = comp.gpa; 2672 const io = comp.io; 2673 var name = try ip.getOrPutStringFmt(gpa, io, pt.tid, fmt, args, .no_embedded_nulls); 2674 var gop = try iter.seen_decls.getOrPut(gpa, name); 2675 var next_suffix: u32 = 0; 2676 while (gop.found_existing) { 2677 name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls); 2678 gop = try iter.seen_decls.getOrPut(gpa, name); 2679 next_suffix += 1; 2680 } 2681 return name; 2682 } 2683 2684 fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { 2685 const tracy = trace(@src()); 2686 defer tracy.end(); 2687 2688 const pt = iter.pt; 2689 const zcu = pt.zcu; 2690 const comp = zcu.comp; 2691 const namespace_index = iter.namespace_index; 2692 const namespace = zcu.namespacePtr(namespace_index); 2693 const gpa = comp.gpa; 2694 const io = comp.io; 2695 const file = namespace.fileScope(zcu); 2696 const zir = file.zir.?; 2697 const ip = &zcu.intern_pool; 2698 2699 const decl = zir.getDeclaration(decl_inst); 2700 2701 const maybe_name: InternPool.OptionalNullTerminatedString = switch (decl.kind) { 2702 .@"comptime" => name: { 2703 if (iter.pass != .unnamed) return; 2704 break :name .none; 2705 }, 2706 .unnamed_test => name: { 2707 if (iter.pass != .unnamed) return; 2708 const i = iter.unnamed_test_index; 2709 iter.unnamed_test_index += 1; 2710 break :name (try iter.avoidNameConflict("test_{d}", .{i})).toOptional(); 2711 }, 2712 .@"test", .decltest => |kind| name: { 2713 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. 2714 if (iter.pass != .unnamed) return; 2715 const prefix = @tagName(kind); 2716 break :name (try iter.avoidNameConflict("{s}.{s}", .{ prefix, zir.nullTerminatedString(decl.name) })).toOptional(); 2717 }, 2718 .@"const", .@"var" => name: { 2719 if (iter.pass != .named) return; 2720 const name = try ip.getOrPutString( 2721 gpa, 2722 io, 2723 pt.tid, 2724 zir.nullTerminatedString(decl.name), 2725 .no_embedded_nulls, 2726 ); 2727 try iter.seen_decls.putNoClobber(gpa, name, {}); 2728 break :name name.toOptional(); 2729 }, 2730 }; 2731 2732 const tracked_inst = try ip.trackZir(gpa, io, pt.tid, .{ 2733 .file = namespace.file_scope, 2734 .inst = decl_inst, 2735 }); 2736 2737 const existing_unit = iter.existing_by_inst.get(tracked_inst); 2738 2739 const unit, const want_analysis = switch (decl.kind) { 2740 .@"comptime" => unit: { 2741 const cu = if (existing_unit) |eu| 2742 eu.unwrap().@"comptime" 2743 else 2744 try ip.createComptimeUnit(gpa, io, pt.tid, tracked_inst, namespace_index); 2745 2746 const unit: AnalUnit = .wrap(.{ .@"comptime" = cu }); 2747 2748 try namespace.comptime_decls.append(gpa, cu); 2749 2750 if (existing_unit == null) { 2751 // For a `comptime` declaration, whether to analyze is based solely on whether the unit 2752 // is outdated. So, add this fresh one to `outdated` and `outdated_ready`. 2753 try zcu.outdated.ensureUnusedCapacity(gpa, 1); 2754 try zcu.outdated_ready.ensureUnusedCapacity(gpa, 1); 2755 zcu.outdated.putAssumeCapacityNoClobber(unit, 0); 2756 zcu.outdated_ready.putAssumeCapacityNoClobber(unit, {}); 2757 } 2758 2759 break :unit .{ unit, true }; 2760 }, 2761 else => unit: { 2762 const name = maybe_name.unwrap().?; 2763 const fqn = try namespace.internFullyQualifiedName(ip, gpa, io, pt.tid, name); 2764 const nav = if (existing_unit) |eu| eu.unwrap().nav_val else nav: { 2765 const nav = try ip.createDeclNav(gpa, io, pt.tid, name, fqn, tracked_inst, namespace_index); 2766 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); 2767 break :nav nav; 2768 }; 2769 2770 const unit: AnalUnit = .wrap(.{ .nav_val = nav }); 2771 2772 assert(ip.getNav(nav).name == name); 2773 assert(ip.getNav(nav).fqn == fqn); 2774 2775 const want_analysis = switch (decl.kind) { 2776 .@"comptime" => unreachable, 2777 .unnamed_test, .@"test", .decltest => a: { 2778 const is_named = decl.kind != .unnamed_test; 2779 try namespace.test_decls.append(gpa, nav); 2780 // TODO: incremental compilation! 2781 // * remove from `test_functions` if no longer matching filter 2782 // * add to `test_functions` if newly passing filter 2783 // This logic is unaware of incremental: we'll end up with duplicates. 2784 // Perhaps we should add all test indiscriminately and filter at the end of the update. 2785 if (!comp.config.is_test) break :a false; 2786 if (file.mod != zcu.main_mod) break :a false; 2787 if (is_named and comp.test_filters.len > 0) { 2788 const fqn_slice = fqn.toSlice(ip); 2789 for (comp.test_filters) |test_filter| { 2790 if (std.mem.indexOf(u8, fqn_slice, test_filter) != null) break; 2791 } else break :a false; 2792 } 2793 try zcu.test_functions.put(gpa, nav, {}); 2794 break :a true; 2795 }, 2796 .@"const", .@"var" => a: { 2797 if (decl.is_pub) { 2798 try namespace.pub_decls.putContext(gpa, nav, {}, .{ .zcu = zcu }); 2799 } else { 2800 try namespace.priv_decls.putContext(gpa, nav, {}, .{ .zcu = zcu }); 2801 } 2802 break :a false; 2803 }, 2804 }; 2805 break :unit .{ unit, want_analysis }; 2806 }, 2807 }; 2808 2809 if (existing_unit == null and (want_analysis or decl.linkage == .@"export")) { 2810 log.debug( 2811 "scanDecl queue analyze_comptime_unit file='{s}' unit={f}", 2812 .{ namespace.fileScope(zcu).sub_file_path, zcu.fmtAnalUnit(unit) }, 2813 ); 2814 try comp.queueJob(.{ .analyze_comptime_unit = unit }); 2815 } 2816 } 2817 }; 2818 2819 fn analyzeFnBodyInner(pt: Zcu.PerThread, func_index: InternPool.Index) Zcu.SemaError!Air { 2820 const tracy = trace(@src()); 2821 defer tracy.end(); 2822 2823 const zcu = pt.zcu; 2824 const comp = zcu.comp; 2825 const gpa = comp.gpa; 2826 const io = comp.io; 2827 const ip = &zcu.intern_pool; 2828 2829 const anal_unit = AnalUnit.wrap(.{ .func = func_index }); 2830 const func = zcu.funcInfo(func_index); 2831 const inst_info = func.zir_body_inst.resolveFull(ip) orelse return error.AnalysisFail; 2832 const file = zcu.fileByIndex(inst_info.file); 2833 const zir = file.zir.?; 2834 2835 try zcu.analysis_in_progress.putNoClobber(gpa, anal_unit, {}); 2836 errdefer _ = zcu.analysis_in_progress.swapRemove(anal_unit); 2837 2838 func.setAnalyzed(ip, io); 2839 if (func.analysisUnordered(ip).inferred_error_set) { 2840 func.setResolvedErrorSet(ip, io, .none); 2841 } 2842 2843 if (zcu.comp.time_report) |*tr| { 2844 if (func.generic_owner != .none) { 2845 tr.stats.n_generic_instances += 1; 2846 } 2847 } 2848 2849 // This is the `Nau` corresponding to the `declaration` instruction which the function or its generic owner originates from. 2850 const decl_nav = ip.getNav(if (func.generic_owner == .none) 2851 func.owner_nav 2852 else 2853 zcu.funcInfo(func.generic_owner).owner_nav); 2854 2855 const func_nav = ip.getNav(func.owner_nav); 2856 2857 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); 2858 2859 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 2860 defer analysis_arena.deinit(); 2861 2862 var comptime_err_ret_trace = std.array_list.Managed(Zcu.LazySrcLoc).init(gpa); 2863 defer comptime_err_ret_trace.deinit(); 2864 2865 // In the case of a generic function instance, this is the type of the 2866 // instance, which has comptime parameters elided. In other words, it is 2867 // the runtime-known parameters only, not to be confused with the 2868 // generic_owner function type, which potentially has more parameters, 2869 // including comptime parameters. 2870 const fn_ty = Type.fromInterned(func.ty); 2871 const fn_ty_info = zcu.typeToFunc(fn_ty).?; 2872 2873 var sema: Sema = .{ 2874 .pt = pt, 2875 .gpa = gpa, 2876 .arena = analysis_arena.allocator(), 2877 .code = zir, 2878 .owner = anal_unit, 2879 .func_index = func_index, 2880 .func_is_naked = fn_ty_info.cc == .naked, 2881 .fn_ret_ty = Type.fromInterned(fn_ty_info.return_type), 2882 .fn_ret_ty_ies = null, 2883 .branch_quota = @max(func.branchQuotaUnordered(ip), Sema.default_branch_quota), 2884 .comptime_err_ret_trace = &comptime_err_ret_trace, 2885 }; 2886 defer sema.deinit(); 2887 2888 // Every runtime function has a dependency on the source of the Decl it originates from. 2889 // It also depends on the value of its owner Decl. 2890 try sema.declareDependency(.{ .src_hash = decl_nav.analysis.?.zir_index }); 2891 try sema.declareDependency(.{ .nav_val = func.owner_nav }); 2892 2893 if (func.analysisUnordered(ip).inferred_error_set) { 2894 const ies = try analysis_arena.allocator().create(Sema.InferredErrorSet); 2895 ies.* = .{ .func = func_index }; 2896 sema.fn_ret_ty_ies = ies; 2897 } 2898 2899 // reset in case calls to errorable functions are removed. 2900 ip.funcSetHasErrorTrace(io, func_index, fn_ty_info.cc == .auto); 2901 2902 // First few indexes of extra are reserved and set at the end. 2903 const reserved_count = @typeInfo(Air.ExtraIndex).@"enum".fields.len; 2904 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count); 2905 sema.air_extra.items.len += reserved_count; 2906 2907 var inner_block: Sema.Block = .{ 2908 .parent = null, 2909 .sema = &sema, 2910 .namespace = decl_nav.analysis.?.namespace, 2911 .instructions = .{}, 2912 .inlining = null, 2913 .comptime_reason = null, 2914 .src_base_inst = decl_nav.analysis.?.zir_index, 2915 .type_name_ctx = func_nav.fqn, 2916 }; 2917 defer inner_block.instructions.deinit(gpa); 2918 2919 const fn_info = sema.code.getFnInfo(func.zirBodyInstUnordered(ip).resolve(ip) orelse return error.AnalysisFail); 2920 2921 // Here we are performing "runtime semantic analysis" for a function body, which means 2922 // we must map the parameter ZIR instructions to `arg` AIR instructions. 2923 // AIR requires the `arg` parameters to be the first N instructions. 2924 // This could be a generic function instantiation, however, in which case we need to 2925 // map the comptime parameters to constant values and only emit arg AIR instructions 2926 // for the runtime ones. 2927 const runtime_params_len = fn_ty_info.param_types.len; 2928 try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len); 2929 try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len); 2930 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); 2931 2932 // In the case of a generic function instance, pre-populate all the comptime args. 2933 if (func.comptime_args.len != 0) { 2934 for ( 2935 fn_info.param_body[0..func.comptime_args.len], 2936 func.comptime_args.get(ip), 2937 ) |inst, comptime_arg| { 2938 if (comptime_arg == .none) continue; 2939 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.internedToRef(comptime_arg)); 2940 } 2941 } 2942 2943 const src_params_len = if (func.comptime_args.len != 0) 2944 func.comptime_args.len 2945 else 2946 runtime_params_len; 2947 2948 var runtime_param_index: usize = 0; 2949 for (fn_info.param_body[0..src_params_len], 0..) |inst, zir_param_index| { 2950 const gop = sema.inst_map.getOrPutAssumeCapacity(inst); 2951 if (gop.found_existing) continue; // provided above by comptime arg 2952 2953 const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index]; 2954 runtime_param_index += 1; 2955 2956 const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) { 2957 error.ComptimeReturn => unreachable, 2958 error.ComptimeBreak => unreachable, 2959 else => |e| return e, 2960 }; 2961 if (opt_opv) |opv| { 2962 gop.value_ptr.* = Air.internedToRef(opv.toIntern()); 2963 continue; 2964 } 2965 const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 2966 gop.value_ptr.* = arg_index.toRef(); 2967 inner_block.instructions.appendAssumeCapacity(arg_index); 2968 sema.air_instructions.appendAssumeCapacity(.{ 2969 .tag = .arg, 2970 .data = .{ .arg = .{ 2971 .ty = Air.internedToRef(param_ty), 2972 .zir_param_index = @intCast(zir_param_index), 2973 } }, 2974 }); 2975 } 2976 2977 const last_arg_index = inner_block.instructions.items.len; 2978 2979 // Save the error trace as our first action in the function. 2980 // If this is unnecessary after all, Liveness will clean it up for us. 2981 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&inner_block); 2982 sema.error_return_trace_index_on_fn_entry = error_return_trace_index; 2983 inner_block.error_return_trace_index = error_return_trace_index; 2984 2985 sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) { 2986 error.ComptimeReturn => unreachable, 2987 else => |e| return e, 2988 }; 2989 2990 for (sema.unresolved_inferred_allocs.keys()) |ptr_inst| { 2991 // The lack of a resolve_inferred_alloc means that this instruction 2992 // is unused so it just has to be a no-op. 2993 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ 2994 .tag = .alloc, 2995 .data = .{ .ty = .ptr_const_comptime_int }, 2996 }); 2997 } 2998 2999 func.setBranchHint(ip, io, sema.branch_hint orelse .none); 3000 3001 if (zcu.comp.config.any_error_tracing and func.analysisUnordered(ip).has_error_trace and fn_ty_info.cc != .auto) { 3002 // We're using an error trace, but didn't start out with one from the caller. 3003 // We'll have to create it at the start of the function. 3004 sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) { 3005 error.ComptimeReturn => unreachable, 3006 error.ComptimeBreak => unreachable, 3007 else => |e| return e, 3008 }; 3009 } 3010 3011 // Copy the block into place and mark that as the main block. 3012 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".fields.len + 3013 inner_block.instructions.items.len); 3014 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{ 3015 .body_len = @intCast(inner_block.instructions.items.len), 3016 }); 3017 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(inner_block.instructions.items)); 3018 sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index; 3019 3020 // Resolving inferred error sets is done *before* setting the function 3021 // state to success, so that "unable to resolve inferred error set" errors 3022 // can be emitted here. 3023 if (sema.fn_ret_ty_ies) |ies| { 3024 sema.resolveInferredErrorSetPtr(&inner_block, .{ 3025 .base_node_inst = inner_block.src_base_inst, 3026 .offset = Zcu.LazySrcLoc.Offset.nodeOffset(.zero), 3027 }, ies) catch |err| switch (err) { 3028 error.ComptimeReturn => unreachable, 3029 error.ComptimeBreak => unreachable, 3030 else => |e| return e, 3031 }; 3032 assert(ies.resolved != .none); 3033 func.setResolvedErrorSet(ip, io, ies.resolved); 3034 } 3035 3036 assert(zcu.analysis_in_progress.swapRemove(anal_unit)); 3037 3038 // Finally we must resolve the return type and parameter types so that backends 3039 // have full access to type information. 3040 // Crucially, this happens *after* we set the function state to success above, 3041 // so that dependencies on the function body will now be satisfied rather than 3042 // result in circular dependency errors. 3043 // TODO: this can go away once we fix backends having to resolve `StackTrace`. 3044 // The codegen timing guarantees that the parameter types will be populated. 3045 sema.resolveFnTypes(fn_ty, inner_block.nodeOffset(.zero)) catch |err| switch (err) { 3046 error.ComptimeReturn => unreachable, 3047 error.ComptimeBreak => unreachable, 3048 else => |e| return e, 3049 }; 3050 3051 try sema.flushExports(); 3052 3053 defer { 3054 sema.air_instructions = .empty; 3055 sema.air_extra = .empty; 3056 } 3057 return .{ 3058 .instructions = sema.air_instructions.slice(), 3059 .extra = sema.air_extra, 3060 }; 3061 } 3062 3063 pub fn createNamespace(pt: Zcu.PerThread, initialization: Zcu.Namespace) !Zcu.Namespace.Index { 3064 const comp = pt.zcu.comp; 3065 return pt.zcu.intern_pool.createNamespace(comp.gpa, comp.io, pt.tid, initialization); 3066 } 3067 3068 pub fn destroyNamespace(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) void { 3069 return pt.zcu.intern_pool.destroyNamespace(pt.tid, namespace_index); 3070 } 3071 3072 pub fn getErrorValue( 3073 pt: Zcu.PerThread, 3074 name: InternPool.NullTerminatedString, 3075 ) Allocator.Error!Zcu.ErrorInt { 3076 const comp = pt.zcu.comp; 3077 return pt.zcu.intern_pool.getErrorValue(comp.gpa, comp.io, pt.tid, name); 3078 } 3079 3080 pub fn getErrorValueFromSlice(pt: Zcu.PerThread, name: []const u8) Allocator.Error!Zcu.ErrorInt { 3081 const comp = pt.zcu.comp; 3082 const gpa = comp.gpa; 3083 const io = comp.io; 3084 return pt.getErrorValue(try pt.zcu.intern_pool.getOrPutString(gpa, io, name)); 3085 } 3086 3087 /// Removes any entry from `Zcu.failed_files` associated with `file`. Acquires `Compilation.mutex` as needed. 3088 /// `file.zir` must be unchanged from the last update, as it is used to determine if there is such an entry. 3089 fn lockAndClearFileCompileError(pt: Zcu.PerThread, file_index: Zcu.File.Index, file: *Zcu.File) void { 3090 const maybe_has_error = switch (file.status) { 3091 .never_loaded => false, 3092 .retryable_failure => true, 3093 .astgen_failure => true, 3094 .success => switch (file.getMode()) { 3095 .zig => has_error: { 3096 const zir = file.zir orelse break :has_error false; 3097 break :has_error zir.hasCompileErrors(); 3098 }, 3099 .zon => has_error: { 3100 const zoir = file.zoir orelse break :has_error false; 3101 break :has_error zoir.hasCompileErrors(); 3102 }, 3103 }, 3104 }; 3105 3106 // If runtime safety is on, let's quickly lock the mutex and check anyway. 3107 if (!maybe_has_error and !std.debug.runtime_safety) { 3108 return; 3109 } 3110 3111 const comp = pt.zcu.comp; 3112 const io = comp.io; 3113 comp.mutex.lockUncancelable(io); 3114 defer comp.mutex.unlock(io); 3115 if (pt.zcu.failed_files.fetchSwapRemove(file_index)) |kv| { 3116 assert(maybe_has_error); // the runtime safety case above 3117 if (kv.value) |msg| pt.zcu.gpa.free(msg); // delete previous error message 3118 } 3119 } 3120 3121 /// Called from `Compilation.update`, after everything is done, just before 3122 /// reporting compile errors. In this function we emit exported symbol collision 3123 /// errors and communicate exported symbols to the linker backend. 3124 pub fn processExports(pt: Zcu.PerThread) !void { 3125 const zcu = pt.zcu; 3126 const gpa = zcu.gpa; 3127 3128 if (zcu.single_exports.count() == 0 and zcu.multi_exports.count() == 0) { 3129 // We can avoid a call to `resolveReferences` in this case. 3130 return; 3131 } 3132 3133 // First, construct a mapping of every exported value and Nav to the indices of all its different exports. 3134 var nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, std.ArrayList(Zcu.Export.Index)) = .empty; 3135 var uav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, std.ArrayList(Zcu.Export.Index)) = .empty; 3136 defer { 3137 for (nav_exports.values()) |*exports| { 3138 exports.deinit(gpa); 3139 } 3140 nav_exports.deinit(gpa); 3141 for (uav_exports.values()) |*exports| { 3142 exports.deinit(gpa); 3143 } 3144 uav_exports.deinit(gpa); 3145 } 3146 3147 // We note as a heuristic: 3148 // * It is rare to export a value. 3149 // * It is rare for one Nav to be exported multiple times. 3150 // So, this ensureTotalCapacity serves as a reasonable (albeit very approximate) optimization. 3151 try nav_exports.ensureTotalCapacity(gpa, zcu.single_exports.count() + zcu.multi_exports.count()); 3152 3153 const unit_references = try zcu.resolveReferences(); 3154 3155 for (zcu.single_exports.keys(), zcu.single_exports.values()) |exporter, export_idx| { 3156 const exp = export_idx.ptr(zcu); 3157 if (!unit_references.contains(exporter)) { 3158 // This export might already have been sent to the linker on a previous update, in which case we need to delete it. 3159 // The linker export API should be modified to eliminate this call. #23616 3160 if (zcu.comp.bin_file) |lf| { 3161 if (zcu.llvm_object == null) { 3162 lf.deleteExport(exp.exported, exp.opts.name); 3163 } 3164 } 3165 continue; 3166 } 3167 const value_ptr, const found_existing = switch (exp.exported) { 3168 .nav => |nav| gop: { 3169 const gop = try nav_exports.getOrPut(gpa, nav); 3170 break :gop .{ gop.value_ptr, gop.found_existing }; 3171 }, 3172 .uav => |uav| gop: { 3173 const gop = try uav_exports.getOrPut(gpa, uav); 3174 break :gop .{ gop.value_ptr, gop.found_existing }; 3175 }, 3176 }; 3177 if (!found_existing) value_ptr.* = .{}; 3178 try value_ptr.append(gpa, export_idx); 3179 } 3180 3181 for (zcu.multi_exports.keys(), zcu.multi_exports.values()) |exporter, info| { 3182 const exports = zcu.all_exports.items[info.index..][0..info.len]; 3183 if (!unit_references.contains(exporter)) { 3184 // This export might already have been sent to the linker on a previous update, in which case we need to delete it. 3185 // The linker export API should be modified to eliminate this loop. #23616 3186 if (zcu.comp.bin_file) |lf| { 3187 if (zcu.llvm_object == null) { 3188 for (exports) |exp| { 3189 lf.deleteExport(exp.exported, exp.opts.name); 3190 } 3191 } 3192 } 3193 continue; 3194 } 3195 for (exports, info.index..) |exp, export_idx| { 3196 const value_ptr, const found_existing = switch (exp.exported) { 3197 .nav => |nav| gop: { 3198 const gop = try nav_exports.getOrPut(gpa, nav); 3199 break :gop .{ gop.value_ptr, gop.found_existing }; 3200 }, 3201 .uav => |uav| gop: { 3202 const gop = try uav_exports.getOrPut(gpa, uav); 3203 break :gop .{ gop.value_ptr, gop.found_existing }; 3204 }, 3205 }; 3206 if (!found_existing) value_ptr.* = .{}; 3207 try value_ptr.append(gpa, @enumFromInt(export_idx)); 3208 } 3209 } 3210 3211 // If there are compile errors, we won't call `updateExports`. Not only would it be redundant 3212 // work, but the linker may not have seen an exported `Nav` due to a compile error, so linker 3213 // implementations would have to handle that case. This early return avoids that. 3214 const skip_linker_work = zcu.comp.anyErrors(); 3215 3216 // Map symbol names to `Export` for name collision detection. 3217 var symbol_exports: SymbolExports = .{}; 3218 defer symbol_exports.deinit(gpa); 3219 3220 for (nav_exports.keys(), nav_exports.values()) |exported_nav, exports_list| { 3221 const exported: Zcu.Exported = .{ .nav = exported_nav }; 3222 try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work); 3223 } 3224 3225 for (uav_exports.keys(), uav_exports.values()) |exported_uav, exports_list| { 3226 const exported: Zcu.Exported = .{ .uav = exported_uav }; 3227 try pt.processExportsInner(&symbol_exports, exported, exports_list.items, skip_linker_work); 3228 } 3229 } 3230 3231 const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, Zcu.Export.Index); 3232 3233 fn processExportsInner( 3234 pt: Zcu.PerThread, 3235 symbol_exports: *SymbolExports, 3236 exported: Zcu.Exported, 3237 export_indices: []const Zcu.Export.Index, 3238 skip_linker_work: bool, 3239 ) error{OutOfMemory}!void { 3240 const zcu = pt.zcu; 3241 const gpa = zcu.gpa; 3242 const ip = &zcu.intern_pool; 3243 3244 for (export_indices) |export_idx| { 3245 const new_export = export_idx.ptr(zcu); 3246 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); 3247 if (gop.found_existing) { 3248 new_export.status = .failed_retryable; 3249 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); 3250 const msg = try Zcu.ErrorMsg.create(gpa, new_export.src, "exported symbol collision: {f}", .{ 3251 new_export.opts.name.fmt(ip), 3252 }); 3253 errdefer msg.destroy(gpa); 3254 const other_export = gop.value_ptr.ptr(zcu); 3255 try zcu.errNote(other_export.src, msg, "other symbol here", .{}); 3256 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); 3257 new_export.status = .failed; 3258 } else { 3259 gop.value_ptr.* = export_idx; 3260 } 3261 } 3262 3263 switch (exported) { 3264 .nav => |nav_index| if (failed: { 3265 const nav = ip.getNav(nav_index); 3266 if (zcu.failed_codegen.contains(nav_index)) break :failed true; 3267 if (nav.analysis != null) { 3268 const unit: AnalUnit = .wrap(.{ .nav_val = nav_index }); 3269 if (zcu.failed_analysis.contains(unit)) break :failed true; 3270 if (zcu.transitive_failed_analysis.contains(unit)) break :failed true; 3271 } 3272 const val = switch (nav.status) { 3273 .unresolved, .type_resolved => break :failed true, 3274 .fully_resolved => |r| Value.fromInterned(r.val), 3275 }; 3276 // If the value is a function, we also need to check if that function succeeded analysis. 3277 if (val.typeOf(zcu).zigTypeTag(zcu) == .@"fn") { 3278 const func_unit = AnalUnit.wrap(.{ .func = val.toIntern() }); 3279 if (zcu.failed_analysis.contains(func_unit)) break :failed true; 3280 if (zcu.transitive_failed_analysis.contains(func_unit)) break :failed true; 3281 } 3282 break :failed false; 3283 }) { 3284 // This `Nav` is failed, so was never sent to codegen. There should be a compile error. 3285 assert(skip_linker_work); 3286 }, 3287 .uav => {}, 3288 } 3289 3290 if (skip_linker_work) return; 3291 3292 if (zcu.llvm_object) |llvm_object| { 3293 try zcu.handleUpdateExports(export_indices, llvm_object.updateExports(pt, exported, export_indices)); 3294 } else if (zcu.comp.bin_file) |lf| { 3295 try zcu.handleUpdateExports(export_indices, lf.updateExports(pt, exported, export_indices)); 3296 } 3297 } 3298 3299 pub fn populateTestFunctions(pt: Zcu.PerThread) Allocator.Error!void { 3300 const zcu = pt.zcu; 3301 const comp = zcu.comp; 3302 const gpa = comp.gpa; 3303 const io = comp.io; 3304 const ip = &zcu.intern_pool; 3305 3306 // Our job is to correctly set the value of the `test_functions` declaration if it has been 3307 // analyzed and sent to codegen, It usually will have been, because the test runner will 3308 // reference it, and `std.builtin` shouldn't have type errors. However, if it hasn't been 3309 // analyzed, we will just terminate early, since clearly the test runner hasn't referenced 3310 // `test_functions` so there's no point populating it. More to the the point, we potentially 3311 // *can't* populate it without doing some type resolution, and... let's try to leave Sema in 3312 // the past here. 3313 3314 const builtin_mod = zcu.builtin_modules.get(zcu.root_mod.getBuiltinOptions(zcu.comp.config).hash()).?; 3315 const builtin_file_index = zcu.module_roots.get(builtin_mod).?.unwrap().?; 3316 const builtin_root_type = zcu.fileRootType(builtin_file_index); 3317 if (builtin_root_type == .none) return; // `@import("builtin")` never analyzed 3318 const builtin_namespace = Type.fromInterned(builtin_root_type).getNamespace(zcu).unwrap().?; 3319 // We know that the namespace has a `test_functions`... 3320 const nav_index = zcu.namespacePtr(builtin_namespace).pub_decls.getKeyAdapted( 3321 try ip.getOrPutString(gpa, io, pt.tid, "test_functions", .no_embedded_nulls), 3322 Zcu.Namespace.NameAdapter{ .zcu = zcu }, 3323 ).?; 3324 // ...but it might not be populated, so let's check that! 3325 if (zcu.failed_analysis.contains(.wrap(.{ .nav_val = nav_index })) or 3326 zcu.transitive_failed_analysis.contains(.wrap(.{ .nav_val = nav_index })) or 3327 ip.getNav(nav_index).status != .fully_resolved) 3328 { 3329 // The value of `builtin.test_functions` was either never referenced, or failed analysis. 3330 // Either way, we don't need to do anything. 3331 return; 3332 } 3333 3334 // Okay, `builtin.test_functions` is (potentially) referenced and valid. Our job now is to swap 3335 // its placeholder `&.{}` value for the actual list of all test functions. 3336 3337 const test_fns_val = zcu.navValue(nav_index); 3338 const test_fn_ty = test_fns_val.typeOf(zcu).slicePtrFieldType(zcu).childType(zcu); 3339 3340 const array_anon_decl: InternPool.Key.Ptr.BaseAddr.Uav = array: { 3341 // Add zcu.test_functions to an array decl then make the test_functions 3342 // decl reference it as a slice. 3343 const test_fn_vals = try gpa.alloc(InternPool.Index, zcu.test_functions.count()); 3344 defer gpa.free(test_fn_vals); 3345 3346 for (test_fn_vals, zcu.test_functions.keys()) |*test_fn_val, test_nav_index| { 3347 const test_nav = ip.getNav(test_nav_index); 3348 3349 { 3350 // The test declaration might have failed; if that's the case, just return, as we'll 3351 // be emitting a compile error anyway. 3352 const anal_unit: AnalUnit = .wrap(.{ .nav_val = test_nav_index }); 3353 if (zcu.failed_analysis.contains(anal_unit) or 3354 zcu.transitive_failed_analysis.contains(anal_unit)) 3355 { 3356 return; 3357 } 3358 } 3359 3360 const test_nav_name = test_nav.fqn; 3361 const test_nav_name_len = test_nav_name.length(ip); 3362 const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.Uav = n: { 3363 const test_name_ty = try pt.arrayType(.{ 3364 .len = test_nav_name_len, 3365 .child = .u8_type, 3366 }); 3367 const test_name_val = try pt.intern(.{ .aggregate = .{ 3368 .ty = test_name_ty.toIntern(), 3369 .storage = .{ .bytes = test_nav_name.toString() }, 3370 } }); 3371 break :n .{ 3372 .orig_ty = (try pt.singleConstPtrType(test_name_ty)).toIntern(), 3373 .val = test_name_val, 3374 }; 3375 }; 3376 3377 const test_fn_fields = .{ 3378 // name 3379 try pt.intern(.{ .slice = .{ 3380 .ty = .slice_const_u8_type, 3381 .ptr = try pt.intern(.{ .ptr = .{ 3382 .ty = .manyptr_const_u8_type, 3383 .base_addr = .{ .uav = test_name_anon_decl }, 3384 .byte_offset = 0, 3385 } }), 3386 .len = try pt.intern(.{ .int = .{ 3387 .ty = .usize_type, 3388 .storage = .{ .u64 = test_nav_name_len }, 3389 } }), 3390 } }), 3391 // func 3392 try pt.intern(.{ .ptr = .{ 3393 .ty = (try pt.navPtrType(test_nav_index)).toIntern(), 3394 .base_addr = .{ .nav = test_nav_index }, 3395 .byte_offset = 0, 3396 } }), 3397 }; 3398 test_fn_val.* = (try pt.aggregateValue(test_fn_ty, &test_fn_fields)).toIntern(); 3399 } 3400 3401 const array_ty = try pt.arrayType(.{ 3402 .len = test_fn_vals.len, 3403 .child = test_fn_ty.toIntern(), 3404 .sentinel = .none, 3405 }); 3406 break :array .{ 3407 .orig_ty = (try pt.singleConstPtrType(array_ty)).toIntern(), 3408 .val = (try pt.aggregateValue(array_ty, test_fn_vals)).toIntern(), 3409 }; 3410 }; 3411 3412 { 3413 const new_ty = try pt.ptrType(.{ 3414 .child = test_fn_ty.toIntern(), 3415 .flags = .{ 3416 .is_const = true, 3417 .size = .slice, 3418 }, 3419 }); 3420 const new_init = try pt.intern(.{ .slice = .{ 3421 .ty = new_ty.toIntern(), 3422 .ptr = try pt.intern(.{ .ptr = .{ 3423 .ty = new_ty.slicePtrFieldType(zcu).toIntern(), 3424 .base_addr = .{ .uav = array_anon_decl }, 3425 .byte_offset = 0, 3426 } }), 3427 .len = (try pt.intValue(Type.usize, zcu.test_functions.count())).toIntern(), 3428 } }); 3429 ip.mutateVarInit(io, test_fns_val.toIntern(), new_init); 3430 } 3431 // The linker thread is not running, so we actually need to dispatch this task directly. 3432 @import("../link.zig").linkTestFunctionsNav(pt, nav_index); 3433 } 3434 3435 /// Stores an error in `pt.zcu.failed_files` for this file, and sets the file 3436 /// status to `retryable_failure`. 3437 pub fn reportRetryableFileError( 3438 pt: Zcu.PerThread, 3439 file_index: Zcu.File.Index, 3440 comptime format: []const u8, 3441 args: anytype, 3442 ) error{OutOfMemory}!void { 3443 const zcu = pt.zcu; 3444 const comp = zcu.comp; 3445 const io = comp.io; 3446 const gpa = comp.gpa; 3447 3448 const file = zcu.fileByIndex(file_index); 3449 3450 file.status = .retryable_failure; 3451 3452 const msg = try std.fmt.allocPrint(gpa, format, args); 3453 errdefer gpa.free(msg); 3454 3455 const old_msg: ?[]u8 = old_msg: { 3456 comp.mutex.lockUncancelable(io); 3457 defer comp.mutex.unlock(io); 3458 3459 const gop = try zcu.failed_files.getOrPut(gpa, file_index); 3460 const old: ?[]u8 = if (gop.found_existing) old: { 3461 break :old gop.value_ptr.*; 3462 } else null; 3463 gop.value_ptr.* = msg; 3464 3465 break :old_msg old; 3466 }; 3467 if (old_msg) |m| gpa.free(m); 3468 } 3469 3470 /// Shortcut for calling `intern_pool.get`. 3471 pub fn intern(pt: Zcu.PerThread, key: InternPool.Key) Allocator.Error!InternPool.Index { 3472 const comp = pt.zcu.comp; 3473 return pt.zcu.intern_pool.get(comp.gpa, comp.io, pt.tid, key); 3474 } 3475 3476 /// Essentially a shortcut for calling `intern_pool.getCoerced`. 3477 /// However, this function also allows coercing `extern`s. The `InternPool` function can't do 3478 /// this because it requires potentially pushing to the job queue. 3479 pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!Value { 3480 const ip = &pt.zcu.intern_pool; 3481 const comp = pt.zcu.comp; 3482 const gpa = comp.gpa; 3483 const io = comp.io; 3484 switch (ip.indexToKey(val.toIntern())) { 3485 .@"extern" => |e| { 3486 const coerced = try pt.getExtern(.{ 3487 .name = e.name, 3488 .ty = new_ty.toIntern(), 3489 .lib_name = e.lib_name, 3490 .is_const = e.is_const, 3491 .is_threadlocal = e.is_threadlocal, 3492 .linkage = e.linkage, 3493 .visibility = e.visibility, 3494 .is_dll_import = e.is_dll_import, 3495 .relocation = e.relocation, 3496 .alignment = e.alignment, 3497 .@"addrspace" = e.@"addrspace", 3498 .zir_index = e.zir_index, 3499 .owner_nav = undefined, // ignored by `getExtern`. 3500 .source = e.source, 3501 }); 3502 return Value.fromInterned(coerced); 3503 }, 3504 else => {}, 3505 } 3506 return Value.fromInterned(try ip.getCoerced(gpa, io, pt.tid, val.toIntern(), new_ty.toIntern())); 3507 } 3508 3509 pub fn intType(pt: Zcu.PerThread, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type { 3510 return Type.fromInterned(try pt.intern(.{ .int_type = .{ 3511 .signedness = signedness, 3512 .bits = bits, 3513 } })); 3514 } 3515 3516 pub fn errorIntType(pt: Zcu.PerThread) std.mem.Allocator.Error!Type { 3517 return pt.intType(.unsigned, pt.zcu.errorSetBits()); 3518 } 3519 3520 pub fn arrayType(pt: Zcu.PerThread, info: InternPool.Key.ArrayType) Allocator.Error!Type { 3521 return Type.fromInterned(try pt.intern(.{ .array_type = info })); 3522 } 3523 3524 pub fn vectorType(pt: Zcu.PerThread, info: InternPool.Key.VectorType) Allocator.Error!Type { 3525 return Type.fromInterned(try pt.intern(.{ .vector_type = info })); 3526 } 3527 3528 pub fn optionalType(pt: Zcu.PerThread, child_type: InternPool.Index) Allocator.Error!Type { 3529 return Type.fromInterned(try pt.intern(.{ .opt_type = child_type })); 3530 } 3531 3532 pub fn ptrType(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Allocator.Error!Type { 3533 var canon_info = info; 3534 3535 if (info.flags.size == .c) canon_info.flags.is_allowzero = true; 3536 3537 // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee 3538 // type, we change it to 0 here. If this causes an assertion trip because the 3539 // pointee type needs to be resolved more, that needs to be done before calling 3540 // this ptr() function. 3541 if (info.flags.alignment != .none and 3542 info.flags.alignment == Type.fromInterned(info.child).abiAlignment(pt.zcu)) 3543 { 3544 canon_info.flags.alignment = .none; 3545 } 3546 3547 switch (info.flags.vector_index) { 3548 // Canonicalize host_size. If it matches the bit size of the pointee type, 3549 // we change it to 0 here. If this causes an assertion trip, the pointee type 3550 // needs to be resolved before calling this ptr() function. 3551 .none => if (info.packed_offset.host_size != 0) { 3552 const elem_bit_size = Type.fromInterned(info.child).bitSize(pt.zcu); 3553 assert(info.packed_offset.bit_offset + elem_bit_size <= info.packed_offset.host_size * 8); 3554 if (info.packed_offset.host_size * 8 == elem_bit_size) { 3555 canon_info.packed_offset.host_size = 0; 3556 } 3557 }, 3558 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size), 3559 } 3560 3561 return Type.fromInterned(try pt.intern(.{ .ptr_type = canon_info })); 3562 } 3563 3564 /// Like `ptrType`, but if `info` specifies an `alignment`, first ensures the pointer 3565 /// child type's alignment is resolved so that an invalid alignment is not used. 3566 /// In general, prefer this function during semantic analysis. 3567 pub fn ptrTypeSema(pt: Zcu.PerThread, info: InternPool.Key.PtrType) Zcu.SemaError!Type { 3568 if (info.flags.alignment != .none) { 3569 _ = try Type.fromInterned(info.child).abiAlignmentSema(pt); 3570 } 3571 return pt.ptrType(info); 3572 } 3573 3574 pub fn singleMutPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type { 3575 return pt.ptrType(.{ .child = child_type.toIntern() }); 3576 } 3577 3578 pub fn singleConstPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type { 3579 return pt.ptrType(.{ 3580 .child = child_type.toIntern(), 3581 .flags = .{ 3582 .is_const = true, 3583 }, 3584 }); 3585 } 3586 3587 pub fn manyConstPtrType(pt: Zcu.PerThread, child_type: Type) Allocator.Error!Type { 3588 return pt.ptrType(.{ 3589 .child = child_type.toIntern(), 3590 .flags = .{ 3591 .size = .many, 3592 .is_const = true, 3593 }, 3594 }); 3595 } 3596 3597 pub fn adjustPtrTypeChild(pt: Zcu.PerThread, ptr_ty: Type, new_child: Type) Allocator.Error!Type { 3598 var info = ptr_ty.ptrInfo(pt.zcu); 3599 info.child = new_child.toIntern(); 3600 return pt.ptrType(info); 3601 } 3602 3603 pub fn funcType(pt: Zcu.PerThread, key: InternPool.GetFuncTypeKey) Allocator.Error!Type { 3604 const comp = pt.zcu.comp; 3605 return .fromInterned(try pt.zcu.intern_pool.getFuncType(comp.gpa, comp.io, pt.tid, key)); 3606 } 3607 3608 /// Use this for `anyframe->T` only. 3609 /// For `anyframe`, use the `InternPool.Index.anyframe` tag directly. 3610 pub fn anyframeType(pt: Zcu.PerThread, payload_ty: Type) Allocator.Error!Type { 3611 return Type.fromInterned(try pt.intern(.{ .anyframe_type = payload_ty.toIntern() })); 3612 } 3613 3614 pub fn errorUnionType(pt: Zcu.PerThread, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type { 3615 return Type.fromInterned(try pt.intern(.{ .error_union_type = .{ 3616 .error_set_type = error_set_ty.toIntern(), 3617 .payload_type = payload_ty.toIntern(), 3618 } })); 3619 } 3620 3621 pub fn singleErrorSetType(pt: Zcu.PerThread, name: InternPool.NullTerminatedString) Allocator.Error!Type { 3622 const names: *const [1]InternPool.NullTerminatedString = &name; 3623 const comp = pt.zcu.comp; 3624 return Type.fromInterned(try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names)); 3625 } 3626 3627 /// Sorts `names` in place. 3628 pub fn errorSetFromUnsortedNames( 3629 pt: Zcu.PerThread, 3630 names: []InternPool.NullTerminatedString, 3631 ) Allocator.Error!Type { 3632 std.mem.sort( 3633 InternPool.NullTerminatedString, 3634 names, 3635 {}, 3636 InternPool.NullTerminatedString.indexLessThan, 3637 ); 3638 const comp = pt.zcu.comp; 3639 const new_ty = try pt.zcu.intern_pool.getErrorSetType(comp.gpa, comp.io, pt.tid, names); 3640 return Type.fromInterned(new_ty); 3641 } 3642 3643 /// Supports only pointers, not pointer-like optionals. 3644 pub fn ptrIntValue(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value { 3645 const zcu = pt.zcu; 3646 assert(ty.zigTypeTag(zcu) == .pointer and !ty.isSlice(zcu)); 3647 assert(x != 0 or ty.isAllowzeroPtr(zcu)); 3648 return Value.fromInterned(try pt.intern(.{ .ptr = .{ 3649 .ty = ty.toIntern(), 3650 .base_addr = .int, 3651 .byte_offset = x, 3652 } })); 3653 } 3654 3655 /// Creates an enum tag value based on the integer tag value. 3656 pub fn enumValue(pt: Zcu.PerThread, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value { 3657 if (std.debug.runtime_safety) { 3658 const tag = ty.zigTypeTag(pt.zcu); 3659 assert(tag == .@"enum"); 3660 } 3661 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{ 3662 .ty = ty.toIntern(), 3663 .int = tag_int, 3664 } })); 3665 } 3666 3667 /// Creates an enum tag value based on the field index according to source code 3668 /// declaration order. 3669 pub fn enumValueFieldIndex(pt: Zcu.PerThread, ty: Type, field_index: u32) Allocator.Error!Value { 3670 const ip = &pt.zcu.intern_pool; 3671 const enum_type = ip.loadEnumType(ty.toIntern()); 3672 3673 if (enum_type.values.len == 0) { 3674 // Auto-numbered fields. 3675 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{ 3676 .ty = ty.toIntern(), 3677 .int = try pt.intern(.{ .int = .{ 3678 .ty = enum_type.tag_ty, 3679 .storage = .{ .u64 = field_index }, 3680 } }), 3681 } })); 3682 } 3683 3684 return Value.fromInterned(try pt.intern(.{ .enum_tag = .{ 3685 .ty = ty.toIntern(), 3686 .int = enum_type.values.get(ip)[field_index], 3687 } })); 3688 } 3689 3690 pub fn undefValue(pt: Zcu.PerThread, ty: Type) Allocator.Error!Value { 3691 return Value.fromInterned(try pt.intern(.{ .undef = ty.toIntern() })); 3692 } 3693 3694 pub fn undefRef(pt: Zcu.PerThread, ty: Type) Allocator.Error!Air.Inst.Ref { 3695 return Air.internedToRef((try pt.undefValue(ty)).toIntern()); 3696 } 3697 3698 pub fn intValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value { 3699 if (std.math.cast(u64, x)) |casted| return pt.intValue_u64(ty, casted); 3700 if (std.math.cast(i64, x)) |casted| return pt.intValue_i64(ty, casted); 3701 var limbs_buffer: [4]usize = undefined; 3702 var big_int = BigIntMutable.init(&limbs_buffer, x); 3703 return pt.intValue_big(ty, big_int.toConst()); 3704 } 3705 3706 pub fn intRef(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Air.Inst.Ref { 3707 return Air.internedToRef((try pt.intValue(ty, x)).toIntern()); 3708 } 3709 3710 pub fn intValue_big(pt: Zcu.PerThread, ty: Type, x: BigIntConst) Allocator.Error!Value { 3711 if (ty.toIntern() != .comptime_int_type) { 3712 const int_info = ty.intInfo(pt.zcu); 3713 assert(x.fitsInTwosComp(int_info.signedness, int_info.bits)); 3714 } 3715 return .fromInterned(try pt.intern(.{ .int = .{ 3716 .ty = ty.toIntern(), 3717 .storage = .{ .big_int = x }, 3718 } })); 3719 } 3720 3721 pub fn intValue_u64(pt: Zcu.PerThread, ty: Type, x: u64) Allocator.Error!Value { 3722 if (ty.toIntern() != .comptime_int_type and x != 0) { 3723 const int_info = ty.intInfo(pt.zcu); 3724 const unsigned_bits = int_info.bits - @intFromBool(int_info.signedness == .signed); 3725 assert(unsigned_bits >= std.math.log2(x) + 1); 3726 } 3727 return .fromInterned(try pt.intern(.{ .int = .{ 3728 .ty = ty.toIntern(), 3729 .storage = .{ .u64 = x }, 3730 } })); 3731 } 3732 3733 pub fn intValue_i64(pt: Zcu.PerThread, ty: Type, x: i64) Allocator.Error!Value { 3734 if (ty.toIntern() != .comptime_int_type and x != 0) { 3735 const int_info = ty.intInfo(pt.zcu); 3736 const unsigned_bits = int_info.bits - @intFromBool(int_info.signedness == .signed); 3737 if (x > 0) { 3738 assert(unsigned_bits >= std.math.log2(x) + 1); 3739 } else { 3740 assert(int_info.signedness == .signed); 3741 assert(unsigned_bits >= std.math.log2_int_ceil(u64, @abs(x))); 3742 } 3743 } 3744 return .fromInterned(try pt.intern(.{ .int = .{ 3745 .ty = ty.toIntern(), 3746 .storage = .{ .i64 = x }, 3747 } })); 3748 } 3749 3750 /// Shortcut for calling `intern_pool.getUnion`. 3751 /// TODO: remove either this or `unionValue`. 3752 pub fn internUnion(pt: Zcu.PerThread, un: InternPool.Key.Union) Allocator.Error!InternPool.Index { 3753 const comp = pt.zcu.comp; 3754 return pt.zcu.intern_pool.getUnion(comp.gpa, comp.io, pt.tid, un); 3755 } 3756 3757 /// TODO: remove either this or `internUnion`. 3758 pub fn unionValue(pt: Zcu.PerThread, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value { 3759 const comp = pt.zcu.comp; 3760 return Value.fromInterned(try pt.zcu.intern_pool.getUnion(comp.gpa, comp.io, pt.tid, .{ 3761 .ty = union_ty.toIntern(), 3762 .tag = tag.toIntern(), 3763 .val = val.toIntern(), 3764 })); 3765 } 3766 3767 pub fn aggregateValue(pt: Zcu.PerThread, ty: Type, elems: []const InternPool.Index) Allocator.Error!Value { 3768 for (elems) |elem| { 3769 if (!Value.fromInterned(elem).isUndef(pt.zcu)) break; 3770 } else if (elems.len > 0) { 3771 return pt.undefValue(ty); // all-undef 3772 } 3773 return .fromInterned(try pt.intern(.{ .aggregate = .{ 3774 .ty = ty.toIntern(), 3775 .storage = .{ .elems = elems }, 3776 } })); 3777 } 3778 3779 /// Asserts that `ty` is either an array or a vector. 3780 pub fn aggregateSplatValue(pt: Zcu.PerThread, ty: Type, repeated_elem: Value) Allocator.Error!Value { 3781 switch (ty.zigTypeTag(pt.zcu)) { 3782 .array, .vector => {}, 3783 else => unreachable, 3784 } 3785 if (repeated_elem.isUndef(pt.zcu)) return pt.undefValue(ty); 3786 return .fromInterned(try pt.intern(.{ .aggregate = .{ 3787 .ty = ty.toIntern(), 3788 .storage = .{ .repeated_elem = repeated_elem.toIntern() }, 3789 } })); 3790 } 3791 3792 /// This function casts the float representation down to the representation of the type, potentially 3793 /// losing data if the representation wasn't correct. 3794 pub fn floatValue(pt: Zcu.PerThread, ty: Type, x: anytype) Allocator.Error!Value { 3795 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(pt.zcu.getTarget())) { 3796 16 => .{ .f16 = @as(f16, @floatCast(x)) }, 3797 32 => .{ .f32 = @as(f32, @floatCast(x)) }, 3798 64 => .{ .f64 = @as(f64, @floatCast(x)) }, 3799 80 => .{ .f80 = @as(f80, @floatCast(x)) }, 3800 128 => .{ .f128 = @as(f128, @floatCast(x)) }, 3801 else => unreachable, 3802 }; 3803 return Value.fromInterned(try pt.intern(.{ .float = .{ 3804 .ty = ty.toIntern(), 3805 .storage = storage, 3806 } })); 3807 } 3808 3809 pub fn nullValue(pt: Zcu.PerThread, opt_ty: Type) Allocator.Error!Value { 3810 assert(pt.zcu.intern_pool.isOptionalType(opt_ty.toIntern())); 3811 return Value.fromInterned(try pt.intern(.{ .opt = .{ 3812 .ty = opt_ty.toIntern(), 3813 .val = .none, 3814 } })); 3815 } 3816 3817 /// `ty` is an integer or a vector of integers. 3818 pub fn overflowArithmeticTupleType(pt: Zcu.PerThread, ty: Type) !Type { 3819 const zcu = pt.zcu; 3820 const comp = zcu.comp; 3821 const ov_ty: Type = if (ty.zigTypeTag(zcu) == .vector) try pt.vectorType(.{ 3822 .len = ty.vectorLen(zcu), 3823 .child = .u1_type, 3824 }) else .u1; 3825 const tuple_ty = try zcu.intern_pool.getTupleType(comp.gpa, comp.io, pt.tid, .{ 3826 .types = &.{ ty.toIntern(), ov_ty.toIntern() }, 3827 .values = &.{ .none, .none }, 3828 }); 3829 return .fromInterned(tuple_ty); 3830 } 3831 3832 pub fn smallestUnsignedInt(pt: Zcu.PerThread, max: u64) Allocator.Error!Type { 3833 return pt.intType(.unsigned, Type.smallestUnsignedBits(max)); 3834 } 3835 3836 /// Returns the smallest possible integer type containing both `min` and 3837 /// `max`. Asserts that neither value is undef. 3838 /// TODO: if #3806 is implemented, this becomes trivial 3839 pub fn intFittingRange(pt: Zcu.PerThread, min: Value, max: Value) !Type { 3840 const zcu = pt.zcu; 3841 assert(!min.isUndef(zcu)); 3842 assert(!max.isUndef(zcu)); 3843 3844 if (std.debug.runtime_safety) { 3845 assert(Value.order(min, max, zcu).compare(.lte)); 3846 } 3847 3848 const sign = min.orderAgainstZero(zcu) == .lt; 3849 3850 const min_val_bits = pt.intBitsForValue(min, sign); 3851 const max_val_bits = pt.intBitsForValue(max, sign); 3852 3853 return pt.intType( 3854 if (sign) .signed else .unsigned, 3855 @max(min_val_bits, max_val_bits), 3856 ); 3857 } 3858 3859 /// Given a value representing an integer, returns the number of bits necessary to represent 3860 /// this value in an integer. If `sign` is true, returns the number of bits necessary in a 3861 /// twos-complement integer; otherwise in an unsigned integer. 3862 /// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true. 3863 pub fn intBitsForValue(pt: Zcu.PerThread, val: Value, sign: bool) u16 { 3864 const zcu = pt.zcu; 3865 assert(!val.isUndef(zcu)); 3866 3867 const key = zcu.intern_pool.indexToKey(val.toIntern()); 3868 switch (key.int.storage) { 3869 .i64 => |x| { 3870 if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted) + @intFromBool(sign); 3871 assert(sign); 3872 // Protect against overflow in the following negation. 3873 if (x == std.math.minInt(i64)) return 64; 3874 return Type.smallestUnsignedBits(@as(u64, @intCast(-(x + 1)))) + 1; 3875 }, 3876 .u64 => |x| { 3877 return Type.smallestUnsignedBits(x) + @intFromBool(sign); 3878 }, 3879 .big_int => |big| { 3880 if (big.positive) return @as(u16, @intCast(big.bitCountAbs() + @intFromBool(sign))); 3881 3882 // Zero is still a possibility, in which case unsigned is fine 3883 if (big.eqlZero()) return 0; 3884 3885 return @as(u16, @intCast(big.bitCountTwosComp())); 3886 }, 3887 .lazy_align => |lazy_ty| { 3888 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(pt.zcu).toByteUnits() orelse 0) + @intFromBool(sign); 3889 }, 3890 .lazy_size => |lazy_ty| { 3891 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(pt.zcu)) + @intFromBool(sign); 3892 }, 3893 } 3894 } 3895 3896 pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Error!Type { 3897 const zcu = pt.zcu; 3898 const ip = &zcu.intern_pool; 3899 const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) { 3900 .unresolved => unreachable, 3901 .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, 3902 .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const }, 3903 }; 3904 return pt.ptrType(.{ 3905 .child = ty, 3906 .flags = .{ 3907 .alignment = if (alignment == Type.fromInterned(ty).abiAlignment(zcu)) 3908 .none 3909 else 3910 alignment, 3911 .address_space = @"addrspace", 3912 .is_const = is_const, 3913 }, 3914 }); 3915 } 3916 3917 /// Intern an `.@"extern"`, creating a corresponding owner `Nav` if necessary. 3918 /// If necessary, the new `Nav` is queued for codegen. 3919 /// `key.owner_nav` is ignored and may be `undefined`. 3920 pub fn getExtern(pt: Zcu.PerThread, key: InternPool.Key.Extern) Allocator.Error!InternPool.Index { 3921 const zcu = pt.zcu; 3922 const comp = zcu.comp; 3923 const result = try zcu.intern_pool.getExtern(comp.gpa, comp.io, pt.tid, key); 3924 if (result.new_nav.unwrap()) |nav| { 3925 // This job depends on any resolve_type_fully jobs queued up before it. 3926 comp.link_prog_node.increaseEstimatedTotalItems(1); 3927 try comp.queueJob(.{ .link_nav = nav }); 3928 if (comp.debugIncremental()) try zcu.incremental_debug_state.newNav(zcu, nav); 3929 } 3930 return result.index; 3931 } 3932 3933 // TODO: this shouldn't need a `PerThread`! Fix the signature of `Type.abiAlignment`. 3934 pub fn navAlignment(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) InternPool.Alignment { 3935 const zcu = pt.zcu; 3936 const ty: Type, const alignment = switch (zcu.intern_pool.getNav(nav_index).status) { 3937 .unresolved => unreachable, 3938 .type_resolved => |r| .{ .fromInterned(r.type), r.alignment }, 3939 .fully_resolved => |r| .{ Value.fromInterned(r.val).typeOf(zcu), r.alignment }, 3940 }; 3941 if (alignment != .none) return alignment; 3942 return ty.abiAlignment(zcu); 3943 } 3944 3945 /// `ty` is a container type requiring resolution (struct, union, or enum). 3946 /// If `ty` is outdated, it is recreated at a new `InternPool.Index`, which is returned. 3947 /// If the type cannot be recreated because it has been lost, `error.AnalysisFail` is returned. 3948 /// If `ty` is not outdated, that same `InternPool.Index` is returned. 3949 /// If `ty` has already been replaced by this function, the new index will not be returned again. 3950 /// Also, if `ty` is an enum, this function will resolve the new type if needed, and the call site 3951 /// is responsible for checking `[transitive_]failed_analysis` to detect resolution failures. 3952 pub fn ensureTypeUpToDate(pt: Zcu.PerThread, ty: InternPool.Index) Zcu.SemaError!InternPool.Index { 3953 const zcu = pt.zcu; 3954 const gpa = zcu.gpa; 3955 const ip = &zcu.intern_pool; 3956 3957 const anal_unit: AnalUnit = .wrap(.{ .type = ty }); 3958 const outdated = zcu.outdated.swapRemove(anal_unit) or 3959 zcu.potentially_outdated.swapRemove(anal_unit); 3960 3961 if (outdated) { 3962 _ = zcu.outdated_ready.swapRemove(anal_unit); 3963 try zcu.markDependeeOutdated(.marked_po, .{ .interned = ty }); 3964 } 3965 3966 const ty_key = switch (ip.indexToKey(ty)) { 3967 .struct_type, .union_type, .enum_type => |key| key, 3968 else => unreachable, 3969 }; 3970 const declared_ty_key = switch (ty_key) { 3971 .reified => unreachable, // never outdated 3972 .generated_tag => unreachable, // never outdated 3973 .declared => |d| d, 3974 }; 3975 3976 if (declared_ty_key.zir_index.resolve(ip) == null) { 3977 // The instruction has been lost -- this type is dead. 3978 return error.AnalysisFail; 3979 } 3980 3981 if (!outdated) return ty; 3982 3983 // We will recreate the type at a new `InternPool.Index`. 3984 3985 // Delete old state which is no longer in use. Technically, this is not necessary: these exports, 3986 // references, etc, will be ignored because the type itself is unreferenced. However, it allows 3987 // reusing the memory which is currently being used to track this state. 3988 zcu.deleteUnitExports(anal_unit); 3989 zcu.deleteUnitReferences(anal_unit); 3990 zcu.deleteUnitCompileLogs(anal_unit); 3991 if (zcu.failed_analysis.fetchSwapRemove(anal_unit)) |kv| { 3992 kv.value.destroy(gpa); 3993 } 3994 _ = zcu.transitive_failed_analysis.swapRemove(anal_unit); 3995 zcu.intern_pool.removeDependenciesForDepender(gpa, anal_unit); 3996 3997 if (zcu.comp.debugIncremental()) { 3998 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, anal_unit); 3999 info.last_update_gen = zcu.generation; 4000 info.deps.clearRetainingCapacity(); 4001 } 4002 4003 switch (ip.indexToKey(ty)) { 4004 .struct_type => return pt.recreateStructType(ty, declared_ty_key), 4005 .union_type => return pt.recreateUnionType(ty, declared_ty_key), 4006 .enum_type => return pt.recreateEnumType(ty, declared_ty_key), 4007 else => unreachable, 4008 } 4009 } 4010 4011 fn recreateStructType( 4012 pt: Zcu.PerThread, 4013 old_ty: InternPool.Index, 4014 key: InternPool.Key.NamespaceType.Declared, 4015 ) Allocator.Error!InternPool.Index { 4016 const zcu = pt.zcu; 4017 const comp = zcu.comp; 4018 const gpa = comp.gpa; 4019 const io = comp.io; 4020 const ip = &zcu.intern_pool; 4021 4022 const inst_info = key.zir_index.resolveFull(ip).?; 4023 const file = zcu.fileByIndex(inst_info.file); 4024 const zir = file.zir.?; 4025 4026 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); 4027 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; 4028 assert(extended.opcode == .struct_decl); 4029 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 4030 const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand); 4031 var extra_index = extra.end; 4032 4033 const captures_len = if (small.has_captures_len) blk: { 4034 const captures_len = zir.extra[extra_index]; 4035 extra_index += 1; 4036 break :blk captures_len; 4037 } else 0; 4038 const fields_len = if (small.has_fields_len) blk: { 4039 const fields_len = zir.extra[extra_index]; 4040 extra_index += 1; 4041 break :blk fields_len; 4042 } else 0; 4043 4044 assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` 4045 4046 const struct_obj = ip.loadStructType(old_ty); 4047 4048 const wip_ty = switch (try ip.getStructType(gpa, io, pt.tid, .{ 4049 .layout = small.layout, 4050 .fields_len = fields_len, 4051 .known_non_opv = small.known_non_opv, 4052 .requires_comptime = if (small.known_comptime_only) .yes else .unknown, 4053 .any_comptime_fields = small.any_comptime_fields, 4054 .any_default_inits = small.any_default_inits, 4055 .inits_resolved = false, 4056 .any_aligned_fields = small.any_aligned_fields, 4057 .key = .{ .declared_owned_captures = .{ 4058 .zir_index = key.zir_index, 4059 .captures = key.captures.owned, 4060 } }, 4061 }, true)) { 4062 .wip => |wip| wip, 4063 .existing => unreachable, // we passed `replace_existing` 4064 }; 4065 errdefer wip_ty.cancel(ip, pt.tid); 4066 4067 wip_ty.setName(ip, struct_obj.name, struct_obj.name_nav); 4068 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); 4069 zcu.namespacePtr(struct_obj.namespace).owner_type = wip_ty.index; 4070 // No need to re-scan the namespace -- `zirStructDecl` will ultimately do that if the type is still alive. 4071 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 4072 4073 codegen_type: { 4074 if (file.mod.?.strip) break :codegen_type; 4075 // This job depends on any resolve_type_fully jobs queued up before it. 4076 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 4077 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 4078 } 4079 4080 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 4081 const new_ty = wip_ty.finish(ip, struct_obj.namespace); 4082 if (inst_info.inst == .main_struct_inst) { 4083 // This is the root type of a file! Update the reference. 4084 zcu.setFileRootType(inst_info.file, new_ty); 4085 } 4086 return new_ty; 4087 } 4088 4089 fn recreateUnionType( 4090 pt: Zcu.PerThread, 4091 old_ty: InternPool.Index, 4092 key: InternPool.Key.NamespaceType.Declared, 4093 ) Allocator.Error!InternPool.Index { 4094 const zcu = pt.zcu; 4095 const comp = zcu.comp; 4096 const gpa = comp.gpa; 4097 const io = comp.io; 4098 const ip = &zcu.intern_pool; 4099 4100 const inst_info = key.zir_index.resolveFull(ip).?; 4101 const file = zcu.fileByIndex(inst_info.file); 4102 const zir = file.zir.?; 4103 4104 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); 4105 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; 4106 assert(extended.opcode == .union_decl); 4107 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 4108 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); 4109 var extra_index = extra.end; 4110 4111 extra_index += @intFromBool(small.has_tag_type); 4112 const captures_len = if (small.has_captures_len) blk: { 4113 const captures_len = zir.extra[extra_index]; 4114 extra_index += 1; 4115 break :blk captures_len; 4116 } else 0; 4117 extra_index += @intFromBool(small.has_body_len); 4118 const fields_len = if (small.has_fields_len) blk: { 4119 const fields_len = zir.extra[extra_index]; 4120 extra_index += 1; 4121 break :blk fields_len; 4122 } else 0; 4123 4124 assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` 4125 4126 const union_obj = ip.loadUnionType(old_ty); 4127 4128 const namespace_index = union_obj.namespace; 4129 4130 const wip_ty = switch (try ip.getUnionType(gpa, io, pt.tid, .{ 4131 .flags = .{ 4132 .layout = small.layout, 4133 .status = .none, 4134 .runtime_tag = if (small.has_tag_type or small.auto_enum_tag) 4135 .tagged 4136 else if (small.layout != .auto) 4137 .none 4138 else switch (true) { // TODO 4139 true => .safety, 4140 false => .none, 4141 }, 4142 .any_aligned_fields = small.any_aligned_fields, 4143 .requires_comptime = .unknown, 4144 .assumed_runtime_bits = false, 4145 .assumed_pointer_aligned = false, 4146 .alignment = .none, 4147 }, 4148 .fields_len = fields_len, 4149 .enum_tag_ty = .none, // set later 4150 .field_types = &.{}, // set later 4151 .field_aligns = &.{}, // set later 4152 .key = .{ .declared_owned_captures = .{ 4153 .zir_index = key.zir_index, 4154 .captures = key.captures.owned, 4155 } }, 4156 }, true)) { 4157 .wip => |wip| wip, 4158 .existing => unreachable, // we passed `replace_existing` 4159 }; 4160 errdefer wip_ty.cancel(ip, pt.tid); 4161 4162 wip_ty.setName(ip, union_obj.name, union_obj.name_nav); 4163 try pt.addDependency(.wrap(.{ .type = wip_ty.index }), .{ .src_hash = key.zir_index }); 4164 zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; 4165 // No need to re-scan the namespace -- `zirUnionDecl` will ultimately do that if the type is still alive. 4166 try zcu.comp.queueJob(.{ .resolve_type_fully = wip_ty.index }); 4167 4168 codegen_type: { 4169 if (file.mod.?.strip) break :codegen_type; 4170 // This job depends on any resolve_type_fully jobs queued up before it. 4171 zcu.comp.link_prog_node.increaseEstimatedTotalItems(1); 4172 try zcu.comp.queueJob(.{ .link_type = wip_ty.index }); 4173 } 4174 4175 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 4176 return wip_ty.finish(ip, namespace_index); 4177 } 4178 4179 /// This *does* call `Sema.resolveDeclaredEnum`, but errors from it are not propagated. 4180 /// Call sites are resposible for checking `[transitive_]failed_analysis` after `ensureTypeUpToDate` 4181 /// returns in order to detect resolution failures. 4182 fn recreateEnumType( 4183 pt: Zcu.PerThread, 4184 old_ty: InternPool.Index, 4185 key: InternPool.Key.NamespaceType.Declared, 4186 ) (Allocator.Error || Io.Cancelable)!InternPool.Index { 4187 const zcu = pt.zcu; 4188 const comp = zcu.comp; 4189 const gpa = comp.gpa; 4190 const io = comp.io; 4191 const ip = &zcu.intern_pool; 4192 4193 const inst_info = key.zir_index.resolveFull(ip).?; 4194 const file = zcu.fileByIndex(inst_info.file); 4195 const zir = file.zir.?; 4196 4197 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); 4198 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; 4199 assert(extended.opcode == .enum_decl); 4200 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); 4201 const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand); 4202 var extra_index = extra.end; 4203 4204 const tag_type_ref = if (small.has_tag_type) blk: { 4205 const tag_type_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]); 4206 extra_index += 1; 4207 break :blk tag_type_ref; 4208 } else .none; 4209 4210 const captures_len = if (small.has_captures_len) blk: { 4211 const captures_len = zir.extra[extra_index]; 4212 extra_index += 1; 4213 break :blk captures_len; 4214 } else 0; 4215 4216 const body_len = if (small.has_body_len) blk: { 4217 const body_len = zir.extra[extra_index]; 4218 extra_index += 1; 4219 break :blk body_len; 4220 } else 0; 4221 4222 const fields_len = if (small.has_fields_len) blk: { 4223 const fields_len = zir.extra[extra_index]; 4224 extra_index += 1; 4225 break :blk fields_len; 4226 } else 0; 4227 4228 const decls_len = if (small.has_decls_len) blk: { 4229 const decls_len = zir.extra[extra_index]; 4230 extra_index += 1; 4231 break :blk decls_len; 4232 } else 0; 4233 4234 assert(captures_len == key.captures.owned.len); // synchronises with logic in `Zcu.mapOldZirToNew` 4235 4236 extra_index += captures_len * 2; 4237 extra_index += decls_len; 4238 4239 const body = zir.bodySlice(extra_index, body_len); 4240 extra_index += body.len; 4241 4242 const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable; 4243 const body_end = extra_index; 4244 extra_index += bit_bags_count; 4245 4246 const any_values = for (zir.extra[body_end..][0..bit_bags_count]) |bag| { 4247 if (bag != 0) break true; 4248 } else false; 4249 4250 const enum_obj = ip.loadEnumType(old_ty); 4251 4252 const namespace_index = enum_obj.namespace; 4253 4254 const wip_ty = switch (try ip.getEnumType(gpa, io, pt.tid, .{ 4255 .has_values = any_values, 4256 .tag_mode = if (small.nonexhaustive) 4257 .nonexhaustive 4258 else if (tag_type_ref == .none) 4259 .auto 4260 else 4261 .explicit, 4262 .fields_len = fields_len, 4263 .key = .{ .declared_owned_captures = .{ 4264 .zir_index = key.zir_index, 4265 .captures = key.captures.owned, 4266 } }, 4267 }, true)) { 4268 .wip => |wip| wip, 4269 .existing => unreachable, // we passed `replace_existing` 4270 }; 4271 var done = true; 4272 errdefer if (!done) wip_ty.cancel(ip, pt.tid); 4273 4274 wip_ty.setName(ip, enum_obj.name, enum_obj.name_nav); 4275 4276 zcu.namespacePtr(namespace_index).owner_type = wip_ty.index; 4277 // No need to re-scan the namespace -- `zirEnumDecl` will ultimately do that if the type is still alive. 4278 4279 if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip_ty.index); 4280 wip_ty.prepare(ip, namespace_index); 4281 done = true; 4282 4283 Sema.resolveDeclaredEnum( 4284 pt, 4285 wip_ty, 4286 inst_info.inst, 4287 key.zir_index, 4288 namespace_index, 4289 enum_obj.name, 4290 small, 4291 body, 4292 tag_type_ref, 4293 any_values, 4294 fields_len, 4295 zir, 4296 body_end, 4297 ) catch |err| switch (err) { 4298 error.OutOfMemory => |e| return e, 4299 error.Canceled => |e| return e, 4300 error.AnalysisFail => {}, // call sites are responsible for checking `[transitive_]failed_analysis` to detect this 4301 }; 4302 4303 return wip_ty.index; 4304 } 4305 4306 /// Given a namespace, re-scan its declarations from the type definition if they have not 4307 /// yet been re-scanned on this update. 4308 /// If the type declaration instruction has been lost, returns `error.AnalysisFail`. 4309 /// This will effectively short-circuit the caller, which will be semantic analysis of a 4310 /// guaranteed-unreferenced `AnalUnit`, to trigger a transitive analysis error. 4311 pub fn ensureNamespaceUpToDate(pt: Zcu.PerThread, namespace_index: Zcu.Namespace.Index) Zcu.SemaError!void { 4312 const zcu = pt.zcu; 4313 const ip = &zcu.intern_pool; 4314 const namespace = zcu.namespacePtr(namespace_index); 4315 4316 if (namespace.generation == zcu.generation) return; 4317 4318 const Container = enum { @"struct", @"union", @"enum", @"opaque" }; 4319 const container: Container, const full_key = switch (ip.indexToKey(namespace.owner_type)) { 4320 .struct_type => |k| .{ .@"struct", k }, 4321 .union_type => |k| .{ .@"union", k }, 4322 .enum_type => |k| .{ .@"enum", k }, 4323 .opaque_type => |k| .{ .@"opaque", k }, 4324 else => unreachable, // namespaces are owned by a container type 4325 }; 4326 4327 const key = switch (full_key) { 4328 .reified, .generated_tag => { 4329 // Namespace always empty, so up-to-date. 4330 namespace.generation = zcu.generation; 4331 return; 4332 }, 4333 .declared => |d| d, 4334 }; 4335 4336 // Namespace outdated -- re-scan the type if necessary. 4337 4338 const inst_info = key.zir_index.resolveFull(ip) orelse return error.AnalysisFail; 4339 const file = zcu.fileByIndex(inst_info.file); 4340 const zir = file.zir.?; 4341 4342 assert(zir.instructions.items(.tag)[@intFromEnum(inst_info.inst)] == .extended); 4343 const extended = zir.instructions.items(.data)[@intFromEnum(inst_info.inst)].extended; 4344 4345 const decls = switch (container) { 4346 .@"struct" => decls: { 4347 assert(extended.opcode == .struct_decl); 4348 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 4349 const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand); 4350 var extra_index = extra.end; 4351 const captures_len = if (small.has_captures_len) blk: { 4352 const captures_len = zir.extra[extra_index]; 4353 extra_index += 1; 4354 break :blk captures_len; 4355 } else 0; 4356 extra_index += @intFromBool(small.has_fields_len); 4357 const decls_len = if (small.has_decls_len) blk: { 4358 const decls_len = zir.extra[extra_index]; 4359 extra_index += 1; 4360 break :blk decls_len; 4361 } else 0; 4362 extra_index += captures_len * 2; 4363 if (small.has_backing_int) { 4364 const backing_int_body_len = zir.extra[extra_index]; 4365 extra_index += 1; // backing_int_body_len 4366 if (backing_int_body_len == 0) { 4367 extra_index += 1; // backing_int_ref 4368 } else { 4369 extra_index += backing_int_body_len; // backing_int_body_inst 4370 } 4371 } 4372 break :decls zir.bodySlice(extra_index, decls_len); 4373 }, 4374 .@"union" => decls: { 4375 assert(extended.opcode == .union_decl); 4376 const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small); 4377 const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand); 4378 var extra_index = extra.end; 4379 extra_index += @intFromBool(small.has_tag_type); 4380 const captures_len = if (small.has_captures_len) blk: { 4381 const captures_len = zir.extra[extra_index]; 4382 extra_index += 1; 4383 break :blk captures_len; 4384 } else 0; 4385 extra_index += @intFromBool(small.has_body_len); 4386 extra_index += @intFromBool(small.has_fields_len); 4387 const decls_len = if (small.has_decls_len) blk: { 4388 const decls_len = zir.extra[extra_index]; 4389 extra_index += 1; 4390 break :blk decls_len; 4391 } else 0; 4392 extra_index += captures_len * 2; 4393 break :decls zir.bodySlice(extra_index, decls_len); 4394 }, 4395 .@"enum" => decls: { 4396 assert(extended.opcode == .enum_decl); 4397 const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small); 4398 const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand); 4399 var extra_index = extra.end; 4400 extra_index += @intFromBool(small.has_tag_type); 4401 const captures_len = if (small.has_captures_len) blk: { 4402 const captures_len = zir.extra[extra_index]; 4403 extra_index += 1; 4404 break :blk captures_len; 4405 } else 0; 4406 extra_index += @intFromBool(small.has_body_len); 4407 extra_index += @intFromBool(small.has_fields_len); 4408 const decls_len = if (small.has_decls_len) blk: { 4409 const decls_len = zir.extra[extra_index]; 4410 extra_index += 1; 4411 break :blk decls_len; 4412 } else 0; 4413 extra_index += captures_len * 2; 4414 break :decls zir.bodySlice(extra_index, decls_len); 4415 }, 4416 .@"opaque" => decls: { 4417 assert(extended.opcode == .opaque_decl); 4418 const small: Zir.Inst.OpaqueDecl.Small = @bitCast(extended.small); 4419 const extra = zir.extraData(Zir.Inst.OpaqueDecl, extended.operand); 4420 var extra_index = extra.end; 4421 const captures_len = if (small.has_captures_len) blk: { 4422 const captures_len = zir.extra[extra_index]; 4423 extra_index += 1; 4424 break :blk captures_len; 4425 } else 0; 4426 const decls_len = if (small.has_decls_len) blk: { 4427 const decls_len = zir.extra[extra_index]; 4428 extra_index += 1; 4429 break :blk decls_len; 4430 } else 0; 4431 extra_index += captures_len * 2; 4432 break :decls zir.bodySlice(extra_index, decls_len); 4433 }, 4434 }; 4435 4436 try pt.scanNamespace(namespace_index, decls); 4437 namespace.generation = zcu.generation; 4438 } 4439 4440 pub fn refValue(pt: Zcu.PerThread, val: InternPool.Index) Zcu.SemaError!InternPool.Index { 4441 const ptr_ty = (try pt.ptrTypeSema(.{ 4442 .child = pt.zcu.intern_pool.typeOf(val), 4443 .flags = .{ 4444 .alignment = .none, 4445 .is_const = true, 4446 .address_space = .generic, 4447 }, 4448 })).toIntern(); 4449 return pt.intern(.{ .ptr = .{ 4450 .ty = ptr_ty, 4451 .base_addr = .{ .uav = .{ 4452 .val = val, 4453 .orig_ty = ptr_ty, 4454 } }, 4455 .byte_offset = 0, 4456 } }); 4457 } 4458 4459 pub fn addDependency(pt: Zcu.PerThread, unit: AnalUnit, dependee: InternPool.Dependee) Allocator.Error!void { 4460 const zcu = pt.zcu; 4461 const gpa = zcu.comp.gpa; 4462 try zcu.intern_pool.addDependency(gpa, unit, dependee); 4463 if (zcu.comp.debugIncremental()) { 4464 const info = try zcu.incremental_debug_state.getUnitInfo(gpa, unit); 4465 try info.deps.append(gpa, dependee); 4466 } 4467 } 4468 4469 pub const RunCodegenError = Io.Cancelable || error{AlreadyReported}; 4470 4471 /// Performs code generation, which comes after `Sema` but before `link` in the pipeline. This part 4472 /// of the pipeline is self-contained and can usually be run concurrently with other components. 4473 /// 4474 /// This function is called asynchronously by `Zcu.CodegenTaskPool.start` and awaited by the linker. 4475 /// However, if the codegen backend does not support `Zcu.Feature.separate_thread`, then 4476 /// `Compilation.processOneJob` will immediately await the result of the linker task, meaning the 4477 /// pipeline becomes effectively single-threaded. 4478 pub fn runCodegen(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) RunCodegenError!codegen.AnyMir { 4479 const zcu = pt.zcu; 4480 const comp = zcu.comp; 4481 const io = comp.io; 4482 4483 crash_report.CodegenFunc.start(zcu, func_index); 4484 defer crash_report.CodegenFunc.stop(func_index); 4485 4486 var timer = comp.startTimer(); 4487 4488 const codegen_result = runCodegenInner(pt, func_index, air); 4489 4490 if (timer.finish()) |ns_codegen| report_time: { 4491 const ip = &zcu.intern_pool; 4492 const nav = ip.indexToKey(func_index).func.owner_nav; 4493 const zir_decl = ip.getNav(nav).srcInst(ip); 4494 comp.mutex.lockUncancelable(io); 4495 defer comp.mutex.unlock(io); 4496 const tr = &zcu.comp.time_report.?; 4497 tr.stats.cpu_ns_codegen += ns_codegen; 4498 const gop = tr.decl_codegen_ns.getOrPut(comp.gpa, zir_decl) catch |err| switch (err) { 4499 error.OutOfMemory => { 4500 comp.setAllocFailure(); 4501 break :report_time; 4502 }, 4503 }; 4504 if (!gop.found_existing) gop.value_ptr.* = 0; 4505 gop.value_ptr.* += ns_codegen; 4506 } 4507 4508 if (zcu.pending_codegen_jobs.rmw(.Sub, 1, .monotonic) == 1) { 4509 // Decremented to 0, so all done. 4510 zcu.codegen_prog_node.end(); 4511 zcu.codegen_prog_node = .none; 4512 } 4513 4514 return codegen_result catch |err| { 4515 switch (err) { 4516 error.OutOfMemory => comp.setAllocFailure(), 4517 error.CodegenFail => zcu.assertCodegenFailed(zcu.funcInfo(func_index).owner_nav), 4518 error.NoLinkFile => assert(comp.bin_file == null), 4519 error.BackendDoesNotProduceMir => switch (target_util.zigBackend( 4520 &zcu.root_mod.resolved_target.result, 4521 comp.config.use_llvm, 4522 )) { 4523 else => unreachable, // assertion failure 4524 .stage2_spirv, 4525 .stage2_llvm, 4526 => {}, 4527 }, 4528 error.Canceled => |e| return e, 4529 } 4530 return error.AlreadyReported; 4531 }; 4532 } 4533 fn runCodegenInner(pt: Zcu.PerThread, func_index: InternPool.Index, air: *Air) error{ 4534 OutOfMemory, 4535 Canceled, 4536 CodegenFail, 4537 NoLinkFile, 4538 BackendDoesNotProduceMir, 4539 }!codegen.AnyMir { 4540 const zcu = pt.zcu; 4541 const gpa = zcu.gpa; 4542 const ip = &zcu.intern_pool; 4543 const comp = zcu.comp; 4544 4545 const nav = zcu.funcInfo(func_index).owner_nav; 4546 const fqn = ip.getNav(nav).fqn; 4547 4548 const codegen_prog_node = zcu.codegen_prog_node.start(fqn.toSlice(ip), 0); 4549 defer codegen_prog_node.end(); 4550 4551 if (codegen.legalizeFeatures(pt, nav)) |features| { 4552 try air.legalize(pt, features); 4553 } 4554 4555 var liveness: ?Air.Liveness = if (codegen.wantsLiveness(pt, nav)) 4556 try .analyze(zcu, air.*, ip) 4557 else 4558 null; 4559 defer if (liveness) |*l| l.deinit(gpa); 4560 4561 if (build_options.enable_debug_extensions and comp.verbose_air) p: { 4562 const io = comp.io; 4563 const stderr = try io.lockStderr(&.{}, null); 4564 defer io.unlockStderr(); 4565 printVerboseAir(pt, liveness, fqn, air, &stderr.file_writer.interface) catch |err| switch (err) { 4566 error.WriteFailed => switch (stderr.file_writer.err.?) { 4567 error.Canceled => |e| return e, 4568 else => break :p, 4569 }, 4570 }; 4571 } 4572 4573 if (std.debug.runtime_safety) verify_liveness: { 4574 var verify: Air.Liveness.Verify = .{ 4575 .gpa = gpa, 4576 .zcu = zcu, 4577 .air = air.*, 4578 .liveness = liveness orelse break :verify_liveness, 4579 .intern_pool = ip, 4580 }; 4581 defer verify.deinit(); 4582 4583 verify.verify() catch |err| switch (err) { 4584 error.OutOfMemory => return error.OutOfMemory, 4585 else => return zcu.codegenFail(nav, "invalid liveness: {t}", .{err}), 4586 }; 4587 } 4588 4589 // The LLVM backend is special, because we only need to do codegen. There is no equivalent to the 4590 // "emit" step because LLVM does not support incremental linking. Our linker (LLD or self-hosted) 4591 // will just see the ZCU object file which LLVM ultimately emits. 4592 if (zcu.llvm_object) |llvm_object| { 4593 assert(zcu.pending_codegen_jobs.load(.monotonic) == 2); // only one codegen at a time (but the value is 2 because 1 is the base) 4594 try llvm_object.updateFunc(pt, func_index, air, &liveness); 4595 return error.BackendDoesNotProduceMir; 4596 } 4597 4598 const lf = comp.bin_file orelse return error.NoLinkFile; 4599 4600 // Just like LLVM, the SPIR-V backend can't multi-threaded due to SPIR-V design limitations. 4601 if (lf.cast(.spirv)) |spirv_file| { 4602 assert(zcu.pending_codegen_jobs.load(.monotonic) == 2); // only one codegen at a time (but the value is 2 because 1 is the base) 4603 spirv_file.updateFunc(pt, func_index, air, &liveness) catch |err| { 4604 switch (err) { 4605 error.OutOfMemory => comp.link_diags.setAllocFailure(), 4606 } 4607 return error.CodegenFail; 4608 }; 4609 return error.BackendDoesNotProduceMir; 4610 } 4611 4612 return codegen.generateFunction(lf, pt, zcu.navSrcLoc(nav), func_index, air, &liveness) catch |err| switch (err) { 4613 error.OutOfMemory, 4614 error.CodegenFail, 4615 => |e| return e, 4616 error.Overflow, 4617 error.RelocationNotByteAligned, 4618 => return zcu.codegenFail(nav, "unable to codegen: {s}", .{@errorName(err)}), 4619 }; 4620 } 4621 4622 fn printVerboseAir( 4623 pt: Zcu.PerThread, 4624 liveness: ?Air.Liveness, 4625 fqn: InternPool.NullTerminatedString, 4626 air: *const Air, 4627 w: *Io.Writer, 4628 ) Io.Writer.Error!void { 4629 const zcu = pt.zcu; 4630 const ip = &zcu.intern_pool; 4631 try w.print("# Begin Function AIR: {f}:\n", .{fqn.fmt(ip)}); 4632 try air.write(w, pt, liveness); 4633 try w.print("# End Function AIR: {f}\n\n", .{fqn.fmt(ip)}); 4634 }