blob 320627a1 (248069B) - 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 ArrayListUnmanaged = std.ArrayListUnmanaged; 10 const assert = std.debug.assert; 11 const log = std.log.scoped(.module); 12 const BigIntConst = std.math.big.int.Const; 13 const BigIntMutable = std.math.big.int.Mutable; 14 const Target = std.Target; 15 const Ast = std.zig.Ast; 16 const LazySrcLoc = std.zig.LazySrcLoc; 17 18 /// Deprecated, use `Zcu`. 19 const Module = Zcu; 20 const Zcu = @This(); 21 const Compilation = @import("Compilation.zig"); 22 const Cache = std.Build.Cache; 23 const Value = @import("Value.zig"); 24 const Type = @import("type.zig").Type; 25 const Package = @import("Package.zig"); 26 const link = @import("link.zig"); 27 const Air = @import("Air.zig"); 28 const Zir = std.zig.Zir; 29 const trace = @import("tracy.zig").trace; 30 const AstGen = std.zig.AstGen; 31 const Sema = @import("Sema.zig"); 32 const target_util = @import("target.zig"); 33 const build_options = @import("build_options"); 34 const Liveness = @import("Liveness.zig"); 35 const isUpDir = @import("introspect.zig").isUpDir; 36 const clang = @import("clang.zig"); 37 const InternPool = @import("InternPool.zig"); 38 const Alignment = InternPool.Alignment; 39 const BuiltinFn = std.zig.BuiltinFn; 40 const LlvmObject = @import("codegen/llvm.zig").Object; 41 42 comptime { 43 @setEvalBranchQuota(4000); 44 for ( 45 @typeInfo(Zir.Inst.Ref).Enum.fields, 46 @typeInfo(Air.Inst.Ref).Enum.fields, 47 @typeInfo(InternPool.Index).Enum.fields, 48 ) |zir_field, air_field, ip_field| { 49 assert(mem.eql(u8, zir_field.name, ip_field.name)); 50 assert(mem.eql(u8, air_field.name, ip_field.name)); 51 } 52 } 53 54 /// General-purpose allocator. Used for both temporary and long-term storage. 55 gpa: Allocator, 56 comp: *Compilation, 57 /// Usually, the LlvmObject is managed by linker code, however, in the case 58 /// that -fno-emit-bin is specified, the linker code never executes, so we 59 /// store the LlvmObject here. 60 llvm_object: ?*LlvmObject, 61 62 /// Pointer to externally managed resource. 63 root_mod: *Package.Module, 64 /// Normally, `main_mod` and `root_mod` are the same. The exception is `zig test`, in which 65 /// `root_mod` is the test runner, and `main_mod` is the user's source file which has the tests. 66 main_mod: *Package.Module, 67 std_mod: *Package.Module, 68 sema_prog_node: std.Progress.Node = undefined, 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 /// It's rare for a decl to be exported, so we save memory by having a sparse 75 /// map of Decl indexes to details about them being exported. 76 /// The Export memory is owned by the `export_owners` table; the slice itself 77 /// is owned by this table. The slice is guaranteed to not be empty. 78 decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{}, 79 /// Same as `decl_exports` but for exported constant values. 80 value_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, ArrayListUnmanaged(*Export)) = .{}, 81 /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl 82 /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that 83 /// is performing the export of another Decl. 84 /// This table owns the Export memory. 85 export_owners: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{}, 86 /// The set of all the Zig source files in the Module. We keep track of this in order 87 /// to iterate over it and check which source files have been modified on the file system when 88 /// an update is requested, as well as to cache `@import` results. 89 /// Keys are fully resolved file paths. This table owns the keys and values. 90 import_table: std.StringArrayHashMapUnmanaged(*File) = .{}, 91 /// The set of all the files which have been loaded with `@embedFile` in the Module. 92 /// We keep track of this in order to iterate over it and check which files have been 93 /// modified on the file system when an update is requested, as well as to cache 94 /// `@embedFile` results. 95 /// Keys are fully resolved file paths. This table owns the keys and values. 96 embed_table: std.StringArrayHashMapUnmanaged(*EmbedFile) = .{}, 97 98 /// Stores all Type and Value objects. 99 /// The idea is that this will be periodically garbage-collected, but such logic 100 /// is not yet implemented. 101 intern_pool: InternPool = .{}, 102 103 /// We optimize memory usage for a compilation with no compile errors by storing the 104 /// error messages and mapping outside of `Decl`. 105 /// The ErrorMsg memory is owned by the decl, using Module's general purpose allocator. 106 /// Note that a Decl can succeed but the Fn it represents can fail. In this case, 107 /// a Decl can have a failed_decls entry but have analysis status of success. 108 failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{}, 109 /// Keep track of one `@compileLog` callsite per owner Decl. 110 /// The value is the AST node index offset from the Decl. 111 compile_log_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, i32) = .{}, 112 /// Using a map here for consistency with the other fields here. 113 /// The ErrorMsg memory is owned by the `File`, using Module's general purpose allocator. 114 failed_files: std.AutoArrayHashMapUnmanaged(*File, ?*ErrorMsg) = .{}, 115 /// The ErrorMsg memory is owned by the `EmbedFile`, using Module's general purpose allocator. 116 failed_embed_files: std.AutoArrayHashMapUnmanaged(*EmbedFile, *ErrorMsg) = .{}, 117 /// Using a map here for consistency with the other fields here. 118 /// The ErrorMsg memory is owned by the `Export`, using Module's general purpose allocator. 119 failed_exports: std.AutoArrayHashMapUnmanaged(*Export, *ErrorMsg) = .{}, 120 /// If a decl failed due to a cimport error, the corresponding Clang errors 121 /// are stored here. 122 cimport_errors: std.AutoArrayHashMapUnmanaged(Decl.Index, std.zig.ErrorBundle) = .{}, 123 124 /// Key is the error name, index is the error tag value. Index 0 has a length-0 string. 125 global_error_set: GlobalErrorSet = .{}, 126 127 /// Maximum amount of distinct error values, set by --error-limit 128 error_limit: ErrorInt, 129 130 /// Value is the number of PO or outdated Decls which this Depender depends on. 131 potentially_outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{}, 132 /// Value is the number of PO or outdated Decls which this Depender depends on. 133 /// Once this value drops to 0, the Depender is a candidate for re-analysis. 134 outdated: std.AutoArrayHashMapUnmanaged(InternPool.Depender, u32) = .{}, 135 /// This contains all `Depender`s in `outdated` whose PO dependency count is 0. 136 /// Such `Depender`s are ready for immediate re-analysis. 137 /// See `findOutdatedToAnalyze` for details. 138 outdated_ready: std.AutoArrayHashMapUnmanaged(InternPool.Depender, void) = .{}, 139 /// This contains a set of Decls which may not be in `outdated`, but are the 140 /// root Decls of files which have updated source and thus must be re-analyzed. 141 /// If such a Decl is only in this set, the struct type index may be preserved 142 /// (only the namespace might change). If such a Decl is also `outdated`, the 143 /// struct type index must be recreated. 144 outdated_file_root: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, 145 /// This contains a list of Dependers whose analysis or codegen failed, but the 146 /// failure was something like running out of disk space, and trying again may 147 /// succeed. On the next update, we will flush this list, marking all members of 148 /// it as outdated. 149 retryable_failures: std.ArrayListUnmanaged(InternPool.Depender) = .{}, 150 151 stage1_flags: packed struct { 152 have_winmain: bool = false, 153 have_wwinmain: bool = false, 154 have_winmain_crt_startup: bool = false, 155 have_wwinmain_crt_startup: bool = false, 156 have_dllmain_crt_startup: bool = false, 157 have_c_main: bool = false, 158 reserved: u2 = 0, 159 } = .{}, 160 161 compile_log_text: ArrayListUnmanaged(u8) = .{}, 162 163 emit_h: ?*GlobalEmitH, 164 165 test_functions: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, 166 167 global_assembly: std.AutoArrayHashMapUnmanaged(Decl.Index, []u8) = .{}, 168 169 reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct { 170 referencer: Decl.Index, 171 src: LazySrcLoc, 172 }) = .{}, 173 174 panic_messages: [PanicId.len]Decl.OptionalIndex = .{.none} ** PanicId.len, 175 /// The panic function body. 176 panic_func_index: InternPool.Index = .none, 177 null_stack_trace: InternPool.Index = .none, 178 179 pub const PanicId = enum { 180 unreach, 181 unwrap_null, 182 cast_to_null, 183 incorrect_alignment, 184 invalid_error_code, 185 cast_truncated_data, 186 negative_to_unsigned, 187 integer_overflow, 188 shl_overflow, 189 shr_overflow, 190 divide_by_zero, 191 exact_division_remainder, 192 inactive_union_field, 193 integer_part_out_of_bounds, 194 corrupt_switch, 195 shift_rhs_too_big, 196 invalid_enum_value, 197 sentinel_mismatch, 198 unwrap_error, 199 index_out_of_bounds, 200 start_index_greater_than_end, 201 for_len_mismatch, 202 memcpy_len_mismatch, 203 memcpy_alias, 204 noreturn_returned, 205 206 pub const len = @typeInfo(PanicId).Enum.fields.len; 207 }; 208 209 pub const GlobalErrorSet = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void); 210 211 pub const CImportError = struct { 212 offset: u32, 213 line: u32, 214 column: u32, 215 path: ?[*:0]u8, 216 source_line: ?[*:0]u8, 217 msg: [*:0]u8, 218 219 pub fn deinit(err: CImportError, gpa: Allocator) void { 220 if (err.path) |some| gpa.free(std.mem.span(some)); 221 if (err.source_line) |some| gpa.free(std.mem.span(some)); 222 gpa.free(std.mem.span(err.msg)); 223 } 224 }; 225 226 /// A `Module` has zero or one of these depending on whether `-femit-h` is enabled. 227 pub const GlobalEmitH = struct { 228 /// Where to put the output. 229 loc: Compilation.EmitLoc, 230 /// When emit_h is non-null, each Decl gets one more compile error slot for 231 /// emit-h failing for that Decl. This table is also how we tell if a Decl has 232 /// failed emit-h or succeeded. 233 failed_decls: std.AutoArrayHashMapUnmanaged(Decl.Index, *ErrorMsg) = .{}, 234 /// Tracks all decls in order to iterate over them and emit .h code for them. 235 decl_table: std.AutoArrayHashMapUnmanaged(Decl.Index, void) = .{}, 236 /// Similar to the allocated_decls field of Module, this is where `EmitH` objects 237 /// are allocated. There will be exactly one EmitH object per Decl object, with 238 /// identical indexes. 239 allocated_emit_h: std.SegmentedList(EmitH, 0) = .{}, 240 241 pub fn declPtr(global_emit_h: *GlobalEmitH, decl_index: Decl.Index) *EmitH { 242 return global_emit_h.allocated_emit_h.at(@intFromEnum(decl_index)); 243 } 244 }; 245 246 pub const ErrorInt = u32; 247 248 pub const Exported = union(enum) { 249 /// The Decl being exported. Note this is *not* the Decl performing the export. 250 decl_index: Decl.Index, 251 /// Constant value being exported. 252 value: InternPool.Index, 253 }; 254 255 pub const Export = struct { 256 opts: Options, 257 src: LazySrcLoc, 258 /// The Decl that performs the export. Note that this is *not* the Decl being exported. 259 owner_decl: Decl.Index, 260 /// The Decl containing the export statement. Inline function calls 261 /// may cause this to be different from the owner_decl. 262 src_decl: Decl.Index, 263 exported: Exported, 264 status: enum { 265 in_progress, 266 failed, 267 /// Indicates that the failure was due to a temporary issue, such as an I/O error 268 /// when writing to the output file. Retrying the export may succeed. 269 failed_retryable, 270 complete, 271 }, 272 273 pub const Options = struct { 274 name: InternPool.NullTerminatedString, 275 linkage: std.builtin.GlobalLinkage = .strong, 276 section: InternPool.OptionalNullTerminatedString = .none, 277 visibility: std.builtin.SymbolVisibility = .default, 278 }; 279 280 pub fn getSrcLoc(exp: Export, mod: *Module) SrcLoc { 281 const src_decl = mod.declPtr(exp.src_decl); 282 return .{ 283 .file_scope = src_decl.getFileScope(mod), 284 .parent_decl_node = src_decl.src_node, 285 .lazy = exp.src, 286 }; 287 } 288 }; 289 290 const ValueArena = struct { 291 state: std.heap.ArenaAllocator.State, 292 state_acquired: ?*std.heap.ArenaAllocator.State = null, 293 294 /// If this ValueArena replaced an existing one during re-analysis, this is the previous instance 295 prev: ?*ValueArena = null, 296 297 /// Returns an allocator backed by either promoting `state`, or by the existing ArenaAllocator 298 /// that has already promoted `state`. `out_arena_allocator` provides storage for the initial promotion, 299 /// and must live until the matching call to release(). 300 pub fn acquire(self: *ValueArena, child_allocator: Allocator, out_arena_allocator: *std.heap.ArenaAllocator) Allocator { 301 if (self.state_acquired) |state_acquired| { 302 return @as(*std.heap.ArenaAllocator, @fieldParentPtr("state", state_acquired)).allocator(); 303 } 304 305 out_arena_allocator.* = self.state.promote(child_allocator); 306 self.state_acquired = &out_arena_allocator.state; 307 return out_arena_allocator.allocator(); 308 } 309 310 /// Releases the allocator acquired by `acquire. `arena_allocator` must match the one passed to `acquire`. 311 pub fn release(self: *ValueArena, arena_allocator: *std.heap.ArenaAllocator) void { 312 if (@as(*std.heap.ArenaAllocator, @fieldParentPtr("state", self.state_acquired.?)) == arena_allocator) { 313 self.state = self.state_acquired.?.*; 314 self.state_acquired = null; 315 } 316 } 317 318 pub fn deinit(self: ValueArena, child_allocator: Allocator) void { 319 assert(self.state_acquired == null); 320 321 const prev = self.prev; 322 self.state.promote(child_allocator).deinit(); 323 324 if (prev) |p| { 325 p.deinit(child_allocator); 326 } 327 } 328 }; 329 330 pub const Decl = struct { 331 name: InternPool.NullTerminatedString, 332 /// The most recent Value of the Decl after a successful semantic analysis. 333 /// Populated when `has_tv`. 334 val: Value, 335 /// Populated when `has_tv`. 336 @"linksection": InternPool.OptionalNullTerminatedString, 337 /// Populated when `has_tv`. 338 alignment: Alignment, 339 /// Populated when `has_tv`. 340 @"addrspace": std.builtin.AddressSpace, 341 /// The direct parent namespace of the Decl. In the case of the Decl 342 /// corresponding to a file, this is the namespace of the struct, since 343 /// there is no parent. 344 src_namespace: Namespace.Index, 345 346 /// The AST node index of this declaration. 347 /// Must be recomputed when the corresponding source file is modified. 348 src_node: Ast.Node.Index, 349 /// Line number corresponding to `src_node`. Stored separately so that source files 350 /// do not need to be loaded into memory in order to compute debug line numbers. 351 /// This value is absolute. 352 src_line: u32, 353 /// Index of the ZIR `declaration` instruction from which this `Decl` was created. 354 /// For the root `Decl` of a `File` and legacy anonymous decls, this is `.none`. 355 zir_decl_index: InternPool.TrackedInst.Index.Optional, 356 357 /// Represents the "shallow" analysis status. For example, for decls that are functions, 358 /// the function type is analyzed with this set to `in_progress`, however, the semantic 359 /// analysis of the function body is performed with this value set to `success`. Functions 360 /// have their own analysis status field. 361 analysis: enum { 362 /// This Decl corresponds to an AST Node that has not been referenced yet, and therefore 363 /// because of Zig's lazy declaration analysis, it will remain unanalyzed until referenced. 364 unreferenced, 365 /// Semantic analysis for this Decl is running right now. 366 /// This state detects dependency loops. 367 in_progress, 368 /// The file corresponding to this Decl had a parse error or ZIR error. 369 /// There will be a corresponding ErrorMsg in Zcu.failed_files. 370 file_failure, 371 /// This Decl might be OK but it depends on another one which did not 372 /// successfully complete semantic analysis. 373 dependency_failure, 374 /// Semantic analysis failure. 375 /// There will be a corresponding ErrorMsg in Zcu.failed_decls. 376 sema_failure, 377 /// There will be a corresponding ErrorMsg in Zcu.failed_decls. 378 codegen_failure, 379 /// Sematic analysis and constant value codegen of this Decl has 380 /// succeeded. However, the Decl may be outdated due to an in-progress 381 /// update. Note that for a function, this does not mean codegen of the 382 /// function body succeded: that state is indicated by the function's 383 /// `analysis` field. 384 complete, 385 }, 386 /// Whether `typed_value`, `align`, `linksection` and `addrspace` are populated. 387 has_tv: bool, 388 /// If `true` it means the `Decl` is the resource owner of the type/value associated 389 /// with it. That means when `Decl` is destroyed, the cleanup code should additionally 390 /// check if the value owns a `Namespace`, and destroy that too. 391 owns_tv: bool, 392 /// Whether the corresponding AST decl has a `pub` keyword. 393 is_pub: bool, 394 /// Whether the corresponding AST decl has a `export` keyword. 395 is_exported: bool, 396 /// If true `name` is already fully qualified. 397 name_fully_qualified: bool = false, 398 /// What kind of a declaration is this. 399 kind: Kind, 400 401 pub const Kind = enum { 402 @"usingnamespace", 403 @"test", 404 @"comptime", 405 named, 406 anon, 407 }; 408 409 const Index = InternPool.DeclIndex; 410 const OptionalIndex = InternPool.OptionalDeclIndex; 411 412 pub fn zirBodies(decl: Decl, zcu: *Zcu) Zir.Inst.Declaration.Bodies { 413 const zir = decl.getFileScope(zcu).zir; 414 const zir_index = decl.zir_decl_index.unwrap().?.resolve(&zcu.intern_pool); 415 const pl_node = zir.instructions.items(.data)[@intFromEnum(zir_index)].pl_node; 416 const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index); 417 return extra.data.getBodies(@intCast(extra.end), zir); 418 } 419 420 pub fn relativeToNodeIndex(decl: Decl, offset: i32) Ast.Node.Index { 421 return @bitCast(offset + @as(i32, @bitCast(decl.src_node))); 422 } 423 424 pub fn nodeIndexToRelative(decl: Decl, node_index: Ast.Node.Index) i32 { 425 return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(decl.src_node)); 426 } 427 428 pub fn srcLoc(decl: Decl, zcu: *Zcu) SrcLoc { 429 return decl.nodeOffsetSrcLoc(0, zcu); 430 } 431 432 pub fn nodeOffsetSrcLoc(decl: Decl, node_offset: i32, zcu: *Zcu) SrcLoc { 433 return .{ 434 .file_scope = decl.getFileScope(zcu), 435 .parent_decl_node = decl.src_node, 436 .lazy = LazySrcLoc.nodeOffset(node_offset), 437 }; 438 } 439 440 pub fn renderFullyQualifiedName(decl: Decl, zcu: *Zcu, writer: anytype) !void { 441 if (decl.name_fully_qualified) { 442 try writer.print("{}", .{decl.name.fmt(&zcu.intern_pool)}); 443 } else { 444 try zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedName(zcu, decl.name, writer); 445 } 446 } 447 448 pub fn renderFullyQualifiedDebugName(decl: Decl, zcu: *Zcu, writer: anytype) !void { 449 return zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedDebugName(zcu, decl.name, writer); 450 } 451 452 pub fn fullyQualifiedName(decl: Decl, zcu: *Zcu) !InternPool.NullTerminatedString { 453 return if (decl.name_fully_qualified) 454 decl.name 455 else 456 zcu.namespacePtr(decl.src_namespace).fullyQualifiedName(zcu, decl.name); 457 } 458 459 pub fn typeOf(decl: Decl, zcu: *const Zcu) Type { 460 assert(decl.has_tv); 461 return decl.val.typeOf(zcu); 462 } 463 464 /// Small wrapper for Sema to use over direct access to the `val` field. 465 /// If the value is not populated, instead returns `error.AnalysisFail`. 466 pub fn valueOrFail(decl: Decl) error{AnalysisFail}!Value { 467 if (!decl.has_tv) return error.AnalysisFail; 468 return decl.val; 469 } 470 471 pub fn getOwnedFunction(decl: Decl, zcu: *Zcu) ?InternPool.Key.Func { 472 const i = decl.getOwnedFunctionIndex(); 473 if (i == .none) return null; 474 return switch (zcu.intern_pool.indexToKey(i)) { 475 .func => |func| func, 476 else => null, 477 }; 478 } 479 480 /// This returns an InternPool.Index even when the value is not a function. 481 pub fn getOwnedFunctionIndex(decl: Decl) InternPool.Index { 482 return if (decl.owns_tv) decl.val.toIntern() else .none; 483 } 484 485 /// If the Decl owns its value and it is an extern function, returns it, 486 /// otherwise null. 487 pub fn getOwnedExternFunc(decl: Decl, zcu: *Zcu) ?InternPool.Key.ExternFunc { 488 return if (decl.owns_tv) decl.val.getExternFunc(zcu) else null; 489 } 490 491 /// If the Decl owns its value and it is a variable, returns it, 492 /// otherwise null. 493 pub fn getOwnedVariable(decl: Decl, zcu: *Zcu) ?InternPool.Key.Variable { 494 return if (decl.owns_tv) decl.val.getVariable(zcu) else null; 495 } 496 497 /// Gets the namespace that this Decl creates by being a struct, union, 498 /// enum, or opaque. 499 pub fn getInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex { 500 if (!decl.has_tv) return .none; 501 const ip = &zcu.intern_pool; 502 return switch (decl.val.ip_index) { 503 .empty_struct_type => .none, 504 .none => .none, 505 else => switch (ip.indexToKey(decl.val.toIntern())) { 506 .opaque_type => ip.loadOpaqueType(decl.val.toIntern()).namespace, 507 .struct_type => ip.loadStructType(decl.val.toIntern()).namespace, 508 .union_type => ip.loadUnionType(decl.val.toIntern()).namespace, 509 .enum_type => ip.loadEnumType(decl.val.toIntern()).namespace, 510 else => .none, 511 }, 512 }; 513 } 514 515 /// Like `getInnerNamespaceIndex`, but only returns it if the Decl is the owner. 516 pub fn getOwnedInnerNamespaceIndex(decl: Decl, zcu: *Zcu) Namespace.OptionalIndex { 517 if (!decl.owns_tv) return .none; 518 return decl.getInnerNamespaceIndex(zcu); 519 } 520 521 /// Same as `getOwnedInnerNamespaceIndex` but additionally obtains the pointer. 522 pub fn getOwnedInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace { 523 return zcu.namespacePtrUnwrap(decl.getOwnedInnerNamespaceIndex(zcu)); 524 } 525 526 /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer. 527 pub fn getInnerNamespace(decl: Decl, zcu: *Zcu) ?*Namespace { 528 return zcu.namespacePtrUnwrap(decl.getInnerNamespaceIndex(zcu)); 529 } 530 531 pub fn getFileScope(decl: Decl, zcu: *Zcu) *File { 532 return zcu.namespacePtr(decl.src_namespace).file_scope; 533 } 534 535 pub fn getExternDecl(decl: Decl, zcu: *Zcu) OptionalIndex { 536 assert(decl.has_tv); 537 return switch (zcu.intern_pool.indexToKey(decl.val.toIntern())) { 538 .variable => |variable| if (variable.is_extern) variable.decl.toOptional() else .none, 539 .extern_func => |extern_func| extern_func.decl.toOptional(), 540 else => .none, 541 }; 542 } 543 544 pub fn isExtern(decl: Decl, zcu: *Zcu) bool { 545 return decl.getExternDecl(zcu) != .none; 546 } 547 548 pub fn getAlignment(decl: Decl, zcu: *Zcu) Alignment { 549 assert(decl.has_tv); 550 if (decl.alignment != .none) return decl.alignment; 551 return decl.typeOf(zcu).abiAlignment(zcu); 552 } 553 554 /// Upgrade a `LazySrcLoc` to a `SrcLoc` based on the `Decl` provided. 555 pub fn toSrcLoc(decl: *Decl, lazy: LazySrcLoc, mod: *Module) SrcLoc { 556 return switch (lazy) { 557 .unneeded, 558 .entire_file, 559 .byte_abs, 560 .token_abs, 561 .node_abs, 562 => .{ 563 .file_scope = decl.getFileScope(mod), 564 .parent_decl_node = 0, 565 .lazy = lazy, 566 }, 567 568 .byte_offset, 569 .token_offset, 570 .node_offset, 571 .node_offset_main_token, 572 .node_offset_initializer, 573 .node_offset_var_decl_ty, 574 .node_offset_var_decl_align, 575 .node_offset_var_decl_section, 576 .node_offset_var_decl_addrspace, 577 .node_offset_var_decl_init, 578 .node_offset_builtin_call_arg0, 579 .node_offset_builtin_call_arg1, 580 .node_offset_builtin_call_arg2, 581 .node_offset_builtin_call_arg3, 582 .node_offset_builtin_call_arg4, 583 .node_offset_builtin_call_arg5, 584 .node_offset_ptrcast_operand, 585 .node_offset_array_access_index, 586 .node_offset_slice_ptr, 587 .node_offset_slice_start, 588 .node_offset_slice_end, 589 .node_offset_slice_sentinel, 590 .node_offset_call_func, 591 .node_offset_field_name, 592 .node_offset_field_name_init, 593 .node_offset_deref_ptr, 594 .node_offset_asm_source, 595 .node_offset_asm_ret_ty, 596 .node_offset_if_cond, 597 .node_offset_bin_op, 598 .node_offset_bin_lhs, 599 .node_offset_bin_rhs, 600 .node_offset_switch_operand, 601 .node_offset_switch_special_prong, 602 .node_offset_switch_range, 603 .node_offset_switch_prong_capture, 604 .node_offset_switch_prong_tag_capture, 605 .node_offset_fn_type_align, 606 .node_offset_fn_type_addrspace, 607 .node_offset_fn_type_section, 608 .node_offset_fn_type_cc, 609 .node_offset_fn_type_ret_ty, 610 .node_offset_param, 611 .token_offset_param, 612 .node_offset_anyframe_type, 613 .node_offset_lib_name, 614 .node_offset_array_type_len, 615 .node_offset_array_type_sentinel, 616 .node_offset_array_type_elem, 617 .node_offset_un_op, 618 .node_offset_ptr_elem, 619 .node_offset_ptr_sentinel, 620 .node_offset_ptr_align, 621 .node_offset_ptr_addrspace, 622 .node_offset_ptr_bitoffset, 623 .node_offset_ptr_hostsize, 624 .node_offset_container_tag, 625 .node_offset_field_default, 626 .node_offset_init_ty, 627 .node_offset_store_ptr, 628 .node_offset_store_operand, 629 .node_offset_return_operand, 630 .for_input, 631 .for_capture_from_input, 632 .array_cat_lhs, 633 .array_cat_rhs, 634 => .{ 635 .file_scope = decl.getFileScope(mod), 636 .parent_decl_node = decl.src_node, 637 .lazy = lazy, 638 }, 639 inline .call_arg, 640 .fn_proto_param, 641 => |x| .{ 642 .file_scope = decl.getFileScope(mod), 643 .parent_decl_node = mod.declPtr(x.decl).src_node, 644 .lazy = lazy, 645 }, 646 }; 647 } 648 649 pub fn declPtrType(decl: Decl, zcu: *Zcu) !Type { 650 assert(decl.has_tv); 651 const decl_ty = decl.typeOf(zcu); 652 return zcu.ptrType(.{ 653 .child = decl_ty.toIntern(), 654 .flags = .{ 655 .alignment = if (decl.alignment == decl_ty.abiAlignment(zcu)) 656 .none 657 else 658 decl.alignment, 659 .address_space = decl.@"addrspace", 660 .is_const = decl.getOwnedVariable(zcu) == null, 661 }, 662 }); 663 } 664 }; 665 666 /// This state is attached to every Decl when Module emit_h is non-null. 667 pub const EmitH = struct { 668 fwd_decl: ArrayListUnmanaged(u8) = .{}, 669 }; 670 671 pub const DeclAdapter = struct { 672 zcu: *Zcu, 673 674 pub fn hash(self: @This(), s: InternPool.NullTerminatedString) u32 { 675 _ = self; 676 return std.hash.uint32(@intFromEnum(s)); 677 } 678 679 pub fn eql(self: @This(), a: InternPool.NullTerminatedString, b_decl_index: Decl.Index, b_index: usize) bool { 680 _ = b_index; 681 return a == self.zcu.declPtr(b_decl_index).name; 682 } 683 }; 684 685 /// The container that structs, enums, unions, and opaques have. 686 pub const Namespace = struct { 687 parent: OptionalIndex, 688 file_scope: *File, 689 /// Will be a struct, enum, union, or opaque. 690 decl_index: Decl.Index, 691 /// Direct children of the namespace. 692 /// Declaration order is preserved via entry order. 693 /// These are only declarations named directly by the AST; anonymous 694 /// declarations are not stored here. 695 decls: std.ArrayHashMapUnmanaged(Decl.Index, void, DeclContext, true) = .{}, 696 /// Key is usingnamespace Decl itself. To find the namespace being included, 697 /// the Decl Value has to be resolved as a Type which has a Namespace. 698 /// Value is whether the usingnamespace decl is marked `pub`. 699 usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{}, 700 701 const Index = InternPool.NamespaceIndex; 702 const OptionalIndex = InternPool.OptionalNamespaceIndex; 703 704 const DeclContext = struct { 705 zcu: *Zcu, 706 707 pub fn hash(ctx: @This(), decl_index: Decl.Index) u32 { 708 const decl = ctx.zcu.declPtr(decl_index); 709 return std.hash.uint32(@intFromEnum(decl.name)); 710 } 711 712 pub fn eql(ctx: @This(), a_decl_index: Decl.Index, b_decl_index: Decl.Index, b_index: usize) bool { 713 _ = b_index; 714 const a_decl = ctx.zcu.declPtr(a_decl_index); 715 const b_decl = ctx.zcu.declPtr(b_decl_index); 716 return a_decl.name == b_decl.name; 717 } 718 }; 719 720 // This renders e.g. "std.fs.Dir.OpenOptions" 721 pub fn renderFullyQualifiedName( 722 ns: Namespace, 723 zcu: *Zcu, 724 name: InternPool.NullTerminatedString, 725 writer: anytype, 726 ) @TypeOf(writer).Error!void { 727 if (ns.parent.unwrap()) |parent| { 728 try zcu.namespacePtr(parent).renderFullyQualifiedName( 729 zcu, 730 zcu.declPtr(ns.decl_index).name, 731 writer, 732 ); 733 } else { 734 try ns.file_scope.renderFullyQualifiedName(writer); 735 } 736 if (name != .empty) try writer.print(".{}", .{name.fmt(&zcu.intern_pool)}); 737 } 738 739 /// This renders e.g. "std/fs.zig:Dir.OpenOptions" 740 pub fn renderFullyQualifiedDebugName( 741 ns: Namespace, 742 zcu: *Zcu, 743 name: InternPool.NullTerminatedString, 744 writer: anytype, 745 ) @TypeOf(writer).Error!void { 746 const sep: u8 = if (ns.parent.unwrap()) |parent| sep: { 747 try zcu.namespacePtr(parent).renderFullyQualifiedDebugName( 748 zcu, 749 zcu.declPtr(ns.decl_index).name, 750 writer, 751 ); 752 break :sep '.'; 753 } else sep: { 754 try ns.file_scope.renderFullyQualifiedDebugName(writer); 755 break :sep ':'; 756 }; 757 if (name != .empty) try writer.print("{c}{}", .{ sep, name.fmt(&zcu.intern_pool) }); 758 } 759 760 pub fn fullyQualifiedName( 761 ns: Namespace, 762 zcu: *Zcu, 763 name: InternPool.NullTerminatedString, 764 ) !InternPool.NullTerminatedString { 765 const ip = &zcu.intern_pool; 766 const count = count: { 767 var count: usize = name.length(ip) + 1; 768 var cur_ns = &ns; 769 while (true) { 770 const decl = zcu.declPtr(cur_ns.decl_index); 771 count += decl.name.length(ip) + 1; 772 cur_ns = zcu.namespacePtr(cur_ns.parent.unwrap() orelse { 773 count += ns.file_scope.sub_file_path.len; 774 break :count count; 775 }); 776 } 777 }; 778 779 const gpa = zcu.gpa; 780 const start = ip.string_bytes.items.len; 781 // Protects reads of interned strings from being reallocated during the call to 782 // renderFullyQualifiedName. 783 try ip.string_bytes.ensureUnusedCapacity(gpa, count); 784 ns.renderFullyQualifiedName(zcu, name, ip.string_bytes.writer(gpa)) catch unreachable; 785 786 // Sanitize the name for nvptx which is more restrictive. 787 // TODO This should be handled by the backend, not the frontend. Have a 788 // look at how the C backend does it for inspiration. 789 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; 790 if (cpu_arch.isNvptx()) { 791 for (ip.string_bytes.items[start..]) |*byte| switch (byte.*) { 792 '{', '}', '*', '[', ']', '(', ')', ',', ' ', '\'' => byte.* = '_', 793 else => {}, 794 }; 795 } 796 797 return ip.getOrPutTrailingString(gpa, ip.string_bytes.items.len - start, .no_embedded_nulls); 798 } 799 800 pub fn getType(ns: Namespace, zcu: *Zcu) Type { 801 const decl = zcu.declPtr(ns.decl_index); 802 assert(decl.has_tv); 803 return decl.val.toType(); 804 } 805 }; 806 807 pub const File = struct { 808 /// The Decl of the struct that represents this File. 809 root_decl: Decl.OptionalIndex, 810 status: enum { 811 never_loaded, 812 retryable_failure, 813 parse_failure, 814 astgen_failure, 815 success_zir, 816 }, 817 source_loaded: bool, 818 tree_loaded: bool, 819 zir_loaded: bool, 820 /// Relative to the owning package's root_src_dir. 821 /// Memory is stored in gpa, owned by File. 822 sub_file_path: []const u8, 823 /// Whether this is populated depends on `source_loaded`. 824 source: [:0]const u8, 825 /// Whether this is populated depends on `status`. 826 stat: Cache.File.Stat, 827 /// Whether this is populated or not depends on `tree_loaded`. 828 tree: Ast, 829 /// Whether this is populated or not depends on `zir_loaded`. 830 zir: Zir, 831 /// Module that this file is a part of, managed externally. 832 mod: *Package.Module, 833 /// Whether this file is a part of multiple packages. This is an error condition which will be reported after AstGen. 834 multi_pkg: bool = false, 835 /// List of references to this file, used for multi-package errors. 836 references: std.ArrayListUnmanaged(Reference) = .{}, 837 /// The hash of the path to this file, used to store `InternPool.TrackedInst`. 838 /// undefined until `zir_loaded == true`. 839 path_digest: Cache.BinDigest = undefined, 840 841 /// The most recent successful ZIR for this file, with no errors. 842 /// This is only populated when a previously successful ZIR 843 /// newly introduces compile errors during an update. When ZIR is 844 /// successful, this field is unloaded. 845 prev_zir: ?*Zir = null, 846 847 /// A single reference to a file. 848 pub const Reference = union(enum) { 849 /// The file is imported directly (i.e. not as a package) with @import. 850 import: SrcLoc, 851 /// The file is the root of a module. 852 root: *Package.Module, 853 }; 854 855 pub fn unload(file: *File, gpa: Allocator) void { 856 file.unloadTree(gpa); 857 file.unloadSource(gpa); 858 file.unloadZir(gpa); 859 } 860 861 pub fn unloadTree(file: *File, gpa: Allocator) void { 862 if (file.tree_loaded) { 863 file.tree_loaded = false; 864 file.tree.deinit(gpa); 865 } 866 } 867 868 pub fn unloadSource(file: *File, gpa: Allocator) void { 869 if (file.source_loaded) { 870 file.source_loaded = false; 871 gpa.free(file.source); 872 } 873 } 874 875 pub fn unloadZir(file: *File, gpa: Allocator) void { 876 if (file.zir_loaded) { 877 file.zir_loaded = false; 878 file.zir.deinit(gpa); 879 } 880 } 881 882 pub fn deinit(file: *File, mod: *Module) void { 883 const gpa = mod.gpa; 884 const is_builtin = file.mod.isBuiltin(); 885 log.debug("deinit File {s}", .{file.sub_file_path}); 886 if (is_builtin) { 887 file.unloadTree(gpa); 888 file.unloadZir(gpa); 889 } else { 890 gpa.free(file.sub_file_path); 891 file.unload(gpa); 892 } 893 file.references.deinit(gpa); 894 if (file.root_decl.unwrap()) |root_decl| { 895 mod.destroyDecl(root_decl); 896 } 897 if (file.prev_zir) |prev_zir| { 898 prev_zir.deinit(gpa); 899 gpa.destroy(prev_zir); 900 } 901 file.* = undefined; 902 } 903 904 pub const Source = struct { 905 bytes: [:0]const u8, 906 stat: Cache.File.Stat, 907 }; 908 909 pub fn getSource(file: *File, gpa: Allocator) !Source { 910 if (file.source_loaded) return Source{ 911 .bytes = file.source, 912 .stat = file.stat, 913 }; 914 915 // Keep track of inode, file size, mtime, hash so we can detect which files 916 // have been modified when an incremental update is requested. 917 var f = try file.mod.root.openFile(file.sub_file_path, .{}); 918 defer f.close(); 919 920 const stat = try f.stat(); 921 922 if (stat.size > std.math.maxInt(u32)) 923 return error.FileTooBig; 924 925 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); 926 defer if (!file.source_loaded) gpa.free(source); 927 const amt = try f.readAll(source); 928 if (amt != stat.size) 929 return error.UnexpectedEndOfFile; 930 931 // Here we do not modify stat fields because this function is the one 932 // used for error reporting. We need to keep the stat fields stale so that 933 // astGenFile can know to regenerate ZIR. 934 935 file.source = source; 936 file.source_loaded = true; 937 return Source{ 938 .bytes = source, 939 .stat = .{ 940 .size = stat.size, 941 .inode = stat.inode, 942 .mtime = stat.mtime, 943 }, 944 }; 945 } 946 947 pub fn getTree(file: *File, gpa: Allocator) !*const Ast { 948 if (file.tree_loaded) return &file.tree; 949 950 const source = try file.getSource(gpa); 951 file.tree = try Ast.parse(gpa, source.bytes, .zig); 952 file.tree_loaded = true; 953 return &file.tree; 954 } 955 956 pub fn destroy(file: *File, mod: *Module) void { 957 const gpa = mod.gpa; 958 const is_builtin = file.mod.isBuiltin(); 959 file.deinit(mod); 960 if (!is_builtin) gpa.destroy(file); 961 } 962 963 pub fn renderFullyQualifiedName(file: File, writer: anytype) !void { 964 // Convert all the slashes into dots and truncate the extension. 965 const ext = std.fs.path.extension(file.sub_file_path); 966 const noext = file.sub_file_path[0 .. file.sub_file_path.len - ext.len]; 967 for (noext) |byte| switch (byte) { 968 '/', '\\' => try writer.writeByte('.'), 969 else => try writer.writeByte(byte), 970 }; 971 } 972 973 pub fn renderFullyQualifiedDebugName(file: File, writer: anytype) !void { 974 for (file.sub_file_path) |byte| switch (byte) { 975 '/', '\\' => try writer.writeByte('/'), 976 else => try writer.writeByte(byte), 977 }; 978 } 979 980 pub fn fullyQualifiedName(file: File, mod: *Module) !InternPool.NullTerminatedString { 981 const ip = &mod.intern_pool; 982 const start = ip.string_bytes.items.len; 983 try file.renderFullyQualifiedName(ip.string_bytes.writer(mod.gpa)); 984 return ip.getOrPutTrailingString(mod.gpa, ip.string_bytes.items.len - start, .no_embedded_nulls); 985 } 986 987 pub fn fullPath(file: File, ally: Allocator) ![]u8 { 988 return file.mod.root.joinString(ally, file.sub_file_path); 989 } 990 991 pub fn dumpSrc(file: *File, src: LazySrcLoc) void { 992 const loc = std.zig.findLineColumn(file.source.bytes, src); 993 std.debug.print("{s}:{d}:{d}\n", .{ file.sub_file_path, loc.line + 1, loc.column + 1 }); 994 } 995 996 pub fn okToReportErrors(file: File) bool { 997 return switch (file.status) { 998 .parse_failure, .astgen_failure => false, 999 else => true, 1000 }; 1001 } 1002 1003 /// Add a reference to this file during AstGen. 1004 pub fn addReference(file: *File, mod: Module, ref: Reference) !void { 1005 // Don't add the same module root twice. Note that since we always add module roots at the 1006 // front of the references array (see below), this loop is actually O(1) on valid code. 1007 if (ref == .root) { 1008 for (file.references.items) |other| { 1009 switch (other) { 1010 .root => |r| if (ref.root == r) return, 1011 else => break, // reached the end of the "is-root" references 1012 } 1013 } 1014 } 1015 1016 switch (ref) { 1017 // We put root references at the front of the list both to make the above loop fast and 1018 // to make multi-module errors more helpful (since "root-of" notes are generally more 1019 // informative than "imported-from" notes). This path is hit very rarely, so the speed 1020 // of the insert operation doesn't matter too much. 1021 .root => try file.references.insert(mod.gpa, 0, ref), 1022 1023 // Other references we'll just put at the end. 1024 else => try file.references.append(mod.gpa, ref), 1025 } 1026 1027 const pkg = switch (ref) { 1028 .import => |loc| loc.file_scope.mod, 1029 .root => |pkg| pkg, 1030 }; 1031 if (pkg != file.mod) file.multi_pkg = true; 1032 } 1033 1034 /// Mark this file and every file referenced by it as multi_pkg and report an 1035 /// astgen_failure error for them. AstGen must have completed in its entirety. 1036 pub fn recursiveMarkMultiPkg(file: *File, mod: *Module) void { 1037 file.multi_pkg = true; 1038 file.status = .astgen_failure; 1039 1040 // We can only mark children as failed if the ZIR is loaded, which may not 1041 // be the case if there were other astgen failures in this file 1042 if (!file.zir_loaded) return; 1043 1044 const imports_index = file.zir.extra[@intFromEnum(Zir.ExtraIndex.imports)]; 1045 if (imports_index == 0) return; 1046 const extra = file.zir.extraData(Zir.Inst.Imports, imports_index); 1047 1048 var extra_index = extra.end; 1049 for (0..extra.data.imports_len) |_| { 1050 const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index); 1051 extra_index = item.end; 1052 1053 const import_path = file.zir.nullTerminatedString(item.data.name); 1054 if (mem.eql(u8, import_path, "builtin")) continue; 1055 1056 const res = mod.importFile(file, import_path) catch continue; 1057 if (!res.is_pkg and !res.file.multi_pkg) { 1058 res.file.recursiveMarkMultiPkg(mod); 1059 } 1060 } 1061 } 1062 }; 1063 1064 pub const EmbedFile = struct { 1065 /// Relative to the owning module's root directory. 1066 sub_file_path: InternPool.NullTerminatedString, 1067 /// Module that this file is a part of, managed externally. 1068 owner: *Package.Module, 1069 stat: Cache.File.Stat, 1070 val: InternPool.Index, 1071 src_loc: SrcLoc, 1072 }; 1073 1074 /// This struct holds data necessary to construct API-facing `AllErrors.Message`. 1075 /// Its memory is managed with the general purpose allocator so that they 1076 /// can be created and destroyed in response to incremental updates. 1077 /// In some cases, the File could have been inferred from where the ErrorMsg 1078 /// is stored. For example, if it is stored in Module.failed_decls, then the File 1079 /// would be determined by the Decl Scope. However, the data structure contains the field 1080 /// anyway so that `ErrorMsg` can be reused for error notes, which may be in a different 1081 /// file than the parent error message. It also simplifies processing of error messages. 1082 pub const ErrorMsg = struct { 1083 src_loc: SrcLoc, 1084 msg: []const u8, 1085 notes: []ErrorMsg = &.{}, 1086 reference_trace: []Trace = &.{}, 1087 hidden_references: u32 = 0, 1088 1089 pub const Trace = struct { 1090 decl: InternPool.NullTerminatedString, 1091 src_loc: SrcLoc, 1092 }; 1093 1094 pub fn create( 1095 gpa: Allocator, 1096 src_loc: SrcLoc, 1097 comptime format: []const u8, 1098 args: anytype, 1099 ) !*ErrorMsg { 1100 assert(src_loc.lazy != .unneeded); 1101 const err_msg = try gpa.create(ErrorMsg); 1102 errdefer gpa.destroy(err_msg); 1103 err_msg.* = try ErrorMsg.init(gpa, src_loc, format, args); 1104 return err_msg; 1105 } 1106 1107 /// Assumes the ErrorMsg struct and msg were both allocated with `gpa`, 1108 /// as well as all notes. 1109 pub fn destroy(err_msg: *ErrorMsg, gpa: Allocator) void { 1110 err_msg.deinit(gpa); 1111 gpa.destroy(err_msg); 1112 } 1113 1114 pub fn init( 1115 gpa: Allocator, 1116 src_loc: SrcLoc, 1117 comptime format: []const u8, 1118 args: anytype, 1119 ) !ErrorMsg { 1120 return ErrorMsg{ 1121 .src_loc = src_loc, 1122 .msg = try std.fmt.allocPrint(gpa, format, args), 1123 }; 1124 } 1125 1126 pub fn deinit(err_msg: *ErrorMsg, gpa: Allocator) void { 1127 for (err_msg.notes) |*note| { 1128 note.deinit(gpa); 1129 } 1130 gpa.free(err_msg.notes); 1131 gpa.free(err_msg.msg); 1132 gpa.free(err_msg.reference_trace); 1133 err_msg.* = undefined; 1134 } 1135 }; 1136 1137 /// Canonical reference to a position within a source file. 1138 pub const SrcLoc = struct { 1139 file_scope: *File, 1140 /// Might be 0 depending on tag of `lazy`. 1141 parent_decl_node: Ast.Node.Index, 1142 /// Relative to `parent_decl_node`. 1143 lazy: LazySrcLoc, 1144 1145 pub fn declSrcToken(src_loc: SrcLoc) Ast.TokenIndex { 1146 const tree = src_loc.file_scope.tree; 1147 return tree.firstToken(src_loc.parent_decl_node); 1148 } 1149 1150 pub fn declRelativeToNodeIndex(src_loc: SrcLoc, offset: i32) Ast.Node.Index { 1151 return @bitCast(offset + @as(i32, @bitCast(src_loc.parent_decl_node))); 1152 } 1153 1154 pub const Span = Ast.Span; 1155 1156 pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span { 1157 switch (src_loc.lazy) { 1158 .unneeded => unreachable, 1159 .entire_file => return Span{ .start = 0, .end = 1, .main = 0 }, 1160 1161 .byte_abs => |byte_index| return Span{ .start = byte_index, .end = byte_index + 1, .main = byte_index }, 1162 1163 .token_abs => |tok_index| { 1164 const tree = try src_loc.file_scope.getTree(gpa); 1165 const start = tree.tokens.items(.start)[tok_index]; 1166 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1167 return Span{ .start = start, .end = end, .main = start }; 1168 }, 1169 .node_abs => |node| { 1170 const tree = try src_loc.file_scope.getTree(gpa); 1171 return tree.nodeToSpan(node); 1172 }, 1173 .byte_offset => |byte_off| { 1174 const tree = try src_loc.file_scope.getTree(gpa); 1175 const tok_index = src_loc.declSrcToken(); 1176 const start = tree.tokens.items(.start)[tok_index] + byte_off; 1177 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1178 return Span{ .start = start, .end = end, .main = start }; 1179 }, 1180 .token_offset => |tok_off| { 1181 const tree = try src_loc.file_scope.getTree(gpa); 1182 const tok_index = src_loc.declSrcToken() + tok_off; 1183 const start = tree.tokens.items(.start)[tok_index]; 1184 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1185 return Span{ .start = start, .end = end, .main = start }; 1186 }, 1187 .node_offset => |traced_off| { 1188 const node_off = traced_off.x; 1189 const tree = try src_loc.file_scope.getTree(gpa); 1190 const node = src_loc.declRelativeToNodeIndex(node_off); 1191 assert(src_loc.file_scope.tree_loaded); 1192 return tree.nodeToSpan(node); 1193 }, 1194 .node_offset_main_token => |node_off| { 1195 const tree = try src_loc.file_scope.getTree(gpa); 1196 const node = src_loc.declRelativeToNodeIndex(node_off); 1197 const main_token = tree.nodes.items(.main_token)[node]; 1198 return tree.tokensToSpan(main_token, main_token, main_token); 1199 }, 1200 .node_offset_bin_op => |node_off| { 1201 const tree = try src_loc.file_scope.getTree(gpa); 1202 const node = src_loc.declRelativeToNodeIndex(node_off); 1203 assert(src_loc.file_scope.tree_loaded); 1204 return tree.nodeToSpan(node); 1205 }, 1206 .node_offset_initializer => |node_off| { 1207 const tree = try src_loc.file_scope.getTree(gpa); 1208 const node = src_loc.declRelativeToNodeIndex(node_off); 1209 return tree.tokensToSpan( 1210 tree.firstToken(node) - 3, 1211 tree.lastToken(node), 1212 tree.nodes.items(.main_token)[node] - 2, 1213 ); 1214 }, 1215 .node_offset_var_decl_ty => |node_off| { 1216 const tree = try src_loc.file_scope.getTree(gpa); 1217 const node = src_loc.declRelativeToNodeIndex(node_off); 1218 const node_tags = tree.nodes.items(.tag); 1219 const full = switch (node_tags[node]) { 1220 .global_var_decl, 1221 .local_var_decl, 1222 .simple_var_decl, 1223 .aligned_var_decl, 1224 => tree.fullVarDecl(node).?, 1225 .@"usingnamespace" => { 1226 const node_data = tree.nodes.items(.data); 1227 return tree.nodeToSpan(node_data[node].lhs); 1228 }, 1229 else => unreachable, 1230 }; 1231 if (full.ast.type_node != 0) { 1232 return tree.nodeToSpan(full.ast.type_node); 1233 } 1234 const tok_index = full.ast.mut_token + 1; // the name token 1235 const start = tree.tokens.items(.start)[tok_index]; 1236 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1237 return Span{ .start = start, .end = end, .main = start }; 1238 }, 1239 .node_offset_var_decl_align => |node_off| { 1240 const tree = try src_loc.file_scope.getTree(gpa); 1241 const node = src_loc.declRelativeToNodeIndex(node_off); 1242 const full = tree.fullVarDecl(node).?; 1243 return tree.nodeToSpan(full.ast.align_node); 1244 }, 1245 .node_offset_var_decl_section => |node_off| { 1246 const tree = try src_loc.file_scope.getTree(gpa); 1247 const node = src_loc.declRelativeToNodeIndex(node_off); 1248 const full = tree.fullVarDecl(node).?; 1249 return tree.nodeToSpan(full.ast.section_node); 1250 }, 1251 .node_offset_var_decl_addrspace => |node_off| { 1252 const tree = try src_loc.file_scope.getTree(gpa); 1253 const node = src_loc.declRelativeToNodeIndex(node_off); 1254 const full = tree.fullVarDecl(node).?; 1255 return tree.nodeToSpan(full.ast.addrspace_node); 1256 }, 1257 .node_offset_var_decl_init => |node_off| { 1258 const tree = try src_loc.file_scope.getTree(gpa); 1259 const node = src_loc.declRelativeToNodeIndex(node_off); 1260 const full = tree.fullVarDecl(node).?; 1261 return tree.nodeToSpan(full.ast.init_node); 1262 }, 1263 .node_offset_builtin_call_arg0 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 0), 1264 .node_offset_builtin_call_arg1 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 1), 1265 .node_offset_builtin_call_arg2 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 2), 1266 .node_offset_builtin_call_arg3 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 3), 1267 .node_offset_builtin_call_arg4 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 4), 1268 .node_offset_builtin_call_arg5 => |n| return src_loc.byteOffsetBuiltinCallArg(gpa, n, 5), 1269 .node_offset_ptrcast_operand => |node_off| { 1270 const tree = try src_loc.file_scope.getTree(gpa); 1271 const main_tokens = tree.nodes.items(.main_token); 1272 const node_datas = tree.nodes.items(.data); 1273 const node_tags = tree.nodes.items(.tag); 1274 1275 var node = src_loc.declRelativeToNodeIndex(node_off); 1276 while (true) { 1277 switch (node_tags[node]) { 1278 .builtin_call_two, .builtin_call_two_comma => {}, 1279 else => break, 1280 } 1281 1282 if (node_datas[node].lhs == 0) break; // 0 args 1283 if (node_datas[node].rhs != 0) break; // 2 args 1284 1285 const builtin_token = main_tokens[node]; 1286 const builtin_name = tree.tokenSlice(builtin_token); 1287 const info = BuiltinFn.list.get(builtin_name) orelse break; 1288 1289 switch (info.tag) { 1290 else => break, 1291 .ptr_cast, 1292 .align_cast, 1293 .addrspace_cast, 1294 .const_cast, 1295 .volatile_cast, 1296 => {}, 1297 } 1298 1299 node = node_datas[node].lhs; 1300 } 1301 1302 return tree.nodeToSpan(node); 1303 }, 1304 .node_offset_array_access_index => |node_off| { 1305 const tree = try src_loc.file_scope.getTree(gpa); 1306 const node_datas = tree.nodes.items(.data); 1307 const node = src_loc.declRelativeToNodeIndex(node_off); 1308 return tree.nodeToSpan(node_datas[node].rhs); 1309 }, 1310 .node_offset_slice_ptr, 1311 .node_offset_slice_start, 1312 .node_offset_slice_end, 1313 .node_offset_slice_sentinel, 1314 => |node_off| { 1315 const tree = try src_loc.file_scope.getTree(gpa); 1316 const node = src_loc.declRelativeToNodeIndex(node_off); 1317 const full = tree.fullSlice(node).?; 1318 const part_node = switch (src_loc.lazy) { 1319 .node_offset_slice_ptr => full.ast.sliced, 1320 .node_offset_slice_start => full.ast.start, 1321 .node_offset_slice_end => full.ast.end, 1322 .node_offset_slice_sentinel => full.ast.sentinel, 1323 else => unreachable, 1324 }; 1325 return tree.nodeToSpan(part_node); 1326 }, 1327 .node_offset_call_func => |node_off| { 1328 const tree = try src_loc.file_scope.getTree(gpa); 1329 const node = src_loc.declRelativeToNodeIndex(node_off); 1330 var buf: [1]Ast.Node.Index = undefined; 1331 const full = tree.fullCall(&buf, node).?; 1332 return tree.nodeToSpan(full.ast.fn_expr); 1333 }, 1334 .node_offset_field_name => |node_off| { 1335 const tree = try src_loc.file_scope.getTree(gpa); 1336 const node_datas = tree.nodes.items(.data); 1337 const node_tags = tree.nodes.items(.tag); 1338 const node = src_loc.declRelativeToNodeIndex(node_off); 1339 var buf: [1]Ast.Node.Index = undefined; 1340 const tok_index = switch (node_tags[node]) { 1341 .field_access => node_datas[node].rhs, 1342 .call_one, 1343 .call_one_comma, 1344 .async_call_one, 1345 .async_call_one_comma, 1346 .call, 1347 .call_comma, 1348 .async_call, 1349 .async_call_comma, 1350 => blk: { 1351 const full = tree.fullCall(&buf, node).?; 1352 break :blk tree.lastToken(full.ast.fn_expr); 1353 }, 1354 else => tree.firstToken(node) - 2, 1355 }; 1356 const start = tree.tokens.items(.start)[tok_index]; 1357 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1358 return Span{ .start = start, .end = end, .main = start }; 1359 }, 1360 .node_offset_field_name_init => |node_off| { 1361 const tree = try src_loc.file_scope.getTree(gpa); 1362 const node = src_loc.declRelativeToNodeIndex(node_off); 1363 const tok_index = tree.firstToken(node) - 2; 1364 const start = tree.tokens.items(.start)[tok_index]; 1365 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1366 return Span{ .start = start, .end = end, .main = start }; 1367 }, 1368 .node_offset_deref_ptr => |node_off| { 1369 const tree = try src_loc.file_scope.getTree(gpa); 1370 const node = src_loc.declRelativeToNodeIndex(node_off); 1371 return tree.nodeToSpan(node); 1372 }, 1373 .node_offset_asm_source => |node_off| { 1374 const tree = try src_loc.file_scope.getTree(gpa); 1375 const node = src_loc.declRelativeToNodeIndex(node_off); 1376 const full = tree.fullAsm(node).?; 1377 return tree.nodeToSpan(full.ast.template); 1378 }, 1379 .node_offset_asm_ret_ty => |node_off| { 1380 const tree = try src_loc.file_scope.getTree(gpa); 1381 const node = src_loc.declRelativeToNodeIndex(node_off); 1382 const full = tree.fullAsm(node).?; 1383 const asm_output = full.outputs[0]; 1384 const node_datas = tree.nodes.items(.data); 1385 return tree.nodeToSpan(node_datas[asm_output].lhs); 1386 }, 1387 1388 .node_offset_if_cond => |node_off| { 1389 const tree = try src_loc.file_scope.getTree(gpa); 1390 const node = src_loc.declRelativeToNodeIndex(node_off); 1391 const node_tags = tree.nodes.items(.tag); 1392 const src_node = switch (node_tags[node]) { 1393 .if_simple, 1394 .@"if", 1395 => tree.fullIf(node).?.ast.cond_expr, 1396 1397 .while_simple, 1398 .while_cont, 1399 .@"while", 1400 => tree.fullWhile(node).?.ast.cond_expr, 1401 1402 .for_simple, 1403 .@"for", 1404 => { 1405 const inputs = tree.fullFor(node).?.ast.inputs; 1406 const start = tree.firstToken(inputs[0]); 1407 const end = tree.lastToken(inputs[inputs.len - 1]); 1408 return tree.tokensToSpan(start, end, start); 1409 }, 1410 1411 .@"orelse" => node, 1412 .@"catch" => node, 1413 else => unreachable, 1414 }; 1415 return tree.nodeToSpan(src_node); 1416 }, 1417 .for_input => |for_input| { 1418 const tree = try src_loc.file_scope.getTree(gpa); 1419 const node = src_loc.declRelativeToNodeIndex(for_input.for_node_offset); 1420 const for_full = tree.fullFor(node).?; 1421 const src_node = for_full.ast.inputs[for_input.input_index]; 1422 return tree.nodeToSpan(src_node); 1423 }, 1424 .for_capture_from_input => |node_off| { 1425 const tree = try src_loc.file_scope.getTree(gpa); 1426 const token_tags = tree.tokens.items(.tag); 1427 const input_node = src_loc.declRelativeToNodeIndex(node_off); 1428 // We have to actually linear scan the whole AST to find the for loop 1429 // that contains this input. 1430 const node_tags = tree.nodes.items(.tag); 1431 for (node_tags, 0..) |node_tag, node_usize| { 1432 const node = @as(Ast.Node.Index, @intCast(node_usize)); 1433 switch (node_tag) { 1434 .for_simple, .@"for" => { 1435 const for_full = tree.fullFor(node).?; 1436 for (for_full.ast.inputs, 0..) |input, input_index| { 1437 if (input_node == input) { 1438 var count = input_index; 1439 var tok = for_full.payload_token; 1440 while (true) { 1441 switch (token_tags[tok]) { 1442 .comma => { 1443 count -= 1; 1444 tok += 1; 1445 }, 1446 .identifier => { 1447 if (count == 0) 1448 return tree.tokensToSpan(tok, tok + 1, tok); 1449 tok += 1; 1450 }, 1451 .asterisk => { 1452 if (count == 0) 1453 return tree.tokensToSpan(tok, tok + 2, tok); 1454 tok += 1; 1455 }, 1456 else => unreachable, 1457 } 1458 } 1459 } 1460 } 1461 }, 1462 else => continue, 1463 } 1464 } else unreachable; 1465 }, 1466 .call_arg => |call_arg| { 1467 const tree = try src_loc.file_scope.getTree(gpa); 1468 const node = src_loc.declRelativeToNodeIndex(call_arg.call_node_offset); 1469 var buf: [2]Ast.Node.Index = undefined; 1470 const call_full = tree.fullCall(buf[0..1], node) orelse { 1471 const node_tags = tree.nodes.items(.tag); 1472 assert(node_tags[node] == .builtin_call); 1473 const call_args_node = tree.extra_data[tree.nodes.items(.data)[node].rhs - 1]; 1474 switch (node_tags[call_args_node]) { 1475 .array_init_one, 1476 .array_init_one_comma, 1477 .array_init_dot_two, 1478 .array_init_dot_two_comma, 1479 .array_init_dot, 1480 .array_init_dot_comma, 1481 .array_init, 1482 .array_init_comma, 1483 => { 1484 const full = tree.fullArrayInit(&buf, call_args_node).?.ast.elements; 1485 return tree.nodeToSpan(full[call_arg.arg_index]); 1486 }, 1487 .struct_init_one, 1488 .struct_init_one_comma, 1489 .struct_init_dot_two, 1490 .struct_init_dot_two_comma, 1491 .struct_init_dot, 1492 .struct_init_dot_comma, 1493 .struct_init, 1494 .struct_init_comma, 1495 => { 1496 const full = tree.fullStructInit(&buf, call_args_node).?.ast.fields; 1497 return tree.nodeToSpan(full[call_arg.arg_index]); 1498 }, 1499 else => return tree.nodeToSpan(call_args_node), 1500 } 1501 }; 1502 return tree.nodeToSpan(call_full.ast.params[call_arg.arg_index]); 1503 }, 1504 .fn_proto_param => |fn_proto_param| { 1505 const tree = try src_loc.file_scope.getTree(gpa); 1506 const node = src_loc.declRelativeToNodeIndex(fn_proto_param.fn_proto_node_offset); 1507 var buf: [1]Ast.Node.Index = undefined; 1508 const full = tree.fullFnProto(&buf, node).?; 1509 var it = full.iterate(tree); 1510 var i: usize = 0; 1511 while (it.next()) |param| : (i += 1) { 1512 if (i == fn_proto_param.param_index) { 1513 if (param.anytype_ellipsis3) |token| return tree.tokenToSpan(token); 1514 const first_token = param.comptime_noalias orelse 1515 param.name_token orelse 1516 tree.firstToken(param.type_expr); 1517 return tree.tokensToSpan( 1518 first_token, 1519 tree.lastToken(param.type_expr), 1520 first_token, 1521 ); 1522 } 1523 } 1524 unreachable; 1525 }, 1526 .node_offset_bin_lhs => |node_off| { 1527 const tree = try src_loc.file_scope.getTree(gpa); 1528 const node = src_loc.declRelativeToNodeIndex(node_off); 1529 const node_datas = tree.nodes.items(.data); 1530 return tree.nodeToSpan(node_datas[node].lhs); 1531 }, 1532 .node_offset_bin_rhs => |node_off| { 1533 const tree = try src_loc.file_scope.getTree(gpa); 1534 const node = src_loc.declRelativeToNodeIndex(node_off); 1535 const node_datas = tree.nodes.items(.data); 1536 return tree.nodeToSpan(node_datas[node].rhs); 1537 }, 1538 .array_cat_lhs, .array_cat_rhs => |cat| { 1539 const tree = try src_loc.file_scope.getTree(gpa); 1540 const node = src_loc.declRelativeToNodeIndex(cat.array_cat_offset); 1541 const node_datas = tree.nodes.items(.data); 1542 const arr_node = if (src_loc.lazy == .array_cat_lhs) 1543 node_datas[node].lhs 1544 else 1545 node_datas[node].rhs; 1546 1547 const node_tags = tree.nodes.items(.tag); 1548 var buf: [2]Ast.Node.Index = undefined; 1549 switch (node_tags[arr_node]) { 1550 .array_init_one, 1551 .array_init_one_comma, 1552 .array_init_dot_two, 1553 .array_init_dot_two_comma, 1554 .array_init_dot, 1555 .array_init_dot_comma, 1556 .array_init, 1557 .array_init_comma, 1558 => { 1559 const full = tree.fullArrayInit(&buf, arr_node).?.ast.elements; 1560 return tree.nodeToSpan(full[cat.elem_index]); 1561 }, 1562 else => return tree.nodeToSpan(arr_node), 1563 } 1564 }, 1565 1566 .node_offset_switch_operand => |node_off| { 1567 const tree = try src_loc.file_scope.getTree(gpa); 1568 const node = src_loc.declRelativeToNodeIndex(node_off); 1569 const node_datas = tree.nodes.items(.data); 1570 return tree.nodeToSpan(node_datas[node].lhs); 1571 }, 1572 1573 .node_offset_switch_special_prong => |node_off| { 1574 const tree = try src_loc.file_scope.getTree(gpa); 1575 const switch_node = src_loc.declRelativeToNodeIndex(node_off); 1576 const node_datas = tree.nodes.items(.data); 1577 const node_tags = tree.nodes.items(.tag); 1578 const main_tokens = tree.nodes.items(.main_token); 1579 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 1580 const case_nodes = tree.extra_data[extra.start..extra.end]; 1581 for (case_nodes) |case_node| { 1582 const case = tree.fullSwitchCase(case_node).?; 1583 const is_special = (case.ast.values.len == 0) or 1584 (case.ast.values.len == 1 and 1585 node_tags[case.ast.values[0]] == .identifier and 1586 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")); 1587 if (!is_special) continue; 1588 1589 return tree.nodeToSpan(case_node); 1590 } else unreachable; 1591 }, 1592 1593 .node_offset_switch_range => |node_off| { 1594 const tree = try src_loc.file_scope.getTree(gpa); 1595 const switch_node = src_loc.declRelativeToNodeIndex(node_off); 1596 const node_datas = tree.nodes.items(.data); 1597 const node_tags = tree.nodes.items(.tag); 1598 const main_tokens = tree.nodes.items(.main_token); 1599 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 1600 const case_nodes = tree.extra_data[extra.start..extra.end]; 1601 for (case_nodes) |case_node| { 1602 const case = tree.fullSwitchCase(case_node).?; 1603 const is_special = (case.ast.values.len == 0) or 1604 (case.ast.values.len == 1 and 1605 node_tags[case.ast.values[0]] == .identifier and 1606 mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_")); 1607 if (is_special) continue; 1608 1609 for (case.ast.values) |item_node| { 1610 if (node_tags[item_node] == .switch_range) { 1611 return tree.nodeToSpan(item_node); 1612 } 1613 } 1614 } else unreachable; 1615 }, 1616 .node_offset_switch_prong_capture, 1617 .node_offset_switch_prong_tag_capture, 1618 => |node_off| { 1619 const tree = try src_loc.file_scope.getTree(gpa); 1620 const case_node = src_loc.declRelativeToNodeIndex(node_off); 1621 const case = tree.fullSwitchCase(case_node).?; 1622 const token_tags = tree.tokens.items(.tag); 1623 const start_tok = switch (src_loc.lazy) { 1624 .node_offset_switch_prong_capture => case.payload_token.?, 1625 .node_offset_switch_prong_tag_capture => blk: { 1626 var tok = case.payload_token.?; 1627 if (token_tags[tok] == .asterisk) tok += 1; 1628 tok += 2; // skip over comma 1629 break :blk tok; 1630 }, 1631 else => unreachable, 1632 }; 1633 const end_tok = switch (token_tags[start_tok]) { 1634 .asterisk => start_tok + 1, 1635 else => start_tok, 1636 }; 1637 const start = tree.tokens.items(.start)[start_tok]; 1638 const end_start = tree.tokens.items(.start)[end_tok]; 1639 const end = end_start + @as(u32, @intCast(tree.tokenSlice(end_tok).len)); 1640 return Span{ .start = start, .end = end, .main = start }; 1641 }, 1642 .node_offset_fn_type_align => |node_off| { 1643 const tree = try src_loc.file_scope.getTree(gpa); 1644 const node = src_loc.declRelativeToNodeIndex(node_off); 1645 var buf: [1]Ast.Node.Index = undefined; 1646 const full = tree.fullFnProto(&buf, node).?; 1647 return tree.nodeToSpan(full.ast.align_expr); 1648 }, 1649 .node_offset_fn_type_addrspace => |node_off| { 1650 const tree = try src_loc.file_scope.getTree(gpa); 1651 const node = src_loc.declRelativeToNodeIndex(node_off); 1652 var buf: [1]Ast.Node.Index = undefined; 1653 const full = tree.fullFnProto(&buf, node).?; 1654 return tree.nodeToSpan(full.ast.addrspace_expr); 1655 }, 1656 .node_offset_fn_type_section => |node_off| { 1657 const tree = try src_loc.file_scope.getTree(gpa); 1658 const node = src_loc.declRelativeToNodeIndex(node_off); 1659 var buf: [1]Ast.Node.Index = undefined; 1660 const full = tree.fullFnProto(&buf, node).?; 1661 return tree.nodeToSpan(full.ast.section_expr); 1662 }, 1663 .node_offset_fn_type_cc => |node_off| { 1664 const tree = try src_loc.file_scope.getTree(gpa); 1665 const node = src_loc.declRelativeToNodeIndex(node_off); 1666 var buf: [1]Ast.Node.Index = undefined; 1667 const full = tree.fullFnProto(&buf, node).?; 1668 return tree.nodeToSpan(full.ast.callconv_expr); 1669 }, 1670 1671 .node_offset_fn_type_ret_ty => |node_off| { 1672 const tree = try src_loc.file_scope.getTree(gpa); 1673 const node = src_loc.declRelativeToNodeIndex(node_off); 1674 var buf: [1]Ast.Node.Index = undefined; 1675 const full = tree.fullFnProto(&buf, node).?; 1676 return tree.nodeToSpan(full.ast.return_type); 1677 }, 1678 .node_offset_param => |node_off| { 1679 const tree = try src_loc.file_scope.getTree(gpa); 1680 const token_tags = tree.tokens.items(.tag); 1681 const node = src_loc.declRelativeToNodeIndex(node_off); 1682 1683 var first_tok = tree.firstToken(node); 1684 while (true) switch (token_tags[first_tok - 1]) { 1685 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, 1686 else => break, 1687 }; 1688 return tree.tokensToSpan( 1689 first_tok, 1690 tree.lastToken(node), 1691 first_tok, 1692 ); 1693 }, 1694 .token_offset_param => |token_off| { 1695 const tree = try src_loc.file_scope.getTree(gpa); 1696 const token_tags = tree.tokens.items(.tag); 1697 const main_token = tree.nodes.items(.main_token)[src_loc.parent_decl_node]; 1698 const tok_index = @as(Ast.TokenIndex, @bitCast(token_off + @as(i32, @bitCast(main_token)))); 1699 1700 var first_tok = tok_index; 1701 while (true) switch (token_tags[first_tok - 1]) { 1702 .colon, .identifier, .keyword_comptime, .keyword_noalias => first_tok -= 1, 1703 else => break, 1704 }; 1705 return tree.tokensToSpan( 1706 first_tok, 1707 tok_index, 1708 first_tok, 1709 ); 1710 }, 1711 1712 .node_offset_anyframe_type => |node_off| { 1713 const tree = try src_loc.file_scope.getTree(gpa); 1714 const node_datas = tree.nodes.items(.data); 1715 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1716 return tree.nodeToSpan(node_datas[parent_node].rhs); 1717 }, 1718 1719 .node_offset_lib_name => |node_off| { 1720 const tree = try src_loc.file_scope.getTree(gpa); 1721 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1722 var buf: [1]Ast.Node.Index = undefined; 1723 const full = tree.fullFnProto(&buf, parent_node).?; 1724 const tok_index = full.lib_name.?; 1725 const start = tree.tokens.items(.start)[tok_index]; 1726 const end = start + @as(u32, @intCast(tree.tokenSlice(tok_index).len)); 1727 return Span{ .start = start, .end = end, .main = start }; 1728 }, 1729 1730 .node_offset_array_type_len => |node_off| { 1731 const tree = try src_loc.file_scope.getTree(gpa); 1732 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1733 1734 const full = tree.fullArrayType(parent_node).?; 1735 return tree.nodeToSpan(full.ast.elem_count); 1736 }, 1737 .node_offset_array_type_sentinel => |node_off| { 1738 const tree = try src_loc.file_scope.getTree(gpa); 1739 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1740 1741 const full = tree.fullArrayType(parent_node).?; 1742 return tree.nodeToSpan(full.ast.sentinel); 1743 }, 1744 .node_offset_array_type_elem => |node_off| { 1745 const tree = try src_loc.file_scope.getTree(gpa); 1746 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1747 1748 const full = tree.fullArrayType(parent_node).?; 1749 return tree.nodeToSpan(full.ast.elem_type); 1750 }, 1751 .node_offset_un_op => |node_off| { 1752 const tree = try src_loc.file_scope.getTree(gpa); 1753 const node_datas = tree.nodes.items(.data); 1754 const node = src_loc.declRelativeToNodeIndex(node_off); 1755 1756 return tree.nodeToSpan(node_datas[node].lhs); 1757 }, 1758 .node_offset_ptr_elem => |node_off| { 1759 const tree = try src_loc.file_scope.getTree(gpa); 1760 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1761 1762 const full = tree.fullPtrType(parent_node).?; 1763 return tree.nodeToSpan(full.ast.child_type); 1764 }, 1765 .node_offset_ptr_sentinel => |node_off| { 1766 const tree = try src_loc.file_scope.getTree(gpa); 1767 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1768 1769 const full = tree.fullPtrType(parent_node).?; 1770 return tree.nodeToSpan(full.ast.sentinel); 1771 }, 1772 .node_offset_ptr_align => |node_off| { 1773 const tree = try src_loc.file_scope.getTree(gpa); 1774 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1775 1776 const full = tree.fullPtrType(parent_node).?; 1777 return tree.nodeToSpan(full.ast.align_node); 1778 }, 1779 .node_offset_ptr_addrspace => |node_off| { 1780 const tree = try src_loc.file_scope.getTree(gpa); 1781 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1782 1783 const full = tree.fullPtrType(parent_node).?; 1784 return tree.nodeToSpan(full.ast.addrspace_node); 1785 }, 1786 .node_offset_ptr_bitoffset => |node_off| { 1787 const tree = try src_loc.file_scope.getTree(gpa); 1788 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1789 1790 const full = tree.fullPtrType(parent_node).?; 1791 return tree.nodeToSpan(full.ast.bit_range_start); 1792 }, 1793 .node_offset_ptr_hostsize => |node_off| { 1794 const tree = try src_loc.file_scope.getTree(gpa); 1795 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1796 1797 const full = tree.fullPtrType(parent_node).?; 1798 return tree.nodeToSpan(full.ast.bit_range_end); 1799 }, 1800 .node_offset_container_tag => |node_off| { 1801 const tree = try src_loc.file_scope.getTree(gpa); 1802 const node_tags = tree.nodes.items(.tag); 1803 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1804 1805 switch (node_tags[parent_node]) { 1806 .container_decl_arg, .container_decl_arg_trailing => { 1807 const full = tree.containerDeclArg(parent_node); 1808 return tree.nodeToSpan(full.ast.arg); 1809 }, 1810 .tagged_union_enum_tag, .tagged_union_enum_tag_trailing => { 1811 const full = tree.taggedUnionEnumTag(parent_node); 1812 1813 return tree.tokensToSpan( 1814 tree.firstToken(full.ast.arg) - 2, 1815 tree.lastToken(full.ast.arg) + 1, 1816 tree.nodes.items(.main_token)[full.ast.arg], 1817 ); 1818 }, 1819 else => unreachable, 1820 } 1821 }, 1822 .node_offset_field_default => |node_off| { 1823 const tree = try src_loc.file_scope.getTree(gpa); 1824 const node_tags = tree.nodes.items(.tag); 1825 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1826 1827 const full: Ast.full.ContainerField = switch (node_tags[parent_node]) { 1828 .container_field => tree.containerField(parent_node), 1829 .container_field_init => tree.containerFieldInit(parent_node), 1830 else => unreachable, 1831 }; 1832 return tree.nodeToSpan(full.ast.value_expr); 1833 }, 1834 .node_offset_init_ty => |node_off| { 1835 const tree = try src_loc.file_scope.getTree(gpa); 1836 const parent_node = src_loc.declRelativeToNodeIndex(node_off); 1837 1838 var buf: [2]Ast.Node.Index = undefined; 1839 const type_expr = if (tree.fullArrayInit(&buf, parent_node)) |array_init| 1840 array_init.ast.type_expr 1841 else 1842 tree.fullStructInit(&buf, parent_node).?.ast.type_expr; 1843 return tree.nodeToSpan(type_expr); 1844 }, 1845 .node_offset_store_ptr => |node_off| { 1846 const tree = try src_loc.file_scope.getTree(gpa); 1847 const node_tags = tree.nodes.items(.tag); 1848 const node_datas = tree.nodes.items(.data); 1849 const node = src_loc.declRelativeToNodeIndex(node_off); 1850 1851 switch (node_tags[node]) { 1852 .assign => { 1853 return tree.nodeToSpan(node_datas[node].lhs); 1854 }, 1855 else => return tree.nodeToSpan(node), 1856 } 1857 }, 1858 .node_offset_store_operand => |node_off| { 1859 const tree = try src_loc.file_scope.getTree(gpa); 1860 const node_tags = tree.nodes.items(.tag); 1861 const node_datas = tree.nodes.items(.data); 1862 const node = src_loc.declRelativeToNodeIndex(node_off); 1863 1864 switch (node_tags[node]) { 1865 .assign => { 1866 return tree.nodeToSpan(node_datas[node].rhs); 1867 }, 1868 else => return tree.nodeToSpan(node), 1869 } 1870 }, 1871 .node_offset_return_operand => |node_off| { 1872 const tree = try src_loc.file_scope.getTree(gpa); 1873 const node = src_loc.declRelativeToNodeIndex(node_off); 1874 const node_tags = tree.nodes.items(.tag); 1875 const node_datas = tree.nodes.items(.data); 1876 if (node_tags[node] == .@"return" and node_datas[node].lhs != 0) { 1877 return tree.nodeToSpan(node_datas[node].lhs); 1878 } 1879 return tree.nodeToSpan(node); 1880 }, 1881 } 1882 } 1883 1884 pub fn byteOffsetBuiltinCallArg( 1885 src_loc: SrcLoc, 1886 gpa: Allocator, 1887 node_off: i32, 1888 arg_index: u32, 1889 ) !Span { 1890 const tree = try src_loc.file_scope.getTree(gpa); 1891 const node_datas = tree.nodes.items(.data); 1892 const node_tags = tree.nodes.items(.tag); 1893 const node = src_loc.declRelativeToNodeIndex(node_off); 1894 const param = switch (node_tags[node]) { 1895 .builtin_call_two, .builtin_call_two_comma => switch (arg_index) { 1896 0 => node_datas[node].lhs, 1897 1 => node_datas[node].rhs, 1898 else => unreachable, 1899 }, 1900 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + arg_index], 1901 else => unreachable, 1902 }; 1903 return tree.nodeToSpan(param); 1904 } 1905 }; 1906 1907 pub const SemaError = error{ OutOfMemory, AnalysisFail }; 1908 pub const CompileError = error{ 1909 OutOfMemory, 1910 /// When this is returned, the compile error for the failure has already been recorded. 1911 AnalysisFail, 1912 /// Returned when a compile error needed to be reported but a provided LazySrcLoc was set 1913 /// to the `unneeded` tag. The source location was, in fact, needed. It is expected that 1914 /// somewhere up the call stack, the operation will be retried after doing expensive work 1915 /// to compute a source location. 1916 NeededSourceLocation, 1917 /// A Type or Value was needed to be used during semantic analysis, but it was not available 1918 /// because the function is generic. This is only seen when analyzing the body of a param 1919 /// instruction. 1920 GenericPoison, 1921 /// In a comptime scope, a return instruction was encountered. This error is only seen when 1922 /// doing a comptime function call. 1923 ComptimeReturn, 1924 /// In a comptime scope, a break instruction was encountered. This error is only seen when 1925 /// evaluating a comptime block. 1926 ComptimeBreak, 1927 }; 1928 1929 pub fn init(mod: *Module) !void { 1930 const gpa = mod.gpa; 1931 try mod.intern_pool.init(gpa); 1932 try mod.global_error_set.put(gpa, .empty, {}); 1933 } 1934 1935 pub fn deinit(zcu: *Zcu) void { 1936 const gpa = zcu.gpa; 1937 1938 if (zcu.llvm_object) |llvm_object| { 1939 if (build_options.only_c) unreachable; 1940 llvm_object.deinit(); 1941 } 1942 1943 for (zcu.import_table.keys()) |key| { 1944 gpa.free(key); 1945 } 1946 var failed_decls = zcu.failed_decls; 1947 zcu.failed_decls = .{}; 1948 for (zcu.import_table.values()) |value| { 1949 value.destroy(zcu); 1950 } 1951 zcu.import_table.deinit(gpa); 1952 1953 for (zcu.embed_table.keys(), zcu.embed_table.values()) |path, embed_file| { 1954 gpa.free(path); 1955 gpa.destroy(embed_file); 1956 } 1957 zcu.embed_table.deinit(gpa); 1958 1959 zcu.compile_log_text.deinit(gpa); 1960 1961 zcu.local_zir_cache.handle.close(); 1962 zcu.global_zir_cache.handle.close(); 1963 1964 for (failed_decls.values()) |value| { 1965 value.destroy(gpa); 1966 } 1967 failed_decls.deinit(gpa); 1968 1969 if (zcu.emit_h) |emit_h| { 1970 for (emit_h.failed_decls.values()) |value| { 1971 value.destroy(gpa); 1972 } 1973 emit_h.failed_decls.deinit(gpa); 1974 emit_h.decl_table.deinit(gpa); 1975 emit_h.allocated_emit_h.deinit(gpa); 1976 } 1977 1978 for (zcu.failed_files.values()) |value| { 1979 if (value) |msg| msg.destroy(gpa); 1980 } 1981 zcu.failed_files.deinit(gpa); 1982 1983 for (zcu.failed_embed_files.values()) |msg| { 1984 msg.destroy(gpa); 1985 } 1986 zcu.failed_embed_files.deinit(gpa); 1987 1988 for (zcu.failed_exports.values()) |value| { 1989 value.destroy(gpa); 1990 } 1991 zcu.failed_exports.deinit(gpa); 1992 1993 for (zcu.cimport_errors.values()) |*errs| { 1994 errs.deinit(gpa); 1995 } 1996 zcu.cimport_errors.deinit(gpa); 1997 1998 zcu.compile_log_decls.deinit(gpa); 1999 2000 for (zcu.decl_exports.values()) |*export_list| { 2001 export_list.deinit(gpa); 2002 } 2003 zcu.decl_exports.deinit(gpa); 2004 2005 for (zcu.value_exports.values()) |*export_list| { 2006 export_list.deinit(gpa); 2007 } 2008 zcu.value_exports.deinit(gpa); 2009 2010 for (zcu.export_owners.values()) |*value| { 2011 freeExportList(gpa, value); 2012 } 2013 zcu.export_owners.deinit(gpa); 2014 2015 zcu.global_error_set.deinit(gpa); 2016 2017 zcu.potentially_outdated.deinit(gpa); 2018 zcu.outdated.deinit(gpa); 2019 zcu.outdated_ready.deinit(gpa); 2020 zcu.outdated_file_root.deinit(gpa); 2021 zcu.retryable_failures.deinit(gpa); 2022 2023 zcu.test_functions.deinit(gpa); 2024 2025 for (zcu.global_assembly.values()) |s| { 2026 gpa.free(s); 2027 } 2028 zcu.global_assembly.deinit(gpa); 2029 2030 zcu.reference_table.deinit(gpa); 2031 2032 { 2033 var it = zcu.intern_pool.allocated_namespaces.iterator(0); 2034 while (it.next()) |namespace| { 2035 namespace.decls.deinit(gpa); 2036 namespace.usingnamespace_set.deinit(gpa); 2037 } 2038 } 2039 2040 zcu.intern_pool.deinit(gpa); 2041 } 2042 2043 pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void { 2044 const gpa = mod.gpa; 2045 const ip = &mod.intern_pool; 2046 2047 { 2048 _ = mod.test_functions.swapRemove(decl_index); 2049 if (mod.global_assembly.fetchSwapRemove(decl_index)) |kv| { 2050 gpa.free(kv.value); 2051 } 2052 } 2053 2054 ip.destroyDecl(gpa, decl_index); 2055 2056 if (mod.emit_h) |mod_emit_h| { 2057 const decl_emit_h = mod_emit_h.declPtr(decl_index); 2058 decl_emit_h.fwd_decl.deinit(gpa); 2059 decl_emit_h.* = undefined; 2060 } 2061 } 2062 2063 pub fn declPtr(mod: *Module, index: Decl.Index) *Decl { 2064 return mod.intern_pool.declPtr(index); 2065 } 2066 2067 pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace { 2068 return mod.intern_pool.namespacePtr(index); 2069 } 2070 2071 pub fn namespacePtrUnwrap(mod: *Module, index: Namespace.OptionalIndex) ?*Namespace { 2072 return mod.namespacePtr(index.unwrap() orelse return null); 2073 } 2074 2075 /// Returns true if and only if the Decl is the top level struct associated with a File. 2076 pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool { 2077 const decl = mod.declPtr(decl_index); 2078 const namespace = mod.namespacePtr(decl.src_namespace); 2079 if (namespace.parent != .none) return false; 2080 return decl_index == namespace.decl_index; 2081 } 2082 2083 fn freeExportList(gpa: Allocator, export_list: *ArrayListUnmanaged(*Export)) void { 2084 for (export_list.items) |exp| gpa.destroy(exp); 2085 export_list.deinit(gpa); 2086 } 2087 2088 // TODO https://github.com/ziglang/zig/issues/8643 2089 const data_has_safety_tag = @sizeOf(Zir.Inst.Data) != 8; 2090 const HackDataLayout = extern struct { 2091 data: [8]u8 align(@alignOf(Zir.Inst.Data)), 2092 safety_tag: u8, 2093 }; 2094 comptime { 2095 if (data_has_safety_tag) { 2096 assert(@sizeOf(HackDataLayout) == @sizeOf(Zir.Inst.Data)); 2097 } 2098 } 2099 2100 pub fn astGenFile(mod: *Module, file: *File) !void { 2101 assert(!file.mod.isBuiltin()); 2102 2103 const tracy = trace(@src()); 2104 defer tracy.end(); 2105 2106 const comp = mod.comp; 2107 const gpa = mod.gpa; 2108 2109 // In any case we need to examine the stat of the file to determine the course of action. 2110 var source_file = try file.mod.root.openFile(file.sub_file_path, .{}); 2111 defer source_file.close(); 2112 2113 const stat = try source_file.stat(); 2114 2115 const want_local_cache = file.mod == mod.main_mod; 2116 const bin_digest = hash: { 2117 var path_hash: Cache.HashHelper = .{}; 2118 path_hash.addBytes(build_options.version); 2119 path_hash.add(builtin.zig_backend); 2120 if (!want_local_cache) { 2121 path_hash.addOptionalBytes(file.mod.root.root_dir.path); 2122 path_hash.addBytes(file.mod.root.sub_path); 2123 } 2124 path_hash.addBytes(file.sub_file_path); 2125 var bin: Cache.BinDigest = undefined; 2126 path_hash.hasher.final(&bin); 2127 break :hash bin; 2128 }; 2129 file.path_digest = bin_digest; 2130 const hex_digest = hex: { 2131 var hex: Cache.HexDigest = undefined; 2132 _ = std.fmt.bufPrint( 2133 &hex, 2134 "{s}", 2135 .{std.fmt.fmtSliceHexLower(&bin_digest)}, 2136 ) catch unreachable; 2137 break :hex hex; 2138 }; 2139 const cache_directory = if (want_local_cache) mod.local_zir_cache else mod.global_zir_cache; 2140 const zir_dir = cache_directory.handle; 2141 2142 // Determine whether we need to reload the file from disk and redo parsing and AstGen. 2143 var lock: std.fs.File.Lock = switch (file.status) { 2144 .never_loaded, .retryable_failure => lock: { 2145 // First, load the cached ZIR code, if any. 2146 log.debug("AstGen checking cache: {s} (local={}, digest={s})", .{ 2147 file.sub_file_path, want_local_cache, &hex_digest, 2148 }); 2149 2150 break :lock .shared; 2151 }, 2152 .parse_failure, .astgen_failure, .success_zir => lock: { 2153 const unchanged_metadata = 2154 stat.size == file.stat.size and 2155 stat.mtime == file.stat.mtime and 2156 stat.inode == file.stat.inode; 2157 2158 if (unchanged_metadata) { 2159 log.debug("unmodified metadata of file: {s}", .{file.sub_file_path}); 2160 return; 2161 } 2162 2163 log.debug("metadata changed: {s}", .{file.sub_file_path}); 2164 2165 break :lock .exclusive; 2166 }, 2167 }; 2168 2169 // We ask for a lock in order to coordinate with other zig processes. 2170 // If another process is already working on this file, we will get the cached 2171 // version. Likewise if we're working on AstGen and another process asks for 2172 // the cached file, they'll get it. 2173 const cache_file = while (true) { 2174 break zir_dir.createFile(&hex_digest, .{ 2175 .read = true, 2176 .truncate = false, 2177 .lock = lock, 2178 }) catch |err| switch (err) { 2179 error.NotDir => unreachable, // no dir components 2180 error.InvalidUtf8 => unreachable, // it's a hex encoded name 2181 error.InvalidWtf8 => unreachable, // it's a hex encoded name 2182 error.BadPathName => unreachable, // it's a hex encoded name 2183 error.NameTooLong => unreachable, // it's a fixed size name 2184 error.PipeBusy => unreachable, // it's not a pipe 2185 error.WouldBlock => unreachable, // not asking for non-blocking I/O 2186 // There are no dir components, so you would think that this was 2187 // unreachable, however we have observed on macOS two processes racing 2188 // to do openat() with O_CREAT manifest in ENOENT. 2189 error.FileNotFound => continue, 2190 2191 else => |e| return e, // Retryable errors are handled at callsite. 2192 }; 2193 }; 2194 defer cache_file.close(); 2195 2196 while (true) { 2197 update: { 2198 // First we read the header to determine the lengths of arrays. 2199 const header = cache_file.reader().readStruct(Zir.Header) catch |err| switch (err) { 2200 // This can happen if Zig bails out of this function between creating 2201 // the cached file and writing it. 2202 error.EndOfStream => break :update, 2203 else => |e| return e, 2204 }; 2205 const unchanged_metadata = 2206 stat.size == header.stat_size and 2207 stat.mtime == header.stat_mtime and 2208 stat.inode == header.stat_inode; 2209 2210 if (!unchanged_metadata) { 2211 log.debug("AstGen cache stale: {s}", .{file.sub_file_path}); 2212 break :update; 2213 } 2214 log.debug("AstGen cache hit: {s} instructions_len={d}", .{ 2215 file.sub_file_path, header.instructions_len, 2216 }); 2217 2218 file.zir = loadZirCacheBody(gpa, header, cache_file) catch |err| switch (err) { 2219 error.UnexpectedFileSize => { 2220 log.warn("unexpected EOF reading cached ZIR for {s}", .{file.sub_file_path}); 2221 break :update; 2222 }, 2223 else => |e| return e, 2224 }; 2225 file.zir_loaded = true; 2226 file.stat = .{ 2227 .size = header.stat_size, 2228 .inode = header.stat_inode, 2229 .mtime = header.stat_mtime, 2230 }; 2231 file.status = .success_zir; 2232 log.debug("AstGen cached success: {s}", .{file.sub_file_path}); 2233 2234 // TODO don't report compile errors until Sema @importFile 2235 if (file.zir.hasCompileErrors()) { 2236 { 2237 comp.mutex.lock(); 2238 defer comp.mutex.unlock(); 2239 try mod.failed_files.putNoClobber(gpa, file, null); 2240 } 2241 file.status = .astgen_failure; 2242 return error.AnalysisFail; 2243 } 2244 return; 2245 } 2246 2247 // If we already have the exclusive lock then it is our job to update. 2248 if (builtin.os.tag == .wasi or lock == .exclusive) break; 2249 // Otherwise, unlock to give someone a chance to get the exclusive lock 2250 // and then upgrade to an exclusive lock. 2251 cache_file.unlock(); 2252 lock = .exclusive; 2253 try cache_file.lock(lock); 2254 } 2255 2256 // The cache is definitely stale so delete the contents to avoid an underwrite later. 2257 cache_file.setEndPos(0) catch |err| switch (err) { 2258 error.FileTooBig => unreachable, // 0 is not too big 2259 2260 else => |e| return e, 2261 }; 2262 2263 mod.lockAndClearFileCompileError(file); 2264 2265 // If the previous ZIR does not have compile errors, keep it around 2266 // in case parsing or new ZIR fails. In case of successful ZIR update 2267 // at the end of this function we will free it. 2268 // We keep the previous ZIR loaded so that we can use it 2269 // for the update next time it does not have any compile errors. This avoids 2270 // needlessly tossing out semantic analysis work when an error is 2271 // temporarily introduced. 2272 if (file.zir_loaded and !file.zir.hasCompileErrors()) { 2273 assert(file.prev_zir == null); 2274 const prev_zir_ptr = try gpa.create(Zir); 2275 file.prev_zir = prev_zir_ptr; 2276 prev_zir_ptr.* = file.zir; 2277 file.zir = undefined; 2278 file.zir_loaded = false; 2279 } 2280 file.unload(gpa); 2281 2282 if (stat.size > std.math.maxInt(u32)) 2283 return error.FileTooBig; 2284 2285 const source = try gpa.allocSentinel(u8, @as(usize, @intCast(stat.size)), 0); 2286 defer if (!file.source_loaded) gpa.free(source); 2287 const amt = try source_file.readAll(source); 2288 if (amt != stat.size) 2289 return error.UnexpectedEndOfFile; 2290 2291 file.stat = .{ 2292 .size = stat.size, 2293 .inode = stat.inode, 2294 .mtime = stat.mtime, 2295 }; 2296 file.source = source; 2297 file.source_loaded = true; 2298 2299 file.tree = try Ast.parse(gpa, source, .zig); 2300 file.tree_loaded = true; 2301 2302 // Any potential AST errors are converted to ZIR errors here. 2303 file.zir = try AstGen.generate(gpa, file.tree); 2304 file.zir_loaded = true; 2305 file.status = .success_zir; 2306 log.debug("AstGen fresh success: {s}", .{file.sub_file_path}); 2307 2308 const safety_buffer = if (data_has_safety_tag) 2309 try gpa.alloc([8]u8, file.zir.instructions.len) 2310 else 2311 undefined; 2312 defer if (data_has_safety_tag) gpa.free(safety_buffer); 2313 const data_ptr = if (data_has_safety_tag) 2314 if (file.zir.instructions.len == 0) 2315 @as([*]const u8, undefined) 2316 else 2317 @as([*]const u8, @ptrCast(safety_buffer.ptr)) 2318 else 2319 @as([*]const u8, @ptrCast(file.zir.instructions.items(.data).ptr)); 2320 if (data_has_safety_tag) { 2321 // The `Data` union has a safety tag but in the file format we store it without. 2322 for (file.zir.instructions.items(.data), 0..) |*data, i| { 2323 const as_struct = @as(*const HackDataLayout, @ptrCast(data)); 2324 safety_buffer[i] = as_struct.data; 2325 } 2326 } 2327 2328 const header: Zir.Header = .{ 2329 .instructions_len = @as(u32, @intCast(file.zir.instructions.len)), 2330 .string_bytes_len = @as(u32, @intCast(file.zir.string_bytes.len)), 2331 .extra_len = @as(u32, @intCast(file.zir.extra.len)), 2332 2333 .stat_size = stat.size, 2334 .stat_inode = stat.inode, 2335 .stat_mtime = stat.mtime, 2336 }; 2337 var iovecs = [_]std.posix.iovec_const{ 2338 .{ 2339 .iov_base = @as([*]const u8, @ptrCast(&header)), 2340 .iov_len = @sizeOf(Zir.Header), 2341 }, 2342 .{ 2343 .iov_base = @as([*]const u8, @ptrCast(file.zir.instructions.items(.tag).ptr)), 2344 .iov_len = file.zir.instructions.len, 2345 }, 2346 .{ 2347 .iov_base = data_ptr, 2348 .iov_len = file.zir.instructions.len * 8, 2349 }, 2350 .{ 2351 .iov_base = file.zir.string_bytes.ptr, 2352 .iov_len = file.zir.string_bytes.len, 2353 }, 2354 .{ 2355 .iov_base = @as([*]const u8, @ptrCast(file.zir.extra.ptr)), 2356 .iov_len = file.zir.extra.len * 4, 2357 }, 2358 }; 2359 cache_file.writevAll(&iovecs) catch |err| { 2360 log.warn("unable to write cached ZIR code for {}{s} to {}{s}: {s}", .{ 2361 file.mod.root, file.sub_file_path, cache_directory, &hex_digest, @errorName(err), 2362 }); 2363 }; 2364 2365 if (file.zir.hasCompileErrors()) { 2366 { 2367 comp.mutex.lock(); 2368 defer comp.mutex.unlock(); 2369 try mod.failed_files.putNoClobber(gpa, file, null); 2370 } 2371 file.status = .astgen_failure; 2372 return error.AnalysisFail; 2373 } 2374 2375 if (file.prev_zir) |prev_zir| { 2376 try updateZirRefs(mod, file, prev_zir.*); 2377 // No need to keep previous ZIR. 2378 prev_zir.deinit(gpa); 2379 gpa.destroy(prev_zir); 2380 file.prev_zir = null; 2381 } 2382 2383 if (file.root_decl.unwrap()) |root_decl| { 2384 // The root of this file must be re-analyzed, since the file has changed. 2385 comp.mutex.lock(); 2386 defer comp.mutex.unlock(); 2387 2388 log.debug("outdated root Decl: {}", .{root_decl}); 2389 try mod.outdated_file_root.put(gpa, root_decl, {}); 2390 } 2391 } 2392 2393 pub fn loadZirCache(gpa: Allocator, cache_file: std.fs.File) !Zir { 2394 return loadZirCacheBody(gpa, try cache_file.reader().readStruct(Zir.Header), cache_file); 2395 } 2396 2397 fn loadZirCacheBody(gpa: Allocator, header: Zir.Header, cache_file: std.fs.File) !Zir { 2398 var instructions: std.MultiArrayList(Zir.Inst) = .{}; 2399 errdefer instructions.deinit(gpa); 2400 2401 try instructions.setCapacity(gpa, header.instructions_len); 2402 instructions.len = header.instructions_len; 2403 2404 var zir: Zir = .{ 2405 .instructions = instructions.toOwnedSlice(), 2406 .string_bytes = &.{}, 2407 .extra = &.{}, 2408 }; 2409 errdefer zir.deinit(gpa); 2410 2411 zir.string_bytes = try gpa.alloc(u8, header.string_bytes_len); 2412 zir.extra = try gpa.alloc(u32, header.extra_len); 2413 2414 const safety_buffer = if (data_has_safety_tag) 2415 try gpa.alloc([8]u8, header.instructions_len) 2416 else 2417 undefined; 2418 defer if (data_has_safety_tag) gpa.free(safety_buffer); 2419 2420 const data_ptr = if (data_has_safety_tag) 2421 @as([*]u8, @ptrCast(safety_buffer.ptr)) 2422 else 2423 @as([*]u8, @ptrCast(zir.instructions.items(.data).ptr)); 2424 2425 var iovecs = [_]std.posix.iovec{ 2426 .{ 2427 .iov_base = @as([*]u8, @ptrCast(zir.instructions.items(.tag).ptr)), 2428 .iov_len = header.instructions_len, 2429 }, 2430 .{ 2431 .iov_base = data_ptr, 2432 .iov_len = header.instructions_len * 8, 2433 }, 2434 .{ 2435 .iov_base = zir.string_bytes.ptr, 2436 .iov_len = header.string_bytes_len, 2437 }, 2438 .{ 2439 .iov_base = @as([*]u8, @ptrCast(zir.extra.ptr)), 2440 .iov_len = header.extra_len * 4, 2441 }, 2442 }; 2443 const amt_read = try cache_file.readvAll(&iovecs); 2444 const amt_expected = zir.instructions.len * 9 + 2445 zir.string_bytes.len + 2446 zir.extra.len * 4; 2447 if (amt_read != amt_expected) return error.UnexpectedFileSize; 2448 if (data_has_safety_tag) { 2449 const tags = zir.instructions.items(.tag); 2450 for (zir.instructions.items(.data), 0..) |*data, i| { 2451 const union_tag = Zir.Inst.Tag.data_tags[@intFromEnum(tags[i])]; 2452 const as_struct = @as(*HackDataLayout, @ptrCast(data)); 2453 as_struct.* = .{ 2454 .safety_tag = @intFromEnum(union_tag), 2455 .data = safety_buffer[i], 2456 }; 2457 } 2458 } 2459 2460 return zir; 2461 } 2462 2463 /// This is called from the AstGen thread pool, so must acquire 2464 /// the Compilation mutex when acting on shared state. 2465 fn updateZirRefs(zcu: *Module, file: *File, old_zir: Zir) !void { 2466 const gpa = zcu.gpa; 2467 const new_zir = file.zir; 2468 2469 var inst_map: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{}; 2470 defer inst_map.deinit(gpa); 2471 2472 try mapOldZirToNew(gpa, old_zir, new_zir, &inst_map); 2473 2474 const old_tag = old_zir.instructions.items(.tag); 2475 const old_data = old_zir.instructions.items(.data); 2476 2477 // TODO: this should be done after all AstGen workers complete, to avoid 2478 // iterating over this full set for every updated file. 2479 for (zcu.intern_pool.tracked_insts.keys(), 0..) |*ti, idx_raw| { 2480 const ti_idx: InternPool.TrackedInst.Index = @enumFromInt(idx_raw); 2481 if (!std.mem.eql(u8, &ti.path_digest, &file.path_digest)) continue; 2482 const old_inst = ti.inst; 2483 ti.inst = inst_map.get(ti.inst) orelse { 2484 // Tracking failed for this instruction. Invalidate associated `src_hash` deps. 2485 zcu.comp.mutex.lock(); 2486 defer zcu.comp.mutex.unlock(); 2487 log.debug("tracking failed for %{d}", .{old_inst}); 2488 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx }); 2489 continue; 2490 }; 2491 2492 if (old_zir.getAssociatedSrcHash(old_inst)) |old_hash| hash_changed: { 2493 if (new_zir.getAssociatedSrcHash(ti.inst)) |new_hash| { 2494 if (std.zig.srcHashEql(old_hash, new_hash)) { 2495 break :hash_changed; 2496 } 2497 log.debug("hash for (%{d} -> %{d}) changed: {} -> {}", .{ 2498 old_inst, 2499 ti.inst, 2500 std.fmt.fmtSliceHexLower(&old_hash), 2501 std.fmt.fmtSliceHexLower(&new_hash), 2502 }); 2503 } 2504 // The source hash associated with this instruction changed - invalidate relevant dependencies. 2505 zcu.comp.mutex.lock(); 2506 defer zcu.comp.mutex.unlock(); 2507 try zcu.markDependeeOutdated(.{ .src_hash = ti_idx }); 2508 } 2509 2510 // If this is a `struct_decl` etc, we must invalidate any outdated namespace dependencies. 2511 const has_namespace = switch (old_tag[@intFromEnum(old_inst)]) { 2512 .extended => switch (old_data[@intFromEnum(old_inst)].extended.opcode) { 2513 .struct_decl, .union_decl, .opaque_decl, .enum_decl => true, 2514 else => false, 2515 }, 2516 else => false, 2517 }; 2518 if (!has_namespace) continue; 2519 2520 var old_names: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; 2521 defer old_names.deinit(zcu.gpa); 2522 { 2523 var it = old_zir.declIterator(old_inst); 2524 while (it.next()) |decl_inst| { 2525 const decl_name = old_zir.getDeclaration(decl_inst)[0].name; 2526 switch (decl_name) { 2527 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, 2528 _ => if (decl_name.isNamedTest(old_zir)) continue, 2529 } 2530 const name_zir = decl_name.toString(old_zir).?; 2531 const name_ip = try zcu.intern_pool.getOrPutString( 2532 zcu.gpa, 2533 old_zir.nullTerminatedString(name_zir), 2534 .no_embedded_nulls, 2535 ); 2536 try old_names.put(zcu.gpa, name_ip, {}); 2537 } 2538 } 2539 var any_change = false; 2540 { 2541 var it = new_zir.declIterator(ti.inst); 2542 while (it.next()) |decl_inst| { 2543 const decl_name = old_zir.getDeclaration(decl_inst)[0].name; 2544 switch (decl_name) { 2545 .@"comptime", .@"usingnamespace", .unnamed_test, .decltest => continue, 2546 _ => if (decl_name.isNamedTest(old_zir)) continue, 2547 } 2548 const name_zir = decl_name.toString(old_zir).?; 2549 const name_ip = try zcu.intern_pool.getOrPutString( 2550 zcu.gpa, 2551 old_zir.nullTerminatedString(name_zir), 2552 .no_embedded_nulls, 2553 ); 2554 if (!old_names.swapRemove(name_ip)) continue; 2555 // Name added 2556 any_change = true; 2557 zcu.comp.mutex.lock(); 2558 defer zcu.comp.mutex.unlock(); 2559 try zcu.markDependeeOutdated(.{ .namespace_name = .{ 2560 .namespace = ti_idx, 2561 .name = name_ip, 2562 } }); 2563 } 2564 } 2565 // The only elements remaining in `old_names` now are any names which were removed. 2566 for (old_names.keys()) |name_ip| { 2567 any_change = true; 2568 zcu.comp.mutex.lock(); 2569 defer zcu.comp.mutex.unlock(); 2570 try zcu.markDependeeOutdated(.{ .namespace_name = .{ 2571 .namespace = ti_idx, 2572 .name = name_ip, 2573 } }); 2574 } 2575 2576 if (any_change) { 2577 zcu.comp.mutex.lock(); 2578 defer zcu.comp.mutex.unlock(); 2579 try zcu.markDependeeOutdated(.{ .namespace = ti_idx }); 2580 } 2581 } 2582 } 2583 2584 pub fn markDependeeOutdated(zcu: *Zcu, dependee: InternPool.Dependee) !void { 2585 log.debug("outdated dependee: {}", .{dependee}); 2586 var it = zcu.intern_pool.dependencyIterator(dependee); 2587 while (it.next()) |depender| { 2588 if (zcu.outdated.contains(depender)) { 2589 // We do not need to increment the PO dep count, as if the outdated 2590 // dependee is a Decl, we had already marked this as PO. 2591 continue; 2592 } 2593 const opt_po_entry = zcu.potentially_outdated.fetchSwapRemove(depender); 2594 try zcu.outdated.putNoClobber( 2595 zcu.gpa, 2596 depender, 2597 // We do not need to increment this count for the same reason as above. 2598 if (opt_po_entry) |e| e.value else 0, 2599 ); 2600 log.debug("outdated: {}", .{depender}); 2601 if (opt_po_entry == null) { 2602 // This is a new entry with no PO dependencies. 2603 try zcu.outdated_ready.put(zcu.gpa, depender, {}); 2604 } 2605 // If this is a Decl and was not previously PO, we must recursively 2606 // mark dependencies on its tyval as PO. 2607 if (opt_po_entry == null) { 2608 try zcu.markTransitiveDependersPotentiallyOutdated(depender); 2609 } 2610 } 2611 } 2612 2613 fn markPoDependeeUpToDate(zcu: *Zcu, dependee: InternPool.Dependee) !void { 2614 var it = zcu.intern_pool.dependencyIterator(dependee); 2615 while (it.next()) |depender| { 2616 if (zcu.outdated.getPtr(depender)) |po_dep_count| { 2617 // This depender is already outdated, but it now has one 2618 // less PO dependency! 2619 po_dep_count.* -= 1; 2620 if (po_dep_count.* == 0) { 2621 try zcu.outdated_ready.put(zcu.gpa, depender, {}); 2622 } 2623 continue; 2624 } 2625 // This depender is definitely at least PO, because this Decl was just analyzed 2626 // due to being outdated. 2627 const ptr = zcu.potentially_outdated.getPtr(depender).?; 2628 if (ptr.* > 1) { 2629 ptr.* -= 1; 2630 continue; 2631 } 2632 2633 // This dependency is no longer PO, i.e. is known to be up-to-date. 2634 assert(zcu.potentially_outdated.swapRemove(depender)); 2635 // If this is a Decl, we must recursively mark dependencies on its tyval 2636 // as no longer PO. 2637 switch (depender.unwrap()) { 2638 .decl => |decl_index| try zcu.markPoDependeeUpToDate(.{ .decl_val = decl_index }), 2639 .func => |func_index| try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }), 2640 } 2641 } 2642 } 2643 2644 /// Given a Depender which is newly outdated or PO, mark all Dependers which may 2645 /// in turn be PO, due to a dependency on the original Depender's tyval or IES. 2646 fn markTransitiveDependersPotentiallyOutdated(zcu: *Zcu, maybe_outdated: InternPool.Depender) !void { 2647 var it = zcu.intern_pool.dependencyIterator(switch (maybe_outdated.unwrap()) { 2648 .decl => |decl_index| .{ .decl_val = decl_index }, // TODO: also `decl_ref` deps when introduced 2649 .func => |func_index| .{ .func_ies = func_index }, 2650 }); 2651 2652 while (it.next()) |po| { 2653 if (zcu.outdated.getPtr(po)) |po_dep_count| { 2654 // This dependency is already outdated, but it now has one more PO 2655 // dependency. 2656 if (po_dep_count.* == 0) { 2657 _ = zcu.outdated_ready.swapRemove(po); 2658 } 2659 po_dep_count.* += 1; 2660 continue; 2661 } 2662 if (zcu.potentially_outdated.getPtr(po)) |n| { 2663 // There is now one more PO dependency. 2664 n.* += 1; 2665 continue; 2666 } 2667 try zcu.potentially_outdated.putNoClobber(zcu.gpa, po, 1); 2668 // This Depender was not already PO, so we must recursively mark its dependers as also PO. 2669 try zcu.markTransitiveDependersPotentiallyOutdated(po); 2670 } 2671 } 2672 2673 pub fn findOutdatedToAnalyze(zcu: *Zcu) Allocator.Error!?InternPool.Depender { 2674 if (!zcu.comp.debug_incremental) return null; 2675 2676 if (zcu.outdated.count() == 0 and zcu.potentially_outdated.count() == 0) { 2677 log.debug("findOutdatedToAnalyze: no outdated depender", .{}); 2678 return null; 2679 } 2680 2681 // Our goal is to find an outdated Depender which itself has no outdated or 2682 // PO dependencies. Most of the time, such a Depender will exist - we track 2683 // them in the `outdated_ready` set for efficiency. However, this is not 2684 // necessarily the case, since the Decl dependency graph may contain loops 2685 // via mutually recursive definitions: 2686 // pub const A = struct { b: *B }; 2687 // pub const B = struct { b: *A }; 2688 // In this case, we must defer to more complex logic below. 2689 2690 if (zcu.outdated_ready.count() > 0) { 2691 log.debug("findOutdatedToAnalyze: trivial '{s} {d}'", .{ 2692 @tagName(zcu.outdated_ready.keys()[0].unwrap()), 2693 switch (zcu.outdated_ready.keys()[0].unwrap()) { 2694 inline else => |x| @intFromEnum(x), 2695 }, 2696 }); 2697 return zcu.outdated_ready.keys()[0]; 2698 } 2699 2700 // Next, we will see if there is any outdated file root which was not in 2701 // `outdated`. This set will be small (number of files changed in this 2702 // update), so it's alright for us to just iterate here. 2703 for (zcu.outdated_file_root.keys()) |file_decl| { 2704 const decl_depender = InternPool.Depender.wrap(.{ .decl = file_decl }); 2705 if (zcu.outdated.contains(decl_depender)) { 2706 // Since we didn't hit this in the first loop, this Decl must have 2707 // pending dependencies, so is ineligible. 2708 continue; 2709 } 2710 if (zcu.potentially_outdated.contains(decl_depender)) { 2711 // This Decl's struct may or may not need to be recreated depending 2712 // on whether it is outdated. If we analyzed it now, we would have 2713 // to assume it was outdated and recreate it! 2714 continue; 2715 } 2716 log.debug("findOutdatedToAnalyze: outdated file root decl '{d}'", .{file_decl}); 2717 return decl_depender; 2718 } 2719 2720 // There is no single Depender which is ready for re-analysis. Instead, we 2721 // must assume that some Decl with PO dependencies is outdated - e.g. in the 2722 // above example we arbitrarily pick one of A or B. We should select a Decl, 2723 // since a Decl is definitely responsible for the loop in the dependency 2724 // graph (since you can't depend on a runtime function analysis!). 2725 2726 // The choice of this Decl could have a big impact on how much total 2727 // analysis we perform, since if analysis concludes its tyval is unchanged, 2728 // then other PO Dependers may be resolved as up-to-date. To hopefully avoid 2729 // doing too much work, let's find a Decl which the most things depend on - 2730 // the idea is that this will resolve a lot of loops (but this is only a 2731 // heuristic). 2732 2733 log.debug("findOutdatedToAnalyze: no trivial ready, using heuristic; {d} outdated, {d} PO", .{ 2734 zcu.outdated.count(), 2735 zcu.potentially_outdated.count(), 2736 }); 2737 2738 var chosen_decl_idx: ?Decl.Index = null; 2739 var chosen_decl_dependers: u32 = undefined; 2740 2741 for (zcu.outdated.keys()) |depender| { 2742 const decl_index = switch (depender.unwrap()) { 2743 .decl => |d| d, 2744 .func => continue, 2745 }; 2746 2747 var n: u32 = 0; 2748 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); 2749 while (it.next()) |_| n += 1; 2750 2751 if (chosen_decl_idx == null or n > chosen_decl_dependers) { 2752 chosen_decl_idx = decl_index; 2753 chosen_decl_dependers = n; 2754 } 2755 } 2756 2757 for (zcu.potentially_outdated.keys()) |depender| { 2758 const decl_index = switch (depender.unwrap()) { 2759 .decl => |d| d, 2760 .func => continue, 2761 }; 2762 2763 var n: u32 = 0; 2764 var it = zcu.intern_pool.dependencyIterator(.{ .decl_val = decl_index }); 2765 while (it.next()) |_| n += 1; 2766 2767 if (chosen_decl_idx == null or n > chosen_decl_dependers) { 2768 chosen_decl_idx = decl_index; 2769 chosen_decl_dependers = n; 2770 } 2771 } 2772 2773 log.debug("findOutdatedToAnalyze: heuristic returned Decl {d} ({d} dependers)", .{ 2774 chosen_decl_idx.?, 2775 chosen_decl_dependers, 2776 }); 2777 2778 return InternPool.Depender.wrap(.{ .decl = chosen_decl_idx.? }); 2779 } 2780 2781 /// During an incremental update, before semantic analysis, call this to flush all values from 2782 /// `retryable_failures` and mark them as outdated so they get re-analyzed. 2783 pub fn flushRetryableFailures(zcu: *Zcu) !void { 2784 const gpa = zcu.gpa; 2785 for (zcu.retryable_failures.items) |depender| { 2786 if (zcu.outdated.contains(depender)) continue; 2787 if (zcu.potentially_outdated.fetchSwapRemove(depender)) |kv| { 2788 // This Depender was already PO, but we now consider it outdated. 2789 // Any transitive dependencies are already marked PO. 2790 try zcu.outdated.put(gpa, depender, kv.value); 2791 continue; 2792 } 2793 // This Depender was not marked PO, but is now outdated. Mark it as 2794 // such, then recursively mark transitive dependencies as PO. 2795 try zcu.outdated.put(gpa, depender, 0); 2796 try zcu.markTransitiveDependersPotentiallyOutdated(depender); 2797 } 2798 zcu.retryable_failures.clearRetainingCapacity(); 2799 } 2800 2801 pub fn mapOldZirToNew( 2802 gpa: Allocator, 2803 old_zir: Zir, 2804 new_zir: Zir, 2805 inst_map: *std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index), 2806 ) Allocator.Error!void { 2807 // Contain ZIR indexes of namespace declaration instructions, e.g. struct_decl, union_decl, etc. 2808 // Not `declaration`, as this does not create a namespace. 2809 const MatchedZirDecl = struct { 2810 old_inst: Zir.Inst.Index, 2811 new_inst: Zir.Inst.Index, 2812 }; 2813 var match_stack: ArrayListUnmanaged(MatchedZirDecl) = .{}; 2814 defer match_stack.deinit(gpa); 2815 2816 // Main struct inst is always matched 2817 try match_stack.append(gpa, .{ 2818 .old_inst = .main_struct_inst, 2819 .new_inst = .main_struct_inst, 2820 }); 2821 2822 // Used as temporary buffers for namespace declaration instructions 2823 var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa); 2824 defer old_decls.deinit(); 2825 var new_decls = std.ArrayList(Zir.Inst.Index).init(gpa); 2826 defer new_decls.deinit(); 2827 2828 while (match_stack.popOrNull()) |match_item| { 2829 // Match the namespace declaration itself 2830 try inst_map.put(gpa, match_item.old_inst, match_item.new_inst); 2831 2832 // Maps decl name to `declaration` instruction. 2833 var named_decls: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{}; 2834 defer named_decls.deinit(gpa); 2835 // Maps test name to `declaration` instruction. 2836 var named_tests: std.StringHashMapUnmanaged(Zir.Inst.Index) = .{}; 2837 defer named_tests.deinit(gpa); 2838 // All unnamed tests, in order, for a best-effort match. 2839 var unnamed_tests: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; 2840 defer unnamed_tests.deinit(gpa); 2841 // All comptime declarations, in order, for a best-effort match. 2842 var comptime_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; 2843 defer comptime_decls.deinit(gpa); 2844 // All usingnamespace declarations, in order, for a best-effort match. 2845 var usingnamespace_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .{}; 2846 defer usingnamespace_decls.deinit(gpa); 2847 2848 { 2849 var old_decl_it = old_zir.declIterator(match_item.old_inst); 2850 while (old_decl_it.next()) |old_decl_inst| { 2851 const old_decl, _ = old_zir.getDeclaration(old_decl_inst); 2852 switch (old_decl.name) { 2853 .@"comptime" => try comptime_decls.append(gpa, old_decl_inst), 2854 .@"usingnamespace" => try usingnamespace_decls.append(gpa, old_decl_inst), 2855 .unnamed_test, .decltest => try unnamed_tests.append(gpa, old_decl_inst), 2856 _ => { 2857 const name_nts = old_decl.name.toString(old_zir).?; 2858 const name = old_zir.nullTerminatedString(name_nts); 2859 if (old_decl.name.isNamedTest(old_zir)) { 2860 try named_tests.put(gpa, name, old_decl_inst); 2861 } else { 2862 try named_decls.put(gpa, name, old_decl_inst); 2863 } 2864 }, 2865 } 2866 } 2867 } 2868 2869 var unnamed_test_idx: u32 = 0; 2870 var comptime_decl_idx: u32 = 0; 2871 var usingnamespace_decl_idx: u32 = 0; 2872 2873 var new_decl_it = new_zir.declIterator(match_item.new_inst); 2874 while (new_decl_it.next()) |new_decl_inst| { 2875 const new_decl, _ = new_zir.getDeclaration(new_decl_inst); 2876 // Attempt to match this to a declaration in the old ZIR: 2877 // * For named declarations (`const`/`var`/`fn`), we match based on name. 2878 // * For named tests (`test "foo"`), we also match based on name. 2879 // * For unnamed tests and decltests, we match based on order. 2880 // * For comptime blocks, we match based on order. 2881 // * For usingnamespace decls, we match based on order. 2882 // If we cannot match this declaration, we can't match anything nested inside of it either, so we just `continue`. 2883 const old_decl_inst = switch (new_decl.name) { 2884 .@"comptime" => inst: { 2885 if (comptime_decl_idx == comptime_decls.items.len) continue; 2886 defer comptime_decl_idx += 1; 2887 break :inst comptime_decls.items[comptime_decl_idx]; 2888 }, 2889 .@"usingnamespace" => inst: { 2890 if (usingnamespace_decl_idx == usingnamespace_decls.items.len) continue; 2891 defer usingnamespace_decl_idx += 1; 2892 break :inst usingnamespace_decls.items[usingnamespace_decl_idx]; 2893 }, 2894 .unnamed_test, .decltest => inst: { 2895 if (unnamed_test_idx == unnamed_tests.items.len) continue; 2896 defer unnamed_test_idx += 1; 2897 break :inst unnamed_tests.items[unnamed_test_idx]; 2898 }, 2899 _ => inst: { 2900 const name_nts = new_decl.name.toString(old_zir).?; 2901 const name = new_zir.nullTerminatedString(name_nts); 2902 if (new_decl.name.isNamedTest(new_zir)) { 2903 break :inst named_tests.get(name) orelse continue; 2904 } else { 2905 break :inst named_decls.get(name) orelse continue; 2906 } 2907 }, 2908 }; 2909 2910 // Match the `declaration` instruction 2911 try inst_map.put(gpa, old_decl_inst, new_decl_inst); 2912 2913 // Find namespace declarations within this declaration 2914 try old_zir.findDecls(&old_decls, old_decl_inst); 2915 try new_zir.findDecls(&new_decls, new_decl_inst); 2916 2917 // We don't have any smart way of matching up these namespace declarations, so we always 2918 // correlate them based on source order. 2919 const n = @min(old_decls.items.len, new_decls.items.len); 2920 try match_stack.ensureUnusedCapacity(gpa, n); 2921 for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| { 2922 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst }); 2923 } 2924 } 2925 } 2926 } 2927 2928 /// Like `ensureDeclAnalyzed`, but the Decl is a file's root Decl. 2929 pub fn ensureFileAnalyzed(zcu: *Zcu, file: *File) SemaError!void { 2930 if (file.root_decl.unwrap()) |existing_root| { 2931 return zcu.ensureDeclAnalyzed(existing_root); 2932 } else { 2933 return zcu.semaFile(file); 2934 } 2935 } 2936 2937 /// This ensures that the Decl will have an up-to-date Type and Value populated. 2938 /// However the resolution status of the Type may not be fully resolved. 2939 /// For example an inferred error set is not resolved until after `analyzeFnBody`. 2940 /// is called. 2941 pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void { 2942 const tracy = trace(@src()); 2943 defer tracy.end(); 2944 2945 const decl = mod.declPtr(decl_index); 2946 2947 log.debug("ensureDeclAnalyzed '{d}' (name '{}')", .{ 2948 @intFromEnum(decl_index), 2949 decl.name.fmt(&mod.intern_pool), 2950 }); 2951 2952 // Determine whether or not this Decl is outdated, i.e. requires re-analysis 2953 // even if `complete`. If a Decl is PO, we pessismistically assume that it 2954 // *does* require re-analysis, to ensure that the Decl is definitely 2955 // up-to-date when this function returns. 2956 2957 // If analysis occurs in a poor order, this could result in over-analysis. 2958 // We do our best to avoid this by the other dependency logic in this file 2959 // which tries to limit re-analysis to Decls whose previously listed 2960 // dependencies are all up-to-date. 2961 2962 const decl_as_depender = InternPool.Depender.wrap(.{ .decl = decl_index }); 2963 const decl_was_outdated = mod.outdated.swapRemove(decl_as_depender) or 2964 mod.potentially_outdated.swapRemove(decl_as_depender); 2965 2966 if (decl_was_outdated) { 2967 _ = mod.outdated_ready.swapRemove(decl_as_depender); 2968 } 2969 2970 const was_outdated = mod.outdated_file_root.swapRemove(decl_index) or decl_was_outdated; 2971 2972 switch (decl.analysis) { 2973 .in_progress => unreachable, 2974 2975 .file_failure => return error.AnalysisFail, 2976 2977 .sema_failure, 2978 .dependency_failure, 2979 .codegen_failure, 2980 => if (!was_outdated) return error.AnalysisFail, 2981 2982 .complete => if (!was_outdated) return, 2983 2984 .unreferenced => {}, 2985 } 2986 2987 if (was_outdated) { 2988 // The exports this Decl performs will be re-discovered, so we remove them here 2989 // prior to re-analysis. 2990 if (build_options.only_c) unreachable; 2991 try mod.deleteDeclExports(decl_index); 2992 } 2993 2994 var decl_prog_node = mod.sema_prog_node.start("", 0); 2995 decl_prog_node.activate(); 2996 defer decl_prog_node.end(); 2997 2998 const sema_result: SemaDeclResult = blk: { 2999 if (decl.zir_decl_index == .none and !mod.declIsRoot(decl_index)) { 3000 // Anonymous decl. We don't semantically analyze these. 3001 break :blk .{ 3002 .invalidate_decl_val = false, 3003 .invalidate_decl_ref = false, 3004 }; 3005 } 3006 3007 if (mod.declIsRoot(decl_index)) { 3008 const changed = try mod.semaFileUpdate(decl.getFileScope(mod), decl_was_outdated); 3009 break :blk .{ 3010 .invalidate_decl_val = changed, 3011 .invalidate_decl_ref = changed, 3012 }; 3013 } 3014 3015 break :blk mod.semaDecl(decl_index) catch |err| switch (err) { 3016 error.AnalysisFail => { 3017 if (decl.analysis == .in_progress) { 3018 // If this decl caused the compile error, the analysis field would 3019 // be changed to indicate it was this Decl's fault. Because this 3020 // did not happen, we infer here that it was a dependency failure. 3021 decl.analysis = .dependency_failure; 3022 } 3023 return error.AnalysisFail; 3024 }, 3025 error.NeededSourceLocation => unreachable, 3026 error.GenericPoison => unreachable, 3027 else => |e| { 3028 decl.analysis = .sema_failure; 3029 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1); 3030 try mod.retryable_failures.append(mod.gpa, InternPool.Depender.wrap(.{ .decl = decl_index })); 3031 mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( 3032 mod.gpa, 3033 decl.srcLoc(mod), 3034 "unable to analyze: {s}", 3035 .{@errorName(e)}, 3036 )); 3037 return error.AnalysisFail; 3038 }, 3039 }; 3040 }; 3041 3042 // TODO: we do not yet have separate dependencies for decl values vs types. 3043 if (decl_was_outdated) { 3044 if (sema_result.invalidate_decl_val or sema_result.invalidate_decl_ref) { 3045 log.debug("Decl tv invalidated ('{d}')", .{@intFromEnum(decl_index)}); 3046 // This dependency was marked as PO, meaning dependees were waiting 3047 // on its analysis result, and it has turned out to be outdated. 3048 // Update dependees accordingly. 3049 try mod.markDependeeOutdated(.{ .decl_val = decl_index }); 3050 } else { 3051 log.debug("Decl tv up-to-date ('{d}')", .{@intFromEnum(decl_index)}); 3052 // This dependency was previously PO, but turned out to be up-to-date. 3053 // We do not need to queue successive analysis. 3054 try mod.markPoDependeeUpToDate(.{ .decl_val = decl_index }); 3055 } 3056 } 3057 } 3058 3059 pub fn ensureFuncBodyAnalyzed(zcu: *Zcu, maybe_coerced_func_index: InternPool.Index) SemaError!void { 3060 const tracy = trace(@src()); 3061 defer tracy.end(); 3062 3063 const gpa = zcu.gpa; 3064 const ip = &zcu.intern_pool; 3065 3066 // We only care about the uncoerced function. 3067 // We need to do this for the "orphaned function" check below to be valid. 3068 const func_index = ip.unwrapCoercedFunc(maybe_coerced_func_index); 3069 3070 const func = zcu.funcInfo(maybe_coerced_func_index); 3071 const decl_index = func.owner_decl; 3072 const decl = zcu.declPtr(decl_index); 3073 3074 log.debug("ensureFuncBodyAnalyzed '{d}' (instance of '{}')", .{ 3075 @intFromEnum(func_index), 3076 decl.name.fmt(ip), 3077 }); 3078 3079 // First, our owner decl must be up-to-date. This will always be the case 3080 // during the first update, but may not on successive updates if we happen 3081 // to get analyzed before our parent decl. 3082 try zcu.ensureDeclAnalyzed(decl_index); 3083 3084 // On an update, it's possible this function changed such that our owner 3085 // decl now refers to a different function, making this one orphaned. If 3086 // that's the case, we should remove this function from the binary. 3087 if (decl.val.ip_index != func_index) { 3088 try zcu.markDependeeOutdated(.{ .func_ies = func_index }); 3089 ip.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index })); 3090 ip.remove(func_index); 3091 @panic("TODO: remove orphaned function from binary"); 3092 } 3093 3094 // We'll want to remember what the IES used to be before the update for 3095 // dependency invalidation purposes. 3096 const old_resolved_ies = if (func.analysis(ip).inferred_error_set) 3097 func.resolvedErrorSet(ip).* 3098 else 3099 .none; 3100 3101 switch (decl.analysis) { 3102 .unreferenced => unreachable, 3103 .in_progress => unreachable, 3104 3105 .codegen_failure => unreachable, // functions do not perform constant value generation 3106 3107 .file_failure, 3108 .sema_failure, 3109 .dependency_failure, 3110 => return error.AnalysisFail, 3111 3112 .complete => {}, 3113 } 3114 3115 const func_as_depender = InternPool.Depender.wrap(.{ .func = func_index }); 3116 const was_outdated = zcu.outdated.swapRemove(func_as_depender) or 3117 zcu.potentially_outdated.swapRemove(func_as_depender); 3118 3119 if (was_outdated) { 3120 _ = zcu.outdated_ready.swapRemove(func_as_depender); 3121 } 3122 3123 switch (func.analysis(ip).state) { 3124 .success => if (!was_outdated) return, 3125 .sema_failure, 3126 .dependency_failure, 3127 .codegen_failure, 3128 => if (!was_outdated) return error.AnalysisFail, 3129 .none, .queued => {}, 3130 .in_progress => unreachable, 3131 .inline_only => unreachable, // don't queue work for this 3132 } 3133 3134 log.debug("analyze and generate fn body '{d}'; reason='{s}'", .{ 3135 @intFromEnum(func_index), 3136 if (was_outdated) "outdated" else "never analyzed", 3137 }); 3138 3139 var tmp_arena = std.heap.ArenaAllocator.init(gpa); 3140 defer tmp_arena.deinit(); 3141 const sema_arena = tmp_arena.allocator(); 3142 3143 var air = zcu.analyzeFnBody(func_index, sema_arena) catch |err| switch (err) { 3144 error.AnalysisFail => { 3145 if (func.analysis(ip).state == .in_progress) { 3146 // If this decl caused the compile error, the analysis field would 3147 // be changed to indicate it was this Decl's fault. Because this 3148 // did not happen, we infer here that it was a dependency failure. 3149 func.analysis(ip).state = .dependency_failure; 3150 } 3151 return error.AnalysisFail; 3152 }, 3153 error.OutOfMemory => return error.OutOfMemory, 3154 }; 3155 defer air.deinit(gpa); 3156 3157 const invalidate_ies_deps = i: { 3158 if (!was_outdated) break :i false; 3159 if (!func.analysis(ip).inferred_error_set) break :i true; 3160 const new_resolved_ies = func.resolvedErrorSet(ip).*; 3161 break :i new_resolved_ies != old_resolved_ies; 3162 }; 3163 if (invalidate_ies_deps) { 3164 log.debug("func IES invalidated ('{d}')", .{@intFromEnum(func_index)}); 3165 try zcu.markDependeeOutdated(.{ .func_ies = func_index }); 3166 } else if (was_outdated) { 3167 log.debug("func IES up-to-date ('{d}')", .{@intFromEnum(func_index)}); 3168 try zcu.markPoDependeeUpToDate(.{ .func_ies = func_index }); 3169 } 3170 3171 const comp = zcu.comp; 3172 3173 const dump_air = build_options.enable_debug_extensions and comp.verbose_air; 3174 const dump_llvm_ir = build_options.enable_debug_extensions and (comp.verbose_llvm_ir != null or comp.verbose_llvm_bc != null); 3175 3176 if (comp.bin_file == null and zcu.llvm_object == null and !dump_air and !dump_llvm_ir) { 3177 return; 3178 } 3179 3180 var liveness = try Liveness.analyze(gpa, air, ip); 3181 defer liveness.deinit(gpa); 3182 3183 if (dump_air) { 3184 const fqn = try decl.fullyQualifiedName(zcu); 3185 std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)}); 3186 @import("print_air.zig").dump(zcu, air, liveness); 3187 std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)}); 3188 } 3189 3190 if (std.debug.runtime_safety) { 3191 var verify = Liveness.Verify{ 3192 .gpa = gpa, 3193 .air = air, 3194 .liveness = liveness, 3195 .intern_pool = ip, 3196 }; 3197 defer verify.deinit(); 3198 3199 verify.verify() catch |err| switch (err) { 3200 error.OutOfMemory => return error.OutOfMemory, 3201 else => { 3202 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1); 3203 zcu.failed_decls.putAssumeCapacityNoClobber( 3204 decl_index, 3205 try Module.ErrorMsg.create( 3206 gpa, 3207 decl.srcLoc(zcu), 3208 "invalid liveness: {s}", 3209 .{@errorName(err)}, 3210 ), 3211 ); 3212 func.analysis(ip).state = .codegen_failure; 3213 return; 3214 }, 3215 }; 3216 } 3217 3218 if (comp.bin_file) |lf| { 3219 lf.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) { 3220 error.OutOfMemory => return error.OutOfMemory, 3221 error.AnalysisFail => { 3222 func.analysis(ip).state = .codegen_failure; 3223 }, 3224 else => { 3225 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1); 3226 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create( 3227 gpa, 3228 decl.srcLoc(zcu), 3229 "unable to codegen: {s}", 3230 .{@errorName(err)}, 3231 )); 3232 func.analysis(ip).state = .codegen_failure; 3233 try zcu.retryable_failures.append(zcu.gpa, InternPool.Depender.wrap(.{ .func = func_index })); 3234 }, 3235 }; 3236 } else if (zcu.llvm_object) |llvm_object| { 3237 if (build_options.only_c) unreachable; 3238 llvm_object.updateFunc(zcu, func_index, air, liveness) catch |err| switch (err) { 3239 error.OutOfMemory => return error.OutOfMemory, 3240 error.AnalysisFail => { 3241 func.analysis(ip).state = .codegen_failure; 3242 }, 3243 }; 3244 } 3245 } 3246 3247 /// Ensure this function's body is or will be analyzed and emitted. This should 3248 /// be called whenever a potential runtime call of a function is seen. 3249 /// 3250 /// The caller is responsible for ensuring the function decl itself is already 3251 /// analyzed, and for ensuring it can exist at runtime (see 3252 /// `sema.fnHasRuntimeBits`). This function does *not* guarantee that the body 3253 /// will be analyzed when it returns: for that, see `ensureFuncBodyAnalyzed`. 3254 pub fn ensureFuncBodyAnalysisQueued(mod: *Module, func_index: InternPool.Index) !void { 3255 const ip = &mod.intern_pool; 3256 const func = mod.funcInfo(func_index); 3257 const decl_index = func.owner_decl; 3258 const decl = mod.declPtr(decl_index); 3259 3260 switch (decl.analysis) { 3261 .unreferenced => unreachable, 3262 .in_progress => unreachable, 3263 3264 .file_failure, 3265 .sema_failure, 3266 .codegen_failure, 3267 .dependency_failure, 3268 // Analysis of the function Decl itself failed, but we've already 3269 // emitted an error for that. The callee doesn't need the function to be 3270 // analyzed right now, so its analysis can safely continue. 3271 => return, 3272 3273 .complete => {}, 3274 } 3275 3276 assert(decl.has_tv); 3277 3278 const func_as_depender = InternPool.Depender.wrap(.{ .func = func_index }); 3279 const is_outdated = mod.outdated.contains(func_as_depender) or 3280 mod.potentially_outdated.contains(func_as_depender); 3281 3282 switch (func.analysis(ip).state) { 3283 .none => {}, 3284 .queued => return, 3285 // As above, we don't need to forward errors here. 3286 .sema_failure, 3287 .dependency_failure, 3288 .codegen_failure, 3289 .success, 3290 => if (!is_outdated) return, 3291 .in_progress => return, 3292 .inline_only => unreachable, // don't queue work for this 3293 } 3294 3295 // Decl itself is safely analyzed, and body analysis is not yet queued 3296 3297 try mod.comp.work_queue.writeItem(.{ .codegen_func = func_index }); 3298 if (mod.emit_h != null) { 3299 // TODO: we ideally only want to do this if the function's type changed 3300 // since the last update 3301 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); 3302 } 3303 func.analysis(ip).state = .queued; 3304 } 3305 3306 /// https://github.com/ziglang/zig/issues/14307 3307 pub fn semaPkg(mod: *Module, pkg: *Package.Module) !void { 3308 const file = (try mod.importPkg(pkg)).file; 3309 if (file.root_decl == .none) { 3310 return mod.semaFile(file); 3311 } 3312 } 3313 3314 fn getFileRootStruct(zcu: *Zcu, decl_index: Decl.Index, namespace_index: Namespace.Index, file: *File) Allocator.Error!InternPool.Index { 3315 const gpa = zcu.gpa; 3316 const ip = &zcu.intern_pool; 3317 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; 3318 assert(extended.opcode == .struct_decl); 3319 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 3320 assert(!small.has_captures_len); 3321 assert(!small.has_backing_int); 3322 assert(small.layout == .auto); 3323 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; 3324 const fields_len = if (small.has_fields_len) blk: { 3325 const fields_len = file.zir.extra[extra_index]; 3326 extra_index += 1; 3327 break :blk fields_len; 3328 } else 0; 3329 const decls_len = if (small.has_decls_len) blk: { 3330 const decls_len = file.zir.extra[extra_index]; 3331 extra_index += 1; 3332 break :blk decls_len; 3333 } else 0; 3334 const decls = file.zir.bodySlice(extra_index, decls_len); 3335 extra_index += decls_len; 3336 3337 const tracked_inst = try ip.trackZir(gpa, file, .main_struct_inst); 3338 const wip_ty = switch (try ip.getStructType(gpa, .{ 3339 .layout = .auto, 3340 .fields_len = fields_len, 3341 .known_non_opv = small.known_non_opv, 3342 .requires_comptime = if (small.known_comptime_only) .yes else .unknown, 3343 .is_tuple = small.is_tuple, 3344 .any_comptime_fields = small.any_comptime_fields, 3345 .any_default_inits = small.any_default_inits, 3346 .inits_resolved = false, 3347 .any_aligned_fields = small.any_aligned_fields, 3348 .has_namespace = true, 3349 .key = .{ .declared = .{ 3350 .zir_index = tracked_inst, 3351 .captures = &.{}, 3352 } }, 3353 })) { 3354 .existing => unreachable, // we wouldn't be analysing the file root if this type existed 3355 .wip => |wip| wip, 3356 }; 3357 errdefer wip_ty.cancel(ip); 3358 3359 if (zcu.comp.debug_incremental) { 3360 try ip.addDependency( 3361 gpa, 3362 InternPool.Depender.wrap(.{ .decl = decl_index }), 3363 .{ .src_hash = tracked_inst }, 3364 ); 3365 } 3366 3367 const decl = zcu.declPtr(decl_index); 3368 decl.val = Value.fromInterned(wip_ty.index); 3369 decl.has_tv = true; 3370 decl.owns_tv = true; 3371 decl.analysis = .complete; 3372 3373 try zcu.scanNamespace(namespace_index, decls, decl); 3374 3375 return wip_ty.finish(ip, decl_index, namespace_index.toOptional()); 3376 } 3377 3378 /// Re-analyze the root Decl of a file on an incremental update. 3379 /// If `type_outdated`, the struct type itself is considered outdated and is 3380 /// reconstructed at a new InternPool index. Otherwise, the namespace is just 3381 /// re-analyzed. Returns whether the decl's tyval was invalidated. 3382 fn semaFileUpdate(zcu: *Zcu, file: *File, type_outdated: bool) SemaError!bool { 3383 const decl = zcu.declPtr(file.root_decl.unwrap().?); 3384 3385 log.debug("semaFileUpdate mod={s} sub_file_path={s} type_outdated={}", .{ 3386 file.mod.fully_qualified_name, 3387 file.sub_file_path, 3388 type_outdated, 3389 }); 3390 3391 if (file.status != .success_zir) { 3392 if (decl.analysis == .file_failure) { 3393 return false; 3394 } else { 3395 decl.analysis = .file_failure; 3396 return true; 3397 } 3398 } 3399 3400 if (decl.analysis == .file_failure) { 3401 // No struct type currently exists. Create one! 3402 _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file); 3403 return true; 3404 } 3405 3406 assert(decl.has_tv); 3407 assert(decl.owns_tv); 3408 3409 if (type_outdated) { 3410 // Invalidate the existing type, reusing the decl and namespace. 3411 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = file.root_decl.unwrap().? })); 3412 zcu.intern_pool.remove(decl.val.toIntern()); 3413 decl.val = undefined; 3414 _ = try zcu.getFileRootStruct(file.root_decl.unwrap().?, decl.src_namespace, file); 3415 return true; 3416 } 3417 3418 // Only the struct's namespace is outdated. 3419 // Preserve the type - just scan the namespace again. 3420 3421 const extended = file.zir.instructions.items(.data)[@intFromEnum(Zir.Inst.Index.main_struct_inst)].extended; 3422 const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small); 3423 3424 var extra_index: usize = extended.operand + @typeInfo(Zir.Inst.StructDecl).Struct.fields.len; 3425 extra_index += @intFromBool(small.has_fields_len); 3426 const decls_len = if (small.has_decls_len) blk: { 3427 const decls_len = file.zir.extra[extra_index]; 3428 extra_index += 1; 3429 break :blk decls_len; 3430 } else 0; 3431 const decls = file.zir.bodySlice(extra_index, decls_len); 3432 3433 if (!type_outdated) { 3434 try zcu.scanNamespace(decl.src_namespace, decls, decl); 3435 } 3436 3437 return false; 3438 } 3439 3440 /// Regardless of the file status, will create a `Decl` if none exists so that we can track 3441 /// dependencies and re-analyze when the file becomes outdated. 3442 fn semaFile(mod: *Module, file: *File) SemaError!void { 3443 const tracy = trace(@src()); 3444 defer tracy.end(); 3445 3446 assert(file.root_decl == .none); 3447 3448 const gpa = mod.gpa; 3449 log.debug("semaFile mod={s} sub_file_path={s}", .{ 3450 file.mod.fully_qualified_name, file.sub_file_path, 3451 }); 3452 3453 // Because these three things each reference each other, `undefined` 3454 // placeholders are used before being set after the struct type gains an 3455 // InternPool index. 3456 const new_namespace_index = try mod.createNamespace(.{ 3457 .parent = .none, 3458 .decl_index = undefined, 3459 .file_scope = file, 3460 }); 3461 errdefer mod.destroyNamespace(new_namespace_index); 3462 3463 const new_decl_index = try mod.allocateNewDecl(new_namespace_index, 0); 3464 const new_decl = mod.declPtr(new_decl_index); 3465 errdefer @panic("TODO error handling"); 3466 3467 file.root_decl = new_decl_index.toOptional(); 3468 mod.namespacePtr(new_namespace_index).decl_index = new_decl_index; 3469 3470 new_decl.name = try file.fullyQualifiedName(mod); 3471 new_decl.name_fully_qualified = true; 3472 new_decl.src_line = 0; 3473 new_decl.is_pub = true; 3474 new_decl.is_exported = false; 3475 new_decl.alignment = .none; 3476 new_decl.@"linksection" = .none; 3477 new_decl.analysis = .in_progress; 3478 3479 if (file.status != .success_zir) { 3480 new_decl.analysis = .file_failure; 3481 return; 3482 } 3483 assert(file.zir_loaded); 3484 3485 const struct_ty = try mod.getFileRootStruct(new_decl_index, new_namespace_index, file); 3486 errdefer mod.intern_pool.remove(struct_ty); 3487 3488 switch (mod.comp.cache_use) { 3489 .whole => |whole| if (whole.cache_manifest) |man| { 3490 const source = file.getSource(gpa) catch |err| { 3491 try reportRetryableFileError(mod, file, "unable to load source: {s}", .{@errorName(err)}); 3492 return error.AnalysisFail; 3493 }; 3494 3495 const resolved_path = std.fs.path.resolve(gpa, &.{ 3496 file.mod.root.root_dir.path orelse ".", 3497 file.mod.root.sub_path, 3498 file.sub_file_path, 3499 }) catch |err| { 3500 try reportRetryableFileError(mod, file, "unable to resolve path: {s}", .{@errorName(err)}); 3501 return error.AnalysisFail; 3502 }; 3503 errdefer gpa.free(resolved_path); 3504 3505 whole.cache_manifest_mutex.lock(); 3506 defer whole.cache_manifest_mutex.unlock(); 3507 try man.addFilePostContents(resolved_path, source.bytes, source.stat); 3508 }, 3509 .incremental => {}, 3510 } 3511 } 3512 3513 const SemaDeclResult = packed struct { 3514 /// Whether the value of a `decl_val` of this Decl changed. 3515 invalidate_decl_val: bool, 3516 /// Whether the type of a `decl_ref` of this Decl changed. 3517 invalidate_decl_ref: bool, 3518 }; 3519 3520 fn semaDecl(mod: *Module, decl_index: Decl.Index) !SemaDeclResult { 3521 const tracy = trace(@src()); 3522 defer tracy.end(); 3523 3524 const decl = mod.declPtr(decl_index); 3525 const ip = &mod.intern_pool; 3526 3527 if (decl.getFileScope(mod).status != .success_zir) { 3528 return error.AnalysisFail; 3529 } 3530 3531 assert(!mod.declIsRoot(decl_index)); 3532 3533 if (decl.zir_decl_index == .none and decl.owns_tv) { 3534 // We are re-analyzing an anonymous owner Decl (for a function or a namespace type). 3535 return mod.semaAnonOwnerDecl(decl_index); 3536 } 3537 3538 log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)}); 3539 log.debug("decl name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)}); 3540 defer blk: { 3541 log.debug("finish decl name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)}); 3542 } 3543 3544 const old_has_tv = decl.has_tv; 3545 // The following values are ignored if `!old_has_tv` 3546 const old_ty = if (old_has_tv) decl.typeOf(mod) else undefined; 3547 const old_val = decl.val; 3548 const old_align = decl.alignment; 3549 const old_linksection = decl.@"linksection"; 3550 const old_addrspace = decl.@"addrspace"; 3551 const old_is_inline = if (decl.getOwnedFunction(mod)) |prev_func| 3552 prev_func.analysis(ip).state == .inline_only 3553 else 3554 false; 3555 3556 const decl_inst = decl.zir_decl_index.unwrap().?.resolve(ip); 3557 3558 const gpa = mod.gpa; 3559 const zir = decl.getFileScope(mod).zir; 3560 3561 const builtin_type_target_index: InternPool.Index = ip_index: { 3562 const std_mod = mod.std_mod; 3563 if (decl.getFileScope(mod).mod != std_mod) break :ip_index .none; 3564 // We're in the std module. 3565 const std_file = (try mod.importPkg(std_mod)).file; 3566 const std_decl = mod.declPtr(std_file.root_decl.unwrap().?); 3567 const std_namespace = std_decl.getInnerNamespace(mod).?; 3568 const builtin_str = try ip.getOrPutString(gpa, "builtin", .no_embedded_nulls); 3569 const builtin_decl = mod.declPtr(std_namespace.decls.getKeyAdapted(builtin_str, DeclAdapter{ .zcu = mod }) orelse break :ip_index .none); 3570 const builtin_namespace = builtin_decl.getInnerNamespaceIndex(mod).unwrap() orelse break :ip_index .none; 3571 if (decl.src_namespace != builtin_namespace) break :ip_index .none; 3572 // We're in builtin.zig. This could be a builtin we need to add to a specific InternPool index. 3573 for ([_][]const u8{ 3574 "AtomicOrder", 3575 "AtomicRmwOp", 3576 "CallingConvention", 3577 "AddressSpace", 3578 "FloatMode", 3579 "ReduceOp", 3580 "CallModifier", 3581 "PrefetchOptions", 3582 "ExportOptions", 3583 "ExternOptions", 3584 "Type", 3585 }, [_]InternPool.Index{ 3586 .atomic_order_type, 3587 .atomic_rmw_op_type, 3588 .calling_convention_type, 3589 .address_space_type, 3590 .float_mode_type, 3591 .reduce_op_type, 3592 .call_modifier_type, 3593 .prefetch_options_type, 3594 .export_options_type, 3595 .extern_options_type, 3596 .type_info_type, 3597 }) |type_name, type_ip| { 3598 if (decl.name.eqlSlice(type_name, ip)) break :ip_index type_ip; 3599 } 3600 break :ip_index .none; 3601 }; 3602 3603 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .decl = decl_index })); 3604 3605 decl.analysis = .in_progress; 3606 3607 var analysis_arena = std.heap.ArenaAllocator.init(gpa); 3608 defer analysis_arena.deinit(); 3609 3610 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa); 3611 defer comptime_err_ret_trace.deinit(); 3612 3613 var sema: Sema = .{ 3614 .mod = mod, 3615 .gpa = gpa, 3616 .arena = analysis_arena.allocator(), 3617 .code = zir, 3618 .owner_decl = decl, 3619 .owner_decl_index = decl_index, 3620 .func_index = .none, 3621 .func_is_naked = false, 3622 .fn_ret_ty = Type.void, 3623 .fn_ret_ty_ies = null, 3624 .owner_func_index = .none, 3625 .comptime_err_ret_trace = &comptime_err_ret_trace, 3626 .builtin_type_target_index = builtin_type_target_index, 3627 }; 3628 defer sema.deinit(); 3629 3630 // Every Decl (other than file root Decls, which do not have a ZIR index) has a dependency on its own source. 3631 try sema.declareDependency(.{ .src_hash = try ip.trackZir( 3632 sema.gpa, 3633 decl.getFileScope(mod), 3634 decl_inst, 3635 ) }); 3636 3637 var block_scope: Sema.Block = .{ 3638 .parent = null, 3639 .sema = &sema, 3640 .src_decl = decl_index, 3641 .namespace = decl.src_namespace, 3642 .instructions = .{}, 3643 .inlining = null, 3644 .is_comptime = true, 3645 }; 3646 defer block_scope.instructions.deinit(gpa); 3647 3648 const decl_bodies = decl.zirBodies(mod); 3649 3650 const result_ref = try sema.resolveInlineBody(&block_scope, decl_bodies.value_body, decl_inst); 3651 // We'll do some other bits with the Sema. Clear the type target index just 3652 // in case they analyze any type. 3653 sema.builtin_type_target_index = .none; 3654 const align_src: LazySrcLoc = .{ .node_offset_var_decl_align = 0 }; 3655 const section_src: LazySrcLoc = .{ .node_offset_var_decl_section = 0 }; 3656 const address_space_src: LazySrcLoc = .{ .node_offset_var_decl_addrspace = 0 }; 3657 const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 }; 3658 const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 }; 3659 const decl_val = try sema.resolveFinalDeclValue(&block_scope, init_src, result_ref); 3660 const decl_ty = decl_val.typeOf(mod); 3661 3662 // Note this resolves the type of the Decl, not the value; if this Decl 3663 // is a struct, for example, this resolves `type` (which needs no resolution), 3664 // not the struct itself. 3665 try sema.resolveTypeLayout(decl_ty); 3666 3667 if (decl.kind == .@"usingnamespace") { 3668 if (!decl_ty.eql(Type.type, mod)) { 3669 return sema.fail(&block_scope, ty_src, "expected type, found {}", .{ 3670 decl_ty.fmt(mod), 3671 }); 3672 } 3673 const ty = decl_val.toType(); 3674 if (ty.getNamespace(mod) == null) { 3675 return sema.fail(&block_scope, ty_src, "type {} has no namespace", .{ty.fmt(mod)}); 3676 } 3677 3678 decl.val = ty.toValue(); 3679 decl.alignment = .none; 3680 decl.@"linksection" = .none; 3681 decl.has_tv = true; 3682 decl.owns_tv = false; 3683 decl.analysis = .complete; 3684 3685 // TODO: usingnamespace cannot currently participate in incremental compilation 3686 return .{ 3687 .invalidate_decl_val = true, 3688 .invalidate_decl_ref = true, 3689 }; 3690 } 3691 3692 var queue_linker_work = true; 3693 var is_func = false; 3694 var is_inline = false; 3695 switch (decl_val.toIntern()) { 3696 .generic_poison => unreachable, 3697 .unreachable_value => unreachable, 3698 else => switch (ip.indexToKey(decl_val.toIntern())) { 3699 .variable => |variable| { 3700 decl.owns_tv = variable.decl == decl_index; 3701 queue_linker_work = decl.owns_tv; 3702 }, 3703 3704 .extern_func => |extern_func| { 3705 decl.owns_tv = extern_func.decl == decl_index; 3706 queue_linker_work = decl.owns_tv; 3707 is_func = decl.owns_tv; 3708 }, 3709 3710 .func => |func| { 3711 decl.owns_tv = func.owner_decl == decl_index; 3712 queue_linker_work = false; 3713 is_inline = decl.owns_tv and decl_ty.fnCallingConvention(mod) == .Inline; 3714 is_func = decl.owns_tv; 3715 }, 3716 3717 else => {}, 3718 }, 3719 } 3720 3721 decl.val = decl_val; 3722 // Function linksection, align, and addrspace were already set by Sema 3723 if (!is_func) { 3724 decl.alignment = blk: { 3725 const align_body = decl_bodies.align_body orelse break :blk .none; 3726 const align_ref = try sema.resolveInlineBody(&block_scope, align_body, decl_inst); 3727 break :blk try sema.analyzeAsAlign(&block_scope, align_src, align_ref); 3728 }; 3729 decl.@"linksection" = blk: { 3730 const linksection_body = decl_bodies.linksection_body orelse break :blk .none; 3731 const linksection_ref = try sema.resolveInlineBody(&block_scope, linksection_body, decl_inst); 3732 const bytes = try sema.toConstString(&block_scope, section_src, linksection_ref, .{ 3733 .needed_comptime_reason = "linksection must be comptime-known", 3734 }); 3735 if (mem.indexOfScalar(u8, bytes, 0) != null) { 3736 return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{}); 3737 } else if (bytes.len == 0) { 3738 return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{}); 3739 } 3740 break :blk try ip.getOrPutStringOpt(gpa, bytes, .no_embedded_nulls); 3741 }; 3742 decl.@"addrspace" = blk: { 3743 const addrspace_ctx: Sema.AddressSpaceContext = switch (ip.indexToKey(decl_val.toIntern())) { 3744 .variable => .variable, 3745 .extern_func, .func => .function, 3746 else => .constant, 3747 }; 3748 3749 const target = sema.mod.getTarget(); 3750 3751 const addrspace_body = decl_bodies.addrspace_body orelse break :blk switch (addrspace_ctx) { 3752 .function => target_util.defaultAddressSpace(target, .function), 3753 .variable => target_util.defaultAddressSpace(target, .global_mutable), 3754 .constant => target_util.defaultAddressSpace(target, .global_constant), 3755 else => unreachable, 3756 }; 3757 const addrspace_ref = try sema.resolveInlineBody(&block_scope, addrspace_body, decl_inst); 3758 break :blk try sema.analyzeAsAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx); 3759 }; 3760 } 3761 decl.has_tv = true; 3762 decl.analysis = .complete; 3763 3764 const result: SemaDeclResult = if (old_has_tv) .{ 3765 .invalidate_decl_val = !decl_ty.eql(old_ty, mod) or 3766 !decl.val.eql(old_val, decl_ty, mod) or 3767 is_inline != old_is_inline, 3768 .invalidate_decl_ref = !decl_ty.eql(old_ty, mod) or 3769 decl.alignment != old_align or 3770 decl.@"linksection" != old_linksection or 3771 decl.@"addrspace" != old_addrspace or 3772 is_inline != old_is_inline, 3773 } else .{ 3774 .invalidate_decl_val = true, 3775 .invalidate_decl_ref = true, 3776 }; 3777 3778 const has_runtime_bits = queue_linker_work and (is_func or try sema.typeHasRuntimeBits(decl_ty)); 3779 if (has_runtime_bits) { 3780 // Needed for codegen_decl which will call updateDecl and then the 3781 // codegen backend wants full access to the Decl Type. 3782 try sema.resolveTypeFully(decl_ty); 3783 3784 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); 3785 3786 if (result.invalidate_decl_ref and mod.emit_h != null) { 3787 try mod.comp.work_queue.writeItem(.{ .emit_h_decl = decl_index }); 3788 } 3789 } 3790 3791 if (decl.is_exported) { 3792 const export_src: LazySrcLoc = .{ .token_offset = @intFromBool(decl.is_pub) }; 3793 if (is_inline) return sema.fail(&block_scope, export_src, "export of inline function", .{}); 3794 // The scope needs to have the decl in it. 3795 try sema.analyzeExport(&block_scope, export_src, .{ .name = decl.name }, decl_index); 3796 } 3797 3798 return result; 3799 } 3800 3801 fn semaAnonOwnerDecl(zcu: *Zcu, decl_index: Decl.Index) !SemaDeclResult { 3802 const decl = zcu.declPtr(decl_index); 3803 3804 assert(decl.has_tv); 3805 assert(decl.owns_tv); 3806 3807 log.debug("semaAnonOwnerDecl '{d}'", .{@intFromEnum(decl_index)}); 3808 3809 switch (decl.typeOf(zcu).zigTypeTag(zcu)) { 3810 .Fn => @panic("TODO: update fn instance"), 3811 .Type => {}, 3812 else => unreachable, 3813 } 3814 3815 // We are the owner Decl of a type, and we were marked as outdated. That means the *structure* 3816 // of this type changed; not just its namespace. Therefore, we need a new InternPool index. 3817 // 3818 // However, as soon as we make that, the context that created us will require re-analysis anyway 3819 // (as it depends on this Decl's value), meaning the `struct_decl` (or equivalent) instruction 3820 // will be analyzed again. Since Sema already needs to be able to reconstruct types like this, 3821 // why should we bother implementing it here too when the Sema logic will be hit right after? 3822 // 3823 // So instead, let's just mark this Decl as failed - so that any remaining Decls which genuinely 3824 // reference it (via `@This`) end up silently erroring too - and we'll let Sema make a new type 3825 // with a new Decl. 3826 // 3827 // Yes, this does mean that any type owner Decl has a constant value for its entire lifetime. 3828 zcu.intern_pool.removeDependenciesForDepender(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index })); 3829 zcu.intern_pool.remove(decl.val.toIntern()); 3830 decl.analysis = .dependency_failure; 3831 return .{ 3832 .invalidate_decl_val = true, 3833 .invalidate_decl_ref = true, 3834 }; 3835 } 3836 3837 pub const ImportFileResult = struct { 3838 file: *File, 3839 is_new: bool, 3840 is_pkg: bool, 3841 }; 3842 3843 pub fn importPkg(zcu: *Zcu, mod: *Package.Module) !ImportFileResult { 3844 const gpa = zcu.gpa; 3845 3846 // The resolved path is used as the key in the import table, to detect if 3847 // an import refers to the same as another, despite different relative paths 3848 // or differently mapped package names. 3849 const resolved_path = try std.fs.path.resolve(gpa, &.{ 3850 mod.root.root_dir.path orelse ".", 3851 mod.root.sub_path, 3852 mod.root_src_path, 3853 }); 3854 var keep_resolved_path = false; 3855 defer if (!keep_resolved_path) gpa.free(resolved_path); 3856 3857 const gop = try zcu.import_table.getOrPut(gpa, resolved_path); 3858 errdefer _ = zcu.import_table.pop(); 3859 if (gop.found_existing) { 3860 try gop.value_ptr.*.addReference(zcu.*, .{ .root = mod }); 3861 return ImportFileResult{ 3862 .file = gop.value_ptr.*, 3863 .is_new = false, 3864 .is_pkg = true, 3865 }; 3866 } 3867 3868 if (mod.builtin_file) |builtin_file| { 3869 keep_resolved_path = true; // It's now owned by import_table. 3870 gop.value_ptr.* = builtin_file; 3871 try builtin_file.addReference(zcu.*, .{ .root = mod }); 3872 return .{ 3873 .file = builtin_file, 3874 .is_new = false, 3875 .is_pkg = true, 3876 }; 3877 } 3878 3879 const sub_file_path = try gpa.dupe(u8, mod.root_src_path); 3880 errdefer gpa.free(sub_file_path); 3881 3882 const new_file = try gpa.create(File); 3883 errdefer gpa.destroy(new_file); 3884 3885 keep_resolved_path = true; // It's now owned by import_table. 3886 gop.value_ptr.* = new_file; 3887 new_file.* = .{ 3888 .sub_file_path = sub_file_path, 3889 .source = undefined, 3890 .source_loaded = false, 3891 .tree_loaded = false, 3892 .zir_loaded = false, 3893 .stat = undefined, 3894 .tree = undefined, 3895 .zir = undefined, 3896 .status = .never_loaded, 3897 .mod = mod, 3898 .root_decl = .none, 3899 }; 3900 try new_file.addReference(zcu.*, .{ .root = mod }); 3901 return ImportFileResult{ 3902 .file = new_file, 3903 .is_new = true, 3904 .is_pkg = true, 3905 }; 3906 } 3907 3908 pub fn importFile( 3909 mod: *Module, 3910 cur_file: *File, 3911 import_string: []const u8, 3912 ) !ImportFileResult { 3913 if (std.mem.eql(u8, import_string, "std")) { 3914 return mod.importPkg(mod.std_mod); 3915 } 3916 if (std.mem.eql(u8, import_string, "root")) { 3917 return mod.importPkg(mod.root_mod); 3918 } 3919 if (cur_file.mod.deps.get(import_string)) |pkg| { 3920 return mod.importPkg(pkg); 3921 } 3922 if (!mem.endsWith(u8, import_string, ".zig")) { 3923 return error.ModuleNotFound; 3924 } 3925 const gpa = mod.gpa; 3926 3927 // The resolved path is used as the key in the import table, to detect if 3928 // an import refers to the same as another, despite different relative paths 3929 // or differently mapped package names. 3930 const resolved_path = try std.fs.path.resolve(gpa, &.{ 3931 cur_file.mod.root.root_dir.path orelse ".", 3932 cur_file.mod.root.sub_path, 3933 cur_file.sub_file_path, 3934 "..", 3935 import_string, 3936 }); 3937 3938 var keep_resolved_path = false; 3939 defer if (!keep_resolved_path) gpa.free(resolved_path); 3940 3941 const gop = try mod.import_table.getOrPut(gpa, resolved_path); 3942 errdefer _ = mod.import_table.pop(); 3943 if (gop.found_existing) return ImportFileResult{ 3944 .file = gop.value_ptr.*, 3945 .is_new = false, 3946 .is_pkg = false, 3947 }; 3948 3949 const new_file = try gpa.create(File); 3950 errdefer gpa.destroy(new_file); 3951 3952 const resolved_root_path = try std.fs.path.resolve(gpa, &.{ 3953 cur_file.mod.root.root_dir.path orelse ".", 3954 cur_file.mod.root.sub_path, 3955 }); 3956 defer gpa.free(resolved_root_path); 3957 3958 const sub_file_path = p: { 3959 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path); 3960 errdefer gpa.free(relative); 3961 3962 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) { 3963 break :p relative; 3964 } 3965 return error.ImportOutsideModulePath; 3966 }; 3967 errdefer gpa.free(sub_file_path); 3968 3969 log.debug("new importFile. resolved_root_path={s}, resolved_path={s}, sub_file_path={s}, import_string={s}", .{ 3970 resolved_root_path, resolved_path, sub_file_path, import_string, 3971 }); 3972 3973 keep_resolved_path = true; // It's now owned by import_table. 3974 gop.value_ptr.* = new_file; 3975 new_file.* = .{ 3976 .sub_file_path = sub_file_path, 3977 .source = undefined, 3978 .source_loaded = false, 3979 .tree_loaded = false, 3980 .zir_loaded = false, 3981 .stat = undefined, 3982 .tree = undefined, 3983 .zir = undefined, 3984 .status = .never_loaded, 3985 .mod = cur_file.mod, 3986 .root_decl = .none, 3987 }; 3988 return ImportFileResult{ 3989 .file = new_file, 3990 .is_new = true, 3991 .is_pkg = false, 3992 }; 3993 } 3994 3995 pub fn embedFile( 3996 mod: *Module, 3997 cur_file: *File, 3998 import_string: []const u8, 3999 src_loc: SrcLoc, 4000 ) !InternPool.Index { 4001 const gpa = mod.gpa; 4002 4003 if (cur_file.mod.deps.get(import_string)) |pkg| { 4004 const resolved_path = try std.fs.path.resolve(gpa, &.{ 4005 pkg.root.root_dir.path orelse ".", 4006 pkg.root.sub_path, 4007 pkg.root_src_path, 4008 }); 4009 var keep_resolved_path = false; 4010 defer if (!keep_resolved_path) gpa.free(resolved_path); 4011 4012 const gop = try mod.embed_table.getOrPut(gpa, resolved_path); 4013 errdefer { 4014 assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path)); 4015 keep_resolved_path = false; 4016 } 4017 if (gop.found_existing) return gop.value_ptr.*.val; 4018 keep_resolved_path = true; 4019 4020 const sub_file_path = try gpa.dupe(u8, pkg.root_src_path); 4021 errdefer gpa.free(sub_file_path); 4022 4023 return newEmbedFile(mod, pkg, sub_file_path, resolved_path, gop.value_ptr, src_loc); 4024 } 4025 4026 // The resolved path is used as the key in the table, to detect if a file 4027 // refers to the same as another, despite different relative paths. 4028 const resolved_path = try std.fs.path.resolve(gpa, &.{ 4029 cur_file.mod.root.root_dir.path orelse ".", 4030 cur_file.mod.root.sub_path, 4031 cur_file.sub_file_path, 4032 "..", 4033 import_string, 4034 }); 4035 4036 var keep_resolved_path = false; 4037 defer if (!keep_resolved_path) gpa.free(resolved_path); 4038 4039 const gop = try mod.embed_table.getOrPut(gpa, resolved_path); 4040 errdefer { 4041 assert(std.mem.eql(u8, mod.embed_table.pop().key, resolved_path)); 4042 keep_resolved_path = false; 4043 } 4044 if (gop.found_existing) return gop.value_ptr.*.val; 4045 keep_resolved_path = true; 4046 4047 const resolved_root_path = try std.fs.path.resolve(gpa, &.{ 4048 cur_file.mod.root.root_dir.path orelse ".", 4049 cur_file.mod.root.sub_path, 4050 }); 4051 defer gpa.free(resolved_root_path); 4052 4053 const sub_file_path = p: { 4054 const relative = try std.fs.path.relative(gpa, resolved_root_path, resolved_path); 4055 errdefer gpa.free(relative); 4056 4057 if (!isUpDir(relative) and !std.fs.path.isAbsolute(relative)) { 4058 break :p relative; 4059 } 4060 return error.ImportOutsideModulePath; 4061 }; 4062 defer gpa.free(sub_file_path); 4063 4064 return newEmbedFile(mod, cur_file.mod, sub_file_path, resolved_path, gop.value_ptr, src_loc); 4065 } 4066 4067 /// https://github.com/ziglang/zig/issues/14307 4068 fn newEmbedFile( 4069 mod: *Module, 4070 pkg: *Package.Module, 4071 sub_file_path: []const u8, 4072 resolved_path: []const u8, 4073 result: **EmbedFile, 4074 src_loc: SrcLoc, 4075 ) !InternPool.Index { 4076 const gpa = mod.gpa; 4077 const ip = &mod.intern_pool; 4078 4079 const new_file = try gpa.create(EmbedFile); 4080 errdefer gpa.destroy(new_file); 4081 4082 var file = try pkg.root.openFile(sub_file_path, .{}); 4083 defer file.close(); 4084 4085 const actual_stat = try file.stat(); 4086 const stat: Cache.File.Stat = .{ 4087 .size = actual_stat.size, 4088 .inode = actual_stat.inode, 4089 .mtime = actual_stat.mtime, 4090 }; 4091 const size = std.math.cast(usize, actual_stat.size) orelse return error.Overflow; 4092 4093 const bytes = try ip.string_bytes.addManyAsSlice(gpa, try std.math.add(usize, size, 1)); 4094 const actual_read = try file.readAll(bytes[0..size]); 4095 if (actual_read != size) return error.UnexpectedEndOfFile; 4096 bytes[size] = 0; 4097 4098 const comp = mod.comp; 4099 switch (comp.cache_use) { 4100 .whole => |whole| if (whole.cache_manifest) |man| { 4101 const copied_resolved_path = try gpa.dupe(u8, resolved_path); 4102 errdefer gpa.free(copied_resolved_path); 4103 whole.cache_manifest_mutex.lock(); 4104 defer whole.cache_manifest_mutex.unlock(); 4105 try man.addFilePostContents(copied_resolved_path, bytes[0..size], stat); 4106 }, 4107 .incremental => {}, 4108 } 4109 4110 const array_ty = try ip.get(gpa, .{ .array_type = .{ 4111 .len = size, 4112 .sentinel = .zero_u8, 4113 .child = .u8_type, 4114 } }); 4115 const array_val = try ip.get(gpa, .{ .aggregate = .{ 4116 .ty = array_ty, 4117 .storage = .{ .bytes = try ip.getOrPutTrailingString(gpa, bytes.len, .maybe_embedded_nulls) }, 4118 } }); 4119 4120 const ptr_ty = (try mod.ptrType(.{ 4121 .child = array_ty, 4122 .flags = .{ 4123 .alignment = .none, 4124 .is_const = true, 4125 .address_space = .generic, 4126 }, 4127 })).toIntern(); 4128 const ptr_val = try ip.get(gpa, .{ .ptr = .{ 4129 .ty = ptr_ty, 4130 .base_addr = .{ .anon_decl = .{ 4131 .val = array_val, 4132 .orig_ty = ptr_ty, 4133 } }, 4134 .byte_offset = 0, 4135 } }); 4136 4137 result.* = new_file; 4138 new_file.* = .{ 4139 .sub_file_path = try ip.getOrPutString(gpa, sub_file_path, .no_embedded_nulls), 4140 .owner = pkg, 4141 .stat = stat, 4142 .val = ptr_val, 4143 .src_loc = src_loc, 4144 }; 4145 return ptr_val; 4146 } 4147 4148 pub fn scanNamespace( 4149 zcu: *Zcu, 4150 namespace_index: Namespace.Index, 4151 decls: []const Zir.Inst.Index, 4152 parent_decl: *Decl, 4153 ) Allocator.Error!void { 4154 const tracy = trace(@src()); 4155 defer tracy.end(); 4156 4157 const gpa = zcu.gpa; 4158 const namespace = zcu.namespacePtr(namespace_index); 4159 4160 // For incremental updates, `scanDecl` wants to look up existing decls by their ZIR index rather 4161 // than their name. We'll build an efficient mapping now, then discard the current `decls`. 4162 var existing_by_inst: std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index) = .{}; 4163 defer existing_by_inst.deinit(gpa); 4164 4165 try existing_by_inst.ensureTotalCapacity(gpa, @intCast(namespace.decls.count())); 4166 4167 for (namespace.decls.keys()) |decl_index| { 4168 const decl = zcu.declPtr(decl_index); 4169 existing_by_inst.putAssumeCapacityNoClobber(decl.zir_decl_index.unwrap().?, decl_index); 4170 } 4171 4172 var seen_decls: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; 4173 defer seen_decls.deinit(gpa); 4174 4175 try zcu.comp.work_queue.ensureUnusedCapacity(decls.len); 4176 4177 namespace.decls.clearRetainingCapacity(); 4178 try namespace.decls.ensureTotalCapacity(gpa, decls.len); 4179 4180 namespace.usingnamespace_set.clearRetainingCapacity(); 4181 4182 var scan_decl_iter: ScanDeclIter = .{ 4183 .zcu = zcu, 4184 .namespace_index = namespace_index, 4185 .parent_decl = parent_decl, 4186 .seen_decls = &seen_decls, 4187 .existing_by_inst = &existing_by_inst, 4188 .pass = .named, 4189 }; 4190 for (decls) |decl_inst| { 4191 try scanDecl(&scan_decl_iter, decl_inst); 4192 } 4193 scan_decl_iter.pass = .unnamed; 4194 for (decls) |decl_inst| { 4195 try scanDecl(&scan_decl_iter, decl_inst); 4196 } 4197 4198 if (seen_decls.count() != namespace.decls.count()) { 4199 // Do a pass over the namespace contents and remove any decls from the last update 4200 // which were removed in this one. 4201 var i: usize = 0; 4202 while (i < namespace.decls.count()) { 4203 const decl_index = namespace.decls.keys()[i]; 4204 const decl = zcu.declPtr(decl_index); 4205 if (!seen_decls.contains(decl.name)) { 4206 // We must preserve namespace ordering for @typeInfo. 4207 namespace.decls.orderedRemoveAt(i); 4208 i -= 1; 4209 } 4210 } 4211 } 4212 } 4213 4214 const ScanDeclIter = struct { 4215 zcu: *Zcu, 4216 namespace_index: Namespace.Index, 4217 parent_decl: *Decl, 4218 seen_decls: *std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), 4219 existing_by_inst: *const std.AutoHashMapUnmanaged(InternPool.TrackedInst.Index, Decl.Index), 4220 /// Decl scanning is run in two passes, so that we can detect when a generated 4221 /// name would clash with an explicit name and use a different one. 4222 pass: enum { named, unnamed }, 4223 usingnamespace_index: usize = 0, 4224 comptime_index: usize = 0, 4225 unnamed_test_index: usize = 0, 4226 4227 fn avoidNameConflict(iter: *ScanDeclIter, comptime fmt: []const u8, args: anytype) !InternPool.NullTerminatedString { 4228 const zcu = iter.zcu; 4229 const gpa = zcu.gpa; 4230 const ip = &zcu.intern_pool; 4231 var name = try ip.getOrPutStringFmt(gpa, fmt, args, .no_embedded_nulls); 4232 var gop = try iter.seen_decls.getOrPut(gpa, name); 4233 var next_suffix: u32 = 0; 4234 while (gop.found_existing) { 4235 name = try ip.getOrPutStringFmt(gpa, "{}_{d}", .{ name.fmt(ip), next_suffix }, .no_embedded_nulls); 4236 gop = try iter.seen_decls.getOrPut(gpa, name); 4237 next_suffix += 1; 4238 } 4239 return name; 4240 } 4241 }; 4242 4243 fn scanDecl(iter: *ScanDeclIter, decl_inst: Zir.Inst.Index) Allocator.Error!void { 4244 const tracy = trace(@src()); 4245 defer tracy.end(); 4246 4247 const zcu = iter.zcu; 4248 const namespace_index = iter.namespace_index; 4249 const namespace = zcu.namespacePtr(namespace_index); 4250 const gpa = zcu.gpa; 4251 const zir = namespace.file_scope.zir; 4252 const ip = &zcu.intern_pool; 4253 4254 const pl_node = zir.instructions.items(.data)[@intFromEnum(decl_inst)].pl_node; 4255 const extra = zir.extraData(Zir.Inst.Declaration, pl_node.payload_index); 4256 const declaration = extra.data; 4257 4258 const line = iter.parent_decl.src_line + declaration.line_offset; 4259 const decl_node = iter.parent_decl.relativeToNodeIndex(pl_node.src_node); 4260 4261 // Every Decl needs a name. 4262 const decl_name: InternPool.NullTerminatedString, const kind: Decl.Kind, const is_named_test: bool = switch (declaration.name) { 4263 .@"comptime" => info: { 4264 if (iter.pass != .unnamed) return; 4265 const i = iter.comptime_index; 4266 iter.comptime_index += 1; 4267 break :info .{ 4268 try iter.avoidNameConflict("comptime_{d}", .{i}), 4269 .@"comptime", 4270 false, 4271 }; 4272 }, 4273 .@"usingnamespace" => info: { 4274 // TODO: this isn't right! These should be considered unnamed. Name conflicts can happen here. 4275 // The problem is, we need to preserve the decl ordering for `@typeInfo`. 4276 // I'm not bothering to fix this now, since some upcoming changes will change this code significantly anyway. 4277 if (iter.pass != .named) return; 4278 const i = iter.usingnamespace_index; 4279 iter.usingnamespace_index += 1; 4280 break :info .{ 4281 try iter.avoidNameConflict("usingnamespace_{d}", .{i}), 4282 .@"usingnamespace", 4283 false, 4284 }; 4285 }, 4286 .unnamed_test => info: { 4287 if (iter.pass != .unnamed) return; 4288 const i = iter.unnamed_test_index; 4289 iter.unnamed_test_index += 1; 4290 break :info .{ 4291 try iter.avoidNameConflict("test_{d}", .{i}), 4292 .@"test", 4293 false, 4294 }; 4295 }, 4296 .decltest => info: { 4297 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. 4298 if (iter.pass != .unnamed) return; 4299 assert(declaration.flags.has_doc_comment); 4300 const name = zir.nullTerminatedString(@enumFromInt(zir.extra[extra.end])); 4301 break :info .{ 4302 try iter.avoidNameConflict("decltest.{s}", .{name}), 4303 .@"test", 4304 true, 4305 }; 4306 }, 4307 _ => if (declaration.name.isNamedTest(zir)) info: { 4308 // We consider these to be unnamed since the decl name can be adjusted to avoid conflicts if necessary. 4309 if (iter.pass != .unnamed) return; 4310 break :info .{ 4311 try iter.avoidNameConflict("test.{s}", .{zir.nullTerminatedString(declaration.name.toString(zir).?)}), 4312 .@"test", 4313 true, 4314 }; 4315 } else info: { 4316 if (iter.pass != .named) return; 4317 const name = try ip.getOrPutString( 4318 gpa, 4319 zir.nullTerminatedString(declaration.name.toString(zir).?), 4320 .no_embedded_nulls, 4321 ); 4322 try iter.seen_decls.putNoClobber(gpa, name, {}); 4323 break :info .{ 4324 name, 4325 .named, 4326 false, 4327 }; 4328 }, 4329 }; 4330 4331 switch (kind) { 4332 .@"usingnamespace" => try namespace.usingnamespace_set.ensureUnusedCapacity(gpa, 1), 4333 .@"test" => try zcu.test_functions.ensureUnusedCapacity(gpa, 1), 4334 else => {}, 4335 } 4336 4337 const tracked_inst = try ip.trackZir(gpa, iter.parent_decl.getFileScope(zcu), decl_inst); 4338 4339 // We create a Decl for it regardless of analysis status. 4340 4341 const prev_exported, const decl_index = if (iter.existing_by_inst.get(tracked_inst)) |decl_index| decl_index: { 4342 // We need only update this existing Decl. 4343 const decl = zcu.declPtr(decl_index); 4344 const was_exported = decl.is_exported; 4345 assert(decl.kind == kind); // ZIR tracking should preserve this 4346 decl.name = decl_name; 4347 decl.src_node = decl_node; 4348 decl.src_line = line; 4349 decl.is_pub = declaration.flags.is_pub; 4350 decl.is_exported = declaration.flags.is_export; 4351 break :decl_index .{ was_exported, decl_index }; 4352 } else decl_index: { 4353 // Create and set up a new Decl. 4354 const new_decl_index = try zcu.allocateNewDecl(namespace_index, decl_node); 4355 const new_decl = zcu.declPtr(new_decl_index); 4356 new_decl.kind = kind; 4357 new_decl.name = decl_name; 4358 new_decl.src_line = line; 4359 new_decl.is_pub = declaration.flags.is_pub; 4360 new_decl.is_exported = declaration.flags.is_export; 4361 new_decl.zir_decl_index = tracked_inst.toOptional(); 4362 break :decl_index .{ false, new_decl_index }; 4363 }; 4364 4365 const decl = zcu.declPtr(decl_index); 4366 4367 namespace.decls.putAssumeCapacityNoClobberContext(decl_index, {}, .{ .zcu = zcu }); 4368 4369 const comp = zcu.comp; 4370 const decl_mod = namespace.file_scope.mod; 4371 const want_analysis = declaration.flags.is_export or switch (kind) { 4372 .anon => unreachable, 4373 .@"comptime" => true, 4374 .@"usingnamespace" => a: { 4375 namespace.usingnamespace_set.putAssumeCapacityNoClobber(decl_index, declaration.flags.is_pub); 4376 break :a true; 4377 }, 4378 .named => false, 4379 .@"test" => a: { 4380 if (!comp.config.is_test) break :a false; 4381 if (decl_mod != zcu.main_mod) break :a false; 4382 if (is_named_test and comp.test_filters.len > 0) { 4383 const decl_fqn = try namespace.fullyQualifiedName(zcu, decl_name); 4384 const decl_fqn_slice = decl_fqn.toSlice(ip); 4385 for (comp.test_filters) |test_filter| { 4386 if (mem.indexOf(u8, decl_fqn_slice, test_filter)) |_| break; 4387 } else break :a false; 4388 } 4389 zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update 4390 break :a true; 4391 }, 4392 }; 4393 4394 if (want_analysis) { 4395 // We will not queue analysis if the decl has been analyzed on a previous update and 4396 // `is_export` is unchanged. In this case, the incremental update mechanism will handle 4397 // re-analysis for us if necessary. 4398 if (prev_exported != declaration.flags.is_export or decl.analysis == .unreferenced) { 4399 log.debug("scanDecl queue analyze_decl file='{s}' decl_name='{}' decl_index={d}", .{ 4400 namespace.file_scope.sub_file_path, decl_name.fmt(ip), decl_index, 4401 }); 4402 comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = decl_index }); 4403 } 4404 } 4405 4406 if (decl.getOwnedFunction(zcu) != null) { 4407 // TODO this logic is insufficient; namespaces we don't re-scan may still require 4408 // updated line numbers. Look into this! 4409 // TODO Look into detecting when this would be unnecessary by storing enough state 4410 // in `Decl` to notice that the line number did not change. 4411 comp.work_queue.writeItemAssumeCapacity(.{ .update_line_number = decl_index }); 4412 } 4413 } 4414 4415 /// Cancel the creation of an anon decl and delete any references to it. 4416 /// If other decls depend on this decl, they must be aborted first. 4417 pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void { 4418 assert(!mod.declIsRoot(decl_index)); 4419 mod.destroyDecl(decl_index); 4420 } 4421 4422 /// Finalize the creation of an anon decl. 4423 pub fn finalizeAnonDecl(mod: *Module, decl_index: Decl.Index) Allocator.Error!void { 4424 if (mod.declPtr(decl_index).typeOf(mod).isFnOrHasRuntimeBits(mod)) { 4425 try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl_index }); 4426 } 4427 } 4428 4429 /// Delete all the Export objects that are caused by this Decl. Re-analysis of 4430 /// this Decl will cause them to be re-created (or not). 4431 fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void { 4432 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value; 4433 4434 for (export_owners.items) |exp| { 4435 switch (exp.exported) { 4436 .decl_index => |exported_decl_index| { 4437 if (mod.decl_exports.getPtr(exported_decl_index)) |export_list| { 4438 // Remove exports with owner_decl matching the regenerating decl. 4439 const list = export_list.items; 4440 var i: usize = 0; 4441 var new_len = list.len; 4442 while (i < new_len) { 4443 if (list[i].owner_decl == decl_index) { 4444 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); 4445 new_len -= 1; 4446 } else { 4447 i += 1; 4448 } 4449 } 4450 export_list.shrinkAndFree(mod.gpa, new_len); 4451 if (new_len == 0) { 4452 assert(mod.decl_exports.swapRemove(exported_decl_index)); 4453 } 4454 } 4455 }, 4456 .value => |value| { 4457 if (mod.value_exports.getPtr(value)) |export_list| { 4458 // Remove exports with owner_decl matching the regenerating decl. 4459 const list = export_list.items; 4460 var i: usize = 0; 4461 var new_len = list.len; 4462 while (i < new_len) { 4463 if (list[i].owner_decl == decl_index) { 4464 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); 4465 new_len -= 1; 4466 } else { 4467 i += 1; 4468 } 4469 } 4470 export_list.shrinkAndFree(mod.gpa, new_len); 4471 if (new_len == 0) { 4472 assert(mod.value_exports.swapRemove(value)); 4473 } 4474 } 4475 }, 4476 } 4477 if (mod.comp.bin_file) |lf| { 4478 try lf.deleteDeclExport(decl_index, exp.opts.name); 4479 } 4480 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { 4481 failed_kv.value.destroy(mod.gpa); 4482 } 4483 mod.gpa.destroy(exp); 4484 } 4485 export_owners.deinit(mod.gpa); 4486 } 4487 4488 pub fn analyzeFnBody(mod: *Module, func_index: InternPool.Index, arena: Allocator) SemaError!Air { 4489 const tracy = trace(@src()); 4490 defer tracy.end(); 4491 4492 const gpa = mod.gpa; 4493 const ip = &mod.intern_pool; 4494 const func = mod.funcInfo(func_index); 4495 const decl_index = func.owner_decl; 4496 const decl = mod.declPtr(decl_index); 4497 4498 log.debug("func name '{}'", .{(try decl.fullyQualifiedName(mod)).fmt(ip)}); 4499 defer blk: { 4500 log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(mod) catch break :blk).fmt(ip)}); 4501 } 4502 4503 mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.Depender.wrap(.{ .func = func_index })); 4504 4505 var comptime_err_ret_trace = std.ArrayList(SrcLoc).init(gpa); 4506 defer comptime_err_ret_trace.deinit(); 4507 4508 // In the case of a generic function instance, this is the type of the 4509 // instance, which has comptime parameters elided. In other words, it is 4510 // the runtime-known parameters only, not to be confused with the 4511 // generic_owner function type, which potentially has more parameters, 4512 // including comptime parameters. 4513 const fn_ty = decl.typeOf(mod); 4514 const fn_ty_info = mod.typeToFunc(fn_ty).?; 4515 4516 var sema: Sema = .{ 4517 .mod = mod, 4518 .gpa = gpa, 4519 .arena = arena, 4520 .code = decl.getFileScope(mod).zir, 4521 .owner_decl = decl, 4522 .owner_decl_index = decl_index, 4523 .func_index = func_index, 4524 .func_is_naked = fn_ty_info.cc == .Naked, 4525 .fn_ret_ty = Type.fromInterned(fn_ty_info.return_type), 4526 .fn_ret_ty_ies = null, 4527 .owner_func_index = func_index, 4528 .branch_quota = @max(func.branchQuota(ip).*, Sema.default_branch_quota), 4529 .comptime_err_ret_trace = &comptime_err_ret_trace, 4530 }; 4531 defer sema.deinit(); 4532 4533 // Every runtime function has a dependency on the source of the Decl it originates from. 4534 // It also depends on the value of its owner Decl. 4535 try sema.declareDependency(.{ .src_hash = decl.zir_decl_index.unwrap().? }); 4536 try sema.declareDependency(.{ .decl_val = decl_index }); 4537 4538 if (func.analysis(ip).inferred_error_set) { 4539 const ies = try arena.create(Sema.InferredErrorSet); 4540 ies.* = .{ .func = func_index }; 4541 sema.fn_ret_ty_ies = ies; 4542 } 4543 4544 // reset in case calls to errorable functions are removed. 4545 func.analysis(ip).calls_or_awaits_errorable_fn = false; 4546 4547 // First few indexes of extra are reserved and set at the end. 4548 const reserved_count = @typeInfo(Air.ExtraIndex).Enum.fields.len; 4549 try sema.air_extra.ensureTotalCapacity(gpa, reserved_count); 4550 sema.air_extra.items.len += reserved_count; 4551 4552 var inner_block: Sema.Block = .{ 4553 .parent = null, 4554 .sema = &sema, 4555 .src_decl = decl_index, 4556 .namespace = decl.src_namespace, 4557 .instructions = .{}, 4558 .inlining = null, 4559 .is_comptime = false, 4560 }; 4561 defer inner_block.instructions.deinit(gpa); 4562 4563 const fn_info = sema.code.getFnInfo(func.zirBodyInst(ip).resolve(ip)); 4564 4565 // Here we are performing "runtime semantic analysis" for a function body, which means 4566 // we must map the parameter ZIR instructions to `arg` AIR instructions. 4567 // AIR requires the `arg` parameters to be the first N instructions. 4568 // This could be a generic function instantiation, however, in which case we need to 4569 // map the comptime parameters to constant values and only emit arg AIR instructions 4570 // for the runtime ones. 4571 const runtime_params_len = fn_ty_info.param_types.len; 4572 try inner_block.instructions.ensureTotalCapacityPrecise(gpa, runtime_params_len); 4573 try sema.air_instructions.ensureUnusedCapacity(gpa, fn_info.total_params_len); 4574 try sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); 4575 4576 // In the case of a generic function instance, pre-populate all the comptime args. 4577 if (func.comptime_args.len != 0) { 4578 for ( 4579 fn_info.param_body[0..func.comptime_args.len], 4580 func.comptime_args.get(ip), 4581 ) |inst, comptime_arg| { 4582 if (comptime_arg == .none) continue; 4583 sema.inst_map.putAssumeCapacityNoClobber(inst, Air.internedToRef(comptime_arg)); 4584 } 4585 } 4586 4587 const src_params_len = if (func.comptime_args.len != 0) 4588 func.comptime_args.len 4589 else 4590 runtime_params_len; 4591 4592 var runtime_param_index: usize = 0; 4593 for (fn_info.param_body[0..src_params_len], 0..) |inst, src_param_index| { 4594 const gop = sema.inst_map.getOrPutAssumeCapacity(inst); 4595 if (gop.found_existing) continue; // provided above by comptime arg 4596 4597 const param_ty = fn_ty_info.param_types.get(ip)[runtime_param_index]; 4598 runtime_param_index += 1; 4599 4600 const opt_opv = sema.typeHasOnePossibleValue(Type.fromInterned(param_ty)) catch |err| switch (err) { 4601 error.NeededSourceLocation => unreachable, 4602 error.GenericPoison => unreachable, 4603 error.ComptimeReturn => unreachable, 4604 error.ComptimeBreak => unreachable, 4605 else => |e| return e, 4606 }; 4607 if (opt_opv) |opv| { 4608 gop.value_ptr.* = Air.internedToRef(opv.toIntern()); 4609 continue; 4610 } 4611 const arg_index: Air.Inst.Index = @enumFromInt(sema.air_instructions.len); 4612 gop.value_ptr.* = arg_index.toRef(); 4613 inner_block.instructions.appendAssumeCapacity(arg_index); 4614 sema.air_instructions.appendAssumeCapacity(.{ 4615 .tag = .arg, 4616 .data = .{ .arg = .{ 4617 .ty = Air.internedToRef(param_ty), 4618 .src_index = @intCast(src_param_index), 4619 } }, 4620 }); 4621 } 4622 4623 func.analysis(ip).state = .in_progress; 4624 4625 const last_arg_index = inner_block.instructions.items.len; 4626 4627 // Save the error trace as our first action in the function. 4628 // If this is unnecessary after all, Liveness will clean it up for us. 4629 const error_return_trace_index = try sema.analyzeSaveErrRetIndex(&inner_block); 4630 sema.error_return_trace_index_on_fn_entry = error_return_trace_index; 4631 inner_block.error_return_trace_index = error_return_trace_index; 4632 4633 sema.analyzeFnBody(&inner_block, fn_info.body) catch |err| switch (err) { 4634 // TODO make these unreachable instead of @panic 4635 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), 4636 error.GenericPoison => @panic("zig compiler bug: GenericPoison"), 4637 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"), 4638 else => |e| return e, 4639 }; 4640 4641 for (sema.unresolved_inferred_allocs.keys()) |ptr_inst| { 4642 // The lack of a resolve_inferred_alloc means that this instruction 4643 // is unused so it just has to be a no-op. 4644 sema.air_instructions.set(@intFromEnum(ptr_inst), .{ 4645 .tag = .alloc, 4646 .data = .{ .ty = Type.single_const_pointer_to_comptime_int }, 4647 }); 4648 } 4649 4650 // If we don't get an error return trace from a caller, create our own. 4651 if (func.analysis(ip).calls_or_awaits_errorable_fn and 4652 mod.comp.config.any_error_tracing and 4653 !sema.fn_ret_ty.isError(mod)) 4654 { 4655 sema.setupErrorReturnTrace(&inner_block, last_arg_index) catch |err| switch (err) { 4656 // TODO make these unreachable instead of @panic 4657 error.NeededSourceLocation => @panic("zig compiler bug: NeededSourceLocation"), 4658 error.GenericPoison => @panic("zig compiler bug: GenericPoison"), 4659 error.ComptimeReturn => @panic("zig compiler bug: ComptimeReturn"), 4660 error.ComptimeBreak => @panic("zig compiler bug: ComptimeBreak"), 4661 else => |e| return e, 4662 }; 4663 } 4664 4665 // Copy the block into place and mark that as the main block. 4666 try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).Struct.fields.len + 4667 inner_block.instructions.items.len); 4668 const main_block_index = sema.addExtraAssumeCapacity(Air.Block{ 4669 .body_len = @intCast(inner_block.instructions.items.len), 4670 }); 4671 sema.air_extra.appendSliceAssumeCapacity(@ptrCast(inner_block.instructions.items)); 4672 sema.air_extra.items[@intFromEnum(Air.ExtraIndex.main_block)] = main_block_index; 4673 4674 // Resolving inferred error sets is done *before* setting the function 4675 // state to success, so that "unable to resolve inferred error set" errors 4676 // can be emitted here. 4677 if (sema.fn_ret_ty_ies) |ies| { 4678 sema.resolveInferredErrorSetPtr(&inner_block, LazySrcLoc.nodeOffset(0), ies) catch |err| switch (err) { 4679 error.NeededSourceLocation => unreachable, 4680 error.GenericPoison => unreachable, 4681 error.ComptimeReturn => unreachable, 4682 error.ComptimeBreak => unreachable, 4683 error.AnalysisFail => { 4684 // In this case our function depends on a type that had a compile error. 4685 // We should not try to lower this function. 4686 decl.analysis = .dependency_failure; 4687 return error.AnalysisFail; 4688 }, 4689 else => |e| return e, 4690 }; 4691 assert(ies.resolved != .none); 4692 ip.funcIesResolved(func_index).* = ies.resolved; 4693 } 4694 4695 func.analysis(ip).state = .success; 4696 4697 // Finally we must resolve the return type and parameter types so that backends 4698 // have full access to type information. 4699 // Crucially, this happens *after* we set the function state to success above, 4700 // so that dependencies on the function body will now be satisfied rather than 4701 // result in circular dependency errors. 4702 sema.resolveFnTypes(fn_ty) catch |err| switch (err) { 4703 error.NeededSourceLocation => unreachable, 4704 error.GenericPoison => unreachable, 4705 error.ComptimeReturn => unreachable, 4706 error.ComptimeBreak => unreachable, 4707 error.AnalysisFail => { 4708 // In this case our function depends on a type that had a compile error. 4709 // We should not try to lower this function. 4710 decl.analysis = .dependency_failure; 4711 return error.AnalysisFail; 4712 }, 4713 else => |e| return e, 4714 }; 4715 4716 // Similarly, resolve any queued up types that were requested to be resolved for 4717 // the backends. 4718 for (sema.types_to_resolve.keys()) |ty| { 4719 sema.resolveTypeFully(Type.fromInterned(ty)) catch |err| switch (err) { 4720 error.NeededSourceLocation => unreachable, 4721 error.GenericPoison => unreachable, 4722 error.ComptimeReturn => unreachable, 4723 error.ComptimeBreak => unreachable, 4724 error.AnalysisFail => { 4725 // In this case our function depends on a type that had a compile error. 4726 // We should not try to lower this function. 4727 decl.analysis = .dependency_failure; 4728 return error.AnalysisFail; 4729 }, 4730 else => |e| return e, 4731 }; 4732 } 4733 4734 return .{ 4735 .instructions = sema.air_instructions.toOwnedSlice(), 4736 .extra = try sema.air_extra.toOwnedSlice(gpa), 4737 }; 4738 } 4739 4740 pub fn createNamespace(mod: *Module, initialization: Namespace) !Namespace.Index { 4741 return mod.intern_pool.createNamespace(mod.gpa, initialization); 4742 } 4743 4744 pub fn destroyNamespace(mod: *Module, index: Namespace.Index) void { 4745 return mod.intern_pool.destroyNamespace(mod.gpa, index); 4746 } 4747 4748 pub fn allocateNewDecl( 4749 mod: *Module, 4750 namespace: Namespace.Index, 4751 src_node: Ast.Node.Index, 4752 ) !Decl.Index { 4753 const ip = &mod.intern_pool; 4754 const gpa = mod.gpa; 4755 const decl_index = try ip.createDecl(gpa, .{ 4756 .name = undefined, 4757 .src_namespace = namespace, 4758 .src_node = src_node, 4759 .src_line = undefined, 4760 .has_tv = false, 4761 .owns_tv = false, 4762 .val = undefined, 4763 .alignment = undefined, 4764 .@"linksection" = .none, 4765 .@"addrspace" = .generic, 4766 .analysis = .unreferenced, 4767 .zir_decl_index = .none, 4768 .is_pub = false, 4769 .is_exported = false, 4770 .kind = .anon, 4771 }); 4772 4773 if (mod.emit_h) |mod_emit_h| { 4774 if (@intFromEnum(decl_index) >= mod_emit_h.allocated_emit_h.len) { 4775 try mod_emit_h.allocated_emit_h.append(gpa, .{}); 4776 assert(@intFromEnum(decl_index) == mod_emit_h.allocated_emit_h.len); 4777 } 4778 } 4779 4780 return decl_index; 4781 } 4782 4783 pub fn getErrorValue( 4784 mod: *Module, 4785 name: InternPool.NullTerminatedString, 4786 ) Allocator.Error!ErrorInt { 4787 const gop = try mod.global_error_set.getOrPut(mod.gpa, name); 4788 return @as(ErrorInt, @intCast(gop.index)); 4789 } 4790 4791 pub fn getErrorValueFromSlice( 4792 mod: *Module, 4793 name: []const u8, 4794 ) Allocator.Error!ErrorInt { 4795 const interned_name = try mod.intern_pool.getOrPutString(mod.gpa, name); 4796 return getErrorValue(mod, interned_name); 4797 } 4798 4799 pub fn errorSetBits(mod: *Module) u16 { 4800 if (mod.error_limit == 0) return 0; 4801 return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error 4802 } 4803 4804 pub fn initNewAnonDecl( 4805 mod: *Module, 4806 new_decl_index: Decl.Index, 4807 src_line: u32, 4808 val: Value, 4809 name: InternPool.NullTerminatedString, 4810 ) Allocator.Error!void { 4811 const new_decl = mod.declPtr(new_decl_index); 4812 4813 new_decl.name = name; 4814 new_decl.src_line = src_line; 4815 new_decl.val = val; 4816 new_decl.alignment = .none; 4817 new_decl.@"linksection" = .none; 4818 new_decl.has_tv = true; 4819 new_decl.analysis = .complete; 4820 } 4821 4822 pub fn errNoteNonLazy( 4823 mod: *Module, 4824 src_loc: SrcLoc, 4825 parent: *ErrorMsg, 4826 comptime format: []const u8, 4827 args: anytype, 4828 ) error{OutOfMemory}!void { 4829 if (src_loc.lazy == .unneeded) { 4830 assert(parent.src_loc.lazy == .unneeded); 4831 return; 4832 } 4833 const msg = try std.fmt.allocPrint(mod.gpa, format, args); 4834 errdefer mod.gpa.free(msg); 4835 4836 parent.notes = try mod.gpa.realloc(parent.notes, parent.notes.len + 1); 4837 parent.notes[parent.notes.len - 1] = .{ 4838 .src_loc = src_loc, 4839 .msg = msg, 4840 }; 4841 } 4842 4843 /// Deprecated. There is no global target for a Zig Compilation Unit. Instead, 4844 /// look up the target based on the Module that contains the source code being 4845 /// analyzed. 4846 pub fn getTarget(zcu: Module) Target { 4847 return zcu.root_mod.resolved_target.result; 4848 } 4849 4850 /// Deprecated. There is no global optimization mode for a Zig Compilation 4851 /// Unit. Instead, look up the optimization mode based on the Module that 4852 /// contains the source code being analyzed. 4853 pub fn optimizeMode(zcu: Module) std.builtin.OptimizeMode { 4854 return zcu.root_mod.optimize_mode; 4855 } 4856 4857 fn lockAndClearFileCompileError(mod: *Module, file: *File) void { 4858 switch (file.status) { 4859 .success_zir, .retryable_failure => {}, 4860 .never_loaded, .parse_failure, .astgen_failure => { 4861 mod.comp.mutex.lock(); 4862 defer mod.comp.mutex.unlock(); 4863 if (mod.failed_files.fetchSwapRemove(file)) |kv| { 4864 if (kv.value) |msg| msg.destroy(mod.gpa); // Delete previous error message. 4865 } 4866 }, 4867 } 4868 } 4869 4870 pub const SwitchProngSrc = union(enum) { 4871 /// The item for a scalar prong. 4872 scalar: u32, 4873 /// A given single item for a multi prong. 4874 multi: Multi, 4875 /// A given range item for a multi prong. 4876 range: Multi, 4877 /// The item for the special prong. 4878 special, 4879 /// The main capture for a scalar prong. 4880 scalar_capture: u32, 4881 /// The main capture for a multi prong. 4882 multi_capture: u32, 4883 /// The main capture for the special prong. 4884 special_capture, 4885 /// The tag capture for a scalar prong. 4886 scalar_tag_capture: u32, 4887 /// The tag capture for a multi prong. 4888 multi_tag_capture: u32, 4889 /// The tag capture for the special prong. 4890 special_tag_capture, 4891 4892 pub const Multi = struct { 4893 prong: u32, 4894 item: u32, 4895 }; 4896 4897 pub const RangeExpand = enum { none, first, last }; 4898 4899 /// This function is intended to be called only when it is certain that we need 4900 /// the LazySrcLoc in order to emit a compile error. 4901 pub fn resolve( 4902 prong_src: SwitchProngSrc, 4903 mod: *Module, 4904 decl: *Decl, 4905 switch_node_offset: i32, 4906 /// Ignored if `prong_src` is not `.range` 4907 range_expand: RangeExpand, 4908 ) LazySrcLoc { 4909 @setCold(true); 4910 const gpa = mod.gpa; 4911 const tree = decl.getFileScope(mod).getTree(gpa) catch |err| { 4912 // In this case we emit a warning + a less precise source location. 4913 log.warn("unable to load {s}: {s}", .{ 4914 decl.getFileScope(mod).sub_file_path, @errorName(err), 4915 }); 4916 return LazySrcLoc.nodeOffset(0); 4917 }; 4918 const switch_node = decl.relativeToNodeIndex(switch_node_offset); 4919 const main_tokens = tree.nodes.items(.main_token); 4920 const node_datas = tree.nodes.items(.data); 4921 const node_tags = tree.nodes.items(.tag); 4922 const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange); 4923 const case_nodes = tree.extra_data[extra.start..extra.end]; 4924 4925 var multi_i: u32 = 0; 4926 var scalar_i: u32 = 0; 4927 const case_node = for (case_nodes) |case_node| { 4928 const case = tree.fullSwitchCase(case_node).?; 4929 4930 const is_special = special: { 4931 if (case.ast.values.len == 0) break :special true; 4932 if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .identifier) { 4933 break :special mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"); 4934 } 4935 break :special false; 4936 }; 4937 4938 if (is_special) { 4939 switch (prong_src) { 4940 .special, .special_capture, .special_tag_capture => break case_node, 4941 else => continue, 4942 } 4943 } 4944 4945 const is_multi = case.ast.values.len != 1 or 4946 node_tags[case.ast.values[0]] == .switch_range; 4947 4948 switch (prong_src) { 4949 .scalar, 4950 .scalar_capture, 4951 .scalar_tag_capture, 4952 => |i| if (!is_multi and i == scalar_i) break case_node, 4953 4954 .multi_capture, 4955 .multi_tag_capture, 4956 => |i| if (is_multi and i == multi_i) break case_node, 4957 4958 .multi, 4959 .range, 4960 => |m| if (is_multi and m.prong == multi_i) break case_node, 4961 4962 .special, 4963 .special_capture, 4964 .special_tag_capture, 4965 => {}, 4966 } 4967 4968 if (is_multi) { 4969 multi_i += 1; 4970 } else { 4971 scalar_i += 1; 4972 } 4973 } else unreachable; 4974 4975 const case = tree.fullSwitchCase(case_node).?; 4976 4977 switch (prong_src) { 4978 .scalar, .special => return LazySrcLoc.nodeOffset( 4979 decl.nodeIndexToRelative(case.ast.values[0]), 4980 ), 4981 .multi => |m| { 4982 var item_i: u32 = 0; 4983 for (case.ast.values) |item_node| { 4984 if (node_tags[item_node] == .switch_range) continue; 4985 if (item_i == m.item) return LazySrcLoc.nodeOffset( 4986 decl.nodeIndexToRelative(item_node), 4987 ); 4988 item_i += 1; 4989 } 4990 unreachable; 4991 }, 4992 .range => |m| { 4993 var range_i: u32 = 0; 4994 for (case.ast.values) |range| { 4995 if (node_tags[range] != .switch_range) continue; 4996 if (range_i == m.item) switch (range_expand) { 4997 .none => return LazySrcLoc.nodeOffset( 4998 decl.nodeIndexToRelative(range), 4999 ), 5000 .first => return LazySrcLoc.nodeOffset( 5001 decl.nodeIndexToRelative(node_datas[range].lhs), 5002 ), 5003 .last => return LazySrcLoc.nodeOffset( 5004 decl.nodeIndexToRelative(node_datas[range].rhs), 5005 ), 5006 }; 5007 range_i += 1; 5008 } 5009 unreachable; 5010 }, 5011 .scalar_capture, .multi_capture, .special_capture => { 5012 return .{ .node_offset_switch_prong_capture = decl.nodeIndexToRelative(case_node) }; 5013 }, 5014 .scalar_tag_capture, .multi_tag_capture, .special_tag_capture => { 5015 return .{ .node_offset_switch_prong_tag_capture = decl.nodeIndexToRelative(case_node) }; 5016 }, 5017 } 5018 } 5019 }; 5020 5021 pub const PeerTypeCandidateSrc = union(enum) { 5022 /// Do not print out error notes for candidate sources 5023 none: void, 5024 /// When we want to know the the src of candidate i, look up at 5025 /// index i in this slice 5026 override: []const ?LazySrcLoc, 5027 /// resolvePeerTypes originates from a @TypeOf(...) call 5028 typeof_builtin_call_node_offset: i32, 5029 5030 pub fn resolve( 5031 self: PeerTypeCandidateSrc, 5032 mod: *Module, 5033 decl: *Decl, 5034 candidate_i: usize, 5035 ) ?LazySrcLoc { 5036 @setCold(true); 5037 const gpa = mod.gpa; 5038 5039 switch (self) { 5040 .none => { 5041 return null; 5042 }, 5043 .override => |candidate_srcs| { 5044 if (candidate_i >= candidate_srcs.len) 5045 return null; 5046 return candidate_srcs[candidate_i]; 5047 }, 5048 .typeof_builtin_call_node_offset => |node_offset| { 5049 switch (candidate_i) { 5050 0 => return LazySrcLoc{ .node_offset_builtin_call_arg0 = node_offset }, 5051 1 => return LazySrcLoc{ .node_offset_builtin_call_arg1 = node_offset }, 5052 2 => return LazySrcLoc{ .node_offset_builtin_call_arg2 = node_offset }, 5053 3 => return LazySrcLoc{ .node_offset_builtin_call_arg3 = node_offset }, 5054 4 => return LazySrcLoc{ .node_offset_builtin_call_arg4 = node_offset }, 5055 5 => return LazySrcLoc{ .node_offset_builtin_call_arg5 = node_offset }, 5056 else => {}, 5057 } 5058 5059 const tree = decl.getFileScope(mod).getTree(gpa) catch |err| { 5060 // In this case we emit a warning + a less precise source location. 5061 log.warn("unable to load {s}: {s}", .{ 5062 decl.getFileScope(mod).sub_file_path, @errorName(err), 5063 }); 5064 return LazySrcLoc.nodeOffset(0); 5065 }; 5066 const node = decl.relativeToNodeIndex(node_offset); 5067 const node_datas = tree.nodes.items(.data); 5068 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs]; 5069 5070 return LazySrcLoc{ .node_abs = params[candidate_i] }; 5071 }, 5072 } 5073 } 5074 }; 5075 5076 const FieldSrcQuery = struct { 5077 index: usize, 5078 range: enum { name, type, value, alignment } = .name, 5079 }; 5080 5081 fn queryFieldSrc( 5082 tree: Ast, 5083 query: FieldSrcQuery, 5084 file_scope: *File, 5085 container_decl: Ast.full.ContainerDecl, 5086 ) SrcLoc { 5087 var field_index: usize = 0; 5088 for (container_decl.ast.members) |member_node| { 5089 const field = tree.fullContainerField(member_node) orelse continue; 5090 if (field_index == query.index) { 5091 return switch (query.range) { 5092 .name => .{ 5093 .file_scope = file_scope, 5094 .parent_decl_node = 0, 5095 .lazy = .{ .token_abs = field.ast.main_token }, 5096 }, 5097 .type => .{ 5098 .file_scope = file_scope, 5099 .parent_decl_node = 0, 5100 .lazy = .{ .node_abs = field.ast.type_expr }, 5101 }, 5102 .value => .{ 5103 .file_scope = file_scope, 5104 .parent_decl_node = 0, 5105 .lazy = .{ .node_abs = field.ast.value_expr }, 5106 }, 5107 .alignment => .{ 5108 .file_scope = file_scope, 5109 .parent_decl_node = 0, 5110 .lazy = .{ .node_abs = field.ast.align_expr }, 5111 }, 5112 }; 5113 } 5114 field_index += 1; 5115 } 5116 unreachable; 5117 } 5118 5119 pub fn paramSrc( 5120 func_node_offset: i32, 5121 mod: *Module, 5122 decl: *Decl, 5123 param_i: usize, 5124 ) LazySrcLoc { 5125 @setCold(true); 5126 const gpa = mod.gpa; 5127 const tree = decl.getFileScope(mod).getTree(gpa) catch |err| { 5128 // In this case we emit a warning + a less precise source location. 5129 log.warn("unable to load {s}: {s}", .{ 5130 decl.getFileScope(mod).sub_file_path, @errorName(err), 5131 }); 5132 return LazySrcLoc.nodeOffset(0); 5133 }; 5134 const node = decl.relativeToNodeIndex(func_node_offset); 5135 var buf: [1]Ast.Node.Index = undefined; 5136 const full = tree.fullFnProto(&buf, node).?; 5137 var it = full.iterate(tree); 5138 var i: usize = 0; 5139 while (it.next()) |param| : (i += 1) { 5140 if (i == param_i) { 5141 if (param.anytype_ellipsis3) |some| { 5142 const main_token = tree.nodes.items(.main_token)[decl.src_node]; 5143 return .{ .token_offset_param = @as(i32, @bitCast(some)) - @as(i32, @bitCast(main_token)) }; 5144 } 5145 return .{ .node_offset_param = decl.nodeIndexToRelative(param.type_expr) }; 5146 } 5147 } 5148 unreachable; 5149 } 5150 5151 pub fn initSrc( 5152 mod: *Module, 5153 init_node_offset: i32, 5154 decl: *Decl, 5155 init_index: usize, 5156 ) LazySrcLoc { 5157 @setCold(true); 5158 const gpa = mod.gpa; 5159 const tree = decl.getFileScope(mod).getTree(gpa) catch |err| { 5160 // In this case we emit a warning + a less precise source location. 5161 log.warn("unable to load {s}: {s}", .{ 5162 decl.getFileScope(mod).sub_file_path, @errorName(err), 5163 }); 5164 return LazySrcLoc.nodeOffset(0); 5165 }; 5166 const node_tags = tree.nodes.items(.tag); 5167 const node = decl.relativeToNodeIndex(init_node_offset); 5168 var buf: [2]Ast.Node.Index = undefined; 5169 switch (node_tags[node]) { 5170 .array_init_one, 5171 .array_init_one_comma, 5172 .array_init_dot_two, 5173 .array_init_dot_two_comma, 5174 .array_init_dot, 5175 .array_init_dot_comma, 5176 .array_init, 5177 .array_init_comma, 5178 => { 5179 const full = tree.fullArrayInit(&buf, node).?.ast.elements; 5180 return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(full[init_index])); 5181 }, 5182 .struct_init_one, 5183 .struct_init_one_comma, 5184 .struct_init_dot_two, 5185 .struct_init_dot_two_comma, 5186 .struct_init_dot, 5187 .struct_init_dot_comma, 5188 .struct_init, 5189 .struct_init_comma, 5190 => { 5191 const full = tree.fullStructInit(&buf, node).?.ast.fields; 5192 return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(full[init_index]) }; 5193 }, 5194 else => return LazySrcLoc.nodeOffset(init_node_offset), 5195 } 5196 } 5197 5198 pub fn optionsSrc(mod: *Module, decl: *Decl, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc { 5199 @setCold(true); 5200 const gpa = mod.gpa; 5201 const tree = decl.getFileScope(mod).getTree(gpa) catch |err| { 5202 // In this case we emit a warning + a less precise source location. 5203 log.warn("unable to load {s}: {s}", .{ 5204 decl.getFileScope(mod).sub_file_path, @errorName(err), 5205 }); 5206 return LazySrcLoc.nodeOffset(0); 5207 }; 5208 5209 const o_i: struct { off: i32, i: u8 } = switch (base_src) { 5210 .node_offset_builtin_call_arg0 => |n| .{ .off = n, .i = 0 }, 5211 .node_offset_builtin_call_arg1 => |n| .{ .off = n, .i = 1 }, 5212 else => unreachable, 5213 }; 5214 5215 const node = decl.relativeToNodeIndex(o_i.off); 5216 const node_datas = tree.nodes.items(.data); 5217 const node_tags = tree.nodes.items(.tag); 5218 const arg_node = switch (node_tags[node]) { 5219 .builtin_call_two, .builtin_call_two_comma => switch (o_i.i) { 5220 0 => node_datas[node].lhs, 5221 1 => node_datas[node].rhs, 5222 else => unreachable, 5223 }, 5224 .builtin_call, .builtin_call_comma => tree.extra_data[node_datas[node].lhs + o_i.i], 5225 else => unreachable, 5226 }; 5227 var buf: [2]std.zig.Ast.Node.Index = undefined; 5228 const init_nodes = if (tree.fullStructInit(&buf, arg_node)) |struct_init| struct_init.ast.fields else return base_src; 5229 for (init_nodes) |init_node| { 5230 // . IDENTIFIER = init_node 5231 const name_token = tree.firstToken(init_node) - 2; 5232 const name = tree.tokenSlice(name_token); 5233 if (std.mem.eql(u8, name, wanted)) { 5234 return LazySrcLoc{ .node_offset_initializer = decl.nodeIndexToRelative(init_node) }; 5235 } 5236 } 5237 return base_src; 5238 } 5239 5240 /// Called from `Compilation.update`, after everything is done, just before 5241 /// reporting compile errors. In this function we emit exported symbol collision 5242 /// errors and communicate exported symbols to the linker backend. 5243 pub fn processExports(mod: *Module) !void { 5244 // Map symbol names to `Export` for name collision detection. 5245 var symbol_exports: SymbolExports = .{}; 5246 defer symbol_exports.deinit(mod.gpa); 5247 5248 for (mod.decl_exports.keys(), mod.decl_exports.values()) |exported_decl, exports_list| { 5249 const exported: Exported = .{ .decl_index = exported_decl }; 5250 try processExportsInner(mod, &symbol_exports, exported, exports_list.items); 5251 } 5252 5253 for (mod.value_exports.keys(), mod.value_exports.values()) |exported_value, exports_list| { 5254 const exported: Exported = .{ .value = exported_value }; 5255 try processExportsInner(mod, &symbol_exports, exported, exports_list.items); 5256 } 5257 } 5258 5259 const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export); 5260 5261 fn processExportsInner( 5262 zcu: *Zcu, 5263 symbol_exports: *SymbolExports, 5264 exported: Exported, 5265 exports: []const *Export, 5266 ) error{OutOfMemory}!void { 5267 const gpa = zcu.gpa; 5268 5269 for (exports) |new_export| { 5270 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); 5271 if (gop.found_existing) { 5272 new_export.status = .failed_retryable; 5273 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); 5274 const src_loc = new_export.getSrcLoc(zcu); 5275 const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{ 5276 new_export.opts.name.fmt(&zcu.intern_pool), 5277 }); 5278 errdefer msg.destroy(gpa); 5279 const other_export = gop.value_ptr.*; 5280 const other_src_loc = other_export.getSrcLoc(zcu); 5281 try zcu.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); 5282 zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg); 5283 new_export.status = .failed; 5284 } else { 5285 gop.value_ptr.* = new_export; 5286 } 5287 } 5288 if (zcu.comp.bin_file) |lf| { 5289 try handleUpdateExports(zcu, exports, lf.updateExports(zcu, exported, exports)); 5290 } else if (zcu.llvm_object) |llvm_object| { 5291 if (build_options.only_c) unreachable; 5292 try handleUpdateExports(zcu, exports, llvm_object.updateExports(zcu, exported, exports)); 5293 } 5294 } 5295 5296 fn handleUpdateExports( 5297 zcu: *Zcu, 5298 exports: []const *Export, 5299 result: link.File.UpdateExportsError!void, 5300 ) Allocator.Error!void { 5301 const gpa = zcu.gpa; 5302 result catch |err| switch (err) { 5303 error.OutOfMemory => return error.OutOfMemory, 5304 error.AnalysisFail => { 5305 const new_export = exports[0]; 5306 new_export.status = .failed_retryable; 5307 try zcu.failed_exports.ensureUnusedCapacity(gpa, 1); 5308 const src_loc = new_export.getSrcLoc(zcu); 5309 const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{ 5310 @errorName(err), 5311 }); 5312 zcu.failed_exports.putAssumeCapacityNoClobber(new_export, msg); 5313 }, 5314 }; 5315 } 5316 5317 pub fn populateTestFunctions( 5318 mod: *Module, 5319 main_progress_node: *std.Progress.Node, 5320 ) !void { 5321 const gpa = mod.gpa; 5322 const ip = &mod.intern_pool; 5323 const builtin_mod = mod.root_mod.getBuiltinDependency(); 5324 const builtin_file = (mod.importPkg(builtin_mod) catch unreachable).file; 5325 const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?); 5326 const builtin_namespace = mod.namespacePtr(root_decl.src_namespace); 5327 const test_functions_str = try ip.getOrPutString(gpa, "test_functions", .no_embedded_nulls); 5328 const decl_index = builtin_namespace.decls.getKeyAdapted( 5329 test_functions_str, 5330 DeclAdapter{ .zcu = mod }, 5331 ).?; 5332 { 5333 // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions` 5334 // was not referenced by start code. 5335 mod.sema_prog_node = main_progress_node.start("Semantic Analysis", 0); 5336 mod.sema_prog_node.activate(); 5337 defer { 5338 mod.sema_prog_node.end(); 5339 mod.sema_prog_node = undefined; 5340 } 5341 try mod.ensureDeclAnalyzed(decl_index); 5342 } 5343 const decl = mod.declPtr(decl_index); 5344 const test_fn_ty = decl.typeOf(mod).slicePtrFieldType(mod).childType(mod); 5345 5346 const array_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = array: { 5347 // Add mod.test_functions to an array decl then make the test_functions 5348 // decl reference it as a slice. 5349 const test_fn_vals = try gpa.alloc(InternPool.Index, mod.test_functions.count()); 5350 defer gpa.free(test_fn_vals); 5351 5352 for (test_fn_vals, mod.test_functions.keys()) |*test_fn_val, test_decl_index| { 5353 const test_decl = mod.declPtr(test_decl_index); 5354 const test_decl_name = try test_decl.fullyQualifiedName(mod); 5355 const test_decl_name_len = test_decl_name.length(ip); 5356 const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = n: { 5357 const test_name_ty = try mod.arrayType(.{ 5358 .len = test_decl_name_len, 5359 .child = .u8_type, 5360 }); 5361 const test_name_val = try mod.intern(.{ .aggregate = .{ 5362 .ty = test_name_ty.toIntern(), 5363 .storage = .{ .bytes = test_decl_name.toString() }, 5364 } }); 5365 break :n .{ 5366 .orig_ty = (try mod.singleConstPtrType(test_name_ty)).toIntern(), 5367 .val = test_name_val, 5368 }; 5369 }; 5370 5371 const test_fn_fields = .{ 5372 // name 5373 try mod.intern(.{ .slice = .{ 5374 .ty = .slice_const_u8_type, 5375 .ptr = try mod.intern(.{ .ptr = .{ 5376 .ty = .manyptr_const_u8_type, 5377 .base_addr = .{ .anon_decl = test_name_anon_decl }, 5378 .byte_offset = 0, 5379 } }), 5380 .len = try mod.intern(.{ .int = .{ 5381 .ty = .usize_type, 5382 .storage = .{ .u64 = test_decl_name_len }, 5383 } }), 5384 } }), 5385 // func 5386 try mod.intern(.{ .ptr = .{ 5387 .ty = try mod.intern(.{ .ptr_type = .{ 5388 .child = test_decl.typeOf(mod).toIntern(), 5389 .flags = .{ 5390 .is_const = true, 5391 }, 5392 } }), 5393 .base_addr = .{ .decl = test_decl_index }, 5394 .byte_offset = 0, 5395 } }), 5396 }; 5397 test_fn_val.* = try mod.intern(.{ .aggregate = .{ 5398 .ty = test_fn_ty.toIntern(), 5399 .storage = .{ .elems = &test_fn_fields }, 5400 } }); 5401 } 5402 5403 const array_ty = try mod.arrayType(.{ 5404 .len = test_fn_vals.len, 5405 .child = test_fn_ty.toIntern(), 5406 .sentinel = .none, 5407 }); 5408 const array_val = try mod.intern(.{ .aggregate = .{ 5409 .ty = array_ty.toIntern(), 5410 .storage = .{ .elems = test_fn_vals }, 5411 } }); 5412 break :array .{ 5413 .orig_ty = (try mod.singleConstPtrType(array_ty)).toIntern(), 5414 .val = array_val, 5415 }; 5416 }; 5417 5418 { 5419 const new_ty = try mod.ptrType(.{ 5420 .child = test_fn_ty.toIntern(), 5421 .flags = .{ 5422 .is_const = true, 5423 .size = .Slice, 5424 }, 5425 }); 5426 const new_val = decl.val; 5427 const new_init = try mod.intern(.{ .slice = .{ 5428 .ty = new_ty.toIntern(), 5429 .ptr = try mod.intern(.{ .ptr = .{ 5430 .ty = new_ty.slicePtrFieldType(mod).toIntern(), 5431 .base_addr = .{ .anon_decl = array_anon_decl }, 5432 .byte_offset = 0, 5433 } }), 5434 .len = (try mod.intValue(Type.usize, mod.test_functions.count())).toIntern(), 5435 } }); 5436 ip.mutateVarInit(decl.val.toIntern(), new_init); 5437 5438 // Since we are replacing the Decl's value we must perform cleanup on the 5439 // previous value. 5440 decl.val = new_val; 5441 decl.has_tv = true; 5442 } 5443 try mod.linkerUpdateDecl(decl_index); 5444 } 5445 5446 pub fn linkerUpdateDecl(zcu: *Zcu, decl_index: Decl.Index) !void { 5447 const comp = zcu.comp; 5448 5449 if (comp.bin_file) |lf| { 5450 lf.updateDecl(zcu, decl_index) catch |err| switch (err) { 5451 error.OutOfMemory => return error.OutOfMemory, 5452 error.AnalysisFail => { 5453 const decl = zcu.declPtr(decl_index); 5454 decl.analysis = .codegen_failure; 5455 }, 5456 else => { 5457 const decl = zcu.declPtr(decl_index); 5458 const gpa = zcu.gpa; 5459 try zcu.failed_decls.ensureUnusedCapacity(gpa, 1); 5460 zcu.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create( 5461 gpa, 5462 decl.srcLoc(zcu), 5463 "unable to codegen: {s}", 5464 .{@errorName(err)}, 5465 )); 5466 decl.analysis = .codegen_failure; 5467 try zcu.retryable_failures.append(zcu.gpa, InternPool.Depender.wrap(.{ .decl = decl_index })); 5468 }, 5469 }; 5470 } else if (zcu.llvm_object) |llvm_object| { 5471 if (build_options.only_c) unreachable; 5472 llvm_object.updateDecl(zcu, decl_index) catch |err| switch (err) { 5473 error.OutOfMemory => return error.OutOfMemory, 5474 error.AnalysisFail => { 5475 const decl = zcu.declPtr(decl_index); 5476 decl.analysis = .codegen_failure; 5477 }, 5478 }; 5479 } 5480 } 5481 5482 fn reportRetryableFileError( 5483 mod: *Module, 5484 file: *File, 5485 comptime format: []const u8, 5486 args: anytype, 5487 ) error{OutOfMemory}!void { 5488 file.status = .retryable_failure; 5489 5490 const err_msg = try ErrorMsg.create( 5491 mod.gpa, 5492 .{ 5493 .file_scope = file, 5494 .parent_decl_node = 0, 5495 .lazy = .entire_file, 5496 }, 5497 format, 5498 args, 5499 ); 5500 errdefer err_msg.destroy(mod.gpa); 5501 5502 mod.comp.mutex.lock(); 5503 defer mod.comp.mutex.unlock(); 5504 5505 const gop = try mod.failed_files.getOrPut(mod.gpa, file); 5506 if (gop.found_existing) { 5507 if (gop.value_ptr.*) |old_err_msg| { 5508 old_err_msg.destroy(mod.gpa); 5509 } 5510 } 5511 gop.value_ptr.* = err_msg; 5512 } 5513 5514 pub fn addGlobalAssembly(mod: *Module, decl_index: Decl.Index, source: []const u8) !void { 5515 const gop = try mod.global_assembly.getOrPut(mod.gpa, decl_index); 5516 if (gop.found_existing) { 5517 const new_value = try std.fmt.allocPrint(mod.gpa, "{s}\n{s}", .{ gop.value_ptr.*, source }); 5518 mod.gpa.free(gop.value_ptr.*); 5519 gop.value_ptr.* = new_value; 5520 } else { 5521 gop.value_ptr.* = try mod.gpa.dupe(u8, source); 5522 } 5523 } 5524 5525 pub fn getDeclExports(mod: Module, decl_index: Decl.Index) []const *Export { 5526 if (mod.decl_exports.get(decl_index)) |l| { 5527 return l.items; 5528 } else { 5529 return &[0]*Export{}; 5530 } 5531 } 5532 5533 pub const Feature = enum { 5534 panic_fn, 5535 panic_unwrap_error, 5536 safety_check_formatted, 5537 error_return_trace, 5538 is_named_enum_value, 5539 error_set_has_value, 5540 field_reordering, 5541 /// When this feature is supported, the backend supports the following AIR instructions: 5542 /// * `Air.Inst.Tag.add_safe` 5543 /// * `Air.Inst.Tag.sub_safe` 5544 /// * `Air.Inst.Tag.mul_safe` 5545 /// The motivation for this feature is that it makes AIR smaller, and makes it easier 5546 /// to generate better machine code in the backends. All backends should migrate to 5547 /// enabling this feature. 5548 safety_checked_instructions, 5549 }; 5550 5551 pub fn backendSupportsFeature(zcu: Module, feature: Feature) bool { 5552 const cpu_arch = zcu.root_mod.resolved_target.result.cpu.arch; 5553 const ofmt = zcu.root_mod.resolved_target.result.ofmt; 5554 const use_llvm = zcu.comp.config.use_llvm; 5555 return target_util.backendSupportsFeature(cpu_arch, ofmt, use_llvm, feature); 5556 } 5557 5558 /// Shortcut for calling `intern_pool.get`. 5559 pub fn intern(mod: *Module, key: InternPool.Key) Allocator.Error!InternPool.Index { 5560 return mod.intern_pool.get(mod.gpa, key); 5561 } 5562 5563 /// Shortcut for calling `intern_pool.getCoerced`. 5564 pub fn getCoerced(mod: *Module, val: Value, new_ty: Type) Allocator.Error!Value { 5565 return Value.fromInterned((try mod.intern_pool.getCoerced(mod.gpa, val.toIntern(), new_ty.toIntern()))); 5566 } 5567 5568 pub fn intType(mod: *Module, signedness: std.builtin.Signedness, bits: u16) Allocator.Error!Type { 5569 return Type.fromInterned((try intern(mod, .{ .int_type = .{ 5570 .signedness = signedness, 5571 .bits = bits, 5572 } }))); 5573 } 5574 5575 pub fn errorIntType(mod: *Module) std.mem.Allocator.Error!Type { 5576 return mod.intType(.unsigned, mod.errorSetBits()); 5577 } 5578 5579 pub fn arrayType(mod: *Module, info: InternPool.Key.ArrayType) Allocator.Error!Type { 5580 const i = try intern(mod, .{ .array_type = info }); 5581 return Type.fromInterned(i); 5582 } 5583 5584 pub fn vectorType(mod: *Module, info: InternPool.Key.VectorType) Allocator.Error!Type { 5585 const i = try intern(mod, .{ .vector_type = info }); 5586 return Type.fromInterned(i); 5587 } 5588 5589 pub fn optionalType(mod: *Module, child_type: InternPool.Index) Allocator.Error!Type { 5590 const i = try intern(mod, .{ .opt_type = child_type }); 5591 return Type.fromInterned(i); 5592 } 5593 5594 pub fn ptrType(mod: *Module, info: InternPool.Key.PtrType) Allocator.Error!Type { 5595 var canon_info = info; 5596 5597 if (info.flags.size == .C) canon_info.flags.is_allowzero = true; 5598 5599 // Canonicalize non-zero alignment. If it matches the ABI alignment of the pointee 5600 // type, we change it to 0 here. If this causes an assertion trip because the 5601 // pointee type needs to be resolved more, that needs to be done before calling 5602 // this ptr() function. 5603 if (info.flags.alignment != .none and 5604 info.flags.alignment == Type.fromInterned(info.child).abiAlignment(mod)) 5605 { 5606 canon_info.flags.alignment = .none; 5607 } 5608 5609 switch (info.flags.vector_index) { 5610 // Canonicalize host_size. If it matches the bit size of the pointee type, 5611 // we change it to 0 here. If this causes an assertion trip, the pointee type 5612 // needs to be resolved before calling this ptr() function. 5613 .none => if (info.packed_offset.host_size != 0) { 5614 const elem_bit_size = Type.fromInterned(info.child).bitSize(mod); 5615 assert(info.packed_offset.bit_offset + elem_bit_size <= info.packed_offset.host_size * 8); 5616 if (info.packed_offset.host_size * 8 == elem_bit_size) { 5617 canon_info.packed_offset.host_size = 0; 5618 } 5619 }, 5620 .runtime => {}, 5621 _ => assert(@intFromEnum(info.flags.vector_index) < info.packed_offset.host_size), 5622 } 5623 5624 return Type.fromInterned((try intern(mod, .{ .ptr_type = canon_info }))); 5625 } 5626 5627 pub fn singleMutPtrType(mod: *Module, child_type: Type) Allocator.Error!Type { 5628 return ptrType(mod, .{ .child = child_type.toIntern() }); 5629 } 5630 5631 pub fn singleConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type { 5632 return ptrType(mod, .{ 5633 .child = child_type.toIntern(), 5634 .flags = .{ 5635 .is_const = true, 5636 }, 5637 }); 5638 } 5639 5640 pub fn manyConstPtrType(mod: *Module, child_type: Type) Allocator.Error!Type { 5641 return ptrType(mod, .{ 5642 .child = child_type.toIntern(), 5643 .flags = .{ 5644 .size = .Many, 5645 .is_const = true, 5646 }, 5647 }); 5648 } 5649 5650 pub fn adjustPtrTypeChild(mod: *Module, ptr_ty: Type, new_child: Type) Allocator.Error!Type { 5651 var info = ptr_ty.ptrInfo(mod); 5652 info.child = new_child.toIntern(); 5653 return mod.ptrType(info); 5654 } 5655 5656 pub fn funcType(mod: *Module, key: InternPool.GetFuncTypeKey) Allocator.Error!Type { 5657 return Type.fromInterned((try mod.intern_pool.getFuncType(mod.gpa, key))); 5658 } 5659 5660 /// Use this for `anyframe->T` only. 5661 /// For `anyframe`, use the `InternPool.Index.anyframe` tag directly. 5662 pub fn anyframeType(mod: *Module, payload_ty: Type) Allocator.Error!Type { 5663 return Type.fromInterned((try intern(mod, .{ .anyframe_type = payload_ty.toIntern() }))); 5664 } 5665 5666 pub fn errorUnionType(mod: *Module, error_set_ty: Type, payload_ty: Type) Allocator.Error!Type { 5667 return Type.fromInterned((try intern(mod, .{ .error_union_type = .{ 5668 .error_set_type = error_set_ty.toIntern(), 5669 .payload_type = payload_ty.toIntern(), 5670 } }))); 5671 } 5672 5673 pub fn singleErrorSetType(mod: *Module, name: InternPool.NullTerminatedString) Allocator.Error!Type { 5674 const names: *const [1]InternPool.NullTerminatedString = &name; 5675 const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names); 5676 return Type.fromInterned(new_ty); 5677 } 5678 5679 /// Sorts `names` in place. 5680 pub fn errorSetFromUnsortedNames( 5681 mod: *Module, 5682 names: []InternPool.NullTerminatedString, 5683 ) Allocator.Error!Type { 5684 std.mem.sort( 5685 InternPool.NullTerminatedString, 5686 names, 5687 {}, 5688 InternPool.NullTerminatedString.indexLessThan, 5689 ); 5690 const new_ty = try mod.intern_pool.getErrorSetType(mod.gpa, names); 5691 return Type.fromInterned(new_ty); 5692 } 5693 5694 /// Supports only pointers, not pointer-like optionals. 5695 pub fn ptrIntValue(mod: *Module, ty: Type, x: u64) Allocator.Error!Value { 5696 assert(ty.zigTypeTag(mod) == .Pointer and !ty.isSlice(mod)); 5697 assert(x != 0 or ty.isAllowzeroPtr(mod)); 5698 const i = try intern(mod, .{ .ptr = .{ 5699 .ty = ty.toIntern(), 5700 .base_addr = .int, 5701 .byte_offset = x, 5702 } }); 5703 return Value.fromInterned(i); 5704 } 5705 5706 /// Creates an enum tag value based on the integer tag value. 5707 pub fn enumValue(mod: *Module, ty: Type, tag_int: InternPool.Index) Allocator.Error!Value { 5708 if (std.debug.runtime_safety) { 5709 const tag = ty.zigTypeTag(mod); 5710 assert(tag == .Enum); 5711 } 5712 const i = try intern(mod, .{ .enum_tag = .{ 5713 .ty = ty.toIntern(), 5714 .int = tag_int, 5715 } }); 5716 return Value.fromInterned(i); 5717 } 5718 5719 /// Creates an enum tag value based on the field index according to source code 5720 /// declaration order. 5721 pub fn enumValueFieldIndex(mod: *Module, ty: Type, field_index: u32) Allocator.Error!Value { 5722 const ip = &mod.intern_pool; 5723 const gpa = mod.gpa; 5724 const enum_type = ip.loadEnumType(ty.toIntern()); 5725 5726 if (enum_type.values.len == 0) { 5727 // Auto-numbered fields. 5728 return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{ 5729 .ty = ty.toIntern(), 5730 .int = try ip.get(gpa, .{ .int = .{ 5731 .ty = enum_type.tag_ty, 5732 .storage = .{ .u64 = field_index }, 5733 } }), 5734 } }))); 5735 } 5736 5737 return Value.fromInterned((try ip.get(gpa, .{ .enum_tag = .{ 5738 .ty = ty.toIntern(), 5739 .int = enum_type.values.get(ip)[field_index], 5740 } }))); 5741 } 5742 5743 pub fn undefValue(mod: *Module, ty: Type) Allocator.Error!Value { 5744 return Value.fromInterned((try mod.intern(.{ .undef = ty.toIntern() }))); 5745 } 5746 5747 pub fn undefRef(mod: *Module, ty: Type) Allocator.Error!Air.Inst.Ref { 5748 return Air.internedToRef((try mod.undefValue(ty)).toIntern()); 5749 } 5750 5751 pub fn intValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value { 5752 if (std.math.cast(u64, x)) |casted| return intValue_u64(mod, ty, casted); 5753 if (std.math.cast(i64, x)) |casted| return intValue_i64(mod, ty, casted); 5754 var limbs_buffer: [4]usize = undefined; 5755 var big_int = BigIntMutable.init(&limbs_buffer, x); 5756 return intValue_big(mod, ty, big_int.toConst()); 5757 } 5758 5759 pub fn intRef(mod: *Module, ty: Type, x: anytype) Allocator.Error!Air.Inst.Ref { 5760 return Air.internedToRef((try mod.intValue(ty, x)).toIntern()); 5761 } 5762 5763 pub fn intValue_big(mod: *Module, ty: Type, x: BigIntConst) Allocator.Error!Value { 5764 const i = try intern(mod, .{ .int = .{ 5765 .ty = ty.toIntern(), 5766 .storage = .{ .big_int = x }, 5767 } }); 5768 return Value.fromInterned(i); 5769 } 5770 5771 pub fn intValue_u64(mod: *Module, ty: Type, x: u64) Allocator.Error!Value { 5772 const i = try intern(mod, .{ .int = .{ 5773 .ty = ty.toIntern(), 5774 .storage = .{ .u64 = x }, 5775 } }); 5776 return Value.fromInterned(i); 5777 } 5778 5779 pub fn intValue_i64(mod: *Module, ty: Type, x: i64) Allocator.Error!Value { 5780 const i = try intern(mod, .{ .int = .{ 5781 .ty = ty.toIntern(), 5782 .storage = .{ .i64 = x }, 5783 } }); 5784 return Value.fromInterned(i); 5785 } 5786 5787 pub fn unionValue(mod: *Module, union_ty: Type, tag: Value, val: Value) Allocator.Error!Value { 5788 const i = try intern(mod, .{ .un = .{ 5789 .ty = union_ty.toIntern(), 5790 .tag = tag.toIntern(), 5791 .val = val.toIntern(), 5792 } }); 5793 return Value.fromInterned(i); 5794 } 5795 5796 /// This function casts the float representation down to the representation of the type, potentially 5797 /// losing data if the representation wasn't correct. 5798 pub fn floatValue(mod: *Module, ty: Type, x: anytype) Allocator.Error!Value { 5799 const storage: InternPool.Key.Float.Storage = switch (ty.floatBits(mod.getTarget())) { 5800 16 => .{ .f16 = @as(f16, @floatCast(x)) }, 5801 32 => .{ .f32 = @as(f32, @floatCast(x)) }, 5802 64 => .{ .f64 = @as(f64, @floatCast(x)) }, 5803 80 => .{ .f80 = @as(f80, @floatCast(x)) }, 5804 128 => .{ .f128 = @as(f128, @floatCast(x)) }, 5805 else => unreachable, 5806 }; 5807 const i = try intern(mod, .{ .float = .{ 5808 .ty = ty.toIntern(), 5809 .storage = storage, 5810 } }); 5811 return Value.fromInterned(i); 5812 } 5813 5814 pub fn nullValue(mod: *Module, opt_ty: Type) Allocator.Error!Value { 5815 const ip = &mod.intern_pool; 5816 assert(ip.isOptionalType(opt_ty.toIntern())); 5817 const result = try ip.get(mod.gpa, .{ .opt = .{ 5818 .ty = opt_ty.toIntern(), 5819 .val = .none, 5820 } }); 5821 return Value.fromInterned(result); 5822 } 5823 5824 pub fn smallestUnsignedInt(mod: *Module, max: u64) Allocator.Error!Type { 5825 return intType(mod, .unsigned, Type.smallestUnsignedBits(max)); 5826 } 5827 5828 /// Returns the smallest possible integer type containing both `min` and 5829 /// `max`. Asserts that neither value is undef. 5830 /// TODO: if #3806 is implemented, this becomes trivial 5831 pub fn intFittingRange(mod: *Module, min: Value, max: Value) !Type { 5832 assert(!min.isUndef(mod)); 5833 assert(!max.isUndef(mod)); 5834 5835 if (std.debug.runtime_safety) { 5836 assert(Value.order(min, max, mod).compare(.lte)); 5837 } 5838 5839 const sign = min.orderAgainstZero(mod) == .lt; 5840 5841 const min_val_bits = intBitsForValue(mod, min, sign); 5842 const max_val_bits = intBitsForValue(mod, max, sign); 5843 5844 return mod.intType( 5845 if (sign) .signed else .unsigned, 5846 @max(min_val_bits, max_val_bits), 5847 ); 5848 } 5849 5850 /// Given a value representing an integer, returns the number of bits necessary to represent 5851 /// this value in an integer. If `sign` is true, returns the number of bits necessary in a 5852 /// twos-complement integer; otherwise in an unsigned integer. 5853 /// Asserts that `val` is not undef. If `val` is negative, asserts that `sign` is true. 5854 pub fn intBitsForValue(mod: *Module, val: Value, sign: bool) u16 { 5855 assert(!val.isUndef(mod)); 5856 5857 const key = mod.intern_pool.indexToKey(val.toIntern()); 5858 switch (key.int.storage) { 5859 .i64 => |x| { 5860 if (std.math.cast(u64, x)) |casted| return Type.smallestUnsignedBits(casted) + @intFromBool(sign); 5861 assert(sign); 5862 // Protect against overflow in the following negation. 5863 if (x == std.math.minInt(i64)) return 64; 5864 return Type.smallestUnsignedBits(@as(u64, @intCast(-(x + 1)))) + 1; 5865 }, 5866 .u64 => |x| { 5867 return Type.smallestUnsignedBits(x) + @intFromBool(sign); 5868 }, 5869 .big_int => |big| { 5870 if (big.positive) return @as(u16, @intCast(big.bitCountAbs() + @intFromBool(sign))); 5871 5872 // Zero is still a possibility, in which case unsigned is fine 5873 if (big.eqlZero()) return 0; 5874 5875 return @as(u16, @intCast(big.bitCountTwosComp())); 5876 }, 5877 .lazy_align => |lazy_ty| { 5878 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiAlignment(mod).toByteUnits() orelse 0) + @intFromBool(sign); 5879 }, 5880 .lazy_size => |lazy_ty| { 5881 return Type.smallestUnsignedBits(Type.fromInterned(lazy_ty).abiSize(mod)) + @intFromBool(sign); 5882 }, 5883 } 5884 } 5885 5886 pub const AtomicPtrAlignmentError = error{ 5887 FloatTooBig, 5888 IntTooBig, 5889 BadType, 5890 OutOfMemory, 5891 }; 5892 5893 pub const AtomicPtrAlignmentDiagnostics = struct { 5894 bits: u16 = undefined, 5895 max_bits: u16 = undefined, 5896 }; 5897 5898 /// If ABI alignment of `ty` is OK for atomic operations, returns 0. 5899 /// Otherwise returns the alignment required on a pointer for the target 5900 /// to perform atomic operations. 5901 // TODO this function does not take into account CPU features, which can affect 5902 // this value. Audit this! 5903 pub fn atomicPtrAlignment( 5904 mod: *Module, 5905 ty: Type, 5906 diags: *AtomicPtrAlignmentDiagnostics, 5907 ) AtomicPtrAlignmentError!Alignment { 5908 const target = mod.getTarget(); 5909 const max_atomic_bits: u16 = switch (target.cpu.arch) { 5910 .avr, 5911 .msp430, 5912 .spu_2, 5913 => 16, 5914 5915 .arc, 5916 .arm, 5917 .armeb, 5918 .hexagon, 5919 .m68k, 5920 .le32, 5921 .mips, 5922 .mipsel, 5923 .nvptx, 5924 .powerpc, 5925 .powerpcle, 5926 .r600, 5927 .riscv32, 5928 .sparc, 5929 .sparcel, 5930 .tce, 5931 .tcele, 5932 .thumb, 5933 .thumbeb, 5934 .x86, 5935 .xcore, 5936 .amdil, 5937 .hsail, 5938 .spir, 5939 .kalimba, 5940 .lanai, 5941 .shave, 5942 .wasm32, 5943 .renderscript32, 5944 .csky, 5945 .spirv32, 5946 .dxil, 5947 .loongarch32, 5948 .xtensa, 5949 => 32, 5950 5951 .amdgcn, 5952 .bpfel, 5953 .bpfeb, 5954 .le64, 5955 .mips64, 5956 .mips64el, 5957 .nvptx64, 5958 .powerpc64, 5959 .powerpc64le, 5960 .riscv64, 5961 .sparc64, 5962 .s390x, 5963 .amdil64, 5964 .hsail64, 5965 .spir64, 5966 .wasm64, 5967 .renderscript64, 5968 .ve, 5969 .spirv64, 5970 .loongarch64, 5971 => 64, 5972 5973 .aarch64, 5974 .aarch64_be, 5975 .aarch64_32, 5976 => 128, 5977 5978 .x86_64 => if (std.Target.x86.featureSetHas(target.cpu.features, .cx16)) 128 else 64, 5979 }; 5980 5981 const int_ty = switch (ty.zigTypeTag(mod)) { 5982 .Int => ty, 5983 .Enum => ty.intTagType(mod), 5984 .Float => { 5985 const bit_count = ty.floatBits(target); 5986 if (bit_count > max_atomic_bits) { 5987 diags.* = .{ 5988 .bits = bit_count, 5989 .max_bits = max_atomic_bits, 5990 }; 5991 return error.FloatTooBig; 5992 } 5993 return .none; 5994 }, 5995 .Bool => return .none, 5996 else => { 5997 if (ty.isPtrAtRuntime(mod)) return .none; 5998 return error.BadType; 5999 }, 6000 }; 6001 6002 const bit_count = int_ty.intInfo(mod).bits; 6003 if (bit_count > max_atomic_bits) { 6004 diags.* = .{ 6005 .bits = bit_count, 6006 .max_bits = max_atomic_bits, 6007 }; 6008 return error.IntTooBig; 6009 } 6010 6011 return .none; 6012 } 6013 6014 pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File { 6015 return mod.declPtr(decl_index).getFileScope(mod); 6016 } 6017 6018 /// Returns null in the following cases: 6019 /// * `@TypeOf(.{})` 6020 /// * A struct which has no fields (`struct {}`). 6021 /// * Not a struct. 6022 pub fn typeToStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType { 6023 if (ty.ip_index == .none) return null; 6024 const ip = &mod.intern_pool; 6025 return switch (ip.indexToKey(ty.ip_index)) { 6026 .struct_type => ip.loadStructType(ty.ip_index), 6027 else => null, 6028 }; 6029 } 6030 6031 pub fn typeToPackedStruct(mod: *Module, ty: Type) ?InternPool.LoadedStructType { 6032 const s = mod.typeToStruct(ty) orelse return null; 6033 if (s.layout != .@"packed") return null; 6034 return s; 6035 } 6036 6037 pub fn typeToUnion(mod: *Module, ty: Type) ?InternPool.LoadedUnionType { 6038 if (ty.ip_index == .none) return null; 6039 const ip = &mod.intern_pool; 6040 return switch (ip.indexToKey(ty.ip_index)) { 6041 .union_type => ip.loadUnionType(ty.ip_index), 6042 else => null, 6043 }; 6044 } 6045 6046 pub fn typeToFunc(mod: *Module, ty: Type) ?InternPool.Key.FuncType { 6047 if (ty.ip_index == .none) return null; 6048 return mod.intern_pool.indexToFuncType(ty.toIntern()); 6049 } 6050 6051 pub fn funcOwnerDeclPtr(mod: *Module, func_index: InternPool.Index) *Decl { 6052 return mod.declPtr(mod.funcOwnerDeclIndex(func_index)); 6053 } 6054 6055 pub fn funcOwnerDeclIndex(mod: *Module, func_index: InternPool.Index) Decl.Index { 6056 return mod.funcInfo(func_index).owner_decl; 6057 } 6058 6059 pub fn iesFuncIndex(mod: *const Module, ies_index: InternPool.Index) InternPool.Index { 6060 return mod.intern_pool.iesFuncIndex(ies_index); 6061 } 6062 6063 pub fn funcInfo(mod: *Module, func_index: InternPool.Index) InternPool.Key.Func { 6064 return mod.intern_pool.indexToKey(func_index).func; 6065 } 6066 6067 pub fn fieldSrcLoc(mod: *Module, owner_decl_index: Decl.Index, query: FieldSrcQuery) SrcLoc { 6068 @setCold(true); 6069 const owner_decl = mod.declPtr(owner_decl_index); 6070 const file = owner_decl.getFileScope(mod); 6071 const tree = file.getTree(mod.gpa) catch |err| { 6072 // In this case we emit a warning + a less precise source location. 6073 log.warn("unable to load {s}: {s}", .{ 6074 file.sub_file_path, @errorName(err), 6075 }); 6076 return owner_decl.srcLoc(mod); 6077 }; 6078 const node = owner_decl.relativeToNodeIndex(0); 6079 var buf: [2]Ast.Node.Index = undefined; 6080 if (tree.fullContainerDecl(&buf, node)) |container_decl| { 6081 return queryFieldSrc(tree.*, query, file, container_decl); 6082 } else { 6083 // This type was generated using @Type 6084 return owner_decl.srcLoc(mod); 6085 } 6086 } 6087 6088 pub fn toEnum(mod: *Module, comptime E: type, val: Value) E { 6089 return mod.intern_pool.toEnum(E, val.toIntern()); 6090 } 6091 6092 pub fn isAnytypeParam(mod: *Module, func: InternPool.Index, index: u32) bool { 6093 const file = mod.declPtr(func.owner_decl).getFileScope(mod); 6094 6095 const tags = file.zir.instructions.items(.tag); 6096 6097 const param_body = file.zir.getParamBody(func.zir_body_inst); 6098 const param = param_body[index]; 6099 6100 return switch (tags[param]) { 6101 .param, .param_comptime => false, 6102 .param_anytype, .param_anytype_comptime => true, 6103 else => unreachable, 6104 }; 6105 } 6106 6107 pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]const u8 { 6108 const func = mod.funcInfo(func_index); 6109 const file = mod.declPtr(func.owner_decl).getFileScope(mod); 6110 6111 const tags = file.zir.instructions.items(.tag); 6112 const data = file.zir.instructions.items(.data); 6113 6114 const param_body = file.zir.getParamBody(func.zir_body_inst.resolve(&mod.intern_pool)); 6115 const param = param_body[index]; 6116 6117 return switch (tags[@intFromEnum(param)]) { 6118 .param, .param_comptime => blk: { 6119 const extra = file.zir.extraData(Zir.Inst.Param, data[@intFromEnum(param)].pl_tok.payload_index); 6120 break :blk file.zir.nullTerminatedString(extra.data.name); 6121 }, 6122 .param_anytype, .param_anytype_comptime => blk: { 6123 const param_data = data[@intFromEnum(param)].str_tok; 6124 break :blk param_data.get(file.zir); 6125 }, 6126 else => unreachable, 6127 }; 6128 } 6129 6130 pub const UnionLayout = struct { 6131 abi_size: u64, 6132 abi_align: Alignment, 6133 most_aligned_field: u32, 6134 most_aligned_field_size: u64, 6135 biggest_field: u32, 6136 payload_size: u64, 6137 payload_align: Alignment, 6138 tag_align: Alignment, 6139 tag_size: u64, 6140 padding: u32, 6141 }; 6142 6143 pub fn getUnionLayout(mod: *Module, u: InternPool.LoadedUnionType) UnionLayout { 6144 const ip = &mod.intern_pool; 6145 assert(u.haveLayout(ip)); 6146 var most_aligned_field: u32 = undefined; 6147 var most_aligned_field_size: u64 = undefined; 6148 var biggest_field: u32 = undefined; 6149 var payload_size: u64 = 0; 6150 var payload_align: Alignment = .@"1"; 6151 for (u.field_types.get(ip), 0..) |field_ty, i| { 6152 if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; 6153 6154 const explicit_align = u.fieldAlign(ip, @intCast(i)); 6155 const field_align = if (explicit_align != .none) 6156 explicit_align 6157 else 6158 Type.fromInterned(field_ty).abiAlignment(mod); 6159 const field_size = Type.fromInterned(field_ty).abiSize(mod); 6160 if (field_size > payload_size) { 6161 payload_size = field_size; 6162 biggest_field = @intCast(i); 6163 } 6164 if (field_align.compare(.gte, payload_align)) { 6165 payload_align = field_align; 6166 most_aligned_field = @intCast(i); 6167 most_aligned_field_size = field_size; 6168 } 6169 } 6170 const have_tag = u.flagsPtr(ip).runtime_tag.hasTag(); 6171 if (!have_tag or !Type.fromInterned(u.enum_tag_ty).hasRuntimeBits(mod)) { 6172 return .{ 6173 .abi_size = payload_align.forward(payload_size), 6174 .abi_align = payload_align, 6175 .most_aligned_field = most_aligned_field, 6176 .most_aligned_field_size = most_aligned_field_size, 6177 .biggest_field = biggest_field, 6178 .payload_size = payload_size, 6179 .payload_align = payload_align, 6180 .tag_align = .none, 6181 .tag_size = 0, 6182 .padding = 0, 6183 }; 6184 } 6185 6186 const tag_size = Type.fromInterned(u.enum_tag_ty).abiSize(mod); 6187 const tag_align = Type.fromInterned(u.enum_tag_ty).abiAlignment(mod).max(.@"1"); 6188 return .{ 6189 .abi_size = u.size(ip).*, 6190 .abi_align = tag_align.max(payload_align), 6191 .most_aligned_field = most_aligned_field, 6192 .most_aligned_field_size = most_aligned_field_size, 6193 .biggest_field = biggest_field, 6194 .payload_size = payload_size, 6195 .payload_align = payload_align, 6196 .tag_align = tag_align, 6197 .tag_size = tag_size, 6198 .padding = u.padding(ip).*, 6199 }; 6200 } 6201 6202 pub fn unionAbiSize(mod: *Module, u: InternPool.LoadedUnionType) u64 { 6203 return mod.getUnionLayout(u).abi_size; 6204 } 6205 6206 /// Returns 0 if the union is represented with 0 bits at runtime. 6207 pub fn unionAbiAlignment(mod: *Module, u: InternPool.LoadedUnionType) Alignment { 6208 const ip = &mod.intern_pool; 6209 const have_tag = u.flagsPtr(ip).runtime_tag.hasTag(); 6210 var max_align: Alignment = .none; 6211 if (have_tag) max_align = Type.fromInterned(u.enum_tag_ty).abiAlignment(mod); 6212 for (u.field_types.get(ip), 0..) |field_ty, field_index| { 6213 if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; 6214 6215 const field_align = mod.unionFieldNormalAlignment(u, @intCast(field_index)); 6216 max_align = max_align.max(field_align); 6217 } 6218 return max_align; 6219 } 6220 6221 /// Returns the field alignment, assuming the union is not packed. 6222 /// Keep implementation in sync with `Sema.unionFieldAlignment`. 6223 /// Prefer to call that function instead of this one during Sema. 6224 pub fn unionFieldNormalAlignment(mod: *Module, u: InternPool.LoadedUnionType, field_index: u32) Alignment { 6225 const ip = &mod.intern_pool; 6226 const field_align = u.fieldAlign(ip, field_index); 6227 if (field_align != .none) return field_align; 6228 const field_ty = Type.fromInterned(u.field_types.get(ip)[field_index]); 6229 return field_ty.abiAlignment(mod); 6230 } 6231 6232 /// Returns the index of the active field, given the current tag value 6233 pub fn unionTagFieldIndex(mod: *Module, u: InternPool.LoadedUnionType, enum_tag: Value) ?u32 { 6234 const ip = &mod.intern_pool; 6235 if (enum_tag.toIntern() == .none) return null; 6236 assert(ip.typeOf(enum_tag.toIntern()) == u.enum_tag_ty); 6237 return u.loadTagType(ip).tagValueIndex(ip, enum_tag.toIntern()); 6238 } 6239 6240 /// Returns the field alignment of a non-packed struct in byte units. 6241 /// Keep implementation in sync with `Sema.structFieldAlignment`. 6242 /// asserts the layout is not packed. 6243 pub fn structFieldAlignment( 6244 mod: *Module, 6245 explicit_alignment: InternPool.Alignment, 6246 field_ty: Type, 6247 layout: std.builtin.Type.ContainerLayout, 6248 ) Alignment { 6249 assert(layout != .@"packed"); 6250 if (explicit_alignment != .none) return explicit_alignment; 6251 switch (layout) { 6252 .@"packed" => unreachable, 6253 .auto => { 6254 if (mod.getTarget().ofmt == .c) { 6255 return structFieldAlignmentExtern(mod, field_ty); 6256 } else { 6257 return field_ty.abiAlignment(mod); 6258 } 6259 }, 6260 .@"extern" => return structFieldAlignmentExtern(mod, field_ty), 6261 } 6262 } 6263 6264 /// Returns the field alignment of an extern struct in byte units. 6265 /// This logic is duplicated in Type.abiAlignmentAdvanced. 6266 pub fn structFieldAlignmentExtern(mod: *Module, field_ty: Type) Alignment { 6267 const ty_abi_align = field_ty.abiAlignment(mod); 6268 6269 if (field_ty.isAbiInt(mod) and field_ty.intInfo(mod).bits >= 128) { 6270 // The C ABI requires 128 bit integer fields of structs 6271 // to be 16-bytes aligned. 6272 return ty_abi_align.max(.@"16"); 6273 } 6274 6275 return ty_abi_align; 6276 } 6277 6278 /// https://github.com/ziglang/zig/issues/17178 explored storing these bit offsets 6279 /// into the packed struct InternPool data rather than computing this on the 6280 /// fly, however it was found to perform worse when measured on real world 6281 /// projects. 6282 pub fn structPackedFieldBitOffset( 6283 mod: *Module, 6284 struct_type: InternPool.LoadedStructType, 6285 field_index: u32, 6286 ) u16 { 6287 const ip = &mod.intern_pool; 6288 assert(struct_type.layout == .@"packed"); 6289 assert(struct_type.haveLayout(ip)); 6290 var bit_sum: u64 = 0; 6291 for (0..struct_type.field_types.len) |i| { 6292 if (i == field_index) { 6293 return @intCast(bit_sum); 6294 } 6295 const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[i]); 6296 bit_sum += field_ty.bitSize(mod); 6297 } 6298 unreachable; // index out of bounds 6299 }