blob 5dcda2ea (150746B) - Raw
1 //! Compilation of all Zig source code is represented by one `Module`. 2 //! Each `Compilation` has exactly one or zero `Module`, depending on whether 3 //! there is or is not any zig source code, respectively. 4 5 const std = @import("std"); 6 const builtin = @import("builtin"); 7 const mem = std.mem; 8 const Allocator = std.mem.Allocator; 9 const assert = std.debug.assert; 10 const log = std.log.scoped(.module); 11 const BigIntConst = std.math.big.int.Const; 12 const BigIntMutable = std.math.big.int.Mutable; 13 const Target = std.Target; 14 const Ast = std.zig.Ast; 15 16 /// Deprecated, use `Zcu`. 17 const Module = Zcu; 18 const Zcu = @This(); 19 const Compilation = @import("Compilation.zig"); 20 const Cache = std.Build.Cache; 21 const Value = @import("Value.zig"); 22 const Type = @import("Type.zig"); 23 const Package = @import("Package.zig"); 24 const link = @import("link.zig"); 25 const Air = @import("Air.zig"); 26 const Zir = std.zig.Zir; 27 const trace = @import("tracy.zig").trace; 28 const AstGen = std.zig.AstGen; 29 const Sema = @import("Sema.zig"); 30 const target_util = @import("target.zig"); 31 const build_options = @import("build_options"); 32 const Liveness = @import("Liveness.zig"); 33 const isUpDir = @import("introspect.zig").isUpDir; 34 const clang = @import("clang.zig"); 35 const InternPool = @import("InternPool.zig"); 36 const Alignment = InternPool.Alignment; 37 const AnalUnit = InternPool.AnalUnit; 38 const BuiltinFn = std.zig.BuiltinFn; 39 const LlvmObject = @import("codegen/llvm.zig").Object; 40 41 comptime { 42 @setEvalBranchQuota(4000); 43 for ( 44 @typeInfo(Zir.Inst.Ref).Enum.fields, 45 @typeInfo(Air.Inst.Ref).Enum.fields, 46 @typeInfo(InternPool.Index).Enum.fields, 47 ) |zir_field, air_field, ip_field| { 48 assert(mem.eql(u8, zir_field.name, ip_field.name)); 49 assert(mem.eql(u8, air_field.name, ip_field.name)); 50 } 51 } 52 53 /// General-purpose allocator. Used for both temporary and long-term storage. 54 gpa: Allocator, 55 comp: *Compilation, 56 /// Usually, the LlvmObject is managed by linker code, however, in the case 57 /// that -fno-emit-bin is specified, the linker code never executes, so we 58 /// store the LlvmObject here. 59 llvm_object: ?*LlvmObject, 60 61 /// Pointer to externally managed resource. 62 root_mod: *Package.Module, 63 /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which 64 /// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests. 65 main_mod: *Package.Module, 66 std_mod: *Package.Module, 67 sema_prog_node: std.Progress.Node = std.Progress.Node.none, 68 codegen_prog_node: std.Progress.Node = std.Progress.Node.none, 69 70 /// Used by AstGen worker to load and store ZIR cache. 71 global_zir_cache: Compilation.Directory, 72 /// Used by AstGen worker to load and store ZIR cache. 73 local_zir_cache: Compilation.Directory, 74 75 /// This is where all `Export` values are stored. Not all values here are necessarily valid exports; 76 /// to enumerate all exports, `single_exports` and `multi_exports` must be consulted. 77 all_exports: std.ArrayListUnmanaged(Export) = .{}, 78 /// This is a list of free indices in `all_exports`. These indices may be reused by exports from 79 /// future semantic analysis. 80 free_exports: std.ArrayListUnmanaged(u32) = .{}, 81 /// Maps from an `AnalUnit` which performs a single export, to the index into `all_exports` of 82 /// the export it performs. Note that the key is not the `Decl` being exported, but the `AnalUnit` 83 /// whose analysis triggered the export. 84 single_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, 85 /// Like `single_exports`, but for `AnalUnit`s which perform multiple exports. 86 /// The exports are `all_exports.items[index..][0..len]`. 87 multi_exports: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { 88 index: u32, 89 len: u32, 90 }) = .{}, 91 92 /// The set of all the Zig source files in the Zig Compilation Unit. Tracked in 93 /// order to iterate over it and check which source files have been modified on 94 /// the file system when an update is requested, as well as to cache `@import` 95 /// results. 96 /// 97 /// Keys are fully resolved file paths. This table owns the keys and values. 98 /// 99 /// Protected by Compilation's mutex. 100 /// 101 /// Not serialized. This state is reconstructed during the first call to 102 /// `Compilation.update` of the process for a given `Compilation`. 103 /// 104 /// Indexes correspond 1:1 to `files`. 105 import_table: std.StringArrayHashMapUnmanaged(File.Index) = .{}, 106 107 /// The set of all the files which have been loaded with `@embedFile` in the Module. 108 /// We keep track of this in order to iterate over it and check which files have been 109 /// modified on the file system when an update is requested, as well as to cache 110 /// `@embedFile` results. 111 /// Keys are fully resolved file paths. This table owns the keys and values. 112 embed_table: std.StringArrayHashMapUnmanaged(*EmbedFile) = .{}, 113 114 /// Stores all Type and Value objects. 115 /// The idea is that this will be periodically garbage-collected, but such logic 116 /// is not yet implemented. 117 intern_pool: InternPool = .{}, 118 119 /// The ErrorMsg memory is owned by the `AnalUnit`, using Module's general purpose allocator. 120 failed_analysis: std.AutoArrayHashMapUnmanaged(AnalUnit, *ErrorMsg) = .{}, 121 /// Keep track of one `@compileLog` callsite per `AnalUnit`. 122 /// The value is the source location of the `@compileLog` call, convertible to a `LazySrcLoc`. 123 compile_log_sources: std.AutoArrayHashMapUnmanaged(AnalUnit, extern struct { 124 base_node_inst: InternPool.TrackedInst.Index, 125 node_offset: i32, 126 pub fn src(self: @This()) LazySrcLoc { 127 return .{ 128 .base_node_inst = self.base_node_inst, 129 .offset = LazySrcLoc.Offset.nodeOffset(self.node_offset), 130 }; 131 } 132 }) = .{}, 133 /// Using a map here for consistency with the other fields here. 134 /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator. 135 failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{}, 136 /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. 137 failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{}, 138 /// Key is index into `all_exports`. 139 failed_exports: std.AutoArrayHashMapUnmanaged(u32, *ErrorMsg) = .{}, 140 /// If analysis failed due to a cimport error, the corresponding Clang errors 141 /// are stored here. 142 cimport_errors: std.AutoArrayHashMapUnmanaged(AnalUnit, std.zig.ErrorBundle) = .{}, 143 144 /// Key is the error name, index is the error tag value. Index 0 has a length-0 string. 145 global_error_set: GlobalErrorSet = .{}, 146 147 /// Maximum amount of distinct error values, set by --error-limit 148 error_limit: ErrorInt, 149 150 /// Value is the number of PO or outdated Decls which this AnalUnit depends on. 151 potentially_outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, 152 /// Value is the number of PO or outdated Decls which this AnalUnit depends on. 153 /// Once this value drops to 0, the AnalUnit is a candidate for re-analysis. 154 outdated: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, 155 /// This contains all `AnalUnit`s in `outdated` whose PO dependency count is 0. 156 /// Such `AnalUnit`s are ready for immediate re-analysis. 157 /// See `findOutdatedToAnalyze` for details. 158 outdated_ready: std.AutoArrayHashMapUnmanaged(AnalUnit, void) = .{}, 159 /// This contains a set of Decls which may not be in `outdated`, but are the 160 /// root Decls of files which have updated source and thus must be re-analyzed. 161 /// If such a Decl is only in this set, the struct type index may be preserved 162 /// (only the namespace might change). If such a Decl is also `outdated`, the 163 /// struct type index must be recreated. 164 outdated_file_root: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, 165 /// This contains a list of AnalUnit whose analysis or codegen failed, but the 166 /// failure was something like running out of disk space, and trying again may 167 /// succeed. On the next update, we will flush this list, marking all members of 168 /// it as outdated. 169 retryable_failures: std.ArrayListUnmanaged(AnalUnit) = .{}, 170 171 stage1_flags: packed struct { 172 have_winmain: bool = false, 173 have_wwinmain: bool = false, 174 have_winmain_crt_startup: bool = false, 175 have_wwinmain_crt_startup: bool = false, 176 have_dllmain_crt_startup: bool = false, 177 have_c_main: bool = false, 178 reserved: u2 = 0, 179 } = .{}, 180 181 compile_log_text: std.ArrayListUnmanaged(u8) = .{}, 182 183 emit_h: ?*GlobalEmitH, 184 185 test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, 186 187 /// TODO: the key here will be a `Cau.Index`. 188 global_assembly: std.AutoArrayHashMapUnmanaged(Decl.Index, []u8) = .{}, 189 190 /// Key is the `AnalUnit` *performing* the reference. This representation allows 191 /// incremental updates to quickly delete references caused by a specific `AnalUnit`. 192 /// Value is index into `all_reference` of the first reference triggered by the unit. 193 /// The `next` field on the `Reference` forms a linked list of all references 194 /// triggered by the key `AnalUnit`. 195 reference_table: std.AutoArrayHashMapUnmanaged(AnalUnit, u32) = .{}, 196 all_references: std.ArrayListUnmanaged(Reference) = .{}, 197 /// Freelist of indices in `all_references`. 198 free_references: std.ArrayListUnmanaged(u32) = .{}, 199 200 panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len, 201 /// The panic function body. 202 panic_func_index: InternPool.Index = .none, 203 null_stack_trace: InternPool.Index = .none, 204 205 pub const PerThread = @import("Zcu/PerThread.zig"); 206 207 pub const PanicId = enum { 208 unreach, 209 unwrap_null, 210 cast_to_null, 211 incorrect_alignment, 212 invalid_error_code, 213 cast_truncated_data, 214 negative_to_unsigned, 215 integer_overflow, 216 shl_overflow, 217 shr_overflow, 218 divide_by_zero, 219 exact_division_remainder, 220 inactive_union_field, 221 integer_part_out_of_bounds, 222 corrupt_switch, 223 shift_rhs_too_big, 224 invalid_enum_value, 225 sentinel_mismatch, 226 unwrap_error, 227 index_out_of_bounds, 228 start_index_greater_than_end, 229 for_len_mismatch, 230 memcpy_len_mismatch, 231 memcpy_alias, 232 noreturn_returned, 233 234 pub const len = @typeInfo(PanicId).Enum.fields.len; 235 }; 236 237 pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void); 238 239 pub const CImportError = struct { 240 offset: u32, 241 line: u32, 242 column: u32, 243 path: ?[*:0]u8, 244 source_line: ?[*:0]u8, 245 msg: [*:0]u8, 246 247 pub fn deinit(err: CImportError, gpa: Allocator) void { 248 if (err.path) |some| gpa.free(std.mem.span(some)); 249 if (err.source_line) |some| gpa.free(std.mem.span(some)); 250 gpa.free(std.mem.span(err.msg)); 251 } 252 }; 253 254 /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. 255 pub const GlobalEmitH = struct { 256 /// Where to put the output. 257 loc: Compilation.EmitLoc, 258 /// When emit_h is non-null, each Decl gets one more compile error slot for 259 /// emit-h failing for that Decl. This table is also how we tell if a Decl has 260 /// failed emit-h or succeeded. 261 failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{}, 262 /// Tracks all decls in order to iterate over them and emit .h code for them. 263 decl_table: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, 264 /// Similar to the allocated_decls field of Module, this is where `EmitH` objects 265 /// are allocated. There will be exactly one EmitH object per Decl object, with 266 /// identical indexes. 267 allocated_emit_h: std.SegmentedList(EmitH, 0) = .{}, 268 269 pub fn declPtr(global_emit_h: *GlobalEmitH, decl_index: Decl.Index) *EmitH { 270 return global_emit_h.allocated_emit_h.at(@intFromEnum(decl_index)); 271 } 272 }; 273 274 pub const ErrorInt = u32; 275 276 pub const Exported = union(enum) { 277 /// The Decl being exported. Note this is *not* the Decl performing the export. 278 decl_index: Decl.Index, 279 /// Constant value being exported. 280 value: InternPool.Index, 281 282 pub fn getValue(exported: Exported, zcu: *Zcu) Value { 283 return switch (exported) { 284 .decl_index => |decl_index| zcu.declPtr(decl_index).val, 285 .value => |value| Value.fromInterned(value), 286 }; 287 } 288 289 pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment { 290 return switch (exported) { 291 .decl_index => |decl_index| zcu.declPtr(decl_index).alignment, 292 .value => .none, 293 }; 294 } 295 }; 296 297 pub const Export = struct { 298 opts: Options, 299 src: LazySrcLoc, 300 exported: Exported, 301 status: enum { 302 in_progress, 303 failed, 304 /// Indicates that the failure was due to a temporary issue, such as an I/O error 305 /// when writing to the output file. Retrying the export may succeed. 306 failed_retryable, 307 complete, 308 }, 309 310 pub const Options = struct { 311 name: InternPool.NullTerminatedString, 312 linkage: std.builtin.GlobalLinkage = .strong, 313 section: InternPool.OptionalNullTerminatedString = .none, 314 visibility: std.builtin.SymbolVisibility = .default, 315 }; 316 }; 317 318 pub const Reference = struct { 319 /// The `AnalUnit` whose semantic analysis was triggered by this reference. 320 referenced: AnalUnit, 321 /// Index into `all_references` of the next `Reference` triggered by the same `AnalUnit`. 322 /// `std.math.maxInt(u32)` is the sentinel. 323 next: u32, 324 /// The source location of the reference. 325 src: LazySrcLoc, 326 }; 327 328 pub const Decl = struct { 329 /// Equal to `fqn` if already fully qualified. 330 name: InternPool.NullTerminatedString, 331 /// Fully qualified name. 332 fqn: InternPool.NullTerminatedString, 333 /// The most recent Value of the Decl after a successful semantic analysis. 334 /// Populated when `has_tv`. 335 val: Value, 336 /// Populated when `has_tv`. 337 @"linksection": InternPool.OptionalNullTerminatedString, 338 /// Populated when `has_tv`. 339 alignment: Alignment, 340 /// Populated when `has_tv`. 341 @"addrspace": std.builtin.AddressSpace, 342 /// The direct parent namespace of the Decl. In the case of the Decl 343 /// corresponding to a file, this is the namespace of the struct, since 344 /// there is no parent. 345 src_namespace: Namespace.Index, 346 347 /// Index of the ZIR `declaration` instruction from which this `Decl` was created. 348 /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`. 349 zir_decl_index: InternPool.TrackedInst.Index.Optional, 350 351 /// Represents the "shallow" analysis status. For example, for decls that are functions, 352 /// the function type is analyzed with this set to `in_progress`, however, the semantic 353 /// analysis of the function body is performed with this value set to `success`. Functions 354 /// have their own analysis status field. 355 analysis: enum { 356 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore 357 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. 358 unreferenced, 359 /// Semantic analysis for this Decl is running right now. 360 /// This state detects dependency loops. 361 in_progress, 362 /// The file corresponding to this Decl had a parse error or ZIR error. 363 /// There will be a corresponding ErrorMsg in Zcu.failed_files. 364 file_failure, 365 /// This Decl might be OK but it depends on another one which did not 366 /// successfully complete semantic analysis. 367 dependency_failure, 368 /// Semantic analysis failure. 369 /// There will be a corresponding ErrorMsg in Zcu.failed_analysis. 370 sema_failure, 371 /// There will be a corresponding ErrorMsg in Zcu.failed_analysis. 372 codegen_failure, 373 /// Sematic analysis and constant value codegen of this Decl has 374 /// succeeded. However, the Decl may be outdated due to an in-progress 375 /// update. Note that for a function, this does not mean codegen of the 376 /// function body succeded: that state is indicated by the function's 377 /// `analysis` field. 378 complete, 379 }, 380 /// Whether `typed_value`, `align`, `linksection` and `addrspace` are populated. 381 has_tv: bool, 382 /// If `true` it means the `Decl` is the resource owner of the type/value associated 383 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally 384 /// check if the value owns a `Namespace`, and destroy that too. 385 owns_tv: bool, 386 /// Whether the corresponding AST decl has a `pub` keyword. 387 is_pub: bool, 388 /// Whether the corresponding AST decl has a `export` keyword. 389 is_exported: bool, 390 /// What kind of a declaration is this. 391 kind: Kind, 392 393 pub const Kind = enum { 394 @"usingnamespace", 395 @"test", 396 @"comptime", 397 named, 398 anon, 399 }; 400 401 pub const Index = InternPool.DeclIndex; 402 pub const OptionalIndex = InternPool.OptionalDeclIndex; 403 404 pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies { 405 const zir = decl.getFileScope(zcu).zir; 406 const zir_index = decl.zir_decl_index.unwrap().?.resolve(&zcu.intern_pool); 407 const declaration = zir.instructions.items(.data)[@intFromEnum(zir_index)].declaration; 408 const extra = zir.extraData(Zir.Inst.Declaration, declaration.payload_index); 409 return extra.data.getBodies(@intCast(extra.end), zir); 410 } 411 412 pub fn typeOf(decl: Decl, zcu: *const Zcu) Type { 413 assert(decl.has_tv); 414 return decl.val.typeOf(zcu); 415 } 416 417 /// Small wrapper for Sema to use over direct access to the `val` field. 418 /// If the value is not populated, instead returns `error.AnalysisFail`. 419 pub fn valueOrFail(decl: Decl) error{AnalysisFail}!Value { 420 if (!decl.has_tv) return error.AnalysisFail; 421 return decl.val; 422 } 423 424 pub fn getOwnedFunction(decl: Decl, zcu: *Zcu) ?InternPool.Key.Func { 425 const i = decl.getOwnedFunctionIndex(); 426 if (i == .none) return null; 427 return switch (zcu.intern_pool.indexToKey(i)) { 428 .func => |func| func, 429 else => null, 430 }; 431 } 432 433 /// This returns an InternPool.Index even when the value is not a function. 434 pub fn getOwnedFunctionIndex(decl: Decl) InternPool.Index { 435 return if (decl.owns_tv) decl.val.toIntern() else .none; 436 } 437 438 /// If the Decl owns its value and it is an extern function, returns it, 439 /// otherwise null. 440 pub fn getOwnedExternFunc(decl: Decl, zcu: *Zcu) ?InternPool.Key.ExternFunc { 441 return if (decl.owns_tv) decl.val.getExternFunc(zcu) else null; 442 } 443 444 /// If the Decl owns its value and it is a variable, returns it, 445 /// otherwise null. 446 pub fn getOwnedVariable(decl: Decl, zcu: *Zcu) ?InternPool.Key.Variable { 447 return if (decl.owns_tv) decl.val.getVariable(zcu) else null; 448 } 449 450 /// Gets the namespace that this Decl creates by being a struct, union, 451 /// enum, or opaque. 452 pub fn getInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex { 453 if (!decl.has_tv) return .none; 454 const ip = &zcu.intern_pool; 455 return switch (decl.val.ip_index) { 456 .empty_struct_type => .none, 457 .none => .none, 458 else => switch (ip.indexToKey(decl.val.toIntern())) { 459 .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace, 460 .struct_type => ip.loadStructType(decl.val.toIntern()).namespace, 461 .union_type => ip.loadUnionType(decl.val.toIntern()).namespace, 462 .enum_type => ip.loadEnumType(decl.val.toIntern()).namespace, 463 else => .none, 464 }, 465 }; 466 } 467 468 /// Like `getInnerNamespaceIndex`, but only returns it if the Decl is the owner. 469 pub fn getOwnedInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex { 470 if (!decl.owns_tv) return .none; 471 return decl.getInnerNamespaceIndex(zcu); 472 } 473 474 /// Same as `getOwnedInnerNamespaceIndex` but additionally obtains the pointer. 475 pub fn getOwnedInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace { 476 return zcu.namespacePtrUnwrap(decl.getOwnedInnerNamespaceIndex(zcu)); 477 } 478 479 /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer. 480 pub fn getInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace { 481 return zcu.namespacePtrUnwrap(decl.getInnerNamespaceIndex(zcu)); 482 } 483 484 pub fn getFileScope(decl: Decl, zcu: *Zcu) *File { 485 return zcu.fileByIndex(getFileScopeIndex(decl, zcu)); 486 } 487 488 pub fn getFileScopeIndex(decl: Decl, zcu: *Zcu) File.Index { 489 return zcu.namespacePtr(decl.src_namespace).file_scope; 490 } 491 492 pub fn getExternDecl(decl: Decl, zcu: *Zcu) OptionalIndex { 493 assert(decl.has_tv); 494 return switch (zcu.intern_pool.indexToKey(decl.val.toIntern())) { 495 .variable => |variable| if (variable.is_extern) variable.decl.toOptional() else .none, 496 .extern_func => |extern_func| extern_func.decl.toOptional(), 497 else => .none, 498 }; 499 } 500 501 pub fn isExtern(decl: Decl, zcu: *Zcu) bool { 502 return decl.getExternDecl(zcu) != .none; 503 } 504 505 pub fn getAlignment(decl: Decl, pt: Zcu.PerThread) Alignment { 506 assert(decl.has_tv); 507 if (decl.alignment != .none) return decl.alignment; 508 return decl.typeOf(pt.zcu).abiAlignment(pt); 509 } 510 511 pub fn declPtrType(decl: Decl, pt: Zcu.PerThread) !Type { 512 assert(decl.has_tv); 513 const decl_ty = decl.typeOf(pt.zcu); 514 return pt.ptrType(.{ 515 .child = decl_ty.toIntern(), 516 .flags = .{ 517 .alignment = if (decl.alignment == decl_ty.abiAlignment(pt)) 518 .none 519 else 520 decl.alignment, 521 .address_space = decl.@"addrspace", 522 .is_const = decl.getOwnedVariable(pt.zcu) == null, 523 }, 524 }); 525 } 526 527 /// Returns the source location of this `Decl`. 528 /// Asserts that this `Decl` corresponds to what will in future be a `Nav` (Named 529 /// Addressable Value): a source-level declaration or generic instantiation. 530 pub fn navSrcLoc(decl: Decl, zcu: *Zcu) LazySrcLoc { 531 return .{ 532 .base_node_inst = decl.zir_decl_index.unwrap() orelse inst: { 533 // generic instantiation 534 assert(decl.has_tv); 535 assert(decl.owns_tv); 536 const owner = zcu.funcInfo(decl.val.toIntern()).generic_owner; 537 const generic_owner_decl = zcu.declPtr(zcu.funcInfo(owner).owner_decl); 538 break :inst generic_owner_decl.zir_decl_index.unwrap().?; 539 }, 540 .offset = LazySrcLoc.Offset.nodeOffset(0), 541 }; 542 } 543 544 pub fn navSrcLine(decl: Decl, zcu: *Zcu) u32 { 545 const ip = &zcu.intern_pool; 546 const tracked = decl.zir_decl_index.unwrap() orelse inst: { 547 // generic instantiation 548 assert(decl.has_tv); 549 assert(decl.owns_tv); 550 const generic_owner_func = switch (ip.indexToKey(decl.val.toIntern())) { 551 .func => |func| func.generic_owner, 552 else => return 0, // TODO: this is probably a `variable` or something; figure this out when we finish sorting out `Decl`. 553 }; 554 const generic_owner_decl = zcu.declPtr(zcu.funcInfo(generic_owner_func).owner_decl); 555 break :inst generic_owner_decl.zir_decl_index.unwrap().?; 556 }; 557 const info = tracked.resolveFull(ip); 558 const file = zcu.fileByIndex(info.file); 559 assert(file.zir_loaded); 560 const zir = file.zir; 561 const inst = zir.instructions.get(@intFromEnum(info.inst)); 562 assert(inst.tag == .declaration); 563 return zir.extraData(Zir.Inst.Declaration, inst.data.declaration.payload_index).data.src_line; 564 } 565 566 pub fn typeSrcLine(decl: Decl, zcu: *Zcu) u32 { 567 assert(decl.has_tv); 568 assert(decl.owns_tv); 569 return decl.val.toType().typeDeclSrcLine(zcu).?; 570 } 571 }; 572 573 /// This state is attached to every Decl when Module emit_h is non-null. 574 pub const EmitH = struct { 575 fwd_decl: std.ArrayListUnmanaged(u8) = .{}, 576 }; 577 578 pub const DeclAdapter = struct { 579 zcu: *Zcu, 580 581 pub fn hash(self: @This(), s: InternPool.NullTerminatedString) u32 { 582 _ = self; 583 return std.hash.uint32(@intFromEnum(s)); 584 } 585 586 pub fn eql(self: @This(), a: InternPool.NullTerminatedString, b_decl_index: Decl.Index, b_index: usize) bool { 587 _ = b_index; 588 return a == self.zcu.declPtr(b_decl_index).name; 589 } 590 }; 591 592 /// The container that structs, enums, unions, and opaques have. 593 pub const Namespace = struct { 594 parent: OptionalIndex, 595 file_scope: File.Index, 596 /// Will be a struct, enum, union, or opaque. 597 decl_index: Decl.Index, 598 /// Direct children of the namespace. 599 /// Declaration order is preserved via entry order. 600 /// These are only declarations named directly by the AST; anonymous 601 /// declarations are not stored here. 602 decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{}, 603 /// Key is usingnamespace Decl itself. To find the namespace being included, 604 /// the Decl Value has to be resolved as a Type which has a Namespace. 605 /// Value is whether the usingnamespace decl is marked `pub`. 606 usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{}, 607 608 pub const Index = InternPool.NamespaceIndex; 609 pub const OptionalIndex = InternPool.OptionalNamespaceIndex; 610 611 const DeclContext = struct { 612 zcu: *Zcu, 613 614 pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 { 615 const decl = ctx.zcu.declPtr(decl_index); 616 return std.hash.uint32(@intFromEnum(decl.name)); 617 } 618 619 pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool { 620 _ = b_index; 621 const a_decl = ctx.zcu.declPtr(a_decl_index); 622 const b_decl = ctx.zcu.declPtr(b_decl_index); 623 return a_decl.name == b_decl.name; 624 } 625 }; 626 627 pub fn fileScope(ns: Namespace, zcu: *Zcu) *File { 628 return zcu.fileByIndex(ns.file_scope); 629 } 630 631 // This renders e.g. "std.fs.Dir.OpenOptions" 632 pub fn renderFullyQualifiedName( 633 ns: Namespace, 634 zcu: *Zcu, 635 name: InternPool.NullTerminatedString, 636 writer: anytype, 637 ) @TypeOf(writer).Error!void { 638 if (ns.parent.unwrap()) |parent| { 639 try zcu.namespacePtr(parent).renderFullyQualifiedName( 640 zcu, 641 zcu.declPtr(ns.decl_index).name, 642 writer, 643 ); 644 } else { 645 try ns.fileScope(zcu).renderFullyQualifiedName(writer); 646 } 647 if (name != .empty) try writer.print(".{}", .{name.fmt(&zcu.intern_pool)}); 648 } 649 650 /// This renders e.g. "std/fs.zig:Dir.OpenOptions" 651 pub fn renderFullyQualifiedDebugName( 652 ns: Namespace, 653 zcu: *Zcu, 654 name: InternPool.NullTerminatedString, 655 writer: anytype, 656 ) @TypeOf(writer).Error!void { 657 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: { 658 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName( 659 zcu, 660 zcu.declPtr(ns.decl_index).name, 661 writer, 662 ); 663 break :sep '.'; 664 } else sep: { 665 try ns.fileScope(zcu).renderFullyQualifiedDebugName(writer); 666 break :sep ':'; 667 }; 668 if (name != .empty) try writer.print("{c}{}", .{ sep, name.fmt(&zcu.intern_pool) }); 669 } 670 671 pub fn internFullyQualifiedName( 672 ns: Namespace, 673 pt: Zcu.PerThread, 674 name: InternPool.NullTerminatedString, 675 ) !InternPool.NullTerminatedString { 676 const zcu = pt.zcu; 677 const ip = &zcu.intern_pool; 678 679 const gpa = zcu.gpa; 680 const strings = ip.getLocal(pt.tid).getMutableStrings(gpa); 681 // Protects reads of interned strings from being reallocated during the call to 682 // renderFullyQualifiedName. 683 const slice = try strings.addManyAsSlice(count: { 684 var count: usize = name.length(ip) + 1; 685 var cur_ns = &ns; 686 while (true) { 687 const decl = zcu.declPtr(cur_ns.decl_index); 688 cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse { 689 count += ns.fileScope(zcu).fullyQualifiedNameLen(); 690 break :count count; 691 }); 692 count += decl.name.length(ip) + 1; 693 } 694 }); 695 var fbs = std.io.fixedBufferStream(slice[0]); 696 ns.renderFullyQualifiedName(zcu, name, fbs.writer()) catch unreachable; 697 assert(fbs.pos == slice[0].len); 698 699 // Sanitize the name for nvptx which is more restrictive. 700 // TODO This should be handled by the backend, not the frontend. Have a 701 // look at how the C backend does it for inspiration. 702 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; 703 if (cpu_arch.isNvptx()) { 704 for (slice[0]) |*byte| switch (byte.*) { 705 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_', 706 else => {}, 707 }; 708 } 709 710 return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(slice[0].len), .no_embedded_nulls); 711 } 712 713 pub fn getType(ns: Namespace, zcu: *Zcu) Type { 714 const decl = zcu.declPtr(ns.decl_index); 715 assert(decl.has_tv); 716 return decl.val.toType(); 717 } 718 }; 719 720 pub const File = struct { 721 status: enum { 722 never_loaded, 723 retryable_failure, 724 parse_failure, 725 astgen_failure, 726 success_zir, 727 }, 728 source_loaded: bool, 729 tree_loaded: bool, 730 zir_loaded: bool, 731 /// Relative to the owning package's root_src_dir. 732 /// Memory is stored in gpa, owned by File. 733 sub_file_path: []const u8, 734 /// Whether this is populated depends on `source_loaded`. 735 source: [:0]const u8, 736 /// Whether this is populated depends on `status`. 737 stat: Cache.File.Stat, 738 /// Whether this is populated or not depends on `tree_loaded`. 739 tree: Ast, 740 /// Whether this is populated or not depends on `zir_loaded`. 741 zir: Zir, 742 /// Module that this file is a part of, managed externally. 743 mod: *Package.Module, 744 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen. 745 multi_pkg: bool = false, 746 /// List of references to this file, used for multi-package errors. 747 references: std.ArrayListUnmanaged(File.Reference) = .{}, 748 749 /// The most recent successful ZIR for this file, with no errors. 750 /// This is only populated when a previously successful ZIR 751 /// newly introduces compile errors during an update. When ZIR is 752 /// successful, this field is unloaded. 753 prev_zir: ?*Zir = null, 754 755 /// A single reference to a file. 756 pub const Reference = union(enum) { 757 /// The file is imported directly (i.e. not as a package) with @import. 758 import: struct { 759 file: File.Index, 760 token: Ast.TokenIndex, 761 }, 762 /// The file is the root of a module. 763 root: *Package.Module, 764 }; 765 766 pub fn unload(file: *File, gpa: Allocator) void { 767 file.unloadTree(gpa); 768 file.unloadSource(gpa); 769 file.unloadZir(gpa); 770 } 771 772 pub fn unloadTree(file: *File, gpa: Allocator) void { 773 if (file.tree_loaded) { 774 file.tree_loaded = false; 775 file.tree.deinit(gpa); 776 } 777 } 778 779 pub fn unloadSource(file: *File, gpa: Allocator) void { 780 if (file.source_loaded) { 781 file.source_loaded = false; 782 gpa.free(file.source); 783 } 784 } 785 786 pub fn unloadZir(file: *File, gpa: Allocator) void { 787 if (file.zir_loaded) { 788 file.zir_loaded = false; 789 file.zir.deinit(gpa); 790 } 791 } 792 793 pub const Source = struct { 794 bytes: [:0]const u8, 795 stat: Cache.File.Stat, 796 }; 797 798 pub fn getSource(file: *File, gpa: Allocator) !Source { 799 if (file.source_loaded) return Source{ 800 .bytes = file.source, 801 .stat = file.stat, 802 }; 803 804 // Keep track of inode, file size, mtime, hash so we can detect which files 805 // have been modified when an incremental update is requested. 806 var f = try file.mod.root.openFile(file.sub_file_path, .{}); 807 defer f.close(); 808 809 const stat = try f.stat(); 810 811 if (stat.size > std.math.maxInt(u32)) 812 return error.FileTooBig; 813 814 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); 815 defer if (!file.source_loaded) gpa.free(source); 816 const amt = try f.readAll(source); 817 if (amt != stat.size) 818 return error.UnexpectedEndOfFile; 819 820 // Here we do not modify stat fields because this function is the one 821 // used for error reporting. We need to keep the stat fields stale so that 822 // astGenFile can know to regenerate ZIR. 823 824 file.source = source; 825 file.source_loaded = true; 826 return Source{ 827 .bytes = source, 828 .stat = .{ 829 .size = stat.size, 830 .inode = stat.inode, 831 .mtime = stat.mtime, 832 }, 833 }; 834 } 835 836 pub fn getTree(file: *File, gpa: Allocator) !*const Ast { 837 if (file.tree_loaded) return &file.tree; 838 839 const source = try file.getSource(gpa); 840 file.tree = try Ast.parse(gpa, source.bytes, .zig); 841 file.tree_loaded = true; 842 return &file.tree; 843 } 844 845 pub fn fullyQualifiedNameLen(file: File) usize { 846 const ext = std.fs.path.extension(file.sub_file_path); 847 return file.sub_file_path.len - ext.len; 848 } 849 850 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void { 851 // Convert all the slashes into dots and truncate the extension. 852 const ext = std.fs.path.extension(file.sub_file_path); 853 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len]; 854 for (noext) |byte| switch (byte) { 855 '/', '\\' => try writer.writeByte('.'), 856 else => try writer.writeByte(byte), 857 }; 858 } 859 860 pub fn renderFullyQualifiedDebugName(file: File, writer: anytype) !void { 861 for (file.sub_file_path) |byte| switch (byte) { 862 '/', '\\' => try writer.writeByte('/'), 863 else => try writer.writeByte(byte), 864 }; 865 } 866 867 pub fn internFullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString { 868 const gpa = pt.zcu.gpa; 869 const ip = &pt.zcu.intern_pool; 870 const strings = ip.getLocal(pt.tid).getMutableStrings(gpa); 871 const slice = try strings.addManyAsSlice(file.fullyQualifiedNameLen()); 872 var fbs = std.io.fixedBufferStream(slice[0]); 873 file.renderFullyQualifiedName(fbs.writer()) catch unreachable; 874 assert(fbs.pos == slice[0].len); 875 return ip.getOrPutTrailingString(gpa, pt.tid, @intCast(slice[0].len), .no_embedded_nulls); 876 } 877 878 pub fn fullPath(file: File, ally: Allocator) ![]u8 { 879 return file.mod.root.joinString(ally, file.sub_file_path); 880 } 881 882 pub fn dumpSrc(file: *File, src: LazySrcLoc) void { 883 const loc = std.zig.findLineColumn(file.source.bytes, src); 884 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 }); 885 } 886 887 pub fn okToReportErrors(file: File) bool { 888 return switch (file.status) { 889 .parse_failure, .astgen_failure => false, 890 else => true, 891 }; 892 } 893 894 /// Add a reference to this file during AstGen. 895 pub fn addReference(file: *File, zcu: *Zcu, ref: File.Reference) !void { 896 // Don't add the same module root twice. Note that since we always add module roots at the 897 // front of the references array (see below), this loop is actually O(1) on valid code. 898 if (ref == .root) { 899 for (file.references.items) |other| { 900 switch (other) { 901 .root => |r| if (ref.root == r) return, 902 else => break, // reached the end of the "is-root" references 903 } 904 } 905 } 906 907 switch (ref) { 908 // We put root references at the front of the list both to make the above loop fast and 909 // to make multi-module errors more helpful (since "root-of" notes are generally more 910 // informative than "imported-from" notes). This path is hit very rarely, so the speed 911 // of the insert operation doesn't matter too much. 912 .root => try file.references.insert(zcu.gpa, 0, ref), 913 914 // Other references we'll just put at the end. 915 else => try file.references.append(zcu.gpa, ref), 916 } 917 918 const mod = switch (ref) { 919 .import => |import| zcu.fileByIndex(import.file).mod, 920 .root => |mod| mod, 921 }; 922 if (mod != file.mod) file.multi_pkg = true; 923 } 924 925 /// Mark this file and every file referenced by it as multi_pkg and report an 926 /// astgen_failure error for them. AstGen must have completed in its entirety. 927 pub fn recursiveMarkMultiPkg(file: *File, pt: Zcu.PerThread) void { 928 file.multi_pkg = true; 929 file.status = .astgen_failure; 930 931 // We can only mark children as failed if the ZIR is loaded, which may not 932 // be the case if there were other astgen failures in this file 933 if (!file.zir_loaded) return; 934 935 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; 936 if (imports_index == 0) return; 937 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); 938 939 var extra_index = extra.end; 940 for (0..extra.data.imports_len) |_| { 941 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index); 942 extra_index = item.end; 943 944 const import_path = file.zir.nullTerminatedString(item.data.name); 945 if (mem.eql(u8, import_path, "builtin")) continue; 946 947 const res = pt.importFile(file, import_path) catch continue; 948 if (!res.is_pkg and !res.file.multi_pkg) { 949 res.file.recursiveMarkMultiPkg(pt); 950 } 951 } 952 } 953 954 pub const Index = InternPool.FileIndex; 955 }; 956 957 pub const EmbedFile = struct { 958 /// Relative to the owning module's root directory. 959 sub_file_path: InternPool.NullTerminatedString, 960 /// Module that this file is a part of, managed externally. 961 owner: *Package.Module, 962 stat: Cache.File.Stat, 963 val: InternPool.Index, 964 src_loc: LazySrcLoc, 965 }; 966 967 /// This struct holds data necessary to construct API-facing `AllErrors.Message`. 968 /// Its memory is managed with the general purpose allocator so that they 969 /// can be created and destroyed in response to incremental updates. 970 pub const ErrorMsg = struct { 971 src_loc: LazySrcLoc, 972 msg: []const u8, 973 notes: []ErrorMsg = &.{}, 974 reference_trace_root: AnalUnit.Optional = .none, 975 976 pub fn create( 977 gpa: Allocator, 978 src_loc: LazySrcLoc, 979 comptime format: []const u8, 980 args: anytype, 981 ) !*ErrorMsg { 982 assert(src_loc.offset != .unneeded); 983 const err_msg = try gpa.create(ErrorMsg); 984 errdefer gpa.destroy(err_msg); 985 err_msg.* = try ErrorMsg.init(gpa, src_loc, format, args); 986 return err_msg; 987 } 988 989 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`, 990 /// as well as all notes. 991 pub fn destroy(err_msg: *ErrorMsg, gpa: Allocator) void { 992 err_msg.deinit(gpa); 993 gpa.destroy(err_msg); 994 } 995 996 pub fn init( 997 gpa: Allocator, 998 src_loc: LazySrcLoc, 999 comptime format: []const u8, 1000 args: anytype, 1001 ) !ErrorMsg { 1002 return ErrorMsg{ 1003 .src_loc = src_loc, 1004 .msg = try std.fmt.allocPrint(gpa, format, args), 1005 }; 1006 } 1007 1008 pub fn deinit(err_msg: *ErrorMsg, gpa: Allocator) void { 1009 for (err_msg.notes) |*note| { 1010 note.deinit(gpa); 1011 } 1012 gpa.free(err_msg.notes); 1013 gpa.free(err_msg.msg); 1014 err_msg.* = undefined; 1015 } 1016 }; 1017 1018 /// Canonical reference to a position within a source file. 1019 pub const SrcLoc = struct { 1020 file_scope: *File, 1021 base_node: Ast.Node.Index, 1022 /// Relative to `base_node`. 1023 lazy: LazySrcLoc.Offset, 1024 1025 pub fn baseSrcToken(src_loc: SrcLoc) Ast.TokenIndex { 1026 const tree = src_loc.file_scope.tree; 1027 return tree.firstToken(src_loc.base_node); 1028 } 1029 1030 pub fn relativeToNodeIndex(src_loc: SrcLoc, offset: i32) Ast.Node.Index { 1031 return @bitCast(offset + @as(i32, @bitCast(src_loc.base_node))); 1032 } 1033 1034 pub const Span = Ast.Span; 1035 1036 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span { 1037 switch (src_loc.lazy) { 1038 .unneeded => unreachable, 1039 .entire_file => return Span{ .start = 0, .end = 1, .main = 0 }, 1040 1041 .byte_abs => |byte_index| return Span{ .start = byte_index, .end = byte_index + 1, .main = byte_index }, 1042 1043 .token_abs => |tok_index| { 1044 const tree = try src_loc.file_scope.getTree(gpa); 1045 const start = tree.tokens.items(.start)[tok_index]; 1046 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1047 return Span{ .start = start, .end = end, .main = start }; 1048 }, 1049 .node_abs => |node| { 1050 const tree = try src_loc.file_scope.getTree(gpa); 1051 return tree.nodeToSpan(node); 1052 }, 1053 .byte_offset => |byte_off| { 1054 const tree = try src_loc.file_scope.getTree(gpa); 1055 const tok_index = src_loc.baseSrcToken(); 1056 const start = tree.tokens.items(.start)[tok_index] + byte_off; 1057 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1058 return Span{ .start = start, .end = end, .main = start }; 1059 }, 1060 .token_offset => |tok_off| { 1061 const tree = try src_loc.file_scope.getTree(gpa); 1062 const tok_index = src_loc.baseSrcToken() + tok_off; 1063 const start = tree.tokens.items(.start)[tok_index]; 1064 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1065 return Span{ .start = start, .end = end, .main = start }; 1066 }, 1067 .node_offset => |traced_off| { 1068 const node_off = traced_off.x; 1069 const tree = try src_loc.file_scope.getTree(gpa); 1070 const node = src_loc.relativeToNodeIndex(node_off); 1071 assert(src_loc.file_scope.tree_loaded); 1072 return tree.nodeToSpan(node); 1073 }, 1074 .node_offset_main_token => |node_off| { 1075 const tree = try src_loc.file_scope.getTree(gpa); 1076 const node = src_loc.relativeToNodeIndex(node_off); 1077 const main_token = tree.nodes.items(.main_token)[node]; 1078 return tree.tokensToSpan(main_token, main_token, main_token); 1079 }, 1080 .node_offset_bin_op => |node_off| { 1081 const tree = try src_loc.file_scope.getTree(gpa); 1082 const node = src_loc.relativeToNodeIndex(node_off); 1083 assert(src_loc.file_scope.tree_loaded); 1084 return tree.nodeToSpan(node); 1085 }, 1086 .node_offset_initializer => |node_off| { 1087 const tree = try src_loc.file_scope.getTree(gpa); 1088 const node = src_loc.relativeToNodeIndex(node_off); 1089 return tree.tokensToSpan( 1090 tree.firstToken(node) - 3, 1091 tree.lastToken(node), 1092 tree.nodes.items(.main_token)[node] - 2, 1093 ); 1094 }, 1095 .node_offset_var_decl_ty => |node_off| { 1096 const tree = try src_loc.file_scope.getTree(gpa); 1097 const node = src_loc.relativeToNodeIndex(node_off); 1098 const node_tags = tree.nodes.items(.tag); 1099 const full = switch (node_tags[node]) { 1100 .global_var_decl, 1101 .local_var_decl, 1102 .simple_var_decl, 1103 .aligned_var_decl, 1104 => tree.fullVarDecl(node).?, 1105 .@"usingnamespace" => { 1106 const node_data = tree.nodes.items(.data); 1107 return tree.nodeToSpan(node_data[node].lhs); 1108 }, 1109 else => unreachable, 1110 }; 1111 if (full.ast.type_node != 0) { 1112 return tree.nodeToSpan(full.ast.type_node); 1113 } 1114 const tok_index = full.ast.mut_token + 1; // the name token 1115 const start = tree.tokens.items(.start)[tok_index]; 1116 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1117 return Span{ .start = start, .end = end, .main = start }; 1118 }, 1119 .node_offset_var_decl_align => |node_off| { 1120 const tree = try src_loc.file_scope.getTree(gpa); 1121 const node = src_loc.relativeToNodeIndex(node_off); 1122 const full = tree.fullVarDecl(node).?; 1123 return tree.nodeToSpan(full.ast.align_node); 1124 }, 1125 .node_offset_var_decl_section => |node_off| { 1126 const tree = try src_loc.file_scope.getTree(gpa); 1127 const node = src_loc.relativeToNodeIndex(node_off); 1128 const full = tree.fullVarDecl(node).?; 1129 return tree.nodeToSpan(full.ast.section_node); 1130 }, 1131 .node_offset_var_decl_addrspace => |node_off| { 1132 const tree = try src_loc.file_scope.getTree(gpa); 1133 const node = src_loc.relativeToNodeIndex(node_off); 1134 const full = tree.fullVarDecl(node).?; 1135 return tree.nodeToSpan(full.ast.addrspace_node); 1136 }, 1137 .node_offset_var_decl_init => |node_off| { 1138 const tree = try src_loc.file_scope.getTree(gpa); 1139 const node = src_loc.relativeToNodeIndex(node_off); 1140 const full = tree.fullVarDecl(node).?; 1141 return tree.nodeToSpan(full.ast.init_node); 1142 }, 1143 .node_offset_builtin_call_arg => |builtin_arg| { 1144 const tree = try src_loc.file_scope.getTree(gpa); 1145 const node_datas = tree.nodes.items(.data); 1146 const node_tags = tree.nodes.items(.tag); 1147 const node = src_loc.relativeToNodeIndex(builtin_arg.builtin_call_node); 1148 const param = switch (node_tags[node]) { 1149 .builtin_call_two, .builtin_call_two_comma => switch (builtin_arg.arg_index) { 1150 0 => node_datas[node].lhs, 1151 1 => node_datas[node].rhs, 1152 else => unreachable, 1153 }, 1154 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + builtin_arg.arg_index], 1155 else => unreachable, 1156 }; 1157 return tree.nodeToSpan(param); 1158 }, 1159 .node_offset_ptrcast_operand => |node_off| { 1160 const tree = try src_loc.file_scope.getTree(gpa); 1161 const main_tokens = tree.nodes.items(.main_token); 1162 const node_datas = tree.nodes.items(.data); 1163 const node_tags = tree.nodes.items(.tag); 1164 1165 var node = src_loc.relativeToNodeIndex(node_off); 1166 while (true) { 1167 switch (node_tags[node]) { 1168 .builtin_call_two, .builtin_call_two_comma => {}, 1169 else => break, 1170 } 1171 1172 if (node_datas[node].lhs == 0) break; // 0 args 1173 if (node_datas[node].rhs != 0) break; // 2 args 1174 1175 const builtin_token = main_tokens[node]; 1176 const builtin_name = tree.tokenSlice(builtin_token); 1177 const info = BuiltinFn.list.get(builtin_name) orelse break; 1178 1179 switch (info.tag) { 1180 else => break, 1181 .ptr_cast, 1182 .align_cast, 1183 .addrspace_cast, 1184 .const_cast, 1185 .volatile_cast, 1186 => {}, 1187 } 1188 1189 node = node_datas[node].lhs; 1190 } 1191 1192 return tree.nodeToSpan(node); 1193 }, 1194 .node_offset_array_access_index => |node_off| { 1195 const tree = try src_loc.file_scope.getTree(gpa); 1196 const node_datas = tree.nodes.items(.data); 1197 const node = src_loc.relativeToNodeIndex(node_off); 1198 return tree.nodeToSpan(node_datas[node].rhs); 1199 }, 1200 .node_offset_slice_ptr, 1201 .node_offset_slice_start, 1202 .node_offset_slice_end, 1203 .node_offset_slice_sentinel, 1204 => |node_off| { 1205 const tree = try src_loc.file_scope.getTree(gpa); 1206 const node = src_loc.relativeToNodeIndex(node_off); 1207 const full = tree.fullSlice(node).?; 1208 const part_node = switch (src_loc.lazy) { 1209 .node_offset_slice_ptr => full.ast.sliced, 1210 .node_offset_slice_start => full.ast.start, 1211 .node_offset_slice_end => full.ast.end, 1212 .node_offset_slice_sentinel => full.ast.sentinel, 1213 else => unreachable, 1214 }; 1215 return tree.nodeToSpan(part_node); 1216 }, 1217 .node_offset_call_func => |node_off| { 1218 const tree = try src_loc.file_scope.getTree(gpa); 1219 const node = src_loc.relativeToNodeIndex(node_off); 1220 var buf: [1]Ast.Node.Index = undefined; 1221 const full = tree.fullCall(&buf, node).?; 1222 return tree.nodeToSpan(full.ast.fn_expr); 1223 }, 1224 .node_offset_field_name => |node_off| { 1225 const tree = try src_loc.file_scope.getTree(gpa); 1226 const node_datas = tree.nodes.items(.data); 1227 const node_tags = tree.nodes.items(.tag); 1228 const node = src_loc.relativeToNodeIndex(node_off); 1229 var buf: [1]Ast.Node.Index = undefined; 1230 const tok_index = switch (node_tags[node]) { 1231 .field_access => node_datas[node].rhs, 1232 .call_one, 1233 .call_one_comma, 1234 .async_call_one, 1235 .async_call_one_comma, 1236 .call, 1237 .call_comma, 1238 .async_call, 1239 .async_call_comma, 1240 => blk: { 1241 const full = tree.fullCall(&buf, node).?; 1242 break :blk tree.lastToken(full.ast.fn_expr); 1243 }, 1244 else => tree.firstToken(node) - 2, 1245 }; 1246 const start = tree.tokens.items(.start)[tok_index]; 1247 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1248 return Span{ .start = start, .end = end, .main = start }; 1249 }, 1250 .node_offset_field_name_init => |node_off| { 1251 const tree = try src_loc.file_scope.getTree(gpa); 1252 const node = src_loc.relativeToNodeIndex(node_off); 1253 const tok_index = tree.firstToken(node) - 2; 1254 const start = tree.tokens.items(.start)[tok_index]; 1255 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1256 return Span{ .start = start, .end = end, .main = start }; 1257 }, 1258 .node_offset_deref_ptr => |node_off| { 1259 const tree = try src_loc.file_scope.getTree(gpa); 1260 const node = src_loc.relativeToNodeIndex(node_off); 1261 return tree.nodeToSpan(node); 1262 }, 1263 .node_offset_asm_source => |node_off| { 1264 const tree = try src_loc.file_scope.getTree(gpa); 1265 const node = src_loc.relativeToNodeIndex(node_off); 1266 const full = tree.fullAsm(node).?; 1267 return tree.nodeToSpan(full.ast.template); 1268 }, 1269 .node_offset_asm_ret_ty => |node_off| { 1270 const tree = try src_loc.file_scope.getTree(gpa); 1271 const node = src_loc.relativeToNodeIndex(node_off); 1272 const full = tree.fullAsm(node).?; 1273 const asm_output = full.outputs[0]; 1274 const node_datas = tree.nodes.items(.data); 1275 return tree.nodeToSpan(node_datas[asm_output].lhs); 1276 }, 1277 1278 .node_offset_if_cond => |node_off| { 1279 const tree = try src_loc.file_scope.getTree(gpa); 1280 const node = src_loc.relativeToNodeIndex(node_off); 1281 const node_tags = tree.nodes.items(.tag); 1282 const src_node = switch (node_tags[node]) { 1283 .if_simple, 1284 .@"if", 1285 => tree.fullIf(node).?.ast.cond_expr, 1286 1287 .while_simple, 1288 .while_cont, 1289 .@"while", 1290 => tree.fullWhile(node).?.ast.cond_expr, 1291 1292 .for_simple, 1293 .@"for", 1294 => { 1295 const inputs = tree.fullFor(node).?.ast.inputs; 1296 const start = tree.firstToken(inputs[0]); 1297 const end = tree.lastToken(inputs[inputs.len - 1]); 1298 return tree.tokensToSpan(start, end, start); 1299 }, 1300 1301 .@"orelse" => node, 1302 .@"catch" => node, 1303 else => unreachable, 1304 }; 1305 return tree.nodeToSpan(src_node); 1306 }, 1307 .for_input => |for_input| { 1308 const tree = try src_loc.file_scope.getTree(gpa); 1309 const node = src_loc.relativeToNodeIndex(for_input.for_node_offset); 1310 const for_full = tree.fullFor(node).?; 1311 const src_node = for_full.ast.inputs[for_input.input_index]; 1312 return tree.nodeToSpan(src_node); 1313 }, 1314 .for_capture_from_input => |node_off| { 1315 const tree = try src_loc.file_scope.getTree(gpa); 1316 const token_tags = tree.tokens.items(.tag); 1317 const input_node = src_loc.relativeToNodeIndex(node_off); 1318 // We have to actually linear scan the whole AST to find the for loop 1319 // that contains this input. 1320 const node_tags = tree.nodes.items(.tag); 1321 for (node_tags, 0..) |node_tag, node_usize| { 1322 const node = @as(Ast.Node.Index, @intCast(node_usize)); 1323 switch (node_tag) { 1324 .for_simple, .@"for" => { 1325 const for_full = tree.fullFor(node).?; 1326 for (for_full.ast.inputs, 0..) |input, input_index| { 1327 if (input_node == input) { 1328 var count = input_index; 1329 var tok = for_full.payload_token; 1330 while (true) { 1331 switch (token_tags[tok]) { 1332 .comma => { 1333 count -= 1; 1334 tok += 1; 1335 }, 1336 .identifier => { 1337 if (count == 0) 1338 return tree.tokensToSpan(tok, tok + 1, tok); 1339 tok += 1; 1340 }, 1341 .asterisk => { 1342 if (count == 0) 1343 return tree.tokensToSpan(tok, tok + 2, tok); 1344 tok += 1; 1345 }, 1346 else => unreachable, 1347 } 1348 } 1349 } 1350 } 1351 }, 1352 else => continue, 1353 } 1354 } else unreachable; 1355 }, 1356 .call_arg => |call_arg| { 1357 const tree = try src_loc.file_scope.getTree(gpa); 1358 const node = src_loc.relativeToNodeIndex(call_arg.call_node_offset); 1359 var buf: [2]Ast.Node.Index = undefined; 1360 const call_full = tree.fullCall(buf[0..1], node) orelse { 1361 const node_tags = tree.nodes.items(.tag); 1362 assert(node_tags[node] == .builtin_call); 1363 const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1]; 1364 switch (node_tags[call_args_node]) { 1365 .array_init_one, 1366 .array_init_one_comma, 1367 .array_init_dot_two, 1368 .array_init_dot_two_comma, 1369 .array_init_dot, 1370 .array_init_dot_comma, 1371 .array_init, 1372 .array_init_comma, 1373 => { 1374 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements; 1375 return tree.nodeToSpan(full[call_arg.arg_index]); 1376 }, 1377 .struct_init_one, 1378 .struct_init_one_comma, 1379 .struct_init_dot_two, 1380 .struct_init_dot_two_comma, 1381 .struct_init_dot, 1382 .struct_init_dot_comma, 1383 .struct_init, 1384 .struct_init_comma, 1385 => { 1386 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields; 1387 return tree.nodeToSpan(full[call_arg.arg_index]); 1388 }, 1389 else => return tree.nodeToSpan(call_args_node), 1390 } 1391 }; 1392 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]); 1393 }, 1394 .fn_proto_param, .fn_proto_param_type => |fn_proto_param| { 1395 const tree = try src_loc.file_scope.getTree(gpa); 1396 const node = src_loc.relativeToNodeIndex(fn_proto_param.fn_proto_node_offset); 1397 var buf: [1]Ast.Node.Index = undefined; 1398 const full = tree.fullFnProto(&buf, node).?; 1399 var it = full.iterate(tree); 1400 var i: usize = 0; 1401 while (it.next()) |param| : (i += 1) { 1402 if (i != fn_proto_param.param_index) continue; 1403 1404 switch (src_loc.lazy) { 1405 .fn_proto_param_type => if (param.anytype_ellipsis3) |tok| { 1406 return tree.tokenToSpan(tok); 1407 } else { 1408 return tree.nodeToSpan(param.type_expr); 1409 }, 1410 .fn_proto_param => if (param.anytype_ellipsis3) |tok| { 1411 const first = param.comptime_noalias orelse param.name_token orelse tok; 1412 return tree.tokensToSpan(first, tok, first); 1413 } else { 1414 const first = param.comptime_noalias orelse param.name_token orelse tree.firstToken(param.type_expr); 1415 return tree.tokensToSpan(first, tree.lastToken(param.type_expr), first); 1416 }, 1417 else => unreachable, 1418 } 1419 } 1420 unreachable; 1421 }, 1422 .node_offset_bin_lhs => |node_off| { 1423 const tree = try src_loc.file_scope.getTree(gpa); 1424 const node = src_loc.relativeToNodeIndex(node_off); 1425 const node_datas = tree.nodes.items(.data); 1426 return tree.nodeToSpan(node_datas[node].lhs); 1427 }, 1428 .node_offset_bin_rhs => |node_off| { 1429 const tree = try src_loc.file_scope.getTree(gpa); 1430 const node = src_loc.relativeToNodeIndex(node_off); 1431 const node_datas = tree.nodes.items(.data); 1432 return tree.nodeToSpan(node_datas[node].rhs); 1433 }, 1434 .array_cat_lhs, .array_cat_rhs => |cat| { 1435 const tree = try src_loc.file_scope.getTree(gpa); 1436 const node = src_loc.relativeToNodeIndex(cat.array_cat_offset); 1437 const node_datas = tree.nodes.items(.data); 1438 const arr_node = if (src_loc.lazy == .array_cat_lhs) 1439 node_datas[node].lhs 1440 else 1441 node_datas[node].rhs; 1442 1443 const node_tags = tree.nodes.items(.tag); 1444 var buf: [2]Ast.Node.Index = undefined; 1445 switch (node_tags[arr_node]) { 1446 .array_init_one, 1447 .array_init_one_comma, 1448 .array_init_dot_two, 1449 .array_init_dot_two_comma, 1450 .array_init_dot, 1451 .array_init_dot_comma, 1452 .array_init, 1453 .array_init_comma, 1454 => { 1455 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements; 1456 return tree.nodeToSpan(full[cat.elem_index]); 1457 }, 1458 else => return tree.nodeToSpan(arr_node), 1459 } 1460 }, 1461 1462 .node_offset_switch_operand => |node_off| { 1463 const tree = try src_loc.file_scope.getTree(gpa); 1464 const node = src_loc.relativeToNodeIndex(node_off); 1465 const node_datas = tree.nodes.items(.data); 1466 return tree.nodeToSpan(node_datas[node].lhs); 1467 }, 1468 1469 .node_offset_switch_special_prong => |node_off| { 1470 const tree = try src_loc.file_scope.getTree(gpa); 1471 const switch_node = src_loc.relativeToNodeIndex(node_off); 1472 const node_datas = tree.nodes.items(.data); 1473 const node_tags = tree.nodes.items(.tag); 1474 const main_tokens = tree.nodes.items(.main_token); 1475 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 1476 const case_nodes = tree.extra_data[extra.start..extra.end]; 1477 for (case_nodes) |case_node| { 1478 const case = tree.fullSwitchCase(case_node).?; 1479 const is_special = (case.ast.values.len == 0) or 1480 (case.ast.values.len == 1 and 1481 node_tags[case.ast.values[0]] == .identifier and 1482 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")); 1483 if (!is_special) continue; 1484 1485 return tree.nodeToSpan(case_node); 1486 } else unreachable; 1487 }, 1488 1489 .node_offset_switch_range => |node_off| { 1490 const tree = try src_loc.file_scope.getTree(gpa); 1491 const switch_node = src_loc.relativeToNodeIndex(node_off); 1492 const node_datas = tree.nodes.items(.data); 1493 const node_tags = tree.nodes.items(.tag); 1494 const main_tokens = tree.nodes.items(.main_token); 1495 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 1496 const case_nodes = tree.extra_data[extra.start..extra.end]; 1497 for (case_nodes) |case_node| { 1498 const case = tree.fullSwitchCase(case_node).?; 1499 const is_special = (case.ast.values.len == 0) or 1500 (case.ast.values.len == 1 and 1501 node_tags[case.ast.values[0]] == .identifier and 1502 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")); 1503 if (is_special) continue; 1504 1505 for (case.ast.values) |item_node| { 1506 if (node_tags[item_node] == .switch_range) { 1507 return tree.nodeToSpan(item_node); 1508 } 1509 } 1510 } else unreachable; 1511 }, 1512 .node_offset_fn_type_align => |node_off| { 1513 const tree = try src_loc.file_scope.getTree(gpa); 1514 const node = src_loc.relativeToNodeIndex(node_off); 1515 var buf: [1]Ast.Node.Index = undefined; 1516 const full = tree.fullFnProto(&buf, node).?; 1517 return tree.nodeToSpan(full.ast.align_expr); 1518 }, 1519 .node_offset_fn_type_addrspace => |node_off| { 1520 const tree = try src_loc.file_scope.getTree(gpa); 1521 const node = src_loc.relativeToNodeIndex(node_off); 1522 var buf: [1]Ast.Node.Index = undefined; 1523 const full = tree.fullFnProto(&buf, node).?; 1524 return tree.nodeToSpan(full.ast.addrspace_expr); 1525 }, 1526 .node_offset_fn_type_section => |node_off| { 1527 const tree = try src_loc.file_scope.getTree(gpa); 1528 const node = src_loc.relativeToNodeIndex(node_off); 1529 var buf: [1]Ast.Node.Index = undefined; 1530 const full = tree.fullFnProto(&buf, node).?; 1531 return tree.nodeToSpan(full.ast.section_expr); 1532 }, 1533 .node_offset_fn_type_cc => |node_off| { 1534 const tree = try src_loc.file_scope.getTree(gpa); 1535 const node = src_loc.relativeToNodeIndex(node_off); 1536 var buf: [1]Ast.Node.Index = undefined; 1537 const full = tree.fullFnProto(&buf, node).?; 1538 return tree.nodeToSpan(full.ast.callconv_expr); 1539 }, 1540 1541 .node_offset_fn_type_ret_ty => |node_off| { 1542 const tree = try src_loc.file_scope.getTree(gpa); 1543 const node = src_loc.relativeToNodeIndex(node_off); 1544 var buf: [1]Ast.Node.Index = undefined; 1545 const full = tree.fullFnProto(&buf, node).?; 1546 return tree.nodeToSpan(full.ast.return_type); 1547 }, 1548 .node_offset_param => |node_off| { 1549 const tree = try src_loc.file_scope.getTree(gpa); 1550 const token_tags = tree.tokens.items(.tag); 1551 const node = src_loc.relativeToNodeIndex(node_off); 1552 1553 var first_tok = tree.firstToken(node); 1554 while (true) switch (token_tags[first_tok - 1]) { 1555 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, 1556 else => break, 1557 }; 1558 return tree.tokensToSpan( 1559 first_tok, 1560 tree.lastToken(node), 1561 first_tok, 1562 ); 1563 }, 1564 .token_offset_param => |token_off| { 1565 const tree = try src_loc.file_scope.getTree(gpa); 1566 const token_tags = tree.tokens.items(.tag); 1567 const main_token = tree.nodes.items(.main_token)[src_loc.base_node]; 1568 const tok_index = @as(Ast.TokenIndex, @bitCast(token_off + @as(i32, @bitCast(main_token)))); 1569 1570 var first_tok = tok_index; 1571 while (true) switch (token_tags[first_tok - 1]) { 1572 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, 1573 else => break, 1574 }; 1575 return tree.tokensToSpan( 1576 first_tok, 1577 tok_index, 1578 first_tok, 1579 ); 1580 }, 1581 1582 .node_offset_anyframe_type => |node_off| { 1583 const tree = try src_loc.file_scope.getTree(gpa); 1584 const node_datas = tree.nodes.items(.data); 1585 const parent_node = src_loc.relativeToNodeIndex(node_off); 1586 return tree.nodeToSpan(node_datas[parent_node].rhs); 1587 }, 1588 1589 .node_offset_lib_name => |node_off| { 1590 const tree = try src_loc.file_scope.getTree(gpa); 1591 const parent_node = src_loc.relativeToNodeIndex(node_off); 1592 var buf: [1]Ast.Node.Index = undefined; 1593 const full = tree.fullFnProto(&buf, parent_node).?; 1594 const tok_index = full.lib_name.?; 1595 const start = tree.tokens.items(.start)[tok_index]; 1596 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1597 return Span{ .start = start, .end = end, .main = start }; 1598 }, 1599 1600 .node_offset_array_type_len => |node_off| { 1601 const tree = try src_loc.file_scope.getTree(gpa); 1602 const parent_node = src_loc.relativeToNodeIndex(node_off); 1603 1604 const full = tree.fullArrayType(parent_node).?; 1605 return tree.nodeToSpan(full.ast.elem_count); 1606 }, 1607 .node_offset_array_type_sentinel => |node_off| { 1608 const tree = try src_loc.file_scope.getTree(gpa); 1609 const parent_node = src_loc.relativeToNodeIndex(node_off); 1610 1611 const full = tree.fullArrayType(parent_node).?; 1612 return tree.nodeToSpan(full.ast.sentinel); 1613 }, 1614 .node_offset_array_type_elem => |node_off| { 1615 const tree = try src_loc.file_scope.getTree(gpa); 1616 const parent_node = src_loc.relativeToNodeIndex(node_off); 1617 1618 const full = tree.fullArrayType(parent_node).?; 1619 return tree.nodeToSpan(full.ast.elem_type); 1620 }, 1621 .node_offset_un_op => |node_off| { 1622 const tree = try src_loc.file_scope.getTree(gpa); 1623 const node_datas = tree.nodes.items(.data); 1624 const node = src_loc.relativeToNodeIndex(node_off); 1625 1626 return tree.nodeToSpan(node_datas[node].lhs); 1627 }, 1628 .node_offset_ptr_elem => |node_off| { 1629 const tree = try src_loc.file_scope.getTree(gpa); 1630 const parent_node = src_loc.relativeToNodeIndex(node_off); 1631 1632 const full = tree.fullPtrType(parent_node).?; 1633 return tree.nodeToSpan(full.ast.child_type); 1634 }, 1635 .node_offset_ptr_sentinel => |node_off| { 1636 const tree = try src_loc.file_scope.getTree(gpa); 1637 const parent_node = src_loc.relativeToNodeIndex(node_off); 1638 1639 const full = tree.fullPtrType(parent_node).?; 1640 return tree.nodeToSpan(full.ast.sentinel); 1641 }, 1642 .node_offset_ptr_align => |node_off| { 1643 const tree = try src_loc.file_scope.getTree(gpa); 1644 const parent_node = src_loc.relativeToNodeIndex(node_off); 1645 1646 const full = tree.fullPtrType(parent_node).?; 1647 return tree.nodeToSpan(full.ast.align_node); 1648 }, 1649 .node_offset_ptr_addrspace => |node_off| { 1650 const tree = try src_loc.file_scope.getTree(gpa); 1651 const parent_node = src_loc.relativeToNodeIndex(node_off); 1652 1653 const full = tree.fullPtrType(parent_node).?; 1654 return tree.nodeToSpan(full.ast.addrspace_node); 1655 }, 1656 .node_offset_ptr_bitoffset => |node_off| { 1657 const tree = try src_loc.file_scope.getTree(gpa); 1658 const parent_node = src_loc.relativeToNodeIndex(node_off); 1659 1660 const full = tree.fullPtrType(parent_node).?; 1661 return tree.nodeToSpan(full.ast.bit_range_start); 1662 }, 1663 .node_offset_ptr_hostsize => |node_off| { 1664 const tree = try src_loc.file_scope.getTree(gpa); 1665 const parent_node = src_loc.relativeToNodeIndex(node_off); 1666 1667 const full = tree.fullPtrType(parent_node).?; 1668 return tree.nodeToSpan(full.ast.bit_range_end); 1669 }, 1670 .node_offset_container_tag => |node_off| { 1671 const tree = try src_loc.file_scope.getTree(gpa); 1672 const node_tags = tree.nodes.items(.tag); 1673 const parent_node = src_loc.relativeToNodeIndex(node_off); 1674 1675 switch (node_tags[parent_node]) { 1676 .container_decl_arg, .container_decl_arg_trailing => { 1677 const full = tree.containerDeclArg(parent_node); 1678 return tree.nodeToSpan(full.ast.arg); 1679 }, 1680 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => { 1681 const full = tree.taggedUnionEnumTag(parent_node); 1682 1683 return tree.tokensToSpan( 1684 tree.firstToken(full.ast.arg) - 2, 1685 tree.lastToken(full.ast.arg) + 1, 1686 tree.nodes.items(.main_token)[full.ast.arg], 1687 ); 1688 }, 1689 else => unreachable, 1690 } 1691 }, 1692 .node_offset_field_default => |node_off| { 1693 const tree = try src_loc.file_scope.getTree(gpa); 1694 const node_tags = tree.nodes.items(.tag); 1695 const parent_node = src_loc.relativeToNodeIndex(node_off); 1696 1697 const full: Ast.full.ContainerField = switch (node_tags[parent_node]) { 1698 .container_field => tree.containerField(parent_node), 1699 .container_field_init => tree.containerFieldInit(parent_node), 1700 else => unreachable, 1701 }; 1702 return tree.nodeToSpan(full.ast.value_expr); 1703 }, 1704 .node_offset_init_ty => |node_off| { 1705 const tree = try src_loc.file_scope.getTree(gpa); 1706 const parent_node = src_loc.relativeToNodeIndex(node_off); 1707 1708 var buf: [2]Ast.Node.Index = undefined; 1709 const type_expr = if (tree.fullArrayInit(&buf, parent_node)) |array_init| 1710 array_init.ast.type_expr 1711 else 1712 tree.fullStructInit(&buf, parent_node).?.ast.type_expr; 1713 return tree.nodeToSpan(type_expr); 1714 }, 1715 .node_offset_store_ptr => |node_off| { 1716 const tree = try src_loc.file_scope.getTree(gpa); 1717 const node_tags = tree.nodes.items(.tag); 1718 const node_datas = tree.nodes.items(.data); 1719 const node = src_loc.relativeToNodeIndex(node_off); 1720 1721 switch (node_tags[node]) { 1722 .assign => { 1723 return tree.nodeToSpan(node_datas[node].lhs); 1724 }, 1725 else => return tree.nodeToSpan(node), 1726 } 1727 }, 1728 .node_offset_store_operand => |node_off| { 1729 const tree = try src_loc.file_scope.getTree(gpa); 1730 const node_tags = tree.nodes.items(.tag); 1731 const node_datas = tree.nodes.items(.data); 1732 const node = src_loc.relativeToNodeIndex(node_off); 1733 1734 switch (node_tags[node]) { 1735 .assign => { 1736 return tree.nodeToSpan(node_datas[node].rhs); 1737 }, 1738 else => return tree.nodeToSpan(node), 1739 } 1740 }, 1741 .node_offset_return_operand => |node_off| { 1742 const tree = try src_loc.file_scope.getTree(gpa); 1743 const node = src_loc.relativeToNodeIndex(node_off); 1744 const node_tags = tree.nodes.items(.tag); 1745 const node_datas = tree.nodes.items(.data); 1746 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) { 1747 return tree.nodeToSpan(node_datas[node].lhs); 1748 } 1749 return tree.nodeToSpan(node); 1750 }, 1751 .container_field_name, 1752 .container_field_value, 1753 .container_field_type, 1754 .container_field_align, 1755 => |field_idx| { 1756 const tree = try src_loc.file_scope.getTree(gpa); 1757 const node = src_loc.relativeToNodeIndex(0); 1758 var buf: [2]Ast.Node.Index = undefined; 1759 const container_decl = tree.fullContainerDecl(&buf, node) orelse 1760 return tree.nodeToSpan(node); 1761 1762 var cur_field_idx: usize = 0; 1763 for (container_decl.ast.members) |member_node| { 1764 const field = tree.fullContainerField(member_node) orelse continue; 1765 if (cur_field_idx < field_idx) { 1766 cur_field_idx += 1; 1767 continue; 1768 } 1769 const field_component_node = switch (src_loc.lazy) { 1770 .container_field_name => 0, 1771 .container_field_value => field.ast.value_expr, 1772 .container_field_type => field.ast.type_expr, 1773 .container_field_align => field.ast.align_expr, 1774 else => unreachable, 1775 }; 1776 if (field_component_node == 0) { 1777 return tree.tokenToSpan(field.ast.main_token); 1778 } else { 1779 return tree.nodeToSpan(field_component_node); 1780 } 1781 } else unreachable; 1782 }, 1783 .init_elem => |init_elem| { 1784 const tree = try src_loc.file_scope.getTree(gpa); 1785 const init_node = src_loc.relativeToNodeIndex(init_elem.init_node_offset); 1786 var buf: [2]Ast.Node.Index = undefined; 1787 if (tree.fullArrayInit(&buf, init_node)) |full| { 1788 const elem_node = full.ast.elements[init_elem.elem_index]; 1789 return tree.nodeToSpan(elem_node); 1790 } else if (tree.fullStructInit(&buf, init_node)) |full| { 1791 const field_node = full.ast.fields[init_elem.elem_index]; 1792 return tree.tokensToSpan( 1793 tree.firstToken(field_node) - 3, 1794 tree.lastToken(field_node), 1795 tree.nodes.items(.main_token)[field_node] - 2, 1796 ); 1797 } else unreachable; 1798 }, 1799 .init_field_name, 1800 .init_field_linkage, 1801 .init_field_section, 1802 .init_field_visibility, 1803 .init_field_rw, 1804 .init_field_locality, 1805 .init_field_cache, 1806 .init_field_library, 1807 .init_field_thread_local, 1808 => |builtin_call_node| { 1809 const wanted = switch (src_loc.lazy) { 1810 .init_field_name => "name", 1811 .init_field_linkage => "linkage", 1812 .init_field_section => "section", 1813 .init_field_visibility => "visibility", 1814 .init_field_rw => "rw", 1815 .init_field_locality => "locality", 1816 .init_field_cache => "cache", 1817 .init_field_library => "library", 1818 .init_field_thread_local => "thread_local", 1819 else => unreachable, 1820 }; 1821 const tree = try src_loc.file_scope.getTree(gpa); 1822 const node_datas = tree.nodes.items(.data); 1823 const node_tags = tree.nodes.items(.tag); 1824 const node = src_loc.relativeToNodeIndex(builtin_call_node); 1825 const arg_node = switch (node_tags[node]) { 1826 .builtin_call_two, .builtin_call_two_comma => node_datas[node].rhs, 1827 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + 1], 1828 else => unreachable, 1829 }; 1830 var buf: [2]Ast.Node.Index = undefined; 1831 const full = tree.fullStructInit(&buf, arg_node) orelse 1832 return tree.nodeToSpan(arg_node); 1833 for (full.ast.fields) |field_node| { 1834 // . IDENTIFIER = field_node 1835 const name_token = tree.firstToken(field_node) - 2; 1836 const name = tree.tokenSlice(name_token); 1837 if (std.mem.eql(u8, name, wanted)) { 1838 return tree.tokensToSpan( 1839 name_token - 1, 1840 tree.lastToken(field_node), 1841 tree.nodes.items(.main_token)[field_node] - 2, 1842 ); 1843 } 1844 } 1845 return tree.nodeToSpan(arg_node); 1846 }, 1847 .switch_case_item, 1848 .switch_case_item_range_first, 1849 .switch_case_item_range_last, 1850 .switch_capture, 1851 .switch_tag_capture, 1852 => { 1853 const switch_node_offset, const want_case_idx = switch (src_loc.lazy) { 1854 .switch_case_item, 1855 .switch_case_item_range_first, 1856 .switch_case_item_range_last, 1857 => |x| .{ x.switch_node_offset, x.case_idx }, 1858 .switch_capture, 1859 .switch_tag_capture, 1860 => |x| .{ x.switch_node_offset, x.case_idx }, 1861 else => unreachable, 1862 }; 1863 1864 const tree = try src_loc.file_scope.getTree(gpa); 1865 const node_datas = tree.nodes.items(.data); 1866 const node_tags = tree.nodes.items(.tag); 1867 const main_tokens = tree.nodes.items(.main_token); 1868 const switch_node = src_loc.relativeToNodeIndex(switch_node_offset); 1869 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 1870 const case_nodes = tree.extra_data[extra.start..extra.end]; 1871 1872 var multi_i: u32 = 0; 1873 var scalar_i: u32 = 0; 1874 const case = for (case_nodes) |case_node| { 1875 const case = tree.fullSwitchCase(case_node).?; 1876 const is_special = special: { 1877 if (case.ast.values.len == 0) break :special true; 1878 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .identifier) { 1879 break :special mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"); 1880 } 1881 break :special false; 1882 }; 1883 if (is_special) { 1884 if (want_case_idx.isSpecial()) { 1885 break case; 1886 } 1887 } 1888 1889 const is_multi = case.ast.values.len != 1 or 1890 node_tags[case.ast.values[0]] == .switch_range; 1891 1892 if (!want_case_idx.isSpecial()) switch (want_case_idx.kind) { 1893 .scalar => if (!is_multi and want_case_idx.index == scalar_i) break case, 1894 .multi => if (is_multi and want_case_idx.index == multi_i) break case, 1895 }; 1896 1897 if (is_multi) { 1898 multi_i += 1; 1899 } else { 1900 scalar_i += 1; 1901 } 1902 } else unreachable; 1903 1904 const want_item = switch (src_loc.lazy) { 1905 .switch_case_item, 1906 .switch_case_item_range_first, 1907 .switch_case_item_range_last, 1908 => |x| x.item_idx, 1909 .switch_capture, .switch_tag_capture => { 1910 const token_tags = tree.tokens.items(.tag); 1911 const start = switch (src_loc.lazy) { 1912 .switch_capture => case.payload_token.?, 1913 .switch_tag_capture => tok: { 1914 var tok = case.payload_token.?; 1915 if (token_tags[tok] == .asterisk) tok += 1; 1916 tok += 2; // skip over comma 1917 break :tok tok; 1918 }, 1919 else => unreachable, 1920 }; 1921 const end = switch (token_tags[start]) { 1922 .asterisk => start + 1, 1923 else => start, 1924 }; 1925 return tree.tokensToSpan(start, end, start); 1926 }, 1927 else => unreachable, 1928 }; 1929 1930 switch (want_item.kind) { 1931 .single => { 1932 var item_i: u32 = 0; 1933 for (case.ast.values) |item_node| { 1934 if (node_tags[item_node] == .switch_range) continue; 1935 if (item_i != want_item.index) { 1936 item_i += 1; 1937 continue; 1938 } 1939 return tree.nodeToSpan(item_node); 1940 } else unreachable; 1941 }, 1942 .range => { 1943 var range_i: u32 = 0; 1944 for (case.ast.values) |item_node| { 1945 if (node_tags[item_node] != .switch_range) continue; 1946 if (range_i != want_item.index) { 1947 range_i += 1; 1948 continue; 1949 } 1950 return switch (src_loc.lazy) { 1951 .switch_case_item => tree.nodeToSpan(item_node), 1952 .switch_case_item_range_first => tree.nodeToSpan(node_datas[item_node].lhs), 1953 .switch_case_item_range_last => tree.nodeToSpan(node_datas[item_node].rhs), 1954 else => unreachable, 1955 }; 1956 } else unreachable; 1957 }, 1958 } 1959 }, 1960 } 1961 } 1962 }; 1963 1964 pub const LazySrcLoc = struct { 1965 /// This instruction provides the source node locations are resolved relative to. 1966 /// It is a `declaration`, `struct_decl`, `union_decl`, `enum_decl`, or `opaque_decl`. 1967 /// This must be valid even if `relative` is an absolute value, since it is required to 1968 /// determine the file which the `LazySrcLoc` refers to. 1969 base_node_inst: InternPool.TrackedInst.Index, 1970 /// This field determines the source location relative to `base_node_inst`. 1971 offset: Offset, 1972 1973 pub const Offset = union(enum) { 1974 /// When this tag is set, the code that constructed this `LazySrcLoc` is asserting 1975 /// that all code paths which would need to resolve the source location are 1976 /// unreachable. If you are debugging this tag incorrectly being this value, 1977 /// look into using reverse-continue with a memory watchpoint to see where the 1978 /// value is being set to this tag. 1979 /// `base_node_inst` is unused. 1980 unneeded, 1981 /// Means the source location points to an entire file; not any particular 1982 /// location within the file. `file_scope` union field will be active. 1983 entire_file, 1984 /// The source location points to a byte offset within a source file, 1985 /// offset from 0. The source file is determined contextually. 1986 byte_abs: u32, 1987 /// The source location points to a token within a source file, 1988 /// offset from 0. The source file is determined contextually. 1989 token_abs: u32, 1990 /// The source location points to an AST node within a source file, 1991 /// offset from 0. The source file is determined contextually. 1992 node_abs: u32, 1993 /// The source location points to a byte offset within a source file, 1994 /// offset from the byte offset of the base node within the file. 1995 byte_offset: u32, 1996 /// This data is the offset into the token list from the base node's first token. 1997 token_offset: u32, 1998 /// The source location points to an AST node, which is this value offset 1999 /// from its containing base node AST index. 2000 node_offset: TracedOffset, 2001 /// The source location points to the main token of an AST node, found 2002 /// by taking this AST node index offset from the containing base node. 2003 node_offset_main_token: i32, 2004 /// The source location points to the beginning of a struct initializer. 2005 node_offset_initializer: i32, 2006 /// The source location points to a variable declaration type expression, 2007 /// found by taking this AST node index offset from the containing 2008 /// base node, which points to a variable declaration AST node. Next, navigate 2009 /// to the type expression. 2010 node_offset_var_decl_ty: i32, 2011 /// The source location points to the alignment expression of a var decl. 2012 node_offset_var_decl_align: i32, 2013 /// The source location points to the linksection expression of a var decl. 2014 node_offset_var_decl_section: i32, 2015 /// The source location points to the addrspace expression of a var decl. 2016 node_offset_var_decl_addrspace: i32, 2017 /// The source location points to the initializer of a var decl. 2018 node_offset_var_decl_init: i32, 2019 /// The source location points to the given argument of a builtin function call. 2020 /// `builtin_call_node` points to the builtin call. 2021 /// `arg_index` is the index of the argument which hte source location refers to. 2022 node_offset_builtin_call_arg: struct { 2023 builtin_call_node: i32, 2024 arg_index: u32, 2025 }, 2026 /// Like `node_offset_builtin_call_arg` but recurses through arbitrarily many calls 2027 /// to pointer cast builtins (taking the first argument of the most nested). 2028 node_offset_ptrcast_operand: i32, 2029 /// The source location points to the index expression of an array access 2030 /// expression, found by taking this AST node index offset from the containing 2031 /// base node, which points to an array access AST node. Next, navigate 2032 /// to the index expression. 2033 node_offset_array_access_index: i32, 2034 /// The source location points to the LHS of a slice expression 2035 /// expression, found by taking this AST node index offset from the containing 2036 /// base node, which points to a slice AST node. Next, navigate 2037 /// to the sentinel expression. 2038 node_offset_slice_ptr: i32, 2039 /// The source location points to start expression of a slice expression 2040 /// expression, found by taking this AST node index offset from the containing 2041 /// base node, which points to a slice AST node. Next, navigate 2042 /// to the sentinel expression. 2043 node_offset_slice_start: i32, 2044 /// The source location points to the end expression of a slice 2045 /// expression, found by taking this AST node index offset from the containing 2046 /// base node, which points to a slice AST node. Next, navigate 2047 /// to the sentinel expression. 2048 node_offset_slice_end: i32, 2049 /// The source location points to the sentinel expression of a slice 2050 /// expression, found by taking this AST node index offset from the containing 2051 /// base node, which points to a slice AST node. Next, navigate 2052 /// to the sentinel expression. 2053 node_offset_slice_sentinel: i32, 2054 /// The source location points to the callee expression of a function 2055 /// call expression, found by taking this AST node index offset from the containing 2056 /// base node, which points to a function call AST node. Next, navigate 2057 /// to the callee expression. 2058 node_offset_call_func: i32, 2059 /// The payload is offset from the containing base node. 2060 /// The source location points to the field name of: 2061 /// * a field access expression (`a.b`), or 2062 /// * the callee of a method call (`a.b()`) 2063 node_offset_field_name: i32, 2064 /// The payload is offset from the containing base node. 2065 /// The source location points to the field name of the operand ("b" node) 2066 /// of a field initialization expression (`.a = b`) 2067 node_offset_field_name_init: i32, 2068 /// The source location points to the pointer of a pointer deref expression, 2069 /// found by taking this AST node index offset from the containing 2070 /// base node, which points to a pointer deref AST node. Next, navigate 2071 /// to the pointer expression. 2072 node_offset_deref_ptr: i32, 2073 /// The source location points to the assembly source code of an inline assembly 2074 /// expression, found by taking this AST node index offset from the containing 2075 /// base node, which points to inline assembly AST node. Next, navigate 2076 /// to the asm template source code. 2077 node_offset_asm_source: i32, 2078 /// The source location points to the return type of an inline assembly 2079 /// expression, found by taking this AST node index offset from the containing 2080 /// base node, which points to inline assembly AST node. Next, navigate 2081 /// to the return type expression. 2082 node_offset_asm_ret_ty: i32, 2083 /// The source location points to the condition expression of an if 2084 /// expression, found by taking this AST node index offset from the containing 2085 /// base node, which points to an if expression AST node. Next, navigate 2086 /// to the condition expression. 2087 node_offset_if_cond: i32, 2088 /// The source location points to a binary expression, such as `a + b`, found 2089 /// by taking this AST node index offset from the containing base node. 2090 node_offset_bin_op: i32, 2091 /// The source location points to the LHS of a binary expression, found 2092 /// by taking this AST node index offset from the containing base node, 2093 /// which points to a binary expression AST node. Next, navigate to the LHS. 2094 node_offset_bin_lhs: i32, 2095 /// The source location points to the RHS of a binary expression, found 2096 /// by taking this AST node index offset from the containing base node, 2097 /// which points to a binary expression AST node. Next, navigate to the RHS. 2098 node_offset_bin_rhs: i32, 2099 /// The source location points to the operand of a switch expression, found 2100 /// by taking this AST node index offset from the containing base node, 2101 /// which points to a switch expression AST node. Next, navigate to the operand. 2102 node_offset_switch_operand: i32, 2103 /// The source location points to the else/`_` prong of a switch expression, found 2104 /// by taking this AST node index offset from the containing base node, 2105 /// which points to a switch expression AST node. Next, navigate to the else/`_` prong. 2106 node_offset_switch_special_prong: i32, 2107 /// The source location points to all the ranges of a switch expression, found 2108 /// by taking this AST node index offset from the containing base node, 2109 /// which points to a switch expression AST node. Next, navigate to any of the 2110 /// range nodes. The error applies to all of them. 2111 node_offset_switch_range: i32, 2112 /// The source location points to the align expr of a function type 2113 /// expression, found by taking this AST node index offset from the containing 2114 /// base node, which points to a function type AST node. Next, navigate to 2115 /// the calling convention node. 2116 node_offset_fn_type_align: i32, 2117 /// The source location points to the addrspace expr of a function type 2118 /// expression, found by taking this AST node index offset from the containing 2119 /// base node, which points to a function type AST node. Next, navigate to 2120 /// the calling convention node. 2121 node_offset_fn_type_addrspace: i32, 2122 /// The source location points to the linksection expr of a function type 2123 /// expression, found by taking this AST node index offset from the containing 2124 /// base node, which points to a function type AST node. Next, navigate to 2125 /// the calling convention node. 2126 node_offset_fn_type_section: i32, 2127 /// The source location points to the calling convention of a function type 2128 /// expression, found by taking this AST node index offset from the containing 2129 /// base node, which points to a function type AST node. Next, navigate to 2130 /// the calling convention node. 2131 node_offset_fn_type_cc: i32, 2132 /// The source location points to the return type of a function type 2133 /// expression, found by taking this AST node index offset from the containing 2134 /// base node, which points to a function type AST node. Next, navigate to 2135 /// the return type node. 2136 node_offset_fn_type_ret_ty: i32, 2137 node_offset_param: i32, 2138 token_offset_param: i32, 2139 /// The source location points to the type expression of an `anyframe->T` 2140 /// expression, found by taking this AST node index offset from the containing 2141 /// base node, which points to a `anyframe->T` expression AST node. Next, navigate 2142 /// to the type expression. 2143 node_offset_anyframe_type: i32, 2144 /// The source location points to the string literal of `extern "foo"`, found 2145 /// by taking this AST node index offset from the containing 2146 /// base node, which points to a function prototype or variable declaration 2147 /// expression AST node. Next, navigate to the string literal of the `extern "foo"`. 2148 node_offset_lib_name: i32, 2149 /// The source location points to the len expression of an `[N:S]T` 2150 /// expression, found by taking this AST node index offset from the containing 2151 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate 2152 /// to the len expression. 2153 node_offset_array_type_len: i32, 2154 /// The source location points to the sentinel expression of an `[N:S]T` 2155 /// expression, found by taking this AST node index offset from the containing 2156 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate 2157 /// to the sentinel expression. 2158 node_offset_array_type_sentinel: i32, 2159 /// The source location points to the elem expression of an `[N:S]T` 2160 /// expression, found by taking this AST node index offset from the containing 2161 /// base node, which points to an `[N:S]T` expression AST node. Next, navigate 2162 /// to the elem expression. 2163 node_offset_array_type_elem: i32, 2164 /// The source location points to the operand of an unary expression. 2165 node_offset_un_op: i32, 2166 /// The source location points to the elem type of a pointer. 2167 node_offset_ptr_elem: i32, 2168 /// The source location points to the sentinel of a pointer. 2169 node_offset_ptr_sentinel: i32, 2170 /// The source location points to the align expr of a pointer. 2171 node_offset_ptr_align: i32, 2172 /// The source location points to the addrspace expr of a pointer. 2173 node_offset_ptr_addrspace: i32, 2174 /// The source location points to the bit-offset of a pointer. 2175 node_offset_ptr_bitoffset: i32, 2176 /// The source location points to the host size of a pointer. 2177 node_offset_ptr_hostsize: i32, 2178 /// The source location points to the tag type of an union or an enum. 2179 node_offset_container_tag: i32, 2180 /// The source location points to the default value of a field. 2181 node_offset_field_default: i32, 2182 /// The source location points to the type of an array or struct initializer. 2183 node_offset_init_ty: i32, 2184 /// The source location points to the LHS of an assignment. 2185 node_offset_store_ptr: i32, 2186 /// The source location points to the RHS of an assignment. 2187 node_offset_store_operand: i32, 2188 /// The source location points to the operand of a `return` statement, or 2189 /// the `return` itself if there is no explicit operand. 2190 node_offset_return_operand: i32, 2191 /// The source location points to a for loop input. 2192 for_input: struct { 2193 /// Points to the for loop AST node. 2194 for_node_offset: i32, 2195 /// Picks one of the inputs from the condition. 2196 input_index: u32, 2197 }, 2198 /// The source location points to one of the captures of a for loop, found 2199 /// by taking this AST node index offset from the containing 2200 /// base node, which points to one of the input nodes of a for loop. 2201 /// Next, navigate to the corresponding capture. 2202 for_capture_from_input: i32, 2203 /// The source location points to the argument node of a function call. 2204 call_arg: struct { 2205 /// Points to the function call AST node. 2206 call_node_offset: i32, 2207 /// The index of the argument the source location points to. 2208 arg_index: u32, 2209 }, 2210 fn_proto_param: FnProtoParam, 2211 fn_proto_param_type: FnProtoParam, 2212 array_cat_lhs: ArrayCat, 2213 array_cat_rhs: ArrayCat, 2214 /// The source location points to the name of the field at the given index 2215 /// of the container type declaration at the base node. 2216 container_field_name: u32, 2217 /// Like `continer_field_name`, but points at the field's default value. 2218 container_field_value: u32, 2219 /// Like `continer_field_name`, but points at the field's type. 2220 container_field_type: u32, 2221 /// Like `continer_field_name`, but points at the field's alignment. 2222 container_field_align: u32, 2223 /// The source location points to the given element/field of a struct or 2224 /// array initialization expression. 2225 init_elem: struct { 2226 /// Points to the AST node of the initialization expression. 2227 init_node_offset: i32, 2228 /// The index of the field/element the source location points to. 2229 elem_index: u32, 2230 }, 2231 // The following source locations are like `init_elem`, but refer to a 2232 // field with a specific name. If such a field is not given, the entire 2233 // initialization expression is used instead. 2234 // The `i32` points to the AST node of a builtin call, whose *second* 2235 // argument is the init expression. 2236 init_field_name: i32, 2237 init_field_linkage: i32, 2238 init_field_section: i32, 2239 init_field_visibility: i32, 2240 init_field_rw: i32, 2241 init_field_locality: i32, 2242 init_field_cache: i32, 2243 init_field_library: i32, 2244 init_field_thread_local: i32, 2245 /// The source location points to the value of an item in a specific 2246 /// case of a `switch`. 2247 switch_case_item: SwitchItem, 2248 /// The source location points to the "first" value of a range item in 2249 /// a specific case of a `switch`. 2250 switch_case_item_range_first: SwitchItem, 2251 /// The source location points to the "last" value of a range item in 2252 /// a specific case of a `switch`. 2253 switch_case_item_range_last: SwitchItem, 2254 /// The source location points to the main capture of a specific case of 2255 /// a `switch`. 2256 switch_capture: SwitchCapture, 2257 /// The source location points to the "tag" capture (second capture) of 2258 /// a specific case of a `switch`. 2259 switch_tag_capture: SwitchCapture, 2260 2261 pub const FnProtoParam = struct { 2262 /// The offset of the function prototype AST node. 2263 fn_proto_node_offset: i32, 2264 /// The index of the parameter the source location points to. 2265 param_index: u32, 2266 }; 2267 2268 pub const SwitchItem = struct { 2269 /// The offset of the switch AST node. 2270 switch_node_offset: i32, 2271 /// The index of the case to point to within this switch. 2272 case_idx: SwitchCaseIndex, 2273 /// The index of the item to point to within this case. 2274 item_idx: SwitchItemIndex, 2275 }; 2276 2277 pub const SwitchCapture = struct { 2278 /// The offset of the switch AST node. 2279 switch_node_offset: i32, 2280 /// The index of the case whose capture to point to. 2281 case_idx: SwitchCaseIndex, 2282 }; 2283 2284 pub const SwitchCaseIndex = packed struct(u32) { 2285 kind: enum(u1) { scalar, multi }, 2286 index: u31, 2287 2288 pub const special: SwitchCaseIndex = @bitCast(@as(u32, std.math.maxInt(u32))); 2289 pub fn isSpecial(idx: SwitchCaseIndex) bool { 2290 return @as(u32, @bitCast(idx)) == @as(u32, @bitCast(special)); 2291 } 2292 }; 2293 2294 pub const SwitchItemIndex = packed struct(u32) { 2295 kind: enum(u1) { single, range }, 2296 index: u31, 2297 }; 2298 2299 const ArrayCat = struct { 2300 /// Points to the array concat AST node. 2301 array_cat_offset: i32, 2302 /// The index of the element the source location points to. 2303 elem_index: u32, 2304 }; 2305 2306 pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; 2307 2308 noinline fn nodeOffsetDebug(node_offset: i32) Offset { 2309 var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } }; 2310 result.node_offset.trace.addAddr(@returnAddress(), "init"); 2311 return result; 2312 } 2313 2314 fn nodeOffsetRelease(node_offset: i32) Offset { 2315 return .{ .node_offset = .{ .x = node_offset } }; 2316 } 2317 2318 /// This wraps a simple integer in debug builds so that later on we can find out 2319 /// where in semantic analysis the value got set. 2320 pub const TracedOffset = struct { 2321 x: i32, 2322 trace: std.debug.Trace = std.debug.Trace.init, 2323 2324 const want_tracing = false; 2325 }; 2326 }; 2327 2328 pub const unneeded: LazySrcLoc = .{ 2329 .base_node_inst = undefined, 2330 .offset = .unneeded, 2331 }; 2332 2333 pub fn resolveBaseNode(base_node_inst: InternPool.TrackedInst.Index, zcu: *Zcu) struct { *File, Ast.Node.Index } { 2334 const ip = &zcu.intern_pool; 2335 const file_index, const zir_inst = inst: { 2336 const info = base_node_inst.resolveFull(ip); 2337 break :inst .{ info.file, info.inst }; 2338 }; 2339 const file = zcu.fileByIndex(file_index); 2340 assert(file.zir_loaded); 2341 2342 const zir = file.zir; 2343 const inst = zir.instructions.get(@intFromEnum(zir_inst)); 2344 const base_node: Ast.Node.Index = switch (inst.tag) { 2345 .declaration => inst.data.declaration.src_node, 2346 .extended => switch (inst.data.extended.opcode) { 2347 .struct_decl => zir.extraData(Zir.Inst.StructDecl, inst.data.extended.operand).data.src_node, 2348 .union_decl => zir.extraData(Zir.Inst.UnionDecl, inst.data.extended.operand).data.src_node, 2349 .enum_decl => zir.extraData(Zir.Inst.EnumDecl, inst.data.extended.operand).data.src_node, 2350 .opaque_decl => zir.extraData(Zir.Inst.OpaqueDecl, inst.data.extended.operand).data.src_node, 2351 .reify => zir.extraData(Zir.Inst.Reify, inst.data.extended.operand).data.node, 2352 else => unreachable, 2353 }, 2354 else => unreachable, 2355 }; 2356 return .{ file, base_node }; 2357 } 2358 2359 /// Resolve the file and AST node of `base_node_inst` to get a resolved `SrcLoc`. 2360 /// The resulting `SrcLoc` should only be used ephemerally, as it is not correct across incremental updates. 2361 pub fn upgrade(lazy: LazySrcLoc, zcu: *Zcu) SrcLoc { 2362 const file, const base_node = resolveBaseNode(lazy.base_node_inst, zcu); 2363 return .{ 2364 .file_scope = file, 2365 .base_node = base_node, 2366 .lazy = lazy.offset, 2367 }; 2368 } 2369 }; 2370 2371 pub const SemaError = error{ OutOfMemory, AnalysisFail }; 2372 pub const CompileError = error{ 2373 OutOfMemory, 2374 /// When this is returned, the compile error for the failure has already been recorded. 2375 AnalysisFail, 2376 /// A Type or Value was needed to be used during semantic analysis, but it was not available 2377 /// because the function is generic. This is only seen when analyzing the body of a param 2378 /// instruction. 2379 GenericPoison, 2380 /// In a comptime scope, a return instruction was encountered. This error is only seen when 2381 /// doing a comptime function call. 2382 ComptimeReturn, 2383 /// In a comptime scope, a break instruction was encountered. This error is only seen when 2384 /// evaluating a comptime block. 2385 ComptimeBreak, 2386 }; 2387 2388 pub fn init(mod: *Module, thread_count: usize) !void { 2389 const gpa = mod.gpa; 2390 try mod.intern_pool.init(gpa, thread_count); 2391 try mod.global_error_set.put(gpa, .empty, {}); 2392 } 2393 2394 pub fn deinit(zcu: *Zcu) void { 2395 const pt: Zcu.PerThread = .{ .tid = .main, .zcu = zcu }; 2396 const gpa = zcu.gpa; 2397 2398 if (zcu.llvm_object) |llvm_object| { 2399 if (build_options.only_c) unreachable; 2400 llvm_object.deinit(); 2401 } 2402 2403 for (zcu.import_table.keys()) |key| { 2404 gpa.free(key); 2405 } 2406 for (0..zcu.import_table.entries.len) |file_index_usize| { 2407 const file_index: File.Index = @enumFromInt(file_index_usize); 2408 pt.destroyFile(file_index); 2409 } 2410 zcu.import_table.deinit(gpa); 2411 2412 for (zcu.embed_table.keys(), zcu.embed_table.values()) |path, embed_file| { 2413 gpa.free(path); 2414 gpa.destroy(embed_file); 2415 } 2416 zcu.embed_table.deinit(gpa); 2417 2418 zcu.compile_log_text.deinit(gpa); 2419 2420 zcu.local_zir_cache.handle.close(); 2421 zcu.global_zir_cache.handle.close(); 2422 2423 for (zcu.failed_analysis.values()) |value| { 2424 value.destroy(gpa); 2425 } 2426 zcu.failed_analysis.deinit(gpa); 2427 2428 if (zcu.emit_h) |emit_h| { 2429 for (emit_h.failed_decls.values()) |value| { 2430 value.destroy(gpa); 2431 } 2432 emit_h.failed_decls.deinit(gpa); 2433 emit_h.decl_table.deinit(gpa); 2434 emit_h.allocated_emit_h.deinit(gpa); 2435 } 2436 2437 for (zcu.failed_files.values()) |value| { 2438 if (value) |msg| msg.destroy(gpa); 2439 } 2440 zcu.failed_files.deinit(gpa); 2441 2442 for (zcu.failed_embed_files.values()) |msg| { 2443 msg.destroy(gpa); 2444 } 2445 zcu.failed_embed_files.deinit(gpa); 2446 2447 for (zcu.failed_exports.values()) |value| { 2448 value.destroy(gpa); 2449 } 2450 zcu.failed_exports.deinit(gpa); 2451 2452 for (zcu.cimport_errors.values()) |*errs| { 2453 errs.deinit(gpa); 2454 } 2455 zcu.cimport_errors.deinit(gpa); 2456 2457 zcu.compile_log_sources.deinit(gpa); 2458 2459 zcu.all_exports.deinit(gpa); 2460 zcu.free_exports.deinit(gpa); 2461 zcu.single_exports.deinit(gpa); 2462 zcu.multi_exports.deinit(gpa); 2463 2464 zcu.global_error_set.deinit(gpa); 2465 2466 zcu.potentially_outdated.deinit(gpa); 2467 zcu.outdated.deinit(gpa); 2468 zcu.outdated_ready.deinit(gpa); 2469 zcu.outdated_file_root.deinit(gpa); 2470 zcu.retryable_failures.deinit(gpa); 2471 2472 zcu.test_functions.deinit(gpa); 2473 2474 for (zcu.global_assembly.values()) |s| { 2475 gpa.free(s); 2476 } 2477 zcu.global_assembly.deinit(gpa); 2478 2479 zcu.reference_table.deinit(gpa); 2480 zcu.all_references.deinit(gpa); 2481 zcu.free_references.deinit(gpa); 2482 2483 zcu.intern_pool.deinit(gpa); 2484 } 2485 2486 pub fn declPtr(mod: *Module, index: Decl.Index) *Decl { 2487 return mod.intern_pool.declPtr(index); 2488 } 2489 2490 pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace { 2491 return mod.intern_pool.namespacePtr(index); 2492 } 2493 2494 pub fn namespacePtrUnwrap(mod: *Module, index: Namespace.OptionalIndex) ?*Namespace { 2495 return mod.namespacePtr(index.unwrap() orelse return null); 2496 } 2497 2498 /// Returns true if and only if the Decl is the top level struct associated with a File. 2499 pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool { 2500 const decl = mod.declPtr(decl_index); 2501 const namespace = mod.namespacePtr(decl.src_namespace); 2502 if (namespace.parent != .none) return false; 2503 return decl_index == namespace.decl_index; 2504 } 2505 2506 // TODO https://github.com/ziglang/zig/issues/8643 2507 pub const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8; 2508 pub const HackDataLayout = extern struct { 2509 data: [8]u8 align(@alignOf(Zir.Inst.Data)), 2510 safety_tag: u8, 2511 }; 2512 comptime { 2513 if (data_has_safety_tag) { 2514 assert(@sizeOf(HackDataLayout) == @sizeOf(Zir.Inst.Data)); 2515 } 2516 } 2517 2518 pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir { 2519 return loadZirCacheBody(gpa, try cache_file.reader().readStruct(Zir.Header), cache_file); 2520 } 2521 2522 pub fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File) !Zir { 2523 var instructions: std.MultiArrayList(Zir.Inst) = .{}; 2524 errdefer instructions.deinit(gpa); 2525 2526 try instructions.setCapacity(gpa, header.instructions_len); 2527 instructions.len = header.instructions_len; 2528 2529 var zir: Zir = .{ 2530 .instructions = instructions.toOwnedSlice(), 2531 .string_bytes = &.{}, 2532 .extra = &.{}, 2533 }; 2534 errdefer zir.deinit(gpa); 2535 2536 zir.string_bytes = try gpa.alloc(u8, header.string_bytes_len); 2537 zir.extra = try gpa.alloc(u32, header.extra_len); 2538 2539 const safety_buffer = if (data_has_safety_tag) 2540 try gpa.alloc([8]u8, header.instructions_len) 2541 else 2542 undefined; 2543 defer if (data_has_safety_tag) gpa.free(safety_buffer); 2544 2545 const data_ptr = if (data_has_safety_tag) 2546 @as([*]u8, @ptrCast(safety_buffer.ptr)) 2547 else 2548 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr)); 2549 2550 var iovecs = [_]std.posix.iovec{ 2551 .{ 2552 .base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)), 2553 .len = header.instructions_len, 2554 }, 2555 .{ 2556 .base = data_ptr, 2557 .len = header.instructions_len * 8, 2558 }, 2559 .{ 2560 .base = zir.string_bytes.ptr, 2561 .len = header.string_bytes_len, 2562 }, 2563 .{ 2564 .base = @as([*]u8, @ptrCast(zir.extra.ptr)), 2565 .len = header.extra_len * 4, 2566 }, 2567 }; 2568 const amt_read = try cache_file.readvAll(&iovecs); 2569 const amt_expected = zir.instructions.len * 9 + 2570 zir.string_bytes.len + 2571 zir.extra.len * 4; 2572 if (amt_read != amt_expected) return error.UnexpectedFileSize; 2573 if (data_has_safety_tag) { 2574 const tags = zir.instructions.items(.tag); 2575 for (zir.instructions.items(.data), 0..) |*data, i| { 2576 const union_tag = Zir.Inst.Tag.data_tags[@intFromEnum(tags[i])]; 2577 const as_struct = @as(*HackDataLayout, @ptrCast(data)); 2578 as_struct.* = .{ 2579 .safety_tag = @intFromEnum(union_tag), 2580 .data = safety_buffer[i], 2581 }; 2582 } 2583 } 2584 2585 return zir; 2586 } 2587 2588 pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { 2589 log.debug("outdated dependee: {}", .{dependee}); 2590 var it = zcu.intern_pool.dependencyIterator(dependee); 2591 while (it.next()) |depender| { 2592 if (zcu.outdated.contains(depender)) { 2593 // We do not need to increment the PO dep count, as if the outdated 2594 // dependee is a Decl, we had already marked this as PO. 2595 continue; 2596 } 2597 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender); 2598 try zcu.outdated.putNoClobber( 2599 zcu.gpa, 2600 depender, 2601 // We do not need to increment this count for the same reason as above. 2602 if (opt_po_entry) |e| e.value else 0, 2603 ); 2604 log.debug("outdated: {}", .{depender}); 2605 if (opt_po_entry == null) { 2606 // This is a new entry with no PO dependencies. 2607 try zcu.outdated_ready.put(zcu.gpa, depender, {}); 2608 } 2609 // If this is a Decl and was not previously PO, we must recursively 2610 // mark dependencies on its tyval as PO. 2611 if (opt_po_entry == null) { 2612 try zcu.markTransitiveDependersPotentiallyOutdated(depender); 2613 } 2614 } 2615 } 2616 2617 pub fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { 2618 var it = zcu.intern_pool.dependencyIterator(dependee); 2619 while (it.next()) |depender| { 2620 if (zcu.outdated.getPtr(depender)) |po_dep_count| { 2621 // This depender is already outdated, but it now has one 2622 // less PO dependency! 2623 po_dep_count.* -= 1; 2624 if (po_dep_count.* == 0) { 2625 try zcu.outdated_ready.put(zcu.gpa, depender, {}); 2626 } 2627 continue; 2628 } 2629 // This depender is definitely at least PO, because this Decl was just analyzed 2630 // due to being outdated. 2631 const ptr = zcu.potentially_outdated.getPtr(depender).?; 2632 if (ptr.* > 1) { 2633 ptr.* -= 1; 2634 continue; 2635 } 2636 2637 // This dependency is no longer PO, i.e. is known to be up-to-date. 2638 assert(zcu.potentially_outdated.swapRemove(depender)); 2639 // If this is a Decl, we must recursively mark dependencies on its tyval 2640 // as no longer PO. 2641 switch (depender.unwrap()) { 2642 .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }), 2643 .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }), 2644 } 2645 } 2646 } 2647 2648 /// Given a AnalUnit which is newly outdated or PO, mark all AnalUnits which may 2649 /// in turn be PO, due to a dependency on the original AnalUnit's tyval or IES. 2650 fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: AnalUnit) !void { 2651 var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) { 2652 .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced 2653 .func => |func_index| .{ .func_ies = func_index }, 2654 }); 2655 2656 while (it.next()) |po| { 2657 if (zcu.outdated.getPtr(po)) |po_dep_count| { 2658 // This dependency is already outdated, but it now has one more PO 2659 // dependency. 2660 if (po_dep_count.* == 0) { 2661 _ = zcu.outdated_ready.swapRemove(po); 2662 } 2663 po_dep_count.* += 1; 2664 continue; 2665 } 2666 if (zcu.potentially_outdated.getPtr(po)) |n| { 2667 // There is now one more PO dependency. 2668 n.* += 1; 2669 continue; 2670 } 2671 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); 2672 // This AnalUnit was not already PO, so we must recursively mark its dependers as also PO. 2673 try zcu.markTransitiveDependersPotentiallyOutdated(po); 2674 } 2675 } 2676 2677 pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?AnalUnit { 2678 if (!zcu.comp.debug_incremental) return null; 2679 2680 if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) { 2681 log.debug("findOutdatedToAnalyze: no outdated depender", .{}); 2682 return null; 2683 } 2684 2685 // Our goal is to find an outdated AnalUnit which itself has no outdated or 2686 // PO dependencies. Most of the time, such an AnalUnit will exist - we track 2687 // them in the `outdated_ready` set for efficiency. However, this is not 2688 // necessarily the case, since the Decl dependency graph may contain loops 2689 // via mutually recursive definitions: 2690 // pub const A = struct { b: *B }; 2691 // pub const B = struct { b: *A }; 2692 // In this case, we must defer to more complex logic below. 2693 2694 if (zcu.outdated_ready.count() > 0) { 2695 log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{ 2696 @tagName(zcu.outdated_ready.keys()[0].unwrap()), 2697 switch (zcu.outdated_ready.keys()[0].unwrap()) { 2698 inline else => |x| @intFromEnum(x), 2699 }, 2700 }); 2701 return zcu.outdated_ready.keys()[0]; 2702 } 2703 2704 // Next, we will see if there is any outdated file root which was not in 2705 // `outdated`. This set will be small (number of files changed in this 2706 // update), so it's alright for us to just iterate here. 2707 for (zcu.outdated_file_root.keys()) |file_decl| { 2708 const decl_depender = AnalUnit.wrap(.{ .decl = file_decl }); 2709 if (zcu.outdated.contains(decl_depender)) { 2710 // Since we didn't hit this in the first loop, this Decl must have 2711 // pending dependencies, so is ineligible. 2712 continue; 2713 } 2714 if (zcu.potentially_outdated.contains(decl_depender)) { 2715 // This Decl's struct may or may not need to be recreated depending 2716 // on whether it is outdated. If we analyzed it now, we would have 2717 // to assume it was outdated and recreate it! 2718 continue; 2719 } 2720 log.debug("findOutdatedToAnalyze: outdated file root decl '{d}'", .{file_decl}); 2721 return decl_depender; 2722 } 2723 2724 // There is no single AnalUnit which is ready for re-analysis. Instead, we 2725 // must assume that some Decl with PO dependencies is outdated - e.g. in the 2726 // above example we arbitrarily pick one of A or B. We should select a Decl, 2727 // since a Decl is definitely responsible for the loop in the dependency 2728 // graph (since you can't depend on a runtime function analysis!). 2729 2730 // The choice of this Decl could have a big impact on how much total 2731 // analysis we perform, since if analysis concludes its tyval is unchanged, 2732 // then other PO AnalUnit may be resolved as up-to-date. To hopefully avoid 2733 // doing too much work, let's find a Decl which the most things depend on - 2734 // the idea is that this will resolve a lot of loops (but this is only a 2735 // heuristic). 2736 2737 log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{ 2738 zcu.outdated.count(), 2739 zcu.potentially_outdated.count(), 2740 }); 2741 2742 var chosen_decl_idx: ?Decl.Index = null; 2743 var chosen_decl_dependers: u32 = undefined; 2744 2745 for (zcu.outdated.keys()) |depender| { 2746 const decl_index = switch (depender.unwrap()) { 2747 .decl => |d| d, 2748 .func => continue, 2749 }; 2750 2751 var n: u32 = 0; 2752 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); 2753 while (it.next()) |_| n += 1; 2754 2755 if (chosen_decl_idx == null or n > chosen_decl_dependers) { 2756 chosen_decl_idx = decl_index; 2757 chosen_decl_dependers = n; 2758 } 2759 } 2760 2761 for (zcu.potentially_outdated.keys()) |depender| { 2762 const decl_index = switch (depender.unwrap()) { 2763 .decl => |d| d, 2764 .func => continue, 2765 }; 2766 2767 var n: u32 = 0; 2768 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); 2769 while (it.next()) |_| n += 1; 2770 2771 if (chosen_decl_idx == null or n > chosen_decl_dependers) { 2772 chosen_decl_idx = decl_index; 2773 chosen_decl_dependers = n; 2774 } 2775 } 2776 2777 log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{ 2778 chosen_decl_idx.?, 2779 chosen_decl_dependers, 2780 }); 2781 2782 return AnalUnit.wrap(.{ .decl = chosen_decl_idx.? }); 2783 } 2784 2785 /// During an incremental update, before semantic analysis, call this to flush all values from 2786 /// `retryable_failures` and mark them as outdated so they get re-analyzed. 2787 pub fn flushRetryableFailures(zcu: *Zcu) !void { 2788 const gpa = zcu.gpa; 2789 for (zcu.retryable_failures.items) |depender| { 2790 if (zcu.outdated.contains(depender)) continue; 2791 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| { 2792 // This AnalUnit was already PO, but we now consider it outdated. 2793 // Any transitive dependencies are already marked PO. 2794 try zcu.outdated.put(gpa, depender, kv.value); 2795 continue; 2796 } 2797 // This AnalUnit was not marked PO, but is now outdated. Mark it as 2798 // such, then recursively mark transitive dependencies as PO. 2799 try zcu.outdated.put(gpa, depender, 0); 2800 try zcu.markTransitiveDependersPotentiallyOutdated(depender); 2801 } 2802 zcu.retryable_failures.clearRetainingCapacity(); 2803 } 2804 2805 pub fn mapOldZirToNew( 2806 gpa: Allocator, 2807 old_zir: Zir, 2808 new_zir: Zir, 2809 inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), 2810 ) Allocator.Error!void { 2811 // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc. 2812 // Not `declaration`, as this does not create a namespace. 2813 const MatchedZirDecl = struct { 2814 old_inst: Zir.Inst.Index, 2815 new_inst: Zir.Inst.Index, 2816 }; 2817 var match_stack: std.ArrayListUnmanaged(MatchedZirDecl) = .{}; 2818 defer match_stack.deinit(gpa); 2819 2820 // Main struct inst is always matched 2821 try match_stack.append(gpa, .{ 2822 .old_inst = .main_struct_inst, 2823 .new_inst = .main_struct_inst, 2824 }); 2825 2826 // Used as temporary buffers for namespace declaration instructions 2827 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa); 2828 defer old_decls.deinit(); 2829 var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa); 2830 defer new_decls.deinit(); 2831 2832 while (match_stack.popOrNull()) |match_item| { 2833 // Match the namespace declaration itself 2834 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst); 2835 2836 // Maps decl name to `declaration` instruction. 2837 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{}; 2838 defer named_decls.deinit(gpa); 2839 // Maps test name to `declaration` instruction. 2840 var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{}; 2841 defer named_tests.deinit(gpa); 2842 // All unnamed tests, in order, for a best-effort match. 2843 var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; 2844 defer unnamed_tests.deinit(gpa); 2845 // All comptime declarations, in order, for a best-effort match. 2846 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; 2847 defer comptime_decls.deinit(gpa); 2848 // All usingnamespace declarations, in order, for a best-effort match. 2849 var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; 2850 defer usingnamespace_decls.deinit(gpa); 2851 2852 { 2853 var old_decl_it = old_zir.declIterator(match_item.old_inst); 2854 while (old_decl_it.next()) |old_decl_inst| { 2855 const old_decl, _ = old_zir.getDeclaration(old_decl_inst); 2856 switch (old_decl.name) { 2857 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), 2858 .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst), 2859 .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst), 2860 _ => { 2861 const name_nts = old_decl.name.toString(old_zir).?; 2862 const name = old_zir.nullTerminatedString(name_nts); 2863 if (old_decl.name.isNamedTest(old_zir)) { 2864 try named_tests.put(gpa, name, old_decl_inst); 2865 } else { 2866 try named_decls.put(gpa, name, old_decl_inst); 2867 } 2868 }, 2869 } 2870 } 2871 } 2872 2873 var unnamed_test_idx: u32 = 0; 2874 var comptime_decl_idx: u32 = 0; 2875 var usingnamespace_decl_idx: u32 = 0; 2876 2877 var new_decl_it = new_zir.declIterator(match_item.new_inst); 2878 while (new_decl_it.next()) |new_decl_inst| { 2879 const new_decl, _ = new_zir.getDeclaration(new_decl_inst); 2880 // Attempt to match this to a declaration in the old ZIR: 2881 // * For named declarations (`const`/`var`/`fn`), we match based on name. 2882 // * For named tests (`test "foo"`), we also match based on name. 2883 // * For unnamed tests and decltests, we match based on order. 2884 // * For comptime blocks, we match based on order. 2885 // * For usingnamespace decls, we match based on order. 2886 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`. 2887 const old_decl_inst = switch (new_decl.name) { 2888 .@"comptime" => inst: { 2889 if (comptime_decl_idx == comptime_decls.items.len) continue; 2890 defer comptime_decl_idx += 1; 2891 break :inst comptime_decls.items[comptime_decl_idx]; 2892 }, 2893 .@"usingnamespace" => inst: { 2894 if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue; 2895 defer usingnamespace_decl_idx += 1; 2896 break :inst usingnamespace_decls.items[usingnamespace_decl_idx]; 2897 }, 2898 .unnamed_test, .decltest => inst: { 2899 if (unnamed_test_idx == unnamed_tests.items.len) continue; 2900 defer unnamed_test_idx += 1; 2901 break :inst unnamed_tests.items[unnamed_test_idx]; 2902 }, 2903 _ => inst: { 2904 const name_nts = new_decl.name.toString(old_zir).?; 2905 const name = new_zir.nullTerminatedString(name_nts); 2906 if (new_decl.name.isNamedTest(new_zir)) { 2907 break :inst named_tests.get(name) orelse continue; 2908 } else { 2909 break :inst named_decls.get(name) orelse continue; 2910 } 2911 }, 2912 }; 2913 2914 // Match the `declaration` instruction 2915 try inst_map.put(gpa, old_decl_inst, new_decl_inst); 2916 2917 // Find namespace declarations within this declaration 2918 try old_zir.findDecls(&old_decls, old_decl_inst); 2919 try new_zir.findDecls(&new_decls, new_decl_inst); 2920 2921 // We don't have any smart way of matching up these namespace declarations, so we always 2922 // correlate them based on source order. 2923 const n = @min(old_decls.items.len, new_decls.items.len); 2924 try match_stack.ensureUnusedCapacity(gpa, n); 2925 for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| { 2926 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst }); 2927 } 2928 } 2929 } 2930 } 2931 2932 /// Ensure this function's body is or will be analyzed and emitted. This should 2933 /// be called whenever a potential runtime call of a function is seen. 2934 /// 2935 /// The caller is responsible for ensuring the function decl itself is already 2936 /// analyzed, and for ensuring it can exist at runtime (see 2937 /// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body 2938 /// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`. 2939 pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) !void { 2940 const ip = &mod.intern_pool; 2941 const func = mod.funcInfo(func_index); 2942 const decl_index = func.owner_decl; 2943 const decl = mod.declPtr(decl_index); 2944 2945 switch (decl.analysis) { 2946 .unreferenced => unreachable, 2947 .in_progress => unreachable, 2948 2949 .file_failure, 2950 .sema_failure, 2951 .codegen_failure, 2952 .dependency_failure, 2953 // Analysis of the function Decl itself failed, but we've already 2954 // emitted an error for that. The callee doesn't need the function to be 2955 // analyzed right now, so its analysis can safely continue. 2956 => return, 2957 2958 .complete => {}, 2959 } 2960 2961 assert(decl.has_tv); 2962 2963 const func_as_depender = AnalUnit.wrap(.{ .func = func_index }); 2964 const is_outdated = mod.outdated.contains(func_as_depender) or 2965 mod.potentially_outdated.contains(func_as_depender); 2966 2967 switch (func.analysis(ip).state) { 2968 .none => {}, 2969 .queued => return, 2970 // As above, we don't need to forward errors here. 2971 .sema_failure, 2972 .dependency_failure, 2973 .codegen_failure, 2974 .success, 2975 => if (!is_outdated) return, 2976 .in_progress => return, 2977 .inline_only => unreachable, // don't queue work for this 2978 } 2979 2980 // Decl itself is safely analyzed, and body analysis is not yet queued 2981 2982 try mod.comp.work_queue.writeItem(.{ .analyze_func = func_index }); 2983 if (mod.emit_h != null) { 2984 // TODO: we ideally only want to do this if the function's type changed 2985 // since the last update 2986 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); 2987 } 2988 func.analysis(ip).state = .queued; 2989 } 2990 2991 pub const SemaDeclResult = packed struct { 2992 /// Whether the value of a `decl_val` of this Decl changed. 2993 invalidate_decl_val: bool, 2994 /// Whether the type of a `decl_ref` of this Decl changed. 2995 invalidate_decl_ref: bool, 2996 }; 2997 2998 pub const ImportFileResult = struct { 2999 file: *File, 3000 file_index: File.Index, 3001 is_new: bool, 3002 is_pkg: bool, 3003 }; 3004 3005 pub fn computePathDigest(zcu: *Zcu, mod: *Package.Module, sub_file_path: []const u8) Cache.BinDigest { 3006 const want_local_cache = mod == zcu.main_mod; 3007 var path_hash: Cache.HashHelper = .{}; 3008 path_hash.addBytes(build_options.version); 3009 path_hash.add(builtin.zig_backend); 3010 if (!want_local_cache) { 3011 path_hash.addOptionalBytes(mod.root.root_dir.path); 3012 path_hash.addBytes(mod.root.sub_path); 3013 } 3014 path_hash.addBytes(sub_file_path); 3015 var bin: Cache.BinDigest = undefined; 3016 path_hash.hasher.final(&bin); 3017 return bin; 3018 } 3019 3020 /// Delete all the Export objects that are caused by this `AnalUnit`. Re-analysis of 3021 /// this `AnalUnit` will cause them to be re-created (or not). 3022 pub fn deleteUnitExports(zcu: *Zcu, anal_unit: AnalUnit) void { 3023 const gpa = zcu.gpa; 3024 3025 const exports_base, const exports_len = if (zcu.single_exports.fetchSwapRemove(anal_unit)) |kv| 3026 .{ kv.value, 1 } 3027 else if (zcu.multi_exports.fetchSwapRemove(anal_unit)) |info| 3028 .{ info.value.index, info.value.len } 3029 else 3030 return; 3031 3032 const exports = zcu.all_exports.items[exports_base..][0..exports_len]; 3033 3034 // In an only-c build, we're guaranteed to never use incremental compilation, so there are 3035 // guaranteed not to be any exports in the output file that need deleting (since we only call 3036 // `updateExports` on flush). 3037 // This case is needed because in some rare edge cases, `Sema` wants to add and delete exports 3038 // within a single update. 3039 if (!build_options.only_c) { 3040 for (exports, exports_base..) |exp, export_idx| { 3041 if (zcu.comp.bin_file) |lf| { 3042 lf.deleteExport(exp.exported, exp.opts.name); 3043 } 3044 if (zcu.failed_exports.fetchSwapRemove(@intCast(export_idx))) |failed_kv| { 3045 failed_kv.value.destroy(gpa); 3046 } 3047 } 3048 } 3049 3050 zcu.free_exports.ensureUnusedCapacity(gpa, exports_len) catch { 3051 // This space will be reused eventually, so we need not propagate this error. 3052 // Just leak it for now, and let GC reclaim it later on. 3053 return; 3054 }; 3055 for (exports_base..exports_base + exports_len) |export_idx| { 3056 zcu.free_exports.appendAssumeCapacity(@intCast(export_idx)); 3057 } 3058 } 3059 3060 /// Delete all references in `reference_table` which are caused by this `AnalUnit`. 3061 /// Re-analysis of the `AnalUnit` will cause appropriate references to be recreated. 3062 pub fn deleteUnitReferences(zcu: *Zcu, anal_unit: AnalUnit) void { 3063 const gpa = zcu.gpa; 3064 3065 const kv = zcu.reference_table.fetchSwapRemove(anal_unit) orelse return; 3066 var idx = kv.value; 3067 3068 while (idx != std.math.maxInt(u32)) { 3069 zcu.free_references.append(gpa, idx) catch { 3070 // This space will be reused eventually, so we need not propagate this error. 3071 // Just leak it for now, and let GC reclaim it later on. 3072 return; 3073 }; 3074 idx = zcu.all_references.items[idx].next; 3075 } 3076 } 3077 3078 pub fn addUnitReference(zcu: *Zcu, src_unit: AnalUnit, referenced_unit: AnalUnit, ref_src: LazySrcLoc) Allocator.Error!void { 3079 const gpa = zcu.gpa; 3080 3081 try zcu.reference_table.ensureUnusedCapacity(gpa, 1); 3082 3083 const ref_idx = zcu.free_references.popOrNull() orelse idx: { 3084 _ = try zcu.all_references.addOne(gpa); 3085 break :idx zcu.all_references.items.len - 1; 3086 }; 3087 3088 errdefer comptime unreachable; 3089 3090 const gop = zcu.reference_table.getOrPutAssumeCapacity(src_unit); 3091 3092 zcu.all_references.items[ref_idx] = .{ 3093 .referenced = referenced_unit, 3094 .next = if (gop.found_existing) gop.value_ptr.* else std.math.maxInt(u32), 3095 .src = ref_src, 3096 }; 3097 3098 gop.value_ptr.* = @intCast(ref_idx); 3099 } 3100 3101 pub fn getErrorValue( 3102 mod: *Module, 3103 name: InternPool.NullTerminatedString, 3104 ) Allocator.Error!ErrorInt { 3105 const gop = try mod.global_error_set.getOrPut(mod.gpa, name); 3106 return @as(ErrorInt, @intCast(gop.index)); 3107 } 3108 3109 pub fn getErrorValueFromSlice( 3110 mod: *Module, 3111 name: []const u8, 3112 ) Allocator.Error!ErrorInt { 3113 const interned_name = try mod.intern_pool.getOrPutString(mod.gpa, name); 3114 return getErrorValue(mod, interned_name); 3115 } 3116 3117 pub fn errorSetBits(mod: *Module) u16 { 3118 if (mod.error_limit == 0) return 0; 3119 return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error 3120 } 3121 3122 pub fn errNote( 3123 mod: *Module, 3124 src_loc: LazySrcLoc, 3125 parent: *ErrorMsg, 3126 comptime format: []const u8, 3127 args: anytype, 3128 ) error{OutOfMemory}!void { 3129 const msg = try std.fmt.allocPrint(mod.gpa, format, args); 3130 errdefer mod.gpa.free(msg); 3131 3132 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); 3133 parent.notes[parent.notes.len - 1] = .{ 3134 .src_loc = src_loc, 3135 .msg = msg, 3136 }; 3137 } 3138 3139 /// Deprecated. There is no global target for a Zig Compilation Unit. Instead, 3140 /// look up the target based on the Module that contains the source code being 3141 /// analyzed. 3142 pub fn getTarget(zcu: Module) Target { 3143 return zcu.root_mod.resolved_target.result; 3144 } 3145 3146 /// Deprecated. There is no global optimization mode for a Zig Compilation 3147 /// Unit. Instead, look up the optimization mode based on the Module that 3148 /// contains the source code being analyzed. 3149 pub fn optimizeMode(zcu: Module) std.builtin.OptimizeMode { 3150 return zcu.root_mod.optimize_mode; 3151 } 3152 3153 fn lockAndClearFileCompileError(mod: *Module, file: *File) void { 3154 switch (file.status) { 3155 .success_zir, .retryable_failure => {}, 3156 .never_loaded, .parse_failure, .astgen_failure => { 3157 mod.comp.mutex.lock(); 3158 defer mod.comp.mutex.unlock(); 3159 if (mod.failed_files.fetchSwapRemove(file)) |kv| { 3160 if (kv.value) |msg| msg.destroy(mod.gpa); // Delete previous error message. 3161 } 3162 }, 3163 } 3164 } 3165 3166 pub fn handleUpdateExports( 3167 zcu: *Zcu, 3168 export_indices: []const u32, 3169 result: link.File.UpdateExportsError!void, 3170 ) Allocator.Error!void { 3171 const gpa = zcu.gpa; 3172 result catch |err| switch (err) { 3173 error.OutOfMemory => return error.OutOfMemory, 3174 error.AnalysisFail => { 3175 const export_idx = export_indices[0]; 3176 const new_export = &zcu.all_exports.items[export_idx]; 3177 new_export.status = .failed_retryable; 3178 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); 3179 const msg = try ErrorMsg.create(gpa, new_export.src, "unable to export: {s}", .{ 3180 @errorName(err), 3181 }); 3182 zcu.failed_exports.putAssumeCapacityNoClobber(export_idx, msg); 3183 }, 3184 }; 3185 } 3186 3187 pub fn reportRetryableFileError( 3188 zcu: *Zcu, 3189 file_index: File.Index, 3190 comptime format: []const u8, 3191 args: anytype, 3192 ) error{OutOfMemory}!void { 3193 const gpa = zcu.gpa; 3194 const ip = &zcu.intern_pool; 3195 3196 const file = zcu.fileByIndex(file_index); 3197 file.status = .retryable_failure; 3198 3199 const err_msg = try ErrorMsg.create( 3200 gpa, 3201 .{ 3202 .base_node_inst = try ip.trackZir(gpa, file_index, .main_struct_inst), 3203 .offset = .entire_file, 3204 }, 3205 format, 3206 args, 3207 ); 3208 errdefer err_msg.destroy(gpa); 3209 3210 zcu.comp.mutex.lock(); 3211 defer zcu.comp.mutex.unlock(); 3212 3213 const gop = try zcu.failed_files.getOrPut(gpa, file); 3214 if (gop.found_existing) { 3215 if (gop.value_ptr.*) |old_err_msg| { 3216 old_err_msg.destroy(gpa); 3217 } 3218 } 3219 gop.value_ptr.* = err_msg; 3220 } 3221 3222 pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void { 3223 const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index); 3224 if (gop.found_existing) { 3225 const new_value = try std.fmt.allocPrint(mod.gpa, "{s}\n{s}", .{ gop.value_ptr.*, source }); 3226 mod.gpa.free(gop.value_ptr.*); 3227 gop.value_ptr.* = new_value; 3228 } else { 3229 gop.value_ptr.* = try mod.gpa.dupe(u8, source); 3230 } 3231 } 3232 3233 pub const Feature = enum { 3234 panic_fn, 3235 panic_unwrap_error, 3236 safety_check_formatted, 3237 error_return_trace, 3238 is_named_enum_value, 3239 error_set_has_value, 3240 field_reordering, 3241 /// When this feature is supported, the backend supports the following AIR instructions: 3242 /// * `Air.Inst.Tag.add_safe` 3243 /// * `Air.Inst.Tag.sub_safe` 3244 /// * `Air.Inst.Tag.mul_safe` 3245 /// The motivation for this feature is that it makes AIR smaller, and makes it easier 3246 /// to generate better machine code in the backends. All backends should migrate to 3247 /// enabling this feature. 3248 safety_checked_instructions, 3249 /// If the backend supports running from another thread. 3250 separate_thread, 3251 }; 3252 3253 pub fn backendSupportsFeature(zcu: Module, comptime feature: Feature) bool { 3254 const backend = target_util.zigBackend(zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); 3255 return target_util.backendSupportsFeature(backend, feature); 3256 } 3257 3258 pub const AtomicPtrAlignmentError = error{ 3259 FloatTooBig, 3260 IntTooBig, 3261 BadType, 3262 OutOfMemory, 3263 }; 3264 3265 pub const AtomicPtrAlignmentDiagnostics = struct { 3266 bits: u16 = undefined, 3267 max_bits: u16 = undefined, 3268 }; 3269 3270 /// If ABI alignment of `ty` is OK for atomic operations, returns 0. 3271 /// Otherwise returns the alignment required on a pointer for the target 3272 /// to perform atomic operations. 3273 // TODO this function does not take into account CPU features, which can affect 3274 // this value. Audit this! 3275 pub fn atomicPtrAlignment( 3276 mod: *Module, 3277 ty: Type, 3278 diags: *AtomicPtrAlignmentDiagnostics, 3279 ) AtomicPtrAlignmentError!Alignment { 3280 const target = mod.getTarget(); 3281 const max_atomic_bits: u16 = switch (target.cpu.arch) { 3282 .avr, 3283 .msp430, 3284 .spu_2, 3285 => 16, 3286 3287 .arc, 3288 .arm, 3289 .armeb, 3290 .hexagon, 3291 .m68k, 3292 .le32, 3293 .mips, 3294 .mipsel, 3295 .nvptx, 3296 .powerpc, 3297 .powerpcle, 3298 .r600, 3299 .riscv32, 3300 .sparc, 3301 .sparcel, 3302 .tce, 3303 .tcele, 3304 .thumb, 3305 .thumbeb, 3306 .x86, 3307 .xcore, 3308 .amdil, 3309 .hsail, 3310 .spir, 3311 .kalimba, 3312 .lanai, 3313 .shave, 3314 .wasm32, 3315 .renderscript32, 3316 .csky, 3317 .spirv32, 3318 .dxil, 3319 .loongarch32, 3320 .xtensa, 3321 => 32, 3322 3323 .amdgcn, 3324 .bpfel, 3325 .bpfeb, 3326 .le64, 3327 .mips64, 3328 .mips64el, 3329 .nvptx64, 3330 .powerpc64, 3331 .powerpc64le, 3332 .riscv64, 3333 .sparc64, 3334 .s390x, 3335 .amdil64, 3336 .hsail64, 3337 .spir64, 3338 .wasm64, 3339 .renderscript64, 3340 .ve, 3341 .spirv64, 3342 .loongarch64, 3343 => 64, 3344 3345 .aarch64, 3346 .aarch64_be, 3347 .aarch64_32, 3348 => 128, 3349 3350 .x86_64 => if (std.Target.x86.featureSetHas(target.cpu.features, .cx16)) 128 else 64, 3351 3352 .spirv => @panic("TODO what should this value be?"), 3353 }; 3354 3355 const int_ty = switch (ty.zigTypeTag(mod)) { 3356 .Int => ty, 3357 .Enum => ty.intTagType(mod), 3358 .Float => { 3359 const bit_count = ty.floatBits(target); 3360 if (bit_count > max_atomic_bits) { 3361 diags.* = .{ 3362 .bits = bit_count, 3363 .max_bits = max_atomic_bits, 3364 }; 3365 return error.FloatTooBig; 3366 } 3367 return .none; 3368 }, 3369 .Bool => return .none, 3370 else => { 3371 if (ty.isPtrAtRuntime(mod)) return .none; 3372 return error.BadType; 3373 }, 3374 }; 3375 3376 const bit_count = int_ty.intInfo(mod).bits; 3377 if (bit_count > max_atomic_bits) { 3378 diags.* = .{ 3379 .bits = bit_count, 3380 .max_bits = max_atomic_bits, 3381 }; 3382 return error.IntTooBig; 3383 } 3384 3385 return .none; 3386 } 3387 3388 pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File { 3389 return mod.declPtr(decl_index).getFileScope(mod); 3390 } 3391 3392 /// Returns null in the following cases: 3393 /// * `@TypeOf(.{})` 3394 /// * A struct which has no fields (`struct {}`). 3395 /// * Not a struct. 3396 pub fn typeToStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType { 3397 if (ty.ip_index == .none) return null; 3398 const ip = &mod.intern_pool; 3399 return switch (ip.indexToKey(ty.ip_index)) { 3400 .struct_type => ip.loadStructType(ty.ip_index), 3401 else => null, 3402 }; 3403 } 3404 3405 pub fn typeToPackedStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType { 3406 const s = mod.typeToStruct(ty) orelse return null; 3407 if (s.layout != .@"packed") return null; 3408 return s; 3409 } 3410 3411 pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.LoadedUnionType { 3412 if (ty.ip_index == .none) return null; 3413 const ip = &mod.intern_pool; 3414 return switch (ip.indexToKey(ty.ip_index)) { 3415 .union_type => ip.loadUnionType(ty.ip_index), 3416 else => null, 3417 }; 3418 } 3419 3420 pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType { 3421 if (ty.ip_index == .none) return null; 3422 return mod.intern_pool.indexToFuncType(ty.toIntern()); 3423 } 3424 3425 pub fn funcOwnerDeclPtr(mod: *Module, func_index: InternPool.Index) *Decl { 3426 return mod.declPtr(mod.funcOwnerDeclIndex(func_index)); 3427 } 3428 3429 pub fn funcOwnerDeclIndex(mod: *Module, func_index: InternPool.Index) Decl.Index { 3430 return mod.funcInfo(func_index).owner_decl; 3431 } 3432 3433 pub fn iesFuncIndex(mod: *const Module, ies_index: InternPool.Index) InternPool.Index { 3434 return mod.intern_pool.iesFuncIndex(ies_index); 3435 } 3436 3437 pub fn funcInfo(mod: *Module, func_index: InternPool.Index) InternPool.Key.Func { 3438 return mod.intern_pool.indexToKey(func_index).func; 3439 } 3440 3441 pub fn toEnum(mod: *Module, comptime E: type, val: Value) E { 3442 return mod.intern_pool.toEnum(E, val.toIntern()); 3443 } 3444 3445 pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool { 3446 const file = mod.declPtr(func.owner_decl).getFileScope(mod); 3447 3448 const tags = file.zir.instructions.items(.tag); 3449 3450 const param_body = file.zir.getParamBody(func.zir_body_inst); 3451 const param = param_body[index]; 3452 3453 return switch (tags[param]) { 3454 .param, .param_comptime => false, 3455 .param_anytype, .param_anytype_comptime => true, 3456 else => unreachable, 3457 }; 3458 } 3459 3460 pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]const u8 { 3461 const func = mod.funcInfo(func_index); 3462 const file = mod.declPtr(func.owner_decl).getFileScope(mod); 3463 3464 const tags = file.zir.instructions.items(.tag); 3465 const data = file.zir.instructions.items(.data); 3466 3467 const param_body = file.zir.getParamBody(func.zir_body_inst.resolve(&mod.intern_pool)); 3468 const param = param_body[index]; 3469 3470 return switch (tags[@intFromEnum(param)]) { 3471 .param, .param_comptime => blk: { 3472 const extra = file.zir.extraData(Zir.Inst.Param, data[@intFromEnum(param)].pl_tok.payload_index); 3473 break :blk file.zir.nullTerminatedString(extra.data.name); 3474 }, 3475 .param_anytype, .param_anytype_comptime => blk: { 3476 const param_data = data[@intFromEnum(param)].str_tok; 3477 break :blk param_data.get(file.zir); 3478 }, 3479 else => unreachable, 3480 }; 3481 } 3482 3483 pub const UnionLayout = struct { 3484 abi_size: u64, 3485 abi_align: Alignment, 3486 most_aligned_field: u32, 3487 most_aligned_field_size: u64, 3488 biggest_field: u32, 3489 payload_size: u64, 3490 payload_align: Alignment, 3491 tag_align: Alignment, 3492 tag_size: u64, 3493 padding: u32, 3494 }; 3495 3496 /// Returns the index of the active field, given the current tag value 3497 pub fn unionTagFieldIndex(mod: *Module, loaded_union: InternPool.LoadedUnionType, enum_tag: Value) ?u32 { 3498 const ip = &mod.intern_pool; 3499 if (enum_tag.toIntern() == .none) return null; 3500 assert(ip.typeOf(enum_tag.toIntern()) == loaded_union.enum_tag_ty); 3501 return loaded_union.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern()); 3502 } 3503 3504 pub const ResolvedReference = struct { 3505 referencer: AnalUnit, 3506 src: LazySrcLoc, 3507 }; 3508 3509 /// Returns a mapping from an `AnalUnit` to where it is referenced. 3510 /// TODO: in future, this must be adapted to traverse from roots of analysis. That way, we can 3511 /// use the returned map to determine which units have become unreferenced in an incremental update. 3512 pub fn resolveReferences(zcu: *Zcu) !std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) { 3513 const gpa = zcu.gpa; 3514 3515 var result: std.AutoHashMapUnmanaged(AnalUnit, ResolvedReference) = .{}; 3516 errdefer result.deinit(gpa); 3517 3518 // This is not a sufficient size, but a lower bound. 3519 try result.ensureTotalCapacity(gpa, @intCast(zcu.reference_table.count())); 3520 3521 for (zcu.reference_table.keys(), zcu.reference_table.values()) |referencer, first_ref_idx| { 3522 assert(first_ref_idx != std.math.maxInt(u32)); 3523 var ref_idx = first_ref_idx; 3524 while (ref_idx != std.math.maxInt(u32)) { 3525 const ref = zcu.all_references.items[ref_idx]; 3526 const gop = try result.getOrPut(gpa, ref.referenced); 3527 if (!gop.found_existing) { 3528 gop.value_ptr.* = .{ .referencer = referencer, .src = ref.src }; 3529 } 3530 ref_idx = ref.next; 3531 } 3532 } 3533 3534 return result; 3535 } 3536 3537 pub fn fileByIndex(zcu: *Zcu, i: File.Index) *File { 3538 const ip = &zcu.intern_pool; 3539 return ip.filePtr(i); 3540 } 3541 3542 /// Returns the `Decl` of the struct that represents this `File`. 3543 pub fn fileRootDecl(zcu: *const Zcu, i: File.Index) Decl.OptionalIndex { 3544 const ip = &zcu.intern_pool; 3545 return ip.files.values()[@intFromEnum(i)]; 3546 } 3547 3548 pub fn setFileRootDecl(zcu: *Zcu, i: File.Index, root_decl: Decl.OptionalIndex) void { 3549 const ip = &zcu.intern_pool; 3550 ip.files.values()[@intFromEnum(i)] = root_decl; 3551 } 3552 3553 pub fn filePathDigest(zcu: *const Zcu, i: File.Index) Cache.BinDigest { 3554 const ip = &zcu.intern_pool; 3555 return ip.files.keys()[@intFromEnum(i)]; 3556 }